diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000000..31f8ccd9abf2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +# editor and OS artifacts +*~ +.DS_STORE + +# query compilation caches +.cache + +# qltest projects and artifacts +*/ql/test/**/*.testproj +*/ql/test/**/*.actual diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000000..784c0518bf96 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,46 @@ +# Contributing to QL + +We welcome contributions to our standard library and standard checks, got an idea for a new check, or how to improve an existing query? Then please go ahead an open a Pull Request! + +Before we accept your pull request, we will require that you have agreed to our Contributor License Agreement, this is not something that you need to do before you submit your pull request, but until you've done so, we will be unable to accept your contribution. + +## 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 might be made 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 QL product. + +Please do get in touch (privacy@semmle.com) if you have any questions about +this or our data protection policies. + +## Contributor License Agreement + +This Contributor License Agreement (“Agreement”) is entered into between Semmle Limited (“Semmle,” “we” or “us” etc.), and You (as defined and further identified below). + +Accordingly, You hereby agree to the following terms for Your present and future Contributions submitted to Semmle: + +1. **Definitions**. + + * "You" (or "Your") shall mean the Contribution copyright owner (whether an individual or organization) or legal entity authorized by the copyright owner that is making this Agreement with Semmle. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + * "Contribution(s)" shall mean the code, documentation or other original works of authorship, including any modifications or additions to an existing work, submitted by You to Semmle for inclusion in, or documentation of, any of the products or projects owned or managed by Semmle (the "Work(s)"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to Semmle or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, Semmle for the purpose of discussing and/or improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution." + +2. **Grant of Copyright License**. You hereby grant to Semmle and to recipients of software distributed by Semmle a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works. + +3. **Grant of Patent License**. You hereby grant to Semmle and to recipients of software distributed by Semmle a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that Your Contribution, or the Work to which You have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed. + +4. **Ownership**. Except as set out above, You keep all right, title, and interest in Your Contribution. The rights that You grant to us under this Agreement are effective on the date You first submitted a Contribution to us, even if Your submission took place before the date You entered this Agreement. + +5. **Representations**. You represent and warrant that: (i) the Contributions are an original work and that You can legally grant the rights set out in this Agreement; (ii) the Contributions and Semmle’s exercise of any license rights granted hereunder, does not and will not, infringe the rights of any third party; (iii) You are not aware of any pending or threatened claims, suits, actions, or charges pertaining to the Contributions, including without limitation any claims or allegations that any or all of the Contributions infringes, violates, or misappropriate the intellectual property rights of any third party (You further agree that You will notify Semmle immediately if You become aware of any such actual or potential claims, suits, actions, allegations or charges). + +6. **Employer**. If Your employer(s) has rights to intellectual property that You create that includes Your Contributions, You represent and warrant that Your employer has waived such rights for Your Contributions to Semmle, or that You have received permission to make Contributions on behalf of that employer and that You are authorized to execute this Agreement on behalf of Your employer. + +7. **Inclusion of Code**. We determine the code that is in our Works. You understand that the decision to include the Contribution in any project or source repository is entirely that of Semmle, and this agreement does not guarantee that the Contributions will be included in any product. + +8. **Disclaimer**. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Except as set forth herein, and unless required by applicable law or agreed to in writing, You provide Your Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND. + +9. **General**. The failure of either party to enforce its rights under this Agreement for any period shall not be construed as a waiver of such rights. No changes or modifications or waivers to this Agreement will be effective unless in writing and signed by both parties. In the event that any provision of this Agreement shall be determined to be illegal or unenforceable, that provision will be limited or eliminated to the minimum extent necessary so that this Agreement shall otherwise remain in full force and effect and enforceable. This Agreement shall be governed by and construed in accordance with the laws of the State of California in the United States without regard to the conflicts of laws provisions thereof. In any action or proceeding to enforce rights under this Agreement, the prevailing party will be entitled to recover costs and attorneys’ fees. diff --git a/COPYRIGHT b/COPYRIGHT new file mode 100644 index 000000000000..65d947ff2746 --- /dev/null +++ b/COPYRIGHT @@ -0,0 +1,13 @@ +Copyright (c) Semmle Inc and other contributors. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the +License at http://www.apache.org/licenses/LICENSE-2.0 + +THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED +WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +MERCHANTABLITY OR NON-INFRINGEMENT. + +See the Apache Version 2.0 License for specific language governing permissions +and limitations under the License. diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000000..d9a10c0d8e86 --- /dev/null +++ b/LICENSE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/README.md b/README.md new file mode 100644 index 000000000000..e21af7ebfbfd --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# Semmle QL + +This open source repository contains the standard QL libraries and queries that power [LGTM](https://lgtm.com), and the other products that [Semmle](https://semmle.com) makes available to its customers worldwide. + +## How do I learn QL and run queries? + +LGTM has [extensive documentation](https://lgtm.com/help/ql/introduction-to-ql) on getting started writing QL, and also has an [interactive query console](https://lgtm.com/help/lgtm/using-query-console) that allows you to try out your queries on any open-source project that's currently being analysed. + +## Contributing + +We welcome contributions to our standard library and standard checks, got an idea for a new check, or how to improve an existing query? Then please go ahead an open a Pull Request! Before you do though, please take the time to read our [contributing guidelines](CONTRIBUTING.md). + +## License + +The lgtm queries are licensed under [Apache License 2.0](LICENSE) by [Semmle](https://semmle.com). diff --git a/change-notes/1.18/analysis-csharp.md b/change-notes/1.18/analysis-csharp.md new file mode 100644 index 000000000000..8ae26bd493e5 --- /dev/null +++ b/change-notes/1.18/analysis-csharp.md @@ -0,0 +1,35 @@ +# Improvements to C# analysis + +> NOTES +> +> Please describe your changes in terms that are suitable for +> customers to read. These notes will have only minor tidying up +> before they are published as part of the release notes. + +## General improvements + +> Changes that affect alerts in many files or from many queries +> For example, changes to file classification + +## New queries + +| **Query** | **Tags** | **Purpose** | +|-----------------------------|-----------|--------------------------------------------------------------------| +| Local scope variable shadows member (cs/local-shadows-member) | maintainability, readability | Replaces the existing queries [Local variable shadows class member (cs/local-shadows-class-member)](https://help.semmle.com/wiki/display/CSHARP/Local+variable+shadows+class+member), [Local variable shadows struct member (cs/local-shadows-struct-member)](https://help.semmle.com/wiki/display/CSHARP/Local+variable+shadows+struct+member), [Parameter shadows class member (cs/parameter-shadows-class-member)](https://help.semmle.com/wiki/display/CSHARP/Parameter+shadows+class+member), and [Parameter shadows struct member (cs/parameter-shadows-struct-member)](https://help.semmle.com/wiki/display/CSHARP/Parameter+shadows+struct+member). | + +## Changes to existing queries + +| **Query** | **Expected impact** | **Change** | +|----------------------------|------------------------|------------------------------------------------------------------| +| [Missing Dispose call on local IDisposable (cs/local-not-disposed)](https://help.semmle.com/wiki/display/CSHARP/Missing+Dispose+call+on+local+IDisposable) | Fewer results | The query identifies more cases where the local variable may be disposed by a library call. | +| [Nested loops with same variable (cs/nested-loops-with-same-variable)](https://help.semmle.com/wiki/display/CSHARP/Nested+loops+with+same+variable) | Fewer results | Results are no longer highlighted in nested loops that share the same condition, and do not use the variable after the inner loop. | +| [Potentially incorrect CompareTo(...) signature (cs/wrong-compareto-signature)](https://help.semmle.com/wiki/display/CSHARP/Potentially+incorrect+CompareTo%28...%29+signature) | Fewer results | Results are no longer highlighted in constructed types. | +| [Useless upcast (cs/useless-upcast)](https://help.semmle.com/wiki/display/CSHARP/Useless+upcast) | Fewer results | The query has been improved to cover more cases where upcasts may be needed. | + +## Changes to code extraction + +* *Series of bullet points* + +## Changes to QL libraries + +* A new non-member predicate `mayBeDisposed()` can be used to determine if a variable is potentially disposed inside a library. It will analyse the CIL code in the library to determine this. diff --git a/change-notes/1.18/analysis-javascript.md b/change-notes/1.18/analysis-javascript.md new file mode 100644 index 000000000000..782927a35a70 --- /dev/null +++ b/change-notes/1.18/analysis-javascript.md @@ -0,0 +1,45 @@ +# Improvements to JavaScript analysis + +## General improvements + +* Additional heuristics have been added to `semmle.javascript.heuristics`. Add `import semmle.javascript.heuristics.all` to a query in order to activate all of the heuristics at once. + +* Modelling of data flow through destructuring assignments has been improved. This may give additional results for the security queries and other queries that rely on data flow. + +* Support for popular libraries has been improved. Consequently, queries may produce more results on code bases that use the following libraries: + - [bluebird](http://bluebirdjs.com) + - [browserid-crypto](https://github.com/mozilla/browserid-crypto) + - [cookie-parser](https://github.com/expressjs/cookie-parser) + - [cookie-session](https://github.com/expressjs/cookie-session) + - [crypto-js](https://github.com/https://github.com/brix/crypto-js) + - [express-jwt](https://github.com/auth0/express-jwt) + - [express-session](https://github.com/expressjs/session) + - [forge](https://github.com/digitalbazaar/forge) + - [MySQL2](https://github.com/sidorares/node-mysql2) + - [q](http://documentup.com/kriskowal/q/) + +## New queries + +| **Query** | **Tags** | **Purpose** | +|-----------------------------|-----------|--------------------------------------------------------------------| +| Disabling Electron webSecurity (`js/disabling-electron-websecurity`) | security, frameworks/electron | Highlights Electron browser objects that are created with the `webSecurity` property set to false. Results shown on LGTM by default. | +| Enabling Electron allowRunningInsecureContent (`js/enabling-electron-insecure-content`) | security, frameworks/electron | Highlights Electron browser objects that are created with the `allowRunningInsecureContent` property set to true. Results shown on LGTM by default. | +| Use of externally-controlled format string (`js/tainted-format-string`) | security, external/cwe/cwe-134 | Highlights format strings containing user-provided data, indicating a violation of [CWE-134](https://cwe.mitre.org/data/definitions/134.html). Results shown on LGTM by default. | + +## Changes to existing queries + +| **Query** | **Expected impact** | **Change** | +|----------------------------|------------------------|------------------------------------------------------------------| +| Arguments redefined | Fewer results | This rule previously also flagged redefinitions of `eval`. This was an oversight that is now fixed. | +| CORS misconfiguration for credentials transfer | More true-positive results | This rule now treats header names case-insensitively. | +| Hard-coded credentials | More true-positive results | This rule now recognizes secret cryptographic keys. | +| Insecure randomness | More true-positive results | This rule now recognizes secret cryptographic keys. | +| Missing X-Frame-Options HTTP header | Fewer false-positive results | This rule now treats header names case-insensitively. | +| Reflected cross-site scripting | Fewer false-positive results | This rule now treats header names case-insensitively. | +| Server-side URL redirect | More true-positive results | This rule now treats header names case-insensitively. | +| Uncontrolled command line | More true-positive results | This rule now recognizes indirect command injection through `sh -c` and similar. | +| Unused variable | Fewer results | This rule no longer flags class expressions that could be made anonymous. While technically true, these results are not interesting. | + +## Changes to QL libraries + +* HTTP header names are now always normalized to lower case to reflect the fact that they are case insensitive. In particular, the result of `HeaderDefinition.getAHeaderName`, and the first parameter of `HeaderDefinition.defines`, `ExplicitHeaderDefinition.definesExplicitly` and `RouteHandler.getAResponseHeader` is now always a lower-case string. diff --git a/cpp/ql/src/.project b/cpp/ql/src/.project new file mode 100644 index 000000000000..6d421778b511 --- /dev/null +++ b/cpp/ql/src/.project @@ -0,0 +1,12 @@ + + + semmlecode-cpp-queries + + + + + + + com.semmle.plugin.qdt.core.qlnature + + diff --git a/cpp/ql/src/.qlpath b/cpp/ql/src/.qlpath new file mode 100644 index 000000000000..d2d16af9aa1a --- /dev/null +++ b/cpp/ql/src/.qlpath @@ -0,0 +1,10 @@ + + + + /semmlecode-cpp-queries + + /semmlecode-cpp-queries/semmlecode.cpp.dbscheme + + cpp + + diff --git a/cpp/ql/src/.vs/VSWorkspaceSettings.json b/cpp/ql/src/.vs/VSWorkspaceSettings.json new file mode 100644 index 000000000000..c98ab5ececab --- /dev/null +++ b/cpp/ql/src/.vs/VSWorkspaceSettings.json @@ -0,0 +1,8 @@ +{ + "ql.projects" : { + "." : { + "dbScheme" : "semmlecode.cpp.dbscheme", + "libraryPath" : [] + } + } +} \ No newline at end of file diff --git a/cpp/ql/src/AlertSuppression.ql b/cpp/ql/src/AlertSuppression.ql new file mode 100644 index 000000000000..874bb9ceab65 --- /dev/null +++ b/cpp/ql/src/AlertSuppression.ql @@ -0,0 +1,89 @@ +/** + * @name Alert suppression + * @description Generates information about alert suppressions. + * @kind alert-suppression + * @id cpp/alert-suppression + */ + +import cpp + +/** + * An alert suppression comment. + */ +class SuppressionComment extends CppStyleComment { + string annotation; + string text; + + SuppressionComment() { + text = getContents().suffix(2) and + ( // match `lgtm[...]` anywhere in the comment + annotation = text.regexpFind("(?i)\\blgtm\\s*\\[[^\\]]*\\]", _, _) + or + // match `lgtm` at the start of the comment and after semicolon + annotation = text.regexpFind("(?i)(?<=^|;)\\s*lgtm(?!\\B|\\s*\\[)", _, _) + .trim() + ) + } + + + /** Gets the text in this comment, excluding the leading //. */ + string getText() { + result = text + } + + /** Gets the suppression annotation in this comment. */ + string getAnnotation() { + result = annotation + } + + /** + * Holds if this comment applies to the range from column `startcolumn` of line `startline` + * to column `endcolumn` of line `endline` in file `filepath`. + */ + predicate covers(string filepath, int startline, int startcolumn, int endline, int endcolumn) { + this.getLocation().hasLocationInfo(filepath, startline, _, endline, endcolumn) and + startcolumn = 1 + } + + /** Gets the scope of this suppression. */ + SuppressionScope getScope() { + this = result.getSuppressionComment() + } +} + +/** + * The scope of an alert suppression comment. + */ +class SuppressionScope extends @comment { + SuppressionScope() { + this instanceof SuppressionComment + } + + /** Gets a suppression comment with this scope. */ + SuppressionComment getSuppressionComment() { + result = this + } + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [LGTM locations](https://lgtm.com/help/ql/locations). + */ + predicate hasLocationInfo(string filepath, int startline, int startcolumn, int endline, int endcolumn) { + this.(SuppressionComment).covers(filepath, startline, startcolumn, endline, endcolumn) + } + + /** Gets a textual representation of this element. */ + string toString() { + result = "suppression range" + } +} + +from SuppressionComment c +select c, // suppression comment + c.getText(), // text of suppression comment (excluding delimiters) + c.getAnnotation(), // text of suppression annotation + c.getScope() // scope of suppression + diff --git a/cpp/ql/src/Architecture/FeatureEnvy.qhelp b/cpp/ql/src/Architecture/FeatureEnvy.qhelp new file mode 100644 index 000000000000..bbc964fcc7a0 --- /dev/null +++ b/cpp/ql/src/Architecture/FeatureEnvy.qhelp @@ -0,0 +1,32 @@ + + + + + +

+This rule finds functions that use more functions and variables from another file than functions and variables from its own file. +This function may be better placed in the other file, to avoid exposing internals of the file it depends on. +

+ +
+ +

See if the function can be moved to the file which contains most of its dependencies.

+ +
+ + +
  • W. C. Wake, Refactoring Workbook, pp. 95 – 96. Addison-Wesley Professional, 2004.
  • +
  • E. Gamma, R. Helm, R. Johnson, J. Vlissides, + Design patterns: elements of reusable object-oriented software. + Addison-Wesley Longman Publishing Co., Inc. Boston, MA, 1995.
  • +
  • + MSDN Magazine: Patterns in Practice: Cohesion And Coupling +
  • + + + + +
    +
    diff --git a/cpp/ql/src/Architecture/FeatureEnvy.ql b/cpp/ql/src/Architecture/FeatureEnvy.ql new file mode 100644 index 000000000000..452ee489ee9f --- /dev/null +++ b/cpp/ql/src/Architecture/FeatureEnvy.ql @@ -0,0 +1,65 @@ +/** + * @name Feature envy + * @description A function that uses more functions and variables from another file than functions and variables from its own file. This function might be better placed in the other file, to avoid exposing internals of the file it depends on. + * @kind problem + * @problem.severity recommendation + * @precision high + * @id cpp/feature-envy + * @tags maintainability + * modularity + * statistical + * non-attributable + */ +import cpp + +predicate functionUsesVariable(Function source, Variable v, File target) { + v.getAnAccess().getEnclosingFunction() = source and + not (v.(LocalScopeVariable).getFunction() = source) and + v.getFile() = target +} + +predicate functionUsesFunction(Function source, Function f, File target) { + exists(FunctionCall fc | fc.getEnclosingFunction() = source and fc.getTarget() = f) and + f.getFile() = target +} + +predicate dependencyCount(Function source, File target, int res) { + res = strictcount(Declaration d | + functionUsesVariable(source, d, target) or + functionUsesFunction(source, d, target) + ) +} + +predicate selfDependencyCountOrZero(Function source, int res) { + exists(File target + | target = source.getFile() and onlyInFile(source, target) + | res = max(int i | dependencyCount(source, target, i) or i = 0)) +} + +predicate dependsHighlyOn(Function source, File target, int res) { + dependencyCount(source, target, res) and + target.fromSource() and + exists(int selfCount | + selfDependencyCountOrZero(source, selfCount) and + res > 2*selfCount and + res > 4 + ) +} + +predicate onlyInFile(Function f, File file) { + file = f.getFile() and + not exists(File file2 | file2 = f.getFile() and file2 != file) +} + +from Function f, File other, int selfCount, int depCount, string selfDeps +where dependsHighlyOn(f, other, depCount) and + selfDependencyCountOrZero(f, selfCount) and + not exists(File yetAnother | dependsHighlyOn(f, yetAnother, _) and yetAnother != other) and + not other instanceof HeaderFile and + not f instanceof MemberFunction + and if selfCount = 0 then selfDeps = "0 dependencies" + else if selfCount = 1 then selfDeps = "only 1 dependency" + else selfDeps = "only " + selfCount.toString() + " dependencies" +select f, "Function " + f.getName() + " could be moved to file $@" + + " since it has " + depCount.toString() + " dependencies to that file, but " + + selfDeps + " to its own file.", other, other.getBaseName() diff --git a/cpp/ql/src/Architecture/General Class-Level Information/ClassHierarchies.qhelp b/cpp/ql/src/Architecture/General Class-Level Information/ClassHierarchies.qhelp new file mode 100644 index 000000000000..423adb9f47ad --- /dev/null +++ b/cpp/ql/src/Architecture/General Class-Level Information/ClassHierarchies.qhelp @@ -0,0 +1,24 @@ + + + + + +

    This query shows graph of class inheritance hierarchy

    + +

    + + +

    +

    + + + + + +

    + + + +
    diff --git a/cpp/ql/src/Architecture/General Class-Level Information/ClassHierarchies.ql b/cpp/ql/src/Architecture/General Class-Level Information/ClassHierarchies.ql new file mode 100644 index 000000000000..3f783276c19c --- /dev/null +++ b/cpp/ql/src/Architecture/General Class-Level Information/ClassHierarchies.ql @@ -0,0 +1,15 @@ +/** + * @name Class hierarchies + * @description Shows classes and their base classes. + * @kind graph + * @id cpp/architecture/class-hierarchies + * @graph.layout organic + * @workingset jhotdraw + * @result succeed 48 + * @result_ondemand succeed 48 + */ +import cpp + +from Class s +where s.fromSource() +select s, s.getABaseClass() diff --git a/cpp/ql/src/Architecture/General Class-Level Information/HubClasses.qhelp b/cpp/ql/src/Architecture/General Class-Level Information/HubClasses.qhelp new file mode 100644 index 000000000000..2199dbe9447f --- /dev/null +++ b/cpp/ql/src/Architecture/General Class-Level Information/HubClasses.qhelp @@ -0,0 +1,25 @@ + + + + + +

    This query shows coupling between classes.

    + +

    Red, large boxes are hub types that depend on many other classes +and are depended on by many other classes.

    + +
    +
    +

    + + + + + +

    + + + +
    diff --git a/cpp/ql/src/Architecture/General Class-Level Information/HubClasses.ql b/cpp/ql/src/Architecture/General Class-Level Information/HubClasses.ql new file mode 100644 index 000000000000..52614712d879 --- /dev/null +++ b/cpp/ql/src/Architecture/General Class-Level Information/HubClasses.ql @@ -0,0 +1,16 @@ +/** + * @name Hub classes + * @description Shows coupling between classes; red, large boxes are hub types that depend on many other classes + * and are depended on by many other classes. + * @kind treemap + * @id cpp/architecture/hub-classes + * @treemap.warnOn highValues + */ +import cpp + +from Class c +where c.fromSource() +select c as Class, + c.getMetrics().getAfferentCoupling() as AfferentCoupling, + c.getMetrics().getEfferentSourceCoupling() as EfferentCoupling +order by AfferentCoupling desc diff --git a/cpp/ql/src/Architecture/General Class-Level Information/InheritanceDepthDistribution.qhelp b/cpp/ql/src/Architecture/General Class-Level Information/InheritanceDepthDistribution.qhelp new file mode 100644 index 000000000000..ae24ffdf2e52 --- /dev/null +++ b/cpp/ql/src/Architecture/General Class-Level Information/InheritanceDepthDistribution.qhelp @@ -0,0 +1,48 @@ + + + + + +

    This query shows the distribution of inheritance depth across all types, i.e. classes. Library types are ignored.

    + +

    The result of this query is a line graph showing, for each number n, how many types have an inheritance depth of n, where +the inheritance depth of a type is the length of a longest path in the inheritance hierarchy from top class to the type.

    + +

    When hovering the mouse pointer over a specific depth value, the number of types having this inheritance depth is displayed.

    + +
    +
    +

    The depth of a type is an indication of how deeply nested a type is in a given design. +Very deep types can be an indication of over-engineering, whereas a system with predominantly shallow types +may not be exploiting object-orientation to the full.

    + + + + + +
    + +
  • +Shyam R. Chidamber and Chris F. Kemerer. +A Metrics Suite for Object Oriented Design +. +IEEE Transactions on Software Engineering, +20(6), pages 476-493, June 1994. + + + +Diomides D. Spinnelis. +Code Quality: The Open Source Perspective. +Addison-Wesley 2007. + + + +Diomides D. Spinnelis. +ckjm - Chidamber and Kemerer Java Metrics. +(implementation of CK metrics), 2006. + + + +
  • diff --git a/cpp/ql/src/Architecture/General Class-Level Information/InheritanceDepthDistribution.ql b/cpp/ql/src/Architecture/General Class-Level Information/InheritanceDepthDistribution.ql new file mode 100644 index 000000000000..2c445b6cf4e0 --- /dev/null +++ b/cpp/ql/src/Architecture/General Class-Level Information/InheritanceDepthDistribution.ql @@ -0,0 +1,22 @@ +/** + * @name Inheritance depth distribution + * @description Shows distribution of inheritance depth across all classes. + * @kind chart + * @id cpp/architecture/inheritance-depth-distribution + * @chart.type line + * @workingset jhotdraw + * @result succeed 48 + * @result_ondemand succeed 48 + */ +import cpp + +/** does source class c have inheritance depth d? */ +predicate hasInheritanceDepth(Class c, int d) { + c.fromSource() and d = c.getMetrics().getInheritanceDepth() +} + +from int depth +where hasInheritanceDepth(_, depth) +select depth as InheritanceDepth, + count(Class c | hasInheritanceDepth(c, depth)) as NumberOfClasses +order by InheritanceDepth diff --git a/cpp/ql/src/Architecture/General Class-Level Information/index.qhelp b/cpp/ql/src/Architecture/General Class-Level Information/index.qhelp new file mode 100644 index 000000000000..353c3ce36494 --- /dev/null +++ b/cpp/ql/src/Architecture/General Class-Level Information/index.qhelp @@ -0,0 +1,13 @@ + + + + + +

    about General Class-Level Information

    + + + +
    +
    diff --git a/cpp/ql/src/Architecture/General Namespace-Level Information/CyclicNamespaces.qhelp b/cpp/ql/src/Architecture/General Namespace-Level Information/CyclicNamespaces.qhelp new file mode 100644 index 000000000000..aed3cf620956 --- /dev/null +++ b/cpp/ql/src/Architecture/General Namespace-Level Information/CyclicNamespaces.qhelp @@ -0,0 +1,25 @@ + + + + + +

    This query shows namespaces that cyclically depend +on one another.

    + +

    + + +

    +

    If there are cyclic dependencies between packages, they cannot be developed and tested independently. It is thus preferable to +eliminate such cycles from the program.

    + + + + + +
    + +
  • Robert Martin's Acyclic Dependencies Principle. +
  • diff --git a/cpp/ql/src/Architecture/General Namespace-Level Information/CyclicNamespaces.ql b/cpp/ql/src/Architecture/General Namespace-Level Information/CyclicNamespaces.ql new file mode 100644 index 000000000000..2f948c1cbcbe --- /dev/null +++ b/cpp/ql/src/Architecture/General Namespace-Level Information/CyclicNamespaces.ql @@ -0,0 +1,13 @@ +/** + * @name Cyclic namespaces + * @description Shows namespaces that cyclically depend on one another. + * @kind graph + * @id cpp/architecture/cyclic-namespaces + * @graph.layout hierarchical + * @tags maintainability + */ +import cpp + +from MetricNamespace a, MetricNamespace b +where a.getANamespaceDependency() = b and b.getANamespaceDependency*() = a +select a, b diff --git a/cpp/ql/src/Architecture/General Namespace-Level Information/GlobalNamespaceClasses.qhelp b/cpp/ql/src/Architecture/General Namespace-Level Information/GlobalNamespaceClasses.qhelp new file mode 100644 index 000000000000..739cce689e8b --- /dev/null +++ b/cpp/ql/src/Architecture/General Namespace-Level Information/GlobalNamespaceClasses.qhelp @@ -0,0 +1,22 @@ + + + + + +

    This query finds classes that belong to no namespace

    + +
    +
    +

    If there are too many classes that belong to no namespace, consider creating namespaces to get a better project structure.

    + + + + + +
    + + + +
    diff --git a/cpp/ql/src/Architecture/General Namespace-Level Information/GlobalNamespaceClasses.ql b/cpp/ql/src/Architecture/General Namespace-Level Information/GlobalNamespaceClasses.ql new file mode 100644 index 000000000000..81f8efb1c742 --- /dev/null +++ b/cpp/ql/src/Architecture/General Namespace-Level Information/GlobalNamespaceClasses.ql @@ -0,0 +1,13 @@ +/** + * @name Global namespace classes + * @description Finds classes that belong to no namespace + * @kind table + * @id cpp/architecture/global-namespace-classes + */ +import cpp + +from Class c +where c.fromSource() + and c.isTopLevel() + and c.getParentScope() instanceof GlobalNamespace +select c, "This class is not declared in any namespace" diff --git a/cpp/ql/src/Architecture/General Namespace-Level Information/NamespaceDependencies.qhelp b/cpp/ql/src/Architecture/General Namespace-Level Information/NamespaceDependencies.qhelp new file mode 100644 index 000000000000..f7b4615d029f --- /dev/null +++ b/cpp/ql/src/Architecture/General Namespace-Level Information/NamespaceDependencies.qhelp @@ -0,0 +1,24 @@ + + + + + +

    This query finds namespace dependencies and draws hierarchical graph.

    + +

    + + +

    +

    + + + + + +

    + + + +
    diff --git a/cpp/ql/src/Architecture/General Namespace-Level Information/NamespaceDependencies.ql b/cpp/ql/src/Architecture/General Namespace-Level Information/NamespaceDependencies.ql new file mode 100644 index 000000000000..e2f8e569ff11 --- /dev/null +++ b/cpp/ql/src/Architecture/General Namespace-Level Information/NamespaceDependencies.ql @@ -0,0 +1,12 @@ +/** + * @name Namespace dependencies + * @description Shows dependencies between namespaces. + * @kind graph + * @id cpp/architecture/namespace-dependencies + * @graph.layout hierarchical + */ +import cpp + +from MetricNamespace a, MetricNamespace b +where a.getANamespaceDependency() = b +select a, b diff --git a/cpp/ql/src/Architecture/General Top-Level Information/GeneralStatistics.qhelp b/cpp/ql/src/Architecture/General Top-Level Information/GeneralStatistics.qhelp new file mode 100644 index 000000000000..a8230232c93f --- /dev/null +++ b/cpp/ql/src/Architecture/General Top-Level Information/GeneralStatistics.qhelp @@ -0,0 +1,34 @@ + + + + + +

    This query shows general statistics about the application, organized as a table.

    + + +

    The query shows the number of namespaces making up the application, the number of files, header files, C files, CPP files, classes, structures, unions, and +functions, and the total number of source code resp. comment lines.

    + +

    The self-containedness measure indicates how much the application +depends on third-party libraries: low self-containedness means that many dependencies +are to library classes (as opposed to source classes within the same application).

    + +
    +
    + +

    The results of this query are purely informative and more useful for getting an overall impression of the application than for +identifying particular defects.

    + + + + + +
    + + + + + +
    diff --git a/cpp/ql/src/Architecture/General Top-Level Information/GeneralStatistics.ql b/cpp/ql/src/Architecture/General Top-Level Information/GeneralStatistics.ql new file mode 100644 index 000000000000..10a560d28368 --- /dev/null +++ b/cpp/ql/src/Architecture/General Top-Level Information/GeneralStatistics.ql @@ -0,0 +1,34 @@ +/** + * @name General statistics + * @description Shows general statistics about the application. + * @kind table + * @id cpp/architecture/general-statistics + */ +import cpp + +from string l, string n +where (l = "Number of Namespaces" and + n = count(Namespace p | p.fromSource()).toString()) + or (l = "Number of Files" and + n = count(File f | f.fromSource()).toString()) + or (l = "Number of Header Files" and + n = count(HeaderFile f | f.fromSource()).toString()) + or (l = "Number of C Files" and + n = count(CFile f | f.fromSource()).toString()) + or (l = "Number of C++ Files" and + n = count(CppFile f | f.fromSource()).toString()) + or (l = "Number of Classes" and + n = count(Class c | c.fromSource() and not c instanceof Struct).toString()) + or (l = "Number of Structs" and + n = count(Struct s | s.fromSource()and not s instanceof Union).toString()) + or (l = "Number of Unions" and + n = count(Union u | u.fromSource()).toString()) + or (l = "Number of Functions" and + n = count(Function f | f.fromSource()).toString()) + or (l = "Number of Lines Of Code" and + n = sum(File f, int toSum | (f.fromSource()) and (toSum = f.getMetrics().getNumberOfLinesOfCode()) | toSum).toString()) + or (l = "Self-Containedness" and + n = (100 * sum(Class c, int toSum | (c.fromSource()) and (toSum = c.getMetrics().getEfferentSourceCoupling()) | toSum) + / sum(Class c, int toSum | (c.fromSource()) and (toSum = c.getMetrics().getEfferentCoupling()) | toSum)).toString() + + "%") +select l as Title, n as Value diff --git a/cpp/ql/src/Architecture/InappropriateIntimacy.cpp b/cpp/ql/src/Architecture/InappropriateIntimacy.cpp new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/cpp/ql/src/Architecture/InappropriateIntimacy.qhelp b/cpp/ql/src/Architecture/InappropriateIntimacy.qhelp new file mode 100644 index 000000000000..728714fefb65 --- /dev/null +++ b/cpp/ql/src/Architecture/InappropriateIntimacy.qhelp @@ -0,0 +1,38 @@ + + + + + +

    +This rule looks for two files that share too much information about each other (accessing many operations or variables in both directions). +It would be better to invert some of the dependencies to reduce the coupling between the two files. +

    + +

    +Having two files have too many dependencies on each other makes it difficult to modify one file without requiring modifications to the +other. This could lead to defects when the programmer forgets to put in the necessary changes to one file when he makes a change to the other. +

    + +
    + +

    Move some of the methods and variables from one file to another, so that most of the dependencies go only in one direction. If possible, +try to make all the dependencies go in one direction.

    + +
    + + +
  • W. C. Wake, Refactoring Workbook, pp. 95 – 96. Addison-Wesley Professional, 2004.
  • +
  • E. Gamma, R. Helm, R. Johnson, J. Vlissides, + Design patterns: elements of reusable object-oriented software. + Addison-Wesley Longman Publishing Co., Inc. Boston, MA, 1995.
  • +
  • + MSDN Magazine: Patterns in Practice: Cohesion And Coupling +
  • + + + + +
    +
    diff --git a/cpp/ql/src/Architecture/InappropriateIntimacy.ql b/cpp/ql/src/Architecture/InappropriateIntimacy.ql new file mode 100644 index 000000000000..8962063520d3 --- /dev/null +++ b/cpp/ql/src/Architecture/InappropriateIntimacy.ql @@ -0,0 +1,63 @@ +/** + * @name Inappropriate Intimacy + * @description Two files share too much information about each other (accessing many operations or variables in both directions). It would be better to invert some of the dependencies to reduce the coupling between the two files. + * @kind problem + * @problem.severity recommendation + * @precision high + * @id cpp/file-intimacy + * @tags maintainability + * modularity + * statistical + * non-attributable + */ +import cpp + +predicate remoteVarAccess(File source, File target, VariableAccess va) { + va.getFile() = source and + va.getTarget().getFile() = target and + // Ignore variables with locations in multiple files + strictcount(File f | f = va.getTarget().getFile()) = 1 and + source != target +} + +predicate remoteFunAccess(File source, File target, FunctionCall fc) { + fc.getFile() = source and + fc.getTarget().getFile() = target and + // Ignore functions with locations in multiple files + strictcount(File f | f = fc.getTarget().getFile()) = 1 and + source != target +} + +predicate candidateFilePair(File source, File target) { + remoteVarAccess(source, target, _) or + remoteFunAccess(source, target, _) +} + +predicate variableDependencyCount(File source, File target, int res) { + candidateFilePair(source, target) and + res = count(VariableAccess va | remoteVarAccess(source, target, va)) +} + +predicate functionDependencyCount(File source, File target, int res) { + candidateFilePair(source, target) and + res = count(FunctionCall fc | remoteFunAccess(source, target, fc)) +} + +predicate highDependencyCount(File source, File target, int res) { + exists(int varCount, int funCount | + variableDependencyCount(source, target, varCount) and + functionDependencyCount(source, target, funCount) and + res = varCount + funCount and + res > 20) +} + +from File a, File b, int ca, int cb +where highDependencyCount(a, b, ca) and + highDependencyCount(b, a, cb) and + ca >= cb and + a != b and + not a instanceof HeaderFile and + not b instanceof HeaderFile and + b.getShortName().trim().length() > 0 +select a, "File is too closely tied to $@ (" + ca.toString() + " dependencies one way and " + cb.toString() + " the other).", + b, b.getBaseName() diff --git a/cpp/ql/src/Architecture/Refactoring Opportunities/ClassesWithManyDependencies.cpp b/cpp/ql/src/Architecture/Refactoring Opportunities/ClassesWithManyDependencies.cpp new file mode 100644 index 000000000000..fea666c554d2 --- /dev/null +++ b/cpp/ql/src/Architecture/Refactoring Opportunities/ClassesWithManyDependencies.cpp @@ -0,0 +1,17 @@ +// an include declaration just adds one source dependency, it does not automatically +// add a dependency from this file to all the declarations in stdio.h +#include +#include // contains non-static global myfile_err + +extern int myfile_err; // this external declaration adds a dependency on myfile.h + +class C { +public: + C() { + // one dependency for printf: + printf("Hello world!"); + // one dependency for FILE type, and one for NULL macro: + FILE fp = NULL; + } +}; + diff --git a/cpp/ql/src/Architecture/Refactoring Opportunities/ClassesWithManyDependencies.qhelp b/cpp/ql/src/Architecture/Refactoring Opportunities/ClassesWithManyDependencies.qhelp new file mode 100644 index 000000000000..a4daf69ffa0d --- /dev/null +++ b/cpp/ql/src/Architecture/Refactoring Opportunities/ClassesWithManyDependencies.qhelp @@ -0,0 +1,34 @@ + + + + + +

    This rule finds classes that depend on many other types. Depending on too many other classes makes a class vulnerable to changes +and defects in its dependencies, and is also a sign that the class lacks cohesion (i.e. a single purpose). These classes +could be refactored into smaller, more cohesive classes.

    + +
    + +

    These classes could probably be refactored into smaller classes with fewer dependencies.

    + +
    + + + + + + + +
  • W. Stevens, G. Myers, L. Constantine, Structured Design, IBM Systems Journal, 13 (2), 115-139, 1974.
  • +
  • +Microsoft Patterns & Practices Team. Architectural Patterns and Styles Microsoft Application Architecture Guide, 2nd Edition. Microsoft Press, 2009. +
  • +
  • + Wikipedia: Code refactoring +
  • + + +
    +
    diff --git a/cpp/ql/src/Architecture/Refactoring Opportunities/ClassesWithManyDependencies.ql b/cpp/ql/src/Architecture/Refactoring Opportunities/ClassesWithManyDependencies.ql new file mode 100644 index 000000000000..941bc2383f44 --- /dev/null +++ b/cpp/ql/src/Architecture/Refactoring Opportunities/ClassesWithManyDependencies.ql @@ -0,0 +1,20 @@ +/** + * @name Classes with too many source dependencies + * @description Finds classes that depend on many other types; they could probably be refactored into smaller classes with fewer dependencies. + * @kind problem + * @id cpp/architecture/classes-with-many-dependencies + * @problem.severity recommendation + * @workingset jhotdraw + * @result succeed 20 + * @result_ondemand succeed 20 + * @tags maintainability + * statistical + * non-attributable + */ +import cpp + +from Class t, int n +where t.fromSource() and + n = t.getMetrics().getEfferentSourceCoupling() and + n > 10 +select t as Class, "This class has too many dependencies (" + n.toString() + ")" diff --git a/cpp/ql/src/Architecture/Refactoring Opportunities/ClassesWithManyFields.cpp b/cpp/ql/src/Architecture/Refactoring Opportunities/ClassesWithManyFields.cpp new file mode 100644 index 000000000000..0b576cc29b0d --- /dev/null +++ b/cpp/ql/src/Architecture/Refactoring Opportunities/ClassesWithManyFields.cpp @@ -0,0 +1,20 @@ +//This struct contains 30 fields. +struct MyParticle { + bool isActive; + int priority; + + float x, y, z; + float dx, dy, dz; + float ddx, ddy, ddz; + bool isCollider; + + int age, maxAge; + float size1, size2; + + bool hasColor; + unsigned char r1, g1, b1, a1; + unsigned char r2, g2, b2, a2; + + class texture *tex; + float u1, v1, u2, v2; +}; diff --git a/cpp/ql/src/Architecture/Refactoring Opportunities/ClassesWithManyFields.qhelp b/cpp/ql/src/Architecture/Refactoring Opportunities/ClassesWithManyFields.qhelp new file mode 100644 index 000000000000..f74f13629a83 --- /dev/null +++ b/cpp/ql/src/Architecture/Refactoring Opportunities/ClassesWithManyFields.qhelp @@ -0,0 +1,36 @@ + + + + + +

    This rule finds classes with more than 15 instance (i.e., non-static) fields. Library classes +are not shown. Having too many fields in one class is a sign that the class lacks cohesion (i.e. lacks a single purpose). +These classes can be split into smaller, more cohesive classes. Alternatively, the related fields can be grouped +into structs.

    + +
    + +

    Classes with many fields may be hard to maintain. They could probably be refactored by breaking them down into smaller classes +and using composition.

    + +
    + + + + + + + +
  • W. Stevens, G. Myers, L. Constantine, Structured Design. IBM Systems Journal, 13 (2), 115-139, 1974.
  • +
  • +Microsoft Patterns & Practices Team, Microsoft Application Architecture Guide (2nd Edition), Chapter 3: Architectural Patterns and Styles. Microsoft Press, 2009 (available online). +
  • +
  • + Wikipedia: Code refactoring +
  • + + +
    +
    diff --git a/cpp/ql/src/Architecture/Refactoring Opportunities/ClassesWithManyFields.ql b/cpp/ql/src/Architecture/Refactoring Opportunities/ClassesWithManyFields.ql new file mode 100644 index 000000000000..1ad2b13154cd --- /dev/null +++ b/cpp/ql/src/Architecture/Refactoring Opportunities/ClassesWithManyFields.ql @@ -0,0 +1,113 @@ +/** + * @name Classes with too many fields + * @description Finds classes with many fields; they could probably be refactored by breaking them down into smaller classes, and using composition. + * @kind problem + * @problem.severity recommendation + * @precision high + * @id cpp/class-many-fields + * @tags maintainability + * statistical + * non-attributable + */ +import cpp + +string kindstr(Class c) +{ + exists(int kind | usertypes(c, _, kind) | + (kind = 1 and result = "Struct") or + (kind = 2 and result = "Class") or + (kind = 6 and result = "Template class") + ) +} + +predicate vdeInfo(VariableDeclarationEntry vde, Class c, File f, int line) +{ + c = vde.getVariable().getDeclaringType() and + f = vde.getLocation().getFile() and + line = vde.getLocation().getStartLine() +} + +predicate previousVde(VariableDeclarationEntry previous, VariableDeclarationEntry vde) +{ + exists(Class c, File f, int line | vdeInfo(vde, c, f, line) | + vdeInfo(previous, c, f, line - 3) or + vdeInfo(previous, c, f, line - 2) or + vdeInfo(previous, c, f, line - 1) or + (vdeInfo(previous, c, f, line) and exists(int prevCol, int vdeCol | + prevCol = previous.getLocation().getStartColumn() and vdeCol = vde.getLocation().getStartColumn() | + prevCol < vdeCol or (prevCol = vdeCol and previous.getName() < vde.getName()) + )) + ) +} + +predicate masterVde(VariableDeclarationEntry master, VariableDeclarationEntry vde) +{ + (not previousVde(_, vde) and master = vde) or + exists(VariableDeclarationEntry previous | previousVde(previous, vde) and masterVde(master, previous)) +} + +class VariableDeclarationGroup extends @var_decl { + VariableDeclarationGroup() { + not previousVde(_, this) + } + Class getClass() { + vdeInfo(this, result, _, _) + } + + // pragma[noopt] since otherwise the two locationInfo relations get join-ordered + // after each other + pragma[noopt] + predicate hasLocationInfo(string path, int startline, int startcol, int endline, int endcol) { + exists(VariableDeclarationEntry last, Location lstart, Location lend | + masterVde(this, last) and + this instanceof VariableDeclarationGroup and + not previousVde(last, _) and + exists(VariableDeclarationEntry vde | vde=this and vde instanceof VariableDeclarationEntry and vde.getLocation() = lstart) and + last.getLocation() = lend and + lstart.hasLocationInfo(path, startline, startcol, _, _) and + lend.hasLocationInfo(path, _, _, endline, endcol) + ) + } + + string toString() { + if previousVde(this, _) then + result = "group of " + + strictcount(string name + | exists(VariableDeclarationEntry vde + | masterVde(this, vde) and + name = vde.getName())) + + " fields here" + else + result = "declaration of " + this.(VariableDeclarationEntry).getVariable().getName() + } +} + +class ExtClass extends Class { + predicate hasOneVariableGroup() { + strictcount(VariableDeclarationGroup vdg | vdg.getClass() = this) = 1 + } + + predicate hasLocationInfo(string path, int startline, int startcol, int endline, int endcol) { + if hasOneVariableGroup() then + exists(VariableDeclarationGroup vdg | vdg.getClass() = this | vdg.hasLocationInfo(path, startline, startcol, endline, endcol)) + else + getLocation().hasLocationInfo(path, startline, startcol, endline, endcol) + } +} + +from ExtClass c, int n, VariableDeclarationGroup vdg, string suffix +where n = strictcount(string fieldName + | exists(Field f + | f.getDeclaringType() = c and + fieldName = f.getName() and + // IBOutlet's are a way of building GUIs + // automatically out of ObjC properties. + // We don't want to count those for the + // purposes of this query. + not (f.getType().getAnAttribute().hasName("iboutlet")))) and + n > 15 and + not c.isConstructedFrom(_) and + c = vdg.getClass() and + if c.hasOneVariableGroup() then suffix = "" else suffix = " - see $@" +select c, kindstr(c) + " " + c.getName() + " has " + n + " fields, which is too many" + suffix + ".", + vdg, vdg.toString() diff --git a/cpp/ql/src/Architecture/Refactoring Opportunities/ComplexFunctions.qhelp b/cpp/ql/src/Architecture/Refactoring Opportunities/ComplexFunctions.qhelp new file mode 100644 index 000000000000..d1fdf7f039bc --- /dev/null +++ b/cpp/ql/src/Architecture/Refactoring Opportunities/ComplexFunctions.qhelp @@ -0,0 +1,22 @@ + + + + + +

    This rule finds functions that make too many calls. Having too many dependencies makes a function vulnerable to +changes and defects in those dependencies, and is also a sign that the function lacks cohesion (i.e. lacks a single purpose). +These functions can be split into smaller, more cohesive functions.

    + +
    + +

    Splitting these functions would increase maintainability and readability.

    + +
    + + + +
  • W. Stevens, G. Myers, L. Constantine, Structured Design, IBM Systems Journal, 13 (2), 115-139, 1974. + +
  • diff --git a/cpp/ql/src/Architecture/Refactoring Opportunities/ComplexFunctions.ql b/cpp/ql/src/Architecture/Refactoring Opportunities/ComplexFunctions.ql new file mode 100644 index 000000000000..bc75e4bb6580 --- /dev/null +++ b/cpp/ql/src/Architecture/Refactoring Opportunities/ComplexFunctions.ql @@ -0,0 +1,18 @@ +/** + * @name Complex functions + * @description Finds functions which call too many other functions. Splitting these functions would increase maintainability and readability. + * @kind problem + * @id cpp/architecture/complex-functions + * @problem.severity recommendation + * @tags maintainability + * statistical + * non-attributable + */ +import cpp + +from Function f, int n +where f.fromSource() and + n = f.getMetrics().getNumberOfCalls() and + n > 99 and + not f.isMultiplyDefined() +select f as Function, "This function makes too many calls (" + n.toString() + ")" diff --git a/cpp/ql/src/Architecture/Refactoring Opportunities/CyclomaticComplexity.qhelp b/cpp/ql/src/Architecture/Refactoring Opportunities/CyclomaticComplexity.qhelp new file mode 100644 index 000000000000..7b01d3ba04b9 --- /dev/null +++ b/cpp/ql/src/Architecture/Refactoring Opportunities/CyclomaticComplexity.qhelp @@ -0,0 +1,30 @@ + + + + + +

    This rule computes cyclomatic complexity per function. Cyclomatic complexity is a measure of how complex a function +is, and is derived from the number of branching statements in the function. Complex functions can be difficult to understand +and maintain, and usually can be split into smaller, less complex functions.

    + +

    The rule finds functions with high (e.g. >50) cyclomatic complexity.

    + +
    + +

    With increasing cyclomatic complexity there need to be more test cases that are necessary to achieve a complete branch coverage when testing the function. +Try to reduce the function's complexity by splitting it into several more cohesive functions. +A particularly effective way of reducing complexity is to put code that handles a particular case in an large if or +switch statement into a separate function with a descriptive name. This not only reduces the complexity of the function, +but makes it considerably more readable as the function's descriptive name gives an idea of its purpose without the developer +analyzing each line of code. +

    + +
    + + +
  • Wikipedia: Cyclomatic complexity.
  • +
  • T. McCabe. A Complexity Measure. ICSE '76: Proceedings of the 2nd international conference on Software engineering, 1976.
  • +
    +
    diff --git a/cpp/ql/src/Architecture/Refactoring Opportunities/CyclomaticComplexity.ql b/cpp/ql/src/Architecture/Refactoring Opportunities/CyclomaticComplexity.ql new file mode 100644 index 000000000000..a4f9fe18af27 --- /dev/null +++ b/cpp/ql/src/Architecture/Refactoring Opportunities/CyclomaticComplexity.ql @@ -0,0 +1,16 @@ +/** + * @name Cyclomatic Complexity + * @description Functions with high cyclomatic complexity. With increasing cyclomatic complexity there need to be more test cases that are necessary to achieve a complete branch coverage when testing this function. + * @kind problem + * @id cpp/architecture/cyclomatic-complexity + * @problem.severity warning + * @tags testability + * statistical + * non-attributable + */ +import cpp + +from Function f, int complexity +where complexity = f.getMetrics().getCyclomaticComplexity() + and complexity > 250 +select f, "Function has high cyclomatic complexity: " + complexity.toString() diff --git a/cpp/ql/src/Architecture/Refactoring Opportunities/FunctionsWithManyParameters.cpp b/cpp/ql/src/Architecture/Refactoring Opportunities/FunctionsWithManyParameters.cpp new file mode 100644 index 000000000000..624226bff6ad --- /dev/null +++ b/cpp/ql/src/Architecture/Refactoring Opportunities/FunctionsWithManyParameters.cpp @@ -0,0 +1,8 @@ +// this example has 15 parameters. +void fillRect(int x, int y, int w, int h, + int r1, int g1, int b1, int a1, + int r2, int g2, int b2, int a2, + gradient_type grad, unsigned int flags, bool border) +{ + // ... +} diff --git a/cpp/ql/src/Architecture/Refactoring Opportunities/FunctionsWithManyParameters.qhelp b/cpp/ql/src/Architecture/Refactoring Opportunities/FunctionsWithManyParameters.qhelp new file mode 100644 index 000000000000..859073a482dc --- /dev/null +++ b/cpp/ql/src/Architecture/Refactoring Opportunities/FunctionsWithManyParameters.qhelp @@ -0,0 +1,25 @@ + + + + + +

    This rule finds functions with many parameters. Passing too many parameters to a function is a sign that the function +is not cohesive (i.e. lacks a single purpose). These functions could be split into smaller, more cohesive functions.

    + +
    + +

    These functions could probably be refactored by wrapping related parameters into structs.

    + +
    + + + + + + + +
  • S. McConnell. Code Complete, 2d ed. Microsoft Press, 2004.
  • +
    +
    diff --git a/cpp/ql/src/Architecture/Refactoring Opportunities/FunctionsWithManyParameters.ql b/cpp/ql/src/Architecture/Refactoring Opportunities/FunctionsWithManyParameters.ql new file mode 100644 index 000000000000..47162f763385 --- /dev/null +++ b/cpp/ql/src/Architecture/Refactoring Opportunities/FunctionsWithManyParameters.ql @@ -0,0 +1,18 @@ +/** + * @name Functions with too many parameters + * @description Finds functions with many parameters; + * they could probably be refactored by wrapping parameters into a struct. + * @kind problem + * @id cpp/architecture/functions-with-many-parameters + * @problem.severity recommendation + * @tags testability + * statistical + * non-attributable + */ +import cpp + +from Function f +where f.fromSource() and + f.getMetrics().getNumberOfParameters() > 15 +select f, "This function has too many parameters (" + + f.getMetrics().getNumberOfParameters().toString() + ")" diff --git a/cpp/ql/src/Architecture/index.qhelp b/cpp/ql/src/Architecture/index.qhelp new file mode 100644 index 000000000000..3474dcf8dfac --- /dev/null +++ b/cpp/ql/src/Architecture/index.qhelp @@ -0,0 +1,13 @@ + + + + + +

    about Architecture

    + + + +
    +
    diff --git a/cpp/ql/src/Best Practices/BlockWithTooManyStatements.qhelp b/cpp/ql/src/Best Practices/BlockWithTooManyStatements.qhelp new file mode 100644 index 000000000000..a1615e81a556 --- /dev/null +++ b/cpp/ql/src/Best Practices/BlockWithTooManyStatements.qhelp @@ -0,0 +1,42 @@ + + + + + +

    +This rule finds blocks of code that have too many complex statements, +such as branching statements (if, switch), and loops (for, while). +

    + +

    +Blocks with too many consecutive statements are candidates for refactoring. +Only complex statements are counted here (eg. for, while, switch ...). +The top-level logic will be clearer if each complex statement is extracted to a function. +

    + +
    + +

    It is often the case that each consecutive complex statement performs a dedicated separate task. It is a very common case that each complex statement is actually commented with a description of the task. Extract each such task into its own function for improved readability and to promote reuse.

    + +
    + + +
  • + M. Fowler. Refactoring Addison-Wesley, 1999. +
  • +
  • + Wikipedia: Code refactoring +
  • +
  • + Microsoft Patterns & Practices Team. Architectural Patterns and Styles Microsoft Application Architecture Guide, 2nd Edition. Microsoft Press, 2009. +
  • + + + + + + +
    +
    diff --git a/cpp/ql/src/Best Practices/BlockWithTooManyStatements.ql b/cpp/ql/src/Best Practices/BlockWithTooManyStatements.ql new file mode 100644 index 000000000000..b1d82f04853c --- /dev/null +++ b/cpp/ql/src/Best Practices/BlockWithTooManyStatements.ql @@ -0,0 +1,27 @@ +/** + * @name Block with too many statements + * @description Blocks with too many consecutive statements are candidates for refactoring. Only complex statements are counted here (eg. for, while, switch ...). The top-level logic will be clearer if each complex statement is extracted to a function. + * @kind problem + * @problem.severity recommendation + * @precision high + * @id cpp/complex-block + * @tags testability + * readability + * maintainability + */ +import cpp + +class ComplexStmt extends Stmt { + ComplexStmt() { + exists(Block body | body = this.(Loop ).getStmt() or + body = this.(SwitchStmt).getStmt() + | strictcount(body.getAStmt+()) > 6) + and not exists (this.getGeneratingMacro()) + } +} + +from Block b, int n, ComplexStmt complexStmt +where n = strictcount(ComplexStmt s | s = b.getAStmt()) and n > 3 + and complexStmt = b.getAStmt() +select b, "Block with too many statements (" + n.toString() + " complex statements in the block). Complex statements at: $@", complexStmt, complexStmt.toString() + diff --git a/cpp/ql/src/Best Practices/ComplexCondition.cpp b/cpp/ql/src/Best Practices/ComplexCondition.cpp new file mode 100644 index 000000000000..8a8638c5bbe1 --- /dev/null +++ b/cpp/ql/src/Best Practices/ComplexCondition.cpp @@ -0,0 +1,13 @@ +//This condition is too complex and can be improved by using local variables +bool accept_message = + (message_type == CONNECT && _state != CONNECTED) || + (message_type == DISCONNECT && _state == CONNECTED) || + (message_type == DATA && _state == CONNECTED); + +//This condition is acceptable, as all the logical operators are of the same type (&&) +bool valid_connect = + message_type == CONNECT && + _state != CONNECTED && + time_since_prev_connect > MAX_CONNECT_INTERVAL && + message_length <= MAX_PACKET_SIZE && + checksum(message) == get_checksum_field(message); \ No newline at end of file diff --git a/cpp/ql/src/Best Practices/ComplexCondition.qhelp b/cpp/ql/src/Best Practices/ComplexCondition.qhelp new file mode 100644 index 000000000000..93c5d0651c18 --- /dev/null +++ b/cpp/ql/src/Best Practices/ComplexCondition.qhelp @@ -0,0 +1,43 @@ + + + + + +

    +This rule finds boolean expressions that have more than 5 consecutive operators that are not of the same type (e.g. alternating && and || operators). +Long chains of operators of the same type are not flagged as violations of this rule. +

    + +

    +Complex boolean expressions are hard to read. Consequently, when modifying such expressions +there is an increased risk of introducing defects. +Naming intermediate results as local variables will make the logic easier to read and understand. +

    + +
    + +

    Use local variables or macros to represent intermediate values to make the condition easier to understand.

    + +
    + + + + + + +
  • +Operators +
  • +
  • +Conditionals +
  • + + + + + + +
    +
    diff --git a/cpp/ql/src/Best Practices/ComplexCondition.ql b/cpp/ql/src/Best Practices/ComplexCondition.ql new file mode 100644 index 000000000000..dccfc5d8b84a --- /dev/null +++ b/cpp/ql/src/Best Practices/ComplexCondition.ql @@ -0,0 +1,33 @@ +/** + * @name Complex condition + * @description Boolean expressions that are too deeply nested are hard to read and understand. Consider naming intermediate results as local variables. + * @kind problem + * @problem.severity recommendation + * @precision high + * @id cpp/complex-condition + * @tags testability + * readability + * maintainability + * statistical + * non-attributable + */ +import cpp + +predicate logicalOp(string op) { + op = "&&" or op = "||" +} + +predicate nontrivialLogicalOperator(Operation e) { + exists(string op | + op = e.getOperator() and + logicalOp(op) and + not (op = e.getParent().(Operation).getOperator()) + ) + and not e.isInMacroExpansion() +} + +from Expr e, int operators +where not (e.getParent() instanceof Expr) + and operators = count(Operation op | op.getParent*() = e and nontrivialLogicalOperator(op)) + and operators > 5 +select e, "Complex condition: too many logical operations in this expression." diff --git a/cpp/ql/src/Best Practices/Exceptions/AccidentalRethrow.qhelp b/cpp/ql/src/Best Practices/Exceptions/AccidentalRethrow.qhelp new file mode 100644 index 000000000000..99a70191614d --- /dev/null +++ b/cpp/ql/src/Best Practices/Exceptions/AccidentalRethrow.qhelp @@ -0,0 +1,40 @@ + + + + + +

    The C++ throw expression can take several forms. One form throws a new exception, whereas the +other re-throws the current exception. In the latter case, if there is no current exception, then the program +will be terminated. Presence of a re-throw outside of an exception handling context is often caused by the +programmer not knowing what kind of exception to throw.

    + +
    + +

    The throw expression should be changed to throw a particular type of exception.

    + +
    + + +void bad() { + /* ... */ + if(error_condition) + throw; +} + +void good() { + /* ... */ + if(error_condition) + throw std::exception("Something went wrong."); +} + + + + + +
  • Open Standards: Standard for Programming Language C++, draft n3337 [except.throw], clause 9, page 380.
  • + + +
    +
    diff --git a/cpp/ql/src/Best Practices/Exceptions/AccidentalRethrow.ql b/cpp/ql/src/Best Practices/Exceptions/AccidentalRethrow.ql new file mode 100644 index 000000000000..2ed8cbf333a9 --- /dev/null +++ b/cpp/ql/src/Best Practices/Exceptions/AccidentalRethrow.ql @@ -0,0 +1,25 @@ +/** + * @name Accidental rethrow + * @description When there is nothing to rethrow, attempting to rethrow an exception will terminate the program. + * @kind problem + * @problem.severity warning + * @precision high + * @id cpp/rethrow-no-exception + * @tags reliability + * correctness + * exceptions + */ + +import cpp + +predicate isInCatch(Expr e) { + e.getEnclosingStmt().getParent*() instanceof CatchBlock or // Lexically enclosing catch blocks will cause there to be a current exception, + exists(Function f | f = e.getEnclosingFunction() | + isInCatch(f.getACallToThisFunction()) or // as will dynamically enclosing catch blocks. + f.getName().toLowerCase().matches("%exception%") // We assume that rethrows are intended when the function is called *exception*. + ) +} + +from ReThrowExpr e +where not isInCatch(e) +select e, "As there is no current exception, this rethrow expression will terminate the program." diff --git a/cpp/ql/src/Best Practices/Exceptions/CatchingByValue.qhelp b/cpp/ql/src/Best Practices/Exceptions/CatchingByValue.qhelp new file mode 100644 index 000000000000..3c60845c4a54 --- /dev/null +++ b/cpp/ql/src/Best Practices/Exceptions/CatchingByValue.qhelp @@ -0,0 +1,51 @@ + + + + + +

    Catching an exception by value will create a new local variable which is a copy of the originally thrown object. +Creating the copy is slightly wasteful, but not catastrophic. More worrisome is the fact that if the type being +caught is a strict supertype of the originally thrown type, then the copy might not contain as much information +as the original exception.

    + +
    + +

    The parameter to the catch block should have its type changed from T to T& +or const T&.

    + +
    + + +void bad() { + try { + /* ... */ + } + catch(std::exception a_copy_of_the_thrown_exception) { + // Do something with a_copy_of_the_thrown_exception + } +} + +void good() { + try { + /* ... */ + } + catch(const std::exception& the_thrown_exception) { + // Do something with the_thrown_exception + } +} + + + + + +
  • C++ FAQ: + What should I throw?, + What should I catch?.
  • +
  • Wikibooks: + Throwing objects.
  • + + +
    +
    diff --git a/cpp/ql/src/Best Practices/Exceptions/CatchingByValue.ql b/cpp/ql/src/Best Practices/Exceptions/CatchingByValue.ql new file mode 100644 index 000000000000..f593ae32f842 --- /dev/null +++ b/cpp/ql/src/Best Practices/Exceptions/CatchingByValue.ql @@ -0,0 +1,17 @@ +/** + * @name Catching by value + * @description Catching an exception by value will create a copy of the thrown exception, thereby potentially slicing the original exception object. + * @kind problem + * @problem.severity warning + * @precision very-high + * @id cpp/catch-by-value + * @tags efficiency + * correctness + * exceptions + */ + +import cpp + +from CatchBlock cb, Class caughtType +where caughtType = cb.getParameter().getType().getUnderlyingType().getUnspecifiedType() +select cb, "This should catch a " + caughtType.getName() + " by (const) reference rather than by value." diff --git a/cpp/ql/src/Best Practices/Exceptions/LeakyCatch.qhelp b/cpp/ql/src/Best Practices/Exceptions/LeakyCatch.qhelp new file mode 100644 index 000000000000..1f413108bb94 --- /dev/null +++ b/cpp/ql/src/Best Practices/Exceptions/LeakyCatch.qhelp @@ -0,0 +1,46 @@ + + + + + +

    Modern C++ code and frameworks should not throw or catch pointers. Older frameworks, such as Microsoft's MFC, +do throw and catch pointers. Said pointers will generally point to an exception object allocated on the heap, +and therefore need to be freed when they are caught. Failure to free them will result in a memory leak.

    + +
    + +

    The catch block should be augmented to delete the exception pointer.

    + +
    + + +void bad() { + try { + /* ... */ + } + catch(CException* e) { + e->ReportError(); + } +} + +void good() { + try { + /* ... */ + } + catch(CException* e) { + e->ReportError(); + e->Delete(); + } +} + + + + + +
  • MSDN Library for MFC: Exceptions: Catching and Deleting Exceptions.
  • + + +
    +
    diff --git a/cpp/ql/src/Best Practices/Exceptions/LeakyCatch.ql b/cpp/ql/src/Best Practices/Exceptions/LeakyCatch.ql new file mode 100644 index 000000000000..2ed289779353 --- /dev/null +++ b/cpp/ql/src/Best Practices/Exceptions/LeakyCatch.ql @@ -0,0 +1,48 @@ +/** + * @name Leaky catch + * @description If an exception is allocated on the heap, then it should be deleted when caught. + * @kind problem + * @problem.severity warning + * @precision high + * @id cpp/catch-missing-free + * @tags efficiency + * correctness + * exceptions + * external/cwe/cwe-401 + */ + +import cpp + +predicate doesRethrow(Function f) { + exists(ReThrowExpr e | e.getEnclosingFunction() = f | + not e.getEnclosingStmt().getParent*() instanceof CatchBlock + ) + or + exists(FunctionCall fc | fc.getEnclosingFunction() = f | + doesRethrow(fc.getTarget()) + ) +} + +predicate deletesException(Expr expr, Parameter exception) { + expr.getEnclosingBlock().getParent*().(CatchBlock).getParameter() = exception and ( + exists(FunctionCall fc | fc = expr | + // Calling a delete function on the exception will free it (MFC's CException has a Delete function). + (fc.getQualifier() = exception.getAnAccess() and fc.getTarget().getName().toLowerCase().matches("%delete%")) or + // Passing the exception to a function might free it. + (fc.getAnArgument() = exception.getAnAccess()) or + // Calling a function which rethrows the current exception might cause the exception to be freed. + doesRethrow(fc.getTarget()) + ) or + // Calling operator delete on the exception will free it. + exists(DeleteExpr d | d = expr | + d.getExpr() = exception.getAnAccess() + ) + ) +} + +from CatchBlock cb +where cb.getParameter().getType().getUnderlyingType() instanceof PointerType +and not exists(Expr e | e.getEnclosingBlock().getParent*() = cb | + deletesException(e, cb.getParameter()) +) +select cb, "This catch block does not free the caught exception, thereby leaking memory." diff --git a/cpp/ql/src/Best Practices/Exceptions/ThrowingPointers.qhelp b/cpp/ql/src/Best Practices/Exceptions/ThrowingPointers.qhelp new file mode 100644 index 000000000000..a05903bd350e --- /dev/null +++ b/cpp/ql/src/Best Practices/Exceptions/ThrowingPointers.qhelp @@ -0,0 +1,44 @@ + + + + + +

    As C++ is not a garbage collected language, exceptions should not be dynamically allocated. Dynamically +allocating an exception puts an onus on every catch site to ensure that the memory is freed.

    + +

    As a special case, it is permissible to throw anything derived from Microsoft MFC's CException +class as a pointer. This is for historical reasons; modern code and modern frameworks should not throw +pointer values.

    + +
    + +

    The new keyword immediately following the throw keyword should be removed. Any +catch sites which previously caught the pointer should be changed to catch by reference or +const reference.

    + +
    + + +void bad() { + throw new std::exception("This is how not to throw an exception"); +} + +void good() { + throw std::exception("This is how to throw an exception"); +} + + + + + +
  • C++ FAQ: + What should I throw?, + What should I catch?.
  • +
  • Wikibooks: + Throwing objects.
  • + + +
    +
    diff --git a/cpp/ql/src/Best Practices/Exceptions/ThrowingPointers.ql b/cpp/ql/src/Best Practices/Exceptions/ThrowingPointers.ql new file mode 100644 index 000000000000..5d4b27d02ef4 --- /dev/null +++ b/cpp/ql/src/Best Practices/Exceptions/ThrowingPointers.ql @@ -0,0 +1,20 @@ +/** + * @name Throwing pointers + * @description Exceptions should be objects rather than pointers to objects. + * @kind problem + * @problem.severity warning + * @precision high + * @id cpp/throwing-pointer + * @tags efficiency + * correctness + * exceptions + */ + +import cpp + +from ThrowExpr throw, NewExpr new, Type t +where new.getParent() = throw + // Microsoft MFC's CException hierarchy should be thrown (and caught) as pointers + and t = new.getAllocatedType() + and not t.getUnderlyingType().(Class).getABaseClass*().hasName("CException") +select throw, "This should throw a " + t.toString() + " rather than a pointer to one." diff --git a/cpp/ql/src/Best Practices/Hiding/DeclarationHidesParameter.cpp b/cpp/ql/src/Best Practices/Hiding/DeclarationHidesParameter.cpp new file mode 100644 index 000000000000..687129d88152 --- /dev/null +++ b/cpp/ql/src/Best Practices/Hiding/DeclarationHidesParameter.cpp @@ -0,0 +1,6 @@ +void f(int i) { + for (int i = 0; i < 10; ++i) { //the loop variable hides the parameter to f() + ... + } +} + diff --git a/cpp/ql/src/Best Practices/Hiding/DeclarationHidesParameter.qhelp b/cpp/ql/src/Best Practices/Hiding/DeclarationHidesParameter.qhelp new file mode 100644 index 000000000000..54c6b0e006a1 --- /dev/null +++ b/cpp/ql/src/Best Practices/Hiding/DeclarationHidesParameter.qhelp @@ -0,0 +1,30 @@ + + + + + +

    This rule finds declarations of local variables that hide parameters of the surrounding function. Such declarations +create variables with the same name but different scopes. This makes it hard to understand which variable is actually +being used in an expression.

    + +
    + +

    Consider changing the name of either the variable or the parameter to keep them distinct.

    + +
    + + + + + + + +
  • + B. Stroustrup. The C++ Programming Language Special Edition p 82. Addison Wesley. 2000. +
  • + + +
    +
    diff --git a/cpp/ql/src/Best Practices/Hiding/DeclarationHidesParameter.ql b/cpp/ql/src/Best Practices/Hiding/DeclarationHidesParameter.ql new file mode 100644 index 000000000000..54b59d3f5c2f --- /dev/null +++ b/cpp/ql/src/Best Practices/Hiding/DeclarationHidesParameter.ql @@ -0,0 +1,36 @@ +/** + * @name Declaration hides parameter + * @description A local variable hides a parameter. This may be confusing. Consider renaming one of them. + * @kind problem + * @problem.severity recommendation + * @precision very-high + * @id cpp/declaration-hides-parameter + * @tags maintainability + * readability + */ +import cpp + + +/* Names of parameters in the implementation of a function. + Notice that we need to exclude parameter names used in prototype + declarations and only include the ones from the actual definition. + We also exclude names from functions that have multiple definitions. + 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. */ +ParameterDeclarationEntry functionParameterNames(Function f, string name) { + exists(FunctionDeclarationEntry fe | + result.getFunctionDeclarationEntry() = fe + and fe.getFunction() = f + and fe.getLocation() = f.getDefinitionLocation() + and strictcount(f.getDefinitionLocation()) = 1 + and result.getName() = name + ) +} + +from Function f, LocalVariable lv, ParameterDeclarationEntry pde +where f = lv.getFunction() and + pde = functionParameterNames(f, lv.getName()) and + not lv.isInMacroExpansion() +select lv, "Local variable '"+ lv.getName() +"' hides a $@.", + pde, "parameter of the same name" diff --git a/cpp/ql/src/Best Practices/Hiding/DeclarationHidesVariable.cpp b/cpp/ql/src/Best Practices/Hiding/DeclarationHidesVariable.cpp new file mode 100644 index 000000000000..bc96def17dfd --- /dev/null +++ b/cpp/ql/src/Best Practices/Hiding/DeclarationHidesVariable.cpp @@ -0,0 +1,12 @@ +void f() { + int i = 10; + + for (int i = 0; i < 10; i++) { //the loop counter hides the variable + ... + } + + { + int i = 12; //this variable hides the variable in the outer block + ... + } +} diff --git a/cpp/ql/src/Best Practices/Hiding/DeclarationHidesVariable.qhelp b/cpp/ql/src/Best Practices/Hiding/DeclarationHidesVariable.qhelp new file mode 100644 index 000000000000..0fbcbbd92b8f --- /dev/null +++ b/cpp/ql/src/Best Practices/Hiding/DeclarationHidesVariable.qhelp @@ -0,0 +1,30 @@ + + + + + +

    This rule finds declarations of local variables that hide a local variable from a surrounding scope. Such declarations +create variables with the same name but different scopes. This makes it difficult to know which variable is actually +used in an expression.

    + +
    + +

    Consider changing the name of either variable to keep them distinct.

    + +
    + + + + + + + +
  • + B. Stroustrup. The C++ Programming Language Special Edition p 82. Addison Wesley. 2000. +
  • + + +
    +
    diff --git a/cpp/ql/src/Best Practices/Hiding/DeclarationHidesVariable.ql b/cpp/ql/src/Best Practices/Hiding/DeclarationHidesVariable.ql new file mode 100644 index 000000000000..7056380f5bb9 --- /dev/null +++ b/cpp/ql/src/Best Practices/Hiding/DeclarationHidesVariable.ql @@ -0,0 +1,21 @@ +/** + * @name Declaration hides variable + * @description A local variable hides another local variable from a surrounding scope. This may be confusing. Consider renaming one of the variables. + * @kind problem + * @problem.severity recommendation + * @precision high + * @id cpp/declaration-hides-variable + * @tags maintainability + * readability + */ +import cpp + +import Best_Practices.Hiding.Shadowing + +from LocalVariable lv1, LocalVariable lv2 +where shadowing(lv1, lv2) and + not lv1.getParentScope().(Block).isInMacroExpansion() and + not lv2.getParentScope().(Block).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/Hiding/LocalVariableHidesGlobalVariable.cpp b/cpp/ql/src/Best Practices/Hiding/LocalVariableHidesGlobalVariable.cpp new file mode 100644 index 000000000000..61590c860111 --- /dev/null +++ b/cpp/ql/src/Best Practices/Hiding/LocalVariableHidesGlobalVariable.cpp @@ -0,0 +1,12 @@ +int i = 10; + +void f() { + for (int i = 0; i < 10; i++) { //the loop counter hides the global variable i + ... + } + + { + int i = 12; //this variable hides the global variable i + ... + } +} diff --git a/cpp/ql/src/Best Practices/Hiding/LocalVariableHidesGlobalVariable.qhelp b/cpp/ql/src/Best Practices/Hiding/LocalVariableHidesGlobalVariable.qhelp new file mode 100644 index 000000000000..1d96a5786139 --- /dev/null +++ b/cpp/ql/src/Best Practices/Hiding/LocalVariableHidesGlobalVariable.qhelp @@ -0,0 +1,29 @@ + + + + + +

    This rule finds declarations of local variables or parameters that hide a global variable. +Such declarations create variables with the same name but different scopes. This makes it +difficult to know which variable is actually used in an expression.

    + +
    + +

    Consider changing the name of either variable to keep them distinct.

    + +
    + + + + + + +
  • + B. Stroustrup. The C++ Programming Language Special Edition p 82. Addison Wesley. 2000. +
  • + + +
    +
    diff --git a/cpp/ql/src/Best Practices/Hiding/LocalVariableHidesGlobalVariable.ql b/cpp/ql/src/Best Practices/Hiding/LocalVariableHidesGlobalVariable.ql new file mode 100644 index 000000000000..5a08878d358f --- /dev/null +++ b/cpp/ql/src/Best Practices/Hiding/LocalVariableHidesGlobalVariable.ql @@ -0,0 +1,36 @@ +/** + * @name Local variable hides global variable + * @description A local variable or parameter that hides a global variable of the same name. This may be confusing. Consider renaming one of the variables. + * @kind problem + * @problem.severity warning + * @precision very-high + * @id cpp/local-variable-hides-global-variable + * @tags maintainability + * readability + */ +import cpp + +class LocalVariableOrParameter extends VariableDeclarationEntry { + LocalVariableOrParameter() { + this.getVariable() instanceof LocalScopeVariable and + ( + // we only need to report parameters hiding globals when the clash is with the parameter + // name as used in the function definition. The parameter name used in any other function + // declaration is harmless. + this instanceof ParameterDeclarationEntry + implies + exists(this.(ParameterDeclarationEntry).getFunctionDeclarationEntry().getBlock()) + ) + } + + string type() { + if this.getVariable() instanceof Parameter + then result = "Parameter " + else result = "Local variable " + } +} + +from LocalVariableOrParameter lv, GlobalVariable gv +where lv.getName() = gv.getName() and + lv.getFile() = gv.getFile() +select lv, lv.type() + gv.getName() + " hides $@ with the same name.", gv, "a global variable" diff --git a/cpp/ql/src/Best Practices/Hiding/Shadowing.qll b/cpp/ql/src/Best Practices/Hiding/Shadowing.qll new file mode 100644 index 000000000000..9d051085fe9d --- /dev/null +++ b/cpp/ql/src/Best Practices/Hiding/Shadowing.qll @@ -0,0 +1,33 @@ +import cpp + +predicate ancestorScope(Element b1, Element b2) { + b1.getParentScope+() = b2 +} + +pragma[noopt] +predicate localVariablesSameNameInNestedScopes(LocalVariable lv1, LocalVariable lv2) { + exists(Element b1, Element b2 + | b1 = lv1.getParentScope() and + not b1 instanceof Namespace and + lv1 instanceof LocalVariable and + ancestorScope(b1, b2) and + not b2 instanceof Namespace and + b2 = lv2.getParentScope() and + lv2 instanceof LocalVariable and + lv1.getName() = lv2.getName()) +} + +predicate shadowing(LocalVariable lv1, LocalVariable lv2) { + localVariablesSameNameInNestedScopes(lv1, lv2) and + exists(Location l1, Location l2 | + l1 = lv1.getLocation() and + l2 = lv2.getLocation() and + ( + // variables declared later in parent scope are not shadowed + l2.getEndLine() < l1.getStartLine() + or (l2.getEndLine() = l1.getStartLine() and + l2.getEndColumn() <= l1.getStartColumn()) + ) + ) +} + diff --git a/cpp/ql/src/Best Practices/Likely Errors/EmptyBlock.cpp b/cpp/ql/src/Best Practices/Likely Errors/EmptyBlock.cpp new file mode 100644 index 000000000000..547810752ec8 --- /dev/null +++ b/cpp/ql/src/Best Practices/Likely Errors/EmptyBlock.cpp @@ -0,0 +1,9 @@ +void f(int i) { + if (i == 10); //empty then block + ... //won't be part of the if statement + + if (i == 12) { + ... + } else { //empty else block, most likely a mistake + } +} diff --git a/cpp/ql/src/Best Practices/Likely Errors/EmptyBlock.qhelp b/cpp/ql/src/Best Practices/Likely Errors/EmptyBlock.qhelp new file mode 100644 index 000000000000..54767f91a1cd --- /dev/null +++ b/cpp/ql/src/Best Practices/Likely Errors/EmptyBlock.qhelp @@ -0,0 +1,26 @@ + + + + + +

    This rule finds empty blocks that occur as a branch of a conditional or as a loop body. +This may indicate badly maintained code or a defect due to an unhandled case. It is common to find commented-out code in the empty body. Commented-out code is discouraged and is a source of defects and maintainability issues.

    + +
    + +

    If the conditional or loop is useless, remove it.

    +

    If only the else-branch of an if statement is empty, omit it. If the then-branch is empty, invert the sense of the condition.

    + +
    + + + + + + + + + +
    diff --git a/cpp/ql/src/Best Practices/Likely Errors/EmptyBlock.ql b/cpp/ql/src/Best Practices/Likely Errors/EmptyBlock.ql new file mode 100644 index 000000000000..14a3d1fb3218 --- /dev/null +++ b/cpp/ql/src/Best Practices/Likely Errors/EmptyBlock.ql @@ -0,0 +1,84 @@ +/** + * @name Empty branch of conditional + * @description An empty block after a conditional can be a sign of an omission + * and can decrease maintainability of the code. Such blocks + * should contain an explanatory comment to aid future + * maintainers. + * @kind problem + * @problem.severity recommendation + * @precision very-high + * @id cpp/empty-block + * @tags reliability + * readability + */ +import cpp + +predicate emptyBlock(ControlStructure s, Block b) { + b = s.getAChild() and + not exists(b.getAChild()) and + not b.isInMacroExpansion() and + not s instanceof Loop +} + +class AffectedFile extends File { + AffectedFile() { + exists(Block b | + emptyBlock(_, b) and + this = b.getFile() + ) + } +} + +class BlockOrNonChild extends Element { + BlockOrNonChild() { + ( this instanceof Block + or + this instanceof Comment + or + this instanceof PreprocessorDirective + or + this instanceof MacroInvocation + ) and + this.getFile() instanceof AffectedFile + } + + private int getNonContiguousStartRankIn(AffectedFile file) { + // When using `rank` with `order by`, the ranks may not be contiguous. + this = rank[result](BlockOrNonChild boc, int startLine, int startCol | + boc.getLocation() + .hasLocationInfo(file.getAbsolutePath(), startLine, startCol, _, _) + | boc + order by startLine, startCol + ) + } + + int getStartRankIn(AffectedFile file) { + this.getNonContiguousStartRankIn(file) = rank[result](int rnk | + exists(BlockOrNonChild boc | boc.getNonContiguousStartRankIn(file) = rnk) + ) + } + + int getNonContiguousEndRankIn(AffectedFile file) { + this = rank[result](BlockOrNonChild boc, int endLine, int endCol | + boc.getLocation() + .hasLocationInfo(file.getAbsolutePath(), _, _, endLine, endCol) + | boc + order by endLine, endCol + ) + } +} + +predicate emptyBlockContainsNonchild(Block b) { + emptyBlock(_, b) and + exists(BlockOrNonChild c, AffectedFile file | + c.(BlockOrNonChild).getStartRankIn(file) = + 1 + b.(BlockOrNonChild).getStartRankIn(file) and + c.(BlockOrNonChild).getNonContiguousEndRankIn(file) < + b.(BlockOrNonChild).getNonContiguousEndRankIn(file) + ) +} + +from ControlStructure s, Block eb +where emptyBlock(s, eb) + and not emptyBlockContainsNonchild(eb) +select eb, "Empty block without comment" diff --git a/cpp/ql/src/Best Practices/Likely Errors/OffsetUseBeforeRangeCheck.c b/cpp/ql/src/Best Practices/Likely Errors/OffsetUseBeforeRangeCheck.c new file mode 100644 index 000000000000..1b9a83d62759 --- /dev/null +++ b/cpp/ql/src/Best Practices/Likely Errors/OffsetUseBeforeRangeCheck.c @@ -0,0 +1,24 @@ +int find(int start, char *str, char goal) +{ + int len = strlen(str); + //Potential buffer overflow + for (int i = start; str[i] != 0 && i < len; i++) { + if (str[i] == goal) + return i; + } + return -1; +} + +int findRangeCheck(int start, char *str, char goal) +{ + int len = strlen(str); + //Range check protects against buffer overflow + for (int i = start; i < len && str[i] != 0 ; i++) { + if (str[i] == goal) + return i; + } + return -1; +} + + + diff --git a/cpp/ql/src/Best Practices/Likely Errors/OffsetUseBeforeRangeCheck.qhelp b/cpp/ql/src/Best Practices/Likely Errors/OffsetUseBeforeRangeCheck.qhelp new file mode 100644 index 000000000000..6ffb30be5840 --- /dev/null +++ b/cpp/ql/src/Best Practices/Likely Errors/OffsetUseBeforeRangeCheck.qhelp @@ -0,0 +1,33 @@ + + + +

    The program contains an and-expression where the array access is defined before the range check. Consequently the array is accessed without any bounds checking. The range check does not protect the program from segmentation faults caused by attempts to read beyond the end of a buffer.

    + +
    + +

    Update the and-expression so that the range check precedes the array offset. This will ensure that the bounds are checked before the array is accessed.

    + +
    + +

    The find function can read past the end of the buffer pointed to by str if start is longer than or equal to the length of the buffer (or longer than len, depending on the contents of the buffer).

    + + +

    Update the and-expression so that the range check precedes the array offset (for example, the findRangeCheck function).

    + +
    + + +
  • cplusplus.com: + C++: array.
  • +
  • Wikipedia: + Bounds checking.
  • + + + + + + +
    +
    diff --git a/cpp/ql/src/Best Practices/Likely Errors/OffsetUseBeforeRangeCheck.ql b/cpp/ql/src/Best Practices/Likely Errors/OffsetUseBeforeRangeCheck.ql new file mode 100644 index 000000000000..872f01d4421b --- /dev/null +++ b/cpp/ql/src/Best Practices/Likely Errors/OffsetUseBeforeRangeCheck.ql @@ -0,0 +1,22 @@ +/** + * @name Array offset used before range check + * @description Accessing an array offset before checking the range means that + * the program may attempt to read beyond the end of a buffer + * @kind problem + * @id cpp/offset-use-before-range-check + * @problem.severity warning + * @tags reliability + * security + * external/cwe/cwe-120 + * external/cwe/cwe-125 + */ + +import cpp + +from Variable v, LogicalAndExpr andexpr, ArrayExpr access, LTExpr rangecheck +where access.getArrayOffset() = v.getAnAccess() + and andexpr.getLeftOperand().getAChild() = access + and andexpr.getRightOperand() = rangecheck + and rangecheck.getLeftOperand() = v.getAnAccess() + and not access.isInMacroExpansion() +select access, "This use of offset '" + v.getName() + "' should follow the $@.", rangecheck, "range check" diff --git a/cpp/ql/src/Best Practices/Likely Errors/Slicing.cpp b/cpp/ql/src/Best Practices/Likely Errors/Slicing.cpp new file mode 100644 index 000000000000..09d1f414e5de --- /dev/null +++ b/cpp/ql/src/Best Practices/Likely Errors/Slicing.cpp @@ -0,0 +1,43 @@ +static int idctr = 0; +//Basic connection with id +class Connection { +public: + int connId; + virtual void print_info() { + cout << "id: " << connId << "\n"; + } + Connection() { + connId = idctr++; + } +}; + +//Adds counters, and an overriding print_info +class MeteredConnection : public Connection { +public: + int txCtr; + int rxCtr; + MeteredConnection() { + txCtr = 0; + rxCtr = 0; + } + virtual void print_info() { + cout << "id: " << connId << "\n" << "tx/rx: " << txCtr << "/" << rxCtr << "\n"; + } +}; + +int main(int argc, char* argv[]) { + Connection conn; + MeteredConnection m_conn; + + Connection curr_conn = conn; + curr_conn.print_info(); + curr_conn = m_conn; //Wrong: Derived MetricConnection assigned to Connection + //variable, will slice off the counters and the overriding print_info + curr_conn.print_info(); //Will not print the counters. + + Connection* curr_pconn = &conn; + curr_pconn->print_info(); + curr_pconn = &m_conn; //Correct: Pointer assigned to address of the MetricConnection. + //Counters and virtual functions remain intact. + curr_pconn->print_info(); //Will call the correct method MeteredConnection::print_info +} diff --git a/cpp/ql/src/Best Practices/Likely Errors/Slicing.qhelp b/cpp/ql/src/Best Practices/Likely Errors/Slicing.qhelp new file mode 100644 index 000000000000..576d77c8839c --- /dev/null +++ b/cpp/ql/src/Best Practices/Likely Errors/Slicing.qhelp @@ -0,0 +1,31 @@ + + + + + +

    This query finds assignments of a non-reference instance of a derived type to a variable of the base type where the derived type has more fields than the base. +These assignments slice off all the fields added by the derived type, and can cause unexpected state when accessed as the derived type.

    + +
    + +

    Change the type of the variable at the left-hand side of the assignment to the subclass.

    + +
    + + + + + + +
  • + Wikipedia: Object slicing. +
  • +
  • + DevX.com: Slicing in C++. +
  • + + +
    +
    diff --git a/cpp/ql/src/Best Practices/Likely Errors/Slicing.ql b/cpp/ql/src/Best Practices/Likely Errors/Slicing.ql new file mode 100644 index 000000000000..3ba420c56f1b --- /dev/null +++ b/cpp/ql/src/Best Practices/Likely Errors/Slicing.ql @@ -0,0 +1,24 @@ +/** + * @name Slicing + * @description Assigning a non-reference instance of a derived type to a variable of the base type slices off all members added by the derived class, and can cause an unexpected state. + * @kind problem + * @problem.severity warning + * @precision high + * @id cpp/slicing + * @tags reliability + * correctness + * types + */ +import cpp + +//see http://www.cs.ualberta.ca/~hoover/Courses/201/201-New-Notes/lectures/section/slice.htm +//Does not find anything in rivers (unfortunately) +from AssignExpr e, Class lhsType, Class rhsType +where e.getLValue().getType() = lhsType + and e.getRValue().getType() = rhsType + and rhsType.getABaseClass+() = lhsType + and exists(Declaration m | rhsType.getAMember() = m and + not m.(VirtualFunction).isPure()) //add additional checks for concrete members in in-between supertypes +select e, "This assignment expression slices from type $@ to $@", + rhsType, rhsType.getName(), + lhsType, lhsType.getName() diff --git a/cpp/ql/src/Best Practices/Magic Constants/MagicConstants.qll b/cpp/ql/src/Best Practices/Magic Constants/MagicConstants.qll new file mode 100644 index 000000000000..02d2068a900d --- /dev/null +++ b/cpp/ql/src/Best Practices/Magic Constants/MagicConstants.qll @@ -0,0 +1,327 @@ +import cpp +import semmle.code.cpp.AutogeneratedFile + +/* + * + * Counting nontrivial literal occurrences + * + */ + +predicate trivialPositiveIntValue(string s) { + s="0" or s="1" or s="2" or s="3" or s="4" or s="5" or s="6" or s="7" or s="8" or + s="9" or s="10" or s="11" or s="12" or s="13" or s="14" or s="15" or s="16" or s="17" or + s="18" or s="19" or s="20" + + or + + s="16" or s="24" or s="32" or s="64" or s="128" or s="256" or s="512" or s="1024" or + s="2048" or s="4096" or s="16384" or s="32768" or s="65536" or + s="1048576" or s="2147483648" or s="4294967296" + + or + + s="15" or s="31" or s="63" or s="127" or s="255" or s="511" or s="1023" or + s="2047" or s="4095" or s="16383" or s="32767" or s="65535" or + s="1048577" or s="2147483647" or s="4294967295" + + or + + s = "0x00000001" or s = "0x00000002" or s = "0x00000004" or s = "0x00000008" or s = "0x00000010" or s = "0x00000020" or s = "0x00000040" or s = "0x00000080" or s = "0x00000100" or s = "0x00000200" or s = "0x00000400" or s = "0x00000800" or s = "0x00001000" or s = "0x00002000" or s = "0x00004000" or s = "0x00008000" or s = "0x00010000" or s = "0x00020000" or s = "0x00040000" or s = "0x00080000" or s = "0x00100000" or s = "0x00200000" or s = "0x00400000" or s = "0x00800000" or s = "0x01000000" or s = "0x02000000" or s = "0x04000000" or s = "0x08000000" or s = "0x10000000" or s = "0x20000000" or s = "0x40000000" or s = "0x80000000" or + s = "0x00000001" or s = "0x00000003" or s = "0x00000007" or s = "0x0000000f" or s = "0x0000001f" or s = "0x0000003f" or s = "0x0000007f" or s = "0x000000ff" or s = "0x000001ff" or s = "0x000003ff" or s = "0x000007ff" or s = "0x00000fff" or s = "0x00001fff" or s = "0x00003fff" or s = "0x00007fff" or s = "0x0000ffff" or s = "0x0001ffff" or s = "0x0003ffff" or s = "0x0007ffff" or s = "0x000fffff" or s = "0x001fffff" or s = "0x003fffff" or s = "0x007fffff" or s = "0x00ffffff" or s = "0x01ffffff" or s = "0x03ffffff" or s = "0x07ffffff" or s = "0x0fffffff" or s = "0x1fffffff" or s = "0x3fffffff" or s = "0x7fffffff" or s = "0xffffffff" or + s = "0x0001" or s = "0x0002" or s = "0x0004" or s = "0x0008" or s = "0x0010" or s = "0x0020" or s = "0x0040" or s = "0x0080" or s = "0x0100" or s = "0x0200" or s = "0x0400" or s = "0x0800" or s = "0x1000" or s = "0x2000" or s = "0x4000" or s = "0x8000" or + s = "0x0001" or s = "0x0003" or s = "0x0007" or s = "0x000f" or s = "0x001f" or s = "0x003f" or s = "0x007f" or s = "0x00ff" or s = "0x01ff" or s = "0x03ff" or s = "0x07ff" or s = "0x0fff" or s = "0x1fff" or s = "0x3fff" or s = "0x7fff" or s = "0xffff" or + s = "0x01" or s = "0x02" or s = "0x04" or s = "0x08" or s = "0x10" or s = "0x20" or s = "0x40" or s = "0x80" or + s = "0x01" or s = "0x03" or s = "0x07" or s = "0x0f" or s = "0x1f" or s = "0x3f" or s = "0x7f" or s = "0xff" or + s = "0x00" + + or + + s = "10" or s = "100" or s = "1000" or s = "10000" or s = "100000" or s = "1000000" or s = "10000000" or s = "100000000" or s = "1000000000" +} + +predicate trivialIntValue(string s) { + trivialPositiveIntValue(s) or + exists(string pos | trivialPositiveIntValue(pos) and s = "-" + pos) +} + +predicate trivialLongValue(string s) { + exists(string v | trivialIntValue(v) and s = v + "L") +} + +predicate intTrivial(Literal lit) { + exists(string v | trivialIntValue(v) and v = lit.getValue()) +} + +predicate longTrivial(Literal lit) { + exists(string v | trivialLongValue(v) and v = lit.getValue()) +} + +predicate powerOfTen(float f) { + f = 10 or f = 100 or f = 1000 or f = 10000 or f = 100000 or f = 1000000 or f = 10000000 or f = 100000000 or f = 1000000000 +} + +predicate floatTrivial(Literal lit) { + lit.getType() instanceof FloatingPointType and + exists(string value, float f | + lit.getValue() = value and + f = value.toFloat() and + (f.abs() <= 20.0 or powerOfTen(f)) + ) +} + +predicate charLiteral(Literal lit) { + lit instanceof CharLiteral +} + + +Type literalType(Literal literal) { + result = literal.getType() +} + +predicate stringType(DerivedType t) { + t.getBaseType() instanceof CharType + or + exists(SpecifiedType constCharType | + t.getBaseType() = constCharType and + constCharType.isConst() and + constCharType.getBaseType() instanceof CharType + ) +} + +predicate numberType(Type t) { + t instanceof FloatingPointType or t instanceof IntegralType +} + +predicate stringLiteral(Literal literal) { + literal instanceof StringLiteral +} + +predicate stringTrivial(Literal lit) { + stringLiteral(lit) and + lit.getValue().length() < 8 +} + +predicate joiningStringTrivial(Literal lit) { + // We want to be more lenient with string literals that are being + // joined together, because replacing sentence fragments with named + // constants could actually result in code that is harder to + // understand (which is against the spirit of these queries). + stringLiteral(lit) and + exists(FunctionCall fc | + ( + fc.getTarget().getName() = "operator+" or + fc.getTarget().getName() = "operator<<" + ) and + fc.getAnArgument().getAChild*() = lit + ) and + lit.getValue().length() < 16 +} + +predicate small(Literal lit) { + lit.getValue().length() <= 1 +} + +predicate trivial(Literal lit) { + charLiteral(lit) or + intTrivial(lit) or + floatTrivial(lit) or + stringTrivial(lit) or + joiningStringTrivial(lit) or + longTrivial(lit) or + small(lit) +} + +private predicate isReferenceTo(Variable ref, Variable to) { + exists(VariableAccess a | + ref.getInitializer().getExpr().getConversion().(ReferenceToExpr).getExpr() = a and a.getTarget() = to) +} + +private predicate variableNotModifiedAfterInitializer(Variable v) { + not exists(VariableAccess a | a.getTarget() = v and a.isModified()) and + not exists(AddressOfExpr e | e.getAddressable() = v) and + forall(Variable v2 | + isReferenceTo(v2, v) | + variableNotModifiedAfterInitializer(v2)) +} + +predicate literalIsConstantInitializer(Literal literal, Variable f) { + f.getInitializer().getExpr() = literal and + variableNotModifiedAfterInitializer(f) and + not f instanceof Parameter +} + +predicate literalIsEnumInitializer(Literal literal) { + exists(EnumConstant ec | ec.getInitializer().getExpr() = literal) +} + +predicate literalInArrayInitializer(Literal literal) { + exists(AggregateLiteral arrayInit | + arrayInitializerChild(arrayInit, literal) + ) +} + +predicate arrayInitializerChild(AggregateLiteral parent, Expr e) { + e = parent + or + exists (Expr mid | arrayInitializerChild(parent, mid) and e.getParent() = mid) +} + +// i.e. not a constant folded expression +predicate literallyLiteral(Literal lit) { + lit.getValueText().regexpMatch(".*\".*|\\s*+[-+]?+\\s*+(0[xob][0-9a-fA-F]|[0-9])[0-9a-fA-F,._]*+([eE][-+]?+[0-9,._]*+)?+\\s*+[a-zA-Z]*+\\s*+") +} + +predicate nonTrivialValue(string value, Literal literal) { + value = literal.getValue() and + not trivial(literal) and + not literalIsConstantInitializer(literal, _) and + not literalIsEnumInitializer(literal) and + not literalInArrayInitializer(literal) and + not literal.isAffectedByMacro() and + literallyLiteral(literal) +} + +predicate valueOccurrenceCount(string value, int n) { + n = strictcount(Location loc | + exists(Literal lit | + lit.getLocation() = loc | + nonTrivialValue(value, lit) + ) and + + // Exclude generated files (they do not have the same maintainability + // concerns as ordinary source files) + not loc.getFile() instanceof AutogeneratedFile + ) and + n > 20 +} + +predicate occurenceCount(Literal lit, string value, int n) { + valueOccurrenceCount(value, n) and + value = lit.getValue() and + nonTrivialValue(_, lit) +} + + +/* + * + * Literals repeated frequently + * + */ + +predicate check(Literal lit, string value, int n, File f) { + // Check that the literal is nontrivial + not trivial(lit) and + // Check that it is repeated a number of times + occurenceCount(lit, value, n) and n > 20 and + f = lit.getFile() and + + // Exclude generated files + not f instanceof AutogeneratedFile +} + +predicate checkWithFileCount(string value, int overallCount, int fileCount, File f) { + fileCount = strictcount(Location loc | + exists(Literal lit | + lit.getLocation() = loc | + check(lit, value, overallCount, f))) +} + +predicate start(Literal lit, int startLine) { + exists(Location l | l = lit.getLocation() and startLine = l.getStartLine()) +} + +predicate firstOccurrence(Literal lit, string value, int n) { + exists(File f, int fileCount | + checkWithFileCount(value, n, fileCount, f) and + fileCount < 100 and + check(lit, value, n, f) and + not exists(Literal lit2, int start1, int start2 | + check(lit2, value, n, f) and + start(lit, start1) and + start(lit2, start2) and + start2 < start1) + ) +} + +predicate magicConstant(Literal e, string msg) { + exists(string value, int n | firstOccurrence(e, value, n) + and msg = "Magic constant: literal '" + value + "' is repeated " + n.toString() + " times and should be encapsulated in a constant.") +} + +/* + * + * Literals where there is a defined constant with the same value + * + */ + +predicate relevantVariable(Variable f, string value) { + exists(Literal lit | not trivial(lit) and value = lit.getValue() and literalIsConstantInitializer(lit, f)) +} + +predicate relevantCallable(Function f, string value) { + exists(Literal lit | not trivial(lit) and value = lit.getValue() and lit.getEnclosingFunction() = f) +} + + +predicate isVisible(Variable field, Function fromCallable) { + exists(string value | + //public fields + ( + relevantVariable(field, value) and + field.(MemberVariable).isPublic() and + relevantCallable(fromCallable, value) + ) + or + //in same class + ( + relevantVariable(field, value) and + exists(Type t | + t = field.getDeclaringType() and + t = fromCallable.getDeclaringType()) + and relevantCallable(fromCallable, value) + ) + or + //in subclass and not private + ( + relevantVariable(field, value) and + not field.(MemberVariable).isPrivate() and + exists(Class sup, Class sub | + sup = field.getDeclaringType() and + sub.getABaseClass+() = sup and + sub = fromCallable.getDeclaringType() + ) and + relevantCallable(fromCallable, value) + ) + ) +} + +predicate canUseFieldInsteadOfLiteral(Variable constField, Literal magicLiteral) { + exists(Literal initLiteral | + literalIsConstantInitializer(initLiteral, constField) and + not trivial(initLiteral) and + not constField.getType().hasName("boolean") and + exists(string value | + value = initLiteral.getValue() and + magicLiteral.getValue() = value + ) + and constField.getType() = magicLiteral.getType() + and not literalIsConstantInitializer(magicLiteral, _) + and exists(Function c | + c = magicLiteral.getEnclosingFunction() and + ( + ( + constField.isTopLevel() and (not constField.isStatic() or constField.getFile() = c.getFile()) + ) + or + isVisible(constField,c) + ) + ) + ) +} + +predicate literalInsteadOfConstant(Literal magicLiteral, string message, Variable constField, string linkText) { + canUseFieldInsteadOfLiteral(constField, magicLiteral) and + message = + "Literal value '" + magicLiteral.getValue() + "' used instead of constant $@." and + linkText = constField.getName() +} diff --git a/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsNumbers.cpp b/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsNumbers.cpp new file mode 100644 index 000000000000..f637c8d68e51 --- /dev/null +++ b/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsNumbers.cpp @@ -0,0 +1,16 @@ +void sanitize(Fields[] record) { + //The number of fields here can be put in a const + for (fieldCtr = 0; field < 7; field++) { + sanitize(fields[fieldCtr]); + } +} + +#define NUM_FIELDS 7 + +void process(Fields[] record) { + //This avoids using a magic constant by using the macro instead + for (fieldCtr = 0; field < NUM_FIELDS; field++) { + process(fields[fieldCtr]); + } +} + diff --git a/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsNumbers.qhelp b/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsNumbers.qhelp new file mode 100644 index 000000000000..16fc75fc7ad8 --- /dev/null +++ b/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsNumbers.qhelp @@ -0,0 +1,50 @@ + + + + + +

    A magic number is a numeric literal (for example, 8080, +2048) that is used in the middle of a block of code without +explanation. It is considered good practice to avoid magic numbers by assigning +the numbers to named constants and using the named constants instead. The +reasons for this are twofold:

    + +
      +
    1. A number in isolation can be inexplicable to later programmers, whereas a +named constant (such as MAX_GUESTS) is more readily understood. +
    2. +
    3. Using the same named constant in many places, makes the code much easier to +update if the requirements change (for example, one more guest is permitted). +
    4. +
    + +

    This rule finds magic numbers for which there is no pre-existing named +constant (for example, the line marked (4) below).

    + +
    + +

    Consider creating a const or a macro to encapsulate the literal, +then replace all the relevant occurrences in the code.

    + +
    + + + + + +
  • +Magic number (Wikipedia) +
  • +
  • + Mats Henricson and Erik Nyquist, Industrial Strength C++, published by Prentice Hall PTR (1997). + Chapter 5: Object Life Cycle, Rec 5.4 (PDF). +
  • +
  • + DCL06-C. Use meaningful symbolic constants to represent literal values +
  • + + +
    +
    diff --git a/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsNumbers.ql b/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsNumbers.ql new file mode 100644 index 000000000000..89531ad23a8a --- /dev/null +++ b/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsNumbers.ql @@ -0,0 +1,22 @@ +/** + * @name Magic numbers + * @description 'Magic constants' should be avoided: if a nontrivial constant is used repeatedly, it should be encapsulated into a const variable or macro definition. + * @kind problem + * @id cpp/magic-number + * @problem.severity recommendation + * @precision medium + * @tags maintainability + * statistical + * non-attributable + */ +import cpp +import MagicConstants + +pragma[noopt] +predicate selection(Element e, string msg) { + magicConstant(e, msg) and exists(Literal l, Type t | l=e and t = l.getType() and numberType(t) and l instanceof Literal) +} + +from Literal e, string msg +where selection(e, msg) +select e, msg diff --git a/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsString.qhelp b/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsString.qhelp new file mode 100644 index 000000000000..ca01aac69f02 --- /dev/null +++ b/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsString.qhelp @@ -0,0 +1,49 @@ + + + + + +

    A magic string is a string literal (for example, "SELECT", +"127.0.0.1") that is used in the middle of a block of code without +explanation. It is considered good practice to avoid magic strings by assigning +the strings to named constants and using the named constants instead. The +reasons for this are twofold:

    + +
      +
    1. A string in isolation can be inexplicable to later programmers, whereas a +named constant (such as SMTP_HELO) is more readily understood. +
    2. +
    3. Using the same named constant in many places, makes the code much easier to +update if the requirements change (for example, a protocol is updated). +
    4. +
    + +

    This rule finds magic strings for which there is no pre-existing named +constant.

    + + +
    + + +

    Consider replacing the magic string with a new named constant.

    + +
    + + + +
  • +Magic string (Wikipedia) +
  • +
  • + Mats Henricson and Erik Nyquist, Industrial Strength C++, published by Prentice Hall PTR (1997). + Chapter 5: Object Life Cycle, Rec 5.4 (PDF). +
  • +
  • + DCL06-C. Use meaningful symbolic constants to represent literal values +
  • + + +
    +
    diff --git a/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsString.ql b/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsString.ql new file mode 100644 index 000000000000..6f0dab2c4c77 --- /dev/null +++ b/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsString.ql @@ -0,0 +1,22 @@ +/** + * @name Magic strings + * @description 'Magic constants' should be avoided: if a nontrivial constant is used repeatedly, it should be encapsulated into a const variable or macro definition. + * @kind problem + * @id cpp/magic-string + * @problem.severity recommendation + * @precision medium + * @tags maintainability + * statistical + * non-attributable + */ +import cpp +import MagicConstants + +pragma[noopt] +predicate selection(Element e, string msg) { + magicConstant(e, msg) and stringLiteral(e) +} + +from Literal e, string msg +where selection(e, msg) +select e, msg diff --git a/cpp/ql/src/Best Practices/Magic Constants/MagicNumbersUseConstant.ql b/cpp/ql/src/Best Practices/Magic Constants/MagicNumbersUseConstant.ql new file mode 100644 index 000000000000..1a9852990ffe --- /dev/null +++ b/cpp/ql/src/Best Practices/Magic Constants/MagicNumbersUseConstant.ql @@ -0,0 +1,14 @@ +/** + * @name Magic numbers: use defined constant + * @description A numeric literal that matches the initializer of a constant variable was found. Consider using the constant variable instead of the numeric literal. + * @kind problem + * @id cpp/use-number-constant + * @problem.severity recommendation + */ +import cpp +import MagicConstants + +from Literal magicLiteral, string message, Variable constant, string linkText +where numberType(magicLiteral.getType()) + and literalInsteadOfConstant(magicLiteral, message, constant, linkText) +select magicLiteral, message, constant, linkText diff --git a/cpp/ql/src/Best Practices/Magic Constants/MagicStringsUseConstant.ql b/cpp/ql/src/Best Practices/Magic Constants/MagicStringsUseConstant.ql new file mode 100644 index 000000000000..990de6048e5a --- /dev/null +++ b/cpp/ql/src/Best Practices/Magic Constants/MagicStringsUseConstant.ql @@ -0,0 +1,14 @@ +/** + * @name Magic strings: use defined constant + * @description A string literal that matches the initializer of a constant variable was found. Consider using the constant variable instead of the string literal. + * @kind problem + * @id cpp/use-string-constant + * @problem.severity recommendation + */ +import cpp +import MagicConstants + +from Literal magicLiteral, string message, Variable constant, string linkText +where stringLiteral(magicLiteral) + and literalInsteadOfConstant(magicLiteral, message, constant, linkText) +select magicLiteral, message, constant, linkText diff --git a/cpp/ql/src/Best Practices/NVI.ql b/cpp/ql/src/Best Practices/NVI.ql new file mode 100644 index 000000000000..5e5a20fd15a6 --- /dev/null +++ b/cpp/ql/src/Best Practices/NVI.ql @@ -0,0 +1,18 @@ +/** + * @name Public virtual method + * @description When public methods can be overridden, base classes are unable + * to enforce invariants that should hold for the whole hierarchy. + * @kind problem + * @id cpp/nvi + * @problem.severity warning + */ +import cpp + +//see http://www.gotw.ca/publications/mill18.htm + +from MemberFunction f +where f.hasSpecifier("public") and + f.hasSpecifier("virtual") and + f.getFile().fromSource() and + not (f instanceof Destructor) +select f, "Avoid having public virtual methods (NVI idiom)" diff --git a/cpp/ql/src/Best Practices/NVIHub.ql b/cpp/ql/src/Best Practices/NVIHub.ql new file mode 100644 index 000000000000..6d4600b68f9d --- /dev/null +++ b/cpp/ql/src/Best Practices/NVIHub.ql @@ -0,0 +1,22 @@ +/** + * @name Public virtual method in Hub Class + * @description When public methods can be overridden, base classes are unable + * to enforce invariants that should hold for the whole hierarchy. + * This is especially problematic in classes with many + * dependencies or dependents. + * @kind table + * @id cpp/nvi-hub + */ +import cpp + +//see http://www.gotw.ca/publications/mill18.htm + +from MemberFunction f, int hubIndex, Class fclass +where f.hasSpecifier("public") and + f.hasSpecifier("virtual") and + f.getFile().fromSource() and + not (f instanceof Destructor) and + fclass = f.getDeclaringType() and + hubIndex = fclass.getMetrics().getAfferentCoupling() * fclass.getMetrics().getEfferentCoupling() and + hubIndex > 100 +select f.getFile(), f, "Avoid having public virtual methods (NVI idiom)" diff --git a/cpp/ql/src/Best Practices/RuleOfThree.qhelp b/cpp/ql/src/Best Practices/RuleOfThree.qhelp new file mode 100644 index 000000000000..b18af74d9e59 --- /dev/null +++ b/cpp/ql/src/Best Practices/RuleOfThree.qhelp @@ -0,0 +1,24 @@ + + + + + +

    This query finds classes that define a destructor, a copy constructor, or a copy assignment operator, but not all three of them. The compiler generates default implementations for these functions, and since they deal with similar concerns it is likely that if the default implementation of one of them is not satisfactory, then neither are those of the others.

    + +

    The query flags any such class with a warning, and also display the list of generated warnings in the result view.

    + +
    +
    +

    Explicitly define the missing functions.

    + + + + + +
    + +
  • Wikipedia article + +
  • diff --git a/cpp/ql/src/Best Practices/RuleOfThree.ql b/cpp/ql/src/Best Practices/RuleOfThree.ql new file mode 100644 index 000000000000..5c41969c1947 --- /dev/null +++ b/cpp/ql/src/Best Practices/RuleOfThree.ql @@ -0,0 +1,26 @@ +/** + * @name Rule of three + * @description Classes that have an explicit destructor, copy constructor, or + * copy assignment operator may behave inconsistently if they do + * not have all three. + * @kind problem + * @id cpp/rule-of-three + * @problem.severity warning + * @tags reliability + */ +import cpp + +class BigThree extends MemberFunction { + BigThree() { + this instanceof Destructor + or this instanceof CopyConstructor + or this instanceof CopyAssignmentOperator + } +} + +from Class c, BigThree b +where b.getDeclaringType() = c and + not (c.hasDestructor() and + c.getAMemberFunction() instanceof CopyConstructor and + c.getAMemberFunction() instanceof CopyAssignmentOperator) +select c, "Class defines a destructor, copy constructor, or copy assignment operator, but not all three." diff --git a/cpp/ql/src/Best Practices/RuleOfTwo.cpp b/cpp/ql/src/Best Practices/RuleOfTwo.cpp new file mode 100644 index 000000000000..4e602c10543d --- /dev/null +++ b/cpp/ql/src/Best Practices/RuleOfTwo.cpp @@ -0,0 +1,26 @@ +class C { +private: + Other* other = NULL; +public: + C(const C& copyFrom) { + Other* newOther = new Other(); + *newOther = copyFrom.other; + this->other = newOther; + } + + //No operator=, by default will just copy the pointer other, will not create a new object +}; + +class D { + Other* other = NULL; +public: + D& operator=(D& rhs) { + Other* newOther = new Other(); + *newOther = rhs.other; + this->other = newOther; + return *this; + } + + //No copy constructor, will just copy the pointer other and not create a new object +}; + diff --git a/cpp/ql/src/Best Practices/RuleOfTwo.qhelp b/cpp/ql/src/Best Practices/RuleOfTwo.qhelp new file mode 100644 index 000000000000..b8d4e13d283e --- /dev/null +++ b/cpp/ql/src/Best Practices/RuleOfTwo.qhelp @@ -0,0 +1,53 @@ + + + + + +

    This rule finds classes that define a copy constructor or a copy assignment operator, but not both of them. The compiler generates default implementations for these functions, and since they deal with similar concerns it is likely that if the default implementation of one of them is not satisfactory, then neither is that of the other.

    + +

    When a class defines a copy constructor or a copy assignment operator, but not both, this can cause +unexpected behavior. The object initialization (that is, Class c1 = c2) may behave differently +from object assignment (that is, c1 = c2).

    +
    + + +

    First, consider whether the user-defined member needs to be explicitly +defined at all. If no user-defined copy constructor is provided for a class, +the compiler will always attempt to generate a public copy constructor that +recursively invokes the copy constructor of each field. If the existing +user-defined copy constructor does exactly the same, it is most likely +beneficial to delete it. The compiler-generated version may be more efficient, +and it does not need to be manually maintained as fields are added and deleted. +

    + +

    If the user-defined member does need to exist, the other +corresponding member should be defined too. It can be defined as defaulted +(using = default) if the compiler-generated implementation is +acceptable, or it can be defined as deleted (using = delete) if it +should never be called. +

    + +
    + + + + + + + +
  • + Rule of Three [Wikipedia] +
  • +
  • + The Law of The Big Two +
  • +
  • + cppreference.com: copy constructor + and copy assignment +
  • + + +
    +
    diff --git a/cpp/ql/src/Best Practices/RuleOfTwo.ql b/cpp/ql/src/Best Practices/RuleOfTwo.ql new file mode 100644 index 000000000000..88adf10ec504 --- /dev/null +++ b/cpp/ql/src/Best Practices/RuleOfTwo.ql @@ -0,0 +1,65 @@ +/** + * @name Inconsistent definition of copy constructor and assignment ('Rule of Two') + * @description Classes that have an explicit copy constructor or copy + * assignment operator may behave inconsistently if they do + * not have both. + * @kind problem + * @problem.severity warning + * @precision high + * @id cpp/rule-of-two + * @tags reliability + * readability + * language-features + */ +import cpp + +// This query enforces the Rule of Two, which is a conservative variation of +// the more well-known Rule of Three. +// +// The Rule of Two is usually phrased informally, ignoring the distinction +// between whether a member is missing because it's auto-generated (missing +// from the source) or missing because it can't be called (missing from the +// generated code). +// +// This query checks if one member is explicitly defined while the other is +// auto-generated. This can lead to memory safety issues. It's a separate issue +// whether one is callable while the other is not callable; that is an API +// design question and carries has no safety risk. + +predicate generatedCopyAssignment(CopyConstructor cc, string msg) { + cc.getDeclaringType().hasImplicitCopyAssignmentOperator() and + msg = "No matching copy assignment operator in class " + + cc.getDeclaringType().getName() + + ". It is good practice to match a copy constructor with a " + + "copy assignment operator." +} + +predicate generatedCopyConstructor(CopyAssignmentOperator ca, string msg) { + ca.getDeclaringType().hasImplicitCopyConstructor() and + msg = "No matching copy constructor in class " + + ca.getDeclaringType().getName() + + ". It is good practice to match a copy assignment operator with a " + + "copy constructor." +} + +from MemberFunction f, string msg +where (generatedCopyAssignment(f, msg) or + generatedCopyConstructor(f, msg)) + // Ignore template instantiations to prevent an explosion of alerts + and not f.getDeclaringType().isConstructedFrom(_) + // Ignore private members since a private constructor or assignment operator + // is a common idiom that simulates suppressing the default-generated + // members. It would be better to use C++11's "delete" facility or use + // appropriate Boost helper classes, but it is too common to report as a + // violation. + and not f.isPrivate() + // If it is truly user-defined then it must have a body. This leaves out + // C++11 members that use `= delete` or `= default`. + and exists(f.getBlock()) + // In rare cases, the extractor pretends that an auto-generated copy + // constructor has a block that is one character long and is located on top + // of the first character of the class name. Checking for + // `isCompilerGenerated` will remove those results. + and not f.isCompilerGenerated() + and not f.isDeleted() +select f, msg diff --git a/cpp/ql/src/Best Practices/SloppyGlobal.qhelp b/cpp/ql/src/Best Practices/SloppyGlobal.qhelp new file mode 100644 index 000000000000..a6255cc6a9e2 --- /dev/null +++ b/cpp/ql/src/Best Practices/SloppyGlobal.qhelp @@ -0,0 +1,32 @@ + + + + + +

    +This rule finds global variables which have a name of length three characters or less. It is particularly important to use descriptive names for global variables. Use of a clear naming convention for global variables helps document their use, avoids pollution of the namespace and reduces the risk of shadowing with local variables. +

    + + +
    + +

    +Review the purpose of the each global variable flagged by this rule and update each name to reflect the purpose of the variable. +

    + +
    + + +
  • + Mats Henricson and Erik Nyquist, Industrial Strength C++, published by Prentice Hall PTR (1997). + Chapter 1: Naming, Rec 1.1 (PDF). +
  • +
  • + Global variables. +
  • + + +
    +
    diff --git a/cpp/ql/src/Best Practices/SloppyGlobal.ql b/cpp/ql/src/Best Practices/SloppyGlobal.ql new file mode 100644 index 000000000000..d45ca8c4f183 --- /dev/null +++ b/cpp/ql/src/Best Practices/SloppyGlobal.ql @@ -0,0 +1,15 @@ +/** + * @name Short global name + * @description Global variables should have descriptive names, to help document their use, avoid namespace pollution and reduce the risk of shadowing with local variables. + * @kind problem + * @problem.severity warning + * @precision high + * @id cpp/short-global-name + * @tags maintainability + */ +import cpp + +from GlobalVariable gv +where gv.getName().length() <= 3 + and not gv.isStatic() +select gv, "Poor global variable name '" + gv.getName() + "'. Prefer longer, descriptive names for globals (eg. kMyGlobalConstant, not foo)." diff --git a/cpp/ql/src/Best Practices/SwitchLongCase.cpp b/cpp/ql/src/Best Practices/SwitchLongCase.cpp new file mode 100644 index 000000000000..422c3fdbf562 --- /dev/null +++ b/cpp/ql/src/Best Practices/SwitchLongCase.cpp @@ -0,0 +1,32 @@ +//This switch statement has long case statements, and can become difficult to +//read as the processing for each message type becomes more complex +switch (message_type) { + case CONNECT: + _state = CONNECTING; + int message_id = message_get_id(message); + int source = connect_get_source(message); + //More code here... + send(connect_response); + break; + case DISCONNECT: + _state = DISCONNECTING; + int message_id = message_get_id(message); + int source = disconnect_get_source(message); + //More code here... + send(disconnect_response); + break; + default: + log("Invalid message, id : %d", message_get_id(message)); +} + +//This is better, as each case is split out to a separate function +switch (packet_type) { + case STREAM: + process_stream_packet(packet); + break; + case DATAGRAM: + process_datagram_packet(packet); + break; + default: + log("Invalid packet type: %d", packet_type); +} \ No newline at end of file diff --git a/cpp/ql/src/Best Practices/SwitchLongCase.qhelp b/cpp/ql/src/Best Practices/SwitchLongCase.qhelp new file mode 100644 index 000000000000..0a84f57da3fe --- /dev/null +++ b/cpp/ql/src/Best Practices/SwitchLongCase.qhelp @@ -0,0 +1,32 @@ + + + + + +

    +This rule finds switch statements that have too much code in their cases. +Long case statements often lead to large amounts of nesting, adding to the difficulty of understanding what the code actually does. +Consider wrapping the code for each case in a function and just using the switch statement to invoke the appropriate function in each case. +

    + +

    The indicated switch statement has a case that is more than 30 lines long.

    + +
    + +

    Consider creating a separate function for the code in the long case statement.

    + +
    + + + + + +
  • + Switch statements +
  • + + +
    +
    diff --git a/cpp/ql/src/Best Practices/SwitchLongCase.ql b/cpp/ql/src/Best Practices/SwitchLongCase.ql new file mode 100644 index 000000000000..9a40feaf69e3 --- /dev/null +++ b/cpp/ql/src/Best Practices/SwitchLongCase.ql @@ -0,0 +1,45 @@ +/** + * @name Long switch case + * @description A switch statement with too much code in its cases can make the control flow hard to follow. Consider wrapping the code for each case in a function and just using the switch statement to invoke the appropriate function in each case. + * @kind problem + * @problem.severity recommendation + * @precision high + * @id cpp/long-switch + * @tags maintainability + * readability + */ +import cpp + +predicate switchCaseStartLine(SwitchCase sc, int start) { + sc.getLocation().getStartLine() = start +} +predicate switchStmtEndLine(SwitchStmt s, int start) { + s.getLocation().getEndLine() = start +} + +predicate switchCaseLength(SwitchCase sc, int length) { + exists(SwitchCase next, int l1, int l2 | + next = sc.getNextSwitchCase() and + switchCaseStartLine(next, l1) and + switchCaseStartLine(sc, l2) and + length = l1 - l2 - 1 + ) + or + ( + not exists(sc.getNextSwitchCase()) and + exists(int l1, int l2 | + switchStmtEndLine(sc.getSwitchStmt(), l1) and + switchCaseStartLine(sc, l2) and + length = l1 - l2 - 1 + ) + ) +} + +predicate tooLong(SwitchCase sc) { + exists(int n | switchCaseLength(sc, n) and n > 30) +} + +from SwitchStmt switch, SwitchCase sc, int lines +where sc = switch.getASwitchCase() and tooLong(sc) + and switchCaseLength(sc, lines) +select switch, "Switch has at least one case that is too long: $@", sc, sc.getExpr().toString() + " (" + lines.toString() + " lines)" diff --git a/cpp/ql/src/Best Practices/Unused Entities/UnusedIncludes.ql b/cpp/ql/src/Best Practices/Unused Entities/UnusedIncludes.ql new file mode 100644 index 000000000000..c8480a037b63 --- /dev/null +++ b/cpp/ql/src/Best Practices/Unused Entities/UnusedIncludes.ql @@ -0,0 +1,24 @@ +/** + * @name Unused include + * @description Finds #include directives that are not needed because none of + * the included elements are used. + * @kind problem + * @id cpp/unused-includes + * @problem.severity warning + */ + +import cpp + +File sourceFile() { + result instanceof CFile or + result instanceof CppFile +} + +from Include include, File source, File unneeded +where include.getFile() = source + and source = sourceFile() + and unneeded = include.getIncludedFile() + and not unneeded.getAnIncludedFile*() = source.getMetrics().getAFileDependency() + and unneeded.fromSource() + and not unneeded.getBaseName().matches("%Debug.h") +select include, "Redundant include, this file does not require $@.", unneeded, unneeded.getAbsolutePath() diff --git a/cpp/ql/src/Best Practices/Unused Entities/UnusedLocals.cpp b/cpp/ql/src/Best Practices/Unused Entities/UnusedLocals.cpp new file mode 100644 index 000000000000..0f20fd9d5291 --- /dev/null +++ b/cpp/ql/src/Best Practices/Unused Entities/UnusedLocals.cpp @@ -0,0 +1,5 @@ +{ + int x = 0; //x is unused + int y = 0; + cout << y; +} diff --git a/cpp/ql/src/Best Practices/Unused Entities/UnusedLocals.qhelp b/cpp/ql/src/Best Practices/Unused Entities/UnusedLocals.qhelp new file mode 100644 index 000000000000..f98f65d68490 --- /dev/null +++ b/cpp/ql/src/Best Practices/Unused Entities/UnusedLocals.qhelp @@ -0,0 +1,34 @@ + + + + + +

    This rule finds local variables that are never accessed after declaration. +Unused variables should be removed to increase readability and avoid misuse.

    + +
    + +

    Removing these unused local variables will make code more readable.

    + +
    + + + + + + +
  • + Variable scope +
  • +
  • + MSC12-C. Detect and remove code that has no effect +
  • + + + + + +
    +
    diff --git a/cpp/ql/src/Best Practices/Unused Entities/UnusedLocals.ql b/cpp/ql/src/Best Practices/Unused Entities/UnusedLocals.ql new file mode 100644 index 000000000000..dbe35b56a0cf --- /dev/null +++ b/cpp/ql/src/Best Practices/Unused Entities/UnusedLocals.ql @@ -0,0 +1,57 @@ +/** + * @name Unused local variable + * @description A local variable that is never called or accessed may be an + * indication that the code is incomplete or has a typo. + * @kind problem + * @problem.severity recommendation + * @precision high + * @id cpp/unused-local-variable + * @tags maintainability + * useless-code + * external/cwe/cwe-563 + */ +import cpp + +/** + * A type that contains a template parameter type + * (doesn't count pointers or references). + * + * These types may have a constructor / destructor when they are + * instantiated, that is not visible in their template form. + * + * Such types include template parameters, classes with a member variable + * of template parameter type, and classes that derive from other such + * classes. + */ +class TemplateDependentType extends Type { + TemplateDependentType() { + this instanceof TemplateParameter or + exists(TemplateDependentType t | + this.refersToDirectly(t) and + not this instanceof PointerType and + not this instanceof ReferenceType + ) + } +} + +/** + * A variable whose declaration has, or may have, side effects. + */ +predicate declarationHasSideEffects(Variable v) { + exists (Class c | c = v.getType().getUnderlyingType().getUnspecifiedType() | + c.hasConstructor() or + c.hasDestructor() + ) or + v.getType() instanceof TemplateDependentType // may have a constructor/destructor +} + +from LocalVariable v, Function f +where f = v.getFunction() + and not exists(v.getAnAccess()) + and not v.isConst() // workaround for folded constants + and not exists(DeclStmt ds | ds.getADeclaration() = v and ds.isInMacroExpansion()) // variable declared in a macro expansion + and not declarationHasSideEffects(v) + and not exists(AsmStmt s | f = s.getEnclosingFunction()) + and not v.getAnAttribute().getName() = "unused" + and not any(ErrorExpr e).getEnclosingFunction() = f // unextracted expr likely used `v` +select v, "Variable " + v.getName() + " is not used" diff --git a/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticFunctions.cpp b/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticFunctions.cpp new file mode 100644 index 000000000000..9e2685ea7c47 --- /dev/null +++ b/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticFunctions.cpp @@ -0,0 +1,14 @@ +//start of file +static void f() { //static function f() is unused in the file + //... +} +static void g() { + //... +} +void public_func() { //non-static function public_func is not called in file, + //but could be visible in other files + //... + g(); //call to g() + //... +} +//end of file diff --git a/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticFunctions.qhelp b/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticFunctions.qhelp new file mode 100644 index 000000000000..998450169ee1 --- /dev/null +++ b/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticFunctions.qhelp @@ -0,0 +1,32 @@ + + + + + +

    +This rule finds static functions with definitions that are never called or accessed. These unused functions should be +removed to increase code readability, reduce object size and avoid misuse. +

    + +
    + +

    Removing these unused static functions will make code more readable. A common pitfall is that code using a static function is guarded by conditional compilation but the static function is not. Notice that this detects directly unused functions and removing a static function may expose more unused functions.

    + +
    + + + + + + +
  • + MSC12-C. Detect and remove code that has no effect +
  • + + + + +
    +
    diff --git a/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticFunctions.ql b/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticFunctions.ql new file mode 100644 index 000000000000..1844f523b4ec --- /dev/null +++ b/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticFunctions.ql @@ -0,0 +1,92 @@ +/** + * @name Unused static function + * @description A static function that is never called or accessed may be an + * indication that the code is incomplete or has a typo. + * @description Static functions that are never called or accessed. + * @kind problem + * @problem.severity recommendation + * @precision high + * @id cpp/unused-static-function + * @tags efficiency + * useless-code + * external/cwe/cwe-561 + */ +import cpp + +predicate immediatelyReachableFunction(Function f) { + not f.isStatic() + or exists(BlockExpr be | be.getFunction() = f) + or f instanceof MemberFunction + or f instanceof TemplateFunction + or f.getFile() instanceof HeaderFile + or f.getAnAttribute().hasName("constructor") + or f.getAnAttribute().hasName("destructor") + or f.getAnAttribute().hasName("used") + or f.getAnAttribute().hasName("unused") +} + +predicate immediatelyReachableVariable(Variable v) { + (v.isTopLevel() and not v.isStatic()) + or exists(v.getDeclaringType()) + or v.getFile() instanceof HeaderFile + or v.getAnAttribute().hasName("used") + or v.getAnAttribute().hasName("unused") +} + +class ImmediatelyReachableThing extends Thing { + ImmediatelyReachableThing() { + immediatelyReachableFunction(this) or + immediatelyReachableVariable(this) + } +} + +predicate reachableThing(Thing t) { + t instanceof ImmediatelyReachableThing or + exists(Thing mid | reachableThing(mid) and mid.callsOrAccesses() = t) +} + +class Thing extends Locatable { + Thing() { + this instanceof Function or + this instanceof Variable + } + + string getName() { + result = this.(Function).getName() or + result = this.(Variable).getName() + } + + Thing callsOrAccesses() { + this.(Function).calls((Function)result) or + this.(Function).accesses((Function)result) or + this.(Function).accesses((Variable)result) or + (exists(Access a | this.(Variable).getInitializer().getExpr().getAChild*() = a + | result = a.getTarget())) + } +} + +class FunctionToRemove extends Function { + FunctionToRemove() { + this.hasDefinition() + and not reachableThing(this) + } + + Thing getOther() { + result.callsOrAccesses+() = this + and this != result + // We will already be reporting the enclosing function of a + // local variable, so don't also report the variable + and not result instanceof LocalVariable + } +} + +from FunctionToRemove f, string clarification, Thing other +where if exists(f.getOther()) + then (clarification = " ($@ must be removed at the same time)" + and other = f.getOther()) + else (clarification = "" and other = f) +select f, + "Static function " + f.getName() + " is unreachable" + clarification, + other, + other.getName() + diff --git a/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticVariables.cpp b/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticVariables.cpp new file mode 100644 index 000000000000..4627aab67b3a --- /dev/null +++ b/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticVariables.cpp @@ -0,0 +1,5 @@ +void f() { + static int i = 0; //i is unused + ... + return; +} diff --git a/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticVariables.qhelp b/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticVariables.qhelp new file mode 100644 index 000000000000..ebb3c84b6578 --- /dev/null +++ b/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticVariables.qhelp @@ -0,0 +1,38 @@ + + + + + +

    This rule finds static variables that are never accessed. These static variables should be removed, to increase code comprehensibility, +reduce memory usage and avoid misuse. Unused static const variables could also be an indication of a defect +caused by an unhandled case.

    + +
    + +

    Check that the unused static variable does not indicate a defect, for example, an unhandled case. If the static variable is genuinuely not needed, +then removing it will make code more readable. If the static variable is needed then you should update the code to fix the defect.

    + +
    + + + + + +
  • + Variable scope +
  • +
  • + Detect and remove code that has no effect +
  • +
  • + Minimize the scope of variables and methods +
  • + + + + + +
    +
    diff --git a/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticVariables.ql b/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticVariables.ql new file mode 100644 index 000000000000..1dbd0204263d --- /dev/null +++ b/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticVariables.ql @@ -0,0 +1,28 @@ +/** + * @name Unused static variable + * @description A static variable that is never accessed may be an indication + * that the code is incomplete or has a typo. + * @kind problem + * @problem.severity recommendation + * @precision high + * @id cpp/unused-static-variable + * @tags efficiency + * useless-code + * external/cwe/cwe-563 + */ +import cpp + +predicate declarationHasSideEffects(Variable v) { + exists (Class c | c = v.getType().getUnderlyingType().getUnspecifiedType() | + c.hasConstructor() or c.hasDestructor() + ) +} + +from Variable v +where v.isStatic() + and v.hasDefinition() + and not exists(VariableAccess a | a.getTarget() = v) + and not v instanceof MemberVariable + and not declarationHasSideEffects(v) + and not v.getAnAttribute().hasName("used") +select v, "Static variable " + v.getName() + " is never read" diff --git a/cpp/ql/src/Best Practices/UseOfGoto.qhelp b/cpp/ql/src/Best Practices/UseOfGoto.qhelp new file mode 100644 index 000000000000..8326fa2ba8b4 --- /dev/null +++ b/cpp/ql/src/Best Practices/UseOfGoto.qhelp @@ -0,0 +1,56 @@ + + + + +

    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, or jumping to error-handling code at +the end of a function. This rule identifies complex (and therefore hard to +understand) uses of goto statements. Function bodies that include +multiple goto statements that jump forward and multiple +goto statements that jump backwards are highlighted.

    + +
    + +

    +In most cases the code can be rewritten and/or rearranged by: +

    +
      +
    • using structured control flow constructs, such as if, while, + and for;
    • +
    • using break or continue to stop a loop iteration early; or
    • +
    • using return to exit a function early
    • +
    + +

    +The goto statement may be the best solution for breaking out of +deeply nested loops, or for jumping to error handling code, without adversely +affecting the readability of the function. Such uses will not be flagged by +this rule. +

    + +
    + + +
  • + Joint Strike Fighter Air Vehicle C++ Coding Standards, AV Rule 189. Lockheed Martin Corporation, 2005. +
  • +
  • + E. W. Dijkstra Archive: A Case against the GO TO Statement (EWD-215). +
  • +
  • + MSDN Library: The goto Statement. +
  • +
  • + Mats Henricson and Erik Nyquist, Industrial Strength C++, Rule 4.6. Prentice Hall PTR, 1997. + (PDF). +
  • +
  • + cplusplus.com: Control Structures. +
  • + + +
    +
    diff --git a/cpp/ql/src/Best Practices/UseOfGoto.ql b/cpp/ql/src/Best Practices/UseOfGoto.ql new file mode 100644 index 000000000000..9c8258a85931 --- /dev/null +++ b/cpp/ql/src/Best Practices/UseOfGoto.ql @@ -0,0 +1,40 @@ +/** + * @name Use of goto + * @description The goto statement can make the control flow of a function hard + * to understand, when used for purposes other than error handling. + * @kind problem + * @problem.severity warning + * @precision high + * @id cpp/use-of-goto + * @tags maintainability + * readability + * language-features + */ + +import cpp + +class JumpTarget extends Stmt { + JumpTarget() { + exists(GotoStmt g | g.getTarget() = this) + } + + FunctionDeclarationEntry getFDE() { + result.getBlock() = this.getParentStmt+() + } + + predicate isForward() { + exists(GotoStmt g | g.getTarget() = this | g.getLocation().getEndLine() < this.getLocation().getStartLine()) + } + + predicate isBackward() { + exists(GotoStmt g | g.getTarget() = this | this.getLocation().getEndLine() < g.getLocation().getStartLine()) + } +} + +from FunctionDeclarationEntry fde, int nforward, int nbackward +where nforward = strictcount(JumpTarget t | t.getFDE() = fde and t.isForward()) + and nbackward = strictcount(JumpTarget t | t.getFDE() = fde and t.isBackward()) + and nforward != 1 + and nbackward != 1 +select fde, "Multiple forward and backward goto statements may make function " + + fde.getName() + " hard to understand." diff --git a/cpp/ql/src/Best Practices/index.qhelp b/cpp/ql/src/Best Practices/index.qhelp new file mode 100644 index 000000000000..30e854502fe8 --- /dev/null +++ b/cpp/ql/src/Best Practices/index.qhelp @@ -0,0 +1,16 @@ + + + + + +

    about best practices

    + + + + + + +
    +
    diff --git a/cpp/ql/src/CPython/ArgParse.qll b/cpp/ql/src/CPython/ArgParse.qll new file mode 100644 index 000000000000..bb439aa5207d --- /dev/null +++ b/cpp/ql/src/CPython/ArgParse.qll @@ -0,0 +1,199 @@ + +import cpp +import CPython.Extensions + +/* A call to an argument parsing function */ +class PyArgParseTupleCall extends FunctionCall { + + PyArgParseTupleCall() { + this.getTarget().hasGlobalName("PyArg_Parse") or + this.getTarget().hasGlobalName("PyArg_ParseTuple") or + this.getTarget().hasGlobalName("PyArg_VaParse") or + this.getTarget().hasGlobalName("PyArg_ParseTupleAndKeywords") or + this.getTarget().hasGlobalName("PyArg_VaParseAndKeywords") + } + + private int getFormatIndex() { + exists(Function f | f = this.getTarget() | + (f.hasGlobalName("PyArg_Parse") or f.hasGlobalName("PyArg_ParseTuple") or f.hasGlobalName("PyArg_VaParse")) and result = 1 + or + (f.hasGlobalName("PyArg_ParseTupleAndKeywords") or f.hasGlobalName("PyArg_VaParseAndKeywords")) and result = 2 + ) + } + + private string getFormatString() { + result = this.getArgument(this.getFormatIndex()).(StringLiteral).getValue() + } + + string getArgumentFormat() { + exists(string fmt | fmt = this.getFormatString() | + exists(int i | fmt.charAt(i) = ";" or fmt.charAt(i) = ":" | result = fmt.prefix(i)) + or + not exists(int i | fmt.charAt(i) = ";" or fmt.charAt(i) = ":") and result = fmt + ) + } + + string getPyArgumentType(int index) { + parse_format_string(this.getArgumentFormat(), index, _, result) and result != "typed" + or + exists(int cindex, PythonClass cls | parse_format_string(this.getArgumentFormat(), index, cindex, "typed") | + cls.getAnAccess() = this.getArgument(this.getFormatIndex() * 2 + cindex).(AddressOfExpr).getOperand() and + result = cls.getTpName() + ) + or + exists(int cindex | parse_format_string(this.getArgumentFormat(), index, cindex, "typed") and + not exists(PythonClass cls | cls.getAnAccess() = this.getArgument(this.getFormatIndex() * 2 + cindex).(AddressOfExpr).getOperand()) + and result = "object" + ) + } + + predicate pyArgumentIsOptional(int index) { + exists(string suffix | split_format_string(this.getArgumentFormat(), _, _, suffix, index, _) | + suffix.charAt(0) = "|") + } + + predicate pyArgumentIsKwOnly(int index) { + exists(string suffix | split_format_string(this.getArgumentFormat(), _, _, suffix, index, _) | + suffix.charAt(0) = "$") + } + +} + +class PyUnpackTupleCall extends FunctionCall { + + PyUnpackTupleCall() { + this.getTarget().hasGlobalName("PyArg_UnpackTuple") + } + + int getMinSize() { + result = this.getArgument(2).getValue().toInt() + } + + int getMaxSize() { + result = this.getArgument(3).getValue().toInt() + } + +} + +predicate limiting_format(string text, string limit) { + text = "t#" and limit = "read-only" + or + (text = "B" or text = "H" or text = "I" or text = "k" or text = "K") and limit = "non-negative" + or + (text = "c" or text = "C") and limit = "length-one" +} + +predicate format_string(string text, string type, int cargs) { + tuple_format(text, type, cargs) or simple_format(text, type, cargs) +} + +private +predicate simple_format(string text, string type, int cargs) { + text = "s" and (type = "str" or type = "unicode") and cargs = 1 + or + text = "s#" and (type = "str" or type = "unicode") and cargs = 2 + or + text = "s*" and (type = "str" or type = "unicode") and cargs = 1 + or + text = "z" and (type = "str" or type = "unicode" or type = "NoneType") and cargs = 1 + or + text = "z#" and (type = "str" or type = "unicode" or type = "NoneType" or type = "buffer") and cargs = 2 + or + text = "z*" and (type = "str" or type = "unicode" or type = "NoneType" or type = "buffer") and cargs = 1 + or + text = "u" and type = "unicode" and cargs = 1 + or + text = "u#" and type = "unicode" and cargs = 2 + or + text = "O" and type = "object" and cargs = 1 + or + text = "p" and type = "object" and cargs = 1 + or + text = "O&" and type = "object" and cargs = 2 + or + text = "O!" and type = "typed" and cargs = 2 + or + (text = "b" or text = "h" or text = "i" or text = "l" or text = "L" or text = "n") and type = "int" and cargs = 1 + or + (text = "B" or text = "H" or text = "I" or text = "k" or text = "K") and type = "int" and cargs = 1 + or + text = "c" and (type = "bytes" or type = "bytearray") and cargs = 1 + or + text = "C" and type = "unicode" and cargs = 1 + or + text = "D" and type = "complex" and cargs = 1 + or + (text = "f" or text = "d") and type = "float" and cargs = 1 + or + text = "S" and type = "str" and cargs = 1 + or + text = "U" and type = "unicode" and cargs = 1 + or + text = "t#" and type = "buffer" and cargs = 2 + or + text = "w" and type = "buffer" and cargs = 1 + or + text = "w#" and type = "buffer" and cargs = 2 + or + text = "w*" and type = "buffer" and cargs = 1 + or + (text = "es" or text = "et") and (type = "str" or type = "unicode" or type = "buffer") and cargs = 2 + or + (text = "es#" or text = "et#") and (type = "str" or type = "unicode" or type = "buffer") and cargs = 3 + or + text = "y" and type = "bytes" and cargs = 1 + or + text = "y*" and (type = "bytes" or type = "bytearray" or type = "buffer") and cargs = 1 + or + text = "y#" and (type = "bytes" or type = "bytearray" or type = "buffer") and cargs = 2 +} + +private +predicate tuple_format(string text, string type, int cargs) { + type = "tuple" and + exists(PyArgParseTupleCall call | exists(call.getArgumentFormat().indexOf(text))) + and + exists(string body | text = "(" + body + ")" | tuple_body(body, _, cargs)) +} + +private +predicate tuple_body(string body, int pyargs, int cargs) { + body = "" and cargs = 0 and pyargs = 0 + or + (exists(PyArgParseTupleCall call | exists(call.getArgumentFormat().indexOf(body))) and + exists(string p, int pargs, string s, int sargs, int pyargsm1 | pyargs = pyargsm1+1 and tuple_body(p, pyargsm1, pargs) and + format_string(s, _, sargs) and body = p + s and cargs = pargs + sargs) + ) +} + +predicate format_token(string token, int delta, int cdelta) { + format_string(token, _, cdelta) and delta = 1 + or + token = "|" and delta = 0 and cdelta = 0 + or + token = "$" and delta = 0 and cdelta = 0 +} + +predicate split_format_string(string full, string prefix, string text, string suffix, int index, int cindex) { + exists(PyArgParseTupleCall call | call.getArgumentFormat() = full) and + full = prefix + text + suffix and + (suffix = "" or exists(string s | suffix.prefix(s.length()) = s | format_token(s, _, _))) and + format_token(text, _, _) and + (prefix = "" and index = 0 and cindex = 0 and suffix = full.suffix(text.length()) + or + exists(string prefixm1, string suffixm1, string textm1, int im1, int cim1, int prev, int cprev | + full = prefixm1 + textm1 + suffixm1 and + split_format_string(full, prefixm1, textm1, suffixm1, im1, cim1) and + format_token(textm1, prev, cprev) and + index = im1+prev and + cindex = cim1+cprev and + prefix = prefixm1 + textm1 and + suffix = suffixm1.suffix(text.length()) and + text = suffixm1.prefix(text.length()) + ) + ) +} + +predicate parse_format_string(string full, int index, int cindex, string type) { + exists(string prefix, string text, string suffix | split_format_string(full, prefix, text, suffix, index, cindex) and format_string(text, type, _)) +} diff --git a/cpp/ql/src/CPython/ArgumentTypeTrap.ql b/cpp/ql/src/CPython/ArgumentTypeTrap.ql new file mode 100644 index 000000000000..1c5e582fede0 --- /dev/null +++ b/cpp/ql/src/CPython/ArgumentTypeTrap.ql @@ -0,0 +1,14 @@ +/** + * @name Parameter type trap file generator + * @description Generate trap files (in CSV form) describing CPython extension function parameter types. + * @kind trap + * @id cpp/c-python/argument-type-trap + */ + +import cpp + +import CPython.Extensions + +from TypedPythonExtensionFunction func, int arg, PythonClass cls +where func.getArgumentType(arg) = cls +select "ext_argtype", func.getTrapID(), arg, cls.getTrapID() diff --git a/cpp/ql/src/CPython/CObjectSourcesTrap.ql b/cpp/ql/src/CPython/CObjectSourcesTrap.ql new file mode 100644 index 000000000000..c1e3cb250c0d --- /dev/null +++ b/cpp/ql/src/CPython/CObjectSourcesTrap.ql @@ -0,0 +1,13 @@ +/** + * @name py_cobject_sources() trap file generator + * @description Generate trap files (in CSV form) for CPython objects. + * @kind trap + * @id cpp/c-python/c-object-sources-trap + */ + +import cpp + +import CPython.Extensions + +from CObject c +select "py_cobject_sources", c.getTrapID(), 1 diff --git a/cpp/ql/src/CPython/CObjectTrap.ql b/cpp/ql/src/CPython/CObjectTrap.ql new file mode 100644 index 000000000000..92deadb8c8d3 --- /dev/null +++ b/cpp/ql/src/CPython/CObjectTrap.ql @@ -0,0 +1,14 @@ +/** + * @name py_cobject() trap file generator + * @description Generate trap files (in CSV form) for CPython objects. + * @kind trap + * @id cpp/c-python/c-object-trap + */ + +import cpp + +import CPython.Extensions + + +from CObject c +select "py_cobjects", c.getTrapID() diff --git a/cpp/ql/src/CPython/Extensions.qll b/cpp/ql/src/CPython/Extensions.qll new file mode 100644 index 000000000000..72fe4a4b03d0 --- /dev/null +++ b/cpp/ql/src/CPython/Extensions.qll @@ -0,0 +1,907 @@ +import cpp +import CPython.ArgParse + + +/* Root class of all 'C' objects */ +abstract class CObject extends @element { + + abstract string getTrapID(); + + /** Gets a textual representation of this element. */ + abstract string toString(); +} + + + +/** +A Python class is an instance of PyTypeObject. +*/ +class PythonClass extends Variable, CObject { + + PythonClass() { + getType().hasName("PyTypeObject") + } + + /** Gets the function table (if any) associated with the class. */ + PythonFunctionTable getFunctionTable() { + exists(ClassAggregateLiteral l, TypedefType tt, Field f | + l = getInitializer().getExpr() + and tt.hasName("PyTypeObject") + and f.hasName("tp_methods") + and f.getDeclaringType() = tt.getBaseType() + and result.getAnAccess() = l.getFieldExpr(f) + ) + } + + /** Gets the getset table (if any) associated with the class. */ + PythonGetSetTable getGetSetTable() { + exists(ClassAggregateLiteral l, TypedefType tt, Field f | + l = getInitializer().getExpr() + and tt.hasName("PyTypeObject") + and f.hasName("tp_getset") + and f.getDeclaringType() = tt.getBaseType() + and result.getAnAccess() = l.getFieldExpr(f) + ) + } + + /** Gets the Python module (if any) containing this class. */ + PythonModule getModule() { + result = getFile() + } + + Expr getSlot(string name) { + exists(ClassAggregateLiteral l, TypedefType tt, Field f | + l = getInitializer().getExpr() + and tt.hasName("PyTypeObject") + and f.hasName(name) + and f.getDeclaringType() = tt.getBaseType() + and l.getFieldExpr(f) = result + ) + } + + string getTpName() { + exists(StringLiteral lit | + lit = this.getSlot("tp_name") | + result = lit.getValue() + ) + } + + Expr getSizeOf() { + exists(ClassAggregateLiteral l, TypedefType tt, Field f | + l = getInitializer().getExpr() + and tt.hasName("PyTypeObject") + and f.hasName("tp_basicsize") + and f.getDeclaringType() = tt.getBaseType() + and l.getFieldExpr(f) = result + ) + } + + override string getTrapID() { + /* This needs to be kept in sync with extractor-python/semmle/passes/type.py */ + result = "C_type$" + this.getTpName() + } + + /** Gets a textual representation of this element. */ + override string toString() { + result = Variable.super.toString() + } +} + +/** +A call to a Py_InitModule function. These functions register a Python module. +*/ +class Py_InitModuleCall extends FunctionCall { + Py_InitModuleCall() { + // Py_InitModule itself is actually a macro, ultimately defined to be something like Py_InitModule4_64. + getTarget().getName().matches("Py\\_Init%") + } + + /** Gets the name of the module being registered. */ + string getModuleName() { + result = getArgument(0).(StringLiteral).getValue() + } + + /** Gets the function table associated with this Py_InitModule call. */ + PythonFunctionTable getFunctionTable() { + exists(VariableAccess va | + va = getArgument(1) + and result = va.getTarget() + ) + } +} + +/** +A Python module, represented by the file containing an initialising call for it. +*/ +class PythonModule extends File { + PythonModule() { + exists(PythonModuleDefinition def | def.getFile() = this) + or + exists(FunctionCall c | c.getFile() = this | + c.getTarget().getName().matches("Py\\_InitModule%") + ) + } + + /** Gets a Python class that is in this module. */ + PythonClass getAClass() { + result.getFile() = this + } + + /** Gets the function table associated with the module. */ + PythonFunctionTable getFunctionTable() { + result = this.getDefinition().getFunctionTable() + or + exists(FunctionCall c | c.getFile() = this | + c.getTarget().getName().matches("Py\\_InitModule%") and + c.getAnArgument() = result.getAnAccess() + ) + } + + /** Gets the Py_InitModule call that was used to register the module. */ + //private + PythonModuleDefinition getDefinition() { + result.getFile() = this + } + + /** Gets the name of the module. */ + string getModuleName() { + result = this.getDefinition().getModuleName() + or + exists(FunctionCall c |c.getFile() = this | + c.getTarget().getName().matches("Py\\_InitModule%") and + c.getArgument(0).getValue() = result + ) + } +} + +/** +The function table for a Python module. +*/ +class PythonFunctionTable extends Variable { + + PythonFunctionTable() { + not(this instanceof Parameter) + and exists(ArrayType at | at = getType().getUnspecifiedType() and at.getBaseType().hasName("PyMethodDef")) + } + + /** Gets an entry in the table. */ + PythonFunctionTableEntry getATableEntry() { + result = getInitializer().getExpr().getAChild() + and exists(result.getRegisteredFunctionName()) + } + + /** Gets the class (if any) for which this is the function table. */ + PythonClass getClass() { + result.getFunctionTable() = this + or + exists(FunctionAccess getattr, Call find_method | + result.getSlot("tp_getattr") = getattr | + find_method.getEnclosingFunction() = getattr.getTarget() and + find_method.getArgument(0) = this.getAnAccess() + ) + } + + /** Gets the module (if any) for which this is the function table. */ + PythonModule getModule() { + result.getFunctionTable() = this + } +} + +/** +The getset table for a Python module or type +*/ +class PythonGetSetTable extends Variable { + + PythonGetSetTable() { + not(this instanceof Parameter) and + exists(ArrayType at | at = getType() and at.getBaseType().hasName("PyGetSetDef")) + } + + /** Gets the class (if any) for which this is the function table. */ + PythonClass getClass() { + result.getGetSetTable() = this + } + + /** Gets an entry in the table. */ + PythonGetSetTableEntry getATableEntry() { + result = getInitializer().getExpr().getAChild() + and exists(result.getRegisteredPropertyName()) + } + +} + +class PythonModuleDefinition extends Variable { + + PythonModuleDefinition() { + not(this instanceof Parameter) + and exists(Type moddef_t | moddef_t = this.getType() and moddef_t.hasName("PyModuleDef")) + } + + /** Gets the function table (if any) associated with the class. */ + PythonFunctionTable getFunctionTable() { + exists(ClassAggregateLiteral l, Type moddef_t, Field f | + l = this.getInitializer().getExpr() + and moddef_t.hasName("PyModuleDef") + and f.hasName("m_methods") + and f.getDeclaringType() = moddef_t + and result.getAnAccess() = l.getFieldExpr(f) + ) + } + + /** Gets the function table (if any) associated with the class. */ + string getModuleName() { + exists(ClassAggregateLiteral l, Type moddef_t, Field f | + l = this.getInitializer().getExpr() + and moddef_t.hasName("PyModuleDef") + and f.hasName("m_name") + and f.getDeclaringType() = moddef_t + and result = l.getFieldExpr(f).getValue() + ) + } + +} + +/** A special (__xxx__) method implemented in C +*/ +class PythonSpecialMethod extends Function { + + PythonSpecialMethod() { + class_special_methods(_, _, this) + } + + PythonClass getClass() { + class_special_methods(result, _, this) + } + + string getPyName() { + class_special_methods(_, result, this) + } + +} + +predicate class_special_methods(PythonClass cls, string name, Function method) { + + exists(string slot, FunctionAccess fa | + special_methods(name, slot) and cls.getSlot(slot) = fa and fa.getTarget() = method + or + number_methods(name, slot) and + exists(ClassAggregateLiteral l, TypedefType tt, Field f | + l = cls.getSlot("tp_as_number") + and tt.hasName("PyNumberMethods") + and f.hasName(slot) + and f.getDeclaringType() = tt.getBaseType() + and l.getFieldExpr(f) = fa + and fa.getTarget() = method + ) + or + sequence_methods(name, slot) and + exists(ClassAggregateLiteral l, TypedefType tt, Field f | + l = cls.getSlot("tp_as_sequence") + and tt.hasName("PySequenceMethods") + and f.hasName(slot) + and f.getDeclaringType() = tt.getBaseType() + and l.getFieldExpr(f) = fa + and fa.getTarget() = method + ) + or + mapping_methods(name, slot) and + exists(ClassAggregateLiteral l, TypedefType tt, Field f | + l = cls.getSlot("tp_as_mapping") + and tt.hasName("PyMappingMethods") + and f.hasName(slot) + and f.getDeclaringType() = tt.getBaseType() + and l.getFieldExpr(f) = fa + and fa.getTarget() = method + ) + ) +} + + +predicate special_methods(string name, string slot_name) { + name = "__getattr__" and slot_name = "tp_getattr" + or + name = "__hash__" and slot_name = "tp_hash" + or + name = "__call__" and slot_name = "tp_call" + or + name = "__str__" and slot_name = "tp_str" + or + name = "__getattribute__" and slot_name = "tp_getattro" + or + name = "__setattro__" and slot_name = "tp_setattro" + or + name = "__iter__" and slot_name = "tp_iter" + or + name = "__descr_get__" and slot_name = "tp_descr_get" + or + name = "__descr_set__" and slot_name = "tp_descr_set" +} + +predicate number_methods(string name, string slot_name) { + name = "__add__" and slot_name = "nb_add" + or + name = "__sub__" and slot_name = "nb_subtract" + or + name = "__mul__" and slot_name = "nb_multiply" + or + name = "__mod__" and slot_name = "nb_remainder" + or + name = "__pow__" and slot_name = "nb_power" + or + name = "__neg__" and slot_name = "nb_negative" + or + name = "__pos__" and slot_name = "nb_positive" + or + name = "__abs__" and slot_name = "nb_absolute" + or + name = "__bool__" and slot_name = "nb_bool" + or + name = "__bool__" and slot_name = "nb_bool" + or + name = "__invert__" and slot_name = "nb_invert" + or + name = "__lshift__" and slot_name = "nb_lshift" + or + name = "__rshift__" and slot_name = "nb_rshift" + or + name = "__and__" and slot_name = "nb_and" + or + name = "__xor__" and slot_name = "nb_xor" + or + name = "__or__" and slot_name = "nb_or" + or + name = "__int__" and slot_name = "nb_int" + or + name = "__long__" and slot_name = "nb_long" + or + name = "__float__" and slot_name = "nb_float" + or + name = "__iadd__" and slot_name = "nb_inplace_add" + or + name = "__isub__" and slot_name = "nb_inplace_subtract" + or + name = "__imul__" and slot_name = "nb_inplace_multiply" + or + name = "__imod__" and slot_name = "nb_inplace_remainder" + or + name = "__ilshift__" and slot_name = "nb_inplace_lshift" + or + name = "__irshift__" and slot_name = "nb_inplace_rshift" + or + name = "__iand__" and slot_name = "nb_inplace_and" + or + name = "__ixor__" and slot_name = "nb_inplace_xor" + or + name = "__ior__" and slot_name = "nb_inplace_or" + or + name = "__index__" and slot_name = "nb_index" +} + +predicate sequence_methods(string name, string slot_name) { + name = "__len__" and slot_name = "sq_length" + or + name = "__add__" and slot_name = "sq_concat" + or + name = "__mul__" and slot_name = "sq_repeat" + or + name = "__getitem__" and slot_name = "sq_item" + or + name = "__setitem__" and slot_name = "sq_ass_item" + or + name = "__contains__" and slot_name = "sq_contains" + or + name = "__iadd__" and slot_name = "sq_inplace_concat" + or + name = "__imul__" and slot_name = "sq_inplace_repeat" +} + +predicate mapping_methods(string name, string slot_name) { + name = "__len__" and slot_name = "mp_length" + or + name = "__getitem__" and slot_name = "mp_subscript" + or + name = "__setitem__" and slot_name = "mp_ass_subscript" +} + + +/** +An entry in the getset table for a Python class. +This is the C code item that corresponds 1-to-1 with the Python-level property +*/ +class PythonGetSetTableEntry extends AggregateLiteral { + + PythonGetSetTableEntry() { + this.getUnderlyingType().hasName("PyGetSetDef") + and + this.getChild(0) instanceof StringLiteral + } + + Function getGetter() { + exists(FunctionAccess fa | fa = getChild(1) and result = fa.getTarget()) + } + + Function getSetter() { + exists(FunctionAccess fa | fa = getChild(2) and result = fa.getTarget()) + } + + StringLiteral getRegisteredPropertyName() { + result = this.getChild(0) + } + + PythonGetSetTable getTable() { + result.getATableEntry() = this + } +} + +/** +An entry in the function table for a Python class or module. +This is the C code item that corresponds 1-to-1 with the Python-level function. +*/ +class PythonFunctionTableEntry extends AggregateLiteral { + + PythonFunctionTableEntry() { + this.getUnderlyingType().hasName("PyMethodDef") + and + this.getChild(0) instanceof StringLiteral + } + + /** Gets the doc string to be associated with the function being registered. */ + string getDocString() { + result = getChild(3).(StringLiteral).getValueText() + } + + /** Gets the flags for the function being registered. */ + int getFlags() { + result = getChild(2).getValue().toInt() + } + + /** Gets the function being registered. */ + Function getFunction() { + exists(FunctionAccess fa | fa = getChild(1) and result = fa.getTarget()) + } + + /** Gets the module containing the function table. */ + PythonModule getModule() { + result = getTable().getModule() + } + + /** Gets the name with which the function should be referenced from Python. */ + StringLiteral getRegisteredFunctionName() { + result = this.getChild(0) + } + + /** Gets the function table containing this entry. */ + PythonFunctionTable getTable() { + result.getATableEntry() = this + } + + /** Gets a flag associated with this function. */ + string getAFlag() { + exists(int f | f = this.getFlags() | + (f % 2 = 1 and result = "METH_VARARGS") + or ((f / 2) % 2 = 1 and result = "METH_KEYWORDS") + or ((f / 4) % 2 = 1 and result = "METH_NOARGS") + or ((f / 8) % 2 = 1 and result = "METH_O") + or ((f / 16) % 2 = 1 and result = "METH_CLASS") + or ((f / 32) % 2 = 1 and result = "METH_STATIC") + or ((f / 64) % 2 = 1 and result = "METH_COEXIST") + ) + } + +} + +library class PythonBuildReturnCall extends FunctionCall { + PythonBuildReturnCall() { + exists(string name | name = getTarget().getName() | + name = "Py_BuildValue" + or name = "Py_VaBuildValue" + ) + } + + string getFormatString() { + result = getArgument(0).(StringLiteral).getValue() + } + +} + +/** +An extension function for Python (written in C). +*/ +class PythonExtensionFunction extends Function { + PythonExtensionFunction() { + exists(PythonFunctionTableEntry e | e.getFunction() = this) + and exists(getAParameter()) + } + + /** Gets a function table entry registering this function. */ + PythonFunctionTableEntry getATableEntry() { + result.getFunction() = this + } + +} + +class TypedPythonExtensionProperty extends PythonGetSetTableEntry, CObject { + + override + string toString() { + result = PythonGetSetTableEntry.super.toString() + } + + PythonClass getPropertyType() { + result = py_return_type(this.getGetter()) + } + + private string trapClass() { + result = this.getClass().getTrapID() + } + + override string getTrapID() { + result = this.trapClass() + "$" + this.getPyName() + } + + string getPyName() { + result = this.getRegisteredPropertyName().getValue() + } + + /** Gets the class containing this function. */ + PythonClass getClass() { + result = this.getTable().getClass() + } + +} + + +/* An extension function for Python (written in C); Python facing aspect */ +abstract class TypedPythonExtensionFunction extends PythonFunctionTableEntry, CObject { + + override Location getLocation() { + result = this.getRegisteredFunctionName().getLocation() + } + + override + string toString() { + result = "MethodDef " + this.getRegisteredFunctionName().getValue() + } + + abstract PythonClass getArgumentType(int index); + + abstract predicate argumentIsOptional(int index); + + abstract predicate argumentIsKwOnly(int index); + + PythonExtensionFunction getCode() { + result.getATableEntry() = this + } + + predicate isMethod() { + exists(this.getTable().getClass()) and not this.getAFlag() = "METH_STATIC" + } + + int c_index(int index) { + index in [0..20] and result in [-1..20] + and + (if this.isMethod() then + result = index - 1 + else + result = index + ) + } + + string getPyName() { + result = this.getRegisteredFunctionName().getValue() + } + + PythonClass getReturnType() { + result = py_return_type(this.getCode()) + } + + + /** Gets the module containing this function. */ + override PythonModule getModule() { + result = getTable().getModule() + } + + /** Gets the class containing this function. */ + PythonClass getClass() { + result = getTable().getClass() + } + + //private + string trapModule() { + result = this.getModule().getModuleName() + } + + //private + string trapClass() { + result = this.getClass().getTrapID() + } + + /* A globally unique name for use in trap files. + */ + override string getTrapID() { + result = "C_builtin_function_or_method$" + this.trapModule() + "." + this.getPyName() + or + result = this.trapClass() + "$" + this.getPyName() + } + +} + +predicate src_dest_pair(Element src, ControlFlowNode dest) { + exists(LocalScopeVariable v, ControlFlowNode def | + definitionUsePair(v, def, dest) and + exprDefinition(v, def, src) and + not exists(AddressOfExpr addr | addr.getOperand() = v.getAnAccess()) + ) + or + exists(Parameter p | dest = p.getAnAccess() and not definitionUsePair(_, _, dest) and src = p) +} + +cached +predicate local_flows_to(Element src, ControlFlowNode dest) { + not unreachable(src) and not unreachable(dest) and + (src = dest + or + src_dest_pair(src, dest) + or + exists(Element mid | local_flows_to(src, mid) and src_dest_pair(mid, dest)) + ) +} + +PythonClass py_return_type(Function f) { + exists(ReturnStmt ret | + ret.getEnclosingFunction() = f and + result = python_type(ret.getExpr()) and + not ret.getExpr().getValue() = "0" + ) + or + exists(Macro m | m.getAnInvocation().getEnclosingFunction() = f and m.getName() = "Py_RETURN_NONE" and result.getTpName() = "NoneType") + or + exists(Macro m | m.getAnInvocation().getEnclosingFunction() = f and m.getName() = "Py_RETURN_TRUE" and result.getTpName() = "bool") + or + exists(Macro m | m.getAnInvocation().getEnclosingFunction() = f and m.getName() = "Py_RETURN_FALSE" and result.getTpName() = "bool") +} + +PythonClass python_type_from_size(Expr e) { + exists(Type t, string name | + t = e.getUnderlyingType().(PointerType).getBaseType() and name = t.getName() and name.matches("Py\\_%Object") | + exists(PythonClass cls | cls.getSizeOf().getValueText() = "sizeof(" + t.getName() + ")" | + result = cls and not result.getTpName() = "int" and not result.getTpName() = "bool" + ) + ) +} + +predicate py_bool(Expr e) { + exists(MacroInvocation mi, string name | + mi.getExpr() = e and name = mi.getMacroName() | + name = "Py_False" or name = "Py_True" + ) +} + +PythonClass python_type_from_name(Expr e) { + exists(Type t, string name | + t = e.getUnderlyingType().(PointerType).getBaseType() and name = t.getName() | + name = "PyBytesObject" and result.getTpName() = "bytes" + or + name = "PyTupleObject" and result.getTpName() = "tuple" + or + name = "PyLongObject" and result.getTpName() = "int" and not py_bool(e) + or + name = "PyIntObject" and result.getTpName() = "int" and not py_bool(e) + or + name = "PyStringObject" and result.getTpName() = "str" and cpython_major_version() = 2 + or + name = "PyStringObject" and result.getTpName() = "bytes" and cpython_major_version() = 3 + or + name = "PyUnicodeObject" and result.getTpName() = "unicode" and cpython_major_version() = 2 + or + name = "PyUnicodeObject" and result.getTpName() = "str" and cpython_major_version() = 3 + ) +} + +PythonClass python_type(Expr e) { + result = python_type_from_size(e) + or + result = python_type_from_name(e) + or + call_to_new(e, result) + or + exists(Element src | result = python_type(src) and local_flows_to(src, e)) + or + result = type_from_build_value(e) + or + result = type_from_call(e) + or + py_bool(e) and result.getTpName() = "bool" + or + call_to_type(e, result) + or + exists(MacroInvocation mi | + mi.getExpr() = e and mi.getMacroName() = "Py_None" | + result.getTpName() = "NoneType" + ) +} + +predicate build_value_function(Function f) { + f.getName().regexpMatch("_?Py_(Va)?BuildValue(_SizeT)?") +} + +PythonClass type_from_build_value(Expr e) { + exists(FunctionCall c | + c = e | + build_value_function(c.getTarget()) and + result = type_from_build_value_code(c.getArgument(0).getValue()) + ) +} + +PythonClass type_from_call(Expr e) { + exists(Function f | + not build_value_function(f) and + /* Do not type to infer return type of the interpreter */ + not f.getName().matches("PyEval%") and + f = e.(FunctionCall).getTarget() and + result = py_return_type(f) + ) + or + exists(Function f | + f = e.(FunctionCall).getTarget() and + result.getTpName() = "dict" + | + f.hasName("PyEval_GetBuiltins") or + f.hasName("PyEval_GetGlobals") or + f.hasName("PyEval_GetLocals") + ) +} + +PythonClass type_from_build_value_code(string s) { + exists(FunctionCall c | c.getArgument(0).getValue() = s) + and + (result = type_from_simple_code(s) + or + result.getTpName() = "dict" and s.charAt(0) = "{" + or + result.getTpName() = "tuple" and not exists(type_from_simple_code(s)) and not s.charAt(0) = "{" + ) +} + +private PythonClass theBytesClass() { + cpython_major_version() = 2 and result.getTpName() = "str" + or + cpython_major_version() = 3 and result.getTpName() = "bytes" +} + +private PythonClass theUnicodeClass() { + cpython_major_version() = 2 and result.getTpName() = "unicode" + or + cpython_major_version() = 3 and result.getTpName() = "str" +} + +PythonClass type_from_simple_code(string s) { + (s = "s" or s = "s#" or s = "z" or s = "z#") and result.getTpName() = "str" + or + (s = "u" or s = "u#" or s = "U" or s = "U#" or s = "C") and result = theUnicodeClass() + or + (s = "y" or s = "y#" or s = "c") and result = theBytesClass() + or + (s = "i" or s = "I" or s = "b" or s = "B" or s = "h" or s = "H" or + s = "k" or s = "K" or s = "l" or s = "L" or s = "n" + ) and result.getTpName() = "int" + or + (s = "f" or s = "d") and result.getTpName() = "float" + or + s = "D" and result.getTpName() = "complex" + or + (s = "O" or s = "O&" or s = "N") and result.getTpName() = "object" +} + +predicate call_to_new(FunctionCall call, PythonClass cls) { + exists(string name | + name = call.getTarget().getName() | + name = "_PyObject_New" or + name = "_PyObject_GC_New" or + name = "_PyObject_NewVar" or + name = "_PyObject_GC_NewVar" + ) and + exists(AddressOfExpr addr | + addr = call.getArgument(0) | + addr.getAddressable() = cls + ) +} + +predicate call_to_type(FunctionCall call, PythonClass cls) { + exists(AddressOfExpr aoe | + call.getTarget().getName().matches("PyObject\\_Call%") and + call.getArgument(0) = aoe and + aoe.getAddressable() = cls + ) +} + +predicate pyargs_function(PythonFunctionTableEntry func, PyArgParseTupleCall call) { + func.getFunction().getParameter(1).getAnAccess() = call.getArgument(0) +} + + +class PyArgsFunction extends TypedPythonExtensionFunction { + + PyArgsFunction() { + this.getAFlag() = "METH_VARARGS" + } + + PyArgParseTupleCall getParseArgsCall() { + strictcount(PyArgParseTupleCall other | this.getCode().getParameter(1).getAnAccess() = other.getArgument(0)) = 1 and + pyargs_function(this, result) + } + + override PythonClass getArgumentType(int index) { + this.isMethod() and index = 0 and result = this.getTable().getClass() + or + result.getTpName() = this.getParseArgsCall().getPyArgumentType(this.c_index(index)) + } + + override predicate argumentIsOptional(int index) { + this.getParseArgsCall().pyArgumentIsOptional(this.c_index(index)) + } + + override predicate argumentIsKwOnly(int index) { + this.getParseArgsCall().pyArgumentIsKwOnly(this.c_index(index)) + } + +} + +class PyOFunction extends TypedPythonExtensionFunction { + + PyOFunction() { + this.getAFlag() = "METH_O" + } + + override PythonClass getArgumentType(int index) { + this.isMethod() and index = 0 and result = this.getTable().getClass() + or + /* TO DO -- Better analysis */ + this.c_index(index) = 0 and result.getTpName() = "object" + } + + override predicate argumentIsOptional(int index) { + none() + } + + override predicate argumentIsKwOnly(int index) { + none() + } +} + +class PyNoArgFunction extends TypedPythonExtensionFunction { + + PyNoArgFunction() { + this.getAFlag() = "METH_NOARGS" + } + + override PythonClass getArgumentType(int index) { + this.isMethod() and index = 0 and result = this.getTable().getClass() + } + + override predicate argumentIsOptional(int index) { + none() + } + + override predicate argumentIsKwOnly(int index) { + none() + } +} + +int cpython_major_version() { + exists(MacroInvocation mi | + mi.getMacroName() = "PY_MAJOR_VERSION" | + result = mi.getExpr().getValue().toInt() + ) +} + +int cpython_minor_version() { + exists(MacroInvocation mi | + mi.getMacroName() = "PY_MINOR_VERSION" | + result = mi.getExpr().getValue().toInt() + ) +} + +string cpython_version() { + result = cpython_major_version().toString() + "." + cpython_minor_version().toString() +} diff --git a/cpp/ql/src/CPython/ParameterReturnTrap.ql b/cpp/ql/src/CPython/ParameterReturnTrap.ql new file mode 100644 index 000000000000..1c3fd7c5c302 --- /dev/null +++ b/cpp/ql/src/CPython/ParameterReturnTrap.ql @@ -0,0 +1,20 @@ +/** + * @name Parameter return trap file generator + * @description Generate trap files (in CSV form) describing CPython extension functions return one of their parameters. + * @kind trap + * @id cpp/c-python/parameter-return-trap + */ + +import cpp +import CPython.Extensions + +predicate argument_flows_to_return(Function func, int arg) { + exists(Parameter p | p = func.getParameter(arg) | + forall(ReturnStmt ret | ret.getEnclosingFunction() = func | + local_flows_to(p, ret.getExpr())) + ) +} + +from TypedPythonExtensionFunction func, PythonExtensionFunction code, int arg +where func.getCode() = code and argument_flows_to_return(code, arg) +select "ext_argreturn", func.getTrapID(), arg diff --git a/cpp/ql/src/CPython/PropertyTypeTrap.ql b/cpp/ql/src/CPython/PropertyTypeTrap.ql new file mode 100644 index 000000000000..543b5be1c68b --- /dev/null +++ b/cpp/ql/src/CPython/PropertyTypeTrap.ql @@ -0,0 +1,15 @@ +/** + * @name Property type trap file generator + * @description Generate trap files (in CSV form) describing CPython extension property types. + * @kind trap + * @id cpp/c-python/property-type-trap + */ + +import cpp + +import CPython.Extensions + + +from TypedPythonExtensionProperty p, PythonClass cls +where cls = p.getPropertyType() +select "ext_proptype", p.getTrapID(), cls.getTrapID() diff --git a/cpp/ql/src/CPython/ReturnTypeTrap.ql b/cpp/ql/src/CPython/ReturnTypeTrap.ql new file mode 100644 index 000000000000..b8ae2f23d8fa --- /dev/null +++ b/cpp/ql/src/CPython/ReturnTypeTrap.ql @@ -0,0 +1,15 @@ +/** + * @name Return type trap file generator + * @description Generate trap files (in CSV form) describing CPython extension function return types. + * @kind trap + * @id cpp/c-python/return-type-trap + */ + +import cpp + +import CPython.Extensions + + +from TypedPythonExtensionFunction func, PythonClass cls +where cls = func.getReturnType() +select "ext_rettype", func.getTrapID(), cls.getTrapID() diff --git a/cpp/ql/src/Critical/DeadCodeCondition.cpp b/cpp/ql/src/Critical/DeadCodeCondition.cpp new file mode 100644 index 000000000000..743dc39c1b31 --- /dev/null +++ b/cpp/ql/src/Critical/DeadCodeCondition.cpp @@ -0,0 +1,19 @@ +while(result) { + if ( ... ) + ... + else if (result //wrong: this test is redundant + && result->flags != 0) + ... + result = next(queue); +} + + +fp = fopen(log, "r"); +if (fp) { + /* + * large block of code + */ + if (!fp) { //wrong: always false + ... /* dead code */ + } +} diff --git a/cpp/ql/src/Critical/DeadCodeCondition.qhelp b/cpp/ql/src/Critical/DeadCodeCondition.qhelp new file mode 100644 index 000000000000..10b1e3253a1b --- /dev/null +++ b/cpp/ql/src/Critical/DeadCodeCondition.qhelp @@ -0,0 +1,27 @@ + + + + + +

    This rule finds branching statements with conditions that always evaluate to the same value. +More likely than not these conditions indicate a defect in the branching condition or are an artifact left behind after debugging.

    + + + +
    + +

    Check the branch condition for defects, and verify that it isn't a remnant from debugging.

    + +
    + + + + + + + + + +
    diff --git a/cpp/ql/src/Critical/DeadCodeCondition.ql b/cpp/ql/src/Critical/DeadCodeCondition.ql new file mode 100644 index 000000000000..e7e5b4ef36cc --- /dev/null +++ b/cpp/ql/src/Critical/DeadCodeCondition.ql @@ -0,0 +1,57 @@ +/** + * @name Branching condition always evaluates to same value + * @description The condition of the branching statement always evaluates to the same value. This means that only one branch will ever be executed. + * @kind problem + * @id cpp/dead-code-condition + * @problem.severity warning + * @tags reliability + * external/cwe/cwe-561 + */ +import cpp + +predicate testAndBranch(Expr e, Stmt branch) +{ + exists(IfStmt ifstmt | ifstmt.getCondition() = e and + (ifstmt.getThen() = branch or ifstmt.getElse() = branch)) + or + exists(WhileStmt while | while.getCondition() = e and + while.getStmt() = branch) +} + +predicate choice(LocalScopeVariable v, Stmt branch, string value) +{ + exists(AnalysedExpr e | + testAndBranch(e, branch) and + ( + (e.getNullSuccessor(v) = branch and value = "null") + or + (e.getNonNullSuccessor(v) = branch and value = "non-null") + )) +} + + +predicate guarded(LocalScopeVariable v, Stmt loopstart, AnalysedExpr child) +{ + choice(v, loopstart, _) and + loopstart.getChildStmt*() = child.getEnclosingStmt() and + (definition(v, child) or exists(child.getNullSuccessor(v))) +} + +predicate addressLeak(Variable v, Stmt leak) +{ + exists(VariableAccess access | + v.getAnAccess() = access and + access.getEnclosingStmt() = leak and + access.isAddressOfAccess()) +} + +from LocalScopeVariable v, Stmt branch, AnalysedExpr cond, string context, string test, string testresult +where choice(v, branch, context) + and forall(ControlFlowNode def | definition(v, def) and definitionReaches(def, cond) | not guarded(v, branch, def)) + and not cond.isDef(v) + and guarded(v, branch, cond) + and exists(cond.getNullSuccessor(v)) + and not addressLeak(v, branch.getChildStmt*()) + and ((cond.isNullCheck(v) and test = "null") or (cond.isValidCheck(v) and test = "non-null")) + and (if context = test then testresult = "succeed" else testresult = "fail") +select cond, "Variable '" + v.getName() + "' is always " + context + " here, this check will always " + testresult + "." diff --git a/cpp/ql/src/Critical/DeadCodeFunction.cpp b/cpp/ql/src/Critical/DeadCodeFunction.cpp new file mode 100644 index 000000000000..e16dc9c3bf54 --- /dev/null +++ b/cpp/ql/src/Critical/DeadCodeFunction.cpp @@ -0,0 +1,12 @@ +class C { +public: + void g() { + ... + //f() was previously used but is now commented, orphaning f() + //f(); + ... + } +private: + void f() { //is now unused, and can be removed + } +}; diff --git a/cpp/ql/src/Critical/DeadCodeFunction.qhelp b/cpp/ql/src/Critical/DeadCodeFunction.qhelp new file mode 100644 index 000000000000..f43e6cefa91f --- /dev/null +++ b/cpp/ql/src/Critical/DeadCodeFunction.qhelp @@ -0,0 +1,30 @@ + + + + + +

    This rule finds functions that are non-public, non-virtual and are never called. Dead functions are often deprecated pieces of code, and should be removed +as they may increase object code size, decrease code comprehensibility, and create the possibility of misuse.

    + +

    +public and protected functions are not considered by the check, as they could be part of the program's +API and could be used by external programs. +

    + + + +
    + +

    Consider removing the function.

    + +
    + + + + + + + +
    diff --git a/cpp/ql/src/Critical/DeadCodeFunction.ql b/cpp/ql/src/Critical/DeadCodeFunction.ql new file mode 100644 index 000000000000..8ddb30804ae6 --- /dev/null +++ b/cpp/ql/src/Critical/DeadCodeFunction.ql @@ -0,0 +1,34 @@ +/** + * @name Function is never called + * @description A function is never called, and should be considered for removal. Unused functions may increase object size, decrease readability and create the possibility of misuse. + * @kind problem + * @id cpp/dead-code-function + * @problem.severity warning + * @tags maintainability + * external/cwe/cwe-561 + */ +import cpp + +predicate limitedScope(Function f) +{ + (f.isStatic() and not (f instanceof MemberFunction)) + or + f.(MemberFunction).isPrivate() +} + +predicate uncalled(Function f) +{ + limitedScope(f) + and not exists(Function g | + g = f or g = f.(VirtualFunction).getAnOverriddenFunction+() | + exists(g.getACallToThisFunction()) or + exists(FunctionAccess fa | fa.getTarget() = g)) +} + +from Function f +where uncalled(f) + and forall(Function instance | f.(TemplateFunction).getAnInstantiation() = instance | uncalled(instance)) + // tweaks for good results: + and exists(f.getBlock()) + and not(f instanceof Constructor or f instanceof Destructor or f.hasName("operator=")) +select f, "Dead Code: this function is never called." diff --git a/cpp/ql/src/Critical/DescriptorMayNotBeClosed.cpp b/cpp/ql/src/Critical/DescriptorMayNotBeClosed.cpp new file mode 100644 index 000000000000..69b83a9a4904 --- /dev/null +++ b/cpp/ql/src/Critical/DescriptorMayNotBeClosed.cpp @@ -0,0 +1,9 @@ +int f() { + try { + int sockfd = socket(AF_INET, SOCK_STREAM, 0); + do_stuff(sockfd); + return sockfd; //if there are no exceptions, the socket is returned + } catch (int do_stuff_exception) { + return -1; //return error value, but sockfd may still be open + } +} diff --git a/cpp/ql/src/Critical/DescriptorMayNotBeClosed.qhelp b/cpp/ql/src/Critical/DescriptorMayNotBeClosed.qhelp new file mode 100644 index 000000000000..c929da28e5d9 --- /dev/null +++ b/cpp/ql/src/Critical/DescriptorMayNotBeClosed.qhelp @@ -0,0 +1,28 @@ + + + + + +

    +This rule looks at functions that return file or socket descriptors, but may return an error value before actually closing the resource. +This can occur when an operation performed on the open descriptor fails, and the function returns with an error before closing the open resource. An improperly handled error could cause the function to leak resource descriptors. +

    + + + +
    + +

    Ensure that the function frees all the resources it acquired when an error occurs.

    + +
    + + + + + + + + +
    diff --git a/cpp/ql/src/Critical/DescriptorMayNotBeClosed.ql b/cpp/ql/src/Critical/DescriptorMayNotBeClosed.ql new file mode 100644 index 000000000000..3ee0d26a0e0c --- /dev/null +++ b/cpp/ql/src/Critical/DescriptorMayNotBeClosed.ql @@ -0,0 +1,60 @@ +/** + * @name Open descriptor may not be closed + * @description A function may return before closing a socket or file that was opened in the function. Closing resources in the same function that opened them ties the lifetime of the resource to that of the function call, making it easier to avoid and detect resource leaks. + * @kind problem + * @id cpp/descriptor-may-not-be-closed + * @problem.severity warning + * @tags efficiency + * external/cwe/cwe-775 + */ +import semmle.code.cpp.pointsto.PointsTo +import Negativity + +predicate closeCall(FunctionCall fc, Variable v) +{ + (fc.getTarget().hasQualifiedName("close") and v.getAnAccess() = fc.getArgument(0)) + or + exists(FunctionCall midcall, Function mid, int arg | + fc.getArgument(arg) = v.getAnAccess() and + fc.getTarget() = mid and + midcall.getEnclosingFunction() = mid and + closeCall(midcall, mid.getParameter(arg))) +} + +predicate openDefinition(LocalScopeVariable v, ControlFlowNode def) +{ + exists(Expr expr | + exprDefinition(v, def, expr) and allocateDescriptorCall(expr)) +} + +predicate openReaches(ControlFlowNode def, ControlFlowNode node) +{ + exists(LocalScopeVariable v | + openDefinition(v, def) and node = def.getASuccessor()) + or + exists(LocalScopeVariable v, ControlFlowNode mid | + openDefinition(v, def) and + openReaches(def, mid) and + not(errorSuccessor(v, mid)) and + not(closeCall(mid, v)) and + not(assignedToFieldOrGlobal(v, mid)) and + node = mid.getASuccessor()) +} + +predicate assignedToFieldOrGlobal(LocalScopeVariable v, Assignment assign) +{ + exists(Variable external | + assign.getRValue() = v.getAnAccess() and + assign.getLValue().(VariableAccess).getTarget() = external and + (external instanceof Field or external instanceof GlobalVariable)) +} + +from LocalScopeVariable v, ControlFlowNode def, ReturnStmt ret +where openDefinition(v, def) + and openReaches(def, ret) + and checkedSuccess(v, ret) + and not(ret.getExpr().getAChild*() = v.getAnAccess()) + and exists(ReturnStmt other | other.getExpr() = v.getAnAccess()) +select ret, + "Descriptor assigned to '" + v.getName().toString() + "' (line " + + def.getLocation().getStartLine().toString() + ") may not be closed." diff --git a/cpp/ql/src/Critical/DescriptorNeverClosed.cpp b/cpp/ql/src/Critical/DescriptorNeverClosed.cpp new file mode 100644 index 000000000000..d791a7024bff --- /dev/null +++ b/cpp/ql/src/Critical/DescriptorNeverClosed.cpp @@ -0,0 +1,6 @@ +int main(int argc, char* argv[]) { + int sockfd = socket(AF_INET, SOCK_STREAM, 0); + int status = 0; + ... //code that does not close sockfd + return status; //sockfd is never closed +} diff --git a/cpp/ql/src/Critical/DescriptorNeverClosed.qhelp b/cpp/ql/src/Critical/DescriptorNeverClosed.qhelp new file mode 100644 index 000000000000..47d82555a73c --- /dev/null +++ b/cpp/ql/src/Critical/DescriptorNeverClosed.qhelp @@ -0,0 +1,25 @@ + + + + + +

    +This rule finds calls to open or socket with no corresponding close call in the entire program. +Leaving descriptors open will cause a resource leak that will persist even after the program terminates. +

    + + + +
    + +

    Ensure that all file or socket descriptors allocated by the program are freed before it terminates.

    + +
    + + + + + +
    diff --git a/cpp/ql/src/Critical/DescriptorNeverClosed.ql b/cpp/ql/src/Critical/DescriptorNeverClosed.ql new file mode 100644 index 000000000000..4c5fb4915fa7 --- /dev/null +++ b/cpp/ql/src/Critical/DescriptorNeverClosed.ql @@ -0,0 +1,28 @@ +/** + * @name Open descriptor never closed + * @description A function always returns before closing a socket or file that was opened in the function. Closing resources in the same function that opened them ties the lifetime of the resource to that of the function call, making it easier to avoid and detect resource leaks. + * @kind problem + * @id cpp/descriptor-never-closed + * @problem.severity warning + * @tags efficiency + * external/cwe/cwe-775 + */ +import semmle.code.cpp.pointsto.PointsTo + +predicate closed(Expr e) +{ + exists(FunctionCall fc | + fc.getTarget().hasQualifiedName("close") and + fc.getArgument(0) = e) +} + +class ClosedExpr extends PointsToExpr +{ + ClosedExpr() { closed(this) } + override predicate interesting() { closed(this) } +} + +from Expr alloc +where allocateDescriptorCall(alloc) + and not exists(ClosedExpr closed | closed.pointsTo() = alloc) +select alloc, "This file descriptor is never closed" diff --git a/cpp/ql/src/Critical/FileClosed.qll b/cpp/ql/src/Critical/FileClosed.qll new file mode 100644 index 000000000000..2aa47db3d9b3 --- /dev/null +++ b/cpp/ql/src/Critical/FileClosed.qll @@ -0,0 +1,18 @@ +import semmle.code.cpp.pointsto.PointsTo + +predicate closed(Expr e) { + fcloseCall(_, e) or + exists(ExprCall c | + // cautiously assume that any ExprCall could be a call to fclose. + c.getAnArgument() = e + ) +} + +class ClosedExpr extends PointsToExpr { + ClosedExpr() { closed(this) } + override predicate interesting() { closed(this) } +} + +predicate fopenCallMayBeClosed(FunctionCall fc) { + fopenCall(fc) and anythingPointsTo(fc) +} diff --git a/cpp/ql/src/Critical/FileMayNotBeClosed.cpp b/cpp/ql/src/Critical/FileMayNotBeClosed.cpp new file mode 100644 index 000000000000..cd8234c95f90 --- /dev/null +++ b/cpp/ql/src/Critical/FileMayNotBeClosed.cpp @@ -0,0 +1,9 @@ +FILE* f() { + try { + FILE *fp = fopen("foo.txt", "w"); + do_stuff(fp); + return fp; //if there are no exceptions, the file pointer is returned correctly + } catch (int do_stuff_exception) { + return NULL; //returns NULL on error, but does not close fp + } +} diff --git a/cpp/ql/src/Critical/FileMayNotBeClosed.qhelp b/cpp/ql/src/Critical/FileMayNotBeClosed.qhelp new file mode 100644 index 000000000000..b05af84c9bf6 --- /dev/null +++ b/cpp/ql/src/Critical/FileMayNotBeClosed.qhelp @@ -0,0 +1,26 @@ + + + + + +

    +This rule looks at functions that return a FILE*, but may return an error value before actually closing the resource. +This can occur when an operation performed on the open descriptor fails, and the function returns with an error before closing the open resource. An improperly handled error may cause the function to leak file descriptors. +

    + + + +
    + +

    Ensure that the function frees all the resources it acquired when an error occurs.

    + +
    + + + + + + +
    diff --git a/cpp/ql/src/Critical/FileMayNotBeClosed.ql b/cpp/ql/src/Critical/FileMayNotBeClosed.ql new file mode 100644 index 000000000000..8481c03338b9 --- /dev/null +++ b/cpp/ql/src/Critical/FileMayNotBeClosed.ql @@ -0,0 +1,185 @@ +/** + * @name Open file may not be closed + * @description A function may return before closing a file that was opened in the function. Closing resources in the same function that opened them ties the lifetime of the resource to that of the function call, making it easier to avoid and detect resource leaks. + * @kind problem + * @id cpp/file-may-not-be-closed + * @problem.severity warning + * @tags efficiency + * security + * external/cwe/cwe-775 + */ +import FileClosed +import semmle.code.cpp.controlflow.LocalScopeVariableReachability + +/** + * Extend the NullValue class used by Nullness.qll to include simple -1 as a 'null' value + * (for example 'open' returns -1 if there was an error) + */ +class MinusOne extends NullValue +{ + MinusOne() { this.(UnaryMinusExpr).getOperand().(Literal).getValue() = "1" } +} + +/** + * 'call' is either a direct call to f, or a possible call to f + * via a function pointer. + */ +predicate mayCallFunction(Expr call, Function f) +{ + call.(FunctionCall).getTarget() = f or + call.(VariableCall).getVariable().getAnAssignedValue().getAChild*().(FunctionAccess).getTarget() = f +} + +predicate fopenCallOrIndirect(Expr e) +{ + ( + // direct fopen call + fopenCall(e) and + + // We are only interested in fopen calls that are + // actually closed somehow, as FileNeverClosed + // will catch those that aren't. + fopenCallMayBeClosed(e) + ) or exists(ReturnStmt rtn | + // indirect fopen call + mayCallFunction(e, rtn.getEnclosingFunction()) and + ( + // return fopen + fopenCallOrIndirect(rtn.getExpr()) or + + // return variable assigned with fopen + exists(Variable v | + v = rtn.getExpr().(VariableAccess).getTarget() and + fopenCallOrIndirect(v.getAnAssignedValue()) and + not assignedToFieldOrGlobal(v, _) + ) + ) + ) +} + +predicate fcloseCallOrIndirect(FunctionCall fc, Variable v) +{ + // direct fclose call + fcloseCall(fc, v.getAnAccess()) + or + // indirect fclose call + exists(FunctionCall midcall, Function mid, int arg | + fc.getArgument(arg) = v.getAnAccess() and + mayCallFunction(fc, mid) and + midcall.getEnclosingFunction() = mid and + fcloseCallOrIndirect(midcall, mid.getParameter(arg)) + ) +} + +predicate fopenDefinition(LocalScopeVariable v, ControlFlowNode def) +{ + exists(Expr expr | + exprDefinition(v, def, expr) and fopenCallOrIndirect(expr) + ) +} + +class FOpenVariableReachability extends LocalScopeVariableReachabilityWithReassignment { + FOpenVariableReachability() { this = "FOpenVariableReachability" } + + override predicate isSourceActual(ControlFlowNode node, LocalScopeVariable v) { + fopenDefinition(v, node) + } + + override predicate isSinkActual(ControlFlowNode node, LocalScopeVariable v) { + // node may be used in fopenReaches + exists(node.(AnalysedExpr).getNullSuccessor(v)) or + fcloseCallOrIndirect(node, v) or + assignedToFieldOrGlobal(v, node) or + + // node may be used directly in query + v.getFunction() = node.(ReturnStmt).getEnclosingFunction() + } + + override predicate isBarrier(ControlFlowNode node, LocalScopeVariable v) { + definitionBarrier(v, node) + } +} + +/** + * The value from fopen at `def` is still held in Variable `v` upon entering `node`. + */ +predicate fopenVariableReaches(LocalScopeVariable v, ControlFlowNode def, ControlFlowNode node) +{ + exists(FOpenVariableReachability r | + // reachability + r.reachesTo(def, _, node, v) or + + // accept def node itself + ( + r.isSource(def, v) and + node = def + ) + ) +} + +class FOpenReachability extends LocalScopeVariableReachabilityExt { + FOpenReachability() { this = "FOpenReachability" } + + override predicate isSource(ControlFlowNode node, LocalScopeVariable v) { + fopenDefinition(v, node) + } + + override predicate isSink(ControlFlowNode node, LocalScopeVariable v) { + v.getFunction() = node.(ReturnStmt).getEnclosingFunction() + } + + override predicate isBarrier(ControlFlowNode source, ControlFlowNode node, ControlFlowNode next, LocalScopeVariable v) { + isSource(source, v) and + next = node.getASuccessor() and + + // the file (stored in any variable `v0`) opened at `source` is closed or + // assigned to a global at node, or NULL checked on the edge node -> next. + exists(LocalScopeVariable v0 | fopenVariableReaches(v0, source, node) | + node.(AnalysedExpr).getNullSuccessor(v0) = next or + fcloseCallOrIndirect(node, v0) or + assignedToFieldOrGlobal(v0, node) + ) + } +} + +/** + * The value returned by fopen `def` has not been closed, confirmed to be null, + * or potentially leaked globally upon reaching `node` (regardless of what variable + * it's still held in, if any). + */ +predicate fopenReaches(ControlFlowNode def, ControlFlowNode node) +{ + exists(FOpenReachability r | + r.reaches(def, _, node) + ) +} + +predicate assignedToFieldOrGlobal(LocalScopeVariable v, Expr e) +{ + ( + // assigned to anything except a LocalScopeVariable + // (typically a field or global, but for example also *ptr = v) + e.(Assignment).getRValue() = v.getAnAccess() and + not e.(Assignment).getLValue().(VariableAccess).getTarget() instanceof LocalScopeVariable + ) or exists(Expr midExpr, Function mid, int arg | + // indirect assignment + e.(FunctionCall).getArgument(arg) = v.getAnAccess() and + mayCallFunction(e, mid) and + midExpr.getEnclosingFunction() = mid and + assignedToFieldOrGlobal(mid.getParameter(arg), midExpr) + ) or ( + // assigned to a field via constructor field initializer + e.(ConstructorFieldInit).getExpr() = v.getAnAccess() + ) +} + +from ControlFlowNode def, ReturnStmt ret +where + fopenReaches(def, ret) and + not exists(LocalScopeVariable v | + fopenVariableReaches(v, def, ret) and + ret.getAChild*() = v.getAnAccess() + ) +select + def, "The file opened here may not be closed at $@.", + ret, "this exit point" diff --git a/cpp/ql/src/Critical/FileNeverClosed.cpp b/cpp/ql/src/Critical/FileNeverClosed.cpp new file mode 100644 index 000000000000..8ffeed611604 --- /dev/null +++ b/cpp/ql/src/Critical/FileNeverClosed.cpp @@ -0,0 +1,6 @@ +int main(int argc, char* argv[]) { + FILE *fp = fopen("foo.txt", "w"); + int status = 0; + ... //code that does not close fp + return status; //fp is never closed +} diff --git a/cpp/ql/src/Critical/FileNeverClosed.qhelp b/cpp/ql/src/Critical/FileNeverClosed.qhelp new file mode 100644 index 000000000000..b870e9b50c30 --- /dev/null +++ b/cpp/ql/src/Critical/FileNeverClosed.qhelp @@ -0,0 +1,26 @@ + + + + + +

    +This rule finds calls to fopen with no corresponding fclose call in the entire program. +Leaving files open will cause a resource leak that will persist even after the program terminates. +

    + + + +
    + +

    Ensure that all file or socket descriptors allocated by the program are freed before it terminates.

    + +
    + + + + + + +
    diff --git a/cpp/ql/src/Critical/FileNeverClosed.ql b/cpp/ql/src/Critical/FileNeverClosed.ql new file mode 100644 index 000000000000..0b17c3ea0d2c --- /dev/null +++ b/cpp/ql/src/Critical/FileNeverClosed.ql @@ -0,0 +1,15 @@ +/** + * @name Open file is not closed + * @description A function always returns before closing a file that was opened in the function. Closing resources in the same function that opened them ties the lifetime of the resource to that of the function call, making it easier to avoid and detect resource leaks. + * @kind problem + * @id cpp/file-never-closed + * @problem.severity warning + * @tags efficiency + * security + * external/cwe/cwe-775 + */ +import FileClosed + +from Expr alloc +where fopenCall(alloc) and not fopenCallMayBeClosed(alloc) +select alloc, "The file is never closed" diff --git a/cpp/ql/src/Critical/GlobalUseBeforeInit.cpp b/cpp/ql/src/Critical/GlobalUseBeforeInit.cpp new file mode 100644 index 000000000000..30fb4ca1cee0 --- /dev/null +++ b/cpp/ql/src/Critical/GlobalUseBeforeInit.cpp @@ -0,0 +1,11 @@ +int g_callCtr; + +void initGlobals() { + g_callCtr = 0; +} + +int main(int argc, char* argv[]) { + ... + cout << g_callCtr; //callCtr used before it is initialized + initGlobals(); +} diff --git a/cpp/ql/src/Critical/GlobalUseBeforeInit.qhelp b/cpp/ql/src/Critical/GlobalUseBeforeInit.qhelp new file mode 100644 index 000000000000..26680a2598bf --- /dev/null +++ b/cpp/ql/src/Critical/GlobalUseBeforeInit.qhelp @@ -0,0 +1,30 @@ + + + + + +

    This rule finds calls to functions that use a global variable which happen before the variable was initialized. +Not all compilers generate code that zero-out memory, especially when optimizations are enabled or the compiler +is not compliant with the latest language standards. Accessing uninitialized memory will lead to undefined results. +

    + + + +
    + +

    +Initialize the global variable. If no constant can be used for initialization, ensure that all accesses to the variable occur after +the initialization code is executed. +

    + +
    + + + + + + + +
    diff --git a/cpp/ql/src/Critical/GlobalUseBeforeInit.ql b/cpp/ql/src/Critical/GlobalUseBeforeInit.ql new file mode 100644 index 000000000000..cd0fd5839d65 --- /dev/null +++ b/cpp/ql/src/Critical/GlobalUseBeforeInit.ql @@ -0,0 +1,109 @@ +/** + * @name Global variable used before initialization + * @description A function that uses a global variable has been called before the variable has been initialized. Not all compilers zero-out memory for variables, especially when optimizations are enabled, or if the compiler is not compliant with the latest language standards. Using an uninitialized variable leads to undefined results. + * @kind problem + * @id cpp/global-use-before-init + * @problem.severity warning + * @tags reliability + * external/cwe/cwe-457 + */ +import cpp +import semmle.code.cpp.pointsto.CallGraph + +predicate initFunc(GlobalVariable v, Function f) +{ + exists(VariableAccess access | + v.getAnAccess() = access and + access.isUsedAsLValue() and + access.getEnclosingFunction() = f) +} + +predicate useFunc(GlobalVariable v, Function f) +{ + exists(VariableAccess access | + v.getAnAccess() = access and + access.isRValue() and + access.getEnclosingFunction() = f) + and + not initFunc(v, f) +} + +predicate uninitialisedBefore(GlobalVariable v, Function f) +{ + f.hasQualifiedName("main") + or + exists(Call call, Function g | + uninitialisedBefore(v, g) and + call.getEnclosingFunction() = g and + (not functionInitialises(f, v) or locallyUninitialisedAt(v, call)) and + resolvedCall(call, f)) +} + +predicate functionInitialises(Function f, GlobalVariable v) +{ + exists(Call call | + call.getEnclosingFunction() = f and + initialisedBy(v, call)) +} + +// this predicate is restricted to global variables used in the +// same function as "call" +predicate locallyUninitialisedAt(GlobalVariable v, Call call) +{ + functionInitialises(call.getEnclosingFunction(), v) and + ( + firstCall(call) + or + exists(Call mid | + locallyUninitialisedAt(v, mid) and not initialisedBy(v, mid) and callPair(mid, call)) + ) +} + +predicate initialisedBy(GlobalVariable v, Call call) +{ + exists(Function f | + resolvedCall(call, f) and + initialises(v, f)) +} + +predicate initialises(GlobalVariable v, Function f) +{ + initFunc(v, f) + or + exists(Function mid | initialises(v, mid) and allCalls(f, mid)) +} + +predicate firstCall(Call call) +{ + beforeCall(call) +} + +predicate beforeCall(ControlFlowNode node) +{ + exists(Function f | f.getBlock() = node) + or + exists(ControlFlowNode mid | + beforeCall(mid) and + not mid instanceof Call and + node = mid.getASuccessor()) +} + +predicate callPair(Call call, Call successor) +{ + callReaches(call, successor) +} + +predicate callReaches(Call call, ControlFlowNode successor) +{ + call.getASuccessor() = successor + or + exists(ControlFlowNode mid | + callReaches(call, mid) and + not mid instanceof Call + and mid.getASuccessor() = successor) +} + +from GlobalVariable v, Function f +where uninitialisedBefore(v, f) + and useFunc(v, f) +select f, "The variable '" + v.getName() + " is used in this function but may not be initialized when it is called." diff --git a/cpp/ql/src/Critical/InconsistentNullnessTesting.cpp b/cpp/ql/src/Critical/InconsistentNullnessTesting.cpp new file mode 100644 index 000000000000..b071e79ecdb8 --- /dev/null +++ b/cpp/ql/src/Critical/InconsistentNullnessTesting.cpp @@ -0,0 +1,10 @@ +void* f() { + block = malloc(BLOCK_SIZE); + if (block) { //correct: block is checked for nullness here + block->id = NORMAL_BLOCK_ID; + } + //... + /* make sure data-portion is null-terminated */ + block[BLOCK_SIZE - 1] = '\0'; //wrong: block not checked for nullness here + return block; +} diff --git a/cpp/ql/src/Critical/InconsistentNullnessTesting.qhelp b/cpp/ql/src/Critical/InconsistentNullnessTesting.qhelp new file mode 100644 index 000000000000..ee1b29807270 --- /dev/null +++ b/cpp/ql/src/Critical/InconsistentNullnessTesting.qhelp @@ -0,0 +1,27 @@ + + + + + +

    This rule finds pointer dereferences that do not check the pointer for nullness, while the same pointer is checked for nullness in other +places in the code. It is most likely that the nullness check was omitted, and that a NULL pointer dereference can occur. +Dereferencing a null pointer and attempting to modify its contents can lead to anything from a segfault to corrupting +important system data (i.e. the interrupt table in some architectures). +

    + + + +
    + +

    Make the nullness check on the pointer consistent across all dereferences.

    + +
    + + + + + + +
    diff --git a/cpp/ql/src/Critical/InconsistentNullnessTesting.ql b/cpp/ql/src/Critical/InconsistentNullnessTesting.ql new file mode 100644 index 000000000000..134a52af41bd --- /dev/null +++ b/cpp/ql/src/Critical/InconsistentNullnessTesting.ql @@ -0,0 +1,22 @@ +/** + * @name Inconsistent null check of pointer + * @description A dereferenced pointer is not checked for nullness in the given location, but is checked in other locations. Dereferencing a NULL pointer leads to undefined results. + * @kind problem + * @id cpp/inconsistent-nullness-testing + * @problem.severity warning + * @tags reliability + * external/cwe/cwe-476 + */ +import cpp + +from LocalScopeVariable v, ControlFlowNode def, + VariableAccess checked, VariableAccess unchecked +where checked = v.getAnAccess() and dereferenced(checked) + and unchecked = v.getAnAccess() and dereferenced(unchecked) + and definitionUsePair(v, def, checked) + and definitionUsePair(v, def, unchecked) + and checkedValid(v, checked) + and not(checkedValid(v, unchecked)) + and not(unchecked.getParent+() instanceof SizeofOperator) + and forall(ControlFlowNode other | definitionUsePair(v, other, checked) | definitionUsePair(v, other, unchecked)) +select unchecked, "This dereference is not guarded by a non-null check, whereas other dereferences are guarded" diff --git a/cpp/ql/src/Critical/InitialisationNotRun.cpp b/cpp/ql/src/Critical/InitialisationNotRun.cpp new file mode 100644 index 000000000000..2c937d4b9b90 --- /dev/null +++ b/cpp/ql/src/Critical/InitialisationNotRun.cpp @@ -0,0 +1,12 @@ +GlobalStorage *g_storage; + +void init() { //initializes g_storage, but is never run from main + g_storage = new GlobalStorage(); + ... +} + +int main(int argc, char *argv[]) { + ... //init not called + strcpy(g_storage->name, argv[1]); // g_storage is used before init() is called + ... +} diff --git a/cpp/ql/src/Critical/InitialisationNotRun.qhelp b/cpp/ql/src/Critical/InitialisationNotRun.qhelp new file mode 100644 index 000000000000..384a37cf71d9 --- /dev/null +++ b/cpp/ql/src/Critical/InitialisationNotRun.qhelp @@ -0,0 +1,27 @@ + + + + + +

    The rule finds code that initializes a global variable (i.e. uses it as an L-value) but is never called from main. +Unless there is another entry point that triggers the initialization, the code should be modified so that the variable is initialized. +Uninitialized variables may contain any value, as not all compilers generate code that zero-out memory, especially when +optimizations are enabled or the compiler is not compliant with the latest language standards. +

    + + + +
    + +

    Examine the code and ensure that the initialization is always run.

    + +
    + + + + + + +
    diff --git a/cpp/ql/src/Critical/InitialisationNotRun.ql b/cpp/ql/src/Critical/InitialisationNotRun.ql new file mode 100644 index 000000000000..59b69cec77b6 --- /dev/null +++ b/cpp/ql/src/Critical/InitialisationNotRun.ql @@ -0,0 +1,42 @@ +/** + * @name Initialization code not run + * @description A global variable has initialization code, but that code is never run (i.e. is called directly or indirectly from main()). Accessing uninitialized variables leads to undefined results. + * @kind problem + * @id cpp/initialization-not-run + * @problem.severity warning + * @tags reliability + * external/cwe/cwe-456 + */ +import cpp +import semmle.code.cpp.pointsto.CallGraph + +predicate global(GlobalVariable v) +{ + not exists(v.getInitializer()) + and not v.getType() instanceof ArrayType + and not v.getType() instanceof Class + and v.getAnAccess().isUsedAsLValue() +} + +predicate mainCalled(Function f) +{ + f.getQualifiedName() = "main" + or + exists(Function caller | + mainCalled(caller) and allCalls(caller, f)) +} + +predicate called(Function f) +{ + mainCalled(f) + or + exists(FunctionAccess fa | fa.getTarget() = f) +} + +from GlobalVariable v +where global(v) + and not exists(VariableAccess lval | + v.getAnAccess() = lval and lval.isUsedAsLValue() and + called(lval.getEnclosingFunction()) + ) +select v, "Initialization code for '" + v.getName() + "' is never run." diff --git a/cpp/ql/src/Critical/LargeParameter.cpp b/cpp/ql/src/Critical/LargeParameter.cpp new file mode 100644 index 000000000000..5e829636132e --- /dev/null +++ b/cpp/ql/src/Critical/LargeParameter.cpp @@ -0,0 +1,13 @@ +typedef struct Names { + char first[100]; + char last[100]; +} Names; + +int doFoo(Names n) { //wrong: n is passed by value (meaning the entire structure + //is copied onto the stack, instead of just a pointer) + ... +} + +int doBar(Names &n) { //better, only a reference is passed + ... +} diff --git a/cpp/ql/src/Critical/LargeParameter.qhelp b/cpp/ql/src/Critical/LargeParameter.qhelp new file mode 100644 index 000000000000..795168444000 --- /dev/null +++ b/cpp/ql/src/Critical/LargeParameter.qhelp @@ -0,0 +1,38 @@ + + + + + +

    +This rule finds parameters greater than (for example) 64 bytes in size that are passed by value to function calls. It is not good practice to put large objects +on the stack, as widespread use of this anti-pattern throughout a software project can easily lead to stack overflows. Performance-wise, +the initial copy to the stack is more likely than not to be more expensive than any cost due to indirection caused accessing the object +through a pointer. In terms of security, an overrun on an array in an object copied to the stack is a much greater risk than one that happens +on the heap. +

    +

    +In C++, there is the added cost of calling the constructor and destructor of the object being passed as well as those of any other objects that it +(recursively) contains. +

    + +
    + +

    Pass the address of the object instead. There is usually no good reason for putting anything large on the stack.

    + +
    + + + + + + + +
  • + S. Meyers. Effective C++ 3d ed. pp 86-90. Addison-Wesley Professional, 2005. +
  • + + +
    +
    diff --git a/cpp/ql/src/Critical/LargeParameter.ql b/cpp/ql/src/Critical/LargeParameter.ql new file mode 100644 index 000000000000..1571e96bbec9 --- /dev/null +++ b/cpp/ql/src/Critical/LargeParameter.ql @@ -0,0 +1,23 @@ +/** + * @name Large object passed by value + * @description An object larger than 64 bytes is passed by value to a function. Passing large objects by value unnecessarily use up scarce stack space, increase the cost of calling a function and can be a security risk. Use a pointer to the object instead. + * @kind problem + * @problem.severity warning + * @precision high + * @id cpp/large-parameter + * @tags efficiency + * readability + * statistical + * non-attributable + */ +import cpp + +from Function f, Parameter p, Type t, int size +where f.getAParameter() = p + and p.getType() = t + and t.getSize() = size + and size > 64 + and not t.getUnderlyingType() instanceof ArrayType +select + p, "This parameter of type $@ is " + size.toString() + " bytes - consider passing a pointer/reference instead.", + t, t.toString() diff --git a/cpp/ql/src/Critical/LateNegativeTest.cpp b/cpp/ql/src/Critical/LateNegativeTest.cpp new file mode 100644 index 000000000000..83912da99863 --- /dev/null +++ b/cpp/ql/src/Critical/LateNegativeTest.cpp @@ -0,0 +1,11 @@ +Record records[SIZE] = ...; + +int f() { + int recordIdx = 0; + cin >> recordIdx; + printRecord(&(records[recordIdx])); //incorrect: recordIdx may be negative here + + if (recordIdx >= 0) { + processRecord(&(records[recordIdx])); //correct: index checked before use + } +} diff --git a/cpp/ql/src/Critical/LateNegativeTest.qhelp b/cpp/ql/src/Critical/LateNegativeTest.qhelp new file mode 100644 index 000000000000..de8f347bca0d --- /dev/null +++ b/cpp/ql/src/Critical/LateNegativeTest.qhelp @@ -0,0 +1,34 @@ + + + + + +

    +This rule finds integer values that are first used to index an array and +subsequently tested for being negative. If it is relevant to perform this test +at all then it should probably happen before the indexing, not +after. Otherwise, if the value is negative then the program will have failed +before performing the test. +

    + + +
    + +

    +See if the value needs checking before being used as array index. Double-check +if the value is derived from user input. If the value clearly cannot be +negative then the negativity test is redundant and can be removed. +

    + +
    + + + + + + + + +
    diff --git a/cpp/ql/src/Critical/LateNegativeTest.ql b/cpp/ql/src/Critical/LateNegativeTest.ql new file mode 100644 index 000000000000..78f46df72d3a --- /dev/null +++ b/cpp/ql/src/Critical/LateNegativeTest.ql @@ -0,0 +1,40 @@ +/** + * @name Pointer offset used before it is checked + * @description A value is used as a pointer offset before it is tested for + * being positive/negative. This may mean that an unsuitable + * pointer offset will be used before the test occurs. + * @kind problem + * @id cpp/late-negative-test + * @problem.severity warning + * @tags reliability + * external/cwe/cwe-823 + */ +import cpp + +predicate negativeCheck(LocalScopeVariable v, ComparisonOperation op) +{ + exists(int varindex, string constant, Literal lit | + op.getChild(varindex) = v.getAnAccess() and + op.getChild(1 - varindex) = lit and + lit.getValue() = constant and + ( + (op.getOperator() = "<" and varindex = 0 and constant = "0") or + (op.getOperator() = "<" and varindex = 1 and constant = "-1") or + (op.getOperator() = ">" and varindex = 0 and constant = "-1") or + (op.getOperator() = ">" and varindex = 1 and constant = "0") or + (op.getOperator() = "<=" and varindex = 0 and constant = "-1") or + (op.getOperator() = "<=" and varindex = 1 and constant = "0") or + (op.getOperator() = ">=" and varindex = 0 and constant = "0") or + (op.getOperator() = ">=" and varindex = 1 and constant = "-1") + ) + ) +} + +from LocalScopeVariable v, ArrayExpr dangerous, Expr check +where useUsePair(v, dangerous.getArrayOffset(), check.getAChild()) + and negativeCheck(v, check) + and not exists(Expr other | negativeCheck(v, other) and useUsePair(v, other.getAChild(), dangerous.getArrayOffset())) +select dangerous, + "Variable '" + v.getName() + + "' is used as an array-offset before it is tested for being negative (test on line " + + check.getLocation().getStartLine().toString() + "). " diff --git a/cpp/ql/src/Critical/LoopBounds.qll b/cpp/ql/src/Critical/LoopBounds.qll new file mode 100644 index 000000000000..abc93ef1736d --- /dev/null +++ b/cpp/ql/src/Critical/LoopBounds.qll @@ -0,0 +1,63 @@ +/** Provides helpers for OverflowStatic.ql */ +import cpp + +class ZeroAssignment extends AssignExpr +{ + ZeroAssignment() { + this.getAnOperand() instanceof VariableAccess and + this.getAnOperand() instanceof Zero + } + + Variable assignedVariable() { + result.getAnAccess() = this.getAnOperand() + } +} + +private predicate staticLimit(RelationalOperation op, Variable v, int limit) +{ + ( + op instanceof LTExpr and + op.getLeftOperand() = v.getAnAccess() and + op.getRightOperand().getValue().toInt() - 1 = limit + ) or ( + op instanceof LEExpr and + op.getLeftOperand() = v.getAnAccess() and + op.getRightOperand().getValue().toInt() = limit + ) +} + +private predicate simpleInc(IncrementOperation inc, Variable v) +{ + inc.getAChild() = v.getAnAccess() +} + +/** + * A `for` loop of the form `for (x = 0; x < limit; x++)` with no modification + * of `x` in the body. Variations with `<=` and `++x` are allowed. + */ +class ClassicForLoop extends ForStmt +{ + ClassicForLoop() { + exists(LocalVariable v | + this.getInitialization().getAChild() instanceof ZeroAssignment and + staticLimit(this.getCondition(), v, _) and + simpleInc(this.getUpdate(), v) and + not exists(VariableAccess access | + access.isUsedAsLValue() and + v.getAnAccess() = access and + this.getStmt().getAChild*() = access.getEnclosingStmt() + ) + ) + } + + /** Gets the loop variable. */ + LocalVariable counter() { + simpleInc(this.getUpdate(), result) + } + + /** Gets the maximum value that the loop variable may have inside the loop + * body. The minimum is 0. */ + int limit() { + staticLimit(this.getCondition(), _, result) + } +} diff --git a/cpp/ql/src/Critical/MemoryFreed.qll b/cpp/ql/src/Critical/MemoryFreed.qll new file mode 100644 index 000000000000..773f476b9a04 --- /dev/null +++ b/cpp/ql/src/Critical/MemoryFreed.qll @@ -0,0 +1,26 @@ +import semmle.code.cpp.pointsto.PointsTo + +private predicate freed(Expr e) +{ + exists(FunctionCall fc, Expr arg | + freeCall(fc, arg) and + arg = e + ) or exists(DeleteExpr de | + de.getExpr() = e + ) or exists(DeleteArrayExpr de | + de.getExpr() = e + ) or exists(ExprCall c | + // cautiously assume that any ExprCall could be a freeCall. + c.getAnArgument() = e + ) +} + +class FreedExpr extends PointsToExpr +{ + FreedExpr() { freed(this) } + override predicate interesting() { freed(this) } +} + +predicate allocMayBeFreed(Expr alloc) { + isAllocationExpr(alloc) and anythingPointsTo(alloc) +} diff --git a/cpp/ql/src/Critical/MemoryMayNotBeFreed.cpp b/cpp/ql/src/Critical/MemoryMayNotBeFreed.cpp new file mode 100644 index 000000000000..c8a2f02a39c2 --- /dev/null +++ b/cpp/ql/src/Critical/MemoryMayNotBeFreed.cpp @@ -0,0 +1,9 @@ +int* f() { + try { + int *buff = malloc(SIZE*sizeof(int)); + do_stuff(buff); + return buff; + } catch (int do_stuff_exception) { + return NULL; //returns NULL on error, but does not free memory + } +} diff --git a/cpp/ql/src/Critical/MemoryMayNotBeFreed.qhelp b/cpp/ql/src/Critical/MemoryMayNotBeFreed.qhelp new file mode 100644 index 000000000000..cb065be0ad02 --- /dev/null +++ b/cpp/ql/src/Critical/MemoryMayNotBeFreed.qhelp @@ -0,0 +1,26 @@ + + + + + +

    +This rule looks for functions that allocate memory, but may return without freeing it. This can occur when an operation performed on the memory block fails, and the function returns with an error before freeing the allocated block. This causes the function to leak memory and may eventually lead to software failure. +

    + + + +
    + +

    Ensure that the function frees all dynamically allocated memory it has acquired in all circumstances, unless that memory is returned to the caller.

    + +
    + + + +

    In this example, if an exception occurs the memory allocated into buff is neither freed or returned. To fix this memory leak, we could add code to free buff to the catch block as follows:

    + + +
    +
    diff --git a/cpp/ql/src/Critical/MemoryMayNotBeFreed.ql b/cpp/ql/src/Critical/MemoryMayNotBeFreed.ql new file mode 100644 index 000000000000..48aac00359c6 --- /dev/null +++ b/cpp/ql/src/Critical/MemoryMayNotBeFreed.ql @@ -0,0 +1,209 @@ +/** + * @name Memory may not be freed + * @description A function may return before freeing memory that was allocated in the function. Freeing all memory allocated in the function before returning ties the lifetime of the memory blocks to that of the function call, making it easier to avoid and detect memory leaks. + * @kind problem + * @id cpp/memory-may-not-be-freed + * @problem.severity warning + * @tags efficiency + * security + * external/cwe/cwe-401 + */ +import MemoryFreed +import semmle.code.cpp.controlflow.LocalScopeVariableReachability + +/** + * 'call' is either a direct call to f, or a possible call to f + * via a function pointer. + */ +predicate mayCallFunction(Expr call, Function f) +{ + call.(FunctionCall).getTarget() = f or + call.(VariableCall).getVariable().getAnAssignedValue().getAChild*().(FunctionAccess).getTarget() = f +} + +predicate allocCallOrIndirect(Expr e) +{ + ( + // direct alloc call + isAllocationExpr(e) and + + // We are only interested in alloc calls that are + // actually freed somehow, as MemoryNeverFreed + // will catch those that aren't. + allocMayBeFreed(e) + ) or exists(ReturnStmt rtn | + // indirect alloc call + mayCallFunction(e, rtn.getEnclosingFunction()) and + ( + // return alloc + allocCallOrIndirect(rtn.getExpr()) or + + // return variable assigned with alloc + exists(Variable v | + v = rtn.getExpr().(VariableAccess).getTarget() and + allocCallOrIndirect(v.getAnAssignedValue()) and + not assignedToFieldOrGlobal(v, _) + ) + ) + ) +} + +/** + * The point at which a call to 'realloc' on 'v' has been verified to + * succeed. A failed realloc does *not* free the input pointer, which + * can cause memory leaks. + */ +predicate verifiedRealloc(FunctionCall reallocCall, Variable v, ControlFlowNode verified) +{ + reallocCall.getTarget().hasQualifiedName("realloc") and + reallocCall.getArgument(0) = v.getAnAccess() and + ( + exists(Variable newV, ControlFlowNode node | + // a realloc followed by a null check at 'node' (return the non-null + // successor, i.e. where the realloc is confirmed to have succeeded) + newV.getAnAssignedValue() = reallocCall and + node.(AnalysedExpr).getNonNullSuccessor(newV) = verified + // note: this case uses naive flow logic (getAnAssignedValue). + ) or ( + // a realloc(ptr, 0), which always succeeds and frees + // (return the realloc itself) + reallocCall.getArgument(1).getValue() = "0" and + verified = reallocCall + ) + ) +} + +predicate freeCallOrIndirect(ControlFlowNode n, Variable v) +{ + ( + // direct free call + freeCall(n, v.getAnAccess()) and + not n.(FunctionCall).getTarget().hasQualifiedName("realloc") + ) or ( + // verified realloc call + verifiedRealloc(_, v, n) + ) or ( + n.(DeleteExpr).getExpr() = v.getAnAccess() + ) or ( + n.(DeleteArrayExpr).getExpr() = v.getAnAccess() + ) or exists(FunctionCall midcall, Function mid, int arg | + // indirect free call + n.(Call).getArgument(arg) = v.getAnAccess() and + mayCallFunction(n, mid) and + midcall.getEnclosingFunction() = mid and + freeCallOrIndirect(midcall, mid.getParameter(arg)) + ) +} + +predicate allocationDefinition(LocalScopeVariable v, ControlFlowNode def) +{ + exists(Expr expr | + exprDefinition(v, def, expr) and allocCallOrIndirect(expr) + ) +} + +class AllocVariableReachability extends LocalScopeVariableReachabilityWithReassignment { + AllocVariableReachability() { this = "AllocVariableReachability" } + + override predicate isSourceActual(ControlFlowNode node, LocalScopeVariable v) { + allocationDefinition(v, node) + } + + override predicate isSinkActual(ControlFlowNode node, LocalScopeVariable v) { + // node may be used in allocationReaches + exists(node.(AnalysedExpr).getNullSuccessor(v)) or + freeCallOrIndirect(node, v) or + assignedToFieldOrGlobal(v, node) or + + // node may be used directly in query + v.getFunction() = node.(ReturnStmt).getEnclosingFunction() + } + + override predicate isBarrier(ControlFlowNode node, LocalScopeVariable v) { + definitionBarrier(v, node) + } +} + +/** + * The value from allocation `def` is still held in Variable `v` upon entering `node`. + */ +predicate allocatedVariableReaches(LocalScopeVariable v, ControlFlowNode def, ControlFlowNode node) +{ + exists(AllocVariableReachability r | + // reachability + r.reachesTo(def, _, node, v) or + + // accept def node itself + ( + r.isSource(def, v) and + node = def + ) + ) +} + +class AllocReachability extends LocalScopeVariableReachabilityExt { + AllocReachability() { this = "AllocReachability" } + + override predicate isSource(ControlFlowNode node, LocalScopeVariable v) { + allocationDefinition(v, node) + } + + override predicate isSink(ControlFlowNode node, LocalScopeVariable v) { + v.getFunction() = node.(ReturnStmt).getEnclosingFunction() + } + + override predicate isBarrier(ControlFlowNode source, ControlFlowNode node, ControlFlowNode next, LocalScopeVariable v) { + isSource(source, v) and + next = node.getASuccessor() and + + // the memory (stored in any variable `v0`) allocated at `source` is freed or + // assigned to a global at node, or NULL checked on the edge node -> next. + exists(LocalScopeVariable v0 | allocatedVariableReaches(v0, source, node) | + node.(AnalysedExpr).getNullSuccessor(v0) = next or + freeCallOrIndirect(node, v0) or + assignedToFieldOrGlobal(v0, node) + ) + } +} + +/** + * The value returned by allocation `def` has not been freed, confirmed to be null, + * or potentially leaked globally upon reaching `node` (regardless of what variable + * it's still held in, if any). + */ +predicate allocationReaches(ControlFlowNode def, ControlFlowNode node) +{ + exists(AllocReachability r | + r.reaches(def, _, node) + ) +} + +predicate assignedToFieldOrGlobal(LocalScopeVariable v, Expr e) +{ + ( + // assigned to anything except a LocalScopeVariable + // (typically a field or global, but for example also *ptr = v) + e.(Assignment).getRValue() = v.getAnAccess() and + not e.(Assignment).getLValue().(VariableAccess).getTarget() instanceof LocalScopeVariable + ) or exists(Expr midExpr, Function mid, int arg | + // indirect assignment + e.(FunctionCall).getArgument(arg) = v.getAnAccess() and + mayCallFunction(e, mid) and + midExpr.getEnclosingFunction() = mid and + assignedToFieldOrGlobal(mid.getParameter(arg), midExpr) + ) or ( + // assigned to a field via constructor field initializer + e.(ConstructorFieldInit).getExpr() = v.getAnAccess() + ) +} + +from ControlFlowNode def, ReturnStmt ret +where + allocationReaches(def, ret) and + not exists(LocalScopeVariable v | + allocatedVariableReaches(v, def, ret) and + ret.getAChild*() = v.getAnAccess() + ) +select + def, "The memory allocated here may not be released at $@.", + ret, "this exit point" diff --git a/cpp/ql/src/Critical/MemoryMayNotBeFreedGood.cpp b/cpp/ql/src/Critical/MemoryMayNotBeFreedGood.cpp new file mode 100644 index 000000000000..12bd397b3134 --- /dev/null +++ b/cpp/ql/src/Critical/MemoryMayNotBeFreedGood.cpp @@ -0,0 +1,13 @@ +int* f() { + int *buff = NULL; + try { + buff = malloc(SIZE*sizeof(int)); + do_stuff(buff); + return buff; + } catch (int do_stuff_exception) { + if (buff != NULL) { + free(buff); + } + return NULL; //returns NULL on error, having freed any allocated memory + } +} diff --git a/cpp/ql/src/Critical/MemoryNeverFreed.cpp b/cpp/ql/src/Critical/MemoryNeverFreed.cpp new file mode 100644 index 000000000000..fe8778c86ef2 --- /dev/null +++ b/cpp/ql/src/Critical/MemoryNeverFreed.cpp @@ -0,0 +1,6 @@ +int main(int argc, char* argv[]) { + int *buff = malloc(SIZE * sizeof(int)); + int status = 0; + ... //code that does not free buff + return status; //buff is never closed +} diff --git a/cpp/ql/src/Critical/MemoryNeverFreed.qhelp b/cpp/ql/src/Critical/MemoryNeverFreed.qhelp new file mode 100644 index 000000000000..c582034b909d --- /dev/null +++ b/cpp/ql/src/Critical/MemoryNeverFreed.qhelp @@ -0,0 +1,26 @@ + + + + + +

    +This rule finds calls to the alloc family of functions without a corresponding free call in the entire program. +This leads to memory leaks. +

    + + + +
    + +

    Ensure that all memory allocated by the program is freed before it terminates.

    + +
    + + + + + + +
    diff --git a/cpp/ql/src/Critical/MemoryNeverFreed.ql b/cpp/ql/src/Critical/MemoryNeverFreed.ql new file mode 100644 index 000000000000..b05933778b0e --- /dev/null +++ b/cpp/ql/src/Critical/MemoryNeverFreed.ql @@ -0,0 +1,15 @@ +/** + * @name Memory is never freed + * @description A function always returns before freeing memory that was allocated in the function. Freeing all memory allocated in the function before returning ties the lifetime of the memory blocks to that of the function call, making it easier to avoid and detect memory leaks. + * @kind problem + * @id cpp/memory-never-freed + * @problem.severity warning + * @tags efficiency + * security + * external/cwe/cwe-401 + */ +import MemoryFreed + +from Expr alloc +where isAllocationExpr(alloc) and not allocMayBeFreed(alloc) +select alloc, "This memory is never freed" diff --git a/cpp/ql/src/Critical/MissingNegativityTest.cpp b/cpp/ql/src/Critical/MissingNegativityTest.cpp new file mode 100644 index 000000000000..102b1bc4f3e7 --- /dev/null +++ b/cpp/ql/src/Critical/MissingNegativityTest.cpp @@ -0,0 +1,9 @@ +Record records[SIZE] = ...; + +int f() { + int recordIdx = 0; + recordIdx = readUserInput(); //recordIdx is returned from a function + // there is no check so it could be negative + doFoo(&(records[recordIdx])); //but is not checked before use as an array offset +} + diff --git a/cpp/ql/src/Critical/MissingNegativityTest.qhelp b/cpp/ql/src/Critical/MissingNegativityTest.qhelp new file mode 100644 index 000000000000..acc5fa2156f9 --- /dev/null +++ b/cpp/ql/src/Critical/MissingNegativityTest.qhelp @@ -0,0 +1,38 @@ + + + + + +

    +This rule finds pointer arithmetic expressions that use a value returned from a function before the value is checked to be positive. +Most pointer arithmetic and almost all array element accesses use a positive value for offsets. A negative value is more likely than not +a defect in the returning function. Checking pointer offsets (particularly if they derive from user input) is necessary to avoid +buffer overruns. +

    + +

    +The rules only looks at return values of functions that may return a negative value (not all functions). +

    + + + + +
    + +

    +Check the function and see whether it needs to check the value to be positive. +

    + +
    + + + + + + + + + +
    diff --git a/cpp/ql/src/Critical/MissingNegativityTest.ql b/cpp/ql/src/Critical/MissingNegativityTest.ql new file mode 100644 index 000000000000..7ef9ed2adda4 --- /dev/null +++ b/cpp/ql/src/Critical/MissingNegativityTest.ql @@ -0,0 +1,65 @@ +/** + * @name Unchecked return value used as offset + * @description A return value from a function is used as a pointer offset before it is checked for being positive/negative. Integer values used as pointer offsets should be checked, especially if they are derived from user input. + * @kind problem + * @id cpp/missing-negativity-test + * @problem.severity warning + * @tags reliability + * external/cwe/cwe-823 + */ +import cpp +import Negativity + +class IntegralReturnValue extends FunctionCall +{ + IntegralReturnValue() { + this.getType().getUnderlyingType() instanceof IntegralType + } + + predicate isChecked() { + exists(ControlFlowNode def, ControlFlowNode test, Variable v | + exprDefinition(v, def, this) and + definitionReaches(def, test) and + errorSuccessor(v, test.getASuccessor())) + } +} + +class FunctionWithNegativeReturn extends Function +{ + FunctionWithNegativeReturn() { + this.getType().getUnderlyingType() instanceof IntegralType and + ( exists(ReturnStmt ret | + ret.getExpr().getValue().toInt() < 0 and + ret.getEnclosingFunction() = this) + or + count(IntegralReturnValue val | val.getTarget() = this and val.isChecked()) * 100 / + count(IntegralReturnValue val | val.getTarget() = this) >= 80) + } +} + +predicate dangerousUse(IntegralReturnValue val, Expr use) +{ + exists(ArrayExpr ae | ae.getArrayOffset() = val and use = val) + or + exists(LocalScopeVariable v, ControlFlowNode def, ArrayExpr ae | + exprDefinition(v, def, val) and + use = ae.getArrayOffset() and + not boundsChecked(v, use) and + definitionUsePair(v, def, use)) + or + (use.getParent().(AddExpr).getAnOperand() = val and + val = use and + use.getType().getUnderlyingType() instanceof PointerType) + or + exists(LocalScopeVariable v, ControlFlowNode def, AddExpr add | + exprDefinition(v, def, val) and + definitionUsePair(v, def, use) and + add.getAnOperand() = use and + not boundsChecked(v, use) and + add.getType().getUnderlyingType() instanceof PointerType) +} + +from FunctionWithNegativeReturn f, IntegralReturnValue val, Expr dangerous +where val.getTarget() = f + and dangerousUse(val, dangerous) +select dangerous, "Dangerous use of possibly negative value (return value of '" + f.getName() + "')." diff --git a/cpp/ql/src/Critical/MissingNullTest.cpp b/cpp/ql/src/Critical/MissingNullTest.cpp new file mode 100644 index 000000000000..074f9d285e93 --- /dev/null +++ b/cpp/ql/src/Critical/MissingNullTest.cpp @@ -0,0 +1,11 @@ +typedef struct { + char name[100]; + int status; +} person; + +void f() { + person* buf = NULL; + buf = malloc(sizeof(person)); + + (*buf).status = 0; //access to buf before it was checked for NULL +} diff --git a/cpp/ql/src/Critical/MissingNullTest.qhelp b/cpp/ql/src/Critical/MissingNullTest.qhelp new file mode 100644 index 000000000000..c586806fb899 --- /dev/null +++ b/cpp/ql/src/Critical/MissingNullTest.qhelp @@ -0,0 +1,28 @@ + + + + + +

    +This rule finds pointer dereferences that use a pointer returned from a function which may return NULL. Always +check your pointers for NULL-ness before dereferencing them. Dereferencing a null pointer and attempting to +modify its contents can lead to anything from a segfault to corrupting important system data +(i.e. the interrupt table in some architectures). +

    + +
    + +

    +Add a null check before dereferencing the pointer, or modify the function so that it always returns a non-null value. +

    + +
    + + + + + + +
    diff --git a/cpp/ql/src/Critical/MissingNullTest.ql b/cpp/ql/src/Critical/MissingNullTest.ql new file mode 100644 index 000000000000..dcd7ad7ddc16 --- /dev/null +++ b/cpp/ql/src/Critical/MissingNullTest.ql @@ -0,0 +1,15 @@ +/** + * @name Returned pointer not checked + * @description A value returned from a function that may return null is not tested to determine whether or not it is null. Dereferencing NULL pointers lead to undefined behavior. + * @kind problem + * @id cpp/missing-null-test + * @problem.severity recommendation + * @tags reliability + * external/cwe/cwe-476 + */ +import cpp + +from VariableAccess access +where maybeNull(access) + and dereferenced(access) +select access, "Value may be null; it should be checked before dereferencing." diff --git a/cpp/ql/src/Critical/Negativity.qll b/cpp/ql/src/Critical/Negativity.qll new file mode 100644 index 000000000000..35627c3a19cc --- /dev/null +++ b/cpp/ql/src/Critical/Negativity.qll @@ -0,0 +1,111 @@ +import cpp + +predicate valueOfVar(Variable v, Expr val) +{ + val = v.getAnAccess() or + val.(AssignExpr).getLValue() = v.getAnAccess() +} + +predicate boundsCheckExpr(Variable v, Expr cond) +{ + exists(EQExpr eq | cond = eq and + eq.getAnOperand().getValue() = "-1" and valueOfVar(v, eq.getAnOperand())) + or + exists(NEExpr ne | cond = ne and + ne.getAnOperand().getValue() = "-1" and valueOfVar(v, ne.getAnOperand())) + or + exists(LTExpr lt | cond = lt and + valueOfVar(v, lt.getAnOperand()) and exists(lt.getAnOperand().getValue())) + or + exists(LEExpr le | cond = le and + valueOfVar(v, le.getAnOperand()) and exists(le.getAnOperand().getValue())) + or + exists(GTExpr gt | cond = gt and + valueOfVar(v, gt.getAnOperand()) and exists(gt.getAnOperand().getValue())) + or + exists(GEExpr ge | cond = ge and + valueOfVar(v, ge.getAnOperand()) and exists(ge.getAnOperand().getValue())) +} + +predicate conditionalSuccessor(ControlFlowNode node, ControlFlowNode succ) +{ + if node.isCondition() then + (succ = node.getATrueSuccessor() or succ = node.getAFalseSuccessor()) + else + exists(BinaryLogicalOperation binop | + binop.getAnOperand() = node and conditionalSuccessor(binop, succ)) +} + +predicate boundsChecked(Variable v, ControlFlowNode node) +{ + exists(Expr test | + boundsCheckExpr(v, test) and + conditionalSuccessor(test, node)) + or + exists(ControlFlowNode mid | + boundsChecked(v, mid) and mid = node.getAPredecessor() and not definitionBarrier(v, mid)) +} + +predicate errorCondition(Variable v, Expr cond) +{ + exists(EQExpr eq | cond = eq and + eq.getAnOperand().getValue() = "-1" and eq.getAnOperand() = v.getAnAccess()) + or + exists(LTExpr lt | cond = lt and + lt.getLeftOperand() = v.getAnAccess() and lt.getRightOperand().getValue() = "0") + or + exists(LEExpr le | cond = le and + le.getRightOperand() = v.getAnAccess() and le.getRightOperand().getValue() = "-1") + or + exists(NotExpr ne | cond = ne and + successCondition(v, ne.getOperand())) +} + +predicate successCondition(Variable v, Expr cond) +{ + exists(NEExpr ne | cond = ne and + ne.getAnOperand().getValue() = "-1" and ne.getAnOperand() = v.getAnAccess()) + or + exists(GEExpr ge | cond = ge and + ge.getLeftOperand() = v.getAnAccess() and ge.getRightOperand().getValue() = "0") + or + exists(GTExpr gt | cond = gt and + gt.getRightOperand() = v.getAnAccess() and gt.getRightOperand().getValue() = "-1") + or + exists(NotExpr ne | cond = ne and + errorCondition(v, ne.getOperand())) +} + +predicate errorSuccessor(Variable v, ControlFlowNode n) +{ + exists(Expr cond | + (errorCondition(v, cond) and n = cond.getATrueSuccessor()) or + (successCondition(v, cond) and n = cond.getAFalseSuccessor())) +} + +predicate successSuccessor(Variable v, ControlFlowNode n) +{ + exists(Expr cond | + (successCondition(v, cond) and n = cond.getATrueSuccessor()) or + (errorCondition(v, cond) and n = cond.getAFalseSuccessor())) +} + +predicate checkedError(Variable v, ControlFlowNode n) +{ + errorSuccessor(v, n) + or + exists(ControlFlowNode mid | + checkedError(v, mid) and + n = mid.getASuccessor() and + not definitionBarrier(v, mid)) +} + +predicate checkedSuccess(Variable v, ControlFlowNode n) +{ + successSuccessor(v, n) + or + exists(ControlFlowNode mid | + checkedSuccess(v, mid) and + n = mid.getASuccessor() and + not definitionBarrier(v, mid)) +} diff --git a/cpp/ql/src/Critical/NewArrayDeleteMismatch.cpp b/cpp/ql/src/Critical/NewArrayDeleteMismatch.cpp new file mode 100644 index 000000000000..2ee7ed26732e --- /dev/null +++ b/cpp/ql/src/Critical/NewArrayDeleteMismatch.cpp @@ -0,0 +1,5 @@ +Record* record = new Record[SIZE]; + +... + +delete record; //record was created using 'new[]', but was freed using 'delete' diff --git a/cpp/ql/src/Critical/NewArrayDeleteMismatch.qhelp b/cpp/ql/src/Critical/NewArrayDeleteMismatch.qhelp new file mode 100644 index 000000000000..5b7992ce8287 --- /dev/null +++ b/cpp/ql/src/Critical/NewArrayDeleteMismatch.qhelp @@ -0,0 +1,84 @@ + + + + + +

    +This rule finds delete expressions that are using a pointer that points to memory +allocated using the new[] operator. This should be avoided since it +results in undefined behavior, as per §5.3.5 of the ISO/IEC C++ Standard: +

    + +
    +

    "In the first alternative (delete object), the value of the operand of delete may be a null pointer value, a pointer to +a non-array object created by a previous new-expression, or a pointer to a sub-object representing a base class of such an object. +If not, the behavior is undefined."

    +
    + +

    +Besides being formally undefined, there are two practical reasons why this is likely to cause defects. For the first of these, consider +what happens when invoking X *p = new X[23]: +

    + +
      +
    1. Sufficient memory is allocated to hold 23 instances of type X by invoking ::operator new(sizeof(X) * 23).
    2. +
    3. Each of the 23 instances of X is constructed at its correct place in memory (as if doing a placement new).
    4. +
    + +

    +If delete[] p is subsequently executed, the reverse happens: +

    + +
      +
    1. The destructor for each of the 23 instances of X is invoked (as if doing an explicit (p + i)->~X()).
    2. +
    3. The memory allocated by ::operator new is deallocated by invoking ::operator delete(p).
    4. +
    + +

    +By contrast, delete p (without the [] brackets) would generally assume that p points to exactly one instance +of X, and only call the destructor for that (although this behavior cannot be relied upon, since the results are formally undefined). +The practical result of this is that the destructors for the remaining X instances, which might do crucial things such as freeing +resources, will not be called. +

    + +

    +There is also a second practical reason why this may cause a defect. In order to call the destructors of the array elements when delete[] +is called, the implementation must know the size of array to which p points at deletion time. Bearing in mind that p is a pointer, +and carries no array size information in its type, this information would not in general be available unless the implementation somehow stores it when +new[] is invoked. There are two common ways in which this is done: +

    + +
      +
    • The most common approach is to allocate a small amount of extra memory (a header) before the start of the array and store the size in it. +When invoking delete[] p, the implementation then just needs to walk back a fixed amount from the passed-in pointer to read the size. +The implication of this is that p itself (the pointer to the first element in the array) is not the pointer returned by +::operator new, and so it is not safe to call ::operator delete on it. Instead, it should be called on a pointer that +points to the start of the header. Invoking delete p would use the wrong address, with potentially catastrophic results.
    • +
    • An alternative, less common, approach is to store a map from pointers to the sizes of the arrays (if any) to which they point. When +invoking delete[] p, the implementation looks up the pointer in the map, invokes the relevant number of destructors, deallocates +the memory and removes the pointer from the map. If delete p is called instead, not only will the relevant number of +destructors likely not be called (as previously noted), but the pointer will also likely not be removed from the map. In practical terms, this is +potentially less of a serious issue than that posed by the first approach, but it should still be avoided.
    • +
    + + + +
    + +

    +Use the delete[] operator when freeing memory allocated with new[]. +

    + +
    + + + + + + +
  • S. Meyers. Effective C++ 3d ed. pp 73-75. Addison-Wesley Professional, 2005.
  • +ISO/IEC 14882:2011, Information technology - Programming languages - C++ §5.3.5. International Organization for Standardization, Geneva, Switzerland, 2011.
  • +
    +
    diff --git a/cpp/ql/src/Critical/NewArrayDeleteMismatch.ql b/cpp/ql/src/Critical/NewArrayDeleteMismatch.ql new file mode 100644 index 000000000000..8cc7e9d8835f --- /dev/null +++ b/cpp/ql/src/Critical/NewArrayDeleteMismatch.ql @@ -0,0 +1,18 @@ +/** + * @name 'new[]' array freed with 'delete' + * @description An array allocated with 'new[]' is being freed using 'delete'. Behavior in such cases is undefined and should be avoided. Use 'delete[]' when freeing arrays allocated with 'new[]'. + * @kind problem + * @problem.severity warning + * @precision high + * @id cpp/new-array-delete-mismatch + * @tags reliability + */ +import NewDelete + +from Expr alloc, Expr free, Expr freed +where + allocReaches(freed, alloc, "new[]") and + freeExprOrIndirect(free, freed, "delete") +select + free, "This memory may have been allocated with '$@', not 'new'.", + alloc, "new[]" diff --git a/cpp/ql/src/Critical/NewDelete.qll b/cpp/ql/src/Critical/NewDelete.qll new file mode 100644 index 000000000000..b7e1aa3d89fe --- /dev/null +++ b/cpp/ql/src/Critical/NewDelete.qll @@ -0,0 +1,126 @@ +/** + * Provides predicates for associating new/malloc calls with delete/free. + */ +import cpp +import semmle.code.cpp.controlflow.SSA + +/** + * Holds if `alloc` is a use of `malloc` or `new`. `kind` is + * a string describing the type of the allocation. + */ +predicate allocExpr(Expr alloc, string kind) { + isAllocationExpr(alloc) and + ( + ( + alloc instanceof FunctionCall and + kind = "malloc" + ) or ( + alloc instanceof NewExpr and + kind = "new" and + + // exclude placement new and custom overloads as they + // may not conform to assumptions + not alloc.(NewExpr).getAllocatorCall().getTarget().getNumberOfParameters() > 1 + ) or ( + alloc instanceof NewArrayExpr and + kind = "new[]" and + + // exclude placement new and custom overloads as they + // may not conform to assumptions + not alloc.(NewArrayExpr).getAllocatorCall().getTarget().getNumberOfParameters() > 1 + ) + ) +} + +/** + * Holds if `alloc` is a use of `malloc` or `new`, or a function + * wrapping one of those. `kind` is a string describing the type + * of the allocation. + */ +predicate allocExprOrIndirect(Expr alloc, string kind) { + // direct alloc + allocExpr(alloc, kind) or + + exists(ReturnStmt rtn | + // indirect alloc via function call + alloc.(FunctionCall).getTarget() = rtn.getEnclosingFunction() and + ( + allocExprOrIndirect(rtn.getExpr(), kind) or + allocReaches(rtn.getExpr(), _, kind) + ) + ) +} + +/** + * Holds if `v` is a non-local variable which is assigned with + * memory allocation `alloc` only (it may also be assigned with + * NULL). `kind` is a string describing the type of that allocation. + */ +private predicate allocReachesVariable(Variable v, Expr alloc, string kind) { + exists(Expr mid | + allocReaches(mid, alloc, kind) and + v.getAnAssignedValue() = mid and + not v instanceof LocalScopeVariable and + count(Expr e | + v.getAnAssignedValue() = e and + not e.getValue().toInt() = 0 + ) = 1 + ) +} + +/** + * Holds if `e` is an expression which may evaluate to the + * result of a previous memory allocation `alloc`. `kind` is a + * string describing the type of that allocation. + */ +predicate allocReaches(Expr e, Expr alloc, string kind) { + ( + // alloc + allocExprOrIndirect(alloc, kind) and + e = alloc + ) or exists(SsaDefinition def, LocalScopeVariable v | + // alloc via SSA + allocReaches(def.getAnUltimateDefiningValue(v), alloc, kind) and + e = def.getAUse(v) + ) or exists(Variable v | + // alloc via a singly assigned global + allocReachesVariable(v, alloc, kind) and + e.(VariableAccess).getTarget() = v + ) +} + +/** + * Holds if `free` is a use of free or delete. `freed` is the + * expression that is freed / deleted and `kind` is a string + * describing the type of that free or delete. + */ +predicate freeExpr(Expr free, Expr freed, string kind) { + ( + freeCall(free, freed) and + kind = "free" + ) or ( + free.(DeleteExpr).getExpr() = freed and + kind = "delete" + ) or ( + free.(DeleteArrayExpr).getExpr() = freed and + kind = "delete[]" + ) +} + +/** + * Holds if `free` is a use of free or delete, or a function + * wrapping one of those. `freed` is the expression that is + * freed / deleted and `kind` is a string describing the type + * of that free or delete. + */ +predicate freeExprOrIndirect(Expr free, Expr freed, string kind) { + // direct free + freeExpr(free, freed, kind) or + + // indirect free via function call + exists(Expr internalFree, Expr internalFreed, int arg | + freeExprOrIndirect(internalFree, internalFreed, kind) and + free.(FunctionCall).getTarget().getParameter(arg) = internalFreed.(VariableAccess).getTarget() and + free.(FunctionCall).getArgument(arg) = freed + ) +} diff --git a/cpp/ql/src/Critical/NewDeleteArrayMismatch.cpp b/cpp/ql/src/Critical/NewDeleteArrayMismatch.cpp new file mode 100644 index 000000000000..31aaa24a8320 --- /dev/null +++ b/cpp/ql/src/Critical/NewDeleteArrayMismatch.cpp @@ -0,0 +1,5 @@ +Record *ptr = new Record(...); + +... + +delete [] ptr; // ptr was created using 'new', but was freed using 'delete[]' diff --git a/cpp/ql/src/Critical/NewDeleteArrayMismatch.qhelp b/cpp/ql/src/Critical/NewDeleteArrayMismatch.qhelp new file mode 100644 index 000000000000..ca039fecc041 --- /dev/null +++ b/cpp/ql/src/Critical/NewDeleteArrayMismatch.qhelp @@ -0,0 +1,38 @@ + + + + + +

    +This rule finds delete[] expressions that are using a pointer that points to memory +allocated using the new operator. Behavior in such cases is undefined and should +be avoided. +

    + +

    +The new operator allocates memory for just one object, then calls that object's constructor, and delete +does the opposite. The array delete[] operator, however, expects the pointer to be pointing to the first element of +an array (which could have header data specifying the length of the array) and would attempt to call the destructor on each +element of the 'array', which would likely lead to a segfault due to the invalid header data. +

    + + + +
    + +

    +Use the delete operator when freeing memory allocated with new. +

    + +
    + + + + + + +
  • S. Meyers. Effective C++ 3d ed. pp 73-75. Addison-Wesley Professional, 2005.
  • +
    +
    diff --git a/cpp/ql/src/Critical/NewDeleteArrayMismatch.ql b/cpp/ql/src/Critical/NewDeleteArrayMismatch.ql new file mode 100644 index 000000000000..e9de16423e30 --- /dev/null +++ b/cpp/ql/src/Critical/NewDeleteArrayMismatch.ql @@ -0,0 +1,18 @@ +/** + * @name 'new' object freed with 'delete[]' + * @description An object that was allocated with 'new' is being freed using 'delete[]'. Behavior in such cases is undefined and should be avoided. Use 'delete' instead. + * @kind problem + * @problem.severity warning + * @precision high + * @id cpp/new-delete-array-mismatch + * @tags reliability + */ +import NewDelete + +from Expr alloc, Expr free, Expr freed +where + allocReaches(freed, alloc, "new") and + freeExprOrIndirect(free, freed, "delete[]") +select + free, "This memory may have been allocated with '$@', not 'new[]'.", + alloc, "new" diff --git a/cpp/ql/src/Critical/NewFreeMismatch.cpp b/cpp/ql/src/Critical/NewFreeMismatch.cpp new file mode 100644 index 000000000000..4a359c0ffa53 --- /dev/null +++ b/cpp/ql/src/Critical/NewFreeMismatch.cpp @@ -0,0 +1,5 @@ +Record *ptr = new Record(...); + +... + +free(ptr); // BAD: ptr was created using 'new', but is being freed using 'free' diff --git a/cpp/ql/src/Critical/NewFreeMismatch.qhelp b/cpp/ql/src/Critical/NewFreeMismatch.qhelp new file mode 100644 index 000000000000..919669168a4f --- /dev/null +++ b/cpp/ql/src/Critical/NewFreeMismatch.qhelp @@ -0,0 +1,30 @@ + + + + + +

    +This rule finds delete expressions whose argument is a pointer that points to memory +allocated using the malloc function, and calls to free whose argument is a +pointer that points to memory allocated using the new operator. Behavior in such cases +is undefined and should be avoided. +

    + +
    + +

    +Use the delete operator when freeing memory allocated with new, and the +free function when freeing memory allocated with malloc. +

    + +
    + + + + +
  • isocpp.org 'Standard C++', "Can I free() pointers allocated with new? Can I delete pointers allocated with malloc()?"
  • +
  • Wikipedia, "Relation to malloc and free" in new and delete (C++).
  • +
    +
    diff --git a/cpp/ql/src/Critical/NewFreeMismatch.ql b/cpp/ql/src/Critical/NewFreeMismatch.ql new file mode 100644 index 000000000000..a44bb14792f9 --- /dev/null +++ b/cpp/ql/src/Critical/NewFreeMismatch.ql @@ -0,0 +1,37 @@ +/** + * @name Mismatching new/free or malloc/delete + * @description An object that was allocated with 'malloc' or 'new' is being freed using a mismatching 'free' or 'delete'. + * @kind problem + * @problem.severity warning + * @precision high + * @id cpp/new-free-mismatch + * @tags reliability + * security + * external/cwe/cwe-401 + */ +import NewDelete + +/** + * Holds if `allocKind` and `freeKind` indicate corresponding + * types of allocation and free. + */ +predicate correspondingKinds(string allocKind, string freeKind) { + ( + allocKind = "malloc" and + freeKind = "free" + ) or ( + allocKind = "new" and + freeKind = "delete" + ) +} + +from + Expr alloc, string allocKind, string allocKindSimple, + Expr free, Expr freed, string freeKind, string freeKindSimple +where + allocReaches(freed, alloc, allocKind) and + freeExprOrIndirect(free, freed, freeKind) and + allocKindSimple = allocKind.replaceAll("[]", "") and + freeKindSimple = freeKind.replaceAll("[]", "") and + not correspondingKinds(allocKindSimple, freeKindSimple) +select free, "There is a " + allocKindSimple + "/" + freeKindSimple + " mismatch between this " + freeKind + " and the corresponding $@.", alloc, allocKind diff --git a/cpp/ql/src/Critical/NotInitialised.cpp b/cpp/ql/src/Critical/NotInitialised.cpp new file mode 100644 index 000000000000..7d0e6b8f9bec --- /dev/null +++ b/cpp/ql/src/Critical/NotInitialised.cpp @@ -0,0 +1,6 @@ +{ + int i; + + ... + int g = COEFF * i; //i is used before it is initialized +} diff --git a/cpp/ql/src/Critical/NotInitialised.qhelp b/cpp/ql/src/Critical/NotInitialised.qhelp new file mode 100644 index 000000000000..b9be541b606a --- /dev/null +++ b/cpp/ql/src/Critical/NotInitialised.qhelp @@ -0,0 +1,27 @@ + + + + + +

    +This rule finds variables that are used before they are initialized. Values of uninitialized variables are +undefined, as not all compilers zero out memory, especially when optimizations are enabled or the compiler +is not compliant with the latest language standards. +

    + +
    + +

    +Initialize the variable before accessing it. +

    + +
    + + + + + + +
    diff --git a/cpp/ql/src/Critical/NotInitialised.ql b/cpp/ql/src/Critical/NotInitialised.ql new file mode 100644 index 000000000000..3de13c786b87 --- /dev/null +++ b/cpp/ql/src/Critical/NotInitialised.ql @@ -0,0 +1,63 @@ +/** + * @name Variable not initialized before use + * @description A variable is used before initialized. The value of a variable is undefined before initialization, and its use should be avoided. + * @kind problem + * @id cpp/not-initialised + * @problem.severity error + * @tags reliability + * external/cwe/cwe-457 + */ +import cpp + +// This query is the JSF version +// +// (see also InitialisationNotRun.ql and GlobalUseBeforeInit.ql) + +// Holds if s defines variable v (conservative) +predicate defines(ControlFlowNode s, Variable lv) { + exists(VariableAccess va | va = s and va.getTarget() = lv and va.isUsedAsLValue()) +} + +// Holds if s uses variable v (conservative) +predicate uses(ControlFlowNode s, Variable lv) { + exists(VariableAccess va | va = s and va.getTarget() = lv and va.isRValue() + and not va.getParent+() instanceof SizeofOperator) +} + +// Holds if there is a path from the declaration of lv to n such that lv is +// definitely not defined before n +predicate noDefPath(LocalVariable lv, ControlFlowNode n) { + n.(DeclStmt).getADeclaration() = lv and not exists(lv.getInitializer()) + or exists(ControlFlowNode p | noDefPath(lv, p) and n = p.getASuccessor() and not defines(p, lv)) +} + +predicate isAggregateType(Type t) { + t instanceof Class or t instanceof ArrayType +} + +// Holds if va is a use of a local variable that has not been previously +// defined +predicate undefinedLocalUse(VariableAccess va) { + exists(LocalVariable lv | + // it is hard to tell when a struct or array has been initialized, so we + // ignore them + not isAggregateType(lv.getUnderlyingType()) and + not lv.getType().hasName("va_list") and + va = lv.getAnAccess() and + noDefPath(lv, va) and + uses(va, lv)) +} + +// Holds if gv is a potentially uninitialized global variable +predicate uninitialisedGlobal(GlobalVariable gv) { + exists(VariableAccess va | + not isAggregateType(gv.getUnderlyingType()) and + va = gv.getAnAccess() and + va.isRValue() and + not gv.hasInitializer() and + not gv.hasSpecifier("extern")) +} + +from Element elt +where undefinedLocalUse(elt) or uninitialisedGlobal(elt) +select elt, "Variable '" + elt.toString() + "' is not initialized." diff --git a/cpp/ql/src/Critical/OverflowCalculated.cpp b/cpp/ql/src/Critical/OverflowCalculated.cpp new file mode 100644 index 000000000000..8e9b7511283c --- /dev/null +++ b/cpp/ql/src/Critical/OverflowCalculated.cpp @@ -0,0 +1,10 @@ +void f(char* string) { + // wrong: allocates space for characters, put not zero terminator + char* buf = malloc(strlen(string)); + strcpy(buf, string); + + char* buf_right = malloc(strlen(string) + 1); //correct: includes the zero terminator + strcpy(buf_right, string); + // buf_right is now full + strcat(buf_right, string); //wrong: appending would overflow the buffer +} diff --git a/cpp/ql/src/Critical/OverflowCalculated.qhelp b/cpp/ql/src/Critical/OverflowCalculated.qhelp new file mode 100644 index 000000000000..7cc1691d89b6 --- /dev/null +++ b/cpp/ql/src/Critical/OverflowCalculated.qhelp @@ -0,0 +1,41 @@ + + + + + +

    +This rule finds malloc that use a strlen for the size but to not take the +zero terminator into consideration, and strcat/strncat calls that are done on buffers that do +not have the sufficient size to contain the new string. +

    + +

    +The indicated expression will cause a buffer overflow due to a buffer that is of insufficient size to contain +the data being copied. Buffer overflows can result to anything from a segfault to a security vulnerability (particularly +if the array is on stack-allocated memory). +

    + + + +
    + +

    +Increase the size of the buffer being allocated. +

    + +
    + + + + + + + +
  • CWE-131: Incorrect Calculation of Buffer Size
  • +
  • I. Gerg. An Overview and Example of the Buffer-Overflow Exploit. IANewsletter vol 7 no 4. 2005.
  • +
  • M. Donaldson. Inside the Buffer Overflow Attack: Mechanism, Method & Prevention. SANS Institute InfoSec Reading Room. 2002.
  • + +
    +
    diff --git a/cpp/ql/src/Critical/OverflowCalculated.ql b/cpp/ql/src/Critical/OverflowCalculated.ql new file mode 100644 index 000000000000..4b5d1c14d7f6 --- /dev/null +++ b/cpp/ql/src/Critical/OverflowCalculated.ql @@ -0,0 +1,51 @@ +/** + * @name Buffer not sufficient for string + * @description A buffer allocated using 'malloc' may not have enough space for a string that is being copied into it. The operation can cause a buffer overrun. Make sure that the buffer contains enough room for the string (including the zero terminator). + * @kind problem + * @id cpp/overflow-calculated + * @problem.severity warning + * @tags reliability + * external/cwe/cwe-131 + * external/cwe/cwe-120 + */ +import cpp + +class MallocCall extends FunctionCall +{ + MallocCall() { this.getTarget().hasQualifiedName("malloc") } + + Expr getAllocatedSize() { + if this.getArgument(0) instanceof VariableAccess then + exists(LocalScopeVariable v, ControlFlowNode def | + definitionUsePair(v, def, this.getArgument(0)) and + exprDefinition(v, def, result)) + else + result = this.getArgument(0) + } +} + +predicate terminationProblem(MallocCall malloc, string msg) +{ + malloc.getAllocatedSize() instanceof StrlenCall and + msg = "This allocation does not include space to null-terminate the string." +} + +predicate spaceProblem(FunctionCall append, string msg) +{ + exists(MallocCall malloc, StrlenCall strlen, AddExpr add, FunctionCall insert, Variable buffer | + add.getAChild() = strlen + and exists(add.getAChild().getValue()) + and malloc.getAllocatedSize() = add + and buffer.getAnAccess() = strlen.getStringExpr() + and (insert.getTarget().hasQualifiedName("strcpy") or insert.getTarget().hasQualifiedName("strncpy")) + and (append.getTarget().hasQualifiedName("strcat") or append.getTarget().hasQualifiedName("strncat")) + and malloc.getASuccessor+() = insert + and insert.getArgument(1) = buffer.getAnAccess() + and insert.getASuccessor+() = append + and msg = "This buffer only contains enough room for '" + buffer.getName() + + "' (copied on line " + insert.getLocation().getStartLine().toString() + ")") +} + +from Expr problem, string msg +where terminationProblem(problem, msg) or spaceProblem(problem, msg) +select problem, msg diff --git a/cpp/ql/src/Critical/OverflowDestination.cpp b/cpp/ql/src/Critical/OverflowDestination.cpp new file mode 100644 index 000000000000..d6413231bbf0 --- /dev/null +++ b/cpp/ql/src/Critical/OverflowDestination.cpp @@ -0,0 +1,13 @@ + +int main(int argc, char* argv[]) { + char param[SIZE]; + + char arg1[10]; + char arg2[20]; + + //wrong: only uses the size of the source (argv[1]) when using strncpy + strncpy(param, argv[1], strlen(arg1)); + + //correct: uses the size of the destination array as well + strncpy(param, argv[1], min(strlen(arg1, sizeof(param) -1))); +} diff --git a/cpp/ql/src/Critical/OverflowDestination.qhelp b/cpp/ql/src/Critical/OverflowDestination.qhelp new file mode 100644 index 000000000000..e8a7e6ef322a --- /dev/null +++ b/cpp/ql/src/Critical/OverflowDestination.qhelp @@ -0,0 +1,29 @@ + + + +

    The bounded copy functions memcpy, memmove, strncpy, strncat accept a size argument. You should call these functions with a size argument that is derived from the size of the destination buffer. Using a size argument that is derived from the source buffer may cause a buffer overflow. Buffer overflows can lead to anything from a segmentation fault to a security vulnerability.

    + + +
    + +

    Check the highlighted function calls carefully, and ensure that the size parameter is derived from the size of the destination buffer, +not the source buffer.

    + + + +
    + + + + + + + +
  • CWE-119: Improper Restriction of Operations within the Bounds of a Memory Buffer
  • +
  • I. Gerg. An Overview and Example of the Buffer-Overflow Exploit. IANewsletter vol 7 no 4. 2005.
  • +
  • M. Donaldson. Inside the Buffer Overflow Attack: Mechanism, Method & Prevention. SANS Institute InfoSec Reading Room. 2002.
  • + +
    +
    diff --git a/cpp/ql/src/Critical/OverflowDestination.ql b/cpp/ql/src/Critical/OverflowDestination.ql new file mode 100644 index 000000000000..1c919b9996a2 --- /dev/null +++ b/cpp/ql/src/Critical/OverflowDestination.ql @@ -0,0 +1,78 @@ +/** + * @name Copy function using source size + * @description Calling a copy operation with a size derived from the source + * buffer instead of the destination buffer may result in a buffer overflow + * @kind problem + * @id cpp/overflow-destination + * @problem.severity warning + * @tags reliability + * external/cwe/cwe-119 + * external/cwe/cwe-131 + */ +import cpp +import semmle.code.cpp.pointsto.PointsTo + +predicate sourceSized(FunctionCall fc) +{ + exists(string name | + (name = "strncpy" or name = "strncat" or name = "memcpy" or name = "memmove") and + fc.getTarget().hasQualifiedName(name)) + and + exists(Expr dest, Expr src, Expr size, Variable v | + fc.getArgument(0) = dest and fc.getArgument(1) = src and fc.getArgument(2) = size and + src = v.getAnAccess() and size.getAChild+() = v.getAnAccess() and + not exists(Variable other | + dest = other.getAnAccess() and size.getAChild+() = other.getAnAccess()) + and + not exists(ArrayType srctype, ArrayType desttype | + dest.getType().getUnderlyingType() = desttype and + src.getType().getUnderlyingType() = srctype and + desttype.getBaseType().getUnderlyingType() = srctype.getBaseType().getUnderlyingType() and + desttype.getArraySize() = srctype.getArraySize())) +} + +class VulnerableArgument extends PointsToExpr +{ + VulnerableArgument() { sourceSized(this.getParent()) } + override predicate interesting() { sourceSized(this.getParent()) } +} + +predicate taintingFunction(Function f, int buf) +{ + (f.hasQualifiedName("read") and buf = 1) or + (f.hasQualifiedName("fgets") and buf = 0) or + (f.hasQualifiedName("fread") and buf = 0) +} + +// Taint `argv[i]`, for all i, but also `*argv`, etc. +predicate commandLineArg(Expr e) +{ + exists(Function f, Parameter argv, VariableAccess access | + f.hasQualifiedName("main") and f.getParameter(1) = argv and + argv.getAnAccess() = access and access.isRValue() and + pointer(access, e)) +} + +predicate tainted(Expr e) +{ + exists(FunctionCall fc, int arg | + taintingFunction(fc.getTarget(), arg) and + e = fc.getArgument(arg)) + or + e.(FunctionCall).getTarget().hasQualifiedName("getenv") + or + commandLineArg(e) +} + +class TaintedArgument extends PointsToExpr +{ + TaintedArgument() { tainted(this) } + override predicate interesting() { tainted(this) } +} + +from FunctionCall fc, VulnerableArgument vuln, TaintedArgument tainted +where sourceSized(fc) + and fc.getArgument(1) = vuln + and vuln.pointsTo() = tainted.pointsTo() + and vuln.confidence() > 0.01 +select fc, "To avoid overflow, this operation should be bounded by destination-buffer size, not source-buffer size." diff --git a/cpp/ql/src/Critical/OverflowStatic.cpp b/cpp/ql/src/Critical/OverflowStatic.cpp new file mode 100644 index 000000000000..8f2af91d6248 --- /dev/null +++ b/cpp/ql/src/Critical/OverflowStatic.cpp @@ -0,0 +1,11 @@ +#define SIZE 30 + +int f(char * s) { + char buf[20]; //buf not set to use SIZE macro + + strncpy(buf, s, SIZE); //wrong: copy may exceed size of buf + + for (int i = 0; i < SIZE; i++) { //wrong: upper limit that is higher than array size + cout << array[i]; + } +} diff --git a/cpp/ql/src/Critical/OverflowStatic.qhelp b/cpp/ql/src/Critical/OverflowStatic.qhelp new file mode 100644 index 000000000000..30f71f4e1464 --- /dev/null +++ b/cpp/ql/src/Critical/OverflowStatic.qhelp @@ -0,0 +1,29 @@ + + + +

    When you use static arrays you must ensure that you do not exceed the size of the array during +write and access operations. If an operation attempts to write to or access an element that is +outside the range of the array then this results in a buffer overflow. Buffer overflows can lead to anything from a segmentation fault to a security vulnerability. +

    + +
    + +

    +Check the offsets and sizes used in the highlighted operations to ensure that a buffer overflow will not occur. +

    + +
    + + + + + + + +
  • I. Gerg. An Overview and Example of the Buffer-Overflow Exploit. IANewsletter vol 7 no 4. 2005.
  • +
  • M. Donaldson. Inside the Buffer Overflow Attack: Mechanism, Method & Prevention. SANS Institute InfoSec Reading Room. 2002.
  • + +
    +
    diff --git a/cpp/ql/src/Critical/OverflowStatic.ql b/cpp/ql/src/Critical/OverflowStatic.ql new file mode 100644 index 000000000000..e8503d3a25da --- /dev/null +++ b/cpp/ql/src/Critical/OverflowStatic.ql @@ -0,0 +1,119 @@ +/** + * @name Static array access may cause overflow + * @description Exceeding the size of a static array during write or access operations + * may result in a buffer overflow. + * @kind problem + * @problem.severity warning + * @precision medium + * @id cpp/static-buffer-overflow + * @tags reliability + * security + * external/cwe/cwe-119 + * external/cwe/cwe-131 + */ +import cpp +import semmle.code.cpp.commons.Buffer +import LoopBounds + +private predicate staticBufferBase(VariableAccess access, Variable v) +{ + v.getType().(ArrayType).getBaseType() instanceof CharType and + access = v.getAnAccess() and + not memberMayBeVarSize(_, v) +} + +predicate staticBuffer(VariableAccess access, Variable v, int size) +{ + staticBufferBase(access, v) and + size = getBufferSize(access, _) +} + +class BufferAccess extends ArrayExpr +{ + BufferAccess() { + exists(int size | + staticBuffer(this.getArrayBase(), _, size) and + size != 0 + ) and + + // exclude accesses in macro implementation of `strcmp`, + // which are carefully controlled but can look dangerous. + not exists(Macro m | + m.getName() = "strcmp" and + m.getAnInvocation().getAnExpandedElement() = this + ) + } + + int bufferSize() { + staticBuffer(this.getArrayBase(), _, result) + } + + Variable buffer() { + result.getAnAccess() = this.getArrayBase() + } +} + +predicate overflowOffsetInLoop(BufferAccess bufaccess, string msg) +{ + exists(ClassicForLoop loop | + loop.getStmt().getAChild*() = bufaccess.getEnclosingStmt() and + loop.limit() >= bufaccess.bufferSize() and + loop.counter().getAnAccess() = bufaccess.getArrayOffset() and + msg = "Potential buffer-overflow: counter '" + loop.counter().toString() + + "' <= " + loop.limit().toString() + " but '" + bufaccess.buffer().getName() + + "' has " + bufaccess.bufferSize().toString() + " elements.") +} + +predicate bufferAndSizeFunction(Function f, int buf, int size) +{ + (f.hasQualifiedName("read") and buf = 1 and size = 2) or + (f.hasQualifiedName("fgets") and buf = 0 and size = 1) or + (f.hasQualifiedName("strncpy") and buf = 0 and size = 2) or + (f.hasQualifiedName("strncat") and buf = 0 and size = 2) or + (f.hasQualifiedName("memcpy") and buf = 0 and size = 2) or + (f.hasQualifiedName("memmove") and buf = 0 and size = 2) or + (f.hasQualifiedName("snprintf") and buf = 0 and size = 1) or + (f.hasQualifiedName("vsnprintf") and buf = 0 and size = 1) +} + +class CallWithBufferSize extends FunctionCall +{ + CallWithBufferSize() { bufferAndSizeFunction(this.getTarget(), _, _) } + Expr buffer() { + exists(int i | + bufferAndSizeFunction(this.getTarget(), i, _) and + result = this.getArgument(i)) + } + Expr statedSize() { + exists(int i | + bufferAndSizeFunction(this.getTarget(), _, i) and + result = this.getArgument(i)) + } +} + +predicate wrongBufferSize(Expr error, string msg) { + exists(CallWithBufferSize call, int bufsize, Variable buf | + staticBuffer(call.buffer(), buf, bufsize) and + call.statedSize().getValue().toInt() > bufsize and + error = call.statedSize() and + msg = "Potential buffer-overflow: '" + buf.getName() + + "' has size " + bufsize.toString() + " not " + call.statedSize().getValue() + ".") +} + +predicate outOfBounds(BufferAccess bufaccess, string msg) +{ + exists(int size, int access, string buf | + buf = bufaccess.buffer().getName() and + bufaccess.bufferSize() = size and + bufaccess.getArrayOffset().getValue().toInt() = access and + ( access > size or + (access = size and not exists(AddressOfExpr addof | bufaccess = addof.getOperand()))) and + msg = "Potential buffer-overflow: '" + buf + "' has size " + size.toString() + + " but '" + buf + "[" + access.toString() + "]' is accessed here.") +} + +from Element error, string msg +where overflowOffsetInLoop(error, msg) + or wrongBufferSize(error, msg) + or outOfBounds(error, msg) +select error, msg diff --git a/cpp/ql/src/Critical/ReturnStackAllocatedObject.cpp b/cpp/ql/src/Critical/ReturnStackAllocatedObject.cpp new file mode 100644 index 000000000000..00dfa215b337 --- /dev/null +++ b/cpp/ql/src/Critical/ReturnStackAllocatedObject.cpp @@ -0,0 +1,7 @@ +Record* fixRecord(Record* r) { + Record myRecord = *r; + delete r; + + myRecord.fix(); + return &myRecord; //returns reference to myRecord, which is a stack-allocated object +} diff --git a/cpp/ql/src/Critical/ReturnStackAllocatedObject.qhelp b/cpp/ql/src/Critical/ReturnStackAllocatedObject.qhelp new file mode 100644 index 000000000000..ee41612d5bde --- /dev/null +++ b/cpp/ql/src/Critical/ReturnStackAllocatedObject.qhelp @@ -0,0 +1,32 @@ + + + + + +

    +This rule finds return statements that return pointers to an object allocated on the stack. +The lifetime of a stack allocated memory location only lasts until the function returns, and +the contents of that memory become undefined after that. Clearly, using a pointer to stack +memory after the function has already returned will have undefined results. +

    + + + +
    + +

    +Do not return pointers to stack memory locations. Instead, create an output parameter, or create a heap-allocated +buffer, copy the contents of the stack allocated memory to that buffer and return that instead. +

    + +
    + + + + + + + +
    diff --git a/cpp/ql/src/Critical/ReturnStackAllocatedObject.ql b/cpp/ql/src/Critical/ReturnStackAllocatedObject.ql new file mode 100644 index 000000000000..d398de051742 --- /dev/null +++ b/cpp/ql/src/Critical/ReturnStackAllocatedObject.ql @@ -0,0 +1,35 @@ +/** + * @name Pointer to stack object used as return value + * @description A function has returned a pointer to an object allocated on + * the stack. The lifetime of stack allocated memory ends when the + * stack frame of the function that allocated it is popped off the + * stack. Any pointer to memory in a function call's stack frame + * will be a dangling pointer after the function returns. + * @kind problem + * @id cpp/return-stack-allocated-object + * @problem.severity warning + * @tags reliability + * external/cwe/cwe-562 + */ +import semmle.code.cpp.pointsto.PointsTo + +class ReturnPointsToExpr extends PointsToExpr +{ + override predicate interesting() { + exists(ReturnStmt ret | ret.getExpr().getFullyConverted() = this) + and pointerValue(this) + } + + ReturnStmt getReturnStmt() { result.getExpr().getFullyConverted() = this } +} + +from ReturnPointsToExpr ret, LocalVariable local, float confidence +where ret.pointsTo() = local + and ret.getReturnStmt().getEnclosingFunction() = local.getFunction() + and not(local.isStatic()) + and confidence = ret.confidence() + and confidence > 0.01 +select ret, + "This may return a pointer to '" + local.getName() + + "' (declared on line " + local.getADeclarationLocation().getStartLine().toString() + + "), which is stack allocated." diff --git a/cpp/ql/src/Critical/ReturnValueIgnored.cpp b/cpp/ql/src/Critical/ReturnValueIgnored.cpp new file mode 100644 index 000000000000..d68d5021bbaa --- /dev/null +++ b/cpp/ql/src/Critical/ReturnValueIgnored.cpp @@ -0,0 +1,23 @@ +int doFoo() { + ... + return status; +} + +void f() { + if (doFoo() == OK) { + ... + } +} + +void g() { + int status = doFoo(); + if (status == OK) { + ... + } +} + +void err() { + doFoo(); //doFoo is called but its return value is not checked, and + //the value is checked in other locations + ... +} diff --git a/cpp/ql/src/Critical/ReturnValueIgnored.qhelp b/cpp/ql/src/Critical/ReturnValueIgnored.qhelp new file mode 100644 index 000000000000..26d0f5c6e848 --- /dev/null +++ b/cpp/ql/src/Critical/ReturnValueIgnored.qhelp @@ -0,0 +1,47 @@ + + + + + +

    +This rule finds calls to a function that ignore the return value. A function call is only marked +as a violation if at least 80% of the total calls to that function check the return value. Not +checking a return value is a common source of defects from standard library functions like malloc or fread. +These functions return the status information and the return values should always be checked +to see if the operation succeeded before operating on any data modified or resources allocated by these functions. +

    + +

    +This rule uses a white/blacklist of functions the value of which can always be ignored (e.g. select) and those +that should always be checked (e.g. fgets). These list can be modified to suit a particular codebase. +

    + +
    + +

    +Check the return value of functions that return status information. +

    + +
    + + + + + + +
  • + M. Henricson and E. Nyquist, Industrial Strength C++, Chapter 12: Error handling. Prentice Hall PTR, 1997 (available online). +
  • +
  • + The CERT C Secure Coding Standard: EXP32-PL. Do not ignore function return values. +
  • + + + + + + +
    +
    diff --git a/cpp/ql/src/Critical/ReturnValueIgnored.ql b/cpp/ql/src/Critical/ReturnValueIgnored.ql new file mode 100644 index 000000000000..c9a456a3fb2f --- /dev/null +++ b/cpp/ql/src/Critical/ReturnValueIgnored.ql @@ -0,0 +1,43 @@ +/** + * @name Return value of a function is ignored + * @description A call to a function ignores its return value, but more than 80% of the total number of calls to the function check the return value. Check the return value of functions consistently, especially for functions like 'fread' or the 'scanf' functions that return the status of the operation. + * @kind problem + * @id cpp/return-value-ignored + * @problem.severity recommendation + * @precision medium + * @tags reliability + * correctness + * external/cwe/cwe-252 + */ +import cpp + +predicate unused(Expr e) +{ + e instanceof ExprInVoidContext +} + +predicate important(Function f, string message) +{ + message = "the result of this function must always be checked." and + getOptions().alwaysCheckReturnValue(f) +} + +// statistically dubious ignored return values +predicate dubious(Function f, string message) +{ + not important(f, _) and + exists(Options opts, int used, int total, int percentage | + used = count(FunctionCall fc | fc.getTarget() = f and not opts.okToIgnoreReturnValue(fc) and not unused(fc)) and + total = count(FunctionCall fc | fc.getTarget() = f and not opts.okToIgnoreReturnValue(fc)) and + used != total and + percentage = used * 100 / total and + percentage >= 90 and + message = percentage.toString() + "% of calls to this function have their result used.") +} + +from FunctionCall unused, string message +where unused(unused) + and not exists(Options opts | opts.okToIgnoreReturnValue(unused)) + and (important(unused.getTarget(), message) or dubious(unused.getTarget(), message)) + and not unused.getTarget().getName().matches("operator%") // exclude user defined operators +select unused, "Result of call to " + unused.getTarget().getName() + " is ignored; " + message diff --git a/cpp/ql/src/Critical/SizeCheck.cpp b/cpp/ql/src/Critical/SizeCheck.cpp new file mode 100644 index 000000000000..6400e9d4fb54 --- /dev/null +++ b/cpp/ql/src/Critical/SizeCheck.cpp @@ -0,0 +1,10 @@ +#define RECORD_SIZE 30 //incorrect or outdated size for record +typedef struct { + char name[30]; + int status; +} Record; + +void f() { + Record* p = malloc(RECORD_SIZE); //not of sufficient size to hold a Record + ... +} diff --git a/cpp/ql/src/Critical/SizeCheck.qhelp b/cpp/ql/src/Critical/SizeCheck.qhelp new file mode 100644 index 000000000000..38a35f90a25b --- /dev/null +++ b/cpp/ql/src/Critical/SizeCheck.qhelp @@ -0,0 +1,33 @@ + + + +

    When you allocate an array from memory using malloc, calloc or +realloc, you should ensure that you allocate enough memory to contain an +instance of the required pointer type. Calls that are assigned to a non-void pointer +variable, but do not allocate enough memory will cause a buffer overflow when a field accessed +on the pointer points to memory that is beyond the allocated array. Buffer overflows can lead +to anything from a segmentation fault to a security vulnerability.

    + +
    + +

    +The highlighted call allocates memory that is too small to contain an instance of the type of +the pointer, which can cause a memory overrun. Use the sizeof operator to ensure +that the function call allocates enough memory for that type. +

    + +
    + + + + + + + +
  • I. Gerg. An Overview and Example of the Buffer-Overflow Exploit. IANewsletter vol 7 no 4. 2005.
  • +
  • M. Donaldson. Inside the Buffer Overflow Attack: Mechanism, Method & Prevention. SANS Institute InfoSec Reading Room. 2002.
  • + +
    +
    diff --git a/cpp/ql/src/Critical/SizeCheck.ql b/cpp/ql/src/Critical/SizeCheck.ql new file mode 100644 index 000000000000..efbd39897b79 --- /dev/null +++ b/cpp/ql/src/Critical/SizeCheck.ql @@ -0,0 +1,66 @@ +/** + * @name Not enough memory allocated for pointer type + * @description Calling 'malloc', 'calloc' or 'realloc' without allocating enough memory to contain + * an instance of the type of the pointer may result in a buffer overflow + * @kind problem + * @problem.severity warning + * @precision medium + * @id cpp/allocation-too-small + * @tags reliability + * security + * external/cwe/cwe-131 + * external/cwe/cwe-122 + */ +import cpp + +class Allocation extends FunctionCall +{ + Allocation() { + exists(string name | + this.getTarget().hasQualifiedName(name) and + (name = "malloc" or name = "calloc" or name = "realloc")) + } + + string getName() { result = this.getTarget().getQualifiedName() } + + int getSize() { + (this.getName() = "malloc" and + this.getArgument(0).getValue().toInt() = result) + or + (this.getName() = "realloc" and + this.getArgument(1).getValue().toInt() = result) + or + (this.getName() = "calloc" and + result = + this.getArgument(0).getValue().toInt() * + this.getArgument(1).getValue().toInt()) + } +} + +predicate baseType(Allocation alloc, Type base) +{ + exists(PointerType pointer | + pointer.getBaseType() = base and + ( + exists(AssignExpr assign | + assign.getRValue() = alloc and assign.getLValue().getType() = pointer) + or + exists(Variable v | + v.getInitializer().getExpr() = alloc and v.getType() = pointer) + ) + ) +} + +predicate decideOnSize(Type t, int size) +{ + // If the codebase has more than one type with the same name, it can have more than one size. + size = min(t.getSize()) +} + +from Allocation alloc, Type base, int basesize, int allocated +where baseType(alloc, base) + and allocated = alloc.getSize() + and decideOnSize(base, basesize) + and basesize > allocated +select alloc, "Type '" + base.getName() + "' is " + basesize.toString() + + " bytes, but only " + allocated.toString() + " bytes are allocated." diff --git a/cpp/ql/src/Critical/SizeCheck2.cpp b/cpp/ql/src/Critical/SizeCheck2.cpp new file mode 100644 index 000000000000..8ac6894ef288 --- /dev/null +++ b/cpp/ql/src/Critical/SizeCheck2.cpp @@ -0,0 +1,11 @@ +#define RECORD_SIZE 30 //incorrect or outdated size for record +typedef struct { + char name[30]; + int status; +} Record; + +void f() { + Record* p = malloc(RECORD_SIZE * 4); //wrong: not a multiple of the size of Record + p[3].status = 1; //will most likely segfault + ... +} diff --git a/cpp/ql/src/Critical/SizeCheck2.qhelp b/cpp/ql/src/Critical/SizeCheck2.qhelp new file mode 100644 index 000000000000..37db77069db2 --- /dev/null +++ b/cpp/ql/src/Critical/SizeCheck2.qhelp @@ -0,0 +1,31 @@ + + + +

    When you allocate an array from memory using malloc, calloc or +realloc, you should ensure that you allocate enough memory to contain a +multiple of the size of the required pointer type. Calls that are assigned to a non-void +pointer variable, but do not allocate enough memory will cause a buffer overflow when a +field accessed on the pointer points to memory that is beyond the allocated array. Buffer +overflows can lead to anything from a segmentation fault to a security vulnerability.

    + +
    + +

    +The highlighted call allocates memory that is not a multiple of the size of the pointer +type, which can cause a memory overrun. Use the sizeof operator to ensure that the function call allocates enough memory for that type. +

    + +
    + + + + + + +
  • I. Gerg. An Overview and Example of the Buffer-Overflow Exploit. IANewsletter vol 7 no 4. 2005.
  • +
  • M. Donaldson. Inside the Buffer Overflow Attack: Mechanism, Method & Prevention. SANS Institute InfoSec Reading Room. 2002.
  • + +
    +
    diff --git a/cpp/ql/src/Critical/SizeCheck2.ql b/cpp/ql/src/Critical/SizeCheck2.ql new file mode 100644 index 000000000000..273fd6f204a0 --- /dev/null +++ b/cpp/ql/src/Critical/SizeCheck2.ql @@ -0,0 +1,64 @@ +/** + * @name Not enough memory allocated for array of pointer type + * @description Calling 'malloc', 'calloc' or 'realloc' without allocating enough memory to contain + * multiple instances of the type of the pointer may result in a buffer overflow + * @kind problem + * @problem.severity warning + * @precision medium + * @id cpp/suspicious-allocation-size + * @tags reliability + * security + * external/cwe/cwe-131 + * external/cwe/cwe-122 + */ +import cpp + +class Allocation extends FunctionCall +{ + Allocation() { + exists(string name | + this.getTarget().hasQualifiedName(name) and + (name = "malloc" or name = "calloc" or name = "realloc")) + } + + string getName() { result = this.getTarget().getQualifiedName() } + + int getSize() { + (this.getName() = "malloc" and + this.getArgument(0).getValue().toInt() = result) + or + (this.getName() = "realloc" and + this.getArgument(1).getValue().toInt() = result) + or + (this.getName() = "calloc" and + result = + this.getArgument(0).getValue().toInt() * + this.getArgument(1).getValue().toInt()) + } +} + +predicate baseType(Allocation alloc, Type base) +{ + exists(PointerType pointer | + pointer.getBaseType() = base and + ( + exists(AssignExpr assign | + assign.getRValue() = alloc and assign.getLValue().getType() = pointer) + or + exists(Variable v | + v.getInitializer().getExpr() = alloc and v.getType() = pointer) + ) + ) +} + +from Allocation alloc, Type base, int basesize, int allocated +where baseType(alloc, base) + and allocated = alloc.getSize() + // If the codebase has more than one type with the same name, check if any matches + and not exists(int size | base.getSize() = size | + size = 0 + or (allocated / size) * size = allocated) + and basesize = min(base.getSize()) +select alloc, "Allocated memory (" + allocated.toString() + + " bytes) is not a multiple of the size of '" + + base.getName() + "' (" + basesize.toString() + " bytes)." diff --git a/cpp/ql/src/Critical/Unused.cpp b/cpp/ql/src/Critical/Unused.cpp new file mode 100644 index 000000000000..01c60357397e --- /dev/null +++ b/cpp/ql/src/Critical/Unused.cpp @@ -0,0 +1,4 @@ +{ + int foo = 1; + ... //foo is unused +} diff --git a/cpp/ql/src/Critical/Unused.qhelp b/cpp/ql/src/Critical/Unused.qhelp new file mode 100644 index 000000000000..c3c55f9d9c78 --- /dev/null +++ b/cpp/ql/src/Critical/Unused.qhelp @@ -0,0 +1,28 @@ + + + + + +

    +This rule finds variables that are assigned a value but are never read. This is usually an indication of a variable that has been orphaned +due to changes in code, or a defect in the code due to the omission of the unused variable. The unused variables should be +removed to avoid misuse. +

    + + +
    + +

    +Examine the code to see if the variable should really be unused, and remove it if it is. +

    + +
    + + + + + + +
    diff --git a/cpp/ql/src/Critical/Unused.ql b/cpp/ql/src/Critical/Unused.ql new file mode 100644 index 000000000000..4774e83ee51c --- /dev/null +++ b/cpp/ql/src/Critical/Unused.ql @@ -0,0 +1,36 @@ +/** + * @name Variable is assigned a value that is never read + * @description A variable is assigned a value but is never read. These variables are usually orphaned due to changes in code and can be removed, or may indicate a bug in the code that is caused by an omission of the unused variable. + * @kind problem + * @id cpp/unused-variable + * @problem.severity warning + * @tags maintainability + * external/cwe/cwe-563 + */ +import cpp + +// Sometimes it is useful to have a class which is instantiated (on the stack) +// but not otherwise used. This is usually to perform some task and have that +// task automatically reversed when the current scope is left. For example, +// sometimes locking is done this way. +// +// Obviously, such instantiations should not be treated as unused values. +class ScopeUtilityClass extends Class +{ + Call getAUse() + { + result = this.getAConstructor().getACallToThisFunction() + } +} + +from LocalScopeVariable v, ControlFlowNode def +where definition(v, def) + and not definitionUsePair(v, def, _) + and not v.isStatic() + and not v.getAnAccess().isAddressOfAccess() + // parameter initializers are not in the call-graph at the moment + and not v.(Parameter).getInitializer().getExpr() = def + and not v.getType().getUnderlyingType() instanceof ReferenceType + and not exists(ScopeUtilityClass util | def = util.getAUse()) + and not def.isInMacroExpansion() +select def, "Variable '" + v.getName() + "' is assigned a value that is never used" diff --git a/cpp/ql/src/Critical/UseAfterFree.cpp b/cpp/ql/src/Critical/UseAfterFree.cpp new file mode 100644 index 000000000000..448696b4bb7b --- /dev/null +++ b/cpp/ql/src/Critical/UseAfterFree.cpp @@ -0,0 +1,9 @@ +int f() { + char* buf = new char[SIZE]; + .... + if (error) { + free(buf); //error handling has freed the buffer + } + ... + log_contents(buf); //but it is still used here for logging +} diff --git a/cpp/ql/src/Critical/UseAfterFree.qhelp b/cpp/ql/src/Critical/UseAfterFree.qhelp new file mode 100644 index 000000000000..9be2dec1a0c5 --- /dev/null +++ b/cpp/ql/src/Critical/UseAfterFree.qhelp @@ -0,0 +1,33 @@ + + + + + +

    +This rule finds accesses through a pointer of a memory location that has already been freed (i.e. through a dangling pointer). +Such memory blocks have already been released to the dynamic memory manager, and modifying them can lead to anything +from a segfault to memory corruption that would cause subsequent calls to the dynamic memory manger to behave +erratically, to a possible security vulnerability. +

    + + + +
    + +

    +Ensure that all execution paths that access memory through a pointer never access that pointer after it is freed. +

    + +
    + + + + + + +
  • I. Gerg. An Overview and Example of the Buffer-Overflow Exploit. IANewsletter vol 7 no 4. 2005.
  • +M. Donaldson. Inside the Buffer Overflow Attack: Mechanism, Method & Prevention. SANS Institute InfoSec Reading Room. 2002.
  • +
    +
    diff --git a/cpp/ql/src/Critical/UseAfterFree.ql b/cpp/ql/src/Critical/UseAfterFree.ql new file mode 100644 index 000000000000..f51a5084436d --- /dev/null +++ b/cpp/ql/src/Critical/UseAfterFree.ql @@ -0,0 +1,74 @@ +/** + * @name Potential use after free + * @description An allocated memory block is used after it has been freed. Behavior in such cases is undefined and can cause memory corruption. + * @kind problem + * @id cpp/use-after-free + * @problem.severity warning + * @tags reliability + * security + * external/cwe/cwe-416 + */ +import cpp +import semmle.code.cpp.controlflow.LocalScopeVariableReachability + +/** `e` is an expression that frees the memory pointed to by `v`. */ +predicate isFreeExpr(Expr e, LocalScopeVariable v) { + exists(VariableAccess va | + va.getTarget() = v | + exists(FunctionCall fc | + fc = e | + fc.getTarget().hasQualifiedName("free") + and va = fc.getArgument(0) + ) + or + e.(DeleteExpr).getExpr() = va + or + e.(DeleteArrayExpr).getExpr() = va + ) +} + +/** `e` is an expression that (may) dereference `v`. */ +predicate isDerefExpr(Expr e, LocalScopeVariable v) { + v.getAnAccess() = e and dereferenced(e) + or + isDerefByCallExpr(_, _, e, v) +} + +/** + * `va` is passed by value as (part of) the `i`th argument in + * call `c`. The target function is either a library function + * or a source code function that dereferences the relevant + * parameter. + */ +predicate isDerefByCallExpr(Call c, int i, VariableAccess va, LocalScopeVariable v) { + v.getAnAccess() = va + and + va = c.getAnArgumentSubExpr(i) + and + not c.passesByReference(i, va) + and + (c.getTarget().hasEntryPoint() implies isDerefExpr(_, c.getTarget().getParameter(i))) +} + +class UseAfterFreeReachability extends LocalScopeVariableReachability { + UseAfterFreeReachability() { this = "UseAfterFree" } + + override predicate isSource(ControlFlowNode node, LocalScopeVariable v) { + isFreeExpr(node, v) + } + + override predicate isSink(ControlFlowNode node, LocalScopeVariable v) { + isDerefExpr(node, v) + } + + override predicate isBarrier(ControlFlowNode node, LocalScopeVariable v) { + definitionBarrier(v, node) or + isFreeExpr(node, v) + } +} + +from UseAfterFreeReachability r, LocalScopeVariable v, Expr free, Expr e +where r.reaches(free, v, e) +select e, + "Memory pointed to by '" + v.getName().toString() + + "' may have been previously freed $@", free, "here" diff --git a/cpp/ql/src/DefaultOptions.qll b/cpp/ql/src/DefaultOptions.qll new file mode 100644 index 000000000000..552f135706af --- /dev/null +++ b/cpp/ql/src/DefaultOptions.qll @@ -0,0 +1,122 @@ +/** + * Provides default predicates that specify information about + * the behavior of the program being analyzed. + * + * This can be overridden for particular code bases in `Options.qll`. + */ + +import cpp +import semmle.code.cpp.controlflow.Dataflow +import semmle.code.cpp.commons.Synchronization +private import semmle.code.cpp.controlflow.internal.ConstantExprs +private import Options as CustomOptions + +/** + * Default predicates that specify information about the behavior of + * the program being analyzed. + */ +class Options extends string +{ + Options() { + this = "Options" + } + + /** + * Holds if we wish to override the "may return NULL" inference for this + * call. If this holds, then rather than trying to infer whether this + * call might return NULL, we will instead test whether `returnsNull` + * holds for it. + * + * By default, this handles the `Xstrdup` function used by the CVS + * project. + */ + predicate overrideReturnsNull(Call call) { + // Used in CVS: + call.(FunctionCall).getTarget().hasQualifiedName("Xstrdup") + or + CustomOptions::overrideReturnsNull(call) // old Options.qll + } + + /** + * Holds if this call may return NULL. This is only used if + * `overrideReturnsNull` holds for the call. + * + * By default, this handles the `Xstrdup` function used by the CVS + * project. + */ + predicate returnsNull(Call call) { + // Used in CVS: + call.(FunctionCall).getTarget().hasQualifiedName("Xstrdup") and + nullValue(call.getArgument(0)) + or + CustomOptions::returnsNull(call) // old Options.qll + } + + /** + * Holds if a call to this function will never return. + * + * By default, this holds for `exit`, `_exit`, `abort`, `__assert_fail`, + * `longjmp`, `error`, `__builtin_unreachable` and any function with a + * `noreturn` attribute. + */ + predicate exits(Function f) { + f.getAnAttribute().hasName("noreturn") or + exists(string name | f.getQualifiedName() = name | + name = "exit" or + name = "_exit" or + name = "abort" or + name = "__assert_fail" or + name = "longjmp" or + name = "error" or + name = "__builtin_unreachable" + ) or + CustomOptions::exits(f) // old Options.qll + } + + /** + * Holds if evaluating expression `e` will never return, or can be assumed + * to never return. For example: + * ``` + * __assume(0); + * ``` + * (note that in this case if the hint is wrong and the expression is reached at + * runtime, the program's behaviour is undefined) + */ + predicate exprExits(Expr e) { + e.(AssumeExpr).getChild(0).(CompileTimeConstantInt).getIntValue() = 0 or + CustomOptions::exprExits(e) // old Options.qll + } + + /** + * Holds if function `f` should always have its return value checked. + * + * By default holds only for `fgets`. + */ + predicate alwaysCheckReturnValue(Function f) { + f.hasQualifiedName("fgets") or + CustomOptions::alwaysCheckReturnValue(f) // old Options.qll + } + + /** + * Holds if it is reasonable to ignore the return value of function + * call `fc`. + * + * By default holds for calls to `select` where the first argument is + * `0` (commonly used as a way of sleeping), and any call inside a + * macro expansion. + */ + predicate okToIgnoreReturnValue(FunctionCall fc) { + fc.isInMacroExpansion() + or + // common way of sleeping using select: + (fc.getTarget().hasQualifiedName("select") and + fc.getArgument(0).getValue() = "0") + or + CustomOptions::okToIgnoreReturnValue(fc) // old Options.qll + } +} + +Options getOptions() +{ + any() +} diff --git a/cpp/ql/src/Documentation/CaptionedComments.qll b/cpp/ql/src/Documentation/CaptionedComments.qll new file mode 100644 index 000000000000..fd511106a19e --- /dev/null +++ b/cpp/ql/src/Documentation/CaptionedComments.qll @@ -0,0 +1,26 @@ +/** + * Provides heuristics to find "todo" and "fixme" comments (in all caps). + */ +import cpp + +string getCommentTextCaptioned(Comment c, string caption) { + (caption = "TODO" or caption = "FIXME") and + exists (string commentContents, string commentBody, int offset, string interestingSuffix, int endOfLine, string dontCare, string captionedLine, string followingLine + | commentContents = c.getContents() + and commentContents.matches("%" + caption + "%") + and // Add some '\n's so that any interesting line, and its + // following line, will definitely begin and end with '\n'. + commentBody = commentContents.regexpReplaceAll("(?s)^/\\*(.*)\\*/$|^//(.*)$", "\n$1$2\n\n") + and dontCare = commentBody.regexpFind("\\n[/* \\t\\x0B\\f\\r]*" + caption, _, offset) + and interestingSuffix = commentBody.suffix(offset) + and endOfLine = interestingSuffix.indexOf("\n", 1, 0) + and captionedLine = interestingSuffix.prefix(endOfLine).regexpReplaceAll("^[/*\\s]*" + caption + "\\s*:?", "").trim() + and followingLine = interestingSuffix.prefix(interestingSuffix.indexOf("\n", 2, 0)).suffix(endOfLine).trim() + and if captionedLine = "" + then result = caption + " comment" + else if followingLine = "" + then result = caption + " comment: " + captionedLine + else result = caption + " comment: " + captionedLine + " [...]" + ) +} + diff --git a/cpp/ql/src/Documentation/CommentedOutCode.qhelp b/cpp/ql/src/Documentation/CommentedOutCode.qhelp new file mode 100644 index 000000000000..4ce0ee029b62 --- /dev/null +++ b/cpp/ql/src/Documentation/CommentedOutCode.qhelp @@ -0,0 +1,7 @@ + + + + + diff --git a/cpp/ql/src/Documentation/CommentedOutCode.ql b/cpp/ql/src/Documentation/CommentedOutCode.ql new file mode 100644 index 000000000000..894117381787 --- /dev/null +++ b/cpp/ql/src/Documentation/CommentedOutCode.ql @@ -0,0 +1,15 @@ +/** + * @name Commented-out code + * @description Commented-out code makes the remaining code more difficult to read. + * @kind problem + * @problem.severity recommendation + * @precision high + * @id cpp/commented-out-code + * @tags maintainability + * documentation + */ + +import CommentedOutCode + +from CommentedOutCode comment +select comment, "This comment appears to contain commented-out code" diff --git a/cpp/ql/src/Documentation/CommentedOutCode.qll b/cpp/ql/src/Documentation/CommentedOutCode.qll new file mode 100644 index 000000000000..c1097701bef7 --- /dev/null +++ b/cpp/ql/src/Documentation/CommentedOutCode.qll @@ -0,0 +1,134 @@ +import cpp + +/** + * Holds if `line` looks like a line of code. + * Matches comment lines ending with '{', '}' or ';' that do not start with '>' or contain '@{' or '@}', but first filters out: + * * HTML entities in common notation (e.g. &gt; and &eacute;) + * * HTML entities in decimal notation (e.g. a&#768;) + * * HTML entities in hexadecimal notation (e.g. &#x705F;) + * To account for the code generated by protobuf, we also insist that the comment + * does not begin with `optional` or `repeated` and end with a `;`, which would + * normally be a quoted bit of literal `.proto` specification above the associated + * declaration. + * To account for emacs folding markers, we ignore any line containing + * `{{{` or `}}}`. + * + * Finally, some code tends to embed GUIDs in comments, so we also exclude those. + */ +bindingset[line] +private predicate looksLikeCode(string line) { + exists(string trimmed | + trimmed = line.regexpReplaceAll("(?i)(^\\s+|&#?[a-z0-9]{1,31};|\\s+$)", "") | + trimmed.regexpMatch(".*[{};]") + and not trimmed.regexpMatch("(>.*|.*[\\\\@][{}].*|(optional|repeated) .*;|.*(\\{\\{\\{|\\}\\}\\}).*|\\{[-0-9a-zA-Z]+\\})")) +} + +/** + * The line of a C++-style comment within its file `f`. + */ +private int lineInFile(CppStyleComment c, File f) { + f = c.getFile() and + result = c.getLocation().getStartLine() +} + +/** + * The "comment block ID" for a comment line in a file. + * The block ID is obtained by subtracting the line rank of the line from + * the line itself, where the line rank is the (1-based) rank within `f` + * of lines containing a C++-style comment. As a result, line comments on + * consecutive lines are assigned the same block ID (as both line number + * and line rank increase by 1 for each line), while intervening lines + * without line comments would increase the line number without increasing + * the rank and thus force a change of block ID. + */ +private pragma[nomagic] int commentLineBlockID(File f, int line) { + exists(int lineRank | + line = rank[lineRank](lineInFile(_, f)) and + result = line - lineRank + ) +} + +/** + * The comment ID of the given comment (on line `line` of file `f`). + * The resulting number is meaningless, except that it will be the same + * for all comments in a run of consecutive comment lines, and different + * for separate runs. + */ +private int commentId(CppStyleComment c, File f, int line) { + result = commentLineBlockID(f, line) and + line = lineInFile(c, f) +} + +/** + * A contiguous block of comments. + */ +class CommentBlock extends Comment { + CommentBlock() { + this instanceof CppStyleComment implies + not exists(CppStyleComment pred, File f | lineInFile(pred, f) + 1 = lineInFile(this, f)) + } + + /** + * Gets the `i`th comment associated with this comment block. + */ + Comment getComment(int i) { + (i = 0 and result = this) or + exists(File f, int thisLine, int resultLine | + commentId(this, f, thisLine) = commentId(result, f, resultLine) | + i = resultLine - thisLine + ) + } + + Comment lastComment() { + result = this.getComment(max(int i | exists(this.getComment(i)))) + } + + string getLine(int i) { + this instanceof CStyleComment and result = this.getContents().regexpCapture("(?s)/\\*+(.*)\\*+/", 1).splitAt("\n", i) + or + this instanceof CppStyleComment and result = this.getComment(i).getContents().suffix(2) + } + + int numLines() { + result = strictcount(int i, string line | line = this.getLine(i) and line.trim() != "") + } + + int numCodeLines() { + result = strictcount(int i, string line | line = this.getLine(i) and looksLikeCode(line)) + } + + predicate isDocumentation() { + // If a C-style comment starts each line with a *, then it's + // probably documentation rather than code. + this instanceof CStyleComment and + forex(int i | i in [1 .. this.numLines() - 1] + | this.getLine(i).trim().matches("*%")) + } + + predicate isCommentedOutCode() { + not this.isDocumentation() and + this.numCodeLines().(float) / this.numLines().(float) > 0.5 + } + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [LGTM locations](https://lgtm.com/help/ql/locations). + */ + predicate hasLocationInfo(string filepath, int startline, int startcolumn, int endline, int endcolumn) { + this.getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and + this.lastComment().getLocation().hasLocationInfo(filepath, _, _, endline, endcolumn) + } +} + +/** + * A piece of commented-out code, identified using heuristics + */ +class CommentedOutCode extends CommentBlock { + CommentedOutCode() { + this.isCommentedOutCode() + } +} + diff --git a/cpp/ql/src/Documentation/DocumentApi.qhelp b/cpp/ql/src/Documentation/DocumentApi.qhelp new file mode 100644 index 000000000000..9bc19d350561 --- /dev/null +++ b/cpp/ql/src/Documentation/DocumentApi.qhelp @@ -0,0 +1,33 @@ + + + + + +

    +Functions that are called from lots of different places are usually important, and justify having documentation written for them. +In particular, if a function is defined in a file, and is called from at least two other files, then the function should probably be documented. +

    +

    +As an exception, because their purpose is usually obvious, it is not necessary to document constructors, destructors, implementations of operator=, or functions with fewer than five lines of code. +

    +
    + +

    +Add comments to document the purpose of the function. In particular, ensure that the public API of the function is carefully documented. This reduces the chance that a future change to the function will introduce a defect by changing the API and breaking the expections of the calling functions. +

    + +
    + + +
  • + C++ Programming Wikibook: Comments +
  • +
  • + Wikipedia: Need for comments +
  • + + +
    +
    diff --git a/cpp/ql/src/Documentation/DocumentApi.ql b/cpp/ql/src/Documentation/DocumentApi.ql new file mode 100644 index 000000000000..5e90609ac77e --- /dev/null +++ b/cpp/ql/src/Documentation/DocumentApi.ql @@ -0,0 +1,36 @@ +/** + * @name Undocumented API function + * @description Functions used from outside the file they are declared in + * should be documented, as they are part of a public API. Without + * comments, modifying such functions is dangerous because callers + * easily come to rely on their exact implementation. + * @kind problem + * @id cpp/document-api + * @problem.severity recommendation + * @precision medium + * @tags maintainability + * documentation + */ +import cpp + + +predicate isCommented(FunctionDeclarationEntry f) { + exists(Comment c | c.getCommentedElement() = f) +} + +// Uses of 'f' in 'other' +Call uses(File other, Function f) { + result.getTarget() = f and result.getFile() = other +} + +from File callerFile, Function f, Call use, int numCalls +where numCalls = strictcount(File other | exists(uses(other, f)) and other != f.getFile()) + and not isCommented(f.getADeclarationEntry()) + and not f instanceof Constructor + and not f instanceof Destructor + and not f.hasName("operator=") + and f.getMetrics().getNumberOfLinesOfCode() >= 5 + and numCalls > 1 + and use = uses(callerFile, f) + and callerFile != f.getFile() +select f, "Functions called from other files should be documented (called from $@).", use, use.getFile().getRelativePath() diff --git a/cpp/ql/src/Documentation/FixmeComments.cpp b/cpp/ql/src/Documentation/FixmeComments.cpp new file mode 100644 index 000000000000..854741d17cb6 --- /dev/null +++ b/cpp/ql/src/Documentation/FixmeComments.cpp @@ -0,0 +1,4 @@ +int isEven(int n) { + //FIXME: Is only correct for small values of n + return n == 0 || n == 2; +} \ No newline at end of file diff --git a/cpp/ql/src/Documentation/FixmeComments.qhelp b/cpp/ql/src/Documentation/FixmeComments.qhelp new file mode 100644 index 000000000000..a4a0b9966a15 --- /dev/null +++ b/cpp/ql/src/Documentation/FixmeComments.qhelp @@ -0,0 +1,36 @@ + + + + + +

    +The indicated comment is a FIXME comment. +FIXME comments are often used to indicate code that does not work correctly or that may not work in all supported environments. +This may be necessary during the implementation of new functionality but FIXME comments should not be present in stable code. +Any FIXME comments should be reviewed and the code improved as soon as possible to avoid the accumulation of partially implemented features. +

    + +
    + +

    +Fix the functionality indicated by the comment. If the comment no longer applies, delete it to avoid confusion. +

    + +
    + + + + + +
  • + Wikipedia: Comment tags +
  • +
  • + The case against TODO (and FIXME) +
  • + + +
    +
    diff --git a/cpp/ql/src/Documentation/FixmeComments.ql b/cpp/ql/src/Documentation/FixmeComments.ql new file mode 100644 index 000000000000..1562910191e2 --- /dev/null +++ b/cpp/ql/src/Documentation/FixmeComments.ql @@ -0,0 +1,17 @@ +/** + * @name FIXME comment + * @description Comments containing 'FIXME' indicate that the code has known bugs. + * @kind problem + * @problem.severity recommendation + * @precision very-high + * @id cpp/fixme-comment + * @tags maintainability + * documentation + * external/cwe/cwe-546 + */ +import cpp +import Documentation.CaptionedComments + +from Comment c, string message +where message = getCommentTextCaptioned(c, "FIXME") +select c, message diff --git a/cpp/ql/src/Documentation/TodoComments.cpp b/cpp/ql/src/Documentation/TodoComments.cpp new file mode 100644 index 000000000000..2404ca2b2e9c --- /dev/null +++ b/cpp/ql/src/Documentation/TodoComments.cpp @@ -0,0 +1,4 @@ +int isOdd(int n) { + //TODO: Works only for positive n. Need to check if negative n is valid input + return (n % 2) == 1; +} \ No newline at end of file diff --git a/cpp/ql/src/Documentation/TodoComments.qhelp b/cpp/ql/src/Documentation/TodoComments.qhelp new file mode 100644 index 000000000000..ad246ebf358f --- /dev/null +++ b/cpp/ql/src/Documentation/TodoComments.qhelp @@ -0,0 +1,39 @@ + + + + + +

    +The indicated comment is a TODO comment. +TODO comments are often used to indicate code that is incomplete. This may be necessary during the implementation of new functionality +but TODO comments should not be present in stable code. Any TODO comments should be reviewed and the code completed as soon as possible +to avoid the accumulation of partially implemented features. +

    + +
    + +

    +Implement the functionality indicated by the comment. If the comment no longer applies, delete it to avoid confusion. +

    + +
    + + + + + +
  • + Wikipedia: Comment tags +
  • +
  • + TODO or not TODO +
  • +
  • + The case against TODO +
  • + + +
    +
    diff --git a/cpp/ql/src/Documentation/TodoComments.ql b/cpp/ql/src/Documentation/TodoComments.ql new file mode 100644 index 000000000000..be1c321b87e8 --- /dev/null +++ b/cpp/ql/src/Documentation/TodoComments.ql @@ -0,0 +1,18 @@ +/** + * @name TODO comment + * @description Comments containing 'TODO' indicate that the code may be in an incomplete state. + * @kind problem + * @problem.severity recommendation + * @precision medium + * @id cpp/todo-comment + * @tags maintainability + * documentation + * external/cwe/cwe-546 + */ +import cpp +import Documentation.CaptionedComments + +from Comment c, string message +where message = getCommentTextCaptioned(c, "TODO") +select c, message + diff --git a/cpp/ql/src/Documentation/UncommentedFunction.cpp b/cpp/ql/src/Documentation/UncommentedFunction.cpp new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/cpp/ql/src/Documentation/UncommentedFunction.cpp @@ -0,0 +1 @@ + diff --git a/cpp/ql/src/Documentation/UncommentedFunction.qhelp b/cpp/ql/src/Documentation/UncommentedFunction.qhelp new file mode 100644 index 000000000000..7fe5c61d9da3 --- /dev/null +++ b/cpp/ql/src/Documentation/UncommentedFunction.qhelp @@ -0,0 +1,34 @@ + + + + + +

    +This rule finds large functions that have too few comment lines. Documentation becomes more important as a function becomes more +complex, and a lack of documentation makes it harder to maintain. +

    + + +
    + +

    +Add comments to document the purpose of the function. Large, complex functions in particular require detailed documentation, not only +because they are harder to understand, but the process of documentation may reveal that the function could be split into smaller, more +cohesive functions. +

    + +
    + + +
  • + C++ Programming, Coding style conventions +
  • +
  • + Wikipedia: Need for comments +
  • + + +
    +
    diff --git a/cpp/ql/src/Documentation/UncommentedFunction.ql b/cpp/ql/src/Documentation/UncommentedFunction.ql new file mode 100644 index 000000000000..2bb443cc9ab0 --- /dev/null +++ b/cpp/ql/src/Documentation/UncommentedFunction.ql @@ -0,0 +1,19 @@ +/** + * @name Poorly documented large function + * @description Large functions that have no or almost no comments are likely to be too complex to understand and maintain. The larger a function is, the more problematic the lack of comments. + * @kind problem + * @problem.severity warning + * @precision medium + * @id cpp/poorly-documented-function + * @tags maintainability + * documentation + * statistical + * non-attributable + */ +import cpp + +from MetricFunction f, int n +where n = f.getNumberOfLines() and n > 100 and + f.getCommentRatio() <= 0.02 and + not f.isMultiplyDefined() +select f, "Poorly documented function: fewer than 2% comments for a function of " + n.toString() + " lines." diff --git a/cpp/ql/src/Header Cleanup/Cleanup-DuplicateIncludeGuard.cpp b/cpp/ql/src/Header Cleanup/Cleanup-DuplicateIncludeGuard.cpp new file mode 100644 index 000000000000..2b4e70c98ab0 --- /dev/null +++ b/cpp/ql/src/Header Cleanup/Cleanup-DuplicateIncludeGuard.cpp @@ -0,0 +1,8 @@ +// header_file.h + +#ifndef HEADER_FILE_H +#define HEADER_FILE_H + + // ... + +#endif // HEADER_FILE_H \ No newline at end of file diff --git a/cpp/ql/src/Header Cleanup/Cleanup-DuplicateIncludeGuard.qhelp b/cpp/ql/src/Header Cleanup/Cleanup-DuplicateIncludeGuard.qhelp new file mode 100644 index 000000000000..20cb7ebe94fc --- /dev/null +++ b/cpp/ql/src/Header Cleanup/Cleanup-DuplicateIncludeGuard.qhelp @@ -0,0 +1,51 @@ + + + + + +

    A common pattern in header files is to use pre-processor directives to guard +a header file against being processed more than once per translation unit. This +practice is intended to prevent compilation errors. However, pre-processor +include guards are prone to human error themselves because each include guard +must be assigned a unique macro name to function correctly. If two header files +share the same guard macro, the compiler may unexpectedly skip the second file it +encounters, leading to compilation errors or configuration bugs.

    + +

    The query will flag the pre-processor #ifndef directive at the +beginning of any include guard that matches another include guard in the +project. Browsing the list of results you will be able to find the other +directive(s) which use the same macro.

    +
    + + +

    First decide whether the duplicate include guard is dangerous. A duplicate +include guard may cause the header file to be skipped over when it shouldn't be, +but occasionally this design is used on purpose to 'override' an existing header +file.

    + +

    To address the issue, rename the macros used by all but one instance of the +duplicate include guard. Remember to change both the #ifndef and the +#define directive to use the new macro name. Alternatively, consider +using the #pragma once directive to prevent multiple inclusion without +the need to define unique macros.

    + +
    + + +

    Here's an example of two header files that have accidentally been given the same +include guard macro. To fix the issue, rename both occurrences of the macro in +the second file, for example to ANOTHER_HEADER_FILE_H.

    + + +
    + + + +
  • + Wikipedia: Include guard +
  • + +
    +
    diff --git a/cpp/ql/src/Header Cleanup/Cleanup-DuplicateIncludeGuard.ql b/cpp/ql/src/Header Cleanup/Cleanup-DuplicateIncludeGuard.ql new file mode 100644 index 000000000000..9e4f2d1276dd --- /dev/null +++ b/cpp/ql/src/Header Cleanup/Cleanup-DuplicateIncludeGuard.ql @@ -0,0 +1,28 @@ +/** + * @name Duplicate include guard + * @description Using the same include guard macro in more than one header file may cause unexpected behavior from the compiler. + * @kind problem + * @problem.severity error + * @precision high + * @id cpp/duplicate-include-guard + * @tags reliability + * maintainability + * modularity + */ +import cpp +import semmle.code.cpp.headers.MultipleInclusion + +/* + * A duplicate include guard is an include guard that uses the same macro name as at least + * one other include guard. We use hasIncludeGuard, which checks the #ifndef and #endif but + * not the #define, to identify them (as we expect the #define to be missing from the database + * in the case of a file that's only ever encountered after other(s) with the same guard macro). + * However one case must be a correctIncludeGuard to prove that this macro really is intended + * to be an include guard. + */ +from HeaderFile hf, PreprocessorDirective ifndef, string macroName, int num +where hasIncludeGuard(hf, ifndef, _, macroName) +and exists(HeaderFile other | hasIncludeGuard(other, _, _, macroName) and hf.getShortName() != other.getShortName()) +and num = strictcount(HeaderFile other | hasIncludeGuard(other, _, _, macroName)) +and correctIncludeGuard(_, _, _, _, macroName) +select ifndef, "The macro name '" + macroName + "' of this include guard is used in " + num + " different header files." diff --git a/cpp/ql/src/Header Cleanup/Cleanup-DuplicateIncludeGuard2.cpp b/cpp/ql/src/Header Cleanup/Cleanup-DuplicateIncludeGuard2.cpp new file mode 100644 index 000000000000..3a5d7afd9c24 --- /dev/null +++ b/cpp/ql/src/Header Cleanup/Cleanup-DuplicateIncludeGuard2.cpp @@ -0,0 +1,8 @@ +// another_header_file.h + +#ifndef HEADER_FILE_H // should be ANOTHER_HEADER_FILE_H +#define HEADER_FILE_H // should be ANOTHER_HEADER_FILE_H + + // ... + +#endif // HEADER_FILE_H \ No newline at end of file diff --git a/cpp/ql/src/JPL_C/LOC-2/Rule 03/ExitNonterminatingLoop.ql b/cpp/ql/src/JPL_C/LOC-2/Rule 03/ExitNonterminatingLoop.ql new file mode 100644 index 000000000000..91934b57023e --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-2/Rule 03/ExitNonterminatingLoop.ql @@ -0,0 +1,28 @@ +/** + * @name Exit from permanent loop + * @description Permanent loops (like "while(1) {..}") are typically meant to be non-terminating and should not be terminated by other means. + * @kind problem + * @id cpp/jpl-c/exit-nonterminating-loop + * @problem.severity warning + */ + +import cpp + +predicate markedAsNonterminating(Loop l) { + exists(Comment c | c.getContents().matches("%@non-terminating@%") | + c.getCommentedElement() = l + ) +} + +Stmt exitFrom(Loop l) { + l.getAChild+() = result and + (result instanceof ReturnStmt or + exists(BreakStmt break | break = result | + not l.getAChild*() = break.getTarget()) + ) +} + +from Loop l, Stmt exit +where markedAsNonterminating(l) and + exit = exitFrom(l) +select exit, "$@ should not be exited.", l, "This permanent loop" diff --git a/cpp/ql/src/JPL_C/LOC-2/Rule 03/LoopBounds.ql b/cpp/ql/src/JPL_C/LOC-2/Rule 03/LoopBounds.ql new file mode 100644 index 000000000000..ed3240be818d --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-2/Rule 03/LoopBounds.ql @@ -0,0 +1,123 @@ +/** + * @name Unbounded loop + * @description All loops should have a fixed upper bound; the counter should also be incremented along all paths within the loop. + This check excludes loops that are meant to be nonterminating (like schedulers). + * @kind problem + * @id cpp/jpl-c/loop-bounds + * @problem.severity warning + */ + +import cpp + +predicate validVarForBound(Loop loop, Variable var) { + // The variable is read in the loop controlling expression + var.getAnAccess().getParent*() = loop.getControllingExpr() and + // The variable is not assigned in the loop body + not inScope(loop, var.getAnAssignment().getEnclosingStmt()) and + // The variable is not incremented/decremented in the loop body + not inScope(loop, var.getAnAccess().getParent().(CrementOperation).getEnclosingStmt()) +} + +predicate upperBoundCheck(Loop loop, VariableAccess checked) { + exists(RelationalOperation rop | loop.getControllingExpr().getAChild*() = rop | + checked = rop.getLesserOperand() and + // The RHS is something "valid", i.e. a constant or + // a variable that isn't assigned in the loop body + ( + exists(rop.getGreaterOperand().getValue()) or + rop.getGreaterOperand().(VariableAccess).getTarget().isConst() or + validVarForBound(loop, rop.getGreaterOperand().(VariableAccess).getTarget()) + ) and + not rop.getGreaterOperand() instanceof CharLiteral) +} + +predicate lowerBoundCheck(Loop loop, VariableAccess checked) { + exists(RelationalOperation rop | loop.getControllingExpr().getAChild*() = rop | + checked = rop.getGreaterOperand() and + // The RHS is something "valid", i.e. a constant or + // a variable that isn't assigned in the loop body + ( + exists(rop.getLesserOperand().getValue()) or + rop.getLesserOperand().(VariableAccess).getTarget().isConst() or + validVarForBound(loop, rop.getLesserOperand().(VariableAccess).getTarget()) + ) and + not rop.getLesserOperand() instanceof CharLiteral) +} + +VariableAccess getAnIncrement(Variable var) { + result.getTarget() = var and + ( + result.getParent() instanceof IncrementOperation + or + exists(AssignAddExpr a | a.getLValue() = result and a.getRValue().getValue().toInt() > 0) + or + exists(AssignExpr a | a.getLValue() = result | + a.getRValue() = + any(AddExpr ae | ae.getAnOperand() = var.getAnAccess() and + ae.getAnOperand().getValue().toInt() > 0)) + ) +} + +VariableAccess getADecrement(Variable var) { + result.getTarget() = var and + ( + result.getParent() instanceof DecrementOperation + or + exists(AssignSubExpr a | a.getLValue() = result and a.getRValue().getValue().toInt() > 0) + or + exists(AssignExpr a | a.getLValue() = result | + a.getRValue() = + any(SubExpr ae | ae.getLeftOperand() = var.getAnAccess() and + ae.getRightOperand().getValue().toInt() > 0)) + ) +} + +predicate inScope(Loop l, Stmt s) { + l.getAChild*() = s +} + +predicate reachesNoInc(VariableAccess source, ControlFlowNode target) { + (upperBoundCheck(_, source) and source.getASuccessor() = target) or + exists(ControlFlowNode mid | reachesNoInc(source, mid) and not mid = getAnIncrement(source.getTarget()) | + target = mid.getASuccessor() and + inScope(source.getEnclosingStmt(), target.getEnclosingStmt())) +} + +predicate reachesNoDec(VariableAccess source, ControlFlowNode target) { + (lowerBoundCheck(_, source) and source.getASuccessor() = target) or + exists(ControlFlowNode mid | reachesNoDec(source, mid) and not mid = getADecrement(source.getTarget()) | + target = mid.getASuccessor() and + inScope(source.getEnclosingStmt(), target.getEnclosingStmt())) +} + +predicate hasSafeBound(Loop l) { + exists(VariableAccess bound | upperBoundCheck(l, bound) | + not reachesNoInc(bound, bound) + ) or exists(VariableAccess bound | lowerBoundCheck(l, bound) | + not reachesNoDec(bound, bound) + ) or exists(l.getControllingExpr().getValue()) +} + +predicate markedAsNonterminating(Loop l) { + exists(Comment c | c.getContents().matches("%@non-terminating@%") | + c.getCommentedElement() = l + ) +} + +from Loop loop, string msg +where not hasSafeBound(loop) and + not markedAsNonterminating(loop) and + ( + ( + not upperBoundCheck(loop, _) and + not lowerBoundCheck(loop, _) and + msg = "This loop does not have a fixed bound." + ) or exists(VariableAccess bound | upperBoundCheck(loop, bound) and + reachesNoInc(bound, bound) and + msg = "The loop counter " + bound.getTarget().getName() + " is not always incremented in the loop body." + ) or exists(VariableAccess bound | lowerBoundCheck(loop, bound) and + reachesNoDec(bound, bound) and + msg = "The loop counter " + bound.getTarget().getName() + " is not always decremented in the loop body." + ) + ) +select loop, msg diff --git a/cpp/ql/src/JPL_C/LOC-2/Rule 04/Recursion.ql b/cpp/ql/src/JPL_C/LOC-2/Rule 04/Recursion.ql new file mode 100644 index 000000000000..d52d1195185d --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-2/Rule 04/Recursion.ql @@ -0,0 +1,22 @@ +/** + * @name Uses of recursion + * @description Avoiding recursion allows tools and people to better analyze the program. + * @kind problem + * @id cpp/jpl-c/recursion + * @problem.severity warning + */ + +import cpp + +class RecursiveCall extends FunctionCall { + RecursiveCall() { + this.getTarget().calls*(this.getEnclosingFunction()) + } +} + +from RecursiveCall call, string msg +where if (call.getTarget() = call.getEnclosingFunction()) then + msg = "This call directly invokes its containing function $@." + else + msg = "The function " + call.getEnclosingFunction() + " is indirectly recursive via this call to $@." +select call, msg, call.getTarget(), call.getTarget().getName() diff --git a/cpp/ql/src/JPL_C/LOC-2/Rule 05/HeapMemory.ql b/cpp/ql/src/JPL_C/LOC-2/Rule 05/HeapMemory.ql new file mode 100644 index 000000000000..7b740959f2ad --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-2/Rule 05/HeapMemory.ql @@ -0,0 +1,32 @@ +/** + * @name Dynamic allocation after initialization + * @description Dynamic memory allocation (using malloc() or calloc()) should be confined to the initialization routines of a program. + * @kind problem + * @id cpp/jpl-c/heap-memory + * @problem.severity warning + */ + +import cpp + +class Initialization extends Function { + Initialization() { + // TODO: This could be refined to match precisely what functions count + // as "initialization", and are, hence, allowed to perform dynamic + // memory allocation. + this.getName().toLowerCase().matches("init%") or + this.getName().toLowerCase().matches("%\\_init") + } +} + +class Allocation extends FunctionCall { + Allocation() { + exists(string name | name = this.getTarget().getName() | + name = "malloc" or name = "calloc" or name = "alloca" or + name = "sbrk" or name = "valloc") + } +} + +from Function f, Allocation a +where not f instanceof Initialization and + a.getEnclosingFunction() = f +select a, "Dynamic memory allocation is only allowed during initialization." diff --git a/cpp/ql/src/JPL_C/LOC-2/Rule 07/ThreadSafety.ql b/cpp/ql/src/JPL_C/LOC-2/Rule 07/ThreadSafety.ql new file mode 100644 index 000000000000..0c7e13dc7d4d --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-2/Rule 07/ThreadSafety.ql @@ -0,0 +1,22 @@ +/** + * @name Use of delay function + * @description Task synchronization shall not be performed through the use of task delays. + * @kind problem + * @id cpp/jpl-c/thread-safety + * @problem.severity warning + */ + +import cpp + +class ForbiddenCall extends FunctionCall { + ForbiddenCall() { + exists(string name | name = this.getTarget().getName() | + name = "task_delay" or name = "taskDelay" or + name = "sleep" or name = "nanosleep" or + name = "clock_nanosleep" + ) + } +} + +from ForbiddenCall call +select call, "Task synchronization shall not be performed through task delays." diff --git a/cpp/ql/src/JPL_C/LOC-2/Rule 09/AvoidNestedSemaphores.ql b/cpp/ql/src/JPL_C/LOC-2/Rule 09/AvoidNestedSemaphores.ql new file mode 100644 index 000000000000..f3d27438155a --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-2/Rule 09/AvoidNestedSemaphores.ql @@ -0,0 +1,32 @@ +/** + * @name Avoid nested semaphores + * @description Nested use of semaphores or locks should be avoided. + * @kind problem + * @id cpp/jpl-c/avoid-nested-semaphores + * @problem.severity warning + */ + +import Semaphores + +LockOperation maybeLocked(Function f) { + result.getEnclosingFunction() = f or + exists(Function g | f.calls(g) | + result = maybeLocked(g) + ) +} + +predicate intraproc(LockOperation inner, string msg, LockOperation outer) { + inner = outer.getAReachedNode() and outer.getLocked() != inner.getLocked() and + msg = "This lock operation is nested in a $@." +} + +predicate interproc(FunctionCall inner, string msg, LockOperation outer) { + inner = outer.getAReachedNode() and + exists(LockOperation lock | lock = maybeLocked(inner.getTarget()) and lock.getLocked() != outer.getLocked() | + msg = "This call may perform a " + lock.say() + " while under the effect of a $@." + ) +} + +from LockOperation outer, FunctionCall inner, string msg +where intraproc(inner, msg, outer) or interproc(inner, msg, outer) +select inner, msg, outer, outer.say() diff --git a/cpp/ql/src/JPL_C/LOC-2/Rule 09/AvoidSemaphores.ql b/cpp/ql/src/JPL_C/LOC-2/Rule 09/AvoidSemaphores.ql new file mode 100644 index 000000000000..159b56fecf73 --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-2/Rule 09/AvoidSemaphores.ql @@ -0,0 +1,14 @@ +/** + * @name Avoid semaphores + * @description The use of semaphores or locks to access shared data should be avoided. + * @kind problem + * @id cpp/jpl-c/avoid-semaphores + * @problem.severity warning + */ + +import Semaphores + +from FunctionCall call, string kind +where (call instanceof SemaphoreCreation and kind = "semaphores") or + (call instanceof LockingPrimitive and kind = "locking primitives") +select call, "Use of " + kind + " should be avoided." diff --git a/cpp/ql/src/JPL_C/LOC-2/Rule 09/OutOfOrderLocks.ql b/cpp/ql/src/JPL_C/LOC-2/Rule 09/OutOfOrderLocks.ql new file mode 100644 index 000000000000..5f8ede45e0fb --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-2/Rule 09/OutOfOrderLocks.ql @@ -0,0 +1,25 @@ +/** + * @name Out-of-order locks + * @description Where nested locks are inevitable, they should always be taken in the same order. + * @kind problem + * @id cpp/jpl-c/out-of-order-locks + * @problem.severity warning + */ + +import Semaphores + +predicate lockOrder(LockOperation outer, LockOperation inner) { + outer.getAReachedNode() = inner and + inner.getLocked() != outer.getLocked() +} + +int orderCount(Declaration outerLock, Declaration innerLock) { + result = strictcount(LockOperation outer, LockOperation inner | + outer.getLocked() = outerLock and inner.getLocked() = innerLock and + lockOrder(outer, inner)) +} + +from LockOperation outer, LockOperation inner +where lockOrder(outer, inner) + and orderCount(outer.getLocked(), inner.getLocked()) <= orderCount(inner.getLocked(), outer.getLocked()) +select inner, "Out-of-order locks: A " + inner.say() + " usually precedes a $@.", outer, outer.say() diff --git a/cpp/ql/src/JPL_C/LOC-2/Rule 09/ReleaseLocksWhenAcquired.ql b/cpp/ql/src/JPL_C/LOC-2/Rule 09/ReleaseLocksWhenAcquired.ql new file mode 100644 index 000000000000..dde55c0d8c4f --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-2/Rule 09/ReleaseLocksWhenAcquired.ql @@ -0,0 +1,13 @@ +/** + * @name Unreleased lock + * @description Unlock operations shall always appear within the body of the same function that performs the matching lock operation. + * @kind problem + * @id cpp/jpl-c/release-locks-when-acquired + * @problem.severity warning + */ + +import Semaphores + +from LockOperation lock +where lock.getAReachedNode() = lock.getEnclosingFunction() +select lock, "This lock operation may escape the function without a matching unlock." diff --git a/cpp/ql/src/JPL_C/LOC-2/Rule 09/Semaphores.qll b/cpp/ql/src/JPL_C/LOC-2/Rule 09/Semaphores.qll new file mode 100644 index 000000000000..d58b240a414b --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-2/Rule 09/Semaphores.qll @@ -0,0 +1,115 @@ +/** + * Provides classes corresponding to VxWorks semaphores and locks. + */ + +import cpp + + +class SemaphoreCreation extends FunctionCall { + SemaphoreCreation() { + exists(string name | name = this.getTarget().getName() | + name = "semBCreate" or name = "semMCreate" or name = "semCCreate" or + name = "semRWCreate" + ) + } + + Variable getSemaphore() { + result.getAnAccess() = this.getParent().(Assignment).getLValue() + } +} + +abstract class LockOperation extends FunctionCall { + abstract UnlockOperation getMatchingUnlock(); + abstract Declaration getLocked(); + abstract string say(); + + ControlFlowNode getAReachedNode() { + result = this or + exists(ControlFlowNode mid | mid = getAReachedNode() | + not(mid != this.getMatchingUnlock()) and + result = mid.getASuccessor() + ) + } +} + +abstract class UnlockOperation extends FunctionCall { + abstract LockOperation getMatchingLock(); +} + +class SemaphoreTake extends LockOperation { + SemaphoreTake() { + exists(string name | name = this.getTarget().getName() | + name = "semTake" or + // '_' is a wildcard, so this matches calls like + // semBTakeScalable or semMTake_inline. + name.matches("sem_Take%") + ) + } + + override Variable getLocked() { + result.getAnAccess() = this.getArgument(0) + } + + override UnlockOperation getMatchingUnlock() { + result.(SemaphoreGive).getLocked() = this.getLocked() + } + + override string say() { + result = "semaphore take of " + getLocked().getName() + } +} + +class SemaphoreGive extends UnlockOperation { + SemaphoreGive() { + exists(string name | name = this.getTarget().getName() | + name = "semGive" or + name.matches("sem%Give%") + ) + } + + Variable getLocked() { + result.getAnAccess() = this.getArgument(0) + } + + override LockOperation getMatchingLock() { + this = result.getMatchingUnlock() + } + +} + +class LockingPrimitive extends FunctionCall, LockOperation { + LockingPrimitive() { + exists(string name | name = this.getTarget().getName() | + name = "taskLock" or name = "intLock" or name = "taskRtpLock" + ) + } + + override Function getLocked() { + result = this.getTarget() + } + + override UnlockOperation getMatchingUnlock() { + result.(UnlockingPrimitive).getTarget().getName() = + this.getTarget().getName().replaceAll("Lock", "Unlock") + } + + override string say() { + result = "call to " + getLocked().getName() + } +} + +class UnlockingPrimitive extends FunctionCall, UnlockOperation { + UnlockingPrimitive() { + exists(string name | name = this.getTarget().getName() | + name = "taskUnlock" or name = "intUnlock" or name = "taskRtpUnlock" + ) + } + + Function getLocked() { + result = getMatchingLock().getLocked() + } + + override LockOperation getMatchingLock() { + this = result.getMatchingUnlock() + } +} diff --git a/cpp/ql/src/JPL_C/LOC-2/Rule 11/SimpleControlFlowGoto.ql b/cpp/ql/src/JPL_C/LOC-2/Rule 11/SimpleControlFlowGoto.ql new file mode 100644 index 000000000000..6aeed8dedbe4 --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-2/Rule 11/SimpleControlFlowGoto.ql @@ -0,0 +1,12 @@ +/** + * @name Use of goto + * @description Using the goto statement complicates function control flow and hinders program understanding. + * @kind problem + * @id cpp/jpl-c/simple-control-flow-goto + * @problem.severity warning + */ + +import cpp + +from GotoStmt goto +select goto, "The goto statement should not be used." 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 new file mode 100644 index 000000000000..6d33502ac9e0 --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-2/Rule 11/SimpleControlFlowJmp.ql @@ -0,0 +1,21 @@ +/** + * @name Use of setjmp or longjmp + * @description Using the setjmp and longjmp functions complicates control flow and hinders program understanding. + * @kind problem + * @id cpp/jpl-c/simple-control-flow-jmp + * @problem.severity warning + */ + +import cpp + +class ForbiddenFunction extends Function { + ForbiddenFunction() { + exists(string name | name = this.getName() | + name = "setjmp" or name = "longjmp" or + name = "sigsetjmp" or name = "siglongjmp") + } +} + +from FunctionCall call +where call.getTarget() instanceof ForbiddenFunction +select call, "The " + call.getTarget().getName() + " function should not be used." diff --git a/cpp/ql/src/JPL_C/LOC-2/Rule 12/EnumInitialization.ql b/cpp/ql/src/JPL_C/LOC-2/Rule 12/EnumInitialization.ql new file mode 100644 index 000000000000..f6bb7cb4cbdc --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-2/Rule 12/EnumInitialization.ql @@ -0,0 +1,59 @@ +/** + * @name Irregular enum initialization + * @description In an enumerator list, the = construct should not be used to explicitly initialize members other than the first, unless all items are explicitly initialized. An exception is the pattern to use the last element of an enumerator list to get the number of possible values. + * @kind problem + * @id cpp/jpl-c/enum-initialization + * @problem.severity warning + */ +import cpp + +predicate hasInitializer(EnumConstant c) { + c.getInitializer().fromSource() +} + +/** Does this have an initializer that is not just a ref to another constant in the same enum? */ +predicate hasNonReferenceInitializer(EnumConstant c) { + exists (Initializer init | + init = c.getInitializer() and + init.fromSource() and + not init.getExpr().(EnumConstantAccess).getTarget().getDeclaringEnum() = c.getDeclaringEnum() + ) +} + +predicate hasReferenceInitializer(EnumConstant c) { + exists (Initializer init | + init = c.getInitializer() and + init.fromSource() and + init.getExpr().(EnumConstantAccess).getTarget().getDeclaringEnum() = c.getDeclaringEnum() + ) +} + + +// There exists another constant whose value is implicit, but it's +// not the last one: the last value is okay to use to get the highest +// enum value automatically. It can be followed by aliases though. +predicate enumThatHasConstantWithImplicitValue(Enum e) { + exists(EnumConstant ec, int pos | + ec = e.getEnumConstant(pos) and + not hasInitializer(ec) and + exists(EnumConstant ec2, int pos2 | + ec2 = e.getEnumConstant(pos2) and + pos2 > pos and + not hasReferenceInitializer(ec2) + ) + ) +} + +from Enum e, int i +where // e is at position i, and has an explicit value in the source - but + // not just a reference to another enum constant + hasNonReferenceInitializer(e.getEnumConstant(i)) and + // but e is not the first or the last constant of the enum + i != 0 and + exists(e.getEnumConstant(i+1)) and + // and there exists another constant whose value is implicit, but it's + // not the last one: the last value is okay to use to get the highest + // enum value automatically. It can be followed by aliases though. + enumThatHasConstantWithImplicitValue(e) + +select e, "In an enumerator list, the = construct should not be used to explicitly initialize members other than the first, unless all items are explicitly initialized." diff --git a/cpp/ql/src/JPL_C/LOC-3/Rule 13/ExternDeclsInHeader.ql b/cpp/ql/src/JPL_C/LOC-3/Rule 13/ExternDeclsInHeader.ql new file mode 100644 index 000000000000..f5c4ff91f412 --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-3/Rule 13/ExternDeclsInHeader.ql @@ -0,0 +1,15 @@ +/** + * @name "extern" declaration in source file + * @description All "extern" declarations should be placed in a header file that is included in every file referring to the corresponding data object. + * @kind problem + * @id cpp/jpl-c/extern-decls-in-header + * @problem.severity warning + */ + +import cpp + +from VariableDeclarationEntry v +where v.getVariable() instanceof GlobalVariable and + v.hasSpecifier("extern") and + not v.getFile() instanceof HeaderFile +select v, v.getName() + " should be declared only in a header file that is included as needed." diff --git a/cpp/ql/src/JPL_C/LOC-3/Rule 13/LimitedScopeFile.ql b/cpp/ql/src/JPL_C/LOC-3/Rule 13/LimitedScopeFile.ql new file mode 100644 index 000000000000..a895174e2157 --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-3/Rule 13/LimitedScopeFile.ql @@ -0,0 +1,16 @@ +/** + * @name Global could be static + * @description Global variables that are not accessed outside their own file should be made static to promote information hiding. + * @kind problem + * @id cpp/jpl-c/limited-scope-file + * @problem.severity warning + */ + +import cpp + +from GlobalVariable v +where forex(VariableAccess va | va.getTarget() = v | va.getFile() = v.getDefinitionLocation().getFile()) + and not v.hasSpecifier("static") + and strictcount(v.getAnAccess().getEnclosingFunction()) > 1 // If = 1, variable should be function-scope. +select v, "The global variable " + v.getName() + " is not accessed outside of " + v.getFile().getBaseName() + + " and could be made static." diff --git a/cpp/ql/src/JPL_C/LOC-3/Rule 13/LimitedScopeFunction.ql b/cpp/ql/src/JPL_C/LOC-3/Rule 13/LimitedScopeFunction.ql new file mode 100644 index 000000000000..40fa3b48a2d7 --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-3/Rule 13/LimitedScopeFunction.ql @@ -0,0 +1,14 @@ +/** + * @name Variable scope too large + * @description Global and file-scope variables that are accessed by only one function should be scoped within that function. + * @kind problem + * @id cpp/jpl-c/limited-scope-function + * @problem.severity warning + */ + +import cpp + +from GlobalVariable v, Function f +where v.getAnAccess().getEnclosingFunction() = f and + strictcount(v.getAnAccess().getEnclosingFunction()) = 1 +select v, "The variable " + v.getName() + " is only accessed in $@ and should be scoped accordingly.", f, f.getName() diff --git a/cpp/ql/src/JPL_C/LOC-3/Rule 13/LimitedScopeLocalHidesGlobal.ql b/cpp/ql/src/JPL_C/LOC-3/Rule 13/LimitedScopeLocalHidesGlobal.ql new file mode 100644 index 000000000000..7b37b6406d6f --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-3/Rule 13/LimitedScopeLocalHidesGlobal.ql @@ -0,0 +1,31 @@ +/** + * @name Local variable hides global variable + * @description A local variable or parameter that hides a global variable of the same name. + * @kind problem + * @id cpp/jpl-c/limited-scope-local-hides-global + * @problem.severity warning + */ +import cpp + +class LocalVariableOrParameter extends Variable { + LocalVariableOrParameter() { + this instanceof LocalVariable or + // A function declaration (i.e. "int foo(int bar);") doesn't usefully + // shadow globals; the parameter should be on the version of the function + // that has a body. + exists(Parameter p | p = this | + p.getFunction().getDefinitionLocation().getFile() = this.getFile() and + exists(p.getFunction().getBlock())) + } + + string type() { + if this instanceof Parameter + then result = "Parameter " + else result = "Local variable " + } +} + +from LocalVariableOrParameter lv, GlobalVariable gv +where lv.getName() = gv.getName() and + lv.getFile() = gv.getFile() +select lv, lv.type() + lv.getName() + " hides the global variable $@.", gv, gv.getName() diff --git a/cpp/ql/src/JPL_C/LOC-3/Rule 14/CheckingReturnValues.ql b/cpp/ql/src/JPL_C/LOC-3/Rule 14/CheckingReturnValues.ql new file mode 100644 index 000000000000..1bcc7784f917 --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-3/Rule 14/CheckingReturnValues.ql @@ -0,0 +1,36 @@ +/** + * @name Unchecked return value + * @description The return value of each non-void function call should be checked for error conditions, or cast to (void) if irrelevant. + * @kind problem + * @id cpp/jpl-c/checking-return-values + * @problem.severity warning + */ + +import cpp + +/** In its full generality, the rule applies to all functions that + * return non-void, including things like 'printf' and 'close', + * which are routinely not checked because the behavior on success + * is the same as the behavior on failure. The recommendation is + * to add an explicit cast to void for such functions. For code + * bases that have not been developed with this rule in mind, at + * least for such commonly ignored functions, it may be better to + * add them as exceptions to this whitelist predicate. + */ +predicate whitelist(Function f) { + // Example: + // f.hasName("printf") or f.hasName("close") or // ... + none() +} + +from FunctionCall c, string msg +where not c.getTarget().getType() instanceof VoidType + and not whitelist(c.getTarget()) + and + ( + (c instanceof ExprInVoidContext and msg = "The return value of non-void function $@ is not checked.") + or + (definition(_, c.getParent()) and not definitionUsePair(_, c.getParent(), _) and + msg = "$@'s return value is stored but not checked.") + ) +select c, msg, c.getTarget() as f, f.getName() diff --git a/cpp/ql/src/JPL_C/LOC-3/Rule 15/CheckingParameterValues.ql b/cpp/ql/src/JPL_C/LOC-3/Rule 15/CheckingParameterValues.ql new file mode 100644 index 000000000000..033597a2a15b --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-3/Rule 15/CheckingParameterValues.ql @@ -0,0 +1,24 @@ +/** + * @name Unchecked function argument + * @description Functions should check their arguments before their first use. + * @kind problem + * @id cpp/jpl-c/checking-parameter-values + * @problem.severity warning + */ + +import JPL_C.Tasks + +predicate flow(Parameter p, ControlFlowNode n) { + (exists(p.getAnAccess()) and n = p.getFunction().getBlock()) or + exists(ControlFlowNode mid | flow(p, mid) and not mid = p.getAnAccess() and n = mid.getASuccessor()) +} + +VariableAccess firstAccess(Parameter p) { + flow(p, result) and result = p.getAnAccess() +} + +from Parameter p, VariableAccess va +where va = firstAccess(p) and p.getFunction() instanceof PublicFunction and + not exists(Expr e | e.isCondition() | e.getAChild*() = va) +select va, "This use of parameter " + p.getName() + " has not been checked." + diff --git a/cpp/ql/src/JPL_C/LOC-3/Rule 16/UseOfAssertionsConstant.ql b/cpp/ql/src/JPL_C/LOC-3/Rule 16/UseOfAssertionsConstant.ql new file mode 100644 index 000000000000..c26f963f4021 --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-3/Rule 16/UseOfAssertionsConstant.ql @@ -0,0 +1,17 @@ +/** + * @name Constant assertion + * @description Assertions should check dynamic properties of pre-/post-conditions and invariants. Assertions that either always succeed or always fail are an error. + * @kind problem + * @id cpp/jpl-c/use-of-assertions-constant + * @problem.severity warning + */ + +import semmle.code.cpp.commons.Assertions + +from Assertion a, string value, string msg +where value = a.getAsserted().getValue() and + if value.toInt() = 0 then + msg = "This assertion is always false." + else + msg = "This assertion is always true." +select a.getAsserted(), msg diff --git a/cpp/ql/src/JPL_C/LOC-3/Rule 16/UseOfAssertionsDensity.ql b/cpp/ql/src/JPL_C/LOC-3/Rule 16/UseOfAssertionsDensity.ql new file mode 100644 index 000000000000..99fc91d9b178 --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-3/Rule 16/UseOfAssertionsDensity.ql @@ -0,0 +1,14 @@ +/** + * @name Long function without assertion + * @description All functions of more than 10 lines should have at least one assertion. + * @kind problem + * @id cpp/jpl-c/use-of-assertions-density + * @problem.severity warning + */ + +import semmle.code.cpp.commons.Assertions + +from Function f +where f.getMetrics().getNumberOfLinesOfCode() > 10 and + not exists(Assertion a | a.getAsserted().getEnclosingFunction() = f) +select f, "All functions of more than 10 lines should have at least one assertion." diff --git a/cpp/ql/src/JPL_C/LOC-3/Rule 16/UseOfAssertionsNonBoolean.ql b/cpp/ql/src/JPL_C/LOC-3/Rule 16/UseOfAssertionsNonBoolean.ql new file mode 100644 index 000000000000..232deb919f6e --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-3/Rule 16/UseOfAssertionsNonBoolean.ql @@ -0,0 +1,13 @@ +/** + * @name Non-boolean assertion + * @description Assertions should be defined as Boolean tests, meaning "assert(p != NULL)" rather than "assert(p)". + * @kind problem + * @id cpp/jpl-c/use-of-assertions-non-boolean + * @problem.severity warning + */ + +import semmle.code.cpp.commons.Assertions + +from Assertion a +where a.getAsserted().getType() instanceof PointerType +select a.getAsserted(), "Assertions should be defined as Boolean tests." diff --git a/cpp/ql/src/JPL_C/LOC-3/Rule 16/UseOfAssertionsSideEffect.ql b/cpp/ql/src/JPL_C/LOC-3/Rule 16/UseOfAssertionsSideEffect.ql new file mode 100644 index 000000000000..226d8c912c98 --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-3/Rule 16/UseOfAssertionsSideEffect.ql @@ -0,0 +1,13 @@ +/** + * @name Assertion with side effects + * @description Assertions should not have side-effects -- they may be disabled completely, changing program behavior. + * @kind problem + * @id cpp/jpl-c/use-of-assertions-side-effect + * @problem.severity warning + */ + +import semmle.code.cpp.commons.Assertions + +from Assertion a +where not a.getAsserted().isPure() +select a.getAsserted(), "Assertions should not have side effects." diff --git a/cpp/ql/src/JPL_C/LOC-3/Rule 17/BasicIntTypes.ql b/cpp/ql/src/JPL_C/LOC-3/Rule 17/BasicIntTypes.ql new file mode 100644 index 000000000000..c837c2a61ffd --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-3/Rule 17/BasicIntTypes.ql @@ -0,0 +1,51 @@ +/** + * @name Use of basic integral type + * @description Typedefs that indicate size and signedness should be used in place of the basic types. + * @kind problem + * @id cpp/jpl-c/basic-int-types + * @problem.severity warning + */ + +import cpp + +predicate allowedTypedefs(TypedefType t) { + exists(string name | name = t.getName() | + name = "I64" or name = "U64" or + name = "I32" or name = "U32" or + name = "I16" or name = "U16" or + name = "I8" or name = "U8" or + name = "F64" or name = "F32" + ) +} + +/** + * Gets a type which appears literally in the declaration of `d`. + */ +Type getAnImmediateUsedType(Declaration d) { + d.isDefined() and ( + result = d.(Function).getType() or + result = d.(Variable).getType() + ) +} + +/** + * Gets a type which appears indirectly in `t`, stopping at allowed typedefs. + */ +Type getAUsedType(Type t) { + not allowedTypedefs(t) and + ( + result = t.(TypedefType).getBaseType() or + result = t.(DerivedType).getBaseType() + ) +} + +predicate problematic(IntegralType t) { + // List any exceptions that should be allowed. + any() +} + +from Declaration d, Type usedType +where usedType = getAUsedType*(getAnImmediateUsedType(d)) and problematic(usedType) + // Ignore violations for which we do not have a valid location. + and not(d.getLocation() instanceof UnknownLocation) +select d, d.getName() + " uses the basic integral type " + usedType.getName() + " rather than a typedef with size and signedness." diff --git a/cpp/ql/src/JPL_C/LOC-3/Rule 18/CompoundExpressions.ql b/cpp/ql/src/JPL_C/LOC-3/Rule 18/CompoundExpressions.ql new file mode 100644 index 000000000000..47c51e11b8d1 --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-3/Rule 18/CompoundExpressions.ql @@ -0,0 +1,18 @@ +/** + * @name Implicit precedence in compound expression + * @description In compound expressions with multiple sub-expressions the intended order of evaluation shall be made explicit with parentheses. + * @kind problem + * @id cpp/jpl-c/compound-expressions + * @problem.severity warning + */ + +import cpp + +from BinaryOperation parent, BinaryOperation child +where parent.getAnOperand() = child and not child.isParenthesised() and + (parent instanceof BinaryBitwiseOperation or child instanceof BinaryBitwiseOperation) and + // Some benign cases... + not (parent instanceof BitwiseAndExpr and child instanceof BitwiseAndExpr) and + not (parent instanceof BitwiseOrExpr and child instanceof BitwiseOrExpr) and + not (parent instanceof BitwiseXorExpr and child instanceof BitwiseXorExpr) +select parent, "This expression involving bitwise operations should be bracketed." diff --git a/cpp/ql/src/JPL_C/LOC-3/Rule 19/NoBooleanSideEffects.ql b/cpp/ql/src/JPL_C/LOC-3/Rule 19/NoBooleanSideEffects.ql new file mode 100644 index 000000000000..d2d90a5d4344 --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-3/Rule 19/NoBooleanSideEffects.ql @@ -0,0 +1,89 @@ +/** + * @name Side effect in a Boolean expression + * @description The evaluation of a Boolean expression shall have no side effects. + * @kind problem + * @id cpp/jpl-c/no-boolean-side-effects + * @problem.severity warning + */ + +import cpp + +/** + * A whitelist of functions that should be considered + * side-effect free. + */ +predicate safeFunctionWhitelist(Function f) { + exists(string name | name = f.getName() | + // List functions by name which are not correctly identified + // as side-effect free. For example, for strlen, one might do: + // name = "strlen" or + none() + ) +} + +/** + * Gets a "pointer type" contained in the given type. This + * traverses typedefs and derived types, including types of + * struct or union members, returning each "pointer to X" + * type encountered on that traversal. + */ +PointerType getAPointerType(Type t) { + result = t or + result = getAPointerType(t.getUnderlyingType()) or + result = getAPointerType(t.(DerivedType).getBaseType()) or + result = getAPointerType(t.(Class).getAMemberVariable().getType()) +} + +/** + * A function is "inherently unsafe" for side effects if it + * writes a global or static variable, or if it calls another + * inherently unsafe function. + */ +predicate inherentlyUnsafe(Function f) { + exists(Variable v | v.getAnAssignedValue().getEnclosingFunction() = f | + v instanceof GlobalVariable or + v.isStatic() + ) or + exists(FunctionCall c | c.getEnclosingFunction() = f | + inherentlyUnsafe(c.getTarget()) + ) +} + +/** + * Find functions that are "safe to call" without causing a side effect. + * Being safe to call means that any "pointer type" in an argument type + * actually refers to a "const" object, and, moreover, the function is + * not inherently unsafe. + */ +predicate safeToCall(Function f) { + forall(PointerType paramPointerType | paramPointerType = getAPointerType(f.getAParameter().getType()) | + paramPointerType.getBaseType().isConst() + ) and + not inherentlyUnsafe(f) +} + +/** + * A "Boolean expression" is an expression forbidden from having side effects + * by this rule. + */ +class BooleanExpression extends Expr { + BooleanExpression() { + exists(Loop l | l.getControllingExpr() = this) or + exists(IfStmt i | i.getCondition() = this) or + exists(ConditionalExpr e | e.getCondition() = this) + } +} + +predicate hasSideEffect(Expr e) { + e instanceof Assignment or + e instanceof CrementOperation or + e instanceof ExprCall or + exists(Function f | f = e.(FunctionCall).getTarget() and not safeFunctionWhitelist(f) | + inherentlyUnsafe(f) or not safeToCall(f) + ) or + hasSideEffect(e.getAChild()) +} + +from BooleanExpression b +where hasSideEffect(b) +select b, "This Boolean expression is not side-effect free." diff --git a/cpp/ql/src/JPL_C/LOC-4/Rule 20/PreprocessorUse.ql b/cpp/ql/src/JPL_C/LOC-4/Rule 20/PreprocessorUse.ql new file mode 100644 index 000000000000..75c11427c214 --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-4/Rule 20/PreprocessorUse.ql @@ -0,0 +1,20 @@ +/** + * @name Disallowed preprocessor use + * @description The use of the preprocessor must be limited to inclusion of header files and simple macro definitions. + * @kind problem + * @id cpp/jpl-c/preprocessor-use + * @problem.severity warning + */ + +import cpp + +from PreprocessorDirective p +where not p instanceof Include and + not p instanceof Macro and + not p instanceof PreprocessorIf and + not p instanceof PreprocessorElif and + not p instanceof PreprocessorElse and + not p instanceof PreprocessorIfdef and + not p instanceof PreprocessorIfndef and + not p instanceof PreprocessorEndif +select p, "This preprocessor directive is not allowed." diff --git a/cpp/ql/src/JPL_C/LOC-4/Rule 20/PreprocessorUseIfdef.ql b/cpp/ql/src/JPL_C/LOC-4/Rule 20/PreprocessorUseIfdef.ql new file mode 100644 index 000000000000..64eda91edf37 --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-4/Rule 20/PreprocessorUseIfdef.ql @@ -0,0 +1,14 @@ +/** + * @name Conditional compilation + * @description The use of conditional compilation directives must be kept to a minimum -- e.g. for header guards only. + * @kind problem + * @id cpp/jpl-c/preprocessor-use-ifdef + * @problem.severity warning + */ + +import cpp + +from PreprocessorDirective i +where (i instanceof PreprocessorIf or i instanceof PreprocessorIfdef or i instanceof PreprocessorIfndef) + and not i.getFile() instanceof HeaderFile +select i, "Use of conditional compilation must be kept to a minimum." diff --git a/cpp/ql/src/JPL_C/LOC-4/Rule 20/PreprocessorUsePartial.ql b/cpp/ql/src/JPL_C/LOC-4/Rule 20/PreprocessorUsePartial.ql new file mode 100644 index 000000000000..2b9c5fd22803 --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-4/Rule 20/PreprocessorUsePartial.ql @@ -0,0 +1,27 @@ +/** + * @name Partial macro + * @description Macros must expand to complete syntactic units -- "#define MY_IF if(" is not legal. + * @kind problem + * @id cpp/jpl-c/preprocessor-use-partial + * @problem.severity warning + */ + +import cpp + +predicate incomplete(Macro m) { + exists(string body | body = m.getBody() and not m.getBody().matches("%\\") | + body.regexpMatch("[^(]*\\).*") or + body.regexpMatch("[^\\[]*].*") or + body.regexpMatch("[^{]*}.*") or + body.regexpMatch(".*\\([^)]*") or + body.regexpMatch(".*\\[[^\\]]*") or + body.regexpMatch(".*\\{[^}]*") or + count(body.indexOf("(")) != count(body.indexOf(")")) or + count(body.indexOf("[")) != count(body.indexOf("]")) or + count(body.indexOf("{")) != count(body.indexOf("}")) + ) +} + +from Macro m +where incomplete(m) +select m, "The macro " + m.getHead() + " will not expand into a syntactic unit." diff --git a/cpp/ql/src/JPL_C/LOC-4/Rule 20/PreprocessorUseUndisciplined.ql b/cpp/ql/src/JPL_C/LOC-4/Rule 20/PreprocessorUseUndisciplined.ql new file mode 100644 index 000000000000..6fb4a6861e49 --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-4/Rule 20/PreprocessorUseUndisciplined.ql @@ -0,0 +1,14 @@ +/** + * @name Undisciplined macro + * @description Macros are not allowed to use complex preprocessor features like variable argument lists and token pasting. + * @kind problem + * @id cpp/jpl-c/preprocessor-use-undisciplined + * @problem.severity warning + */ + +import cpp + +from Macro m, string msg +where (m.getHead().matches("%...%") and msg = "The macro " + m.getHead() + " is variadic, and hence not allowed.") or + (m.getBody().matches("%##%") and msg = "The macro " + m.getHead() + " uses token pasting and is not allowed.") +select m, msg 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 new file mode 100644 index 000000000000..de2d52bc0594 --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-4/Rule 21/MacroInBlock.ql @@ -0,0 +1,19 @@ +/** + * @name Macro definition in block + * @description Macros shall not be #define'd within a function or a block. + * @kind problem + * @id cpp/jpl-c/macro-in-block + * @problem.severity warning + */ + +import cpp + +int lineInBlock(File f) { + exists(Block block, Location blockLocation | block.getFile() = f and blockLocation = block.getLocation()| + result in [blockLocation.getStartLine()..blockLocation.getEndLine()] + ) +} + +from Macro m +where m.getLocation().getStartLine() = lineInBlock(m.getFile()) +select m, "The macro " + m.getHead() + " is defined in a block." diff --git a/cpp/ql/src/JPL_C/LOC-4/Rule 22/UseOfUndef.ql b/cpp/ql/src/JPL_C/LOC-4/Rule 22/UseOfUndef.ql new file mode 100644 index 000000000000..47fab9617114 --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-4/Rule 22/UseOfUndef.ql @@ -0,0 +1,12 @@ +/** + * @name Use of #undef + * @description #undef shall not be used. + * @kind problem + * @id cpp/jpl-c/use-of-undef + * @problem.severity warning + */ + +import cpp + +from PreprocessorUndef u +select u, "The #undef directive shall not be used." diff --git a/cpp/ql/src/JPL_C/LOC-4/Rule 23/MismatchedIfdefs.ql b/cpp/ql/src/JPL_C/LOC-4/Rule 23/MismatchedIfdefs.ql new file mode 100644 index 000000000000..345044449d7c --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-4/Rule 23/MismatchedIfdefs.ql @@ -0,0 +1,89 @@ +/** + * @name Mismatched conditional compilation directive + * @description All #else, #elif and #endif preprocessor directives shall reside in the same file as the #if or #ifdef directive to which they are related. + * @kind problem + * @id cpp/jpl-c/mismatched-ifdefs + * @problem.severity warning + */ + +import cpp + +class FileWithDirectives extends File { + FileWithDirectives() { + exists(Directive d | d.getFile() = this) + } + + int getDirectiveLine(Directive d) { + d.getFile() = this and d.getLocation().getStartLine() = result + } + + int getDirectiveIndex(Directive d) { + exists(int line | line = getDirectiveLine(d) | + line = rank[result](getDirectiveLine(_)) + ) + } + + int depth(Directive d) { + exists(int index | index = getDirectiveIndex(d) | + (index = 1 and result = d.depthChange()) or + exists(Directive prev | getDirectiveIndex(prev) = index-1 | + result = d.depthChange() + depth(prev) + ) + ) + } + + Directive lastDirective() { + getDirectiveIndex(result) = max(getDirectiveIndex(_)) + } +} + +abstract class Directive extends PreprocessorDirective { + abstract int depthChange(); + abstract predicate mismatched(); + + int depth() { + exists(FileWithDirectives f | + f.depth(this) = result + ) + } +} + +class IfDirective extends Directive { + IfDirective() { + this instanceof PreprocessorIf or + this instanceof PreprocessorIfdef or + this instanceof PreprocessorIfndef + } + + override int depthChange() { result = 1 } + override predicate mismatched() { none() } +} + +class ElseDirective extends Directive { + ElseDirective() { + this instanceof PreprocessorElif or + this instanceof PreprocessorElse + } + + override int depthChange() { result = 0 } + override predicate mismatched() { depth() < 1 } +} + +class EndifDirective extends Directive { + EndifDirective() { + this instanceof PreprocessorEndif + } + + override int depthChange() { result = -1 } + override predicate mismatched() { depth() < 0 } +} + +from FileWithDirectives f, Directive d, string msg +where d.getFile() = f and + if d.mismatched() then ( + msg = "'" + d + "' has no matching #if in file " + f.getBaseName() + "." + ) else ( + d = f.lastDirective() and d.depth() > 0 and msg = "File " + f.getBaseName() + + " ends with " + d.depth() + " unterminated #if directives." + ) +select d, msg diff --git a/cpp/ql/src/JPL_C/LOC-4/Rule 24/MultipleStmtsPerLine.ql b/cpp/ql/src/JPL_C/LOC-4/Rule 24/MultipleStmtsPerLine.ql new file mode 100644 index 000000000000..9e76ffeeb22a --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-4/Rule 24/MultipleStmtsPerLine.ql @@ -0,0 +1,34 @@ +/** + * @name More than one statement per line + * @description Putting more than one statement on a single line hinders program understanding. + * @kind problem + * @id cpp/jpl-c/multiple-stmts-per-line + * @problem.severity warning + */ + +import cpp + +class OneLineStmt extends Stmt { + OneLineStmt() { + this.getLocation().getStartLine() = this.getLocation().getEndLine() and + not exists(ForStmt for | this = for.getInitialization()) + } + + predicate onLine(File f, int line) { + f = this.getFile() and line = this.getLocation().getStartLine() + } +} + +int numStmt(File f, int line) { + result = strictcount(OneLineStmt o | o.onLine(f, line)) +} + +from File f, int line, OneLineStmt o, int cnt +where numStmt(f, line) = cnt + and cnt > 1 + and o.onLine(f, line) + and o.getLocation().getStartColumn() = + min(OneLineStmt other, int toMin + | other.onLine(f, line) and toMin = other.getLocation().getStartColumn() + | toMin) +select o, "This line contains " + cnt + " statements; only one is allowed." diff --git a/cpp/ql/src/JPL_C/LOC-4/Rule 24/MultipleVarDeclsPerLine.ql b/cpp/ql/src/JPL_C/LOC-4/Rule 24/MultipleVarDeclsPerLine.ql new file mode 100644 index 000000000000..15a3b3f0ea6a --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-4/Rule 24/MultipleVarDeclsPerLine.ql @@ -0,0 +1,16 @@ +/** + * @name Multiple variable declarations on one line + * @description There should be no more than one variable declaration per line. + * @kind problem + * @id cpp/jpl-c/multiple-var-decls-per-line + * @problem.severity warning + */ + +import cpp + +from DeclStmt d +where exists(Variable v1, Variable v2 | v1 = d.getADeclaration() and v2 = d.getADeclaration() | + v1 != v2 and + v1.getLocation().getStartLine() = v2.getLocation().getStartLine() + ) +select d, "Multiple variable declarations on the same line." diff --git a/cpp/ql/src/JPL_C/LOC-4/Rule 25/FunctionSizeLimits.ql b/cpp/ql/src/JPL_C/LOC-4/Rule 25/FunctionSizeLimits.ql new file mode 100644 index 000000000000..788bb578d030 --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-4/Rule 25/FunctionSizeLimits.ql @@ -0,0 +1,28 @@ +/** + * @name Function too long + * @description Function length should be limited to what can be printed on a single sheet of paper (60 lines). Number of parameters is limited to 6 or fewer. + * @kind problem + * @id cpp/jpl-c/function-size-limits + * @problem.severity warning + */ + +import cpp + +string lengthWarning(Function f) { + exists(int lines | lines = f.getMetrics().getNumberOfLines() | + lines > 60 and + result = f.getName() + " has too many lines (" + lines + ", while 60 are allowed)." + ) +} + +string paramWarning(Function f) { + exists(int params | params = f.getMetrics().getNumberOfParameters() | + params > 6 and + result = f.getName() + " has too many parameters (" + params + ", while 6 are allowed)." + ) +} + +from Function f, string msg +where msg = lengthWarning(f) or + msg = paramWarning(f) +select f, msg diff --git a/cpp/ql/src/JPL_C/LOC-4/Rule 26/DeclarationPointerNesting.ql b/cpp/ql/src/JPL_C/LOC-4/Rule 26/DeclarationPointerNesting.ql new file mode 100644 index 000000000000..23fedeab354f --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-4/Rule 26/DeclarationPointerNesting.ql @@ -0,0 +1,29 @@ +/** + * @name Pointer indirection in declaration too high + * @description The declaration of an object should contain no more than two levels of indirection. + * @kind problem + * @id cpp/jpl-c/declaration-pointer-nesting + * @problem.severity warning + */ + +import cpp + +string var(Variable v) { + exists(int level | level = v.getType().getPointerIndirectionLevel() | + level > 2 and + result = "The type of " + v.getName() + " uses " + level + + " levels of pointer indirection -- maximum allowed is 2." + ) +} + +string fun(Function f) { + exists(int level | level = f.getType().getPointerIndirectionLevel() | + level > 2 and + result = "The return type of " + f.getName() + " uses " + level + + " levels of pointer indirection -- maximum allowed is 2." + ) +} + +from Declaration d, string msg +where msg = var(d) or msg = fun(d) +select d, msg diff --git a/cpp/ql/src/JPL_C/LOC-4/Rule 27/PointerDereferenceInStmt.ql b/cpp/ql/src/JPL_C/LOC-4/Rule 27/PointerDereferenceInStmt.ql new file mode 100644 index 000000000000..e173b18f95ae --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-4/Rule 27/PointerDereferenceInStmt.ql @@ -0,0 +1,15 @@ +/** + * @name Too many pointer dereferences in statement + * @description Statements should contain no more than two levels of dereferencing per object. + * @kind problem + * @id cpp/jpl-c/pointer-dereference-in-stmt + * @problem.severity warning + */ + +import cpp + +from PointerDereferenceExpr e, int n +where not e.getParent+() instanceof PointerDereferenceExpr + and n = strictcount(PointerDereferenceExpr child | child.getParent+() = e) + and n > 1 +select e, "This expression involves " + n + " levels of pointer dereference; 2 are allowed." diff --git a/cpp/ql/src/JPL_C/LOC-4/Rule 28/HiddenPointerDereferenceMacro.ql b/cpp/ql/src/JPL_C/LOC-4/Rule 28/HiddenPointerDereferenceMacro.ql new file mode 100644 index 000000000000..397c558e8681 --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-4/Rule 28/HiddenPointerDereferenceMacro.ql @@ -0,0 +1,19 @@ +/** + * @name Pointer dereference hidden in macro + * @description Pointer dereference operations should not be hidden in macro definitions. + * @kind problem + * @id cpp/jpl-c/hidden-pointer-dereference-macro + * @problem.severity warning + */ + +import cpp + +from Macro m +where forex(MacroInvocation mi | mi.getMacro() = m | + exists(PointerDereferenceExpr e, Location miLoc, Location eLoc | e = mi.getAGeneratedElement() | + miLoc = mi.getLocation() and eLoc = e.getLocation() and + eLoc.getStartColumn() = miLoc.getStartColumn() and + eLoc.getStartLine() = miLoc.getStartLine() + ) + ) +select m, "The macro " + m.getHead() + " hides pointer dereference operations." diff --git a/cpp/ql/src/JPL_C/LOC-4/Rule 28/HiddenPointerIndirectionTypedef.ql b/cpp/ql/src/JPL_C/LOC-4/Rule 28/HiddenPointerIndirectionTypedef.ql new file mode 100644 index 000000000000..db4b7e9b4607 --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-4/Rule 28/HiddenPointerIndirectionTypedef.ql @@ -0,0 +1,13 @@ +/** + * @name Hidden pointer indirection + * @description Pointer indirection may not be hidden by typedefs -- "typedef int* IntPtr;" is not allowed. + * @kind problem + * @id cpp/jpl-c/hidden-pointer-indirection-typedef + * @problem.severity warning + */ + +import cpp + +from TypedefType t +where t.getBaseType().getPointerIndirectionLevel() > 0 +select t, "The typedef " + t.getName() + " hides pointer indirection." diff --git a/cpp/ql/src/JPL_C/LOC-4/Rule 29/NonConstFunctionPointer.ql b/cpp/ql/src/JPL_C/LOC-4/Rule 29/NonConstFunctionPointer.ql new file mode 100644 index 000000000000..0296a582852c --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-4/Rule 29/NonConstFunctionPointer.ql @@ -0,0 +1,13 @@ +/** + * @name Use of non-constant function pointer + * @description Non-constant pointers to functions should not be used. + * @kind problem + * @id cpp/jpl-c/non-const-function-pointer + * @problem.severity warning + */ + +import cpp + +from ExprCall c +where not c.getExpr().getType().isConst() +select c, "This call does not go through a const function pointer." diff --git a/cpp/ql/src/JPL_C/LOC-4/Rule 30/FunctionPointerConversions.ql b/cpp/ql/src/JPL_C/LOC-4/Rule 30/FunctionPointerConversions.ql new file mode 100644 index 000000000000..22255b161b64 --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-4/Rule 30/FunctionPointerConversions.ql @@ -0,0 +1,23 @@ +/** + * @name Invalid function pointer conversion + * @description Conversions shall not be performed between a pointer to a function and any type other than an integral type. + * @kind problem + * @id cpp/jpl-c/function-pointer-conversions + * @problem.severity warning + */ + +import cpp + +predicate permissibleConversion(Type t) { + t instanceof IntegralType or + t instanceof FunctionPointerType or + permissibleConversion(t.getUnspecifiedType()) or + permissibleConversion(t.(TypedefType).getBaseType()) or + permissibleConversion(t.(ReferenceType).getBaseType()) +} + +from Expr e, Type converted +where e.getType() instanceof FunctionPointerType and + e.getFullyConverted().getType() = converted and + not permissibleConversion(converted) +select e, "Function pointer converted to " + converted.getName() + ", which is not an integral type." diff --git a/cpp/ql/src/JPL_C/LOC-4/Rule 31/IncludesFirst.ql b/cpp/ql/src/JPL_C/LOC-4/Rule 31/IncludesFirst.ql new file mode 100644 index 000000000000..77a6e75c36ba --- /dev/null +++ b/cpp/ql/src/JPL_C/LOC-4/Rule 31/IncludesFirst.ql @@ -0,0 +1,26 @@ +/** + * @name Misplaced include + * @description #include directives in a file shall only be preceded by other preprocessor directives or comments. + * @kind problem + * @id cpp/jpl-c/includes-first + * @problem.severity warning + */ + +import cpp + +int firstCodeLine(File f) { + result = min(Declaration d, Location l, int toMin | (l = d.getLocation() and + l.getFile() = f and not d.isInMacroExpansion()) and (toMin = l.getStartLine()) | toMin) +} + +int badIncludeLine(File f, Include i) { + result = i.getLocation().getStartLine() and + result > firstCodeLine(f) and + f = i.getFile() +} + +from File f, Include i, int line +where line = badIncludeLine(f, i) and + line = min(badIncludeLine(f, _)) +select i, "'" + i.toString() + "' is preceded by code -- it should be moved above line " + + firstCodeLine(f) + " in " + f.getBaseName() + "." diff --git a/cpp/ql/src/JPL_C/Tasks.qll b/cpp/ql/src/JPL_C/Tasks.qll new file mode 100644 index 000000000000..9f582585c20b --- /dev/null +++ b/cpp/ql/src/JPL_C/Tasks.qll @@ -0,0 +1,30 @@ +import cpp + +/** + * A function that is used as the entry point of a VxWorks task. + */ +class Task extends Function { + Task() { + exists(FunctionCall taskCreate, string name | name = "taskCreate" or name = "taskSpawn" | + name = taskCreate.getTarget().getName() and + this = taskCreate.getArgument(4).(AddressOfExpr).getAddressable() + ) + } +} + +/** + * From the JPL standard: "A public function is a function that is used + * by multiple tasks, such as a library function". We additionally say that + * a function is not public if it's defined in the same file as a task. + * + * And alternative definition could be to say that all functions defined in + * files that don't define tasks are public. + */ +class PublicFunction extends Function { + PublicFunction() { + not this.isStatic() and ( + strictcount(Task t | t.calls+(this)) > 1 or + not exists(Task t | t.getFile() = this.getFile()) + ) + } +} diff --git a/cpp/ql/src/Likely Bugs/AmbiguouslySignedBitField.cpp b/cpp/ql/src/Likely Bugs/AmbiguouslySignedBitField.cpp new file mode 100644 index 000000000000..7202122f30cd --- /dev/null +++ b/cpp/ql/src/Likely Bugs/AmbiguouslySignedBitField.cpp @@ -0,0 +1,5 @@ +struct { + int s : 4; //wrong: behavior of bit-field members with implicit signage vary across compilers + unsigned int : 24; //correct: explicitly unsigned + signed int : 4; //correct: explicitly signed +} bits; diff --git a/cpp/ql/src/Likely Bugs/AmbiguouslySignedBitField.qhelp b/cpp/ql/src/Likely Bugs/AmbiguouslySignedBitField.qhelp new file mode 100644 index 000000000000..7f7fcdcd3ea2 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/AmbiguouslySignedBitField.qhelp @@ -0,0 +1,40 @@ + + + + + +

    + +The signedness of a plain char, short, int, or long bit field is implementation-specific in C and in +older versions of C++, and declaring their signedness explicitly removes the ambiguity and ensures +portability. +

    + + +
    + +

    +Declare all members of the bit field with explicit signedness. +

    + +
    + + + + + + + + +
  • + AV Rule 154, Joint Strike Fighter Air Vehicle C++ Coding Standards. Lockheed Martin Corporation, 2005. +
  • +
  • + C++ Bit Fields +
  • + + +
    +
    diff --git a/cpp/ql/src/Likely Bugs/AmbiguouslySignedBitField.ql b/cpp/ql/src/Likely Bugs/AmbiguouslySignedBitField.ql new file mode 100644 index 000000000000..321590ecc8fd --- /dev/null +++ b/cpp/ql/src/Likely Bugs/AmbiguouslySignedBitField.ql @@ -0,0 +1,30 @@ +/** + * @name Ambiguously signed bit-field member + * @description Bit fields with integral types should have explicit signedness + * only. For example, use `unsigned int` rather than `int`. It is + * implementation specific whether an `int`-typed bit field is + * signed, so there could be unexpected sign extension or + * overflow. + * @kind problem + * @problem.severity warning + * @precision high + * @id cpp/ambiguously-signed-bit-field + * @tags reliability + * readability + * language-features + * external/cwe/cwe-190 + */ +import cpp + +from BitField bf +where not bf.getType().getUnspecifiedType().(IntegralType).isExplicitlySigned() + and not bf.getType().getUnspecifiedType().(IntegralType).isExplicitlyUnsigned() + and not bf.getType().getUnspecifiedType() instanceof Enum + and not bf.getType().getUnspecifiedType() instanceof BoolType + // At least for C programs on Windows, BOOL is a common typedef for a type + // representing BoolType. + and not bf.getType().hasName("BOOL") + // If this is true, then there cannot be unsigned sign extension or overflow. + and not bf.getDeclaredNumBits() = bf.getType().getSize() * 8 + and not bf.isAnonymous() +select bf, "Bit field " + bf.getName() + " of type " + bf.getUnderlyingType().getName() + " should have explicitly unsigned integral, explicitly signed integral, or enumeration type." diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/BadAdditionOverflowCheck.qhelp b/cpp/ql/src/Likely Bugs/Arithmetic/BadAdditionOverflowCheck.qhelp new file mode 100644 index 000000000000..a8c2b1567a73 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Arithmetic/BadAdditionOverflowCheck.qhelp @@ -0,0 +1,37 @@ + + + +

    + Checking for overflow of integer addition needs to be done with + care, because automatic type promotion can prevent the check + from working correctly. +

    +
    + +

    + Use an explicit cast to make sure that the result of the addition is + not implicitly converted to a larger type. +

    +
    + + +

    + On a typical architecture where short is 16 bits + and int is 32 bits, the operands of the addition are + automatically promoted to int, so it cannot overflow + and the result of the comparison is always false. +

    +

    + The code below implements the check correctly, by using an + explicit cast to make sure that the result of the addition + is unsigned short. +

    + +
    + +
  • Preserving Rules
  • +
  • Understand integer conversion rules
  • +
    +
    diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/BadAdditionOverflowCheck.ql b/cpp/ql/src/Likely Bugs/Arithmetic/BadAdditionOverflowCheck.ql new file mode 100644 index 000000000000..852b73d41abf --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Arithmetic/BadAdditionOverflowCheck.ql @@ -0,0 +1,20 @@ +/** + * @name Bad check for overflow of integer addition + * @description Checking for overflow of integer addition by comparing + * against one of the arguments of the addition does not work + * when the result of the addition is automatically promoted + * to a larger type. + * @kind problem + * @problem.severity error + * @precision very-high + * @id cpp/bad-addition-overflow-check + * @tags reliability + * correctness + */ + +import cpp +import BadAdditionOverflowCheck + +from RelationalOperation cmp, AddExpr a +where badAdditionOverflowCheck(cmp, a) +select cmp, "Bad overflow check." diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/BadAdditionOverflowCheck.qll b/cpp/ql/src/Likely Bugs/Arithmetic/BadAdditionOverflowCheck.qll new file mode 100644 index 000000000000..e2b1b2579b3d --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Arithmetic/BadAdditionOverflowCheck.qll @@ -0,0 +1,47 @@ +/** + * Provides the implementation of the BadAdditionOverflowCheck query. The + * query is implemented as a library, so that we can avoid producing + * duplicate results in other similar queries. + */ + +import cpp +private import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils + +/** + * Holds if `a` and `b` are the operands of `plus`. This predicate + * simplifies the pattern matching logic in `badAdditionOverflowCheck` by + * swapping the operands both ways round. + */ +private predicate addExpr(AddExpr plus, Expr a, Expr b) { + (a = plus.getLeftOperand() and b = plus.getRightOperand()) or + (b = plus.getLeftOperand() and a = plus.getRightOperand()) +} + +/** + * Holds if `cmp` is an overflow check of the following form: + * + * a + b < a + * + * This check does not work if the operands of `a` and `b` are + * automatically promoted to a larger type. If + * `convertedExprMightOverflow(a)` does not hold, then it is impossible for + * the addition to overflow, so the result of the comparison will always be + * false. + */ +predicate badAdditionOverflowCheck(RelationalOperation cmp, AddExpr plus) { + exists (Variable v, VariableAccess a1, VariableAccess a2, Expr b + | addExpr(plus, a1, b) and + a1 = v.getAnAccess() and + a2 = v.getAnAccess() and + not exists (a1.getQualifier()) and // Avoid structure fields + not exists (a2.getQualifier()) and // Avoid structure fields + // Simple type-based check that the addition cannot overflow. + exprMinVal(plus) <= exprMinVal(a1) + exprMinVal(b) and + exprMaxVal(plus) > exprMaxVal(a1) and + exprMaxVal(plus) > exprMaxVal(b) and + // Make sure that the plus isn't explicitly cast to a smaller type. + exprMinVal(plus.getExplicitlyConverted()) <= exprMinVal(plus) and + exprMaxVal(plus.getExplicitlyConverted()) >= exprMaxVal(plus) and + cmp.getAnOperand() = plus and + cmp.getAnOperand() = a2) +} diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/BadAdditionOverflowCheckExample1.cpp b/cpp/ql/src/Likely Bugs/Arithmetic/BadAdditionOverflowCheckExample1.cpp new file mode 100644 index 000000000000..4da403cdf511 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Arithmetic/BadAdditionOverflowCheckExample1.cpp @@ -0,0 +1,3 @@ +bool checkOverflow(unsigned short x, unsigned short y) { + return (x + y < x); // BAD: x and y are automatically promoted to int. +} diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/BadAdditionOverflowCheckExample2.cpp b/cpp/ql/src/Likely Bugs/Arithmetic/BadAdditionOverflowCheckExample2.cpp new file mode 100644 index 000000000000..aba6c41ee988 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Arithmetic/BadAdditionOverflowCheckExample2.cpp @@ -0,0 +1,3 @@ +bool checkOverflow(unsigned short x, unsigned short y) { + return ((unsigned short)(x + y) < x); // GOOD: explicit cast +} diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/BadCheckOdd.cpp b/cpp/ql/src/Likely Bugs/Arithmetic/BadCheckOdd.cpp new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Arithmetic/BadCheckOdd.cpp @@ -0,0 +1 @@ + diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/BadCheckOdd.qhelp b/cpp/ql/src/Likely Bugs/Arithmetic/BadCheckOdd.qhelp new file mode 100644 index 000000000000..7689ada8748a --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Arithmetic/BadCheckOdd.qhelp @@ -0,0 +1,35 @@ + + + + + +

    This rule finds code that uses +x % 2 == 1 +to check whether a number x is odd, which does not work for negative numbers. +Applying % to negative numbers produces negative results. +For example, (-5) % 2 equals -1, not 1. +As a result, this check incorrectly considers all negative numbers as even. +

    + +
    + +

    Consider using +x % 2 != 0 or (x & 1) == 1 instead. +

    + +
    + + +
  • + MSDN Library: Multiplicative Operators: *, /, and %. +
  • +
  • + Wikipedia: Modulo Operation - Common pitfalls. +
  • + + + +
    +
    diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/BadCheckOdd.ql b/cpp/ql/src/Likely Bugs/Arithmetic/BadCheckOdd.ql new file mode 100644 index 000000000000..398e12ec9bed --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Arithmetic/BadCheckOdd.ql @@ -0,0 +1,21 @@ +/** + * @name Bad check for oddness + * @description Using "x % 2 == 1" to check whether x is odd does not work for + * negative numbers. + * @kind problem + * @problem.severity warning + * @precision high + * @id cpp/incomplete-parity-check + * @tags reliability + * correctness + * types + */ +import cpp + +from EqualityOperation t, RemExpr lhs, Literal rhs +where t.getLeftOperand() = lhs and + t.getRightOperand() = rhs and + lhs.getLeftOperand().getType().getUnspecifiedType().(IntegralType).isSigned() and + lhs.getRightOperand().getValue() = "2" and + rhs.getValue() = "1" +select t, "Possibly invalid test for oddness. This will fail for negative numbers." diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/BitwiseSignCheck.cpp b/cpp/ql/src/Likely Bugs/Arithmetic/BitwiseSignCheck.cpp new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Arithmetic/BitwiseSignCheck.cpp @@ -0,0 +1 @@ + diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/BitwiseSignCheck.qhelp b/cpp/ql/src/Likely Bugs/Arithmetic/BitwiseSignCheck.qhelp new file mode 100644 index 000000000000..560cdc2a40f4 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Arithmetic/BitwiseSignCheck.qhelp @@ -0,0 +1,36 @@ + + + + + +

    This rule finds code that checks the sign of the result of a bitwise operation. Such a check may yield unexpected results. As an example, consider the following code that checks if the nth bit of a variable x is set:

    + +
      x & (1 << n) > 0 
    + +

    If x is a 32-bit signed integer, the value of x & (1 << 31) is interpreted as a signed number. If x is negative (that is, its sign bit is set), and n is 31, then x & (1 << 31) evaluates to 0x80000000 (all bits zero except the sign bit). The sign check on this value fails, implying that the 31st bit of x is unset. This is clearly incorrect.

    + +
    + +

    The above sign check should be rewritten as

    + +
      x & (1 << n) != 0
    + +
    + + +
  • + Code Project: An introduction to bitwise operators +
  • +
  • + MSDN Library: Signed Bitwise Operations +
  • + + + + + + +
    +
    diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/BitwiseSignCheck.ql b/cpp/ql/src/Likely Bugs/Arithmetic/BitwiseSignCheck.ql new file mode 100644 index 000000000000..147e42151cb6 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Arithmetic/BitwiseSignCheck.ql @@ -0,0 +1,20 @@ +/** + * @name Sign check of bitwise operation + * @description Checking the sign of a bitwise operation often has surprising + * edge cases. + * @kind problem + * @problem.severity warning + * @precision high + * @id cpp/bitwise-sign-check + * @tags reliability + * correctness + */ +import cpp + +from RelationalOperation e, BinaryBitwiseOperation lhs +where lhs = e.getLeftOperand() and + lhs.getActualType().(IntegralType).isSigned() and + forall(int op | op = lhs.(BitwiseAndExpr).getAnOperand().getValue().toInt() | op < 0) and + e.getRightOperand().getValue() = "0" and + not e.isAffectedByMacro() +select e, "Potential unsafe sign check of a bitwise operation." diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/ComparisonPrecedence.cpp b/cpp/ql/src/Likely Bugs/Arithmetic/ComparisonPrecedence.cpp new file mode 100644 index 000000000000..07f1a0529e90 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Arithmetic/ComparisonPrecedence.cpp @@ -0,0 +1,6 @@ +void h() { + int a, b, c; + + a < b != c; //parenthesize to explicitly define order of operators + (a < b) < c; //correct: parenthesized to specify order +} diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/ComparisonPrecedence.qhelp b/cpp/ql/src/Likely Bugs/Arithmetic/ComparisonPrecedence.qhelp new file mode 100644 index 000000000000..1ffefa41799f --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Arithmetic/ComparisonPrecedence.qhelp @@ -0,0 +1,38 @@ + + + + + +

    +This rule finds comparison expressions that use 2 or more comparison operators and are not completely paranthesized. +It is best to fully parenthesize complex comparison expressions to explicitly define the order of the comparison operators. +

    + +
    + +

    Fully parenthesize complex comparison expressions to avoid confusion.

    + +
    + + + + + +
  • + Operator Precedence and Associativity +
  • +
  • + Operators +
  • +
  • + EXP00-C. Use parentheses for precedence of operation +
  • + + + + + +
    +
    diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/ComparisonPrecedence.ql b/cpp/ql/src/Likely Bugs/Arithmetic/ComparisonPrecedence.ql new file mode 100644 index 000000000000..4e0fd8d9eeea --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Arithmetic/ComparisonPrecedence.ql @@ -0,0 +1,18 @@ +/** + * @name Unclear comparison precedence + * @description Using comparisons as operands of other comparisons is unusual + * in itself, and most readers will require parentheses to be sure + * of the precedence. + * @kind problem + * @problem.severity warning + * @precision very-high + * @id cpp/comparison-precedence + * @tags maintainability + * readability + */ +import cpp + + +from ComparisonOperation co, ComparisonOperation chco +where co.getAChild() = chco and not chco.isParenthesised() +select co, "Check the comparison operator precedence." diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/ComparisonWithCancelingSubExpr.qhelp b/cpp/ql/src/Likely Bugs/Arithmetic/ComparisonWithCancelingSubExpr.qhelp new file mode 100644 index 000000000000..1cfdd6667122 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Arithmetic/ComparisonWithCancelingSubExpr.qhelp @@ -0,0 +1,22 @@ + + + +

    + If a comparison contains multiple linear sub-expressions which + cancel each other out, then the comparison can be simplified by + removing them. This may be indicative of a coding error. +

    +
    + +

    + Check that the redundant sub-expressions are not the result of a + coding error. If not, then simplify the comparison by removing + them. +

    +
    + + + +
    diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/ComparisonWithCancelingSubExpr.ql b/cpp/ql/src/Likely Bugs/Arithmetic/ComparisonWithCancelingSubExpr.ql new file mode 100644 index 000000000000..882e44f83a3c --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Arithmetic/ComparisonWithCancelingSubExpr.ql @@ -0,0 +1,97 @@ +/** + * @name Comparison with canceling sub-expression + * @description If the same sub-expression is added to both sides of a + * comparison, and there is no possibility of overflow or + * rounding, then the sub-expression is redundant and could be + * removed. + * @kind problem + * @problem.severity recommendation + * @precision medium + * @id cpp/comparison-canceling-subexpr + * @tags readability + * maintainability + */ + +import cpp +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis +import BadAdditionOverflowCheck +import PointlessSelfComparison + +/** + * Holds if `parent` is a linear expression of `child`. For example: + * + * `parent = child + E` + * `parent = E - child` + * `parent = 2 * child` + */ +private predicate linearChild(Expr parent, Expr child, float multiplier) { + (child = parent.(AddExpr).getAChild() and multiplier = 1.0) or + (child = parent.(SubExpr).getLeftOperand() and multiplier = 1.0) or + (child = parent.(SubExpr).getRightOperand() and multiplier = -1.0) or + (child = parent.(UnaryPlusExpr).getOperand() and multiplier = 1.0) or + (child = parent.(UnaryMinusExpr).getOperand() and multiplier = -1.0) +} + +/** + * Holds if `child` is a linear sub-expression of `cmp`, and `multiplier` + * is its multiplication factor. For example: + * + * `4*x - y < 3*z` + * + * In this example, `x` has multiplier 4, `y` has multiplier -1, and `z` + * has multiplier -3 (multipliers from the right hand child are negated). + */ +private predicate cmpLinearSubExpr( + ComparisonOperation cmp, Expr child, float multiplier) { + not convertedExprMightOverflow(child) + and + ((child = cmp.getLeftOperand() and multiplier = 1.0) + or + (child = cmp.getRightOperand() and multiplier = -1.0) + or + exists (Expr parent, float m1, float m2 + | cmpLinearSubExpr(cmp, parent, m1) and + linearChild(parent, child, m2) and + multiplier = m1 * m2)) +} + +/** + * Holds if `cmpLinearSubExpr(cmp, child, multiplier)` holds and + * `child` is an access of variable `v`. + */ +private predicate cmpLinearSubVariable( + ComparisonOperation cmp, Variable v, VariableAccess child, float multiplier) { + v = child.getTarget() and + not exists (child.getQualifier()) and + cmpLinearSubExpr(cmp, child, multiplier) +} + +/** + * Holds if there are two linear sub-expressions of `cmp` that + * cancel each other. For example, `v` can be cancelled in each of + * these examples: + * + * `v < v` + * `v + x - v < y` + * `v + x + v < y + 2*v` + */ +private predicate cancelingSubExprs( + ComparisonOperation cmp, VariableAccess a1, VariableAccess a2) { + exists (Variable v + | exists (float m | m < 0 and cmpLinearSubVariable(cmp, v, a1, m)) and + exists (float m | m > 0 and cmpLinearSubVariable(cmp, v, a2, m))) +} + +from ComparisonOperation cmp, VariableAccess a1, VariableAccess a2 +where + cancelingSubExprs(cmp, a1, a2) + + // Most practical examples found by this query are instances of + // BadAdditionOverflowCheck or PointlessSelfComparison. + and not badAdditionOverflowCheck(cmp, _) + and not pointlessSelfComparison(cmp) +select + cmp, + "Comparison can be simplified by canceling $@ with $@.", + a1, a1.getTarget().getName(), + a2, a2.getTarget().getName() diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/ComparisonWithCancelingSubExprExample.cpp b/cpp/ql/src/Likely Bugs/Arithmetic/ComparisonWithCancelingSubExprExample.cpp new file mode 100644 index 000000000000..415214c8b18f --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Arithmetic/ComparisonWithCancelingSubExprExample.cpp @@ -0,0 +1,3 @@ +bool compare_xyz(unsigned short x, unsigned short y, unsigned short z) { + return (x + y < x + z); // x can be canceled +} diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/FloatComparison.cpp b/cpp/ql/src/Likely Bugs/Arithmetic/FloatComparison.cpp new file mode 100644 index 000000000000..0033dce972d7 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Arithmetic/FloatComparison.cpp @@ -0,0 +1,8 @@ +//wrong: could evaluate to 0 (false) due to rounding errors +23.42f == 23.42 + +//wrong: could evaluate to 1 (true) due to rounding errors +1000000000.0f == 1000000001.0f + +//correct: use a margin of error to check equality +fabs(f1 - f2) < EPSILON diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/FloatComparison.qhelp b/cpp/ql/src/Likely Bugs/Arithmetic/FloatComparison.qhelp new file mode 100644 index 000000000000..88916fbf2b87 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Arithmetic/FloatComparison.qhelp @@ -0,0 +1,32 @@ + + + + + +

    +This rule finds comparisons using the equals (==) operator on floating point values. +Such comparisons can yield unexpected results due to conversion or rounding errors. +Pay particular attention if you are dealing with very large or very small floating point values +as rounding errors will be more prominent when using such values. +

    + +
    + +

    Floating point numbers should be considered equal if their difference is within an appropriate margin of error.

    + +
    + + + + + +
  • + D. Goldberg, What Every Computer Scientist Should Know About Floating-Point Arithmetic, +ACM Computing Surveys, Volume 23, Issue 1, March 1991 (available online). +
  • + + +
    +
    diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/FloatComparison.ql b/cpp/ql/src/Likely Bugs/Arithmetic/FloatComparison.ql new file mode 100644 index 000000000000..09cf27de30b5 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Arithmetic/FloatComparison.ql @@ -0,0 +1,20 @@ +/** + * @name Equality test on floating-point values + * @description Comparing results of floating-point computations with '==' or + * '!=' is likely to yield surprising results since floating-point + * computation does not follow the standard rules of algebra. + * @kind problem + * @problem.severity recommendation + * @precision high + * @id cpp/equality-on-floats + * @tags reliability + * correctness + */ +import cpp + +from EqualityOperation ro, Expr left, Expr right +where left = ro.getLeftOperand() and right = ro.getRightOperand() and + ro.getAnOperand().getExplicitlyConverted().getType().getUnderlyingType() instanceof FloatingPointType and + not ro.getAnOperand().isConstant() and // comparisons to constants generate too many false positives + not left.(VariableAccess).getTarget() = right.(VariableAccess).getTarget() // skip self comparison +select ro, "Equality test on floating point values may not behave as expected." diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.cpp b/cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.cpp new file mode 100644 index 000000000000..aec181552d73 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.cpp @@ -0,0 +1,6 @@ +int i = 2000000000; +long j = i * i; //Wrong: due to overflow on the multiplication between ints, + //will result to j being -1651507200, not 4000000000000000000 + +long k = (long) i * i; //Correct: the multiplication is done on longs instead of ints, + //and will not overflow diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.qhelp b/cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.qhelp new file mode 100644 index 000000000000..3cdb1aeddb2f --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.qhelp @@ -0,0 +1,36 @@ + + + + + +

    This rule finds code that converts the result of an integer multiplication to a larger type. +Since the conversion applies after the multiplication, arithmetic overflow may still occur.

    + +

    The rule flags every multiplication of two non-constant integer expressions that +is (explicitly or implicitly) converted to a larger integer type. The conversion is an indication that +the expression would produce a result that would be too large to fit in the smaller integer type.

    + +
    + +

    Use a cast to ensure that the multiplication is done using the larger integer type to avoid overflow.

    + +
    + + + + + +
  • + MSDN Library: Multiplicative Operators: *, /, and %. +
  • +
  • + Cplusplus.com: Integer overflow. +
  • + + + + +
    +
    diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.ql b/cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.ql new file mode 100644 index 000000000000..95db81fd0573 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.ql @@ -0,0 +1,106 @@ +/** + * @name Multiplication result converted to larger type + * @description A multiplication result that is converted to a larger type can + * be a sign that the result can overflow the type converted from. + * @kind problem + * @problem.severity warning + * @precision high + * @id cpp/integer-multiplication-cast-to-long + * @tags reliability + * security + * correctness + * types + * external/cwe/cwe-190 + * external/cwe/cwe-192 + * external/cwe/cwe-197 + * external/cwe/cwe-681 + */ +import cpp +import semmle.code.cpp.controlflow.SSA + +/** + * Holds if `e` is either: + * - a constant + * - an array access to an array of constants + * - flows from one of the above + * In these cases the value of `e` is likely to be small and + * controlled, so we consider it less likely to cause an overflow. + */ +predicate effectivelyConstant(Expr e) { + e.isConstant() or + e.(ArrayExpr).getArrayBase().getType().(ArrayType).getBaseType().isConst() or + exists(SsaDefinition def, Variable v | + def.getAUse(v) = e and + effectivelyConstant(def.getDefiningValue(v)) + ) +} + +/** + * Gets an operand of a multiply expression (we need the restriction + * to multiply expressions to get the correct transitive closure). + */ +Expr getMulOperand(MulExpr me) { + result = me.getAnOperand() +} + +/** + * Gets the number of non-constant operands of a multiply expression, + * exploring into child multiply expressions rather than counting them + * as an operand directly. For example the top level multiply here + * effectively has two non-constant operands: + * ``` + * (x * y) * 2 + * ``` + */ +int getEffectiveMulOperands(MulExpr me) { + result = count(Expr op | + op = getMulOperand*(me) and + not op instanceof MulExpr and + not effectivelyConstant(op) + ) +} + +from MulExpr me, Type t1, Type t2 +where t1 = me.getType().getUnderlyingType() and + t2 = me.getConversion().getType().getUnderlyingType() and + t1.getSize() < t2.getSize() and + ( + ( + t1.getUnspecifiedType() instanceof IntegralType and + t2.getUnspecifiedType() instanceof IntegralType + ) or ( + t1.getUnspecifiedType() instanceof FloatingPointType and + t2.getUnspecifiedType() instanceof FloatingPointType + ) + ) and + + // exclude explicit conversions + me.getConversion().isCompilerGenerated() and + + // require the multiply to have two non-constant operands + // (the intuition here is that multiplying two unknowns is + // much more likely to produce a result that needs significantly + // more bits than the operands did, and thus requires a larger + // type). + getEffectiveMulOperands(me) >= 2 and + + // exclude varargs promotions + not exists(FunctionCall fc, int vararg | + fc.getArgument(vararg) = me and + vararg >= fc.getTarget().getNumberOfParameters() + ) and + + // exclude cases where the type was made bigger by a literal + // (compared to other cases such as assignment, this is more + // likely to be a trivial accident rather than suggesting a + // larger type is needed for the result). + not exists(Expr other, Expr e | + other = me.getParent().(BinaryOperation).getAnOperand() and + not other = me and + ( + e = other or + e = other.(BinaryOperation).getAnOperand*() + ) and + e.(Literal).getType().getSize() = t2.getSize() + ) +select me, "Cast to '" + me.getFullyConverted().getType().toString() + "' before multiplication to avoid potential overflow." diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/PointlessComparison.cpp b/cpp/ql/src/Likely Bugs/Arithmetic/PointlessComparison.cpp new file mode 100644 index 000000000000..8a5a6bdbcd6a --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Arithmetic/PointlessComparison.cpp @@ -0,0 +1,15 @@ +int f() { + int i; + int total = 0; + + for (i = 0; i < 10; i = i+1) { // GOOD: comparison could be either true or false. + total += i; + } + + for (i = 0; i < 10; i = i+1) { // BAD: comparison is always true, because i <= 5. + i = i % 5; + total += i; + } + + return total; +} diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/PointlessComparison.qhelp b/cpp/ql/src/Likely Bugs/Arithmetic/PointlessComparison.qhelp new file mode 100644 index 000000000000..2876b31220a7 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Arithmetic/PointlessComparison.qhelp @@ -0,0 +1,31 @@ + + + + + +

    Comparison operations like x >= y +or x != y will always return the same result if the +ranges of x and y do not overlap. In some +cases this can cause an infinite loop. In the example below the +loop condition on line 9 is always true because the range of +i is [0..5], so the loop will never terminate.

    + +

    The bounds which were deduced for the left and right operands of the +comparison are included in the message as they often make it easier to +understand why a result was reported. For example the message for the +comparison x >= y might read: "Comparison is always false +because x >= 5 and 3 >= y."

    + +
    + +

    Check the expression to see whether a different semantics was intended.

    + +
    + + + + + +
    diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/PointlessComparison.ql b/cpp/ql/src/Likely Bugs/Arithmetic/PointlessComparison.ql new file mode 100644 index 000000000000..fdde7852aa1a --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Arithmetic/PointlessComparison.ql @@ -0,0 +1,58 @@ +/** + * @name Comparison result is always the same + * @description When a comparison operation, such as x < y, always + * returns the same result, it means that the comparison + * is redundant and may mask a bug because a different + * check was intended. + * @kind problem + * @problem.severity warning + * @precision high + * @id cpp/constant-comparison + * @tags maintainability + * readability + */ +import cpp +private import semmle.code.cpp.rangeanalysis.PointlessComparison +private import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils +import UnsignedGEZero + +// Trivial comparisons of the form 1 > 0 are usually due to macro expansion. +// For example: +// +// #define PRINTMSG(val,msg) { if (val >= PRINTLEVEL) printf(msg); } +// +// So to reduce the number of false positives, we do not report a result if +// the comparison is in a macro expansion. +from + ComparisonOperation cmp, SmallSide ss, + float left, float right, boolean value, + string reason +where + not cmp.isInMacroExpansion() and + reachablePointlessComparison(cmp, left, right, value, ss) and + + // a comparison between an enum and zero is always valid because whether + // the underlying type of an enum is signed is compiler-dependent + not exists (Expr e, ConstantZero z + | relOpWithSwap(cmp, e, z, _, _) and + e.getUnderlyingType() instanceof Enum) and + + // Construct a reason for the message. Something like: x >= 5 and 3 >= y. + exists (string cmpOp, string leftReason, string rightReason + | ((ss = LeftIsSmaller() and cmpOp = " <= ") or + (ss = RightIsSmaller() and cmpOp = " >= ")) and + leftReason = cmp.getLeftOperand().toString() + cmpOp + left.toString() and + rightReason = right.toString() + cmpOp + cmp.getRightOperand().toString() and + // If either of the operands is constant, then don't include it. + (if cmp.getLeftOperand().isConstant() + then if cmp.getRightOperand().isConstant() + then none() // Both operands are constant so don't create a message. + else reason = rightReason + else if cmp.getRightOperand().isConstant() + then reason = leftReason + else reason = leftReason + " and " + rightReason)) and + + // Don't report results which have already been reported by UnsignedGEZero. + not unsignedGEZero(cmp, _) +select + cmp, "Comparison is always " + value.toString() + " because " + reason + "." diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/PointlessSelfComparison.qhelp b/cpp/ql/src/Likely Bugs/Arithmetic/PointlessSelfComparison.qhelp new file mode 100644 index 000000000000..46707ecf3e04 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Arithmetic/PointlessSelfComparison.qhelp @@ -0,0 +1,28 @@ + + + +

    + The purpose of comparing a variable to itself is usually to + detect either integer overflow or floating point NaN. If the + comparison does neither then it is most likely a coding error. +

    +
    + +

    + If the purpose of the cast is to detect integer overflow, then + make sure that the comparison uses explicit casts and that the + types are as intended. +

    +
    + + +

    + The comparison is always true, because the explicit cast to + int has no effect. This might mean that the definition + of T has changed since checkOverflow was written and + that checkOverflow is now obsolete. +

    +
    +
    diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/PointlessSelfComparison.ql b/cpp/ql/src/Likely Bugs/Arithmetic/PointlessSelfComparison.ql new file mode 100644 index 000000000000..4d0b4184e1d0 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Arithmetic/PointlessSelfComparison.ql @@ -0,0 +1,21 @@ +/** + * @name Self comparison + * @description Comparing a variable to itself always produces the + same result, unless the purpose is to check for + integer overflow or floating point NaN. + * @kind problem + * @problem.severity warning + * @precision high + * @id cpp/comparison-of-identical-expressions + * @tags readability + * maintainability + */ + +import cpp +import PointlessSelfComparison + +from ComparisonOperation cmp +where pointlessSelfComparison(cmp) + and not nanTest(cmp) + and not overflowTest(cmp) +select cmp, "Self comparison." diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/PointlessSelfComparison.qll b/cpp/ql/src/Likely Bugs/Arithmetic/PointlessSelfComparison.qll new file mode 100644 index 000000000000..22422aab123c --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Arithmetic/PointlessSelfComparison.qll @@ -0,0 +1,73 @@ +/** + * Provides the implementation of the PointlessSelfComparison query. The + * query is implemented as a library, so that we can avoid producing + * duplicate results in other similar queries. + */ + +import cpp +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis + +/** + * Holds if `cmp` is a comparison of the following form: + * + * x == x + * (char)x != x + * x < (int)x + * + * Usually, the intention of the comparison is to detect whether the value + * of `x` overflows when it is cast to a smaller type. However, if + * overflow is impossible then the comparison is either always true or + * always false, depending on the type of comparison (`==`, `!=`, `<`, `>`, + * `<=`, `>=`). + */ +predicate pointlessSelfComparison(ComparisonOperation cmp) { + exists (Variable v, VariableAccess lhs, VariableAccess rhs + | lhs = cmp.getLeftOperand() and + rhs = cmp.getRightOperand() and + lhs = v.getAnAccess() and + rhs = v.getAnAccess() and + not exists (lhs.getQualifier()) and // Avoid structure fields + not exists (rhs.getQualifier()) and // Avoid structure fields + not convertedExprMightOverflow(lhs) and + not convertedExprMightOverflow(rhs)) +} + +/** + * Holds if `cmp` is a floating point self comparison: + * + * x == x + * x != x + * + * If the type of `x` is a floating point type, then such comparisons can + * be used to detect if the value of `x` is NaN. Therefore, they should not + * be reported as results of the PointlessSelfComparison query. + */ +predicate nanTest(EqualityOperation cmp) { + pointlessSelfComparison(cmp) and + exists (Type t + | t = cmp.getLeftOperand().getType().getUnspecifiedType() + | t instanceof FloatingPointType or + t instanceof TemplateParameter) +} + +/** + * Holds if `cmp` looks like a test to see whether a value would fit in a + * smaller type. The type may not be smaller on the platform where we the code + * was extracted, but it could be smaller on a different platform. + * + * For example, `cmp` might be `x == (long)x`. If `x` already has type `long`, + * and `long` does not come from a macro expansion that could be + * platform-dependent, then `cmp` is _not_ and overflow test. + */ +predicate overflowTest(EqualityOperation cmp) { + pointlessSelfComparison(cmp) and + exists(Cast cast | + cast = cmp.getAnOperand().getConversion+() and + not cast.isCompilerGenerated() and + ( + cast.getType() != cast.getExpr().getType() + or + cast.isAffectedByMacro() + ) + ) +} diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/PointlessSelfComparisonExample.cpp b/cpp/ql/src/Likely Bugs/Arithmetic/PointlessSelfComparisonExample.cpp new file mode 100644 index 000000000000..fa247a744945 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Arithmetic/PointlessSelfComparisonExample.cpp @@ -0,0 +1,5 @@ +typedef int T; + +bool checkOverflow(T x) { + return (x == (int)x); // Always returns true. +} diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/UnsignedGEZero.cpp b/cpp/ql/src/Likely Bugs/Arithmetic/UnsignedGEZero.cpp new file mode 100644 index 000000000000..6ba5195d49e6 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Arithmetic/UnsignedGEZero.cpp @@ -0,0 +1,7 @@ +typedef long long LONGLONG; + +int f(unsigned int u, LONGLONG l) { + if(u > 0 || l >=0) //correct: unsigned value is check for > 0 + return 23; + return u >= 0; //wrong: unsigned values are always greater than or equal to 0 +} diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/UnsignedGEZero.qhelp b/cpp/ql/src/Likely Bugs/Arithmetic/UnsignedGEZero.qhelp new file mode 100644 index 000000000000..93108fde7a30 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Arithmetic/UnsignedGEZero.qhelp @@ -0,0 +1,27 @@ + + + + + +

    This rule finds expressions of the form x >= 0 where x is an unsigned value. This comparison is pointless as it will always yield 1.

    + +
    + +

    Check the expression to see whether a different semantics was intended.

    + +
    + + + + + +
  • + Variables. Data types. +
  • + + + +
    +
    diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/UnsignedGEZero.ql b/cpp/ql/src/Likely Bugs/Arithmetic/UnsignedGEZero.ql new file mode 100644 index 000000000000..21f188d8f43e --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Arithmetic/UnsignedGEZero.ql @@ -0,0 +1,18 @@ +/** + * @name Unsigned comparison to zero + * @description An unsigned value is always non-negative, even if it has been + * assigned a negative number, so the comparison is redundant and + * may mask a bug because a different check was intended. + * @kind problem + * @problem.severity warning + * @precision very-high + * @id cpp/unsigned-comparison-zero + * @tags maintainability + * readability + */ +import cpp +import UnsignedGEZero + +from UnsignedGEZero ugez, string msg +where unsignedGEZero(ugez, msg) +select ugez, msg diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/UnsignedGEZero.qll b/cpp/ql/src/Likely Bugs/Arithmetic/UnsignedGEZero.qll new file mode 100644 index 000000000000..3c775db031f3 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Arithmetic/UnsignedGEZero.qll @@ -0,0 +1,54 @@ +/** + * Provides classes and predicates implementing the UnsignedGEZero query. + * This library is also used by the PointlessComparison query, + * so that we can avoid reporting the same result twice. (PointlessComparison + * is a newer and more general query which also finds instances of + * the UnsignedGEZero pattern.) + */ + +import cpp + +class ConstantZero extends Expr { + ConstantZero() { + this.isConstant() and + this.getValue() = "0" + } +} + +class UnsignedGEZero extends GEExpr { + UnsignedGEZero() { + this.getRightOperand() instanceof ConstantZero and + + // left operand was a signed or unsigned IntegralType before conversions + // (not a pointer, checking a pointer >= 0 is an entirely different mistake) + // (not an enum, as the fully converted type of an enum is compiler dependent + // so checking an enum >= 0 is always reasonable) + getLeftOperand().getUnderlyingType() instanceof IntegralType and + + exists(Expr ue | + // ue is some conversion of the left operand + ue = getLeftOperand().getConversion*() and + + // ue is unsigned + ue.getUnderlyingType().(IntegralType).isUnsigned() and + + // ue may be converted to zero or more strictly larger possibly signed types + // before it is fully converted + forall(Expr following | following = ue.getConversion+() | + following.getType().getSize() > ue.getType().getSize() + ) + ) + } +} + +predicate unsignedGEZero(UnsignedGEZero ugez, string msg) { + not exists(MacroInvocation mi | + // ugez is in mi + mi.getAnExpandedElement() = ugez and + + // and ugez was apparently not passed in as a macro parameter + ugez.getLocation().getStartLine() = mi.getLocation().getStartLine() and + ugez.getLocation().getStartColumn() = mi.getLocation().getStartColumn() + ) and + msg = "Pointless comparison of unsigned value to zero." +} diff --git a/cpp/ql/src/Likely Bugs/ContinueInFalseLoop.ql b/cpp/ql/src/Likely Bugs/ContinueInFalseLoop.ql new file mode 100644 index 000000000000..b3a44c0e2b12 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/ContinueInFalseLoop.ql @@ -0,0 +1,33 @@ +/** + * @name Continue statement that does not continue + * @description A 'continue' statement only re-runs the loop if the loop-condition is true. Therefore + * using 'continue' in a loop with a constant false condition is misleading and usually + * a bug. + * @kind problem + * @id cpp/continue-in-false-loop + * @problem.severity warning + */ + +import cpp + +Loop getAFalseLoop() { + result.getControllingExpr().getValue() = "0" + and not result.getControllingExpr().isAffectedByMacro() +} + +Loop enclosingLoop(Stmt s) { + exists(Stmt parent | + parent = s.getParent() and + if parent instanceof Loop then + result = parent + else + result = enclosingLoop(parent)) +} + +from Loop loop, ContinueStmt continue +where loop = getAFalseLoop() + and loop = enclosingLoop(continue) +select continue, + "This 'continue' never re-runs the loop - the $@ is always false.", + loop.getControllingExpr(), + "loop condition" diff --git a/cpp/ql/src/Likely Bugs/Conversion/ArrayArgSizeMismatch.cpp b/cpp/ql/src/Likely Bugs/Conversion/ArrayArgSizeMismatch.cpp new file mode 100644 index 000000000000..eff2e167c1d9 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Conversion/ArrayArgSizeMismatch.cpp @@ -0,0 +1,13 @@ + +//Function foo's array parameter has a specified size +void foo(int a[10]) { + int i = 0; + for (i = 0; i <10; i++) { + a[i] = i * 2; + } +} + +... + +int my_arr[5]; +foo(my_arr); //my_arr is smaller than foo's array parameter, and will cause access to memory outside its bounds \ No newline at end of file diff --git a/cpp/ql/src/Likely Bugs/Conversion/ArrayArgSizeMismatch.qhelp b/cpp/ql/src/Likely Bugs/Conversion/ArrayArgSizeMismatch.qhelp new file mode 100644 index 000000000000..f5e6ca2c13a6 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Conversion/ArrayArgSizeMismatch.qhelp @@ -0,0 +1,24 @@ + + + + + +

    This rule looks for function calls where the size of the array being passed is smaller +than the size of the declared array parameter for the function. This is most likely going to lead +to memory accesses that are beyond the bounds of the passed array.

    + +

    The size of the array being passed is very likely wrong as it does not match that of the function's +parameter.

    + +
    + +

    Check the array being passed to the function is correct, or modify its size to match that of the function's array parameter.

    + +
    + + + + +
    diff --git a/cpp/ql/src/Likely Bugs/Conversion/ArrayArgSizeMismatch.ql b/cpp/ql/src/Likely Bugs/Conversion/ArrayArgSizeMismatch.ql new file mode 100644 index 000000000000..9cfa93154866 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Conversion/ArrayArgSizeMismatch.ql @@ -0,0 +1,24 @@ +/** + * @name Array argument size mismatch + * @description Finds function calls where the size of an array being passed is smaller than the array size of the declared parameter. + * This could lead to accesses to memory locations beyond the parameter's array bounds. + * @kind problem + * @id cpp/array-arg-size-mismatch + * @problem.severity warning + * @tags reliability + */ +import cpp + +from Function f, FunctionCall c, int i, ArrayType argType, ArrayType paramType, int a, int b +where f = c.getTarget() and + argType = c.getArgument(i).getType() and + paramType = f.getParameter(i).getType() and + a = argType.getArraySize() and + b = paramType.getArraySize() and + argType.getBaseType().getSize() = paramType.getBaseType().getSize() and + a < b and + // filter out results for inconsistent declarations + strictcount(f.getParameter(i).getType().getSize()) = 1 +select c.getArgument(i), "Array of size " + a + + " passed to $@ which expects an array of size " + b + ".", + f, f.getName() diff --git a/cpp/ql/src/Likely Bugs/Conversion/CastArrayPointerArithmetic.cpp b/cpp/ql/src/Likely Bugs/Conversion/CastArrayPointerArithmetic.cpp new file mode 100644 index 000000000000..f79a14a89211 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Conversion/CastArrayPointerArithmetic.cpp @@ -0,0 +1,24 @@ +class Base { +public: + int x; +} + +class Derived: public Base { +public: + int y; +}; + +void dereference_base(Base *b) { + b[2].x; +} + +void dereference_derived(Derived *d) { + d[2].x; +} + +void test () { + Derived[4] d; + dereference_base(d); // BAD: implicit conversion to Base* + + dereference_derived(d); // GOOD: implicit conversion to Derived*, which will be the right size +} diff --git a/cpp/ql/src/Likely Bugs/Conversion/CastArrayPointerArithmetic.qhelp b/cpp/ql/src/Likely Bugs/Conversion/CastArrayPointerArithmetic.qhelp new file mode 100644 index 000000000000..c9ad0c74ecb6 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Conversion/CastArrayPointerArithmetic.qhelp @@ -0,0 +1,24 @@ + + + + + +

    A pointer to a derived class may be implicitly converted to a pointer to its +base type when passed as an argument to a function expecting a pointer to the +base type. If pointer arithmetic or an array dereference is then used, it will +be performed using the size of the base type. This can lead to reading data from +unexpected fields in the derived type.

    + +
    + +

    Only convert pointers to single objects. If you must work with a sequence of +objects that are converted to a base type, use an array of pointers rather than +a pointer to an array.

    + +
    + + + +
    diff --git a/cpp/ql/src/Likely Bugs/Conversion/CastArrayPointerArithmetic.ql b/cpp/ql/src/Likely Bugs/Conversion/CastArrayPointerArithmetic.ql new file mode 100644 index 000000000000..78f84e044e71 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Conversion/CastArrayPointerArithmetic.ql @@ -0,0 +1,58 @@ +/** + * @name Upcast array used in pointer arithmetic + * @description An array with elements of a derived struct type is cast to a + * pointer to the base type of the struct. If pointer arithmetic or + * an array dereference is done on the resulting pointer, it will + * use the width of the base type, leading to misaligned reads. + * @kind path-problem + * @problem.severity warning + * @precision high + * @tags correctness + * reliability + * external/cwe/cwe-119 + * external/cwe/cwe-843 + * @id cpp/upcast-array-pointer-arithmetic + */ + +import cpp +import semmle.code.cpp.dataflow.DataFlow +import DataFlow::PathGraph + +class CastToPointerArithFlow extends DataFlow::Configuration { + CastToPointerArithFlow() { + this = "CastToPointerArithFlow" + } + + override predicate isSource(DataFlow::Node node) { + not node.asExpr() instanceof Conversion and + introducesNewField( + node.asExpr().getType().(DerivedType).getBaseType(), + node.asExpr().getConversion*().getType().(DerivedType).getBaseType() + + ) + } + + override predicate isSink(DataFlow::Node node) { + exists(PointerAddExpr pae | + pae.getAnOperand() = node.asExpr() + ) or + exists(ArrayExpr ae | + ae.getArrayBase() = node.asExpr() + ) + } +} + +predicate introducesNewField(Class derived, Class base) { + derived.getABaseClass+() = base and + ( + exists(Field f | + f.getDeclaringType() = derived + ) or + introducesNewField(derived.getABaseClass(), base) + ) +} + +from DataFlow::PathNode source, DataFlow::PathNode sink, CastToPointerArithFlow cfg +where cfg.hasFlowPath(source, sink) + and source.getNode().asExpr().getFullyConverted().getType().getUnspecifiedType() = sink.getNode().asExpr().getFullyConverted().getType().getUnspecifiedType() +select sink, source, sink, "Pointer arithmetic here may be done with the wrong type because of the cast $@.", source, "here" diff --git a/cpp/ql/src/Likely Bugs/Conversion/ConversionChangesSign.cpp b/cpp/ql/src/Likely Bugs/Conversion/ConversionChangesSign.cpp new file mode 100644 index 000000000000..f2f049de4a75 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Conversion/ConversionChangesSign.cpp @@ -0,0 +1,4 @@ +//sz is a signed integer, but malloc expects one that is unsigned. +//Negative values will be interpreted as a large number, which may +//lead to unexpected behavior +char *buf = malloc(sz); diff --git a/cpp/ql/src/Likely Bugs/Conversion/ConversionChangesSign.qhelp b/cpp/ql/src/Likely Bugs/Conversion/ConversionChangesSign.qhelp new file mode 100644 index 000000000000..858859b691d5 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Conversion/ConversionChangesSign.qhelp @@ -0,0 +1,24 @@ + + + + + +

    This rule finds expressions which are implicitly converted from signed to unsigned or vice versa. Casting an unsigned value to +a signed value and vice versa does not change the contents (in bits) of the variable, it just changes the way the sign bit is +interpreted. Casting a large unsigned value (one with the most significant bit set) to a signed value will make it negative, while +casting a negative signed value converts it to a large unsigned number.

    + +
    + +

    Make the conversion explicit by adding a cast (and comparing against zero where appropriate).

    + +
    + + + + + + +
    diff --git a/cpp/ql/src/Likely Bugs/Conversion/ConversionChangesSign.ql b/cpp/ql/src/Likely Bugs/Conversion/ConversionChangesSign.ql new file mode 100644 index 000000000000..44fee5fb133a --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Conversion/ConversionChangesSign.ql @@ -0,0 +1,23 @@ +/** + * @name Conversion changes sign + * @description Finds conversions from unsigned to signed. + * @kind problem + * @id cpp/conversion-changes-sign + * @problem.severity warning + * @tags reliability + */ +import cpp + +from Expr e1, Cast e2, IntegralType it1, IntegralType it2 +where e2 = e1.getConversion() and + e2.isImplicit() and + it1 = e1.getUnderlyingType() and + it2 = e2.getUnderlyingType() and + ( + it1.isUnsigned() and it2.isSigned() and it1.getSize() >= it2.getSize() + or it1.isSigned() and it2.isUnsigned() + ) and + not (e1.isConstant() and 0 <= e1.getValue().toInt() and + e1.getValue().toInt() <= ((it2.getSize()*8-1)*(2.log())).exp()) + and not e1.isConstant() +select e1, "Conversion between signed and unsigned types "+it1.toString()+" and "+it2.toString()+"." diff --git a/cpp/ql/src/Likely Bugs/Conversion/ImplicitDowncastFromBitfield.c b/cpp/ql/src/Likely Bugs/Conversion/ImplicitDowncastFromBitfield.c new file mode 100644 index 000000000000..4fce71d42596 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Conversion/ImplicitDowncastFromBitfield.c @@ -0,0 +1,25 @@ +typedef struct { + unsigned int x : 24; +} my_struct; + +unsigned short getX(my_struct s ) { + return s.x; //BAD: implicit truncation +} + +unsigned int getXGood(my_struct s) { + return s.x //GOOD: no truncation +} + +int main (int argc, char **argv) { + my_struct s; + s.x = USHORT_MAX + 1; + int* array = calloc(sizeof(int), getX(s)); //BAD: buffer allocated is smaller than intended + for (int i = 0; i < s.x; i++) { + array[i] = i; + } + + int* array2 = calloc(sizeof(int), getXGood(s)); //GOOD + for (int i = 0; i < s.x; i++) { + array[i] = i; + } +} diff --git a/cpp/ql/src/Likely Bugs/Conversion/ImplicitDowncastFromBitfield.qhelp b/cpp/ql/src/Likely Bugs/Conversion/ImplicitDowncastFromBitfield.qhelp new file mode 100644 index 000000000000..56b7d4aa4ce5 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Conversion/ImplicitDowncastFromBitfield.qhelp @@ -0,0 +1,39 @@ + + + + + +

    A bitfield may be unintentionally truncated when implicitly cast to an +integer type storing fewer bits. This can lead to inaccurate iteration or +allocation when the bitfield is used to count elements of a data structure, or +to loss of information stored in the upper portion of the bitfield.

    +
    + +

    Use the bitfield with a wider integer type, or use an explicit cast if the +truncation is intended.

    +
    + + +

    In the following example, a bitfield is accessed both through a method that +truncates it and through direct field access. This results in a buffer +overflow in the for loop.

    + + + +
    + + +
  • + cpp-reference.com: Bit field +
  • +
  • + cpp-reference.com: Implicit conversion +
  • +
  • + https://cwe.mitre.org/data/definitions/681.html +
  • +
    + +
    diff --git a/cpp/ql/src/Likely Bugs/Conversion/ImplicitDowncastFromBitfield.ql b/cpp/ql/src/Likely Bugs/Conversion/ImplicitDowncastFromBitfield.ql new file mode 100644 index 000000000000..6670a135b98d --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Conversion/ImplicitDowncastFromBitfield.ql @@ -0,0 +1,23 @@ +/** + * @name Implicit downcast from bitfield + * @description A bitfield is implicitly downcast to a smaller integer type. + * This could lead to loss of upper bits of the bitfield. + * @kind problem + * @problem.severity warning + * @precision high + * @id cpp/implicit-bitfield-downcast + * @tags reliability + * correctness + * types + */ + +import cpp + +from BitField fi, VariableAccess va + +where fi.getNumBits() > va.getFullyConverted().getType().getSize() * 8 + and va.getExplicitlyConverted().getType().getSize() > va.getFullyConverted().getType().getSize() + and va.getTarget() = fi + and not va.getActualType() instanceof BoolType + +select va, "Implicit downcast of bitfield $@", fi, fi.toString() diff --git a/cpp/ql/src/Likely Bugs/Conversion/LossyFunctionResultCast.ql b/cpp/ql/src/Likely Bugs/Conversion/LossyFunctionResultCast.ql new file mode 100644 index 000000000000..a8587d63b707 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Conversion/LossyFunctionResultCast.ql @@ -0,0 +1,18 @@ +/** + * @name Lossy function result cast + * @description Finds function calls whose result type is a floating point type, and which are casted to an integral type. + * Includes only expressions with implicit cast and excludes function calls to ceil, floor and round. {This is a gcc check; doesn't seem wildly useful.} + * @kind problem + * @id cpp/lossy-function-result-cast + * @problem.severity warning + */ +import cpp + +from FunctionCall c, FloatingPointType t1, IntegralType t2 +where t1 = c.getTarget().getType().getUnderlyingType() and + t2 = c.getActualType() and + c.hasImplicitConversion() and + not c.getTarget().getName() = "ceil" and + not c.getTarget().getName() = "floor" and + not c.getTarget().getName() = "round" +select c diff --git a/cpp/ql/src/Likely Bugs/Conversion/LossyPointerCast.cpp b/cpp/ql/src/Likely Bugs/Conversion/LossyPointerCast.cpp new file mode 100644 index 000000000000..9f9bc1b08a8a --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Conversion/LossyPointerCast.cpp @@ -0,0 +1,5 @@ +void f(char *p) { + int my_ptr = p; //Wrong: pointer assigned to int, would be incorrect if sizeof(char*) + //is larger than sizeof(int) + //... +} diff --git a/cpp/ql/src/Likely Bugs/Conversion/LossyPointerCast.qhelp b/cpp/ql/src/Likely Bugs/Conversion/LossyPointerCast.qhelp new file mode 100644 index 000000000000..72ed00e4c112 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Conversion/LossyPointerCast.qhelp @@ -0,0 +1,40 @@ + + + + + +

    This rule finds expressions of pointer type which are (implicitly or explicitly) converted to an integer type of smaller size. +This results in truncation of the most significant bits of the larger integer type.

    + +

    Such conversions are highly non-portable, since the relative size of integer and pointer types may differ between architectures. +For example, while on a 32-bit architecture both type int and type char* are four bytes wide, +the latter occupies eight bytes on a 64-bit machine.

    + +
    + +

    Avoid converting between pointer types and integer types.

    + +
    + + + + + +
  • + MSDN Library: Type Conversions and Type Safety (Modern C++). +
  • +
  • + Cplusplus.com: Type conversions. +
  • + + + + + + + + +
    +
    diff --git a/cpp/ql/src/Likely Bugs/Conversion/LossyPointerCast.ql b/cpp/ql/src/Likely Bugs/Conversion/LossyPointerCast.ql new file mode 100644 index 000000000000..67f27bac6bd7 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Conversion/LossyPointerCast.ql @@ -0,0 +1,28 @@ +/** + * @name Lossy pointer cast + * @description A pointer type is converted to a smaller integer type. This may + * lead to loss of information in the variable and is highly + * non-portable. + * @kind problem + * @problem.severity warning + * @precision high + * @id cpp/lossy-pointer-cast + * @tags reliability + * correctness + * types + */ +import cpp + +predicate lossyPointerCast(Expr e, PointerType pt, IntegralType it) { + not it instanceof BoolType and + e.getConversion().getType().getUnderlyingType() = it and + e.getType().getUnderlyingType() = pt and + it.getSize() < pt.getSize() and + not e.isInMacroExpansion() and + // low bits of pointers are sometimes used to store flags + not exists(BitwiseAndExpr a | a.getAnOperand() = e) +} + +from Expr e, PointerType pt, IntegralType it +where lossyPointerCast(e, pt, it) +select e, "Converted from " + pt.getName() + " to smaller type "+it.getName() diff --git a/cpp/ql/src/Likely Bugs/Conversion/NonzeroValueCastToPointer.cpp b/cpp/ql/src/Likely Bugs/Conversion/NonzeroValueCastToPointer.cpp new file mode 100644 index 000000000000..d384852aa509 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Conversion/NonzeroValueCastToPointer.cpp @@ -0,0 +1,4 @@ +void* p = (void*) 42; //Wrong: non-zero constant assigned to pointer. Is unportable + //and will likely lead to a segfault. + +void* p2 = NULL; //Correct: NULL (which is 0) assigned to a pointer diff --git a/cpp/ql/src/Likely Bugs/Conversion/NonzeroValueCastToPointer.qhelp b/cpp/ql/src/Likely Bugs/Conversion/NonzeroValueCastToPointer.qhelp new file mode 100644 index 000000000000..098cfa846da7 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Conversion/NonzeroValueCastToPointer.qhelp @@ -0,0 +1,37 @@ + + + + + +

    This rule finds compile-time constants other than zero (and some common error markers, like +-1 and 0xdeadbeef) which are (explicitly or implicitly) converted to a pointer type. +This is a dangerous practice, since the meaning of the numerical value of pointers is platform dependent.

    + +
    + +

    Do not assign integer literals (except NULL) to pointers.

    + +
    + + + + + + +
  • + Cplusplus.com: Pointers. +
  • +
  • + The CERT C Secure Coding Standard: INT36-C. Converting a pointer to integer or integer to pointer. +
  • + + + + + + + +
    +
    diff --git a/cpp/ql/src/Likely Bugs/Conversion/NonzeroValueCastToPointer.ql b/cpp/ql/src/Likely Bugs/Conversion/NonzeroValueCastToPointer.ql new file mode 100644 index 000000000000..ba6c25877284 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Conversion/NonzeroValueCastToPointer.ql @@ -0,0 +1,30 @@ +/** + * @name Non-zero value cast to pointer + * @description A constant value other than zero is converted to a pointer type. This is a dangerous practice, since the meaning of the numerical value of pointers is platform dependent. + * @kind problem + * @problem.severity recommendation + * @precision medium + * @id cpp/cast-to-pointer + * @tags reliability + * correctness + * types + */ +import cpp + +predicate commonErrorCode(string value) { + value = "0" or value = "1" or value = "-1" + or value = "18446744073709551615" // 2^64-1, i.e. -1 as an unsigned int64 + or value = "4294967295" // 2^32-1, i.e. -1 as an unsigned int32 + or value = "3735928559" // 0xdeadbeef + or value = "3735929054" // 0xdeadc0de + or value = "3405691582" // 0xcafebabe +} + +from Expr e +where e.isConstant() and + not commonErrorCode(e.getValue()) and + e.getFullyConverted().getType() instanceof PointerType and + not e.getType() instanceof ArrayType and + not e.getType() instanceof PointerType and + not e.isInMacroExpansion() +select e, "Nonzero value " + e.getValueText() + " cast to pointer." diff --git a/cpp/ql/src/Likely Bugs/Format/NonConstantFormat-1-bad.c b/cpp/ql/src/Likely Bugs/Format/NonConstantFormat-1-bad.c new file mode 100644 index 000000000000..d01df0f8e486 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Format/NonConstantFormat-1-bad.c @@ -0,0 +1,6 @@ +#include +int main(int argc, char** argv) { + for(int i = 1; i < argc; ++i) { + printf(argv[i]); + } +} \ No newline at end of file diff --git a/cpp/ql/src/Likely Bugs/Format/NonConstantFormat-1-good.c b/cpp/ql/src/Likely Bugs/Format/NonConstantFormat-1-good.c new file mode 100644 index 000000000000..33fff96854ac --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Format/NonConstantFormat-1-good.c @@ -0,0 +1,6 @@ +#include +int main(int argc, char** argv) { + for(int i = 1; i < argc; ++i) { + printf("%s", argv[i]); + } +} \ No newline at end of file diff --git a/cpp/ql/src/Likely Bugs/Format/NonConstantFormat-2-bad.c b/cpp/ql/src/Likely Bugs/Format/NonConstantFormat-2-bad.c new file mode 100644 index 000000000000..f43c1af1c83b --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Format/NonConstantFormat-2-bad.c @@ -0,0 +1,13 @@ +void log_with_timestamp(const char* message) { + struct tm now; + time(&now); + printf("[%s] ", asctime(now)); + printf(message); +} + +int main(int argc, char** argv) { + log_with_timestamp("Application is starting...\n"); + /* ... */ + log_with_timestamp("Application is closing...\n"); + return 0; +} \ No newline at end of file diff --git a/cpp/ql/src/Likely Bugs/Format/NonConstantFormat-2-good.c b/cpp/ql/src/Likely Bugs/Format/NonConstantFormat-2-good.c new file mode 100644 index 000000000000..82802e9aa5a5 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Format/NonConstantFormat-2-good.c @@ -0,0 +1,16 @@ +void log_with_timestamp(const char* message, ...) { + va_list args; + va_start(args, message); + struct tm now; + time(&now); + printf("[%s] ", asctime(now)); + vprintf(message, args); + va_end(args); +} + +int main(int argc, char** argv) { + log_with_timestamp("%s is starting...\n", argv[0]); + /* ... */ + log_with_timestamp("%s is closing...\n", argv[0]); + return 0; +} \ No newline at end of file diff --git a/cpp/ql/src/Likely Bugs/Format/NonConstantFormat-2-ok.c b/cpp/ql/src/Likely Bugs/Format/NonConstantFormat-2-ok.c new file mode 100644 index 000000000000..576654afc6f4 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Format/NonConstantFormat-2-ok.c @@ -0,0 +1,12 @@ +void log_with_timestamp(const char* message) { + struct tm now; + time(&now); + printf("[%s] %s", asctime(now), message); +} + +int main(int argc, char** argv) { + log_with_timestamp("Application is starting...\n"); + /* ... */ + log_with_timestamp("Application is closing...\n"); + return 0; +} \ No newline at end of file diff --git a/cpp/ql/src/Likely Bugs/Format/NonConstantFormat.qhelp b/cpp/ql/src/Likely Bugs/Format/NonConstantFormat.qhelp new file mode 100644 index 000000000000..523561aa5d66 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Format/NonConstantFormat.qhelp @@ -0,0 +1,59 @@ + + + +

    The printf function, related functions like sprintf and fprintf, +and other functions built atop vprintf all accept a format string as their first argument. +When such format strings are literal constants, it is easy for the programmer (and static analysis tools) +to verify that the format specifiers (such as %s and %02x) in the format string +are compatible with the trailing arguments of the function call. When such format strings are not literal +constants, it is more difficult to maintain the program: programmers (and static analysis tools) must +perform non-local data-flow analysis to deduce what values the format string argument might take.

    + +
    + +

    If the argument passed as a format string is meant to be a plain string rather than a format string, +then pass %s as the format string, and pass the original argument as the sole trailing +argument.

    + +

    If the argument passed as a format string is a parameter to the enclosing function, then consider +redesigning the enclosing function's API to be less brittle.

    + +
    + +

    The following program is meant to echo its command line arguments:

    + +

    The above program behaves as expected in most cases, but breaks when one of its command line arguments +contains a percent character. In such cases, the behavior of the program is undefined: it might echo +garbage, it might crash, or it might give a malicious attacker root access. One way of addressing +the problem is to use a constant %s format string, as in the following program:

    + + +
    + +

    The following program defines a log_with_timestamp function:

    + +

    In the code that is visible, the reader can verify that log_with_timestamp is never called +with a log message containing a percent character, but even if all current calls are correct, this presents +an ongoing maintenance burden to ensure that newly-introduced calls don't contain percent characters. As +in the previous example, one solution is to make the log message a trailing argument of the function call:

    + +

    An alternative solution is to allow log_with_timestamp to accept format arguments:

    + +

    In this formulation, the non-constant format string to printf has been replaced with +a non-constant format string to vprintf. Semmle will no longer consider the body of +log_with_timestamp to be a problem, and will instead check that every call to +log_with_timestamp passes a constant format string.

    + +
    + + + +
  • CERT C Coding +Standard: FIO30-C. Exclude user input from format strings.
  • +
  • M. Howard, D. Leblanc, J. Viega, 19 Deadly Sins of Software Security: Programming Flaws and How to Fix Them.
  • + + +
    +
    diff --git a/cpp/ql/src/Likely Bugs/Format/NonConstantFormat.ql b/cpp/ql/src/Likely Bugs/Format/NonConstantFormat.ql new file mode 100644 index 000000000000..584f3187e0b0 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Format/NonConstantFormat.ql @@ -0,0 +1,214 @@ +/** + * @name Non-constant format string + * @description Passing a non-constant 'format' string to a printf-like function can lead + * to a mismatch between the number of arguments defined by the 'format' and the number + * of arguments actually passed to the function. If the format string ultimately stems + * from an untrusted source, this can be used for exploits. + * @kind problem + * @problem.severity recommendation + * @precision high + * @id cpp/non-constant-format + * @tags maintainability + * correctness + * external/cwe/cwe-134 + */ +import cpp + +/** + * Holds if `t` is a character array or pointer, where `charType` is the type of + * characters in `t`. + */ +predicate stringType(Type t, Type charType) { + ( + ( charType = t.(PointerType).getBaseType() or + charType = t.(ArrayType).getBaseType() + ) and ( + charType.getUnspecifiedType() instanceof CharType or + charType.getUnspecifiedType() instanceof WideCharType + ) + ) + or + stringType(t.getUnderlyingType(), charType) + or + stringType(t.(SpecifiedType).getBaseType(), charType) +} + +predicate gettextFunction(Function f, int arg) { + // basic variations of gettext + (f.getName() = "_" and arg = 0) or + (f.getName() = "gettext" and arg = 0) or + (f.getName() = "dgettext" and arg = 1) or + (f.getName() = "dcgettext" and arg = 1) or + // plural variations of gettext that take one format string for singular and another for plural form + (f.getName() = "ngettext" and (arg = 0 or arg = 1)) or + (f.getName() = "dngettext" and (arg = 1 or arg = 2)) or + (f.getName() = "dcngettext" and (arg = 1 or arg = 2)) +} + +predicate stringArray(Variable arr, AggregateLiteral init) { + arr.getInitializer().getExpr() = init and + stringType(arr.getType().getUnspecifiedType().(ArrayType).getBaseType(), _) + // Ideally, this predicate should also check that no item of `arr` is ever + // reassigned, but such an analysis could get fairly complicated. Instead, we + // just hope that nobody would initialize an array of constants and then + // overwrite some of them with untrusted data. +} + +predicate underscoreMacro(MacroInvocationExpr e) { + e.getMacroName() = "_" +} + +/** + * Holds if a value of character pointer type may flow _directly_ from `src` to + * `dst`. + */ +predicate stringFlowStep(Expr src, Expr dst) { + not underscoreMacro(dst) + and + stringType(dst.getType(), _) + and + ( + src = dst.(VariableAccess).getTarget().getAnAssignedValue() + or + src = dst.(ConditionalExpr).getThen() + or + src = dst.(ConditionalExpr).getElse() + or + src = dst.(AssignExpr).getRValue() + or + src = dst.(CommaExpr).getRightOperand() + or + src = dst.(UnaryPlusExpr).getOperand() + or + stringArray(dst.(ArrayExpr).getArrayBase().(VariableAccess).getTarget(), + src.getParent()) + or + // Follow a parameter to its arguments in all callers. + exists(Parameter p | p = dst.(VariableAccess).getTarget() | + src = p.getFunction().getACallToThisFunction().getArgument(p.getIndex()) + ) + or + // Follow a call to a gettext function without looking at its body even if + // the body is known. This ensures that we report the error in the relevant + // location rather than inside the body of some `_` function. + exists(Function gettext, FunctionCall call, int idx | + gettextFunction(gettext, idx) and + dst = call and + gettext = call.getTarget() + | src = call.getArgument(idx) + ) + or + // Follow a call to a non-gettext function through its return statements. + exists(Function f, ReturnStmt retStmt | + f = dst.(FunctionCall).getTarget() and + f = retStmt.getEnclosingFunction() and + not gettextFunction(f, _) + | src = retStmt.getExpr() + ) + ) +} + +/** Holds if `v` may be written to, other than through `AssignExpr`. */ +predicate nonConstVariable(Variable v) { + exists(Type charType | + stringType(v.getType(), charType) and not charType.isConst() + ) + or + exists(AssignPointerAddExpr e | + e.getLValue().(VariableAccess).getTarget() = v + ) + or + exists(AssignPointerSubExpr e | + e.getLValue().(VariableAccess).getTarget() = v + ) + or + exists(CrementOperation e | e.getOperand().(VariableAccess).getTarget() = v) + or + exists(AddressOfExpr e | e.getOperand().(VariableAccess).getTarget() = v) + or + exists(ReferenceToExpr e | e.getExpr().(VariableAccess).getTarget() = v) +} + +/** + * Holds if this expression is _directly_ considered non-constant for the + * purpose of this query. + * + * Each case of `Expr` that may have string type should be listed either in + * `nonConst` or `stringFlowStep`. Omitting a case leads to false negatives. + * Having a case in both places leads to unnecessary computation. + */ +predicate nonConst(Expr e) { + nonConstVariable(e.(VariableAccess).getTarget()) + or + e instanceof CrementOperation + or + e instanceof AssignPointerAddExpr + or + e instanceof AssignPointerSubExpr + or + e instanceof PointerArithmeticOperation + or + e instanceof FieldAccess + or + e instanceof PointerDereferenceExpr + or + e instanceof AddressOfExpr + or + e instanceof ExprCall + or + exists(ArrayExpr ae | ae = e | + not stringArray(ae.getArrayBase().(VariableAccess).getTarget(), _) + ) + or + e instanceof NewArrayExpr + or + // e is a call to a function whose definition we cannot see. We assume it is + // not constant. + exists(Function f | f = e.(FunctionCall).getTarget() | + not f.hasDefinition() + ) + or + // e is a parameter of a function that's never called. If it were called, we + // would instead have followed the call graph in `stringFlowStep`. + exists(Function f + | f = e.(VariableAccess).getTarget().(Parameter).getFunction() + | not exists(f.getACallToThisFunction()) + ) +} + +predicate formattingFunctionArgument( + FormattingFunction ff, FormattingFunctionCall fc, Expr arg) +{ + fc.getTarget() = ff and + fc.getArgument(ff.getFormatParameterIndex()) = arg and + // Don't look for errors inside functions that are themselves formatting + // functions. We expect that the interesting errors will be in their callers. + not fc.getEnclosingFunction() instanceof FormattingFunction +} + +// Reflexive-transitive closure of `stringFlow`, restricting the base case to +// only consider destination expressions that are arguments to formatting +// functions. +predicate stringFlow(Expr src, Expr dst) { + formattingFunctionArgument(_, _, dst) and src = dst + or + exists(Expr mid | stringFlowStep(src, mid) and stringFlow(mid, dst)) +} + +predicate whitelisted(Expr e) { + gettextFunction(e.(FunctionCall).getTarget(), _) + or + underscoreMacro(e) +} + +predicate flowFromNonConst(Expr src, Expr dst) { + stringFlow(src, dst) and + nonConst(src) and + not whitelisted(src) +} + +from FormattingFunctionCall fc, FormattingFunction ff, Expr format +where formattingFunctionArgument(ff, fc, format) and + flowFromNonConst(_, format) and + not fc.isInMacroExpansion() +select format, "The format string argument to " + ff.getName() + " should be constant to prevent security issues and other potential errors." diff --git a/cpp/ql/src/Likely Bugs/Format/SnprintfOverflow.qhelp b/cpp/ql/src/Likely Bugs/Format/SnprintfOverflow.qhelp new file mode 100644 index 000000000000..619b4507c8df --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Format/SnprintfOverflow.qhelp @@ -0,0 +1,27 @@ + + + +

    The return value of a call to snprintf is the number of characters that would have been written to the buffer assuming there was sufficient space. In the event that the operation reaches the end of the buffer and more than one character is discarded, the return value will be greater than the buffer size. This can cause incorrect behaviour, for example: +

    +
    + + + + + +

    The return value of snprintf should always be checked if it is used, and values larger than the buffer size should be accounted for. +

    +
    + + + + + + +
  • cplusplus.com: snprintf.
  • +
  • Red Hat Customer Portal: The trouble with snprintf.
  • + +
    +
    diff --git a/cpp/ql/src/Likely Bugs/Format/SnprintfOverflow.ql b/cpp/ql/src/Likely Bugs/Format/SnprintfOverflow.ql new file mode 100644 index 000000000000..5f5fdafbbf15 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Format/SnprintfOverflow.ql @@ -0,0 +1,113 @@ +/** + * @name Potentially overflowing call to snprintf + * @description Using the return value from snprintf without proper checks can cause overflow. + * + * @kind problem + * @problem.severity warning + * @precision high + * @id cpp/overflowing-snprintf + * @tags reliability + * correctness + */ + +import cpp +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis + +/** + * Holds if there is a dataflow path from `source` to `sink` + * with no bounds checks along the way. `pathMightOverflow` is + * true if there is an arithmetic operation on the path that + * might overflow. + */ +predicate flowsToExpr( + Expr source, Expr sink, boolean pathMightOverflow) { + // Might the current expression overflow? + exists (boolean otherMightOverflow + | flowsToExprImpl(source, sink, otherMightOverflow) + | if convertedExprMightOverflow(sink) + then pathMightOverflow = true + else pathMightOverflow = otherMightOverflow) +} + +/** + * Implementation of `flowsToExpr`. Does everything except + * checking whether the current expression might overflow. + */ +predicate flowsToExprImpl( + Expr source, Expr sink, boolean pathMightOverflow) { + (source = sink and pathMightOverflow = false and + source.(FunctionCall).getTarget().(Snprintf).returnsFullFormatLength()) + or + exists (RangeSsaDefinition def, LocalScopeVariable v + | flowsToDef(source, def, v, pathMightOverflow) and + sink = def.getAUse(v)) + or + flowsToExpr( + source, sink.(UnaryArithmeticOperation).getOperand(), pathMightOverflow) + or + flowsToExpr( + source, sink.(BinaryArithmeticOperation).getAnOperand(), pathMightOverflow) + or + flowsToExpr( + source, sink.(Assignment).getRValue(), pathMightOverflow) + or + flowsToExpr( + source, sink.(AssignOperation).getLValue(), pathMightOverflow) + or + exists (FormattingFunctionCall call + | sink = call and + flowsToExpr(source, call.getArgument(call.getTarget().getSizeParameterIndex()), pathMightOverflow)) +} + +/** + * Holds if there is a dataflow path from `source` to the SSA + * definition `(def,v)`. with no bounds checks along the way. + * `pathMightOverflow` is true if there is an arithmetic operation + * on the path that might overflow. + */ +predicate flowsToDef( + Expr source, RangeSsaDefinition def, LocalScopeVariable v, + boolean pathMightOverflow) { + // Might the current definition overflow? + exists (boolean otherMightOverflow + | flowsToDefImpl(source, def, v, otherMightOverflow) + | if defMightOverflow(def, v) + then pathMightOverflow = true + else pathMightOverflow = otherMightOverflow) +} + +/** + * Implementation of `flowsToDef`. Does everything except + * checking whether the current definition might overflow. + * + * Note: RangeSsa is used to exclude paths that include a bounds check. + * RangeSsa inserts extra definitions after conditions like `if (x < 10)`. + * Such definitions are ignored here, so the path will terminate when + * a bounds check is encounter. Of course it isn't super accurate + * because useless checks such as `if (x >= 0)` will also terminate + * the path. But it is a good way to reduce the number of false positives. + */ +predicate flowsToDefImpl( + Expr source, RangeSsaDefinition def, LocalScopeVariable v, + boolean pathMightOverflow) { + // Assignment or initialization: `e = v;` + exists (Expr e + | e = def.getDefiningValue(v) and + flowsToExpr(source, e, pathMightOverflow)) + or + // `x++` + exists (CrementOperation crem + | def = crem and + crem.getOperand() = v.getAnAccess() and + flowsToExpr(source, crem.getOperand(), pathMightOverflow)) + or + // Phi definition. + flowsToDef(source, def.getAPhiInput(v), v, pathMightOverflow) +} + +from FormattingFunctionCall call, Expr sink +where flowsToExpr(call, sink, true) +and sink = call.getArgument(call.getTarget().getSizeParameterIndex()) +select + call, "The $@ of this snprintf call is derived from its return value, which may exceed the size of the buffer and overflow.", + sink, "size argument" diff --git a/cpp/ql/src/Likely Bugs/Format/SnprintfOverflowBad.cpp b/cpp/ql/src/Likely Bugs/Format/SnprintfOverflowBad.cpp new file mode 100644 index 000000000000..eb474c7a10f4 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Format/SnprintfOverflowBad.cpp @@ -0,0 +1,14 @@ +#define BUF_SIZE (32) + +int main(int argc, char *argv[]) +{ + char buffer[BUF_SIZE]; + size_t pos = 0; + int i; + + for (i = 0; i < argc; i++) + { + pos += snprintf(buffer + pos, BUF_SIZE - pos, "%s", argv[i]); + // BUF_SIZE - pos may overflow + } +} diff --git a/cpp/ql/src/Likely Bugs/Format/SnprintfOverflowGood.cpp b/cpp/ql/src/Likely Bugs/Format/SnprintfOverflowGood.cpp new file mode 100644 index 000000000000..989d2eaf966c --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Format/SnprintfOverflowGood.cpp @@ -0,0 +1,18 @@ +#define BUF_SIZE (32) + +int main(int argc, char *argv[]) +{ + char buffer[BUF_SIZE]; + size_t pos = 0; + int i; + + for (i = 0; i < argc; i++) + { + int n = snprintf(buffer + pos, BUF_SIZE - pos, "%s", argv[i]); + if (n < 0 || n >= BUF_SIZE - pos) + { + break; + } + pos += n; + } +} diff --git a/cpp/ql/src/Likely Bugs/Format/TooManyFormatArguments.cpp b/cpp/ql/src/Likely Bugs/Format/TooManyFormatArguments.cpp new file mode 100644 index 000000000000..d4e1e4cd5618 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Format/TooManyFormatArguments.cpp @@ -0,0 +1,4 @@ +void log_connection_attempt(const char *user_name, char char *ip_address) { + // This does not print `ip_address`. + fprintf(stderr, "Connection attempted by '%s'\n", user_name, ip_address); +} diff --git a/cpp/ql/src/Likely Bugs/Format/TooManyFormatArguments.qhelp b/cpp/ql/src/Likely Bugs/Format/TooManyFormatArguments.qhelp new file mode 100644 index 000000000000..bbd64254d54c --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Format/TooManyFormatArguments.qhelp @@ -0,0 +1,31 @@ + + + +

    Each call to the printf function, or a related function, should include +the number of arguments defined by the format. Passing the function more arguments than +required is usually harmless from a security perspective but indicates that different +behavior was intended. +

    + +
    + +

    Review the format and arguments expected by the highlighted function calls. Update either +the format or the arguments so that the expected number of arguments are passed to the +function. +

    + +
    + + + + + +
  • cplusplus.com: C++ Functions.
  • +
  • Microsoft C Runtime Library Reference: printf, wprintf.
  • + + + +
    +
    diff --git a/cpp/ql/src/Likely Bugs/Format/TooManyFormatArguments.ql b/cpp/ql/src/Likely Bugs/Format/TooManyFormatArguments.ql new file mode 100644 index 000000000000..4c06b13630be --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Format/TooManyFormatArguments.ql @@ -0,0 +1,21 @@ +/** + * @name Too many arguments to formatting function + * @description A printf-like function called with too many arguments will + * ignore the excess arguments and output less than might + * have been intended. + * @kind problem + * @problem.severity recommendation + * @precision high + * @id cpp/too-many-format-arguments + * @tags reliability + * correctness + */ +import cpp + +from FormatLiteral fl, FormattingFunctionCall ffc, int expected, int given +where ffc = fl.getUse() + and expected = fl.getNumArgNeeded() + and given = ffc.getNumFormatArgument() + and expected < given + and fl.specsAreKnown() +select ffc, "Format expects "+expected.toString()+" arguments but given "+given.toString() diff --git a/cpp/ql/src/Likely Bugs/Format/WrongNumberOfFormatArguments.cpp b/cpp/ql/src/Likely Bugs/Format/WrongNumberOfFormatArguments.cpp new file mode 100644 index 000000000000..300cb6bd29e5 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Format/WrongNumberOfFormatArguments.cpp @@ -0,0 +1,4 @@ +int main() { + printf("%d, %s\n", 42); // Will crash or print garbage + return 0; +} diff --git a/cpp/ql/src/Likely Bugs/Format/WrongNumberOfFormatArguments.qhelp b/cpp/ql/src/Likely Bugs/Format/WrongNumberOfFormatArguments.qhelp new file mode 100644 index 000000000000..66344e93f22d --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Format/WrongNumberOfFormatArguments.qhelp @@ -0,0 +1,40 @@ + + + +

    Each call to the printf function, or a related function, should include +the number of arguments defined by the format. Passing the function more arguments than +required is harmless (although it may be indicative of other defects). However, passing +the function fewer arguments than are defined by the format can be a security vulnerability +since the function will process the next item on the stack as the missing arguments. +

    + +

    This might lead to an information leak if a sensitive value from the stack is printed. It +might cause a crash if a value on the stack is interpreted as a pointer and leads to +accessing unmapped memory. Finally, it may lead to a follow-on vulnerability if an +attacker can use this problem to cause the output string to be too long or have unexpected +contents. +

    + +
    + +

    Review the format and arguments expected by the highlighted function calls. Update either +the format or the arguments so that the expected number of arguments are passed to the +function. +

    + +
    + + + + + +
  • CERT C Coding +Standard: FIO30-C. Exclude user input from format strings.
  • +
  • cplusplus.com: C++ Functions.
  • +
  • Microsoft C Runtime Library Reference: printf, wprintf.
  • + + +
    +
    diff --git a/cpp/ql/src/Likely Bugs/Format/WrongNumberOfFormatArguments.ql b/cpp/ql/src/Likely Bugs/Format/WrongNumberOfFormatArguments.ql new file mode 100644 index 000000000000..d9ccf5199c4d --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Format/WrongNumberOfFormatArguments.ql @@ -0,0 +1,21 @@ +/** + * @name Too few arguments to formatting function + * @description Calling a printf-like function with too few arguments can be + * a source of security issues. + * @kind problem + * @problem.severity error + * @precision high + * @id cpp/wrong-number-format-arguments + * @tags reliability + * correctness + * external/cwe/cwe-685 + */ +import cpp + +from FormatLiteral fl, FormattingFunctionCall ffc, int expected, int given +where ffc = fl.getUse() + and expected = fl.getNumArgNeeded() + and given = ffc.getNumFormatArgument() + and expected > given + and fl.specsAreKnown() +select ffc, "Format expects "+expected.toString()+" arguments but given "+given.toString() diff --git a/cpp/ql/src/Likely Bugs/Format/WrongTypeFormatArguments.cpp b/cpp/ql/src/Likely Bugs/Format/WrongTypeFormatArguments.cpp new file mode 100644 index 000000000000..c3dd09c30719 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Format/WrongTypeFormatArguments.cpp @@ -0,0 +1,4 @@ +int main() { + printf("%s\n", 42); //printf will treat 42 as a char*, will most likely segfault + return 0; +} diff --git a/cpp/ql/src/Likely Bugs/Format/WrongTypeFormatArguments.qhelp b/cpp/ql/src/Likely Bugs/Format/WrongTypeFormatArguments.qhelp new file mode 100644 index 000000000000..c0cf04db823b --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Format/WrongTypeFormatArguments.qhelp @@ -0,0 +1,32 @@ + + + +

    Each call to the printf function or a related function should include +the type and sequence of arguments defined by the format. If the function is passed arguments +of a different type or in a different sequence then the arguments are reinterpreted to fit the type and sequence expected, resulting in unpredictable behavior.

    + +
    + +

    Review the format and arguments expected by the highlighted function calls. Update either +the format or the arguments so that the expected type and sequence of arguments are passed to +the function. +

    + +
    + + + + + +
  • CERT C Coding +Standard: FIO30-C. Exclude user input from format strings.
  • +
  • cplusplus.com: C++ Functions.
  • +
  • MSDN Alphabetical Function Reference: printf, wprintf.
  • + + + + +
    +
    diff --git a/cpp/ql/src/Likely Bugs/Format/WrongTypeFormatArguments.ql b/cpp/ql/src/Likely Bugs/Format/WrongTypeFormatArguments.ql new file mode 100644 index 000000000000..a41f163441fd --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Format/WrongTypeFormatArguments.ql @@ -0,0 +1,179 @@ +/** + * @name Wrong type of arguments to formatting function + * @description Calling a printf-like function with the wrong type of arguments causes unpredictable + * behavior. + * @kind problem + * @problem.severity error + * @precision high + * @id cpp/wrong-type-format-argument + * @tags reliability + * correctness + * external/cwe/cwe-686 + */ + +import cpp + +/** + * Holds if the argument corresponding to the `pos` conversion specifier + * of `ffc` is expected to have type `expected`. + */ +pragma[noopt] +private predicate formattingFunctionCallExpectedType(FormattingFunctionCall ffc, int pos, Type expected) { + exists(FormattingFunction f, int i, FormatLiteral fl | + ffc instanceof FormattingFunctionCall and + ffc.getTarget() = f and + f.getFormatParameterIndex() = i and + ffc.getArgument(i) = fl and + fl.getConversionType(pos) = expected + ) +} + +/** + * Holds if the argument corresponding to the `pos` conversion specifier + * of `ffc` is expected to have type `expected` and the corresponding + * argument `arg` has type `actual`. + */ +pragma[noopt] +predicate formatArgType(FormattingFunctionCall ffc, int pos, Type expected, Expr arg, Type actual) { + exists(Expr argConverted | + formattingFunctionCallExpectedType(ffc, pos, expected) and + ffc.getConversionArgument(pos) = arg and + argConverted = arg.getFullyConverted() and + actual = argConverted.getType() + ) +} + +/** + * Holds if the argument corresponding to the `pos` conversion specifier + * of `ffc` is expected to have a width or precision argument of type + * `expected` and the corresponding argument `arg` has type `actual`. + */ +pragma[noopt] +predicate formatOtherArgType(FormattingFunctionCall ffc, int pos, Type expected, Expr arg, Type actual) { + exists(Expr argConverted | + (arg = ffc.getMinFieldWidthArgument(pos) or arg = ffc.getPrecisionArgument(pos)) and + argConverted = arg.getFullyConverted() and + actual = argConverted.getType() and + exists(IntType it | it instanceof IntType and it.isImplicitlySigned() and expected = it) + ) +} + +/** + * A type that may be expected by a printf format parameter, or that may + * be pointed to by such a type (e.g. `wchar_t`, from `wchar_t *`). + */ +class ExpectedType extends Type +{ + ExpectedType() { + formatArgType(_, _, this, _, _) or + formatOtherArgType(_, _, this, _, _) or + exists(ExpectedType t | + this = t.(PointerType).getBaseType() + ) + } +} + +/** + * Gets an 'interesting' type that can be reached from `t` by removing + * typedefs and specifiers. Note that this does not always mean removing + * all typedefs and specifiers as `Type.getUnspecifiedType()` would, for + * example if the interesting type is itself a typedef. + */ +ExpectedType getAnUnderlyingExpectedType(Type t) { + ( + result = t + ) or ( + result = getAnUnderlyingExpectedType(t.(TypedefType).getBaseType()) + ) or ( + result = getAnUnderlyingExpectedType(t.(SpecifiedType).getBaseType()) + ) +} + +/** + * Holds if it is safe to display a value of type `actual` when `printf` + * expects a value of type `expected`. + * + * Note that variadic arguments undergo default argument promotions before + * they reach `printf`, notably `bool`, `char`, `short` and `enum` types + * are promoted to `int` (or `unsigned int`, as appropriate) and `float`s + * are converted to `double`. + */ +predicate trivialConversion(ExpectedType expected, Type actual) { + formatArgType(_, _, expected, _, actual) and + + exists(Type actualU | + actualU = actual.getUnspecifiedType() and + ( + ( + // allow a pointer type to be displayed with `%p` + expected instanceof VoidPointerType and actualU instanceof PointerType + ) or ( + // allow a function pointer type to be displayed with `%p` + expected instanceof VoidPointerType and actualU instanceof FunctionPointerType and expected.getSize() = actual.getSize() + ) or ( + // allow an `enum` type to be displayed with `%i`, `%c` etc + expected instanceof IntegralType and actualU instanceof Enum + ) or ( + // allow any `char *` type to be displayed with `%s` + expected instanceof CharPointerType and actualU instanceof CharPointerType + ) or ( + // allow `wchar_t *`, or any pointer to an integral type of the same size, to be displayed + // with `%ws` + expected.(PointerType).getBaseType().hasName("wchar_t") and + exists(Wchar_t t | + actual.getUnspecifiedType().(PointerType).getBaseType().(IntegralType).getSize() = t.getSize() + ) + ) or ( + // allow an `int` (or anything promoted to `int`) to be displayed with `%c` + expected instanceof CharType and actualU instanceof IntType + ) or ( + // allow an `int` (or anything promoted to `int`) to be displayed with `%wc` + expected instanceof Wchar_t and actualU instanceof IntType + ) or ( + expected instanceof UnsignedCharType and actualU instanceof IntType + ) or ( + // allow the underlying type of a `size_t` (e.g. `unsigned long`) for + // `%zu`, since this is the type of a `sizeof` expression + expected instanceof Size_t and + actual.getUnspecifiedType() = expected.getUnspecifiedType() + ) or ( + // allow the underlying type of a `ssize_t` (e.g. `long`) for `%zd` + expected instanceof Ssize_t and + actual.getUnspecifiedType() = expected.getUnspecifiedType() + ) or ( + // allow any integral type of the same size + // (this permits signedness changes) + expected.(IntegralType).getSize() = actualU.(IntegralType).getSize() + ) or ( + // allow a pointer to any integral type of the same size + // (this permits signedness changes) + expected.(PointerType).getBaseType().(IntegralType).getSize() = actualU.(PointerType).getBaseType().(IntegralType).getSize() + ) or ( + // allow expected, or a typedef or specified version of expected + expected = getAnUnderlyingExpectedType(actual) + ) + ) + ) +} + +/** + * Gets the size of the `int` type. + */ +int sizeof_IntType() { + exists(IntType it | result = it.getSize()) +} + +from FormattingFunctionCall ffc, int n, Expr arg, ExpectedType expected, Type actual +where ( + ( + formatArgType(ffc, n, expected, arg, actual) and + not trivialConversion(expected, actual) + ) + or + ( + formatOtherArgType(ffc, n, expected, arg, actual) and + not actual.getUnderlyingType().(IntegralType).getSize() = sizeof_IntType() + ) + ) + and not arg.isAffectedByMacro() +select arg, "This argument should be of type '"+expected.getName()+"' but is of type '"+actual.getUnspecifiedType().getName() + "'" diff --git a/cpp/ql/src/Likely Bugs/InconsistentCallOnResult.qhelp b/cpp/ql/src/Likely Bugs/InconsistentCallOnResult.qhelp new file mode 100644 index 000000000000..87e5b6ccb365 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/InconsistentCallOnResult.qhelp @@ -0,0 +1,32 @@ + + + + + +

    If the same operation +(e.g. free, delete, close, etc.) +is usually performed on the result of a method call, +then any instances where it is not performed +may indicate misuse of the API and could cause resource leaks or other issues. +

    + +
    + + +

    Examine the code to determine if the return value is treated correctly in this particular case. +

    + +
    + + + +
  • + Tutorialspoint - The C++ Programming Language: C++ Dynamic Memory +
  • + + + +
    +
    diff --git a/cpp/ql/src/Likely Bugs/InconsistentCallOnResult.ql b/cpp/ql/src/Likely Bugs/InconsistentCallOnResult.ql new file mode 100644 index 000000000000..26da76a31ddb --- /dev/null +++ b/cpp/ql/src/Likely Bugs/InconsistentCallOnResult.ql @@ -0,0 +1,73 @@ +/** + * @name Inconsistent operation on return value + * @description A function is called, and the same operation is usually performed on the return value - for example, free, delete, close etc. However, in some cases it is not performed. These unusual cases may indicate misuse of the API and could cause resource leaks. + * @kind problem + * @problem.severity warning + * @precision medium + * @id cpp/inconsistent-call-on-result + * @tags reliability + * correctness + * statistical + * non-attributable + * external/cwe/cwe-252 + */ +import cpp + +predicate exclude(Function f) { + exists(string name | name = f.getName() | + name.toLowerCase().matches("get%") or + name.matches("strto%") + ) +} + +predicate checkExpr(Expr e, string operation, Variable v) { + exists(FunctionCall fc | fc = e and not exclude(fc.getTarget()) | + fc.getTarget().getName() = operation and + (fc.getAnArgument() = v.getAnAccess() or fc.getQualifier() = v.getAnAccess()) + ) + or + exists(DeleteExpr del | del = e | + del.getExpr() = v.getAnAccess() and + operation = "delete" + ) + or + exists(DeleteArrayExpr del | del = e | + del.getExpr() = v.getAnAccess() and + operation = "delete[]" + ) +} + +predicate checkedFunctionCall(FunctionCall fc, string operation) { + relevantFunctionCall(fc, _) and + exists (Variable v, Expr check | v.getAnAssignedValue() = fc | + checkExpr(check, operation, v) and + check != fc + ) +} + +predicate relevantFunctionCall(FunctionCall fc, Function f) { + fc.getTarget() = f and + exists (Variable v | v.getAnAssignedValue() = fc) and + not okToIgnore(fc) +} + +predicate okToIgnore(FunctionCall fc) { + fc.isInMacroExpansion() +} + +predicate functionStats(Function f, string operation, int used, int total, int percentage) { + exists(PointerType pt | pt.getATypeNameUse() = f.getADeclarationEntry()) and + used = strictcount(FunctionCall fc | checkedFunctionCall(fc, operation) and f = fc.getTarget()) and + total = strictcount(FunctionCall fc | relevantFunctionCall(fc, f)) and + percentage = used * 100 / total +} + +from FunctionCall unchecked, Function f, string operation, int percent +where + relevantFunctionCall(unchecked, f) and + not checkedFunctionCall(unchecked, operation) and + functionStats(f, operation, _, _, percent) and + percent >= 70 + and unchecked.getFile().getAbsolutePath().matches("%fbcode%") + and not unchecked.getFile().getAbsolutePath().matches("%\\_build%") +select unchecked, "After " + percent.toString() + "% of calls to " + f.getName() + " there is a call to " + operation + " on the return value. The call may be missing in this case." diff --git a/cpp/ql/src/Likely Bugs/InconsistentCheckReturnNull.cpp b/cpp/ql/src/Likely Bugs/InconsistentCheckReturnNull.cpp new file mode 100644 index 000000000000..69f25116ca14 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/InconsistentCheckReturnNull.cpp @@ -0,0 +1,30 @@ +struct property { + char *name; + int value; +}; + +struct property * get_property(char *key); +struct property * get_property_default(char *key, int default_value); + +void check_properties() { + // this call will get flagged since most + // calls to get_property handle NULL + struct property *p1 = get_property("time"); + if(p1->value > 600) { + ... + } + + // this call will not get flagged since + // the result of the call is checked for NULL + struct property *p2 = get_property("time"); + if(p2 != NULL && p2->value > 600) { + ... + } + + // this call will not get flagged since calls + // to get_property_default rarely handle NULL + struct property *p3 = get_property_default("time", 50); + if(p3->value > 60) { + ... + } +} diff --git a/cpp/ql/src/Likely Bugs/InconsistentCheckReturnNull.qhelp b/cpp/ql/src/Likely Bugs/InconsistentCheckReturnNull.qhelp new file mode 100644 index 000000000000..c36639335926 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/InconsistentCheckReturnNull.qhelp @@ -0,0 +1,48 @@ + + + + + +

    A common source of defects is to forget to check if the result of a function +call was NULL. This rules detects calls to a function where most other calls +check the result for NULL but this particular call does not. This is +inconsistent with the rest of the code and may be a defect or at least +confusing for other developers.

    + +

    The flagged calls will not necessarily result in run-time errors, but all of +them benefit from being audited. It is important not to mechanically add +nullness checks at every flagged location, because an unnecessary nullness +check is very confusing to other developers. A developer that is familiar with +the code will most likely immediately know what the right action is on a null +result, and if that is not the case there is most likely something confusing +about the API of the called function.

    + +
    + +

    Diagnose if the called function can actually return NULL with the context at +this particular call. If that is the case the code should be updated to cater +for that situation. If the call can not return null in this location, it may be +good to add an assert to document that the code does not handle NULL. This +documentation is important since most calls expect to receive a NULL +value.

    + +
    + + + + + +
  • + Tutorialspoint - The C++ Programming Language: C++ Dynamic Memory, + C++ NULL pointers. +
  • +
  • + Code project: Chained null checks and the Maybe monad. +
  • + + + +
    +
    diff --git a/cpp/ql/src/Likely Bugs/InconsistentCheckReturnNull.ql b/cpp/ql/src/Likely Bugs/InconsistentCheckReturnNull.ql new file mode 100644 index 000000000000..f42aa2ded5b0 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/InconsistentCheckReturnNull.ql @@ -0,0 +1,193 @@ +/** + * @name Inconsistent nullness check + * @description The result value of a function is often checked for nullness, + * but not always. Since the value is mostly checked, it is likely + * that the function can return null values in some cases, and + * omitting the check could crash the program. + * @kind problem + * @problem.severity error + * @precision medium + * @id cpp/inconsistent-null-check + * @tags reliability + * correctness + * statistical + * non-attributable + * external/cwe/cwe-476 + */ +import cpp + +predicate assertMacro(Macro m) { + m.getHead().toLowerCase().matches("%assert%") +} + +predicate assertInvocation(File f, int line) { + exists(MacroInvocation i, Location l | assertMacro(i.getMacro()) and l = i.getLocation() | + l.getStartLine() = line and l.getEndLine() = line and f = l.getFile() + ) +} + +predicate nullCheckAssert(Expr e, Variable v, Declaration qualifier) { + nullCheckInCondition(e, v, qualifier) and + exists(File f, int i | e.getLocation().getStartLine() = i and e.getFile() = f and assertInvocation(f, i)) +} + +VariableAccess qualifiedAccess(Variable v, Declaration qualifier) { + result = v.getAnAccess() and + ( + result.getQualifier().(VariableAccess).getTarget() = qualifier + or exists(PointerDereferenceExpr e, VariableAccess va + | result.getQualifier() = e + | e.getOperand() = va and va.getTarget() = qualifier) + or (not exists(result.getQualifier()) and qualifier = result.getEnclosingFunction()) + or (result.getQualifier() instanceof ThisExpr and qualifier = result.getEnclosingFunction()) + ) +} + +predicate nullCheckInCondition(Expr e, Variable v, Declaration qualifier) { + // if(v) + exists(FunctionCall fc | relevantFunctionCall(fc, _) and fc = assignedValueForVariableAndQualifier(v, qualifier) | + e = qualifiedAccess(v, qualifier) + ) + or exists(AssignExpr a | a = e and a.getLValue() = qualifiedAccess(v, qualifier)) + // if(v == NULL), if(v != NULL), if(NULL != v), if(NULL == v) + or exists(EqualityOperation eq | eq = e and nullCheckInCondition(eq.getAnOperand(), v, qualifier) and + eq.getAnOperand().getValue() = "0") + // if(v && something) + or exists(LogicalAndExpr exp | exp = e and nullCheckInCondition(exp.getAnOperand(), v, qualifier)) + // if(v || something) + or exists(LogicalOrExpr exp | exp = e and nullCheckInCondition(exp.getAnOperand(), v, qualifier)) + // if(!v) + or exists(NotExpr exp | exp = e and nullCheckInCondition(exp.getAnOperand(), v, qualifier)) + or exists(FunctionCall c | c = e and nullCheckInCondition(c.getAnArgument(), v, qualifier) and + c.getTarget().getName() = "__builtin_expect") + or exists(ConditionDeclExpr d | d = e and nullCheckInCondition(d.getExpr(), v, qualifier)) +} + +predicate hasNullCheck(Function enclosing, Variable v, Declaration qualifier) { + exists(Expr exp | nullCheckInCondition(exp, v, qualifier) and exp.getEnclosingFunction() = enclosing | + exists(ControlStructure s | exp = s.getControllingExpr()) + or exists(ConditionalExpr e | exp = e.getCondition()) + or exists(ReturnStmt s | exp = s.getExpr() and not exp instanceof VariableAccess) + or exists(AssignExpr e | exp = e.getRValue() and not exp instanceof VariableAccess) + or exists(AggregateLiteral al | exp = al.getAChild() and not exp instanceof VariableAccess) + or exists(Variable other | exp = other.getInitializer().getExpr() and not exp instanceof VariableAccess) + ) +} + +Expr assignedValueForVariableAndQualifier(Variable v, Declaration qualifier) { + (result = v.getInitializer().getExpr() and qualifier = result.getEnclosingFunction()) + or exists(AssignExpr e | e.getLValue() = qualifiedAccess(v, qualifier) and result = e.getRValue()) +} + + +predicate checkedFunctionCall(FunctionCall fc) { + relevantFunctionCall(fc, _) and + exists (Variable v, Declaration qualifier | fc = assignedValueForVariableAndQualifier(v, qualifier) | + hasNullCheck(fc.getEnclosingFunction(), v, qualifier) + ) +} + +predicate uncheckedFunctionCall(FunctionCall fc) { + relevantFunctionCall(fc, _) and + not checkedFunctionCall(fc) and + not exists(File f, int line | f = fc.getFile() and line = fc.getLocation().getEndLine() | + assertInvocation(f, line+1) or assertInvocation(f, line) + ) and + not exists(Variable v, Declaration qualifier + | fc = assignedValueForVariableAndQualifier(v, qualifier) + | nullCheckAssert(_, v, qualifier)) and + not exists(ControlStructure s + | callResultNullCheckInCondition(s.getControllingExpr(), fc)) and + not exists(FunctionCall other, Variable v, Declaration qualifier, Expr arg + | fc = assignedValueForVariableAndQualifier(v, qualifier) + | arg = other.getAnArgument() and + nullCheckInCondition(arg, v, qualifier) and + not arg instanceof VariableAccess) +} + + +Declaration functionQualifier(FunctionCall fc) { + fc.getQualifier().(VariableAccess).getTarget() = result + or exists(PointerDereferenceExpr e, VariableAccess va + | fc.getQualifier() = e and e.getOperand() = va and va.getTarget() = result) + or (not exists(fc.getQualifier()) and result = fc.getEnclosingFunction()) + or (fc.getQualifier() instanceof ThisExpr and result = fc.getEnclosingFunction()) +} + +predicate callTargetAndEnclosing(FunctionCall fc, Function target, Function enclosing) { + target = fc.getTarget() and enclosing = fc.getEnclosingFunction() +} + +predicate callArgumentVariable(FunctionCall fc, Variable v, int i) { + fc.getArgument(i) = v.getAnAccess() +} + +predicate callResultNullCheckInCondition(Expr e, FunctionCall fc) { + // if(v) + exists(FunctionCall other | e = other and + relevantFunctionCall(fc,_) and not checkedFunctionCall(fc) + and exists(Function called, Function enclosing | + callTargetAndEnclosing(fc, called, enclosing) and + callTargetAndEnclosing(other, called, enclosing)) and + forall(Variable v, int i | callArgumentVariable(fc, v, i) | callArgumentVariable(other, v, i)) and + ( + functionQualifier(fc) = functionQualifier(other) + or + (not exists(functionQualifier(fc)) and not exists(functionQualifier(other))) + ) + ) + // if(v == NULL), if(v != NULL), if(NULL != v), if(NULL == v) + or exists(EqualityOperation eq | eq = e and callResultNullCheckInCondition(eq.getAnOperand(), fc) and + eq.getAnOperand().getValue() = "0") + // if(v && something) + or exists(LogicalAndExpr exp | exp = e and callResultNullCheckInCondition(exp.getAnOperand(), fc)) + // if(v || something) + or exists(LogicalOrExpr exp | exp = e and callResultNullCheckInCondition(exp.getAnOperand(), fc)) + // if(!v) + or exists(NotExpr exp | exp = e and callResultNullCheckInCondition(exp.getAnOperand(), fc)) +} + +predicate dereferenced(Variable v, Declaration qualifier, Function f) { + exists(PointerDereferenceExpr e | + e.getOperand() = qualifiedAccess(v, qualifier) and + e.getEnclosingFunction() = f and + not exists(SizeofExprOperator s | s.getExprOperand() = e) + ) or + exists(FunctionCall c | + c.getQualifier() = qualifiedAccess(v, qualifier) and + c.getEnclosingFunction() = f + ) or + exists(VariableAccess va | + va.getQualifier() = qualifiedAccess(v, qualifier) and + va.getEnclosingFunction() = f + ) +} + +predicate relevantFunctionCall(FunctionCall fc, Function f) { + fc.getTarget() = f and + exists (Variable v, Declaration qualifier + | fc = assignedValueForVariableAndQualifier(v, qualifier) + | dereferenced(v, qualifier, fc.getEnclosingFunction())) and + not okToIgnore(fc) +} + +predicate okToIgnore(FunctionCall fc) { + fc.isInMacroExpansion() +} + +predicate functionStats(Function f, int percentage) { + exists(int used, int total | + exists(PointerType pt | pt.getATypeNameUse() = f.getADeclarationEntry()) and + used = strictcount(FunctionCall fc | checkedFunctionCall(fc) and f = fc.getTarget()) and + total = strictcount(FunctionCall fc | relevantFunctionCall(fc, f)) and + percentage = used * 100 / total + ) +} + +from FunctionCall unchecked, Function f, int percent +where + relevantFunctionCall(unchecked, f) and + uncheckedFunctionCall(unchecked) and + functionStats(f, percent) and + percent >= 70 +select unchecked, "The result of this call to " + f.getName() + " is not checked for null, but " + percent + "% of calls to " + f.getName() + " check for null." diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/AssignWhereCompareMeant.cpp b/cpp/ql/src/Likely Bugs/Likely Typos/AssignWhereCompareMeant.cpp new file mode 100644 index 000000000000..3f8b3443c557 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Likely Typos/AssignWhereCompareMeant.cpp @@ -0,0 +1,4 @@ +if(p = NULL) { //most likely == was intended. Otherwise it evaluates to the value + //of the rhs of the assignment (which is NULL) + ... +} diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/AssignWhereCompareMeant.qhelp b/cpp/ql/src/Likely Bugs/Likely Typos/AssignWhereCompareMeant.qhelp new file mode 100644 index 000000000000..91ae1ce665b1 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Likely Typos/AssignWhereCompareMeant.qhelp @@ -0,0 +1,40 @@ + + + + + +

    This rule finds uses of the assignment operator = in places where the +equality operator == would make more sense. This is a very common mistake in +C and C++, because of the similarity of the = and the == operator, +and the fact that the if statement accepts +a condition with an integral type, instead of limiting it to just the bool type.

    + +

    +The rule flags every occurrence of an assignment in a position where its result is interpreted as a truth value. +An assignment is only flagged if its right hand side is a compile-time constant. +

    + +
    + +

    Check to ensure that the flagged expressions are not typos. If an assignment is really intended to be treated as a truth value, it may be better to surround it with parentheses.

    + +
    + + + + + +
  • + Tutorialspoint - The C++ Programming Language: Operators in C++ +
  • +
  • + Wikipedia: Operators in C and C++ +
  • + + + + +
    +
    diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/AssignWhereCompareMeant.ql b/cpp/ql/src/Likely Bugs/Likely Typos/AssignWhereCompareMeant.ql new file mode 100644 index 000000000000..22b0753e638d --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Likely Typos/AssignWhereCompareMeant.ql @@ -0,0 +1,89 @@ +/** + * @name Assignment where comparison was intended + * @description The '=' operator may have been used accidentally, where '==' + * was intended. + * @kind problem + * @problem.severity error + * @precision high + * @id cpp/assign-where-compare-meant + * @tags reliability + * correctness + * external/cwe/cwe-481 + */ +import cpp +import semmle.code.cpp.controlflow.LocalScopeVariableReachability + +class UndefReachability extends LocalScopeVariableReachability { + UndefReachability() { + this = "UndefReachability" + } + + override predicate isSource(ControlFlowNode node, LocalScopeVariable v) { + candidateVariable(v) and + node = v.getParentScope() and + not v instanceof Parameter and + not v.hasInitializer() + } + + override predicate isSink(ControlFlowNode node, LocalScopeVariable v) { + candidateVariable(v) and + node = v.getAnAccess() + } + + override predicate isBarrier(ControlFlowNode node, LocalScopeVariable v) { + node.(AssignExpr).getLValue() = v.getAnAccess() + } +} + +abstract class BooleanControllingAssignment extends AssignExpr { + abstract predicate isWhitelisted(); +} + +class BooleanControllingAssignmentInExpr extends BooleanControllingAssignment { + BooleanControllingAssignmentInExpr() { + this.getParent() instanceof UnaryLogicalOperation + or this.getParent() instanceof BinaryLogicalOperation + or exists(ConditionalExpr c | c.getCondition() = this) + } + + override predicate isWhitelisted() { + this.getConversion().(ParenthesisExpr).isParenthesised() + } +} + +class BooleanControllingAssignmentInStmt extends BooleanControllingAssignment { + BooleanControllingAssignmentInStmt() { + exists(IfStmt i | i.getCondition() = this) + or exists(ForStmt f | f.getCondition() = this) + or exists(WhileStmt w | w.getCondition() = this) + or exists(DoStmt d | d.getCondition() = this) + } + + override predicate isWhitelisted() { + this.isParenthesised() + } +} + +/** + * Holds if `ae` is a `BooleanControllingAssignment` that would be a result of this query, + * before checking for undef reachability. + */ +predicate candidateResult(BooleanControllingAssignment ae) { + ae.getRValue().isConstant() and + not ae.isWhitelisted() +} + +/** + * Holds if `v` is a `Variable` that might be assigned to in a result of this query. + */ +predicate candidateVariable(Variable v) { + exists(BooleanControllingAssignment ae | + candidateResult(ae) and + ae.getLValue().(VariableAccess).getTarget() = v + ) +} + +from BooleanControllingAssignment ae, UndefReachability undef +where candidateResult(ae) + and not undef.reaches(_, ae.getLValue().(VariableAccess).getTarget(), ae.getLValue()) +select ae, "Use of '=' where '==' may have been intended." diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/BoolValueInBitOp.cpp b/cpp/ql/src/Likely Bugs/Likely Typos/BoolValueInBitOp.cpp new file mode 100644 index 000000000000..6774921fb0ce --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Likely Typos/BoolValueInBitOp.cpp @@ -0,0 +1,13 @@ +if (!flags & SOME_BIT) { //wrong: '!' has higher precedence than '&', so this + // is bracketed as '(!flags) & SOME_BIT', and does not + // check whether a particular bit is set. + // ... +} + +if ((p != NULL) & p->f()) { //wrong: The use of '&' rather than '&&' will still + // de-reference the pointer even if it is NULL. + // ... +} + +int bits = (s > 8) & 0xff; //wrong: Invalid attempt to get the 8 most significant + // bits of a short. diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/BoolValueInBitOp.qhelp b/cpp/ql/src/Likely Bugs/Likely Typos/BoolValueInBitOp.qhelp new file mode 100644 index 000000000000..fc48c63605df --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Likely Typos/BoolValueInBitOp.qhelp @@ -0,0 +1,31 @@ + + + + + +

    This rule finds Boolean values (i.e. expressions that will always have the value 0 or 1) +which are used in a bit-wise context.

    + +

    Due to the restricted range of Boolean values, only the +lowest bit could possibly be set, and thus applying a bit operation +is rarely what is intended. Violations are often indicative of a typo +or a misunderstanding of operator precedence. Another common source +of defects is using the bitwise AND operator instead of logical AND.

    + +
    + +

    Carefully inspect the flagged expressions to make sure they exhibit the intended behavior. +Consider adding parentheses if necessary, or replacing bitwise operations with logical equivalents. +If the code is indeed correct, consider extracting the Boolean value to a variable to make it clear +what is happening.

    + +
    + + + + + + +
    diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/BoolValueInBitOp.ql b/cpp/ql/src/Likely Bugs/Likely Typos/BoolValueInBitOp.ql new file mode 100644 index 000000000000..6ac9241afa60 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Likely Typos/BoolValueInBitOp.ql @@ -0,0 +1,46 @@ +/** + * @name Boolean value in bitwise operation + * @description A Boolean value (i.e. something that has been coerced to have + * a value of either 0 or 1) is used in a bitwise operation. + * This commonly indicates missing parentheses or mistyping + * logical operators as bitwise operators. + * @kind problem + * @id cpp/bool-value-in-bit-op + * @problem.severity warning + * @tags reliability + */ + +import cpp + +class BitwiseOperation extends Expr { + BitwiseOperation() { + this instanceof BinaryBitwiseOperation or + this instanceof UnaryBitwiseOperation + } +} + +class LogicalOperation extends Expr { + LogicalOperation() { + this instanceof BinaryLogicalOperation or + this instanceof UnaryLogicalOperation or + this instanceof ComparisonOperation + } +} + +/** + * It's common in some projects to use "non-short-circuit logic", i.e. to + * apply the bitwise and, or and xor operators to Boolean values. Such use, + * while considered bad practice, is usually not incorrect. + */ +predicate nonShortCircuitLogic2(BinaryBitwiseOperation op) { + (op instanceof BitwiseAndExpr or op instanceof BitwiseOrExpr or op instanceof BitwiseXorExpr) and + (op.getLeftOperand() instanceof LogicalOperation or nonShortCircuitLogic2(op.getLeftOperand())) and + (op.getRightOperand() instanceof LogicalOperation or nonShortCircuitLogic2(op.getRightOperand())) +} + +from LogicalOperation o +where o.getParent() instanceof BitwiseOperation and + not nonShortCircuitLogic2(o.getParent()) and + not o.getParent().isInMacroExpansion() and // It's ok if o itself is in a macro expansion. + not o.getParent().(LShiftExpr).getLeftOperand() = o // Common pattern for producing bit masks: "(a && b) << 16". +select o, "The result of this expression is Boolean, but it is used in a bitwise context." diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/CompareWhereAssignMeant.cpp b/cpp/ql/src/Likely Bugs/Likely Typos/CompareWhereAssignMeant.cpp new file mode 100644 index 000000000000..da9c804ca726 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Likely Typos/CompareWhereAssignMeant.cpp @@ -0,0 +1,3 @@ +int x; +x == 4; // most likely = was intended. Otherwise this statement has no effect. +... diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/CompareWhereAssignMeant.qhelp b/cpp/ql/src/Likely Bugs/Likely Typos/CompareWhereAssignMeant.qhelp new file mode 100644 index 000000000000..6c1921cda991 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Likely Typos/CompareWhereAssignMeant.qhelp @@ -0,0 +1,39 @@ + + + + + +

    This rule finds uses of the equality operator == in places where the +assignment operator = would make more sense. This is a common mistake in +C and C++, because of the similarity of the = and the == operator, +and the fact that expressions are valid as top-level statements.

    + +

    +The rule flags every occurrence of an equality operator in a position where its result is discarded. +

    + +
    + +

    Check to ensure that the flagged expressions are not typos. If the result of an equality test is really intended to be discarded, it should be explicitly cast to void.

    + +
    + + + + + +
  • + Tutorialspoint - The C++ Programming Language: Operators in C++ +
  • +
  • + Wikipedia: Operators in C and C++ +
  • + + + + + +
    +
    diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/CompareWhereAssignMeant.ql b/cpp/ql/src/Likely Bugs/Likely Typos/CompareWhereAssignMeant.ql new file mode 100644 index 000000000000..3e2758c4efab --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Likely Typos/CompareWhereAssignMeant.ql @@ -0,0 +1,19 @@ +/** + * @name Comparison where assignment was intended + * @description The '==' operator may have been used accidentally, where '=' + * was intended, resulting in a useless test. + * @kind problem + * @problem.severity error + * @precision high + * @id cpp/compare-where-assign-meant + * @tags reliability + * correctness + * external/cwe/cwe-482 + */ +import cpp + +from ExprInVoidContext op +where op instanceof EQExpr + or + op.(FunctionCall).getTarget().hasName("operator==") +select op, "This '==' operator has no effect. The assignment ('=') operator was probably intended." diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/DubiousNullCheck.qhelp b/cpp/ql/src/Likely Bugs/Likely Typos/DubiousNullCheck.qhelp new file mode 100644 index 000000000000..25eb9e7fcbd0 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Likely Typos/DubiousNullCheck.qhelp @@ -0,0 +1,38 @@ + + + + + +

    The expression &foo->bar gets the address of foo's member bar, which is +the address of foo plus the offset of the bar member. If said offset is non-zero, then the +expression &foo->bar only equals NULL when the address of foo is negative. +While this is not impossible, it can only happen if foo is a negative integer explicitly cast to a +pointer, or if foo is a pointer into kernel-mode address space. As neither of these cases are particularly +likely, the NULL-check is dubious.

    + +
    + +

    Either the NULL-check is entirely redundant, or the wrong thing is being checked against NULL. +In the former case, the check can be replaced with boolean true or false, and then the surrounding +context can be simplified. In the latter case, consider which sub-expressions might be NULL, and test them +instead. In particular, simply removing the ampersand may yield a more suitable expression to test.

    + +
    + + +struct person { + int id; + char* name; +}; + +bool hasName(person* p) { + return p != NULL // This check is sensible, + && p->name != NULL // as is this one. + && &p->name != NULL; // But this check is dubious. +} + + + +
    diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/DubiousNullCheck.ql b/cpp/ql/src/Likely Bugs/Likely Typos/DubiousNullCheck.ql new file mode 100644 index 000000000000..b6b1f60fdf89 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Likely Typos/DubiousNullCheck.ql @@ -0,0 +1,44 @@ +/** + * @name Dubious NULL check + * @description The address of a field (except the first) will never be NULL, + * so it is misleading, at best, to check for that case. + * @kind problem + * @problem.severity warning + * @precision very-high + * @id cpp/dubious-null-check + * @tags reliability + * readability + */ + +import cpp + +predicate zeroComparison(EqualityOperation e) { + exists(Expr zero | zero.getValue() = "0" | + zero = e.getLeftOperand() or + zero = e.getRightOperand() + ) +} + +predicate inNullContext(AddressOfExpr e) { + e.getFullyConverted().getUnderlyingType() instanceof BoolType + or exists(ControlStructure c | c.getControllingExpr() = e) + or exists(EqualityOperation cmp | zeroComparison(cmp) | + e = cmp.getLeftOperand() or + e = cmp.getRightOperand() + ) +} + +FieldAccess chainedFields(FieldAccess fa) { + result = fa or + result = chainedFields(fa.getQualifier()) +} + +from AddressOfExpr addrof, FieldAccess fa, Variable v, int offset +where fa = addrof.getOperand() + and inNullContext(addrof) + and not addrof.isInMacroExpansion() + and v.getAnAccess() = chainedFields(fa).getQualifier() + and not v instanceof MemberVariable + and offset = strictsum(chainedFields(fa).getTarget().getByteOffset()) + and offset != 0 +select addrof, "This will only be NULL if " + v.getName() + " == -" + offset + "." diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/ExprHasNoEffect.cpp b/cpp/ql/src/Likely Bugs/Likely Typos/ExprHasNoEffect.cpp new file mode 100644 index 000000000000..ec435f55dff9 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Likely Typos/ExprHasNoEffect.cpp @@ -0,0 +1,7 @@ +void f(int j) { + int i=0; + for(i; i<10, j>0; ++i, --j) { //i < 10 has no effect, since the comma + //operator only returns the value of the last expression + /* ... */ + } +} diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/ExprHasNoEffect.qhelp b/cpp/ql/src/Likely Bugs/Likely Typos/ExprHasNoEffect.qhelp new file mode 100644 index 000000000000..4113d714083d --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Likely Typos/ExprHasNoEffect.qhelp @@ -0,0 +1,41 @@ + + + + + +

    +This rule finds expressions without side effects (i.e. changing variable values) that are used in a context where their value is ignored. +These expressions are most likely intended to be part of a condition but were coded improperly. +

    + +

    +In most cases these are defects caused by the unintended use of the comma operator. It is easy to +misuse the comma operator (particularly in conditions) +and it would be good practice to use it only when the brevity it allows is absolutely necessary (e.g. macro definitions). +

    + +
    + +

    +Make sure that the flagged expressions are not oversights. +To document that the value of the expression is deliberately ignored, you could explicitly cast it to void. +

    + +
    + + + + + +
  • + Tutorialspoint - The C++ Programming Language: C++ Comma Operator +
  • + + + + + +
    +
    diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/ExprHasNoEffect.ql b/cpp/ql/src/Likely Bugs/Likely Typos/ExprHasNoEffect.ql new file mode 100644 index 000000000000..9e42cb5d2ea0 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Likely Typos/ExprHasNoEffect.ql @@ -0,0 +1,119 @@ +/** + * @name Expression has no effect + * @description A pure expression whose value is ignored is likely to be the + * result of a typo. + * @kind problem + * @problem.severity warning + * @precision high + * @id cpp/useless-expression + * @tags maintainability + * correctness + * external/cwe/cwe-561 + */ +import cpp + +class PureExprInVoidContext extends ExprInVoidContext { + PureExprInVoidContext() { this.isPure() } +} + +// loop variable mentioned in the init stmt of a for +predicate accessInInitOfForStmt(Expr e) { + e instanceof Access and + exists(ForStmt f, ExprStmt s | f.getInitialization() = s and + s.getExpr() = e) +} + +/** + * Holds if the preprocessor branch `pbd` is on line `pbdStartLine` in file `file`. + */ +predicate pbdLocation(PreprocessorBranchDirective pbd, string file, int pbdStartLine) { + pbd.getLocation().hasLocationInfo(file, pbdStartLine, _, _, _) +} + +/** + * Holds if the body of the function `f` is on lines `fBlockStartLine` to `fBlockEndLine` in file `file`. + */ +predicate functionLocation(Function f, string file, int fBlockStartLine, int fBlockEndLine) { + f.getBlock().getLocation().hasLocationInfo(file, fBlockStartLine, _, fBlockEndLine, _) +} +/** + * Holds if the function `f`, or a function called by it, contains + * code excluded by the preprocessor. + */ +predicate containsDisabledCode(Function f) { + // `f` contains a preprocessor branch that was not taken + exists(PreprocessorBranchDirective pbd, string file, int pbdStartLine, int fBlockStartLine, int fBlockEndLine | + functionLocation(f, file, fBlockStartLine, fBlockEndLine) and + pbdLocation(pbd, file, pbdStartLine) and + pbdStartLine <= fBlockEndLine and + pbdStartLine >= fBlockStartLine and + ( + pbd.(PreprocessorBranch).wasNotTaken() or + + // an else either was not taken, or it's corresponding branch + // was not taken. + pbd instanceof PreprocessorElse + ) + ) or + + // recurse into function calls + exists(FunctionCall fc | + fc.getEnclosingFunction() = f and + containsDisabledCode(fc.getTarget()) + ) +} + + +/** + * Holds if the function `f`, or a function called by it, is inside a + * preprocessor branch that may have code in another arm + */ +predicate definedInIfDef(Function f) { + exists(PreprocessorBranchDirective pbd, string file, int pbdStartLine, int pbdEndLine, int fBlockStartLine, int fBlockEndLine | + functionLocation(f, file, fBlockStartLine, fBlockEndLine) and + pbdLocation(pbd, file, pbdStartLine) and + pbdLocation(pbd.getNext(), file, pbdEndLine) and + pbdStartLine <= fBlockStartLine and + pbdEndLine >= fBlockEndLine and + // pbd is a preprocessor branch where multiple branches exist + ( + pbd.getNext() instanceof PreprocessorElse or + pbd instanceof PreprocessorElse or + pbd.getNext() instanceof PreprocessorElif or + pbd instanceof PreprocessorElif + ) + ) or + + // recurse into function calls + exists(FunctionCall fc | + fc.getEnclosingFunction() = f and + definedInIfDef(fc.getTarget()) + ) +} + +from PureExprInVoidContext peivc, Locatable parent, + Locatable info, string info_text, string tail +where // EQExprs are covered by CompareWhereAssignMeant.ql + not peivc instanceof EQExpr and + // as is operator== + not peivc.(FunctionCall).getTarget().hasName("operator==") and + not accessInInitOfForStmt(peivc) and + not peivc.isCompilerGenerated() and + not exists(Macro m | peivc = m.getAnInvocation().getAnExpandedElement()) and + parent = peivc.getParent() and + not parent.isInMacroExpansion() and + not parent instanceof PureExprInVoidContext and + not peivc.getEnclosingFunction().isCompilerGenerated() and + not peivc.getType() instanceof UnknownType and + not containsDisabledCode(peivc.(FunctionCall).getTarget()) and + not definedInIfDef(peivc.(FunctionCall).getTarget()) and + if peivc instanceof FunctionCall then + exists(Function target | + target = peivc.(FunctionCall).getTarget() and + info = target and + info_text = target.getName() and + tail = " (because $@ has no external side effects).") + else + (tail = "." and info = peivc and info_text = "") +select peivc, "This expression has no effect" + tail, + info, info_text diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/FutileConditional.cpp b/cpp/ql/src/Likely Bugs/Likely Typos/FutileConditional.cpp new file mode 100644 index 000000000000..5fa91d949e38 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Likely Typos/FutileConditional.cpp @@ -0,0 +1,3 @@ +//empty then and else branches, will not do anything (other than the check) +if (check(i)) { +} diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/FutileConditional.qhelp b/cpp/ql/src/Likely Bugs/Likely Typos/FutileConditional.qhelp new file mode 100644 index 000000000000..c668f3b5becb --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Likely Typos/FutileConditional.qhelp @@ -0,0 +1,22 @@ + + + + + +

    This rule finds If-statements with an empty then-branch and no else-branch. These statements are usually unimplemented skeleton code +that should be implemented, or real unused code that should be removed to avoid confusion and misuse.

    + +
    + +

    There might be missing statements in the then-branch or the expression in the condition can be rewritten without using an If-statement.

    + +
    + + + + + + +
    diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/FutileConditional.ql b/cpp/ql/src/Likely Bugs/Likely Typos/FutileConditional.ql new file mode 100644 index 000000000000..50bb19aee243 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Likely Typos/FutileConditional.ql @@ -0,0 +1,58 @@ +/** + * @name Futile conditional + * @description An if-statement with an empty then-branch and no else-branch + * may indicate that the code is incomplete. + * @kind problem + * @problem.severity recommendation + * @precision high + * @id cpp/empty-if + * @tags reliability + * readability + */ +import cpp + +predicate macroUse(Locatable l) { + l instanceof PreprocessorDirective or l instanceof MacroInvocation +} + +predicate macroUseLocation(File f, int start, int end) { + exists(Locatable l, Location loc | + macroUse(l) and + loc = l.getLocation() and + f = loc.getFile() and + start = loc.getStartLine() and + end = loc.getEndLine() + ) +} + +pragma[noopt] +predicate emptyIf(IfStmt s, Block b, File f, int start, int end) { + s instanceof IfStmt and + not exists (s.getElse()) and + b = s.getThen() and + b instanceof Block and + not exists(b.getAChild()) and + f = b.getFile() and + exists (Location l | + l = b.getLocation() and + start = l.getStartLine() and + end = l.getEndLine() + ) +} + +pragma[noopt] +predicate query(IfStmt s, Block b) { + exists(File f, int blockStart, int blockEnd | + emptyIf(s, b, f, blockStart, blockEnd) and + not exists(int macroStart, int macroEnd | + macroUseLocation(f, macroStart, macroEnd) and + macroStart > blockStart and + macroEnd < blockEnd + ) + ) +} + +from IfStmt s, Block b +where query(s, b) and + not b.isInMacroExpansion() +select s, "If-statement with an empty then-branch and no else-branch." diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/LogicalExprCouldBeSimplified.cpp b/cpp/ql/src/Likely Bugs/Likely Typos/LogicalExprCouldBeSimplified.cpp new file mode 100644 index 000000000000..f2de7c43c92f --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Likely Typos/LogicalExprCouldBeSimplified.cpp @@ -0,0 +1,11 @@ +if (0 || condition) { //wrong: can be converted to just 'condition' + //... +} + +if (0 && condition) { //wrong: always evaluates to false, if statement can be removed + // ... +} + +if ('A' == 65 && condition) { // wrong: can be converted to just 'condition' + // ... +} diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/LogicalExprCouldBeSimplified.qhelp b/cpp/ql/src/Likely Bugs/Likely Typos/LogicalExprCouldBeSimplified.qhelp new file mode 100644 index 000000000000..37c04dec87ce --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Likely Typos/LogicalExprCouldBeSimplified.qhelp @@ -0,0 +1,34 @@ + + + + + +

    This rule finds uses of the short-circuit operators && and +|| where at least one of their operands is a compile-time constant. +Such expressions may be confusing for readers and can always be simplified to +a constant value, or to the non-constant operand. The rule +also finds comparisons on constant values (like 1 > 0), which can be +evaluated ahead of time.

    + +
    + +

    Simplify the flagged expressions by using the following equivalences (where c stands for a non-zero constant, +and x can be an arbitrary expression):

    + + + 0 && x == 0 0 || x == x + x && 0 == 0 x || 0 == x + c && x == x c || x == 1 + x && c == x x || c == 1 + + +
    + + + + + + +
    diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/LogicalExprCouldBeSimplified.ql b/cpp/ql/src/Likely Bugs/Likely Typos/LogicalExprCouldBeSimplified.ql new file mode 100644 index 000000000000..2d0596428210 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Likely Typos/LogicalExprCouldBeSimplified.ql @@ -0,0 +1,49 @@ +/** + * @name Logical expression could be simplified + * @description When a logical expression can be easily simplified, there may + * be an opportunity to improve readability by doing so, or it may + * indicate that the code contains a typo. + * @kind problem + * @id cpp/logical-expr-could-be-simplified + * @problem.severity warning + * @tags maintainability + */ +import cpp + +/** + * A simple literal (i.e. not a macro expansion, enum constant + * or template argument). + */ +predicate simple(Literal l) { + l instanceof OctalLiteral or + l instanceof HexLiteral or + l instanceof CharLiteral or + l.getValueText() = "true" or + l.getValueText() = "false" or + // Parsing doubles is too slow... + //exists(l.getValueText().toFloat()) + // Instead, check whether the literal starts with a letter. + not l.getValueText().regexpMatch("[a-zA-Z_].*") +} + +predicate booleanLiteral(Literal l) { + simple(l) and + (l.getValue() = "0" or l.getValue() = "1" or l.getValue() = "true" or l.getValue() = "false") +} + +string boolLiteralInLogicalOp(Literal literal) { + booleanLiteral(literal) and literal.getParent() instanceof BinaryLogicalOperation and + result = "Literal value " + literal.getValueText() + " is used in a logical expression; simplify or use a constant." +} + +string comparisonOnLiterals(ComparisonOperation op) { + simple(op.getLeftOperand()) and simple(op.getRightOperand()) and + not op.getAnOperand().isInMacroExpansion() and + if op.isConstant() then result = "This comparison involves two literals and is always " + op.getValue() + "." + else result = "This comparison involves two literals and should be simplified." +} + +from Expr e, string msg +where (msg = boolLiteralInLogicalOp(e) or msg = comparisonOnLiterals(e)) and + not e.isInMacroExpansion() +select e, msg diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/MissingEnumCaseInSwitch.cpp b/cpp/ql/src/Likely Bugs/Likely Typos/MissingEnumCaseInSwitch.cpp new file mode 100644 index 000000000000..cec5e9337e8c --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Likely Typos/MissingEnumCaseInSwitch.cpp @@ -0,0 +1,30 @@ +typedef enum { + RED, + ORANGE, + YELLOW, + GREEN, + BLUE, + INDIGO, + VIOLET +} colors; + +int f(colors c) { + switch (c) { + case RED: + //... + case GREEN: + //... + case BLUE: + //... + //wrong: does not use all enum values, and has no default + } + + switch(c) { + case RED: + //... + case GREEN: + //... + default: + //correct: does not use all enum values, but has a default + } +} diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/MissingEnumCaseInSwitch.qhelp b/cpp/ql/src/Likely Bugs/Likely Typos/MissingEnumCaseInSwitch.qhelp new file mode 100644 index 000000000000..acc5f3976dfd --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Likely Typos/MissingEnumCaseInSwitch.qhelp @@ -0,0 +1,37 @@ + + + + + +

    This rule finds switch statements that switch on values of an enumeration type +but do not provide cases for all the enumeration constants or a default case. This is an +indication that there may be cases unhandled by the switch statement.

    + +
    + +

    Provide a case for every enumeration constant, or introduce a default case if several constants should be treated the same way.

    + +
    + + + + + +
  • + Tutorialspoint - The C++ Programming Language: C++ switch statement +
  • +
  • + MSDN Library: The switch Statement +
  • +
  • + M. Henricson and E. Nyquist, Industrial Strength C++, Chapter 4: Control Flow, Rec 4.5. Prentice Hall PTR, 1997 (available online). +
  • + + + + + +
    +
    diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/MissingEnumCaseInSwitch.ql b/cpp/ql/src/Likely Bugs/Likely Typos/MissingEnumCaseInSwitch.ql new file mode 100644 index 000000000000..ec3eb804a282 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Likely Typos/MissingEnumCaseInSwitch.ql @@ -0,0 +1,20 @@ +/** + * @name Missing enum case in switch + * @description A switch statement over an enum type is missing a case for some enum constant + * and does not have a default case. This may cause logic errors. + * @kind problem + * @problem.severity warning + * @precision medium + * @id cpp/missing-case-in-switch + * @tags reliability + * correctness + * external/cwe/cwe-478 + */ +import cpp + +from EnumSwitch es, float missing, float total +where not es.hasDefaultCase() + and missing = count(es.getAMissingCase()) + and total = missing + count(es.getASwitchCase()) + and missing/total < 0.30 +select es, "Switch statement is missing case for "+es.getAMissingCase().getName() diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/ShortCircuitBitMask.cpp b/cpp/ql/src/Likely Bugs/Likely Typos/ShortCircuitBitMask.cpp new file mode 100644 index 000000000000..d936408bbb02 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Likely Typos/ShortCircuitBitMask.cpp @@ -0,0 +1,4 @@ + +unsigned int new_mask = old_mask || 0x0100; //wrong, || logical operator just returns 1 or 0 + +unsigned int new_mask = old_mask | 0x0100; //right, | is a bit-mask operator diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/ShortCircuitBitMask.qhelp b/cpp/ql/src/Likely Bugs/Likely Typos/ShortCircuitBitMask.qhelp new file mode 100644 index 000000000000..94b2ae28271b --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Likely Typos/ShortCircuitBitMask.qhelp @@ -0,0 +1,31 @@ + + + + + +

    This rule finds a short-circuiting logical operator that is applied to what looks like a bit-mask. +This may be a typo for a bitwise operator. Bit-masks are assumed to be constant operands whose +value is a power of 2, except 1 and 0, since these could be legitimate truth values. Hexadecimal and octal +literals are also considered bit masks.

    + +
    + +

    Check if there should be a bitwise operator used instead of logical operator, or whether the constant should be compared to something.

    + +
    + + + + + + +
  • + B. Stroustrup, The C++ Programming Language special ed, p 123 Short-circuit operators. Addison-Wesley, 2000. +
  • + + + +
    +
    diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/ShortCircuitBitMask.ql b/cpp/ql/src/Likely Bugs/Likely Typos/ShortCircuitBitMask.ql new file mode 100644 index 000000000000..b3aae5ff2dd1 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Likely Typos/ShortCircuitBitMask.ql @@ -0,0 +1,50 @@ +/** + * @name Short-circuiting operator applied to flag + * @description A short-circuiting logical operator is applied to what looks like a flag. + * This may be a typo for a bitwise operator. + * @kind problem + * @problem.severity warning + * @precision high + * @id cpp/logical-operator-applied-to-flag + * @tags reliability + * correctness + * external/cwe/cwe-480 + */ +import cpp + +/** + * Gets the value of an expression that is a candidate for a violation, and its constant value. + * We look for constant operands of binary logical operations other than 0 and 1. + */ +float candidateExpr(Expr e) { + exists(BinaryLogicalOperation blo | + e = blo.getAnOperand() and + e.isConstant() and + result = e.getValue().toFloat() and + + // exclusions + not e.isFromTemplateInstantiation(_) and + not e instanceof SizeofOperator and + not inMacroExpansion(blo) and + + // exclude values 0 and 1 + result != 0.0 and + result != 1.0 + ) +} + +from Expr e, float v, int l, string msg +where + v = candidateExpr(e) and + + // before reporting an error, we check that the candidate is either a hex/octal + // literal, or its value is a power of two. + l = v.log2().floor() and + if v = 2.pow(l) then + msg = "Operand to short-circuiting operator looks like a flag ("+v+" = 2 ^ "+l+"), may be typo for bitwise operator." + else exists(string kind | + ((e instanceof HexLiteral and kind = "a hexadecimal literal") or + (e instanceof OctalLiteral and kind = "an octal literal")) and + msg = "Operand to short-circuiting operator is " + kind + ", and therefore likely a flag; a bitwise operator may be intended." + ) +select e, msg diff --git a/cpp/ql/src/Likely Bugs/Memory Management/Buffer.qll b/cpp/ql/src/Likely Bugs/Memory Management/Buffer.qll new file mode 100644 index 000000000000..bb4fafb92f77 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/Buffer.qll @@ -0,0 +1,14 @@ +import cpp + +// an expression of the form sizeof(e) or strlen(e) +class BufferSizeExpr extends Expr { + BufferSizeExpr() { + this instanceof SizeofExprOperator or + this instanceof StrlenCall + } + + Expr getArg() { + result = this.(SizeofExprOperator).getExprOperand() or + result = this.(StrlenCall).getStringExpr() + } +} diff --git a/cpp/ql/src/Likely Bugs/Memory Management/ImproperNullTermination.qhelp b/cpp/ql/src/Likely Bugs/Memory Management/ImproperNullTermination.qhelp new file mode 100644 index 000000000000..f539bb3b6bff --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/ImproperNullTermination.qhelp @@ -0,0 +1,47 @@ + + + + + +

    Built-in C string functions such as strcat require that their +input string arguments are null terminated. If the input string arguments are +not null terminated, these functions will read/write beyond the length of the +buffer containing the string, resulting in either buffer over-read or buffer +overflow, respectively. +

    + +
    + + +

    +Review the code and consider whether the variable that holds the string should have +an initializer or whether some path through the program fails to null terminate the +string. +

    + +
    + +

    The destination variable dest used in the call to strcat +does not (necessarily) contain a null terminator. Consequently, the call to strcat +may result in a buffer overflow. +

    + + + +

    In the revised example, dest is properly null terminated before the +the call to strcat. +

    + + + + +
    + + +
  • B. Chess and J. West, Secure Programming with Static Analysis, 6.2 Maintaining the Null Terminator. Addison-Wesley. 2007.
  • +
  • Linux Programmer's Manual: STRCAT(3).
  • + +
    +
    diff --git a/cpp/ql/src/Likely Bugs/Memory Management/ImproperNullTermination.ql b/cpp/ql/src/Likely Bugs/Memory Management/ImproperNullTermination.ql new file mode 100644 index 000000000000..54392d105a68 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/ImproperNullTermination.ql @@ -0,0 +1,53 @@ +/** + * @name Potential improper null termination + * @description Using a string that may not be null terminated as an argument + * to a string function can result in buffer overflow or buffer over-read. + * @kind problem + * @id cpp/improper-null-termination + * @problem.severity warning + * @tags security + * external/cwe/cwe-170 + * external/cwe/cwe-665 + */ + +import cpp +import semmle.code.cpp.controlflow.LocalScopeVariableReachability +import semmle.code.cpp.commons.NullTermination + +/** + * A declaration of a local variable that leaves the variable uninitialized. + */ +DeclStmt declWithNoInit(LocalVariable v) { + result.getADeclaration() = v and + not exists(v.getInitializer()) +} + +class ImproperNullTerminationReachability extends LocalScopeVariableReachabilityWithReassignment { + ImproperNullTerminationReachability() { this = "ImproperNullTerminationReachability" } + + override predicate isSourceActual(ControlFlowNode node, LocalScopeVariable v) { + node = declWithNoInit(v) + or + exists(Call c, VariableAccess va | + c = node and + c.getTarget().hasName("readlink") and + c.getArgument(1) = va and + va.getTarget() = v + ) + } + + override predicate isSinkActual(ControlFlowNode node, LocalScopeVariable v) { + node.(VariableAccess).getTarget() = v and + variableMustBeNullTerminated(node) + } + + override predicate isBarrier(ControlFlowNode node, LocalScopeVariable v) { + exprDefinition(v, node, _) or + mayAddNullTerminator(node, v.getAnAccess()) or + isSinkActual(node, v) // only report first use + } +} + +from ImproperNullTerminationReachability r, LocalVariable v, VariableAccess va +where r.reaches(_, v, va) +select va, "Variable $@ may not be null terminated.", v, v.getName() diff --git a/cpp/ql/src/Likely Bugs/Memory Management/ImproperNullTerminationBad.cpp b/cpp/ql/src/Likely Bugs/Memory Management/ImproperNullTerminationBad.cpp new file mode 100644 index 000000000000..7e9d220c4daf --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/ImproperNullTerminationBad.cpp @@ -0,0 +1,8 @@ +char source[100]; +memset(source, 'A', 100-1); +source[100-1] = '\0'; // null terminate source + +char dest[200]; +memset(dest, 'B', 100-1); + +strcat(dest, source); \ No newline at end of file diff --git a/cpp/ql/src/Likely Bugs/Memory Management/ImproperNullTerminationGood.cpp b/cpp/ql/src/Likely Bugs/Memory Management/ImproperNullTerminationGood.cpp new file mode 100644 index 000000000000..3baa3cdcf7e6 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/ImproperNullTerminationGood.cpp @@ -0,0 +1,9 @@ +char source[100]; +memset(source, 'A', 100-1); +source[100-1] = '\0'; // null terminate source + +char dest[200]; +memset(dest, 'B', 100-1); +dest[100-1] = '\0'; // null terminate destination + +strcat(dest, source); \ No newline at end of file diff --git a/cpp/ql/src/Likely Bugs/Memory Management/Padding/More64BitWaste.ql b/cpp/ql/src/Likely Bugs/Memory Management/Padding/More64BitWaste.ql new file mode 100644 index 000000000000..c82d86aadfdc --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/Padding/More64BitWaste.ql @@ -0,0 +1,16 @@ +/** + * @name Padding increased in 64-bit migration + * @description Highlights structs for which the amount of padding would increase when migrating from a 32-bit architecture to 64-bit. + * @kind problem + * @id cpp/more-64-bit-waste + * @problem.severity warning + */ + +import semmle.code.cpp.padding.Padding + +from PaddedType t, ILP32 ilp32, LP64 lp64, int w32, int w64 +where w32 = t.wastedSpace(ilp32) - t.trailingPadding(ilp32) and + w64 = t.wastedSpace(lp64) - t.trailingPadding(lp64) and + w64 > w32 and + t.isPrecise() +select t, t.getName() + " includes " + w32 + " bits of padding on ILP32, but " + w64 + " bits on LP64." diff --git a/cpp/ql/src/Likely Bugs/Memory Management/Padding/NonPortablePrintf.ql b/cpp/ql/src/Likely Bugs/Memory Management/Padding/NonPortablePrintf.ql new file mode 100644 index 000000000000..95383e86fa50 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/Padding/NonPortablePrintf.ql @@ -0,0 +1,86 @@ +/** + * @name Non-portable call to printf + * @description When using a format specifier like %d ("int"), on a 32-bit architecture it's acceptable to pass a long since it's of the same size; + * when migrating to a 64-bit architecture this becomes problematic. Similar problems exist when printing pointers using 32-bit-wide format specifiers. + * @kind problem + * @id cpp/non-portable-printf + * @problem.severity warning + */ + +import cpp +import semmle.code.cpp.padding.Padding + +/** Used to avoid reporting conflicts between a char + * pointer type with specified signedness and an unspecified + * char pointer (whose signedness is compiler-dependent). + */ +class SignedOrUnsignedCharPointerType extends CharPointerType { + SignedOrUnsignedCharPointerType() { + this.getBaseType().(CharType).isUnsigned() or + this.getBaseType().(CharType).isSigned() + } +} + +pragma[noopt] +private predicate formattingFunctionCallExpectedType(FormattingFunctionCall ffc, int pos, Type expected) { + exists(FormattingFunction f, int i, FormatLiteral fl | + ffc.getTarget() = f and + ffc instanceof FormattingFunctionCall and + f.getFormatParameterIndex() = i and + ffc.getArgument(i) = fl and + fl.getConversionType(pos) = expected + ) +} + +pragma[noopt] +predicate formatArgType(FormattingFunctionCall ffc, int pos, Type expected, Expr arg, Type actual) { + formattingFunctionCallExpectedType(ffc, pos, expected) and + ffc.getConversionArgument(pos) = arg and + exists(Type t | t = arg.getActualType() and t.getUnspecifiedType() = actual) +} + +pragma[noopt] +predicate formatOtherArgType(FormattingFunctionCall ffc, int pos, Type expected, Expr arg, Type actual) { + (arg = ffc.getMinFieldWidthArgument(pos) or arg = ffc.getPrecisionArgument(pos)) and + actual = arg.getActualType() and + exists(IntType it | it instanceof IntType and it.isImplicitlySigned() and expected = it) +} + +predicate trivialConversion(Type expected, Type actual) { + formatArgType(_, _, expected, _, actual) and + ( + expected instanceof VoidPointerType and actual instanceof PointerType + or + expected instanceof IntegralType and actual instanceof Enum + or + expected instanceof CharPointerType and actual instanceof SignedOrUnsignedCharPointerType + or + expected instanceof SignedOrUnsignedCharPointerType and actual instanceof CharPointerType + or + expected instanceof CharType and actual instanceof IntType + or + expected instanceof UnsignedCharType and actual instanceof IntType + or + expected.(IntegralType).getUnsigned() = actual.(IntegralType).getUnsigned() + or + expected = actual + ) +} + +from FormattingFunctionCall ffc, int n, Expr arg, Type expected, Type actual, ILP32 ilp32, LP64 lp64, int size32, int size64 +where ( + ( + formatArgType(ffc, n, expected, arg, actual) and + not trivialConversion(expected, actual) + ) + or + ( + formatOtherArgType(ffc, n, expected, arg, actual) and + not actual instanceof IntType + ) + ) + and not arg.isAffectedByMacro() + and size32 = ilp32.paddedSize(actual) and size64 = lp64.paddedSize(actual) + and size64 != size32 +select arg, "This argument should be of type "+expected.getName()+" but is of type "+actual.getName() + + " (which changes size from " + size32 + " to " + size64 + " on 64-bit systems)." diff --git a/cpp/ql/src/Likely Bugs/Memory Management/Padding/Suboptimal64BitType.ql b/cpp/ql/src/Likely Bugs/Memory Management/Padding/Suboptimal64BitType.ql new file mode 100644 index 000000000000..8578324883ac --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/Padding/Suboptimal64BitType.ql @@ -0,0 +1,23 @@ +/** + * @name Suboptimal type definition + * @description Highlights structs whose members are not laid out optimally, in the sense + * that by reordering them one could reduce the amount of internal padding on a 64-bit architecture. + * @kind problem + * @id cpp/suboptimal-64-bit-type + * @problem.severity warning + */ + +import semmle.code.cpp.padding.Padding + +from PaddedType t, Architecture arch, WideCharType wc, int holes, int size, int percentage, int optimum +where + arch.pointerSize() = 64 and // Select 64-bit architecture + arch.wideCharSize() = (wc.getSize() * 8) and // Select Windows(sizeof(wchar_t == 2)) or non-Windows(sizeof(wchar_t == 4)) + t.isPrecise() and + optimum = t.optimalSize(arch) and + size = arch.paddedSize(t) and + holes = size - optimum and + holes > 0 and + percentage = (holes*100.0/(float)size).ceil() +select t, t.getName() + " could be optimized to save " + holes + "/" + t.wastedSpace(arch) + + " bits of padding (or " + percentage + "% of its size)." diff --git a/cpp/ql/src/Likely Bugs/Memory Management/PotentialBufferOverflow.cpp b/cpp/ql/src/Likely Bugs/Memory Management/PotentialBufferOverflow.cpp new file mode 100644 index 000000000000..efb2569797c9 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/PotentialBufferOverflow.cpp @@ -0,0 +1,12 @@ +void f(char* s, float f) { + char buf[30]; + + //wrong: gets has no limit to the length of data it puts in the buffer + gets(buf); + + //wrong: sprintf does not limit the length of the string put into buf + sprintf(buf, "This is a string: %s", s); + + //wrong: %f can expand to a very long string in extreme cases, easily overrunning this buffer + sprintf(buf, "This is a float: %f", f); +} diff --git a/cpp/ql/src/Likely Bugs/Memory Management/PotentialBufferOverflow.qhelp b/cpp/ql/src/Likely Bugs/Memory Management/PotentialBufferOverflow.qhelp new file mode 100644 index 000000000000..8ddd9a54adc0 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/PotentialBufferOverflow.qhelp @@ -0,0 +1,41 @@ + + + +

    This rule highlights potentially overflowing calls to the functions sprintf, vsprintf, and gets with a warning. +These functions allow unbounded writes to buffers, which may cause an overflow when used on untrusted data or without adequate checks on the size of the data. Function calls of this type constitute a security risk through buffer overflows. The gets function, in particular, +is one of the vulnerabilities exploited by the Internet Worm of 1988, one of the first computer worms to spread through the Internet.

    + +
    + +

    Always control the length of buffer copy and buffer write operations. Use the safer variants snprintf, vsnprintf, and fgets, which include an extra buffer length argument.

    + +
    + + + +

    To improve the security of this example code, three changes should be made:

    +
      +
    1. Introduce a preprocessor define for the size of the buffer.
    2. +
    3. Replace the call to gets with fgets, specifying the define as the maximum length to copy. This will prevent the buffer overflow.
    4. +
    5. Replace both calls to sprintf with snprintf, specifying the define as the maximum length to copy. This will prevent the buffer overflow.
    6. +
    7. Consider using the %g format specifier instead of %f.
    8. +
    + +
    + + +
  • Common Weakness +Enumeration: CWE-120: Buffer Copy without Checking Size of Input ('Classic Buffer Overflow').
  • +
  • CERT C Coding +Standard: STR31-C. Guarantee +that storage for strings has sufficient space for character data and +the null terminator.
  • +
  • M. Howard, D. Leblanc, J. Viega, 19 Deadly Sins of Software Security: Programming Flaws and How to Fix Them, McGraw-Hill Osborne, 2005.
  • +
  • Wikipedia: Morris worm.
  • +
  • E. Spafford. The Internet Worm Program: An Analysis. Purdue Technical Report CSD-TR-823, (online), 1988.
  • + + +
    +
    diff --git a/cpp/ql/src/Likely Bugs/Memory Management/PotentialBufferOverflow.ql b/cpp/ql/src/Likely Bugs/Memory Management/PotentialBufferOverflow.ql new file mode 100644 index 000000000000..bbd05de5c5e2 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/PotentialBufferOverflow.ql @@ -0,0 +1,60 @@ +/** + * @name Use of inherently dangerous function + * @description Using a library function that does not check buffer bounds + * requires the surrounding program to be very carefully written + * to avoid buffer overflows. + * @kind problem + * @id cpp/potential-buffer-overflow + * @problem.severity warning + * @tags reliability + * security + * external/cwe/cwe-242 + */ +import cpp +import semmle.code.cpp.commons.Buffer + +abstract class PotentiallyDangerousFunctionCall extends FunctionCall { + abstract predicate isDangerous(); + abstract string getDescription(); +} + +class GetsCall extends PotentiallyDangerousFunctionCall { + GetsCall() { + this.getTarget().hasName("gets") + } + + override predicate isDangerous() { + any() + } + + override string getDescription() { + result = "gets does not guard against buffer overflow" + } +} + +class SprintfCall extends PotentiallyDangerousFunctionCall { + SprintfCall() { + this.getTarget().hasName("sprintf") or this.getTarget().hasName("vsprintf") + } + + int getBufferSize() { + result = getBufferSize(this.getArgument(0), _) + } + + int getMaxConvertedLength() { + result = this.getArgument(1).(FormatLiteral).getMaxConvertedLength() + } + + override predicate isDangerous() { + this.getMaxConvertedLength() > this.getBufferSize() + } + + override string getDescription() { + result = "This conversion may yield a string of length "+this.getMaxConvertedLength().toString()+ + ", which exceeds the allocated buffer size of "+this.getBufferSize().toString() + } +} + +from PotentiallyDangerousFunctionCall c +where c.isDangerous() +select c, c.getDescription() diff --git a/cpp/ql/src/Likely Bugs/Memory Management/ReturnCstrOfLocalStdString.qhelp b/cpp/ql/src/Likely Bugs/Memory Management/ReturnCstrOfLocalStdString.qhelp new file mode 100644 index 000000000000..9791f39145e5 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/ReturnCstrOfLocalStdString.qhelp @@ -0,0 +1,39 @@ + + + +

    The c_str method of std::string returns a +raw pointer to the memory buffer owned by +the std::string. The pointer is only safe to use while +the std::string is still in scope. When +the std::string goes out of scope, its destructor is +called and the memory is deallocated, so it is no longer safe to use +the pointer.

    +
    + + + + +

    +Avoid using C-strings. It is much safer to use +std::string throughout the codebase, because then the +memory will be automatically managed. +

    +

    If C-strings must be used, then be very careful to make sure that +there are no pointers to the string that can outlive the lifetime of +the string. For example, if the C-string is stack-allocated then it +unsafe to store a pointer to the string anywhere on the heap unless +you are sure that the heap memory will be deallocated before the end +of the function. +

    +
    + + + + +
  • cplusplus.com: std::string::c_str
  • +
  • stackoverflow.com: What +is std::string::c_str() lifetime?
  • +
    +
    diff --git a/cpp/ql/src/Likely Bugs/Memory Management/ReturnCstrOfLocalStdString.ql b/cpp/ql/src/Likely Bugs/Memory Management/ReturnCstrOfLocalStdString.ql new file mode 100644 index 000000000000..f2b189e4ea82 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/ReturnCstrOfLocalStdString.ql @@ -0,0 +1,98 @@ +/** + * @name Return c_str of local std::string + * @description Returning the c_str of a locally allocated std::string + * could cause the program to crash or behave non-deterministically + * because the memory is deallocated when the std::string goes out of + * scope. + * @kind problem + * @problem.severity warning + * @precision high + * @id cpp/return-c-str-of-std-string + * @tags reliability + * correctness + */ + +import cpp +import semmle.code.cpp.controlflow.SSA +import semmle.code.cpp.dataflow.DataFlow + +/** The `std::string` class. */ +class StdString extends Class { + StdString() { + // `std::string` is usually a typedef and the actual class + // is called something like `string std::__cxx11::basic_string`. + exists (Type stdstring, Namespace std + | stdstring.getName() = "string" and + this = stdstring.getUnspecifiedType() and + // Make sure that the class is in the `std` namespace. + std = this.getNamespace().getParentNamespace*() and + std.getName() = "std" and + std.getParentNamespace() instanceof GlobalNamespace) + } +} + +/** + * Holds if `e` is a direct or indirect reference to a locally + * allocated `std::string`. + */ +predicate refToStdString(Expr e, ConstructorCall source) { + exists (StdString stdstring + | stdstring.getAMemberFunction() = source.getTarget() and + not exists(LocalVariable v + | source = v.getInitializer().getExpr() and + v.isStatic()) and + e = source) + or + // Indirect use. + exists(Expr prev + | refToStdString(prev, source) and + DataFlow::localFlowStep(DataFlow::exprNode(prev), DataFlow::exprNode(e))) +} + +/** + * Holds if the function takes a C-style string as one of its arguments and + * includes the pointer in its result without making a copy of the + * string. An example of this is the method `JNIEnv::NewStringUTF()` (from + * Java's JNI), which returns a `jstring` containing a pointer to the + * C-style string. If the C-style string is deallocated then the `jstring` + * will also become invalid. + */ +predicate flowFunction(Function fcn, int argIndex) { + (fcn.getQualifiedName() = "_JNIEnv::NewStringUTF" and argIndex = 0) + or + (fcn.getQualifiedName() = "art::JNI::NewStringUTF" and argIndex = 1) + or + (fcn.getQualifiedName() = "art::CheckJNI::NewStringUTF" and argIndex = 1) + + // Add other functions that behave like NewStringUTF here. +} + +/** + * Holds if `e` is a direct or indirect reference to the result of calling + * `c_str` on a locally allocated `std::string`. + */ +predicate refToCStr(Expr e, ConstructorCall source) { + exists (MemberFunction f, FunctionCall call + | f.getName() = "c_str" and + call = e and + call.getTarget() = f and + refToStdString(call.getQualifier(), source)) + or + // Indirect use. + exists(Expr prev + | refToCStr(prev, source) and + DataFlow::localFlowStep(DataFlow::exprNode(prev), DataFlow::exprNode(e))) + or + // Some functions, such as `JNIEnv::NewStringUTF()` (from Java's JNI) + // embed return a structure containing a reference to the C-style string. + exists (Function f, int argIndex + | flowFunction(f, argIndex) and + f = e.(Call).getTarget() and + refToCStr(e.(Call).getArgument(argIndex), source)) +} + +from ReturnStmt r, ConstructorCall source +where refToCStr(r.getExpr(), source) +select + r, "Return value may contain a dangling pointer to $@.", + source, "this local std::string" diff --git a/cpp/ql/src/Likely Bugs/Memory Management/ReturnCstrOfLocalStdStringBad.cpp b/cpp/ql/src/Likely Bugs/Memory Management/ReturnCstrOfLocalStdStringBad.cpp new file mode 100644 index 000000000000..bc067a0eb157 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/ReturnCstrOfLocalStdStringBad.cpp @@ -0,0 +1,6 @@ +#include + +const char* hello() { + std::string str("hello"); + return str.c_str(); // BAD: returning a dangling pointer. +} diff --git a/cpp/ql/src/Likely Bugs/Memory Management/ReturnCstrOfLocalStdStringGood.cpp b/cpp/ql/src/Likely Bugs/Memory Management/ReturnCstrOfLocalStdStringGood.cpp new file mode 100644 index 000000000000..17c19d14e676 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/ReturnCstrOfLocalStdStringGood.cpp @@ -0,0 +1,6 @@ +#include + +std::string hello() { + std::string str("hello"); + return str; // GOOD: returning a std::string is safe. +} diff --git a/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.cpp b/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.cpp new file mode 100644 index 000000000000..00dfa215b337 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.cpp @@ -0,0 +1,7 @@ +Record* fixRecord(Record* r) { + Record myRecord = *r; + delete r; + + myRecord.fix(); + return &myRecord; //returns reference to myRecord, which is a stack-allocated object +} diff --git a/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.qhelp b/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.qhelp new file mode 100644 index 000000000000..d07066b5087b --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.qhelp @@ -0,0 +1,25 @@ + + + + + +

    This rule finds return statements that return pointers to an object allocated on the stack. +The lifetime of a stack allocated memory location only lasts until the function returns, and +the contents of that memory become undefined after that. Clearly, using a pointer to stack +memory after the function has already returned will have undefined results.

    + +
    + +

    Use the functions of the malloc family to dynamically allocate memory on the heap for data that is used across function calls.

    + +
    + + + + + + + +
    diff --git a/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql b/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql new file mode 100644 index 000000000000..ec4901762749 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql @@ -0,0 +1,50 @@ +/** + * @name Returning stack-allocated memory + * @description A function returns a pointer to a stack-allocated region of + * memory. This memory is deallocated at the end of the function, + * which may lead the caller to dereference a dangling pointer. + * @kind problem + * @id cpp/return-stack-allocated-memory + * @problem.severity warning + * @tags reliability + */ +import cpp + +// an expression is possibly stack allocated if it is an aggregate literal +// or a reference to a possibly stack allocated local variables +predicate exprMaybeStackAllocated(Expr e) { + e instanceof AggregateLiteral + or varMaybeStackAllocated(e.(VariableAccess).getTarget()) +} + +// a local variable is possibly stack allocated if it is not static and +// is initialized to/assigned a possibly stack allocated expression +predicate varMaybeStackAllocated(LocalVariable lv) { + not lv.isStatic() and + ( lv.getType().getUnderlyingType() instanceof ArrayType + or exprMaybeStackAllocated(lv.getInitializer().getExpr()) + or exists(AssignExpr a | a.getLValue().(VariableAccess).getTarget() = lv and + exprMaybeStackAllocated(a.getRValue()))) +} + +// an expression possibly points to the stack if it takes the address of +// a possibly stack allocated expression, if it is a reference to a local variable +// that possibly points to the stack, or if it is a possibly stack allocated array +// that is converted (implicitly or explicitly) to a pointer +predicate exprMayPointToStack(Expr e) { + e instanceof AddressOfExpr and exprMaybeStackAllocated(e.(AddressOfExpr).getAnOperand()) + or varMayPointToStack(e.(VariableAccess).getTarget()) + or exprMaybeStackAllocated(e) and e.getType() instanceof ArrayType and e.getFullyConverted().getType() instanceof PointerType +} + +// a local variable possibly points to the stack if it is initialized to/assigned to +// an expression that possibly points to the stack +predicate varMayPointToStack(LocalVariable lv) { + exprMayPointToStack(lv.getInitializer().getExpr()) + or exists(AssignExpr a | a.getLValue().(VariableAccess).getTarget() = lv and + exprMayPointToStack(a.getRValue())) +} + +from ReturnStmt r +where exprMayPointToStack(r.getExpr()) +select r, "May return stack-allocated memory." diff --git a/cpp/ql/src/Likely Bugs/Memory Management/StackAddressEscapes.cpp b/cpp/ql/src/Likely Bugs/Memory Management/StackAddressEscapes.cpp new file mode 100644 index 000000000000..a3159034ad0d --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/StackAddressEscapes.cpp @@ -0,0 +1,11 @@ +static const int* xptr; + +void example1() { + int x = 0; + xptr = &x; // BAD: address of local variable stored in non-local memory. +} + +void example2() { + static const int x = 0; + xptr = &x; // GOOD: storing address of static variable is safe. +} diff --git a/cpp/ql/src/Likely Bugs/Memory Management/StackAddressEscapes.qhelp b/cpp/ql/src/Likely Bugs/Memory Management/StackAddressEscapes.qhelp new file mode 100644 index 000000000000..e3042d117375 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/StackAddressEscapes.qhelp @@ -0,0 +1,53 @@ + + + +

    +This rule finds assignments that might store the address of a local +variable in non-local memory. The address of the local variable is +only valid until the function returns, after which it becomes a +dangling pointer. Such code is safe if the dangling pointer is never +used after the function returns, but it is not a recommended coding +practice. There is also a risk that the code is not thread-safe, +unless the non-local memory is protected by a mutex. +

    + +
    + + +
      + +
    1. +If it is necessary to take the address of a local variable, then make +sure that the address is only stored in memory that does not outlive +the local variable. For example, it is safe to store the address in +another local variable. Similarly, it is also safe to pass the address +of a local variable to another function provided that the other +function only uses it locally and does not store it in non-local +memory. +
    2. +
    3. +If it is necessary to store an address which will outlive the +current function scope, then it should be allocated on the heap. Care +should be taken to make sure that the memory is deallocated when it is +no longer needed, particularly when using low-level memory management +routines such as malloc/free or +new/delete. Modern C++ applications often use smart +pointers, such as std::shared_ptr, to reduce the chance of +a memory leak. +
    4. +
    + +
    + + + + + + + +
  • Wikipedia: Dangling pointer.
  • + +
    +
    diff --git a/cpp/ql/src/Likely Bugs/Memory Management/StackAddressEscapes.ql b/cpp/ql/src/Likely Bugs/Memory Management/StackAddressEscapes.ql new file mode 100644 index 000000000000..97701d4a6e16 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/StackAddressEscapes.ql @@ -0,0 +1,34 @@ +/** + * @name Local variable address stored in non-local memory + * @description Storing the address of a local variable in non-local + * memory can cause a dangling pointer bug if the address + * is used after the function returns. + * @kind problem + * @problem.severity warning + * @precision medium + * @id cpp/stack-address-escape + * @tags reliability + */ +import cpp +import semmle.code.cpp.dataflow.StackAddress + +/** + * Find assignments where the rhs might be a stack pointer and the lhs is + * not a stack variable. Such assignments might allow a stack address to + * escape. + */ +predicate stackAddressEscapes(AssignExpr assignExpr, Expr source, boolean isLocal) { + stackPointerFlowsToUse(assignExpr.getRValue(), _, source, isLocal) and + not stackReferenceFlowsToUse(assignExpr.getLValue(), _, _, _) +} + +from Expr use, Expr source, boolean isLocal, string msg, string srcStr +where + stackAddressEscapes(use, source, isLocal) and + if isLocal = true + then (msg = "A stack address ($@) may be assigned to a non-local variable." and + srcStr = "source") + else (msg = "A stack address which arrived via a $@ may be assigned to a non-local variable." and + srcStr = "parameter") +select + use, msg, source, srcStr diff --git a/cpp/ql/src/Likely Bugs/Memory Management/StrncpyFlippedArgs.cpp b/cpp/ql/src/Likely Bugs/Memory Management/StrncpyFlippedArgs.cpp new file mode 100644 index 000000000000..07acc91cd5ac --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/StrncpyFlippedArgs.cpp @@ -0,0 +1,2 @@ +strncpy(dest, src, sizeof(src)); //wrong: size of dest should be used +strncpy(dest, src, strlen(src)); //wrong: size of dest should be used diff --git a/cpp/ql/src/Likely Bugs/Memory Management/StrncpyFlippedArgs.qhelp b/cpp/ql/src/Likely Bugs/Memory Management/StrncpyFlippedArgs.qhelp new file mode 100644 index 000000000000..2e297116710f --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/StrncpyFlippedArgs.qhelp @@ -0,0 +1,32 @@ + + + +

    The standard library function strncpy copies a source string to a destination buffer. The third argument defines the maximum number of characters to copy and should be less than +or equal to the size of the destination buffer. Calls of the form strncpy(dest, src, strlen(src)) or strncpy(dest, src, sizeof(src)) incorrectly set the third argument to the size of the source buffer. Executing a call of this type may cause a buffer overflow. Buffer overflows can lead to anything from a segmentation fault to a security vulnerability.

    + +
    + +

    Check the highlighted function calls carefully, and ensure that the size parameter is derived from the size of the destination buffer, +not the source buffer.

    + +
    + + + + + + + +
  • cplusplus.com: strncpy.
  • +
  • + I. Gerg. An Overview and Example of the Buffer-Overflow Exploit. IANewsletter vol 7 no 4. 2005. +
  • +
  • + M. Donaldson. Inside the Buffer Overflow Attack: Mechanism, Method & Prevention. SANS Institute InfoSec Reading Room. 2002. +
  • + + +
    +
    diff --git a/cpp/ql/src/Likely Bugs/Memory Management/StrncpyFlippedArgs.ql b/cpp/ql/src/Likely Bugs/Memory Management/StrncpyFlippedArgs.ql new file mode 100644 index 000000000000..77c59982015e --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/StrncpyFlippedArgs.ql @@ -0,0 +1,120 @@ +/** + * @name Possibly wrong buffer size in string copy + * @description Calling 'strncpy' with the size of the source buffer + * as the third argument may result in a buffer overflow. + * @kind problem + * @problem.severity warning + * @precision medium + * @id cpp/bad-strncpy-size + * @tags reliability + * correctness + * external/cwe/cwe-676 + * external/cwe/cwe-119 + * external/cwe/cwe-251 + */ +import cpp +import Buffer +private import semmle.code.cpp.valuenumbering.GlobalValueNumbering + +predicate isSizePlus(Expr e, BufferSizeExpr baseSize, int plus) +{ + ( + // baseSize + e = baseSize and plus = 0 + ) or exists(AddExpr ae, Expr operand1, Expr operand2, int plusSub | + // baseSize + n or n + baseSize + ae = e and + operand1 = ae.getAnOperand() and + operand2 = ae.getAnOperand() and + operand1 != operand2 and + isSizePlus(operand1, baseSize, plusSub) and + plus = plusSub + operand2.getValue().toInt() + ) or exists(SubExpr se, int plusSub | + // baseSize - n + se = e and + isSizePlus(se.getLeftOperand(), baseSize, plusSub) and + plus = plusSub - se.getRightOperand().getValue().toInt() + ) +} + +predicate strncpyFunction(Function f, int argDest, int argSrc, int argLimit) +{ + exists(string name | name = f.getName() | + ( + ( + name = "strcpy_s" or // strcpy_s(dst, max_amount, src) + name = "wcscpy_s" or // wcscpy_s(dst, max_amount, src) + name = "_mbscpy_s" // _mbscpy_s(dst, max_amount, src) + ) and + argDest = 0 and + argSrc = 2 and + argLimit = 1 + ) or ( + ( + name = "strncpy" or // strncpy(dst, src, max_amount) + name = "strncpy_l" or // strncpy_l(dst, src, max_amount, locale) + name = "wcsncpy" or // wcsncpy(dst, src, max_amount) + name = "_wcsncpy_l" or // _wcsncpy_l(dst, src, max_amount, locale) + name = "_mbsncpy" or // _mbsncpy(dst, src, max_amount) + name = "_mbsncpy_l" // _mbsncpy_l(dst, src, max_amount, locale) + ) and + argDest = 0 and + argSrc = 1 and + argLimit = 2 + ) + ) +} + +string nthString (int num) { + ( + num = 0 and + result = "first" + ) or ( + num = 1 and + result = "second" + ) or ( + num = 2 and + result = "third" + ) +} + +/** + * Gets the size of the expression, if it is initialized + * with a fixed size array. + */ +int arrayExprFixedSize(Expr e) { + result = e.getType().getUnspecifiedType().(ArrayType).getSize() + or + result = e.(NewArrayExpr).getAllocatedType().(ArrayType).getSize() + or + exists (SsaDefinition def, LocalVariable v + | not (e.getType().getUnspecifiedType() instanceof ArrayType) and + e = def.getAUse(v) and + result = arrayExprFixedSize(def.getDefiningValue(v))) +} + +from Function f, FunctionCall fc, int argDest, int argSrc, int argLimit, int charSize, + Access copyDest, Access copySource, string name, string nth +where + f = fc.getTarget() and + strncpyFunction(f, argDest, argSrc, argLimit) and + copyDest = fc.getArgument(argDest) and + copySource = fc.getArgument(argSrc) and + // Some of the functions operate on a larger char type, like `wchar_t`, so we + // need to take this into account in the fixed size case. + charSize = f.getParameter(argDest).getType().getUnspecifiedType().(PointerType).getBaseType().getSize() and + if exists (fc.getArgument(argLimit).getValue().toInt()) then ( + // Fixed sized case + arrayExprFixedSize(copyDest) < charSize * fc.getArgument(argLimit).getValue().toInt() + ) else exists (Access takenSizeOf, BufferSizeExpr sizeExpr, int plus | + // Variable sized case + sizeExpr = fc.getArgument(argLimit).getAChild*() and + isSizePlus(fc.getArgument(argLimit), sizeExpr, plus) and + plus >= 0 and + takenSizeOf = sizeExpr.getArg() and + globalValueNumber(copySource) = globalValueNumber(takenSizeOf) and // e.g. strncpy(x, y, strlen(y)) + globalValueNumber(copyDest) != globalValueNumber(takenSizeOf) // e.g. strncpy(y, y, strlen(y)) + ) + and name = fc.getTarget().getName() + and nth = nthString(argLimit) +select fc, "Potentially unsafe call to " + name + "; " + nth + " argument should be size of destination." diff --git a/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToMemset.cpp b/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToMemset.cpp new file mode 100644 index 000000000000..0aba2b0f2332 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToMemset.cpp @@ -0,0 +1,18 @@ + +// correct, memset uses sizeof(type) +int is[10]; +memset(is, 0, sizeof(is)); +struct S s; +memset(&s, 0, sizeof(struct S)); + +// incorrect examples +struct T *t1 = (struct T*)malloc(sizeof(struct T)); +struct T *t2 = (struct T*)malloc(sizeof(struct T)); +// the size of the struct is probably intended +// but this takes the size of a pointer +memset(t2, 0, sizeof(t2)); + +// correct but discouraged, use sizeof(struct T) instead +memset(t1, 0, sizeof(*t2)); +// correct, but it is preferred to do a direct assignment, i.e., t = 0; +memset(&t2, 0, sizeof(t2)); diff --git a/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToMemset.qhelp b/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToMemset.qhelp new file mode 100644 index 000000000000..7cfae673e196 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToMemset.qhelp @@ -0,0 +1,38 @@ + + + + + +

    This rule finds calls to the standard library function memset where the third argument +(specifying the number of bytes to set) does not appear to correspond with the first argument (the buffer +to be written).

    + +

    Usually, memset is employed to initialize dynamically allocated arrays or structs. +Since memset treats its first argument simply as an array of bytes, the third argument has to specify the size of the buffer in bytes. +For an array, the size is the number of elements of the array multiplied by the size of one of its elements; for a struct, the size is just the size of the struct type.

    + +

    If memset is invoked with a third argument that is not constant and looks like neither of these two cases, there might be a mistake. This could cause a buffer overflow which could in turn cause a segfault or corrupt the contents of other variables in memory.

    + +
    + +

    For structs the preferred way of computing the size is to use sizeof with the type as the argument. Dereferencing a pointer works but is more prone to mistakes. For arrays the best solution is to take the size of the array rather than the type, since the risk of forgetting to multiply with the number of elements is eliminated. Do not use memset to assign to scalars or pointers when a simple assignment would do.

    + +
    + + + + + + +
  • + Cplusplus.comn: memset +
  • +
  • + MSDN Library: memset, sizeof Operator +
  • + + +
    +
    diff --git a/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToMemset.ql b/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToMemset.ql new file mode 100644 index 000000000000..89217fdba6b6 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToMemset.ql @@ -0,0 +1,125 @@ +/** + * @name Suspicious call to memset + * @description Use of memset where the size argument is computed as the size of + * some non-struct type. When initializing a buffer, you should specify + * its size as * to ensure + * portability. + * @kind problem + * @id cpp/suspicious-call-to-memset + * @problem.severity recommendation + * @precision medium + * @tags reliability + * correctness + * external/cwe/cwe-676 + */ +import cpp + +/** + * Holds if `e` is a `sizeof` expression on type `t`, with + * optional multiplication by a constant. + */ +predicate sizeOfExpr(Expr e, Type t) { + ( + t = e.(SizeofTypeOperator).getTypeOperand() + ) or ( + t = e.(SizeofExprOperator).getExprOperand().getType() + ) or ( + sizeOfExpr(e.(MulExpr).getAnOperand(), t) and + e.(MulExpr).getAnOperand() instanceof Literal + ) +} + +/** + * Gets the type `t` with typedefs, array types and references removed. + * + * This is similar to `Type.stripType` except that it doesn't remove + * a `PointerType`. + */ +Type stripType(Type t) { + result = stripType(t.(TypedefType).getBaseType()) or + result = stripType(t.(ArrayType).getBaseType()) or + result = stripType(t.(ReferenceType).getBaseType()) or + result = stripType(t.(SpecifiedType).getBaseType()) or + ( + not t instanceof TypedefType and + not t instanceof ArrayType and + not t instanceof ReferenceType and + not t instanceof SpecifiedType and + result = t + ) +} + +/** + * Holds if `t` points to `base` via a specified number of levels of pointer + * indirection. Intermediate typedefs and array types are allowed. + */ +predicate pointerIndirection(Type t, int indirection, Type base) { + exists(Type u | + u = stripType(t) and + u = stripType(base) and + not u instanceof PointerType and + indirection = 0 + ) or ( + pointerIndirection(stripType(t).(PointerType).getBaseType(), indirection - 1, base) + ) +} + +/** + * Holds if `t` points to a non-pointer, non-array type via a specified number + * of levels of pointer indirection. Intermediate typedefs and array types are + * allowed. + */ +predicate pointerIndirection2(Type t, int indirection) { + ( + not stripType(t) instanceof PointerType and + indirection = 0 + ) or ( + pointerIndirection2(stripType(t).(PointerType).getBaseType(), indirection - 1) + ) +} + +/** + * Holds if `memset(dataArg, _, sizeArg)`, where `sizeArg` has the form + * `sizeof(type)`, could be reasonable. + */ +predicate reasonableMemset(FunctionCall fc) { + exists(Expr dataArg, Expr sizeArg | + dataArg = fc.getArgument(0) and sizeArg = fc.getArgument(2) and + exists(Type dataType, Type sizeOfType | + dataType = dataArg.getType() and + sizeOfExpr(sizeArg, sizeOfType) and + exists (int i | + exists(Type base | + // memset(&t, _, sizeof(t)) + pointerIndirection(dataType, i + 1, base) and + pointerIndirection(sizeOfType, i, base) + ) or exists(Type base | + // memset(t[n], _, sizeof(t)) + pointerIndirection(dataType.getUnspecifiedType().(ArrayType), i, base) and + pointerIndirection(sizeOfType, i, base) + ) or exists(VoidType vt | + // memset(void *, _, sizeof(t)) + pointerIndirection(dataType, i + 1, vt) and + pointerIndirection2(sizeOfType, i) + ) or exists(Type ct | + // memset(char *, _, sizeof(t)) and similar + ct.getSize() = 1 and + pointerIndirection(dataType, i + 1, ct) and + pointerIndirection2(sizeOfType, i) + ) or exists(Type ct | + // memset(char [], _, sizeof(t)) and similar + ct.getSize() = 1 and + pointerIndirection(dataType.getUnspecifiedType().(ArrayType), i, ct) and + pointerIndirection2(sizeOfType, i) + ) + ) + ) + ) +} + +from FunctionCall fc, Type t +where + fc.getTarget().hasName("memset") and + sizeOfExpr(fc.getArgument(2), t) and + not reasonableMemset(fc) +select fc, "The size of the memory area set by memset should not be the size of the type " + t.getName() + "." diff --git a/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToStrncat.cpp b/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToStrncat.cpp new file mode 100644 index 000000000000..219d082414dd --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToStrncat.cpp @@ -0,0 +1,4 @@ +strncat(dest, src, strlen(dest)); //wrong: should use remaining size of dest + +strncat(dest, src, sizeof(dest)); //wrong: should use remaining size of dest. + //Also fails if dest is a pointer and not an array. diff --git a/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToStrncat.qhelp b/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToStrncat.qhelp new file mode 100644 index 000000000000..5424338e1d15 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToStrncat.qhelp @@ -0,0 +1,31 @@ + + + +

    The standard library function strncat appends a source string to a target string. +The third argument defines the maximum number of characters to append and should be less than or equal to the remaining space in the destination buffer. Calls of the form strncat(dest, src, strlen(dest)) or strncat(dest, src, sizeof(dest)) set the third argument to the entire size of the destination buffer. Executing a call of this type may cause a buffer overflow unless the buffer is known to be empty. Buffer overflows can lead to anything from a segmentation fault to a security vulnerability.

    + +
    + +

    Check the highlighted function calls carefully to ensure that no buffer overflow is possible. For a more robust solution, consider updating the function call to include the remaining space in the destination buffer.

    + +
    + + + + + + +
  • cplusplus.com: strncat, + strncpy.
  • +
  • + I. Gerg, An Overview and Example of the Buffer-Overflow Exploit. IANewsletter vol 7 no 4, 2005. +
  • +
  • + M. Donaldson, Inside the Buffer Overflow Attack: Mechanism, Method & Prevention. SANS Institute InfoSec Reading Room, 2002. +
  • + + +
    +
    diff --git a/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToStrncat.ql b/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToStrncat.ql new file mode 100644 index 000000000000..665ea8ecccc3 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToStrncat.ql @@ -0,0 +1,23 @@ +/** + * @name Potentially unsafe call to strncat + * @description Calling 'strncat' with the size of the destination buffer + * as the third argument may result in a buffer overflow. + * @kind problem + * @problem.severity warning + * @precision medium + * @id cpp/unsafe-strncat + * @tags reliability + * correctness + * external/cwe/cwe-676 + * external/cwe/cwe-119 + * external/cwe/cwe-251 + */ +import cpp +import Buffer + +from FunctionCall fc, VariableAccess va1, VariableAccess va2 +where fc.getTarget().(Function).hasName("strncat") and + va1 = fc.getArgument(0) and + va2 = fc.getArgument(2).(BufferSizeExpr).getArg() and + va1.getTarget() = va2.getTarget() +select fc, "Potentially unsafe call to strncat." diff --git a/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousSizeof.cpp b/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousSizeof.cpp new file mode 100644 index 000000000000..7c38d27a73a9 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousSizeof.cpp @@ -0,0 +1,4 @@ +void f(char s[]) { + int size = sizeof(s); //wrong: s is now a char*, not an array. + //sizeof(s) will evaluate to sizeof(char *) +} diff --git a/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousSizeof.qhelp b/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousSizeof.qhelp new file mode 100644 index 000000000000..a3dd9f05dda4 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousSizeof.qhelp @@ -0,0 +1,35 @@ + + + + + +

    This rule finds expressions that take the size of a function parameter of array type. +In C, function parameters of array type are treated as if they had the corresponding pointer type, +so their size is always the size of the pointer type (typically either four or eight). +In particular, one cannot determine the size of a memory buffer passed as a parameter in this way. + +Using the sizeof operator on pointer types will produce unexpected results if the developer intended +to get the size of an array instead of the pointer. +

    + + +
    + +

    Modify the function to take an extra argument indicating the buffer size.

    + +
    + + + + + + +
  • + Comp.lang.c, Frequently Asked Questions: Question 6.3: So what is meant by the "equivalence of pointers and arrays" in C?. +
  • + + +
    +
    diff --git a/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousSizeof.ql b/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousSizeof.ql new file mode 100644 index 000000000000..238f876e1bd2 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousSizeof.ql @@ -0,0 +1,37 @@ +/** + * @name Suspicious 'sizeof' use + * @description Taking 'sizeof' of an array parameter is often mistakenly thought + * to yield the size of the underlying array, but it always yields + * the machine pointer size. + * @kind problem + * @problem.severity warning + * @precision medium + * @id cpp/suspicious-sizeof + * @tags reliability + * correctness + * external/cwe/cwe-467 + */ +import cpp + +class CandidateParameter extends Parameter { + CandidateParameter() { + // an array parameter + getType().getUnspecifiedType() instanceof ArrayType + or + ( + // a pointer parameter + getType().getUnspecifiedType() instanceof PointerType and + + // whose address is never taken (rules out common + // false positive patterns) + not exists(AddressOfExpr aoe | aoe.getAddressable() = this) + ) + } +} + +from SizeofExprOperator seo, VariableAccess va +where seo.getExprOperand() = va and + va.getTarget() instanceof CandidateParameter and + not va.isAffectedByMacro() and + not va.isCompilerGenerated() +select seo, "This evaluates to the size of the pointer type, which may not be what you want." diff --git a/cpp/ql/src/Likely Bugs/Memory Management/UninitializedLocal.cpp b/cpp/ql/src/Likely Bugs/Memory Management/UninitializedLocal.cpp new file mode 100644 index 000000000000..4e818111d26c --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/UninitializedLocal.cpp @@ -0,0 +1,31 @@ +int absWrong(int i) { + int j; + if (i > 0) { + j = i; + } else if (i < 0) { + j = -i; + } + return j; // wrong: j may not be initialized before use +} + +int absCorrect1(int i) { + int j = 0; + if (i > 0) { + j = i; + } else if (i < 0) { + j = -i; + } + return j; // correct: j always initialized before use +} + +int absCorrect2(int i) { + int j; + if (i > 0) { + j = i; + } else if (i < 0) { + j = -i; + } else { + j = 0; + } + return j; // correct: j always initialized before use +} \ No newline at end of file diff --git a/cpp/ql/src/Likely Bugs/Memory Management/UninitializedLocal.qhelp b/cpp/ql/src/Likely Bugs/Memory Management/UninitializedLocal.qhelp new file mode 100644 index 000000000000..1427f74525e3 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/UninitializedLocal.qhelp @@ -0,0 +1,38 @@ + + + + + +

    A local non-static variable of a non-class type has an undefined value +before it is initialized. For example, it is incorrect to rely on +an uninitialized integer to have the value 0. +

    + +
    + + +

    +Review the code and consider whether the variable should have an initializer or +whether some path through the program lacks an assignment to the variable. +

    + +
    + +

    The function absWrong does not initialize the variable j +in the case where i = 0. Functions absCorrect1 and +absCorrect2 remedy this deficiency by adding an initializer and adding +an assignment to one of the paths through the program, respectively. +

    + + + + +
    + + +
  • ISO/IEC 9899:2011: Programming languages - C (Section 6.3.2.1).
  • + +
    +
    diff --git a/cpp/ql/src/Likely Bugs/Memory Management/UninitializedLocal.ql b/cpp/ql/src/Likely Bugs/Memory Management/UninitializedLocal.ql new file mode 100644 index 000000000000..f14b6bbea702 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/UninitializedLocal.ql @@ -0,0 +1,91 @@ +/** + * @name Potentially uninitialized local variable + * @description Reading from a local variable that has not been assigned to + * will typically yield garbage. + * @kind problem + * @id cpp/uninitialized-local + * @problem.severity warning + * @precision medium + * @tags security + * external/cwe/cwe-665 + * external/cwe/cwe-457 + */ + +import cpp +import semmle.code.cpp.controlflow.LocalScopeVariableReachability + +/** + * Auxiliary predicate: Types that don't require initialization + * before they are used, since they're stack-allocated. + */ +predicate allocatedType(Type t) { + /* Arrays: "int foo[1]; foo[0] = 42;" is ok. */ + t instanceof ArrayType or + /* Structs: "struct foo bar; bar.baz = 42" is ok. */ + t instanceof Class or + /* Typedefs to other allocated types are fine. */ + allocatedType(t.(TypedefType).getUnderlyingType()) or + /* Type specifiers don't affect whether or not a type is allocated. */ + allocatedType(t.getUnspecifiedType()) +} + +/** + * A declaration of a local variable that leaves the + * variable uninitialized. + */ +DeclStmt declWithNoInit(LocalVariable v) { + result.getADeclaration() = v and + not exists(v.getInitializer()) and + /* The type of the variable is not stack-allocated. */ + not allocatedType(v.getType()) and + /* The variable is not static (otherwise it is zeroed). */ + not v.isStatic() and + /* The variable is not extern (otherwise it is zeroed). */ + not v.hasSpecifier("extern") +} + +class UninitialisedLocalReachability extends LocalScopeVariableReachability { + UninitialisedLocalReachability() { this = "UninitialisedLocal" } + + override predicate isSource(ControlFlowNode node, LocalScopeVariable v) { + node = declWithNoInit(v) + } + + override predicate isSink(ControlFlowNode node, LocalScopeVariable v) { + useOfVarActual(v, node) + } + + override predicate isBarrier(ControlFlowNode node, LocalScopeVariable v) { + // only report the _first_ possibly uninitialized use + useOfVarActual(v, node) or + definitionBarrier(v, node) + } +} + +pragma[noinline] +predicate containsInlineAssembly(Function f) { + exists(AsmStmt s | s.getEnclosingFunction() = f) +} + +/** + * Auxiliary predicate: List common exceptions or false positives + * for this check to exclude them. + */ +VariableAccess commonException() { + /* If the uninitialized use we've found is in a macro expansion, it's + * typically something like va_start(), and we don't want to complain. + */ + result.getParent().isInMacroExpansion() or + result.getParent() instanceof BuiltInOperation or + /* + * Finally, exclude functions that contain assembly blocks. It's + * anyone's guess what happens in those. + */ + containsInlineAssembly(result.getEnclosingFunction()) +} + +from UninitialisedLocalReachability r, LocalVariable v, VariableAccess va +where + r.reaches(_, v, va) and + not va = commonException() +select va, "The variable $@ may not be initialized here.", v, v.getName() diff --git a/cpp/ql/src/Likely Bugs/Memory Management/UnsafeUseOfStrcat.cpp b/cpp/ql/src/Likely Bugs/Memory Management/UnsafeUseOfStrcat.cpp new file mode 100644 index 000000000000..a17e89e711e2 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/UnsafeUseOfStrcat.cpp @@ -0,0 +1,12 @@ +void f(char *s) { + char buf[80]; + strcpy(buf, "s: "); + strcat(buf, s); // wrong: buffer not checked before strcat +} + +void g(char *s) { + char buf[80]; + strcpy(buf, "s: "); + if(strlen(s) < 77) + strcat(buf, s); // correct: buffer size checked before strcat +} diff --git a/cpp/ql/src/Likely Bugs/Memory Management/UnsafeUseOfStrcat.qhelp b/cpp/ql/src/Likely Bugs/Memory Management/UnsafeUseOfStrcat.qhelp new file mode 100644 index 000000000000..6f985f1b2e48 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/UnsafeUseOfStrcat.qhelp @@ -0,0 +1,35 @@ + + + +

    The standard library function strcat appends a source string to a target string. If you do not check the size of the source string then you cannot guarantee that +appending the data to the target string will not cause a buffer overflow. Buffer overflows can lead to anything from a segmentation fault to a security vulnerability.

    + +
    + +

    Check the highlighted function calls carefully to ensure that no buffer overflow is possible. +For a more robust solution, consider adding explicit range checks or using the strncat +function instead.

    + +
    + + + + + + + +
  • + I. Gerg, An Overview and Example of the Buffer-Overflow Exploit. IANewsletter vol 7, no 4, 2005. +
  • +
  • + M. Donaldson, Inside the Buffer Overflow Attack: Mechanism, Method & Prevention. SANS Institute InfoSec Reading Room. 2002. +
  • + + + + +
    +
    diff --git a/cpp/ql/src/Likely Bugs/Memory Management/UnsafeUseOfStrcat.ql b/cpp/ql/src/Likely Bugs/Memory Management/UnsafeUseOfStrcat.ql new file mode 100644 index 000000000000..ab88ceb96e20 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/UnsafeUseOfStrcat.ql @@ -0,0 +1,38 @@ +/** + * @name Potentially unsafe use of strcat + * @description Using 'strcat' without checking the size of the source string + * may result in a buffer overflow + * @kind problem + * @problem.severity warning + * @precision medium + * @id cpp/unsafe-strcat + * @tags reliability + * correctness + * external/cwe/cwe-676 + * external/cwe/cwe-120 + * external/cwe/cwe-251 + */ +import cpp +import Buffer + +/** + * An access to a variable that is initialized by a constant + * expression, and is never used as an lvalue anywhere else. + */ +predicate isEffectivelyConstAccess(VariableAccess a) +{ + exists(Variable v | + a.getTarget() = v and + v.getInitializer().getExpr().isConstant() and + not v.getAnAccess().isUsedAsLValue() + ) +} + +from FunctionCall fc, VariableAccess src +where fc.getTarget().hasName("strcat") and + src = fc.getArgument(1) and + not src.getType() instanceof ArrayType and + not exists(BufferSizeExpr bse | + bse.getArg().(VariableAccess).getTarget() = src.getTarget()) and + not isEffectivelyConstAccess(src) +select fc, "Always check the size of the source buffer when using strcat." diff --git a/cpp/ql/src/Likely Bugs/NestedLoopSameVar.cpp b/cpp/ql/src/Likely Bugs/NestedLoopSameVar.cpp new file mode 100644 index 000000000000..7dd968cec7d0 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/NestedLoopSameVar.cpp @@ -0,0 +1,16 @@ +int x1 = 0; +for (x1 = 0; x1 < 100; x1++) { + int x2 = 0; + for (x1 = 0; x1 < 300; x1++) { + // this is most likely a typo + // the outer loop will exit immediately + } +} + +for (x1 = 0; x1 < 100; x1++) { + if(x1 == 10 && condition) { + for (; x1 < 75; x1++) { + // this should be written as a while loop + } + } +} diff --git a/cpp/ql/src/Likely Bugs/NestedLoopSameVar.qhelp b/cpp/ql/src/Likely Bugs/NestedLoopSameVar.qhelp new file mode 100644 index 000000000000..9180a91da270 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/NestedLoopSameVar.qhelp @@ -0,0 +1,36 @@ + + + + + +

    This rule finds nested loops in which the iteration variable is the same for both loops. +The behavior of the loop will be difficult to understand as the inner loop will affect the +iteration variable of the outer loop; this is most likely a typo.

    + +

    The rule flags the condition expression in the inner loop that uses the same variable as the iteration variable of the +outer loop.

    + +
    + +

    If the inner loop is starting by initializing the variable to 0, it's most likely a typo and then the inner loop variable should be changed. It is good practice to use descriptive names in nested loops rather than i and j to avoid confusion. If the inner loop is merely consuming the rest of the iteration as a special case, then it's better to replace the inner for loop with a while loop and also document it.

    + +
    + + + + + +
  • + Tutorialspoint - The C++ Programming Language: C++ nested loops +
  • +
  • + MSDN Library: Nested Control Structures +
  • + + + + +
    +
    diff --git a/cpp/ql/src/Likely Bugs/NestedLoopSameVar.ql b/cpp/ql/src/Likely Bugs/NestedLoopSameVar.ql new file mode 100644 index 000000000000..4edf3d0706e7 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/NestedLoopSameVar.ql @@ -0,0 +1,17 @@ +/** + * @name Nested loops with same variable + * @description When a nested loop uses the same iteration variable as its outer loop, the + * behavior of the outer loop easily becomes difficult to understand as the + * inner loop will affect its control flow. It is likely to be a typo. + * @kind problem + * @problem.severity warning + * @precision medium + * @id cpp/nested-loops-with-same-variable + * @tags maintainability + * correctness + */ +import NestedLoopSameVar + +from ForStmt inner, Variable iteration, ForStmt outer +where nestedForViolation(inner, iteration, outer) +select inner.getCondition(), "Nested for statement uses loop variable $@ of enclosing $@.", iteration, iteration.getName(), outer, "for statement" diff --git a/cpp/ql/src/Likely Bugs/NestedLoopSameVar.qll b/cpp/ql/src/Likely Bugs/NestedLoopSameVar.qll new file mode 100644 index 000000000000..0dbe236d418e --- /dev/null +++ b/cpp/ql/src/Likely Bugs/NestedLoopSameVar.qll @@ -0,0 +1,37 @@ +/** + * Provides the implementation of the query 'Nested loops with same variable'. + */ + +import cpp + +/** + * An access to a field of the form `object.field`. + */ +predicate simpleFieldAccess(Variable object, Variable field, VariableAccess access) { + access.getTarget() = field and + access.getQualifier().(VariableAccess).getTarget() = object +} + +/** + * Holds if `inner` and `outer` are nested for statements that + * use the same loop variable `iteration`. + */ +predicate nestedForViolation(ForStmt inner, Variable iteration, ForStmt outer) { + // same variable + iteration = inner.getAnIterationVariable() and + iteration = outer.getAnIterationVariable() and + + // field accesses must have the same object + ( + iteration instanceof Field implies + exists(Variable obj | + simpleFieldAccess(obj, iteration, inner.getCondition().getAChild*()) and + simpleFieldAccess(obj, iteration, outer.getCondition().getAChild*()) + ) + ) and + + // ordinary nested loops + exists(inner.getInitialization()) and + inner.getParent+() = outer and + inner.getASuccessor+() = outer.getCondition() +} diff --git a/cpp/ql/src/Likely Bugs/OO/IncorrectConstructorDelegation.qhelp b/cpp/ql/src/Likely Bugs/OO/IncorrectConstructorDelegation.qhelp new file mode 100644 index 000000000000..88af1203ef9e --- /dev/null +++ b/cpp/ql/src/Likely Bugs/OO/IncorrectConstructorDelegation.qhelp @@ -0,0 +1,68 @@ + + + + + +

    Prior to C++11, there is no mechanism for a constructor to delegate part of the +object initialization to another, although other languages provide this feature. +Consequently, any instance where a constructor call appears in the body of a constructor +without being used is suspect.

    + +
    + +

    The rule flags constructor calls in constructors which are not used in some way. This +is usually a misguided attempt to share some initialization code between multiple +constructors, or to provide sensible defaults for some constructor parameters. The effect +of a flagged expression would be to initialize an instance of the current class on the stack, +and then let it go out of scope at the end of the constructor call.

    + +

    There are several ways to address the underlying issue of sharing initialization code, +and the most appropriate needs to picked in each case. Roughly speaking, the options are:

    + +
      +
    • Introduce actual default values for the constructor parameters.
    • +
    • Duplicate the initialization code in each constructor.
    • +
    • Factor out the initialization code into a member function that is called from + each constructor.
    • +
    • If your compiler supports it, use C++11's constructor delegation feature.
    • +
    + +
    + + +class Circle { +private: + double m_x; + double m_y; + double m_radius; + + double m_area; + +public: + // Real constructor: + Circle(double x, double y, double radius) : + m_x(x), m_y(y), m_radius(radius) + { + m_area = 3.14159 * m_radius * m_radius; + } + + Circle() { + // WRONG: Attempt to define the unit circle by default fails. + Circle(0, 0, 1); + } +}; + + + + + +
  • Dr Dobb's Journal: + Delegating constructors?
  • +
  • Wikipedia: + Object construction improvement in C++11.
  • + + +
    +
    diff --git a/cpp/ql/src/Likely Bugs/OO/IncorrectConstructorDelegation.ql b/cpp/ql/src/Likely Bugs/OO/IncorrectConstructorDelegation.ql new file mode 100644 index 000000000000..5399c0afc9c9 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/OO/IncorrectConstructorDelegation.ql @@ -0,0 +1,21 @@ +/** + * @name Incorrect constructor delegation + * @description A constructor in C++ cannot delegate part of the object + * initialization to another by calling it. This is likely to + * leave part of the object uninitialized. + * @kind problem + * @problem.severity warning + * @precision high + * @id cpp/constructor-delegation + * @tags maintainability + * readability + * language-features + */ + +import cpp + +from FunctionCall call +where call.getTarget() = call.getEnclosingFunction().(Constructor).getDeclaringType().getAConstructor() + and call.getParent() instanceof ExprStmt +select call, "The constructor " + call.getTarget().getName() + + " may leave the instance uninitialized, as it tries to delegate to another constructor." diff --git a/cpp/ql/src/Likely Bugs/OO/NonVirtualDestructor.cpp b/cpp/ql/src/Likely Bugs/OO/NonVirtualDestructor.cpp new file mode 100644 index 000000000000..93ceb8dd1114 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/OO/NonVirtualDestructor.cpp @@ -0,0 +1,31 @@ +class Base { +public: + Resource *p; + Base() { + p = createResource(); + } + //... + ~Base() { + //wrong: this destructor is non-virtual, but Base has a derived class + // with a non-virtual destructor + freeResource(p); + } +}; + +class Derived: public Base { +public: + Resource *dp; + Derived() { + dp = createResource2(); + } + ~Derived() { + freeResource2(dp); + } +}; + +int f() { + Base *b = new Derived(); //creates resources for both Base::p and Derived::dp + //... + delete b; //will only call Base::~Base(), leaking the resource dp. + // Change both destructors to virtual to ensure they are both called. +} diff --git a/cpp/ql/src/Likely Bugs/OO/NonVirtualDestructor.qhelp b/cpp/ql/src/Likely Bugs/OO/NonVirtualDestructor.qhelp new file mode 100644 index 000000000000..864057eaa36d --- /dev/null +++ b/cpp/ql/src/Likely Bugs/OO/NonVirtualDestructor.qhelp @@ -0,0 +1,26 @@ + + + + + +

    This rule finds classes that define a non-virtual destructor, yet they have derived classes that also define a non-virtual destructor. +This can prevent proper cleanup of resources as only the destructor of the type of the variable will be called (instead of the type of the +object instance).

    + +
    + +

    Make the destructor virtual.

    + +
    + + + + + + +
  • R. Chen, When should your destructor be virtual?.
  • +
  • S. Meyers. Effective C++ 3d ed. pp 40-44. Addison-Wesley Professional, 2005.
  • +
    +
    diff --git a/cpp/ql/src/Likely Bugs/OO/NonVirtualDestructor.ql b/cpp/ql/src/Likely Bugs/OO/NonVirtualDestructor.ql new file mode 100644 index 000000000000..b9350c66dca4 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/OO/NonVirtualDestructor.ql @@ -0,0 +1,21 @@ +/** + * @name Non-virtual destructor + * @description When a class and its derived class both define non-virtual + * destructors, the destructor of the derived class may not + * always be called. + * @kind problem + * @id cpp/non-virtual-destructor + * @problem.severity warning + * @tags reliability + */ + +// This query is deprecated, and replaced by jsf/4.10 Classes/AV Rule 78.ql, which has far fewer false positives on typical code. + +import cpp + +from Class base, Destructor d1, Class derived, Destructor d2 +where derived.getABaseClass+() = base and + d1.getDeclaringType() = base and + not d1.isVirtual() and + d2.getDeclaringType() = derived +select d1, "This destructor should probably be virtual." diff --git a/cpp/ql/src/Likely Bugs/OO/SelfAssignmentCheck.cpp b/cpp/ql/src/Likely Bugs/OO/SelfAssignmentCheck.cpp new file mode 100644 index 000000000000..a9b7df2a2b2f --- /dev/null +++ b/cpp/ql/src/Likely Bugs/OO/SelfAssignmentCheck.cpp @@ -0,0 +1,39 @@ + +class MyClass +{ +public: + MyClass() : data(new Data()) {} + ~MyClass() {delete data;} + + MyClass &operator=(const MyClass &other) + { + delete data; + data = other.data->clone(); // BAD: if other == *this, other.data has already been deleted! + return *this; + } + +private: + Data *data; +}; + +// If someone assigns a `MyClass` object to itself, the delete expression deletes both `this->data` +// and `other.data`, since `*this` and `other` are the same object. But the call to `clone` uses +// `*other.data`, which is no longer a valid object. Fix this by adding a check: + +class MyClass +{ +public: + MyClass() : data(new Data()) {} + ~MyClass() {delete data;} + + MyClass &operator=(const MyClass &other) + { + if (this == &other) { return *this; } // FIXED + delete data; + data = other.data->clone(); + return *this; + } + +private: + Data *data; +}; diff --git a/cpp/ql/src/Likely Bugs/OO/SelfAssignmentCheck.qhelp b/cpp/ql/src/Likely Bugs/OO/SelfAssignmentCheck.qhelp new file mode 100644 index 000000000000..aa1e12ab9a98 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/OO/SelfAssignmentCheck.qhelp @@ -0,0 +1,28 @@ + + + + + +

    This rule finds copy assignment operators that deallocate memory but do not check for self assignment. +This could lead to accessing an already freed memory location.

    + +

    This rule is particularly important if the copy assignment operator puts the current object in an invalid state +before getting a new value from the object on the right hand side.

    + +
    + +

    Copy assignment operator should check for self-assignment.

    + +
    + + + + + + + +
  • [1] M. Cline, Part of C++ FAQ: Assignment operators
  • + +
    diff --git a/cpp/ql/src/Likely Bugs/OO/SelfAssignmentCheck.ql b/cpp/ql/src/Likely Bugs/OO/SelfAssignmentCheck.ql new file mode 100644 index 000000000000..de7496e69d5d --- /dev/null +++ b/cpp/ql/src/Likely Bugs/OO/SelfAssignmentCheck.ql @@ -0,0 +1,19 @@ +/** + * @name Self assignment check + * @description Copy assignment operators should guard against self assignment; + * otherwise, self assignment is likely to cause memory + * corruption. + * @kind problem + * @id cpp/self-assignment-check + * @problem.severity warning + * @tags reliability + */ +import cpp + +// find copy assignment operators that deallocate memory but do not check for self assignment +from CopyAssignmentOperator cao +where exists(DestructorCall d | d.getEnclosingFunction() = cao) and + not exists(EqualityOperation eq | eq.getEnclosingFunction() = cao and + eq.getAChild() instanceof ThisExpr and + eq.getAChild().(AddressOfExpr).getAddressable() = cao.getParameter(0)) +select cao, "Copy assignment operator does not check for self assignment." diff --git a/cpp/ql/src/Likely Bugs/OO/ThrowInDestructor.cpp b/cpp/ql/src/Likely Bugs/OO/ThrowInDestructor.cpp new file mode 100644 index 000000000000..77f95e0b2573 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/OO/ThrowInDestructor.cpp @@ -0,0 +1,19 @@ +class C { +public: + //... + ~C(){ + if (error) { + throw "Exception in destructor"; //wrong: exception thrown in destructor + } + } +}; + +void f() { + C* c = new C(); + try { + doOperation(c); + delete c; + } catch ( char * do_operation_exception) { + delete c; //would immediately terminate program if C::~C throws an exception + } +} diff --git a/cpp/ql/src/Likely Bugs/OO/ThrowInDestructor.qhelp b/cpp/ql/src/Likely Bugs/OO/ThrowInDestructor.qhelp new file mode 100644 index 000000000000..a1880228a655 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/OO/ThrowInDestructor.qhelp @@ -0,0 +1,32 @@ + + + + + +

    This rule finds exceptions thrown in destructors. This is dangerous: If the destructor is called during stack unwinding as part of the +handling of another exception, throwing an additional exception will immediately terminate the program as per the C++ specification.

    + +
    + +

    Handle the error condition in a different way.

    + +
    + + + + + + + +
  • + M. Cline, C++ FAQ: How can I handle a destructor that fails? +
  • +
  • + B. Stroustrup. The C++ Programming Language Special Edition p 380. Addison Wesley. 2000. +
  • + + +
    +
    diff --git a/cpp/ql/src/Likely Bugs/OO/ThrowInDestructor.ql b/cpp/ql/src/Likely Bugs/OO/ThrowInDestructor.ql new file mode 100644 index 000000000000..61c099e1b6af --- /dev/null +++ b/cpp/ql/src/Likely Bugs/OO/ThrowInDestructor.ql @@ -0,0 +1,74 @@ +/** + * @name Exception thrown in destructor + * @description Throwing an exception from a destructor may cause immediate + * program termination. + * @kind problem + * @problem.severity warning + * @precision very-high + * @id cpp/throw-in-destructor + * @tags reliability + * readability + * language-features + */ +import cpp + +// This predicate finds the catch block enclosing a rethrow expression. +predicate bindEnclosingCatch(ReThrowExpr te, CatchBlock cb) +{ + te.getEnclosingBlock().getEnclosingBlock*() = cb + and not exists(CatchBlock other | te.getEnclosingBlock().getEnclosingBlock*() = other and other.getEnclosingBlock+() = cb) +} + +// This predicate strips references from types, i.e. T -> T, T* -> T*, T& -> T. +predicate bindStrippedReferenceType(Type qualified, Type unqualified) +{ + (not(qualified instanceof ReferenceType) and unqualified = qualified) + or unqualified = qualified.(ReferenceType).getBaseType() +} + +// This predicate determines (to a first approximation) the type thrown by a throw or rethrow expression. +predicate bindThrownType(ThrowExpr te, Type thrown) +{ + // For normal throws, the thrown type is easily determined as the type of the throw expression. + (not(te instanceof ReThrowExpr) and thrown = te.getActualType()) + + // For rethrows, we use the unqualified version of the type caught by the enclosing catch block. + // Note that this is not precise, but is a reasonable first approximation. + or exists(CatchBlock cb | bindEnclosingCatch(te, cb) and bindStrippedReferenceType(cb.getParameter().getType().getUnspecifiedType(), thrown)) +} + +// This predicate determines the catch blocks that can catch the exceptions thrown by each throw expression. +pragma[inline] +predicate canCatch(ThrowExpr te, CatchBlock cb) +{ + exists(Type thrown, Type caught | + bindThrownType(te, thrown) + and caught = cb.getParameter().getType().getUnspecifiedType() + and not bindEnclosingCatch(te, cb) + + and + ( + // Catching primitives by value or reference + bindStrippedReferenceType(caught, thrown) + + // Catching class types by value or reference + or exists(Class c | c = thrown and bindStrippedReferenceType(caught, c.getABaseClass*())) + ) + ) +} + +// Find throw expressions such that there is a path in the control flow graph from the expression to +// the end of the destructor without an intervening catch block that can catch the type thrown. +from Destructor d, ThrowExpr te +where + te.getEnclosingFunction() = d + and not exists(CatchBlock cb | + te.getASuccessor+() = cb + and cb.getASuccessor+() = d + | canCatch(te, cb) + or + // Catch anything -- written as `catch(...)`. + not exists(cb.getParameter()) + ) +select te, "Exception thrown in destructor." + diff --git a/cpp/ql/src/Likely Bugs/OO/VirtualCallInStructor.cpp b/cpp/ql/src/Likely Bugs/OO/VirtualCallInStructor.cpp new file mode 100644 index 000000000000..8c2fb3a4e172 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/OO/VirtualCallInStructor.cpp @@ -0,0 +1,35 @@ +class Base { +protected: + Resource* resource; +public: + virtual void init() { + resource = createResource(); + } + virtual void release() { + freeResource(resource); + } +}; + +class Derived: public Base { + virtual void init() { + resource = createResourceV2(); + } + virtual void release() { + freeResourceV2(resource); + } +}; + +Base::Base() { + this->init(); +} +Base::~Base() { + this->release(); +} + +int f() { + // this will call Base::Base() and then Derived::Derived(), but this->init() + // inBase::Base() will resolve to Base::init(), not Derived::init() + // The reason for this is that when Base::Base is called, the object being + // created is still of type Base (including the vtable) + Derived* d = new Derived(); +} diff --git a/cpp/ql/src/Likely Bugs/OO/VirtualCallInStructor.qhelp b/cpp/ql/src/Likely Bugs/OO/VirtualCallInStructor.qhelp new file mode 100644 index 000000000000..289b24531f02 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/OO/VirtualCallInStructor.qhelp @@ -0,0 +1,23 @@ + + + + + +

    This rule finds calls to virtual methods from within constructors and destructors. If the constructor of a derived class calls the constructor of its base type, the dynamic type of the object is the base type for the duration of the constructor call, so virtual method dispatch may yield unexpected results. The same holds for destructors.

    + +
    + +

    Carefully check the flagged calls to make sure they will behave as expected.

    + +
    + + + + + + +
  • S. Meyer. Effective C++ 3d ed. pp 48-52. Addison Wesley. 2005. Excerpt available here. + +
  • diff --git a/cpp/ql/src/Likely Bugs/OO/VirtualCallInStructor.ql b/cpp/ql/src/Likely Bugs/OO/VirtualCallInStructor.ql new file mode 100644 index 000000000000..c73f79fb9170 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/OO/VirtualCallInStructor.ql @@ -0,0 +1,27 @@ +/** + * @name Virtual call in constructor or destructor + * @description Calling a virtual function from a constructor or destructor + * rarely has the intended effect. It is likely to either cause a + * bug or confuse readers. + * @kind problem + * @id cpp/virtual-call-in-structor + * @problem.severity warning + * @tags reliability + */ +import cpp + +class Structor extends MemberFunction { + Structor() { + this instanceof Constructor + or this instanceof Destructor + } +} + +from Structor s, FunctionCall c, VirtualFunction vf +where c.getEnclosingFunction() = s and + vf = c.getTarget() and + exists(VirtualFunction vff | + vff.overrides(vf) and + vff.getDeclaringType().getABaseClass+() = s.getDeclaringType()) +select c, "Virtual call in constructor or destructor." + diff --git a/cpp/ql/src/Likely Bugs/ReturnConstType.cpp b/cpp/ql/src/Likely Bugs/ReturnConstType.cpp new file mode 100644 index 000000000000..d93b9a7acb3d --- /dev/null +++ b/cpp/ql/src/Likely Bugs/ReturnConstType.cpp @@ -0,0 +1,9 @@ +// The leftmost const has no effect here. +const int square(const int x) { + return x * x; +} + +// The const has no effect here, and can easily be mistaken for const char*. +char* const id(char* s) { + return s; +} diff --git a/cpp/ql/src/Likely Bugs/ReturnConstType.qhelp b/cpp/ql/src/Likely Bugs/ReturnConstType.qhelp new file mode 100644 index 000000000000..f285cf11df85 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/ReturnConstType.qhelp @@ -0,0 +1,31 @@ + + + + + +

    This rule finds non-member functions with a superfluous const qualifier +on their return type.

    + +
    + +

    The superfluous const qualifier should be removed, as it serves no purpose. +If the return type contains embedded qualifiers, then care should be taken to remove only +the superfluous one.

    + +
    + + + + + + +
  • + comp.lang.c FAQ list · Question 6.3 +
  • + + + +
    +
    diff --git a/cpp/ql/src/Likely Bugs/ReturnConstType.ql b/cpp/ql/src/Likely Bugs/ReturnConstType.ql new file mode 100644 index 000000000000..ab20cef95fca --- /dev/null +++ b/cpp/ql/src/Likely Bugs/ReturnConstType.ql @@ -0,0 +1,17 @@ +/** + * @name Constant return type + * @description A 'const' modifier on a function return type is useless and should be removed for clarity. + * @kind problem + * @problem.severity warning + * @precision very-high + * @id cpp/non-member-const-no-effect + * @tags maintainability + * readability + * language-features + */ +import ReturnConstTypeCommon + +from Function f +where hasSuperfluousConstReturn(f) + and not f instanceof MemberFunction +select f, "The 'const' modifier has no effect on a return type and can be removed." diff --git a/cpp/ql/src/Likely Bugs/ReturnConstTypeCommon.qll b/cpp/ql/src/Likely Bugs/ReturnConstTypeCommon.qll new file mode 100644 index 000000000000..fdcb0bf5ba27 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/ReturnConstTypeCommon.qll @@ -0,0 +1,21 @@ +import cpp + +private predicate mightHaveConstMethods(Type t) { + t instanceof Class + or t instanceof TemplateParameter +} + +predicate hasSuperfluousConstReturn(Function f) { + exists(Type t | t = f.getType() | + // This is the primary thing we're testing for, + t instanceof SpecifiedType + and t.hasSpecifier("const") + and (not affectedByMacro(t)) + // but "const" is meaningful when applied to user defined types, + and not mightHaveConstMethods(t.getUnspecifiedType()) + ) + // and therefore "const T" might be meaningful for other values of "T". + and not exists(TemplateFunction t | f = t.getAnInstantiation() | + t.getType().involvesTemplateParameter() + ) +} diff --git a/cpp/ql/src/Likely Bugs/ReturnConstTypeMember.cpp b/cpp/ql/src/Likely Bugs/ReturnConstTypeMember.cpp new file mode 100644 index 000000000000..cc6b6da3abb4 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/ReturnConstTypeMember.cpp @@ -0,0 +1,13 @@ +struct S { + int val; + + // The const has no effect here. + auto getValIncorrect() -> const int { + return val; + } + + // Whereas here it does make a semantic difference. + auto getValCorrect() const -> int { + return val + } +}; diff --git a/cpp/ql/src/Likely Bugs/ReturnConstTypeMember.qhelp b/cpp/ql/src/Likely Bugs/ReturnConstTypeMember.qhelp new file mode 100644 index 000000000000..4b4d90485411 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/ReturnConstTypeMember.qhelp @@ -0,0 +1,30 @@ + + + + + +

    This rule finds member functions with a superfluous const qualifier +on their return type. This might be caused by confusion about how to declare a const +method. In particular, when C++11 trailing return types are used, it becomes much easier +to mis-declare a const method by putting const on the wrong side of +->.

    + +
    + +

    The superfluous const qualifier should be removed, as it serves no purpose. +If the return type contains embedded qualifiers, then care should be taken to remove only +the superfluous one.

    +

    Alternatively, if the intention was to have a const method, then the qualifier +should be moved to immediately after the argument list.

    + +
    + + + + + + + +
    diff --git a/cpp/ql/src/Likely Bugs/ReturnConstTypeMember.ql b/cpp/ql/src/Likely Bugs/ReturnConstTypeMember.ql new file mode 100644 index 000000000000..724036c7c1eb --- /dev/null +++ b/cpp/ql/src/Likely Bugs/ReturnConstTypeMember.ql @@ -0,0 +1,20 @@ +/** + * @name Constant return type on member + * @description A 'const' modifier on a member function return type is useless. It is usually a typo or misunderstanding, since the syntax for a 'const' function is 'int foo() const', not 'const int foo()'. + * @kind problem + * @problem.severity warning + * @precision very-high + * @id cpp/member-const-no-effect + * @tags maintainability + * readability + * language-features + */ +import ReturnConstTypeCommon + +from MemberFunction f, string message +where hasSuperfluousConstReturn(f) and + if f.hasSpecifier("const") or f.isStatic() then + message = "The 'const' modifier has no effect on return types. The 'const' modifying the return type can be removed." + else + message = "The 'const' modifier has no effect on return types. For a const function, the 'const' should go after the parameter list." +select f, message diff --git a/cpp/ql/src/Likely Bugs/ShortLoopVarName.cpp b/cpp/ql/src/Likely Bugs/ShortLoopVarName.cpp new file mode 100644 index 000000000000..568c994ec640 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/ShortLoopVarName.cpp @@ -0,0 +1,14 @@ +int i = 0; +for (i = 0; i < NUM_RECORDS; i++) { + int j = 0; + //This loop should have a more descriptive iteration variable + for (j = 0; j < NUM_FIELDS; j++) { + process(record[i]->field[j]); + } + + int field_idx = 0; + //Better: the inner loop has a descriptive name + for (field_idx = 0; field_idx < NUM_FIELDS; field_idx++) { + save(record[i]->field[field_idx]); + } +} \ No newline at end of file diff --git a/cpp/ql/src/Likely Bugs/ShortLoopVarName.qhelp b/cpp/ql/src/Likely Bugs/ShortLoopVarName.qhelp new file mode 100644 index 000000000000..4f89ed78a920 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/ShortLoopVarName.qhelp @@ -0,0 +1,30 @@ + + + + + +

    This rule finds loops with an iteration variable that has a short name. + The iteration variable of a nested loop should have a descriptive name: +avoid short names like i, j, or k that can cause confusion except in very simple loops.

    + +
    + +

    Change the name of the inner loop's iteration variables to something more descriptive, to make it +easier to understand code in complex, nested loops.

    + +
    + + + + + +
  • + ROS C++ Style Guide: Variables +
  • + + + +
    +
    diff --git a/cpp/ql/src/Likely Bugs/ShortLoopVarName.ql b/cpp/ql/src/Likely Bugs/ShortLoopVarName.ql new file mode 100644 index 000000000000..92696cfc870d --- /dev/null +++ b/cpp/ql/src/Likely Bugs/ShortLoopVarName.ql @@ -0,0 +1,53 @@ +/** + * @name Error-prone name of loop variable + * @description The iteration variable of a nested loop should have a descriptive name: short names like i, j, or k can cause confusion except in very simple loops. + * @kind problem + * @id cpp/short-loop-var-name + * @problem.severity recommendation + * @precision medium + * @tags maintainability + * readability + */ +import cpp + +predicate short(Variable v) { + v.getName().length() = 1 +} + +predicate forStmtAncestor(Stmt child, ForStmt parent) { + child.getParent() = parent or forStmtAncestor(child.getParent(), parent) +} + +/** + * Gets an `ArrayExpr` that's nested directly inside `ArrayExpr ae`. + */ +ArrayExpr getANestedArrayExpr(ArrayExpr ae) { + result.getArrayBase() = ae +} + +/** + * Holds if variables `a` and `b` are accessed in a way that looks like they + * are a coordinate pair. For example: + * ``` + * arr[x][y] + * arr[(y * width) + x] + * ``` + */ +predicate coordinatePair(Variable a, Variable b) { + exists(ArrayExpr ae | + getANestedArrayExpr*(ae).getArrayOffset().getAChild*() = a.getAnAccess() and + getANestedArrayExpr*(ae).getArrayOffset().getAChild*() = b.getAnAccess() and + not a = b + ) +} + +from ForStmt outer, ForStmt inner, Variable iterationVar, Variable innerVar +where forStmtAncestor(inner, outer) and short(innerVar) + and iterationVar = outer.getAnIterationVariable() + and innerVar = inner.getAnIterationVariable() + and short(iterationVar) + and not coordinatePair(iterationVar, innerVar) +select iterationVar, + "Iteration variable " + iterationVar.getName() + " for $@ should have a descriptive name, since there is $@.", + outer, "this loop", + inner, "a nested loop" diff --git a/cpp/ql/src/Likely Bugs/UseInOwnInitializer.cpp b/cpp/ql/src/Likely Bugs/UseInOwnInitializer.cpp new file mode 100644 index 000000000000..da05a7ea3c3b --- /dev/null +++ b/cpp/ql/src/Likely Bugs/UseInOwnInitializer.cpp @@ -0,0 +1,10 @@ +int f() { + int x = x; // BAD: undefined behavior occurs here + x = 0; + return x; +} + +int g() { + int x = 0; // GOOD + return x; +} diff --git a/cpp/ql/src/Likely Bugs/UseInOwnInitializer.qhelp b/cpp/ql/src/Likely Bugs/UseInOwnInitializer.qhelp new file mode 100644 index 000000000000..945a21cba4ff --- /dev/null +++ b/cpp/ql/src/Likely Bugs/UseInOwnInitializer.qhelp @@ -0,0 +1,29 @@ + + + + + +

    A variable is in scope in its own initializer, but it is undefined behavior to load from it before it is first assigned to.

    + +
    + +

    Do not use a variable in its own initializer unless it is part of an address calculation or a sizeof expression.

    + +
    + + + + + +
  • + + SEI CERT Secure Coding Standard: EXP53-CPP. Do not read uninitialized memory + +
  • + + + +
    +
    diff --git a/cpp/ql/src/Likely Bugs/UseInOwnInitializer.ql b/cpp/ql/src/Likely Bugs/UseInOwnInitializer.ql new file mode 100644 index 000000000000..0ae5a91a54da --- /dev/null +++ b/cpp/ql/src/Likely Bugs/UseInOwnInitializer.ql @@ -0,0 +1,30 @@ +/** + * @name Variable used in its own initializer + * @id cpp/use-in-own-initializer + * @description Loading from a variable in its own initializer may lead to undefined behavior. + * @kind problem + * @problem.severity warning + * @precision high + * @tags maintainability + * correctness + */ + +import cpp + +from Initializer init, Variable v, VariableAccess va +where init.getDeclaration() = v + and va.getTarget() = v + and va.getParent*() = init + and ( + va.hasLValueToRValueConversion() or + exists (Assignment assn | assn.getLValue() = va) or + exists (CrementOperation crement | crement.getAnOperand() = va) + ) + and not va.isUnevaluated() + and not ( + va.getParent() = init and + exists(MacroInvocation mi | + va = mi.getExpr() + ) + ) +select va, v.getName() + " is used in its own initializer." diff --git a/cpp/ql/src/META-INF/MANIFEST.MF b/cpp/ql/src/META-INF/MANIFEST.MF new file mode 100644 index 000000000000..9297719168ec --- /dev/null +++ b/cpp/ql/src/META-INF/MANIFEST.MF @@ -0,0 +1,8 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Semmle C/C++ Default Queries +Bundle-SymbolicName: com.semmle.plugin.semmlecode.cpp.queries;singleton:=true +Bundle-Version: 1.18.0.qualifier +Bundle-Vendor: Semmle Ltd. +Bundle-ActivationPolicy: lazy +Require-Bundle: com.semmle.plugin.qdt.ui;bundle-version="[1.18.0.qualifier,1.18.0.qualifier]" diff --git a/cpp/ql/src/Metrics/Classes/CAfferentCoupling.qhelp b/cpp/ql/src/Metrics/Classes/CAfferentCoupling.qhelp new file mode 100644 index 000000000000..06c4bbf2539d --- /dev/null +++ b/cpp/ql/src/Metrics/Classes/CAfferentCoupling.qhelp @@ -0,0 +1,81 @@ + + + +

    +This metric measures the number of incoming dependencies for each class, + i.e. the number of other classes that depend on it. +

    + +

    +Classes that are depended upon by many other classes typically require a lot of +effort to change, because changing them will force their dependents to change +as well. This is not necessarily a bad thing -- indeed, most systems will have +some such classes (one example might be a string class). However, classes with a high number +of incoming dependencies +and a high number of outgoing dependencies are hard to maintain. They are hard to +change (high afferent coupling) and yet there are many reasons to +change them (high efferent coupling). This contradiction yields code that is very +hard to maintain or test. +

    + +

    +Conversely, some classes may only be depended on by very few other classes. Again, +this is not necessarily a problem -- we would expect, for example, that the +top-level classes of a system would meet this criterion. When lower-level +classes have very few incoming dependencies, however, it can be an indication +that a class is not pulling its weight. In extreme cases, classes may even +have an afferent coupling of 0, indicating that they are dead +code. +

    + +
    + + +

    +It is unwise to refactor a class based purely on its high or low number of +incoming dependencies -- a class's afferent coupling value only makes sense +in the context of its role in the system as a whole. However, when combined +with other metrics such as efferent coupling, it is possible to make some +general recommendations: +

    + +
      +
    • +Classes with high numbers of incoming and outgoing dependencies +are prime candidates for refactoring (although this +will not always be easy). The general strategy is to split the class into +smaller classes, each of which has fewer responsibilities, and refactor the code +that used the original class accordingly. +
    • + +
    • +Classes that have very few incoming dependencies and are not at the top level +of a system may not be pulling their weight and should be refactored. +
    • + +
    • +Classes that have an afferent coupling of 0 may be dead code -- +in this situation, they can often be deleted. +
    • +
    + + +
    + + + +
  • + M. Fowler. Refactoring. Addison-Wesley, 1999. +
  • +
  • + Wikipedia: Code refactoring +
  • +
  • + Refactoring as Meta Programming? +
  • + + +
    +
    diff --git a/cpp/ql/src/Metrics/Classes/CAfferentCoupling.ql b/cpp/ql/src/Metrics/Classes/CAfferentCoupling.ql new file mode 100644 index 000000000000..e2747ecad28b --- /dev/null +++ b/cpp/ql/src/Metrics/Classes/CAfferentCoupling.ql @@ -0,0 +1,17 @@ +/** + * @name Incoming dependencies per class + * @description The number of classes that depend on a class. + * @kind treemap + * @id cpp/afferent-coupling-per-class + * @treemap.warnOn highValues + * @metricType reftype + * @metricAggregate avg max + * @tags maintainability + * modularity + */ +import cpp + +from Class c +where c.fromSource() +select c, c.getMetrics().getAfferentCoupling() as n +order by n desc diff --git a/cpp/ql/src/Metrics/Classes/CEfferentCoupling.qhelp b/cpp/ql/src/Metrics/Classes/CEfferentCoupling.qhelp new file mode 100644 index 000000000000..335280e416c9 --- /dev/null +++ b/cpp/ql/src/Metrics/Classes/CEfferentCoupling.qhelp @@ -0,0 +1,46 @@ + + + +

    +This metric measures the number of outgoing dependencies for each class, +i.e. the number of other classes on which each class depends. +

    + +

    +Classes that depend on many other classes are quite brittle, because if any of +their dependencies change then they may have to as well. Furthermore, the +reason for the high number of dependencies is often that different bits of +the class depend on different sets of other classes, so it is not uncommon to +find that classes with high efferent coupling also lack cohesion. +

    + +
    + + +

    +Efferent coupling can be reduced by splitting a class into pieces along its +dependency fault lines. +

    + +
    + + + +
  • +R. Martin. Agile Software Development: Principles, Patterns and Practices. Pearson, 2011. +
  • +
  • + M. Fowler. Refactoring. Addison-Wesley, 1999. +
  • +
  • + Wikipedia: Code refactoring +
  • +
  • + Refactoring as Meta Programming? +
  • + + +
    +
    diff --git a/cpp/ql/src/Metrics/Classes/CEfferentCoupling.ql b/cpp/ql/src/Metrics/Classes/CEfferentCoupling.ql new file mode 100644 index 000000000000..1c8952810696 --- /dev/null +++ b/cpp/ql/src/Metrics/Classes/CEfferentCoupling.ql @@ -0,0 +1,17 @@ +/** + * @name Outgoing dependencies per class + * @description The number of classes on which a class depends. + * @kind treemap + * @id cpp/outgoing-type-dependencies + * @treemap.warnOn highValues + * @metricType reftype + * @metricAggregate avg max + * @tags testability + * modularity + */ +import cpp + +from Class c +where c.fromSource() +select c, c.getMetrics().getEfferentCoupling() as n +order by n desc diff --git a/cpp/ql/src/Metrics/Classes/CHalsteadBugs.ql b/cpp/ql/src/Metrics/Classes/CHalsteadBugs.ql new file mode 100644 index 000000000000..0346c812c6f7 --- /dev/null +++ b/cpp/ql/src/Metrics/Classes/CHalsteadBugs.ql @@ -0,0 +1,14 @@ +/** + * @name Halstead bug measure + * @description Measures the expected number of delivered defects. + * The Halstead bug count is known to be an underestimate. + * @kind treemap + * @id cpp/halstead-bugs-per-class + * @treemap.warnOn highValues + * @metricType reftype + * @metricAggregate avg sum max + */ +import cpp + +from MetricClass mc +select mc, mc.getHalsteadDeliveredBugs() diff --git a/cpp/ql/src/Metrics/Classes/CHalsteadDifficulty.ql b/cpp/ql/src/Metrics/Classes/CHalsteadDifficulty.ql new file mode 100644 index 000000000000..0346b43974c3 --- /dev/null +++ b/cpp/ql/src/Metrics/Classes/CHalsteadDifficulty.ql @@ -0,0 +1,13 @@ +/** + * @name Halstead difficulty + * @description Measures the error proneness of implementing the program + * @kind treemap + * @id cpp/halstead-difficulty-per-class + * @treemap.warnOn highValues + * @metricType reftype + * @metricAggregate avg sum max + */ +import cpp + +from MetricClass mc +select mc, mc.getHalsteadDifficulty() diff --git a/cpp/ql/src/Metrics/Classes/CHalsteadEffort.ql b/cpp/ql/src/Metrics/Classes/CHalsteadEffort.ql new file mode 100644 index 000000000000..086916029503 --- /dev/null +++ b/cpp/ql/src/Metrics/Classes/CHalsteadEffort.ql @@ -0,0 +1,13 @@ +/** + * @name Halstead effort + * @description Measures the effort to implement the program + * @kind treemap + * @id cpp/halstead-effort-per-class + * @treemap.warnOn highValues + * @metricType reftype + * @metricAggregate avg sum max + */ +import cpp + +from MetricClass mc +select mc, mc.getHalsteadEffort() diff --git a/cpp/ql/src/Metrics/Classes/CHalsteadLength.ql b/cpp/ql/src/Metrics/Classes/CHalsteadLength.ql new file mode 100644 index 000000000000..31351583a674 --- /dev/null +++ b/cpp/ql/src/Metrics/Classes/CHalsteadLength.ql @@ -0,0 +1,13 @@ +/** + * @name Halstead length + * @description Total number of operands and operators + * @kind treemap + * @id cpp/halstead-length-per-class + * @treemap.warnOn highValues + * @metricType reftype + * @metricAggregate avg sum max + */ +import cpp + +from MetricClass mc +select mc, mc.getHalsteadLength() diff --git a/cpp/ql/src/Metrics/Classes/CHalsteadVocabulary.ql b/cpp/ql/src/Metrics/Classes/CHalsteadVocabulary.ql new file mode 100644 index 000000000000..9e92d621ce96 --- /dev/null +++ b/cpp/ql/src/Metrics/Classes/CHalsteadVocabulary.ql @@ -0,0 +1,13 @@ +/** + * @name Halstead vocabulary + * @description Number of distinct operands and operators used + * @kind treemap + * @id cpp/halstead-vocabulary-per-class + * @treemap.warnOn highValues + * @metricType reftype + * @metricAggregate avg sum max + */ +import cpp + +from MetricClass mc +select mc, mc.getHalsteadVocabulary() diff --git a/cpp/ql/src/Metrics/Classes/CHalsteadVolume.ql b/cpp/ql/src/Metrics/Classes/CHalsteadVolume.ql new file mode 100644 index 000000000000..50759cf173cb --- /dev/null +++ b/cpp/ql/src/Metrics/Classes/CHalsteadVolume.ql @@ -0,0 +1,13 @@ +/** + * @name Halstead volume + * @description The information contents of the program + * @kind treemap + * @id cpp/halstead-volume-per-class + * @treemap.warnOn highValues + * @metricType reftype + * @metricAggregate avg sum max + */ +import cpp + +from MetricClass mc +select mc, mc.getHalsteadVolume() diff --git a/cpp/ql/src/Metrics/Classes/CInheritanceDepth.qhelp b/cpp/ql/src/Metrics/Classes/CInheritanceDepth.qhelp new file mode 100644 index 000000000000..25480749bba6 --- /dev/null +++ b/cpp/ql/src/Metrics/Classes/CInheritanceDepth.qhelp @@ -0,0 +1,66 @@ + + + +

    +This metric measures the inheritance depth of a class. +

    + +

    +Whilst inheritance provides an avenue for code reuse, overly-deep class +hierarchies can become a maintenance headache. Classes that inherit from +many other classes can be brittle and hard to understand, because they +depend on all of the classes further up the hierarchy. Conversely, changes +to classes nearer the root of the hierarchy become harder and harder +to make without breaking the descendants. In extreme cases, where the +design of the hierarchy is seriously inappropriate, the class at the top of +the hierarchy can become a 'blob' class: a storage point for anything that +might be needed by one of its descendants. This is a key indicator that some +serious refactoring is needed. +

    + +
    + + +

    +As with many metrics, a high inheritance depth should be seen as an indicator +that something is amiss, but further investigation will be needed to clarify +the cause of the problem. Here are two possibilities: +

    + +
      + +
    • +A class and its superclass represent fundamentally the same abstraction. +In this case, they should generally be merged together (see the 'Collapse +Hierarchy' refactoring on pp.279-80 of [Fowler]). +
    • + +
    • +The class hierarchy is trying to represent variation in more than one +dimension using single inheritance. This can lead to an unnecessarily +deep class hierarchy with lots of code duplication. +
    + +

    +In languages that support it (such as C++), this situation can be modeled +somewhat more effectively using multiple inheritance, but an altogether better +approach is to use a component-based architecture (i.e. composition). +

    + + +
    + + + +
  • + M. Fowler. Refactoring. Addison-Wesley, 1999. +
  • +
  • + Wikipedia: Code refactoring +
  • + + +
    +
    diff --git a/cpp/ql/src/Metrics/Classes/CInheritanceDepth.ql b/cpp/ql/src/Metrics/Classes/CInheritanceDepth.ql new file mode 100644 index 000000000000..77e9414f9fd8 --- /dev/null +++ b/cpp/ql/src/Metrics/Classes/CInheritanceDepth.ql @@ -0,0 +1,16 @@ +/** + * @name Inheritance depth per class + * @description The depth of a class in the inheritance hierarchy. + * @kind treemap + * @id cpp/inheritance-depth-per-class + * @treemap.warnOn highValues + * @metricType reftype + * @metricAggregate avg max + * @tags modularity + */ +import cpp + +from Class c +where c.fromSource() +select c, c.getMetrics().getInheritanceDepth() as n +order by n desc diff --git a/cpp/ql/src/Metrics/Classes/CLackOfCohesionCK.qhelp b/cpp/ql/src/Metrics/Classes/CLackOfCohesionCK.qhelp new file mode 100644 index 000000000000..f283056d2a84 --- /dev/null +++ b/cpp/ql/src/Metrics/Classes/CLackOfCohesionCK.qhelp @@ -0,0 +1,77 @@ + + + +

    +This metric provides an indication of the lack of cohesion of a class, +using a method proposed by Chidamber and Kemerer in 1994. The idea +behind measuring a class's cohesion is that most funcions in well-designed +classes will access the same fields. Types that exhibit a lack of cohesion +are often trying to take on multiple responsibilities, and should be split +into several smaller classes. +

    + +

    +Various measures of lack of cohesion have been proposed: while the basic +intuition is simple, the precise way to measure this property has been the +subject of intense debate. Rather than getting involved in this debate, +more than one such lack of cohesion measure is provided for comparison purposes. +

    + +

    +The Chidamber and Kemerer version of lack of cohesion inspects pairs of +functions. If there are many pairs that access the same data, then the class +is cohesive. On the other hand, if there are many pairs that do not access +any common data, then the class is not cohesive. More precisely, if we let +n1 be the number of pairs of distinct functions in a class +that do not have at least one commonly-accessed field, and let +n2 be the number of pairs of distinct functions in a class +that do have at least one commonly-accessed field, then the lack of +cohesion measure LCOM can be defined as: +

    + +
    +LCOM = max((n1 - n2) / 2, 0)
    +
    + +

    +High values of LCOM indicate a worrisome lack of cohesion. The +precise value of the metric for which warnings are issued is configurable, +but as a rough indication, an LCOM of 500 or more +may give you cause for concern. +

    + +
    + + +

    +Classes generally lack cohesion because they are taking on more responsibilities +than they should (see [Martin] for more on responsibilities). In general, the +solution is to identify each of the different responsibilities the class is +taking on, and split them out into multiple classes, e.g. using the 'Extract +Class' refactoring from [Fowler]. +

    + + + +
    + + + +
  • +S. R. Chidamber and C. F. Kemerer. A metrics suite for object-oriented design. IEEE Transactions on Software Engineering, 20(6):476-493, 1994. +
  • +
  • +M. Fowler. Refactoring pp. 65, 122-5. Addison-Wesley, 1999. +
  • +
  • +R. Martin. The Single Responsibility Principle. Published online. +
  • +
  • +O. de Moor et al. Keynote Address: .QL for Source Code Analysis. Proceedings of the 7th IEEE International Working Conference on Source Code Analysis and Manipulation, 2007. +
  • + + +
    +
    diff --git a/cpp/ql/src/Metrics/Classes/CLackOfCohesionCK.ql b/cpp/ql/src/Metrics/Classes/CLackOfCohesionCK.ql new file mode 100644 index 000000000000..c6dbe2f762e8 --- /dev/null +++ b/cpp/ql/src/Metrics/Classes/CLackOfCohesionCK.ql @@ -0,0 +1,17 @@ +/** + * @name Lack of cohesion per class (LCOM-CK) + * @description Lack of cohesion for a class as defined by Chidamber + * and Kemerer. + * @kind treemap + * @id cpp/lack-of-cohesion-ck + * @treemap.warnOn highValues + * @metricType reftype + * @metricAggregate avg max + * @tags modularity + */ +import cpp + +from Class c +where c.fromSource() +select c, c.getMetrics().getLackOfCohesionCK() as n +order by n desc diff --git a/cpp/ql/src/Metrics/Classes/CLackOfCohesionHS.ql b/cpp/ql/src/Metrics/Classes/CLackOfCohesionHS.ql new file mode 100644 index 000000000000..93050e8ca10e --- /dev/null +++ b/cpp/ql/src/Metrics/Classes/CLackOfCohesionHS.ql @@ -0,0 +1,15 @@ +/** + * @name Lack of cohesion per class (LCOM-HS) + * @description Lack of cohesion for a class as defined by Henderson-Sellers. + * @kind treemap + * @id cpp/lack-of-cohesion-hs + * @treemap.warnOn highValues + * @metricType reftype + * @metricAggregate avg max + */ +import cpp + +from Class c +where c.fromSource() +select c, c.getMetrics().getLackOfCohesionHS() as n +order by n desc diff --git a/cpp/ql/src/Metrics/Classes/CLinesOfCode.qhelp b/cpp/ql/src/Metrics/Classes/CLinesOfCode.qhelp new file mode 100644 index 000000000000..01dca35c20ea --- /dev/null +++ b/cpp/ql/src/Metrics/Classes/CLinesOfCode.qhelp @@ -0,0 +1,51 @@ + + + +

    +This metric measures the number of lines of code for each class. +

    + +

    +Large classes can be problematic: +

    + +
      +
    • +They can be hard to understand and maintain, even with good tool support. +
    • + +
    • +They often arise as a result of bundling many unrelated things into the same +class, and so can be a symptom of weak class cohesion. +
    • +
    + +
    + + +

    +Classes are generally too large because they are taking on more responsibilities +than they should (see [Martin] for more on responsibilities). In general, the +solution is to identify each of the different responsibilities the class is +taking on, and split them out into multiple classes, e.g. using the 'Extract +Class' refactoring from [Fowler]. +

    + + + +
    + + + +
  • +M. Fowler. Refactoring pp. 65, 122-5. Addison-Wesley, 1999. +
  • +
  • +R. Martin. The Single Responsibility Principle. Published online. +
  • + + +
    +
    diff --git a/cpp/ql/src/Metrics/Classes/CLinesOfCode.ql b/cpp/ql/src/Metrics/Classes/CLinesOfCode.ql new file mode 100644 index 000000000000..f958732e4795 --- /dev/null +++ b/cpp/ql/src/Metrics/Classes/CLinesOfCode.ql @@ -0,0 +1,20 @@ +/** + * @name Lines of code per class + * @description The number of lines of code in a class. + * @kind treemap + * @id cpp/lines-of-code-per-class + * @treemap.warnOn highValues + * @metricType reftype + * @metricAggregate avg sum max + * @tags maintainability + */ +import cpp + +from Class c, int n +where c.fromSource() + and n = c.getMetrics().getNumberOfMembers() + + sum(Function f | + c.getACanonicalMemberFunction() = f | + f.getMetrics().getNumberOfLinesOfCode()) +select c, n +order by n desc diff --git a/cpp/ql/src/Metrics/Classes/CNumberOfFields.qhelp b/cpp/ql/src/Metrics/Classes/CNumberOfFields.qhelp new file mode 100644 index 000000000000..a4e09e92caf7 --- /dev/null +++ b/cpp/ql/src/Metrics/Classes/CNumberOfFields.qhelp @@ -0,0 +1,53 @@ + + + +

    +This metric measures the number of fields in classes below this +location in the tree. At a class level, it just measures the number of +fields in the class itself. +

    + +

    +There are at least a couple of reasons why a class may have too many fields: +

    + +
      +
    • The class in general may be too big / have too many responsibilities.
    • +
    • Several of the fields may be part of the same abstraction.
    • +
    + +
    + + +

    +The best resolution depends on the underlying reason behind it: +

    + +
      +
    • +If the class is too big, it should be split into multiple smaller classes. +
    • + +
    • +If several of the fields are part of the same abstraction, they should be +grouped into a separate class, using the 'Extract Class' refactoring described +in [Fowler]. +
    • +
    + +
    + + + +
  • +M. Fowler. Refactoring pp. 65, 122-5. Addison-Wesley, 1999. +
  • +
  • +R. Martin. The Single Responsibility Principle. Published online. +
  • + + +
    +
    diff --git a/cpp/ql/src/Metrics/Classes/CNumberOfFields.ql b/cpp/ql/src/Metrics/Classes/CNumberOfFields.ql new file mode 100644 index 000000000000..a83383975368 --- /dev/null +++ b/cpp/ql/src/Metrics/Classes/CNumberOfFields.ql @@ -0,0 +1,16 @@ +/** + * @name Number of fields per class + * @description The number of fields in a class. + * @kind treemap + * @id cpp/fields-per-type + * @treemap.warnOn highValues + * @metricType reftype + * @metricAggregate avg sum max + * @tags maintainability + */ +import cpp + +from Class c +where c.fromSource() +select c, c.getMetrics().getNumberOfFields() as n +order by n desc diff --git a/cpp/ql/src/Metrics/Classes/CNumberOfFunctions.qhelp b/cpp/ql/src/Metrics/Classes/CNumberOfFunctions.qhelp new file mode 100644 index 000000000000..cc62cb50f498 --- /dev/null +++ b/cpp/ql/src/Metrics/Classes/CNumberOfFunctions.qhelp @@ -0,0 +1,100 @@ + + + +

    +This metric measures the number of functions in classes below this +location in the tree. At a class level, it just measures the number of +functions in the class itself. +

    + +

    +A class with too many functions is generally trying to do too much, either at +the interface or implementation level or both. It can be difficult for readers +to understand because there is a confusing list of operations. Such +classes often lack cohesion and are prime candidates for refactoring. +

    + +
    + + +

    +Classes have too many functions for a variety of reasons, and the appropriate +fix is different in each case: +

    + +
      + +
    • +The class may be too large in general, and taking on more responsibilities +than it should (see [Martin] for more on responsibilities). In this case, +the solution is to identify each of the different responsibilities the class +is taking on, and split it into multiple classes, e.g. using the 'Extract +Class' refactoring from [Fowler]. +
    • + +
    • +There may be lots of duplication within the class itself, i.e. some of the +functions may be doing overlapping things. In this case, the solution is to +redesign the class so that each new function has a single, unique +responsibility. +
    • + +
    • +The class may be quite small at the implementation level, but trying to +provide its user with a wide range of 'utility' functions that don't actually +need to be part of the class. (A classic example of this is the +std::string class in the C++ Standard Library.) As [Sutter] +observes, there are at least two key problems with this approach: + + +
        +
      • +It may be possible to generalize some of the utility functions beyond the +narrow context of the class in question -- by bundling them with the class, +the class author reduces the scope for functionality reuse. +
      • + +
      • +It's usually impossible for the class author to know every possible +operation that the user might want to perform on the class, so the public +interface will inherently be incomplete. New utility functions will end up +having a different syntax to the privileged public functions in the class, +negatively impacting on code consistency. +
      • +
      + +To refactor a class like this, simply move its utility functions elsewhere, +paring its public interface down to the bare minimum. +
    • + +
    • +The class may be a base class in a badly-designed inheritance hierarchy. +Such classes have many public functions in order to support features that +are only used by one or two of their descendants. In this situation, the +solution is to redesign the hierarchy, possibly by refactoring it into a +component-based architecture (see [Microsoft Patterns & Practices Team]). +
    • + +
    + +
    + + + +
  • +M. Fowler. Refactoring pp. 65, 122-5. Addison-Wesley, 1999. +
  • +
  • +R. Martin. The Single Responsibility Principle. Published online. +
  • +
  • +H. Sutter. GotW #84: Monoliths "Unstrung". Published online, 2002. +
  • +
  • +Microsoft Patterns & Practices Team. Architectural Patterns and Styles Microsoft Application Architecture Guide, 2nd Edition. Microsoft Press, 2009. +
  • + +
    +
    diff --git a/cpp/ql/src/Metrics/Classes/CNumberOfFunctions.ql b/cpp/ql/src/Metrics/Classes/CNumberOfFunctions.ql new file mode 100644 index 000000000000..a027f058aa27 --- /dev/null +++ b/cpp/ql/src/Metrics/Classes/CNumberOfFunctions.ql @@ -0,0 +1,16 @@ +/** + * @name Number of functions per class + * @description The number of member functions in a class. + * @kind treemap + * @id cpp/number-of-functions-per-class + * @treemap.warnOn highValues + * @metricType reftype + * @metricAggregate avg sum max + * @tags maintainability + */ +import cpp + +from Class c +where c.fromSource() +select c, c.getMetrics().getNumberOfMemberFunctions() as n +order by n desc diff --git a/cpp/ql/src/Metrics/Classes/CNumberOfStatements.ql b/cpp/ql/src/Metrics/Classes/CNumberOfStatements.ql new file mode 100644 index 000000000000..73285348ad78 --- /dev/null +++ b/cpp/ql/src/Metrics/Classes/CNumberOfStatements.ql @@ -0,0 +1,17 @@ +/** + * @name Number of statements per class + * @description The number of statements in the member functions of a class. + * For template functions, only the statements in the template + * itself, not in the instantiations, are counted. + * @kind treemap + * @id cpp/statements-per-type + * @treemap.warnOn highValues + * @metricType reftype + * @metricAggregate avg sum max + */ +import cpp + +from Class c, int n +where c.fromSource() + and n = count(Stmt s | s.getEnclosingFunction() = c.getACanonicalMemberFunction()) +select c, n diff --git a/cpp/ql/src/Metrics/Classes/CPercentageOfComplexCode.qhelp b/cpp/ql/src/Metrics/Classes/CPercentageOfComplexCode.qhelp new file mode 100644 index 000000000000..176df0bbc151 --- /dev/null +++ b/cpp/ql/src/Metrics/Classes/CPercentageOfComplexCode.qhelp @@ -0,0 +1,51 @@ + + + +

    +This metric measures the percentage of the code within each class that is part +of a complex function (defined to be a function that has a high cyclomatic +complexity, i.e. there are a high number of linearly-independent execution +paths through the function). +

    + +

    +Functions with high cyclomatic complexity are typically difficult to understand +and test. Classes whose code is primarily contained within such tricky functions +are often strong candidates for refactoring. +

    + +
    + + +

    +Each of the individual functions whose cyclomatic complexity is too high should +be simplified, e.g. by tidying up complex logic and/or by splitting the function +into multiple smaller functions using the 'Extract Method' refactoring from +[Fowler]. If splitting the functions up results in a class with too many +functions, the refactoring should be followed up with another one to resolve the +new problem (as per the advice given for that situation). +

    + + + +
    + + + + + +
  • + M. Fowler. Refactoring. Addison-Wesley, 1999. +
  • +
  • + Wikipedia: Code refactoring +
  • +
  • + Refactoring as Meta Programming? +
  • + + +
    +
    diff --git a/cpp/ql/src/Metrics/Classes/CPercentageOfComplexCode.ql b/cpp/ql/src/Metrics/Classes/CPercentageOfComplexCode.ql new file mode 100644 index 000000000000..30d46951ef24 --- /dev/null +++ b/cpp/ql/src/Metrics/Classes/CPercentageOfComplexCode.ql @@ -0,0 +1,26 @@ +/** + * @name Percentage of complex code per class + * @description The percentage of the code in a class that is part of + * a complex member function. + * @kind treemap + * @id cpp/percentage-of-complex-code-per-class + * @treemap.warnOn highValues + * @metricType reftype + * @metricAggregate avg max + * @tags complexity + */ +import cpp + +from Class c, int ccLoc, int loc +where c.fromSource() + and ccLoc = sum(Function f | + c.getACanonicalMemberFunction() = f and + f.getMetrics().getCyclomaticComplexity() > 18 | + f.getMetrics().getNumberOfLinesOfCode()) + and loc = sum(Function f | + c.getACanonicalMemberFunction() = f | + f.getMetrics().getNumberOfLinesOfCode()) + + c.getMetrics().getNumberOfMembers() + and loc != 0 +select c, (ccLoc * 100).(float) / loc as n +order by n desc diff --git a/cpp/ql/src/Metrics/Classes/CResponse.qhelp b/cpp/ql/src/Metrics/Classes/CResponse.qhelp new file mode 100644 index 000000000000..26ba5d6321ec --- /dev/null +++ b/cpp/ql/src/Metrics/Classes/CResponse.qhelp @@ -0,0 +1,38 @@ + + + +

    +This metric measures the number of different functions that can be executed +by an object of that class in response to a message. +

    + +

    +Classes with a high response metric can be difficult to understand and test, +because you have to read through all the functions that can possibly be called +in order to fully understand what's going on. +

    + +
    + + +

    +Generally speaking, when a class has a high response metric, it is because it +contains functions that individually make large numbers of calls and/or +because it has high efferent coupling. The solution is therefore to fix these +underlying problems, and the class's response will decrease accordingly. +

    + + + +
    + + + +
  • +S. R. Chidamber and C. F. Kemerer. A metrics suite for object-oriented design. IEEE Transactions on Software Engineering, 20(6):476-493, 1994. +
  • + +
    +
    diff --git a/cpp/ql/src/Metrics/Classes/CResponse.ql b/cpp/ql/src/Metrics/Classes/CResponse.ql new file mode 100644 index 000000000000..1d3a8b13a3b5 --- /dev/null +++ b/cpp/ql/src/Metrics/Classes/CResponse.ql @@ -0,0 +1,17 @@ +/** + * @name Response per class + * @description The number of different member functions or + * constructors that can be executed by a class. + * @kind treemap + * @id cpp/response-per-class + * @treemap.warnOn highValues + * @metricType reftype + * @metricAggregate avg max + * @tags maintainability + * complexity + */ +import cpp + +from Class c +where c.fromSource() +select c, c.getMetrics().getResponse() diff --git a/cpp/ql/src/Metrics/Classes/CSizeOfAPI.qhelp b/cpp/ql/src/Metrics/Classes/CSizeOfAPI.qhelp new file mode 100644 index 000000000000..0d560f920aa6 --- /dev/null +++ b/cpp/ql/src/Metrics/Classes/CSizeOfAPI.qhelp @@ -0,0 +1,97 @@ + + + +

    +This metric measures the number of public functions for each public class. +

    + +

    +A class with too many public functions is generally trying to do too much, +either at the interface or implementation level or both. +It can be difficult for readers to understand because there is a +confusing list of operations. Such classes often lack cohesion and are prime candidates for refactoring. +

    + +
    + + +

    +Classes have wide interfaces for a variety of different reasons, and the +appropriate fix is different in each case: +

    + +
      + +
    • +The class may be too large in general, and taking on more responsibilities +than it should (see [Martin] for more on responsibilities). In this case, +the solution is to identify each of the different responsibilities the class +is taking on, and split it into multiple classes, e.g. using the 'Extract +Class' refactoring from [Fowler]. +
    • + +
    • +There may be lots of duplication within the class itself, that is, the actions of +some of the public functions may overlap. In this case, the solution +is to redesign the interface so that each new public function has a single, +unique responsibility. +
    • + +
    • +The class may be quite small at the implementation level, but trying to +provide its user with a wide range of 'utility' functions that don't actually +need to be part of the class. (A classic example of this is the +std::string class in the C++ Standard Library.) As [Sutter] +observes, there are at least two key problems with this approach: + +
        +
      • +It may be possible to generalize some of the utility functions beyond the +narrow context of the class in question -- by bundling them with the class, +the class author reduces the scope for functionality reuse. +
      • + +
      • +It's usually impossible for the class author to know every possible +operation that the user might want to perform on the class, so the public +interface will inherently be incomplete. New utility functions will end up +having a different syntax to the privileged public functions in the class, +negatively impacting on code consistency. +
      • +
      + +To refactor a class like this, simply move its utility functions elsewhere, +paring its public interface down to the bare minimum. +
    • + +
    • +The class may be a base class in a badly-designed inheritance hierarchy. +Such classes have many public functions to support features that +are only used by one or two of their descendants. In this situation, the +solution is to redesign the hierarchy, possibly by refactoring it into a +component-based architecture. +
    • + +
    + + + +
    + + + +
  • +M. Fowler. Refactoring pp. 65, 122-5. Addison-Wesley, 1999. +
  • +
  • +R. Martin. The Single Responsibility Principle. Published online. +
  • +
  • +H. Sutter. GotW #84: Monoliths "Unstrung". Published online, 2002. +
  • + + +
    +
    diff --git a/cpp/ql/src/Metrics/Classes/CSizeOfAPI.ql b/cpp/ql/src/Metrics/Classes/CSizeOfAPI.ql new file mode 100644 index 000000000000..353896af952d --- /dev/null +++ b/cpp/ql/src/Metrics/Classes/CSizeOfAPI.ql @@ -0,0 +1,17 @@ +/** + * @name Size of API per class + * @description The number of public member functions in a public class. + * @kind treemap + * @id cpp/size-of-api-per-class + * @treemap.warnOn highValues + * @metricType reftype + * @metricAggregate avg sum max + * @tags modularity + */ +import cpp + +from Class c, int n +where c.fromSource() + and n = count(Function f | c.getAPublicCanonicalMember() = f) +select c, n +order by n desc diff --git a/cpp/ql/src/Metrics/Classes/CSpecialisation.qhelp b/cpp/ql/src/Metrics/Classes/CSpecialisation.qhelp new file mode 100644 index 000000000000..f8744580f7a9 --- /dev/null +++ b/cpp/ql/src/Metrics/Classes/CSpecialisation.qhelp @@ -0,0 +1,48 @@ + + + +

    +This metric measures the extent to which a subclass overrides (replaces) the +behavior of its ancestor classes. It is computed as follows: we determine +the number of overridden functions in the subclass (not counting overrides of +abstract functions), multiply by its depth in the inheritance hierarchy and then +divide by its total number of functions. +

    + +

    +If a class overrides many of the functions of its ancestor classes, it is an +indication that the ancestor classes may not model sensible abstractions. +This is particularly true for subclasses that are lower down in the +inheritance hierarchy. In general, subclasses should add behavior to their +superclasses, rather than redefining the behavior that is already there. +

    + +
    + + +

    +The main reason that classes have a high specialization index is that +multiple subclasses specialize a common base class in exactly the same +way. In this case, the relevant function(s) should be pulled up into the base +class (see the 'Pull Up Method' refactoring in [Fowler]). +

    + +
    + + + +
  • +M. Fowler. Refactoring pp. 260-3. Addison-Wesley, 1999. +
  • +
  • +M. Lorenz and J. Kidd. Object-oriented Software Metrics. Prentice Hall, 1994. +
  • +
  • +O. de Moor et al. Keynote Address: .QL for Source Code Analysis. Proceedings of the 7th IEEE International Working Conference on Source Code Analysis and Manipulation, 2007. +
  • + + +
    +
    diff --git a/cpp/ql/src/Metrics/Classes/CSpecialisation.ql b/cpp/ql/src/Metrics/Classes/CSpecialisation.ql new file mode 100644 index 000000000000..5abaf44cd584 --- /dev/null +++ b/cpp/ql/src/Metrics/Classes/CSpecialisation.ql @@ -0,0 +1,16 @@ +/** + * @name Specialization per class + * @description The extent to which a subclass refines the behavior + * of its superclasses. + * @kind treemap + * @id cpp/specialisation-per-class + * @treemap.warnOn highValues + * @metricType reftype + * @metricAggregate avg max + * @tags modularity + */ +import cpp + +from Class c +where c.fromSource() +select c, c.getMetrics().getSpecialisationIndex() diff --git a/cpp/ql/src/Metrics/Dependencies/ExternalDependencies.ql b/cpp/ql/src/Metrics/Dependencies/ExternalDependencies.ql new file mode 100644 index 000000000000..b41cda73b927 --- /dev/null +++ b/cpp/ql/src/Metrics/Dependencies/ExternalDependencies.ql @@ -0,0 +1,18 @@ +/** + * @name External dependencies + * @description Count the number of dependencies a C/C++ source file has on external libraries. + * @kind treemap + * @treemap.warnOn highValues + * @metricType externalDependency + * @precision medium + * @id cpp/external-dependencies + */ + +import ExternalDependencies + +from File file, int num, string encodedDependency +where + encodedDependencies(file, encodedDependency, num) +select + encodedDependency, num +order by num desc diff --git a/cpp/ql/src/Metrics/Dependencies/ExternalDependencies.qll b/cpp/ql/src/Metrics/Dependencies/ExternalDependencies.qll new file mode 100644 index 000000000000..4d7d05fc9b2b --- /dev/null +++ b/cpp/ql/src/Metrics/Dependencies/ExternalDependencies.qll @@ -0,0 +1,110 @@ +/** + * Support for ExternalDependencies.ql query. + * + * This performs a "technology inventory" by associating each source file + * with the libraries it uses. + */ +import cpp +import semmle.code.cpp.commons.Dependency + +/** + * An `Element` that is to be considered a Library. + */ +abstract class LibraryElement extends Element { + abstract string getName(); + abstract string getVersion(); + abstract File getAFile(); +} + +/** + * Anything that is to be considered a library. + */ +private newtype LibraryT = + LibraryTElement(LibraryElement lib, string name, string version) { + lib.getName() = name and + lib.getVersion() = version + } or + LibraryTExternalPackage(@external_package ep, string name, string version) { + exists(string namespace, string package_name | + external_packages(ep, namespace, package_name, version) and + name = package_name + ) + } + +/** + * A library that can have dependencies on it. + */ +class Library extends LibraryT { + string name; + string version; + + Library() { + exists(LibraryElement lib | + this = LibraryTElement(lib, name, version) + ) or exists(@external_package ep | + this = LibraryTExternalPackage(ep, name, version) + ) + } + + string getName() { + result = name + } + + string getVersion() { + // The versions reported for C/C++ dependencies are just the versions that + // happen to be installed on the system where the build takes place. + // Reporting those versions is likely to cause misunderstandings, both for + // people reading them and for the vulnerability checker of lgtm. + result = "unknown" + } + + string toString() { result = getName() + "-" + getVersion() } + + File getAFile() { + exists(LibraryElement lib | + this = LibraryTElement(lib, _, _) and + result = lib.getAFile() + ) or exists(@external_package ep | + this = LibraryTExternalPackage(ep, _, _) and + header_to_external_package(result, ep) + ) + } +} + +/** + * Holds if there are `num` dependencies from `sourceFile` on `destLib` (and + * `sourceFile` is not in `destLib`). + */ +predicate libDependencies(File sourceFile, Library destLib, int num) { + num = strictcount(Element source, Element dest, File destFile | + // dependency from source -> dest. + dependsOnSimple(source, dest) and + sourceFile = source.getFile() and + destFile = dest.getFile() and + + // destFile is inside destLib, sourceFile is outside. + destFile = destLib.getAFile() and + not sourceFile = destLib.getAFile() and + + // don't include dependencies from template instantiations that + // may depend back on types in the using code. + not source.isFromTemplateInstantiation(_) and + + // exclude very common dependencies + not destLib.getName() = "linux" and + not destLib.getName().regexpMatch("gcc-[0-9]+") and + not destLib.getName() = "glibc" + ) +} + +/** + * Generate the table of dependencies for the query (with some + * packages that basically all projects depend on excluded). + */ +predicate encodedDependencies(File source, string encodedDependency, int num) +{ + exists(Library destLib | + libDependencies(source, destLib, num) and + encodedDependency = "/" + source.getRelativePath() + "<|>" + destLib.getName() + "<|>" + destLib.getVersion() + ) +} diff --git a/cpp/ql/src/Metrics/Dependencies/ExternalDependenciesSourceLinks.ql b/cpp/ql/src/Metrics/Dependencies/ExternalDependenciesSourceLinks.ql new file mode 100644 index 000000000000..a399d867222e --- /dev/null +++ b/cpp/ql/src/Metrics/Dependencies/ExternalDependenciesSourceLinks.ql @@ -0,0 +1,22 @@ +/** + * @name External dependency source links + * @kind source-link + * @metricType externalDependency + * @id cpp/dependency-source-links + */ + +import ExternalDependencies + +/* + * This query creates the source links for the ExternalDependencies.ql query. + * Although the entities in question are of the form '/file/path<|>dependency<|>version', + * the /file/path is a bare string relative to the root of the source archive, and not + * tied to a particular revision. We need the File entity (the second column here) to + * recover that information once we are in the dashboard database, using the + * ExternalEntity.getASourceLink() method. + */ +from File file, int num, string encodedDependency +where + encodedDependencies(file, encodedDependency, num) +select + encodedDependency, file diff --git a/cpp/ql/src/Metrics/External/FileCompilationDisplayStrings.ql b/cpp/ql/src/Metrics/External/FileCompilationDisplayStrings.ql new file mode 100644 index 000000000000..deed26111965 --- /dev/null +++ b/cpp/ql/src/Metrics/External/FileCompilationDisplayStrings.ql @@ -0,0 +1,11 @@ +/** + * @name Display strings for file compilations + * @kind display-string + * @id cpp/file-compilation-display-strings + * @metricType fileCompilation + */ +import cpp + +from Compilation c, int i +select c.toString() + ":" + i.toString(), c.toString() + ":" + i.toString() + ":" + c.getFileCompiled(i) + diff --git a/cpp/ql/src/Metrics/External/FileCompilationSourceLinks.ql b/cpp/ql/src/Metrics/External/FileCompilationSourceLinks.ql new file mode 100644 index 000000000000..e3e88296166e --- /dev/null +++ b/cpp/ql/src/Metrics/External/FileCompilationSourceLinks.ql @@ -0,0 +1,11 @@ +/** + * @name Source links for file compilations + * @kind source-link + * @id cpp/file-compilation-source-links + * @metricType fileCompilation + */ +import cpp + +from Compilation c, int i +select c.toString() + ":" + i.toString(), c.getFileCompiled(i) + diff --git a/cpp/ql/src/Metrics/Files/AutogeneratedLOC.ql b/cpp/ql/src/Metrics/Files/AutogeneratedLOC.ql new file mode 100644 index 000000000000..b8965e931926 --- /dev/null +++ b/cpp/ql/src/Metrics/Files/AutogeneratedLOC.ql @@ -0,0 +1,20 @@ +/** + * @name Autogenerated lines of code + * @kind treemap + * @id cpp/autogenerated-loc + * @description Measures the number of lines in autogenerated files that + * contain code (rather than lines that only contain + * comments or are blank). + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate sum + */ + +import cpp +import semmle.code.cpp.AutogeneratedFile + +from AutogeneratedFile af +where af.fromSource() +select af, af.getMetrics().getNumberOfLinesOfCode() as n +order by n desc + diff --git a/cpp/ql/src/Metrics/Files/ConditionalSegmentConditions.ql b/cpp/ql/src/Metrics/Files/ConditionalSegmentConditions.ql new file mode 100644 index 000000000000..6f1cb2402b62 --- /dev/null +++ b/cpp/ql/src/Metrics/Files/ConditionalSegmentConditions.ql @@ -0,0 +1,33 @@ +/** + * @name Number of distinct conditions used in #if, #ifdef, #ifndef etc. per file + * @description For each file, the number of unique conditions used by + * `#if`, `#ifdef`, and `#ifndef`. + * @kind treemap + * @id cpp/conditional-segment-conditions + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg max + */ +import cpp + +predicate preprocessorOpenCondition(PreprocessorDirective d) { + d instanceof PreprocessorIf or + d instanceof PreprocessorIfdef or + d instanceof PreprocessorIfndef +} + +predicate headerGuard(PreprocessorIfndef notdef) { + notdef.getHead().regexpMatch(".*_H_.*") + or + notdef.getHead().regexpMatch(".*_H") +} + +from File f +where f.fromSource() +select f, count(string s | + exists(PreprocessorDirective open | + preprocessorOpenCondition(open) + and not headerGuard(open) + and open.getFile() = f + and s = open.getHead())) + diff --git a/cpp/ql/src/Metrics/Files/ConditionalSegmentLines.ql b/cpp/ql/src/Metrics/Files/ConditionalSegmentLines.ql new file mode 100644 index 000000000000..b7dcab8758ce --- /dev/null +++ b/cpp/ql/src/Metrics/Files/ConditionalSegmentLines.ql @@ -0,0 +1,122 @@ +/** + * @name Number of conditionally compiled lines + * @description The number of lines that are subject to conditional + * compilation constraints defined using `#if`, `#ifdef`, + * and `#ifndef`. + * @kind treemap + * @id cpp/conditional-segment-lines + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg sum max + */ +import cpp +import semmle.code.cpp.headers.MultipleInclusion + +predicate preprocessorOpenCondition(PreprocessorDirective d, File f, int line) { + (d instanceof PreprocessorIf or + d instanceof PreprocessorIfdef or + d instanceof PreprocessorIfndef) + and + exists(Location l | + l = d.getLocation() | + f = l.getFile() and line = l.getStartLine()) +} + +predicate preprocessorCloseCondition(PreprocessorDirective d, File f, int line) { + d instanceof PreprocessorEndif + and + exists(Location l | + l = d.getLocation() | + f = l.getFile() and line = l.getStartLine()) +} + +private +predicate relevantLine(File f, int line) { + preprocessorOpenCondition(_, f, line) or + preprocessorCloseCondition(_, f, line) +} + +predicate relevantDirective(PreprocessorDirective d, File f, int line) { + preprocessorOpenCondition(d, f, line) or + preprocessorCloseCondition(d, f, line) +} + +private +predicate relevantLineWithRank(File f, int rnk, int line) { + line = rank[rnk](int l | relevantLine(f, l) | l) +} + +private +PreprocessorDirective next(PreprocessorDirective ppd) { + exists(File f, int line, int rnk, int nextLine | + relevantDirective(ppd, f, line) and + relevantLineWithRank(f, rnk, line) and + relevantLineWithRank(f, rnk + 1, nextLine) and + relevantDirective(result, f, nextLine) + ) +} + +private +int level(PreprocessorDirective ppd) { + (relevantDirective(ppd, _, _) + and not exists(PreprocessorDirective previous | ppd = next(previous)) + and result = 0) + or + exists(PreprocessorDirective previous | + ppd = next(previous) and + preprocessorOpenCondition(previous, _, _) and + result = level(previous) + 1) + or + exists(PreprocessorDirective previous | + ppd = next(previous) and + preprocessorCloseCondition(previous, _, _) and + result = level(previous) - 1) +} + +private +predicate openWithDepth(int depth, File f, PreprocessorDirective open, int line) { + preprocessorOpenCondition(open, f, line) and + depth = level(open) and + depth < 2 // beyond 2, we don't care about the macros anymore +} +private +predicate closeWithDepth(int depth, File f, PreprocessorDirective close, int line) { + preprocessorCloseCondition(close, f, line) and + depth = level(close) - 1 and + depth < 2 // beyond 2, we don't care about the macros anymore +} + +predicate length(PreprocessorDirective open, int length) { + exists(int depth, File f, int start, int end | + openWithDepth(depth, f, open, start) and + end = min(PreprocessorDirective endif, int closeLine | + closeWithDepth(depth, f, endif, closeLine) and + closeLine > start| + closeLine) and + length = end - start - 1 + ) +} + +predicate headerGuard(PreprocessorDirective notdef, File f) { + exists(CorrectIncludeGuard g | + notdef = g.getIfndef() and f = notdef.getFile()) +} + +predicate headerGuardChild(PreprocessorDirective open) { + exists(File f, PreprocessorDirective headerGuard | + headerGuard(headerGuard, f) and + openWithDepth(1, f, open, _)) +} + +predicate topLevelOpen(PreprocessorDirective open) { + (openWithDepth(0, _, open, _) and not headerGuard(open,_)) + or + headerGuardChild(open) +} + +from File f +where f.fromSource() +select f, sum(PreprocessorDirective open, int length | + open.getFile() = f and + topLevelOpen(open) and length(open, length) | + length) diff --git a/cpp/ql/src/Metrics/Files/FAfferentCoupling.qhelp b/cpp/ql/src/Metrics/Files/FAfferentCoupling.qhelp new file mode 100644 index 000000000000..cbffbcbfb546 --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FAfferentCoupling.qhelp @@ -0,0 +1,55 @@ + + + +

    This metric measures, for each file, how many other files depend on it.

    + +

    A file depends on another file if:

    +
      +
    • It calls a function in that file
    • +
    • It reads or writes a variable declared in that file
    • +
    • It uses a type declared in that file
    • +
    + +

    Many incoming dependencies can be good or bad, depending on the nature of the file.

    + +

    A large number of incoming dependencies is good for:

    +
      +
    • files containing logging functions
    • +
    • files containing system-wide utility functions
    • +
    + +

    A large number of incoming dependencies may be a problem for:

    +
      +
    • files that should be internal to a particular component
    • +
    • files containing functions or classes with unstable interfaces
    • +
    + +

    A file with many dependencies on it is risky to change, since large parts of a system +may be affected. Such files should be well documented and have clean APIs.

    + +
    + + +

    Group widely used utility functions together. Replace calls to a component's internals +with uses of its public API (augmenting it if necessary).

    + +

    Ensure that the public API is well-documented.

    + +
    + + +
  • + M. Fowler. Refactoring. Addison-Wesley, 1999. +
  • +
  • + Wikipedia: Code refactoring +
  • +
  • + Refactoring as Meta Programming? +
  • + + +
    +
    diff --git a/cpp/ql/src/Metrics/Files/FAfferentCoupling.ql b/cpp/ql/src/Metrics/Files/FAfferentCoupling.ql new file mode 100644 index 000000000000..0aab056038e0 --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FAfferentCoupling.ql @@ -0,0 +1,17 @@ +/** + * @name Incoming dependencies per file + * @description The number of files that depend on a file. + * @kind treemap + * @id cpp/afferent-coupling-per-file + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg max + * @tags maintainability + * modularity + */ +import cpp + +from File f +where f.fromSource() +select f, f.getMetrics().getAfferentCoupling() as n +order by n desc diff --git a/cpp/ql/src/Metrics/Files/FCommentRatio.qhelp b/cpp/ql/src/Metrics/Files/FCommentRatio.qhelp new file mode 100644 index 000000000000..4a5c928f5cd4 --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FCommentRatio.qhelp @@ -0,0 +1,29 @@ + + + +

    This metric measures the percentage of lines in a file that contain a comment or are part of a multi-line comment.

    + +

    Having a low percentage of comments is an indication that a file does not have sufficient +documentation. Undocumented code is hard to understand, modify, and reuse.

    + +
    + + +

    Add documentation to files with a low percentage of comments. It is most useful to start +documenting the public functions first.

    + +
    + + +
  • + C++ Programming, Coding style conventions +
  • +
  • + Wikipedia: Need for comments +
  • + + +
    +
    diff --git a/cpp/ql/src/Metrics/Files/FCommentRatio.ql b/cpp/ql/src/Metrics/Files/FCommentRatio.ql new file mode 100644 index 000000000000..245404ca1a95 --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FCommentRatio.ql @@ -0,0 +1,17 @@ +/** + * @name Percentage of comments + * @description The percentage of lines that contain comments. + * @kind treemap + * @id cpp/comment-ratio-per-file + * @treemap.warnOn lowValues + * @metricType file + * @metricAggregate avg max + * @tags maintainability + * documentation + */ +import cpp + +from File f, int comments, int total +where f.fromSource() and numlines(f, total, _, comments) and total > 0 +select f, 100.0 * (comments.(float) / total.(float)) as ratio +order by ratio desc diff --git a/cpp/ql/src/Metrics/Files/FCyclomaticComplexity.cpp b/cpp/ql/src/Metrics/Files/FCyclomaticComplexity.cpp new file mode 100644 index 000000000000..5a09f4d78c60 --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FCyclomaticComplexity.cpp @@ -0,0 +1,21 @@ +int f(int i, int j) { + // start + int result; + if(i % 2 == 0) { + // iEven + result = i + j; + } + else { + // iOdd + if(j % 2 == 0) { + // jEven + result = i * j; + } + else { + // jOdd + result = i - j; + } + } + return result; + // end +} \ No newline at end of file diff --git a/cpp/ql/src/Metrics/Files/FCyclomaticComplexity.qhelp b/cpp/ql/src/Metrics/Files/FCyclomaticComplexity.qhelp new file mode 100644 index 000000000000..be463857db9a --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FCyclomaticComplexity.qhelp @@ -0,0 +1,77 @@ + + + + +

    +This metric measures the average cyclomatic complexity of the functions in a file. +

    + +

    +The cyclomatic complexity of a function is the number of linearly independent execution paths +through that function. A path is linearly independent path if it differs from all other paths +by at least one node. Straight-line code therefore has a cyclomatic complexity of one, while +branches, switches and loops increase cyclomatic complexity. +

    + +

    +Functions with a high cyclomatic complexity are typically hard to understand and test. By extension, +files whose functions have a high average cyclomatic complexity are problematic, and usually would +benefit from refactoring. +

    + +

    +As a concrete example, consider the following function: +

    + + + +

    +The control flow graph for this function is as follows: +

    + +Control Flow Graph + +

    +The graph shows that the number of linearly independent execution +paths through the function, and hence its cyclomatic complexity, is +3. The three paths are: +

    + +
      +
    • start -> iEven -> end
    • +
    • start -> iOdd -> jEven -> end
    • +
    • start -> iOdd -> jOdd -> end
    • +
    + +
    + + +

    +Functions with a high cyclomatic complexity should be simplified, for instance by +tidying up any complex logic within them or by splitting them into multiple methods +using the Extract Method refactoring. +

    + +
    + + +
  • +M. Fowler, Refactoring. Addison-Wesley, 1999. +
  • +
  • +T. J. McCabe, A Complexity Measure. IEEE Transactions on Software Engineering, SE-2(4), December 1976. +
  • +
  • +Dave Thomas, Refactoring as Meta Programming?, in Journal of Object Technology, vol. 4, no. 1, January-February 2005, pp. 7-11. +
  • +
  • +Wikipedia: Cyclomatic complexity +
  • +
  • +Wikipedia: Code refactoring +
  • + +
    +
    \ No newline at end of file diff --git a/cpp/ql/src/Metrics/Files/FCyclomaticComplexity.ql b/cpp/ql/src/Metrics/Files/FCyclomaticComplexity.ql new file mode 100644 index 000000000000..f07d0b6eba30 --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FCyclomaticComplexity.ql @@ -0,0 +1,29 @@ +/** + * @name Average cyclomatic complexity of files + * @description The average cyclomatic complexity of the functions in a file. + * @kind treemap + * @id cpp/average-cyclomatic-complexity-per-file + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg max + * @tags testability + * complexity + */ +import cpp + +from File f, float complexity, float loc +where f.fromSource() + and loc = sum(FunctionDeclarationEntry fde | + fde.getFile() = f | + fde.getNumberOfLines()).(float) + and if loc > 0 then + // Weighted average of complexity by function length + complexity = + sum(FunctionDeclarationEntry fde | + fde.getFile() = f | + fde.getNumberOfLines() * fde.getCyclomaticComplexity()).(float) + / + loc + else + complexity = 0 +select f, complexity diff --git a/cpp/ql/src/Metrics/Files/FCyclomaticComplexity_ControlFlow.gv b/cpp/ql/src/Metrics/Files/FCyclomaticComplexity_ControlFlow.gv new file mode 100644 index 000000000000..dfccd0640803 --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FCyclomaticComplexity_ControlFlow.gv @@ -0,0 +1,11 @@ +digraph +{ + start -> iEven; + start -> iOdd; + iEven -> end; + iOdd -> jEven; + iOdd -> jOdd; + jEven -> end; + jOdd -> end; +} + diff --git a/cpp/ql/src/Metrics/Files/FCyclomaticComplexity_ControlFlow.png b/cpp/ql/src/Metrics/Files/FCyclomaticComplexity_ControlFlow.png new file mode 100644 index 000000000000..f5c409f8bff9 Binary files /dev/null and b/cpp/ql/src/Metrics/Files/FCyclomaticComplexity_ControlFlow.png differ diff --git a/cpp/ql/src/Metrics/Files/FDirectIncludes.qhelp b/cpp/ql/src/Metrics/Files/FDirectIncludes.qhelp new file mode 100644 index 000000000000..2bab2e5130c0 --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FDirectIncludes.qhelp @@ -0,0 +1,35 @@ + + + +

    This metric measures the number of files that are directly included using #include.

    + +

    Files with lots of #include directives often either have too many +responsibilities or not all of the directives are needed. In the former case, the +file may be hard to comprehend and maintain; the latter causes build-times to be +unnecessarily long.

    + +
    + + + +

    Split and/or refactor files with too many responsibilities, and remove any unnecessary +#include directives.

    + +
    + + +
  • + M. Fowler. Refactoring. Addison-Wesley, 1999. +
  • +
  • + Wikipedia: Code refactoring +
  • +
  • + Refactoring as Meta Programming? +
  • + + +
    +
    diff --git a/cpp/ql/src/Metrics/Files/FDirectIncludes.ql b/cpp/ql/src/Metrics/Files/FDirectIncludes.ql new file mode 100644 index 000000000000..e06969091df4 --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FDirectIncludes.ql @@ -0,0 +1,16 @@ +/** + * @name Includes per file + * @description The number of files directly included by this file using + * `#include`. + * @kind treemap + * @id cpp/direct-includes-per-file + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg max + */ +import cpp + +from File f, int n +where f.fromSource() + and n = count(Include i | i.getFile() = f) +select f, n diff --git a/cpp/ql/src/Metrics/Files/FEfferentCoupling.qhelp b/cpp/ql/src/Metrics/Files/FEfferentCoupling.qhelp new file mode 100644 index 000000000000..013defa9f8bf --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FEfferentCoupling.qhelp @@ -0,0 +1,39 @@ + + + +

    This metric measures the number of files that a particular file depends on. A file depends on another file if:

    +
      +
    • It calls a function in that file
    • +
    • It reads or writes a variable declared in that file
    • +
    • It uses a type declared in that file
    • +
    + +

    The number of other files that a source-file depends on is a way of judging +how cohesive it is. A source-file with a single well-defined purpose is likely +to have fewer dependencies than a source-file where functions have been "thrown in" +over time. The former is desirable, because files with a single well-defined +purpose are more easily comprehended and maintained.

    + +
    + + +

    Consider, breaking the file up into smaller ones. This could either be done by splitting up unrelated functionality, or extracting common operations into a library.

    + +
    + + +
  • + M. Fowler. Refactoring. Addison-Wesley, 1999. +
  • +
  • + Wikipedia: Code refactoring +
  • +
  • + Refactoring as Meta Programming? +
  • + + +
    +
    diff --git a/cpp/ql/src/Metrics/Files/FEfferentCoupling.ql b/cpp/ql/src/Metrics/Files/FEfferentCoupling.ql new file mode 100644 index 000000000000..3e2116ac8776 --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FEfferentCoupling.ql @@ -0,0 +1,18 @@ +/** + * @name Outgoing dependencies per file + * @description The number of files that a file depends on. + * @kind treemap + * @id cpp/efferent-coupling-per-file + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg max + * @tags testability + * modularity + * maintainability + */ +import cpp + +from File f +where f.fromSource() +select f, f.getMetrics().getEfferentCoupling() as n +order by n desc diff --git a/cpp/ql/src/Metrics/Files/FHalsteadBugs.ql b/cpp/ql/src/Metrics/Files/FHalsteadBugs.ql new file mode 100644 index 000000000000..7b6f1d54db83 --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FHalsteadBugs.ql @@ -0,0 +1,15 @@ +/** + * @name Halstead bug measure + * @description Measures the expected number of delivered bugs. The + * Halstead bug count is known to be an underestimate. + * @kind treemap + * @id cpp/halstead-bugs-per-file + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg sum max + */ +import cpp + +from MetricFile mc +where mc.fromSource() +select mc, mc.getHalsteadDeliveredBugs() diff --git a/cpp/ql/src/Metrics/Files/FHalsteadDifficulty.ql b/cpp/ql/src/Metrics/Files/FHalsteadDifficulty.ql new file mode 100644 index 000000000000..f0d6684940c5 --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FHalsteadDifficulty.ql @@ -0,0 +1,14 @@ +/** + * @name Halstead difficulty + * @description Measures the error proneness of implementing the program. + * @kind treemap + * @id cpp/halstead-difficulty-per-file + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg sum max + */ +import cpp + +from MetricFile mc +where mc.fromSource() +select mc, mc.getHalsteadDifficulty() diff --git a/cpp/ql/src/Metrics/Files/FHalsteadEffort.ql b/cpp/ql/src/Metrics/Files/FHalsteadEffort.ql new file mode 100644 index 000000000000..52a090cd03c5 --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FHalsteadEffort.ql @@ -0,0 +1,14 @@ +/** + * @name Halstead effort + * @description Measures the effort to implement the program. + * @kind treemap + * @id cpp/halstead-effort-per-file + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg sum max + */ +import cpp + +from MetricFile mc +where mc.fromSource() +select mc, mc.getHalsteadEffort() diff --git a/cpp/ql/src/Metrics/Files/FHalsteadLength.ql b/cpp/ql/src/Metrics/Files/FHalsteadLength.ql new file mode 100644 index 000000000000..f90bbbec6a0c --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FHalsteadLength.ql @@ -0,0 +1,14 @@ +/** + * @name Halstead length + * @description Total number of operands and operators + * @kind treemap + * @id cpp/halstead-length-per-file + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg sum max + */ +import cpp + +from MetricFile mc +where mc.fromSource() +select mc, mc.getHalsteadLength() diff --git a/cpp/ql/src/Metrics/Files/FHalsteadVocabulary.ql b/cpp/ql/src/Metrics/Files/FHalsteadVocabulary.ql new file mode 100644 index 000000000000..c933aee0cd8e --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FHalsteadVocabulary.ql @@ -0,0 +1,14 @@ +/** + * @name Halstead vocabulary + * @description Number of distinct operands and operators used. + * @kind treemap + * @id cpp/halstead-vocabulary-per-file + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg sum max + */ +import cpp + +from MetricFile mc +where mc.fromSource() +select mc, mc.getHalsteadVocabulary() diff --git a/cpp/ql/src/Metrics/Files/FHalsteadVolume.ql b/cpp/ql/src/Metrics/Files/FHalsteadVolume.ql new file mode 100644 index 000000000000..78528d43dbc2 --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FHalsteadVolume.ql @@ -0,0 +1,14 @@ +/** + * @name Halstead volume + * @description The information contents of the program. + * @kind treemap + * @id cpp/halstead-volume-per-file + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg sum max + */ +import cpp + +from MetricFile mc +where mc.fromSource() +select mc, mc.getHalsteadVolume() diff --git a/cpp/ql/src/Metrics/Files/FLines.ql b/cpp/ql/src/Metrics/Files/FLines.ql new file mode 100644 index 000000000000..c582a8cf1e0a --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FLines.ql @@ -0,0 +1,15 @@ +/** + * @name Number of lines + * @description The number of lines in each file. + * @kind treemap + * @id cpp/lines-per-file + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg sum max + */ +import cpp + +from File f +where f.fromSource() +select f, f.getMetrics().getNumberOfLines() as n +order by n desc diff --git a/cpp/ql/src/Metrics/Files/FLinesOfCode.qhelp b/cpp/ql/src/Metrics/Files/FLinesOfCode.qhelp new file mode 100644 index 000000000000..4eb6c1c5b009 --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FLinesOfCode.qhelp @@ -0,0 +1,30 @@ + + + +

    This metric measures the number of lines of code in a file. This excludes comments and blank lines.

    + +

    Having too many lines of code in a file is an indication that it can be split into several files of more manageable size. Generated code is a notable exception to this.

    + +
    + + +

    Long files should be examined to see if they can be split into smaller, more cohesive files.

    + +
    + + +
  • + M. Fowler. Refactoring. Addison-Wesley, 1999. +
  • +
  • + Wikipedia: Code refactoring +
  • +
  • + Refactoring as Meta Programming? +
  • + + +
    +
    diff --git a/cpp/ql/src/Metrics/Files/FLinesOfCode.ql b/cpp/ql/src/Metrics/Files/FLinesOfCode.ql new file mode 100644 index 000000000000..33d1bfd6b2b6 --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FLinesOfCode.ql @@ -0,0 +1,20 @@ +/** + * @name Lines of code in files + * @kind treemap + * @description Measures the number of lines in a file that contain + * code (rather than lines that only contain comments + * or are blank) + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg sum max + * @precision very-high + * @id cpp/lines-of-code-in-files + * @tags maintainability + * complexity + */ +import cpp + +from File f +where f.fromSource() +select f, f.getMetrics().getNumberOfLinesOfCode() as n +order by n desc diff --git a/cpp/ql/src/Metrics/Files/FLinesOfCommentedOutCode.qhelp b/cpp/ql/src/Metrics/Files/FLinesOfCommentedOutCode.qhelp new file mode 100644 index 000000000000..d435289b652c --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FLinesOfCommentedOutCode.qhelp @@ -0,0 +1,7 @@ + + + + + diff --git a/cpp/ql/src/Metrics/Files/FLinesOfCommentedOutCode.ql b/cpp/ql/src/Metrics/Files/FLinesOfCommentedOutCode.ql new file mode 100644 index 000000000000..6ad0e469a8e5 --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FLinesOfCommentedOutCode.ql @@ -0,0 +1,20 @@ +/** + * @name Lines of commented-out code in files + * @description The number of lines of commented-out code in a file. + * @kind treemap + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg sum max + * @precision high + * @id cpp/lines-of-commented-out-code-in-files + * @tags documentation + */ + +import Documentation.CommentedOutCode + +from File f, int n +where n = sum(CommentedOutCode comment | + comment.getFile() = f | + comment.numCodeLines()) +select f, n +order by n desc diff --git a/cpp/ql/src/Metrics/Files/FLinesOfComments.qhelp b/cpp/ql/src/Metrics/Files/FLinesOfComments.qhelp new file mode 100644 index 000000000000..d1b1d9849181 --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FLinesOfComments.qhelp @@ -0,0 +1,29 @@ + + + +

    This metric measures the number of lines of comments in a file.

    + +

    Files that have too few comments are more likely to be insufficiently documented. +Long files that have no comments at all require particular scrutiny, as these are more likely +to need explicit documentation in comments.

    + +
    + +

    Files with few to no comments should be examined to see if they require documentation. Particular attention +should be given to long files.

    + +
    + + +
  • + C++ Programming, Coding style conventions +
  • +
  • + Wikipedia: Need for comments +
  • + + +
    +
    diff --git a/cpp/ql/src/Metrics/Files/FLinesOfComments.ql b/cpp/ql/src/Metrics/Files/FLinesOfComments.ql new file mode 100644 index 000000000000..05f8ccf9e5b8 --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FLinesOfComments.ql @@ -0,0 +1,20 @@ +/** + * @name Lines of comments in files + * @description Measures the number of lines which contain a comment + * or part of a comment (that is, which are part of a + * multi-line comment). + * @kind treemap + * @treemap.warnOn lowValues + * @metricType file + * @metricAggregate avg sum max + * @precision very-high + * @id cpp/lines-of-comments-in-files + * @tags maintainability + * documentation + */ +import cpp + +from File f +where f.fromSource() +select f, f.getMetrics().getNumberOfLinesOfComments() as n +order by n desc diff --git a/cpp/ql/src/Metrics/Files/FLinesOfDuplicatedCode.qhelp b/cpp/ql/src/Metrics/Files/FLinesOfDuplicatedCode.qhelp new file mode 100644 index 000000000000..30a98df0ceeb --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FLinesOfDuplicatedCode.qhelp @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/cpp/ql/src/Metrics/Files/FLinesOfDuplicatedCode.ql b/cpp/ql/src/Metrics/Files/FLinesOfDuplicatedCode.ql new file mode 100644 index 000000000000..e905793c948a --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FLinesOfDuplicatedCode.ql @@ -0,0 +1,23 @@ +/** + * @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. + * @kind treemap + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg sum max + * @precision high + * @tags testability + * modularity + * @id cpp/duplicated-lines-in-files + */ +import external.CodeDuplication + +from File f, int n +where n = count(int line | + exists(DuplicateBlock d | d.sourceFile() = f | + line in [d.sourceStartLine()..d.sourceEndLine()]) + and not whitelistedLineForDuplication(f, line)) +select f, n +order by n desc diff --git a/cpp/ql/src/Metrics/Files/FMacroRatio.cpp b/cpp/ql/src/Metrics/Files/FMacroRatio.cpp new file mode 100644 index 000000000000..abde7d39a20a --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FMacroRatio.cpp @@ -0,0 +1,10 @@ +/* Example of good macros */ +#define TCP_NODELAY 1 +#define TCP_MAXSEG 2 +#define TCP_CORK 3 + +/* + * In contrast, functions that use this macro are hard to read without + * knowing its exact definition + */ +#define JSKW_TEST_GUESS(index) i = (index); goto test_guess; diff --git a/cpp/ql/src/Metrics/Files/FMacroRatio.qhelp b/cpp/ql/src/Metrics/Files/FMacroRatio.qhelp new file mode 100644 index 000000000000..805c53e9a74a --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FMacroRatio.qhelp @@ -0,0 +1,42 @@ + + + +

    This metric measures the percentage of lines of code in each file that use macros.

    + +

    Macros are useful for defining constants in header files to avoid having magic constants scattered +throughout a project. However, files with a high percentage of lines that use macros are likely to be +using macros to encapsulate functionality. Such use can lead to code that is hard to read and understand, +because standard assumptions about the control-flow of a function, and how many times expressions are +evaluated, may no longer hold.

    + +

    With regards to performance, modern compilers usually perform aggressive inlining and the difference +between a macro invocation and a function call is negligible. Functions have much better error checking +and IDE support than macros.

    + + + +
    + + + +

    Try to limit the use of macros to constants. Complicated uses of macros can often be replaced by functions.

    + +
    + + +
  • + Macros +
  • +
  • + Rejuvenating C++ Programs through +Demacrofication +
  • +
  • + Outgrowing Macrophobia +
  • + + +
    +
    diff --git a/cpp/ql/src/Metrics/Files/FMacroRatio.ql b/cpp/ql/src/Metrics/Files/FMacroRatio.ql new file mode 100644 index 000000000000..81082253221d --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FMacroRatio.ql @@ -0,0 +1,27 @@ +/** + * @name Usage of macros + * @description The percentage of source lines in each file that contain + * use of macros. + * @kind treemap + * @id cpp/macro-ratio-per-file + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg max + */ +import cpp + +predicate macroLines(File f, int line) { + exists(MacroInvocation mi | + mi.getFile() = f and + mi.getLocation().getStartLine() = line) +} + +predicate macroLineCount(File f, int num) { + num = count(int line | macroLines(f, line)) +} + +from MetricFile f, int macroLines, int loc +where f.fromSource() and loc = f.getNumberOfLinesOfCode() and loc > 0 and + macroLineCount(f, macroLines) +select f, 100.0 * (macroLines.(float) / loc.(float)) as ratio +order by ratio desc diff --git a/cpp/ql/src/Metrics/Files/FNumberOfClasses.ql b/cpp/ql/src/Metrics/Files/FNumberOfClasses.ql new file mode 100644 index 000000000000..ff39473ea62c --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FNumberOfClasses.ql @@ -0,0 +1,16 @@ +/** + * @name Number of classes per file + * @kind treemap + * @id cpp/number-of-classes-per-file + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg sum max + * @tags maintainability + */ +import cpp + +from File f, int n +where f.fromSource() + and n = count(Class c | c.getAFile() = f) +select f, n +order by n desc diff --git a/cpp/ql/src/Metrics/Files/FNumberOfTests.qhelp b/cpp/ql/src/Metrics/Files/FNumberOfTests.qhelp new file mode 100644 index 000000000000..d016e3c0e1fa --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FNumberOfTests.qhelp @@ -0,0 +1,54 @@ + + + +

    + This metric measures the number of tests in this file. +

    + +

    + Tests are recognised if they are implemented with one of the major + unit testing frameworks. Currently the Boost and CppUnit frameworks + are recognised. +

    + +

    + In general, having many test cases is a good thing rather than a bad + thing. However, at the file level, tests should typically be grouped + by the functionality they relate to, which makes a file with an + exceptionally high number of tests a strong candidate for splitting + up. At a higher level, this metric makes it possible to compare the + number of tests in different components, potentially flagging + functionality that is comparatively under-tested. +

    +
    + +

    + Since it is typically not a problem to have too many tests, this + metric is usually included for the purposes of collecting + information, rather than finding problematic areas in the code. With + that in mind, it is usually a good idea to avoid an excessive number + of tests in a single file, and to maintain a broadly comparable + level of testing across components. +

    + +

    + When assessing the thoroughness of a code base's test suite, the number + of test methods provides only part of the story. Test coverage + statistics allow a more detailed examination of which parts of the + code deserve improvements in this area. +

    +
    + + +
  • + Boost: official website at + http://www.boost.org/. +
  • +
  • + CppUnit: official website at + https://freedesktop.org/wiki/Software/cppunit/. +
  • +
    +
    diff --git a/cpp/ql/src/Metrics/Files/FNumberOfTests.ql b/cpp/ql/src/Metrics/Files/FNumberOfTests.ql new file mode 100644 index 000000000000..f5e45897970c --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FNumberOfTests.ql @@ -0,0 +1,28 @@ +/** + * @name Number of tests + * @description The number of test methods defined in a file. + * @kind treemap + * @treemap.warnOn lowValues + * @metricType file + * @metricAggregate avg sum max + * @precision medium + * @id cpp/tests-in-files + * @tags maintainability + */ +import cpp + +Expr getTest() { + // cppunit tests; https://freedesktop.org/wiki/Software/cppunit/ + exists(Function f | result.(FunctionCall).getTarget() = f + and f.getNamespace().getName() = "CppUnit" + and f.getName() = "addTest") + or + // boost tests; http://www.boost.org/ + exists(Function f | result.(FunctionCall).getTarget() = f + and f.getQualifiedName() = "boost::unit_test::make_test_case") +} + +from File f, int n +where n = strictcount(Expr e | e = getTest() and e.getFile() = f) +select f, n +order by n desc diff --git a/cpp/ql/src/Metrics/Files/FTimeInFrontend.qhelp b/cpp/ql/src/Metrics/Files/FTimeInFrontend.qhelp new file mode 100644 index 000000000000..8a01952f3756 --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FTimeInFrontend.qhelp @@ -0,0 +1,29 @@ + + + +

    This metric measures the amount of time (in milliseconds) spent compiling a C/C++ file, including time spent processing all files included by the pre-processor.

    + +

    Files that take too long to build usually have too many includes.

    + +
    + +

    Files that take a long time to build should be checked to see if they are including only the necessary files in order to reduce build time.

    + +
    + + +
  • + The #include Directive +
  • +
  • + Include operation +
  • +
  • + C++ Compilation Speed +
  • + + +
    +
    diff --git a/cpp/ql/src/Metrics/Files/FTimeInFrontend.ql b/cpp/ql/src/Metrics/Files/FTimeInFrontend.ql new file mode 100644 index 000000000000..720f0b278a69 --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FTimeInFrontend.ql @@ -0,0 +1,19 @@ +/** + * @name Compilation time + * @description Measures the amount of time (in milliseconds) spent + * compiling a C/C++ file, including time spent processing + * all files included by the pre-processor. + * @kind treemap + * @id cpp/time-in-frontend-per-file + * @metricType fileCompilation + * @metricAggregate avg sum max + */ +import cpp + +from string x, float t +where exists(Compilation c, int i | + x = c.toString() + ":" + i.toString() and + t = 1000 * c.getFrontendCpuSeconds(i) and + c.getFileCompiled(i).fromSource()) +select x, t +order by t desc diff --git a/cpp/ql/src/Metrics/Files/FTodoComments.qhelp b/cpp/ql/src/Metrics/Files/FTodoComments.qhelp new file mode 100644 index 000000000000..a0906beb799e --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FTodoComments.qhelp @@ -0,0 +1,34 @@ + + + +

    This metric measures the number of TODO or FIXME comments in the code.

    + +

    These comments tend to document points of ambiguity in the software's requirements. Often +they refer to corner-cases that are not handled or potential defects. It is therefore very +important to monitor such comments and, where appropriate, escalate comments to an +defect-tracking system or other means of ensuring that action is taken.

    + +
    + + +

    Remove unnecessary TODO/FIXME comments, and those that are no longer relevant. File tickets +for the remaining comments in a defect-tracker, or otherwise ensure that someone is responsible +for fixing them.

    + +
    + + +
  • + Wikipedia: Comment tags +
  • +
  • + TODO or not TODO +
  • +
  • + The case against TODO +
  • + +
    +
    diff --git a/cpp/ql/src/Metrics/Files/FTodoComments.ql b/cpp/ql/src/Metrics/Files/FTodoComments.ql new file mode 100644 index 000000000000..8265f6253164 --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FTodoComments.ql @@ -0,0 +1,21 @@ +/** + * @name Number of todo/fixme comments per file + * @description The number of TODO or FIXME comments in a file. + * @kind treemap + * @id cpp/todo-comments-per-file + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg sum max + * @tags maintainability + * documentation + */ +import cpp + +from File f, int n +where f.fromSource() + and n = count(Comment c | + c.getFile() = f and + (c.getContents().matches("%TODO%") or + c.getContents().matches("%FIXME%"))) +select f, n +order by n diff --git a/cpp/ql/src/Metrics/Files/FTransitiveIncludes.qhelp b/cpp/ql/src/Metrics/Files/FTransitiveIncludes.qhelp new file mode 100644 index 000000000000..4e0b989b1213 --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FTransitiveIncludes.qhelp @@ -0,0 +1,38 @@ + + + +

    This metric measures the number of files that are directly or indirectly included using #include.

    + +

    The value of this metric is usually directly correlated to the file's +build time: the more included files, the longer the compilation time.

    + +

    Often, files with a large number of includes do not require most of the included definitions, so they are contributing to unnecessarily long build times.

    +
    + + + +
      +
    • Remove redundant #include directives
    • +
    • Use the specific header file required, if possible, rather than a high-level header that includes many other header files as well
    • +
    • Split header files that contain lots of unrelated definitions or include large unrelated header files
    • +
    + +
    + + +
  • + Header files +
  • +
  • + Decoupling C Header Files +
  • +
  • + C++ Best Practice - +Designing Header Files +
  • + + +
    +
    diff --git a/cpp/ql/src/Metrics/Files/FTransitiveIncludes.ql b/cpp/ql/src/Metrics/Files/FTransitiveIncludes.ql new file mode 100644 index 000000000000..7d6d88f55b31 --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FTransitiveIncludes.ql @@ -0,0 +1,18 @@ +/** + * @name Indirect includes per file + * @description The number of files included by the + * pre-processor - either directly by an `#include` + * directive, or indirectly (by being included by an + * included file). + * @kind treemap + * @id cpp/transitive-includes-per-file + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg max + */ +import cpp + +from File f, int n +where f.fromSource() + and n = count(File g | g = f.getAnIncludedFile+()) +select f, n diff --git a/cpp/ql/src/Metrics/Files/FTransitiveSourceIncludes.qhelp b/cpp/ql/src/Metrics/Files/FTransitiveSourceIncludes.qhelp new file mode 100644 index 000000000000..a3091c68285d --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FTransitiveSourceIncludes.qhelp @@ -0,0 +1,43 @@ + + + +

    This metric measures the number of files that are directly or indirectly included using +#include, excluding header files which are outside the main source tree (for example, system headers). +

    + +

    The value of this metric is usually correlated to the file's +build time: the more included files, the longer the compilation time. +Since we don't count system headers, however, it is also a measure of +the amount of the current code base that the present file relies on.

    + +

    Often, files with a large number of includes do not require most of the included definitions, so +they are contributing to unnecessarily long build times and creating artificial build dependencies.

    + +
    + + +
      +
    • Remove redundant #include directives
    • +
    • Use the specific header file required, if possible, rather than a high-level header that includes many other header files as well
    • +
    • Split header files that contain lots of unrelated definitions or include large unrelated header files
    • +
    + + +
    + + +
  • + Header files +
  • +
  • + Decoupling C Header Files +
  • +
  • + C++ Best Practice - +Designing Header Files +
  • +
    + +
    diff --git a/cpp/ql/src/Metrics/Files/FTransitiveSourceIncludes.ql b/cpp/ql/src/Metrics/Files/FTransitiveSourceIncludes.ql new file mode 100644 index 000000000000..a919e322a64c --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FTransitiveSourceIncludes.ql @@ -0,0 +1,26 @@ +/** + * @name Indirect source includes per file + * @description The number of source files included by the + * pre-processor - either directly by an `#include` + * directive, or indirectly (by being included by an + * included file). This metric excludes included files + * that aren't part of the main code base (like system + * headers). + * @kind treemap + * @id cpp/transitive-source-includes-per-file + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg max + */ +import cpp + +predicate isInCodebase(File f) { + exists(string prefix | + sourceLocationPrefix(prefix) | + f.getAbsolutePath().prefix(prefix.length()) = prefix) +} + +from File f, int n +where f.fromSource() + and n = count(File g | g = f.getAnIncludedFile+() and isInCodebase(g)) +select f, n diff --git a/cpp/ql/src/Metrics/Files/FunctionLength.qhelp b/cpp/ql/src/Metrics/Files/FunctionLength.qhelp new file mode 100644 index 000000000000..b9dea7ea6c1d --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FunctionLength.qhelp @@ -0,0 +1,29 @@ + + + +

    This metric measures the average number of lines of code per function in a file.

    + +

    Having too many lines of code in a function is an indication that it can be split into several functions of more manageable size.

    + +
    + +

    Long functions should be examined to see if they can be split into smaller, more cohesive functions.

    + +
    + + +
  • + M. Fowler. Refactoring. Addison-Wesley, 1999. +
  • +
  • + Wikipedia: Code refactoring +
  • +
  • + Refactoring as Meta Programming? +
  • + + +
    +
    diff --git a/cpp/ql/src/Metrics/Files/FunctionLength.ql b/cpp/ql/src/Metrics/Files/FunctionLength.ql new file mode 100644 index 000000000000..bd875102550f --- /dev/null +++ b/cpp/ql/src/Metrics/Files/FunctionLength.ql @@ -0,0 +1,17 @@ +/** + * @name Function length + * @description The average number of lines in functions in each file. + * @kind treemap + * @id cpp/function-length + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg max + */ +import cpp + +from File f +where f.fromSource() +select f, avg(MetricFunction fn | + fn.getFile() = f + and not fn instanceof MemberFunction | + fn.getNumberOfLinesOfCode()) diff --git a/cpp/ql/src/Metrics/Files/NumberOfFunctions.qhelp b/cpp/ql/src/Metrics/Files/NumberOfFunctions.qhelp new file mode 100644 index 000000000000..4a088a69e117 --- /dev/null +++ b/cpp/ql/src/Metrics/Files/NumberOfFunctions.qhelp @@ -0,0 +1,38 @@ + + + +

    This metric measures the number of functions defined in each file.

    + + +

    Tracking this metric over time will indicate which parts of the system are under active +development. Cross-referencing with the other metrics "Cyclomatic Complexity" and "Lines of +Code" is recommended, because files with high values for all three metrics are very likely +to be too big and unwieldy; such files should be split up.

    + +
    + + +

    If a file is too big, identify the different tasks that are carried out by its functions +and split the file according to these tasks.

    + +
    + + +
  • + Functions +
  • +
  • + M. Fowler. Refactoring. Addison-Wesley, 1999. +
  • +
  • + Wikipedia: Code refactoring +
  • +
  • + Refactoring as Meta Programming? +
  • + + +
    +
    diff --git a/cpp/ql/src/Metrics/Files/NumberOfFunctions.ql b/cpp/ql/src/Metrics/Files/NumberOfFunctions.ql new file mode 100644 index 000000000000..68092ad63939 --- /dev/null +++ b/cpp/ql/src/Metrics/Files/NumberOfFunctions.ql @@ -0,0 +1,16 @@ +/** + * @name Functions per file + * @description The total number of functions in each file. + * @kind treemap + * @id cpp/number-of-functions + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg sum max + */ +import cpp + +from File f +where f.fromSource() +select f, count(Function fn | + fn.getFile() = f + and not fn instanceof MemberFunction) diff --git a/cpp/ql/src/Metrics/Files/NumberOfGlobals.qhelp b/cpp/ql/src/Metrics/Files/NumberOfGlobals.qhelp new file mode 100644 index 000000000000..feaebc47f7dc --- /dev/null +++ b/cpp/ql/src/Metrics/Files/NumberOfGlobals.qhelp @@ -0,0 +1,34 @@ + + + +

    This metric measures the number of global variables in a file. This includes both public and file-local global variables.

    + +

    Globals in general should be avoided as it is too easy for them to be modified in other parts of the code. +It also pollutes the name space and public globals expose internal representation. This is particularly +important in multi-threaded applications as every global is likely going to require some synchronization +mechanism to ensure that they are accessed and written in the correct order.

    + +
    + +

    Try to eliminate globals by passing them as parameters to functions. If global state is necessary, +try to encapsulate related variables into structs and classes. In C++ it is preferable to group state +as well as functions into classes.

    + +
    + + +
  • + Global variables +
  • +
  • + C++: Global warning +
  • +
  • + Minimize the scope of variables and functions +
  • + + +
    +
    diff --git a/cpp/ql/src/Metrics/Files/NumberOfGlobals.ql b/cpp/ql/src/Metrics/Files/NumberOfGlobals.ql new file mode 100644 index 000000000000..0a957bf83795 --- /dev/null +++ b/cpp/ql/src/Metrics/Files/NumberOfGlobals.ql @@ -0,0 +1,44 @@ +/** + * @name Global variables + * @description The total number of global variables in each file. + * @kind treemap + * @id cpp/number-of-globals + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg sum max + */ +import cpp + +predicate macroLocation(File f, int startLine, int endLine) { + exists(MacroInvocation mi, Location l | + l = mi.getLocation() and + l.getFile() = f and + l.getStartLine() = startLine and + l.getEndLine() = endLine + ) +} + +pragma[nomagic] +Location getVariableLocation(Variable v) { + result = v.getLocation() +} + +predicate globalLocation(GlobalVariable gv, File f, int startLine, int endLine) { + exists(Location l | + l = getVariableLocation(gv) and + l.hasLocationInfo(f.getAbsolutePath(), startLine, _, endLine, _) + ) +} + +predicate inMacro(GlobalVariable gv) { + exists(File f, int macroStart, int macroEnd, int varStart, int varEnd | + macroLocation(f, macroStart, macroEnd) and + globalLocation(gv, f, varStart, varEnd) and + varStart >= macroStart and + varEnd <= macroEnd + ) +} + +from File f +where f.fromSource() +select f, count(GlobalVariable gv | gv.getFile() = f and not inMacro(gv)) diff --git a/cpp/ql/src/Metrics/Files/NumberOfParameters.qhelp b/cpp/ql/src/Metrics/Files/NumberOfParameters.qhelp new file mode 100644 index 000000000000..0186a747a344 --- /dev/null +++ b/cpp/ql/src/Metrics/Files/NumberOfParameters.qhelp @@ -0,0 +1,44 @@ + + + +

    This metric measures the average number of parameters to functions.

    + +

    Functions with an excessive number of parameters often exhibit one or more of the +following problems.

    + +
      +
    • The functions lack cohesion because they do several unrelated tasks.
    • +
    • Several of the arguments would be better grouped together using a struct or class, + because they represent one conceptual entity.
    • +
    • Calling the function is prone to mistakes because arguments may be accidentally transposed, + possibly without a type-error.
    • +
    + +
    + + +

    Consider refactoring functions with too many arguments into smaller functions, each with +a single well-defined purpose.

    + +

    It may also be possible to create new abstractions for usefully grouping arguments. For +example, rather than representing a buffer by a pointer and an integer length, they could be +encapsulated into a single struct, with utility functions for common operations.

    + +
    + + +
  • + M. Fowler. Refactoring. Addison-Wesley, 1999. +
  • +
  • + Wikipedia: Code refactoring +
  • +
  • + Refactoring as Meta Programming? +
  • + + +
    +
    diff --git a/cpp/ql/src/Metrics/Files/NumberOfParameters.ql b/cpp/ql/src/Metrics/Files/NumberOfParameters.ql new file mode 100644 index 000000000000..6c64eaebfe27 --- /dev/null +++ b/cpp/ql/src/Metrics/Files/NumberOfParameters.ql @@ -0,0 +1,18 @@ +/** + * @name Parameters per function + * @description The average number of parameters of functions in each file. + * @kind treemap + * @id cpp/number-of-parameters + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg max + * @tags testability + * complexity + */ +import cpp + +from File f +where f.fromSource() +select f, avg(Function fn | + fn.getFile() = f and not fn instanceof MemberFunction | + fn.getNumberOfParameters()) diff --git a/cpp/ql/src/Metrics/Files/NumberOfPublicFunctions.qhelp b/cpp/ql/src/Metrics/Files/NumberOfPublicFunctions.qhelp new file mode 100644 index 000000000000..d93eb88484c4 --- /dev/null +++ b/cpp/ql/src/Metrics/Files/NumberOfPublicFunctions.qhelp @@ -0,0 +1,42 @@ + + + +

    This metric measures the number of public functions in a file.

    + +

    Public functions are the API between a component and other parts of a program. As such, +it makes sense to monitor the number of public functions and consider the following +questions:

    + +
      +
    • Is the API too large and unwieldy?
    • +
    • When adding new functions, what effect will the new additions have on the cost of + maintaining the additions (given that other components may come to rely on them)?
    • +
    • Do any public functions inadvertently expose implementation details of a component?
    • +
    + +
    + + +

    Adjust the API of a component in consideration of the above questions.

    + +
    + + +
  • + Functions +
  • +
  • + M. Fowler. Refactoring. Addison-Wesley, 1999. +
  • +
  • + Wikipedia: Code refactoring +
  • +
  • + Refactoring as Meta Programming? +
  • + + +
    +
    diff --git a/cpp/ql/src/Metrics/Files/NumberOfPublicFunctions.ql b/cpp/ql/src/Metrics/Files/NumberOfPublicFunctions.ql new file mode 100644 index 000000000000..482425101eb9 --- /dev/null +++ b/cpp/ql/src/Metrics/Files/NumberOfPublicFunctions.ql @@ -0,0 +1,18 @@ +/** + * @name Public functions per file + * @description The total number of public (non-static) functions in + * each file. + * @kind treemap + * @id cpp/number-of-public-functions + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg sum max + */ +import cpp + +from File f +where f.fromSource() +select f, count(Function fn | + fn.getFile() = f + and not fn instanceof MemberFunction + and not fn.isStatic()) diff --git a/cpp/ql/src/Metrics/Files/NumberOfPublicGlobals.qhelp b/cpp/ql/src/Metrics/Files/NumberOfPublicGlobals.qhelp new file mode 100644 index 000000000000..d2b42aae4c56 --- /dev/null +++ b/cpp/ql/src/Metrics/Files/NumberOfPublicGlobals.qhelp @@ -0,0 +1,40 @@ + + + +

    This metric measures the number of public global variables in a file.

    + +

    Global variables can cause several problems:

    + +
      +
    • lack of encapsulation, because state is exposed to the whole program, rather than a single module
    • +
    • difficulty maintaining the software, because code using or manipulating the value of the global may be scattered across the software, so its hard to track them all down when making a change
    • +
    • defects in multi-threading, because the code that uses globals is usually not thread-safe
    • +
    + +
    + + + +

    Make global variables local to a file, and provide functions for manipulating the +value of the variable in a thread-safe way. If possible, make the variable an instance +variable in a class or struct that is passed from function to function as necessary so +that there is no global state.

    + +
    + + +
  • + Global variables +
  • +
  • + C++: Global warning +
  • +
  • + Minimize the scope of variables and functions +
  • + + +
    +
    diff --git a/cpp/ql/src/Metrics/Files/NumberOfPublicGlobals.ql b/cpp/ql/src/Metrics/Files/NumberOfPublicGlobals.ql new file mode 100644 index 000000000000..2abef690d822 --- /dev/null +++ b/cpp/ql/src/Metrics/Files/NumberOfPublicGlobals.ql @@ -0,0 +1,49 @@ +/** + * @name Public global variables + * @description The total number of global variables in each file with + * external (public) visibility. + * @kind treemap + * @id cpp/number-of-public-globals + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg sum max + * @tags maintainability + * modularity + */ +import cpp + +predicate macroLocation(File f, int startLine, int endLine) { + exists(MacroInvocation mi, Location l | + l = mi.getLocation() and + l.getFile() = f and + l.getStartLine() = startLine and + l.getEndLine() = endLine + ) +} + +pragma[nomagic] +Location getVariableLocation(Variable v) { + result = v.getLocation() +} + +predicate globalLocation(GlobalVariable gv, File f, int startLine, int endLine) { + exists(Location l | + l = getVariableLocation(gv) and + l.hasLocationInfo(f.getAbsolutePath(), startLine, _, endLine, _)) +} + +predicate inMacro(GlobalVariable gv) { + exists(File f, int macroStart, int macroEnd, int varStart, int varEnd | + macroLocation(f, macroStart, macroEnd) and + globalLocation(gv, f, varStart, varEnd) and + varStart >= macroStart and + varEnd <= macroEnd + ) +} + +from File f +where f.fromSource() +select f, count(GlobalVariable gv | + gv.getFile() = f + and not gv.isStatic() + and not inMacro(gv)) diff --git a/cpp/ql/src/Metrics/Functions/FunCyclomaticComplexity.qhelp b/cpp/ql/src/Metrics/Functions/FunCyclomaticComplexity.qhelp new file mode 100644 index 000000000000..9dc5a723b115 --- /dev/null +++ b/cpp/ql/src/Metrics/Functions/FunCyclomaticComplexity.qhelp @@ -0,0 +1,40 @@ + + + +

    This metric measures the cyclomatic complexity of each function in the project.

    + +

    The cyclomatic complexity of a function is an indication of the number of paths that can be taken during the execution of a function. +Straight-line code has zero cyclomatic complexity, while branches and loops increase cyclomatic complexity. A cyclomatic complexity above 50 should be considered bad practice and above 75 should definitely be addressed.

    + +

    Functions with high cyclomatic complexity suffer from the following problems:

    +
      +
    • Difficult to test since tests should be provided for each possible execution
    • +
    • Difficult to understand since a developer needs to understand how all conditions interact
    • +
    • Difficult to maintain since many execution paths is an indication of functions that perform too many tasks
    • +
    + +
    + +

    The primary way to reduce the complexity is to extract sub-functionality into separate functions. This improves on all problems described above. If the function naturally breaks up into a sequence of operations it is preferable to extract each operation as a separate function. Even if that's not the case it is often possible to extract the body of an iteration into a separate function to reduce complexity. If the complexity can't be reduced significantly make sure that the function is properly documented and carefully tested.

    + +
    + + +
  • + Functions +
  • +
  • + M. Fowler. Refactoring. Addison-Wesley, 1999. +
  • +
  • + Wikipedia: Code refactoring +
  • +
  • + Refactoring as Meta Programming? +
  • + + +
    +
    diff --git a/cpp/ql/src/Metrics/Functions/FunCyclomaticComplexity.ql b/cpp/ql/src/Metrics/Functions/FunCyclomaticComplexity.ql new file mode 100644 index 000000000000..30b24868d451 --- /dev/null +++ b/cpp/ql/src/Metrics/Functions/FunCyclomaticComplexity.ql @@ -0,0 +1,19 @@ +/** + * @name Cyclomatic complexity of functions + * @description The Cyclomatic complexity (an indication of how many + * tests are necessary, based on the number of branching + * statements) per function. + * @kind treemap + * @id cpp/cyclomatic-complexity-per-function + * @treemap.warnOn highValues + * @metricType callable + * @metricAggregate avg max sum + * @tags testability + * maintainability + * complexity + */ +import cpp + +from Function f +where strictcount(f.getEntryPoint()) = 1 +select f, f.getMetrics().getCyclomaticComplexity() diff --git a/cpp/ql/src/Metrics/Functions/FunIterationNestingDepth.ql b/cpp/ql/src/Metrics/Functions/FunIterationNestingDepth.ql new file mode 100644 index 000000000000..69ab2627e0a1 --- /dev/null +++ b/cpp/ql/src/Metrics/Functions/FunIterationNestingDepth.ql @@ -0,0 +1,19 @@ +/** + * @name Iteration depth + * @description The maximum number of nested loops in each function. + * @kind treemap + * @id cpp/iteration-nesting-depth-per-function + * @treemap.warnOn highValues + * @metricType callable + * @metricAggregate avg max + */ +import cpp + +int iterationDepth(Stmt l) { + result = count(Loop other | l.getParent*() = other) +} + +from Function f, int depth +where depth = max(Stmt s | s.getEnclosingFunction() = f | iterationDepth(s)) + and strictcount(f.getEntryPoint()) = 1 +select f, depth order by depth desc diff --git a/cpp/ql/src/Metrics/Functions/FunLinesOfCode.qhelp b/cpp/ql/src/Metrics/Functions/FunLinesOfCode.qhelp new file mode 100644 index 000000000000..cd0af9c1063b --- /dev/null +++ b/cpp/ql/src/Metrics/Functions/FunLinesOfCode.qhelp @@ -0,0 +1,28 @@ + + + +

    This metric measures the number of lines of code in a function. This excludes comments and blank lines.

    + +

    Having too many lines of code in a function is an indication that it can be split into several functions of more manageable size.

    + +
    + +

    Long functions should be examined to see if they can be split into smaller, more cohesive functions.

    + +
    + + +
  • + M. Fowler. Refactoring. Addison-Wesley, 1999. +
  • +
  • + Wikipedia: Code refactoring +
  • +
  • + Refactoring as Meta Programming? +
  • + +
    +
    diff --git a/cpp/ql/src/Metrics/Functions/FunLinesOfCode.ql b/cpp/ql/src/Metrics/Functions/FunLinesOfCode.ql new file mode 100644 index 000000000000..dd458edb10b5 --- /dev/null +++ b/cpp/ql/src/Metrics/Functions/FunLinesOfCode.ql @@ -0,0 +1,17 @@ +/** + * @name Lines of code per function + * @description Measures the number of lines in a function that contain + * code (rather than lines that only contain comments or + * are blank). + * @kind treemap + * @id cpp/lines-of-code-per-function + * @treemap.warnOn highValues + * @metricType callable + * @metricAggregate avg sum max + * @tags maintainability + */ +import cpp + +from Function f +where strictcount(f.getEntryPoint()) = 1 +select f, f.getMetrics().getNumberOfLinesOfCode() diff --git a/cpp/ql/src/Metrics/Functions/FunLinesOfComments.qhelp b/cpp/ql/src/Metrics/Functions/FunLinesOfComments.qhelp new file mode 100644 index 000000000000..6232b6bd4418 --- /dev/null +++ b/cpp/ql/src/Metrics/Functions/FunLinesOfComments.qhelp @@ -0,0 +1,28 @@ + + + +

    This metric measures the number of lines of comments in a function.

    + +

    Functions that have too few comments are more likely to be insufficiently documented. +Long, complex functions that have no comments at all require particular scrutiny, as these are more likely +to need explicit documentation in comments.

    + +
    + +

    Functions with few to no comments should be examined to see if they require documentation. Particular attention +should be given to long, complex functions.

    + +
    + + +
  • + C++ Programming, Coding style conventions +
  • +
  • + Wikipedia: Need for comments +
  • + +
    +
    diff --git a/cpp/ql/src/Metrics/Functions/FunLinesOfComments.ql b/cpp/ql/src/Metrics/Functions/FunLinesOfComments.ql new file mode 100644 index 000000000000..98440e505d1a --- /dev/null +++ b/cpp/ql/src/Metrics/Functions/FunLinesOfComments.ql @@ -0,0 +1,18 @@ +/** + * @name Lines of comments per function + * @description Measures the number of lines in a function that contain + * a comment or part of a comment (that is, which are part + * of a multi-line comment). + * @kind treemap + * @id cpp/lines-of-comments-per-function + * @treemap.warnOn lowValues + * @metricType callable + * @metricAggregate avg sum max + * @tags maintainability + * documentation + */ +import cpp + +from Function f +where strictcount(f.getEntryPoint()) = 1 +select f, f.getMetrics().getNumberOfLinesOfComments() diff --git a/cpp/ql/src/Metrics/Functions/FunNumberOfCalls.qhelp b/cpp/ql/src/Metrics/Functions/FunNumberOfCalls.qhelp new file mode 100644 index 000000000000..54bcd9ece016 --- /dev/null +++ b/cpp/ql/src/Metrics/Functions/FunNumberOfCalls.qhelp @@ -0,0 +1,32 @@ + + + +

    This metric measures the number of function calls in each function. Only explicit calls are included, i.e., calls in macro expansions and compiler generated calls are not counted. + +

    +

    Functions with many calls are hard to understand and maintain. It is usually a sign of a function with too many responsibilities.

    + +
    + +

    The primary way to reduce the complexity is to extract sub-functionality into separate functions. If the function naturally breaks up into a sequence of operations it is preferable to extract each operation as a separate function. This is most likely the case for large functions with low cyclomatic complexity. Even if the code is straight forward it is better to extract functionality for readability purposes. Moreover, it may enable reuse of the extracted subfunctionality.

    + +
    + + +
  • + Functions +
  • +
  • + M. Fowler. Refactoring. Addison-Wesley, 1999. +
  • +
  • + Wikipedia: Code refactoring +
  • +
  • + Refactoring as Meta Programming? +
  • + +
    +
    diff --git a/cpp/ql/src/Metrics/Functions/FunNumberOfCalls.ql b/cpp/ql/src/Metrics/Functions/FunNumberOfCalls.ql new file mode 100644 index 000000000000..ee972222c36c --- /dev/null +++ b/cpp/ql/src/Metrics/Functions/FunNumberOfCalls.ql @@ -0,0 +1,27 @@ +/** + * @name Number of function calls per function + * @description The number of C/C++ function calls per function. + * @kind treemap + * @id cpp/number-of-calls-per-function + * @treemap.warnOn highValues + * @metricType callable + * @metricAggregate avg sum max + * @tags maintainability + * complexity + */ +import cpp + +predicate callToOperator(FunctionCall fc) { + fc.getTarget() instanceof Operator or + fc.getTarget() instanceof ConversionOperator +} + +from Function f, int n, int o +where strictcount(f.getEntryPoint()) = 1 + and o = count(FunctionCall c | + c.getEnclosingFunction() = f and + not c.isInMacroExpansion() and + not c.isCompilerGenerated() and + not callToOperator(c)) + and n = o / count(f.getBlock()) +select f, n diff --git a/cpp/ql/src/Metrics/Functions/FunNumberOfParameters.qhelp b/cpp/ql/src/Metrics/Functions/FunNumberOfParameters.qhelp new file mode 100644 index 000000000000..e6257c28fae0 --- /dev/null +++ b/cpp/ql/src/Metrics/Functions/FunNumberOfParameters.qhelp @@ -0,0 +1,37 @@ + + + +

    This metric measures the number of formal parameters per function.

    + +

    Functions with too many formal parameters are hard to use and maintain due to their complex +interface. Using them is hard since you need to provide the right number of parameters in the +right order. This can be particularly prone to mistakes if many of the parameters share the same type.

    + +
    + +

    If the function takes many related parameters they should be grouped into a class or struct. +This improves the usability of the function with more explicit naming, and also avoids reduces +the risk of forgetting parameters and accidental reordering. If some of the parameters are optional, +either explicitly with default value, or accepting NULL or 0, then it may be better to create separate +functions for the different use cases.

    + +
    + + +
  • + Functions +
  • +
  • + M. Fowler. Refactoring. Addison-Wesley, 1999. +
  • +
  • + Wikipedia: Code refactoring +
  • +
  • + Refactoring as Meta Programming? +
  • + +
    +
    diff --git a/cpp/ql/src/Metrics/Functions/FunNumberOfParameters.ql b/cpp/ql/src/Metrics/Functions/FunNumberOfParameters.ql new file mode 100644 index 000000000000..f05191a52376 --- /dev/null +++ b/cpp/ql/src/Metrics/Functions/FunNumberOfParameters.ql @@ -0,0 +1,16 @@ +/** + * @name Number of parameters per function + * @description The number of formal parameters for each function. + * @kind treemap + * @id cpp/number-of-parameters-per-function + * @treemap.warnOn highValues + * @metricType callable + * @metricAggregate avg max + * @tags testability + * maintainability + */ +import cpp + +from Function f +where strictcount(f.getEntryPoint()) = 1 +select f, f.getMetrics().getNumberOfParameters() diff --git a/cpp/ql/src/Metrics/Functions/FunNumberOfStatements.qhelp b/cpp/ql/src/Metrics/Functions/FunNumberOfStatements.qhelp new file mode 100644 index 000000000000..6578190d3f01 --- /dev/null +++ b/cpp/ql/src/Metrics/Functions/FunNumberOfStatements.qhelp @@ -0,0 +1,39 @@ + + + +

    This metric measures the number of statements (including control statements such as +if and while) in each function. + +

    +

    Functions with many statements are hard to understand and maintain. It is usually a +sign of a function with too many responsibilities.

    + +
    + +

    The primary way to improve the complexity is to extract sub-functionality into separate +functions. If the function naturally breaks up into a sequence of operations it is +preferable to extract each operation as a separate function. This is most likely the case +for large functions with low cyclomatic complexity. Even if the code is straight forward +it is better to extract functionality for readability purposes. Moreover, it may enable +reuse of the extracted subfunctionality.

    + +
    + + +
  • + Functions +
  • +
  • + M. Fowler. Refactoring. Addison-Wesley, 1999. +
  • +
  • + Wikipedia: Code refactoring +
  • +
  • + Refactoring as Meta Programming? +
  • + +
    +
    diff --git a/cpp/ql/src/Metrics/Functions/FunNumberOfStatements.ql b/cpp/ql/src/Metrics/Functions/FunNumberOfStatements.ql new file mode 100644 index 000000000000..07211dc78f5e --- /dev/null +++ b/cpp/ql/src/Metrics/Functions/FunNumberOfStatements.ql @@ -0,0 +1,16 @@ +/** + * @name Number of statements per function + * @description The number of C/C++ statements per function. + * @kind treemap + * @id cpp/statements-per-function + * @treemap.warnOn highValues + * @metricType callable + * @metricAggregate avg sum max + * @tags maintainability + */ +import cpp + +from Function f, int n +where strictcount(f.getEntryPoint()) = 1 and + n = count(Stmt s | s.getEnclosingFunction() = f) +select f, n diff --git a/cpp/ql/src/Metrics/Functions/FunPercentageOfComments.qhelp b/cpp/ql/src/Metrics/Functions/FunPercentageOfComments.qhelp new file mode 100644 index 000000000000..5f998e3e476d --- /dev/null +++ b/cpp/ql/src/Metrics/Functions/FunPercentageOfComments.qhelp @@ -0,0 +1,27 @@ + + + +

    This metric measures the percentage of lines in an function that contain a comment or +are part of a multi-line comment.

    + +

    Having a low comment ratio is an indication that a function does not have sufficient +documentation.

    + +
    + +

    Start by adding documentation to functions that are used from other parts of the code. Then document large complex functions, as they benefit most from documentation.

    + +
    + + +
  • + C++ Programming, Coding style conventions +
  • +
  • + Wikipedia: Need for comments +
  • + +
    +
    diff --git a/cpp/ql/src/Metrics/Functions/FunPercentageOfComments.ql b/cpp/ql/src/Metrics/Functions/FunPercentageOfComments.ql new file mode 100644 index 000000000000..ebeefec5b423 --- /dev/null +++ b/cpp/ql/src/Metrics/Functions/FunPercentageOfComments.ql @@ -0,0 +1,17 @@ +/** + * @name Comment ratio per function + * @description The ratio of comment lines to the total number of lines + * in a function. + * @kind treemap + * @id cpp/percentage-of-comments-per-function + * @treemap.warnOn lowValues + * @metricType callable + * @metricAggregate avg max + * @tags maintainability + * documentation + */ +import cpp + +from MetricFunction f +where f.getNumberOfLines() > 0 and strictcount(f.getEntryPoint()) = 1 +select f, 100.0 * (f.getNumberOfLinesOfComments().(float) / f.getNumberOfLines().(float)) diff --git a/cpp/ql/src/Metrics/Functions/StatementNestingDepth.qhelp b/cpp/ql/src/Metrics/Functions/StatementNestingDepth.qhelp new file mode 100644 index 000000000000..135a20daa8af --- /dev/null +++ b/cpp/ql/src/Metrics/Functions/StatementNestingDepth.qhelp @@ -0,0 +1,33 @@ + + + +

    +This metric measures the maximum nesting depth in a file. This includes nested branch and loop statements, but not blocks. +

    + +

    +Deep nesting makes code very difficult to read and modify, and is also a sign that a function/class has lost cohesion +(i.e. is doing too many unrelated things). Try to keep nesting depth below 7 levels. +

    + +
    + +

    Reduce the nesting in the file by putting the code in the inner loops/branches in separate functions.

    + +
    + + +
  • + M. Fowler. Refactoring. Addison-Wesley, 1999. +
  • +
  • + Wikipedia: Code refactoring +
  • +
  • + Refactoring as Meta Programming? +
  • + +
    +
    diff --git a/cpp/ql/src/Metrics/Functions/StatementNestingDepth.ql b/cpp/ql/src/Metrics/Functions/StatementNestingDepth.ql new file mode 100644 index 000000000000..0e9ce896611c --- /dev/null +++ b/cpp/ql/src/Metrics/Functions/StatementNestingDepth.ql @@ -0,0 +1,19 @@ +/** + * @name Nesting depth + * @description The maximum number of nested statements (for example, + * `if`, `for`, `while`, etc.). Blocks are not counted. + * @kind treemap + * @id cpp/statement-nesting-depth + * @treemap.warnOn highValues + * @metricType callable + * @metricAggregate avg max + * @tags maintainability + * complexity + */ +import cpp + +from MetricFunction f, int depth +where depth = f.getNestingDepth() + and strictcount(f.getEntryPoint()) = 1 +select f, depth +order by depth desc diff --git a/cpp/ql/src/Metrics/History/HChurn.qhelp b/cpp/ql/src/Metrics/History/HChurn.qhelp new file mode 100644 index 000000000000..2d248a0ac182 --- /dev/null +++ b/cpp/ql/src/Metrics/History/HChurn.qhelp @@ -0,0 +1,42 @@ + + + +

    +This metric measures the number of lines of text that have been added, deleted +or modified in files below this location in the tree. +

    + +

    +Code churn is known to be a good (if not the best) predictor of defects in a +code component (see e.g. [Nagappan] or [Khoshgoftaar]). The intuition is that +files, packages or projects that have experienced a disproportionately high +amount of churn for the amount of code involved may have been harder to write, +and are thus likely to contain more bugs. +

    + +
    + + +

    +It is a fact of life that some code is going to be changed more than the rest, +and little can be done to change this. However, bearing in mind code churn's +effectiveness as a defect predictor, code that has been repeatedly changed +should be subjected to vigorous testing and code review. +

    + +
    + + + +
  • +N. Nagappan et al. Change Bursts as Defect Predictors. In Proceedings of the 21st IEEE International Symposium on Software Reliability Engineering, 2010. +
  • +
  • +T. M. Khoshgoftaar and R. M. Szabo. Improving code churn predictions during the system test and maintenance phases. In ICSM '94, 1994, pp. 58-67. +
  • + + +
    +
    diff --git a/cpp/ql/src/Metrics/History/HChurn.ql b/cpp/ql/src/Metrics/History/HChurn.ql new file mode 100644 index 000000000000..40b4459caca5 --- /dev/null +++ b/cpp/ql/src/Metrics/History/HChurn.ql @@ -0,0 +1,21 @@ +/** + * @name Churned lines per file + * @description Number of churned lines per file, across the revision + * history in the database. + * @kind treemap + * @id cpp/historical-churn + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg sum max + */ +import cpp +import external.VCS + +from File f, int n +where n = sum(Commit entry, int churn | + churn = entry.getRecentChurnForFile(f) and + not artificialChange(entry) | + churn) + and exists(f.getMetrics().getNumberOfLinesOfCode()) +select f, n +order by n desc diff --git a/cpp/ql/src/Metrics/History/HLinesAdded.qhelp b/cpp/ql/src/Metrics/History/HLinesAdded.qhelp new file mode 100644 index 000000000000..fc812fc8357c --- /dev/null +++ b/cpp/ql/src/Metrics/History/HLinesAdded.qhelp @@ -0,0 +1,6 @@ + + + + diff --git a/cpp/ql/src/Metrics/History/HLinesAdded.ql b/cpp/ql/src/Metrics/History/HLinesAdded.ql new file mode 100644 index 000000000000..1a9fcc301e33 --- /dev/null +++ b/cpp/ql/src/Metrics/History/HLinesAdded.ql @@ -0,0 +1,21 @@ +/** + * @name Added lines per file + * @description Number of added lines per file, across the revision + * history in the database. + * @kind treemap + * @id cpp/historical-lines-added + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg sum max + */ +import cpp +import external.VCS + +from File f, int n +where n = sum(Commit entry, int churn | + churn = entry.getRecentAdditionsForFile(f) and + not artificialChange(entry) | + churn) + and exists(f.getMetrics().getNumberOfLinesOfCode()) +select f, n +order by n desc diff --git a/cpp/ql/src/Metrics/History/HLinesDeleted.qhelp b/cpp/ql/src/Metrics/History/HLinesDeleted.qhelp new file mode 100644 index 000000000000..fc812fc8357c --- /dev/null +++ b/cpp/ql/src/Metrics/History/HLinesDeleted.qhelp @@ -0,0 +1,6 @@ + + + + diff --git a/cpp/ql/src/Metrics/History/HLinesDeleted.ql b/cpp/ql/src/Metrics/History/HLinesDeleted.ql new file mode 100644 index 000000000000..72e547060568 --- /dev/null +++ b/cpp/ql/src/Metrics/History/HLinesDeleted.ql @@ -0,0 +1,21 @@ +/** + * @name Deleted lines per file + * @description Number of deleted lines per file, across the revision + * history in the database. + * @kind treemap + * @id cpp/historical-lines-deleted + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg sum max + */ +import cpp +import external.VCS + +from File f, int n +where n = sum(Commit entry, int churn | + churn = entry.getRecentDeletionsForFile(f) + and not artificialChange(entry) | + churn) + and exists(f.getMetrics().getNumberOfLinesOfCode()) +select f, n +order by n desc diff --git a/cpp/ql/src/Metrics/History/HNumberOfAuthors.qhelp b/cpp/ql/src/Metrics/History/HNumberOfAuthors.qhelp new file mode 100644 index 000000000000..00ec48b744f0 --- /dev/null +++ b/cpp/ql/src/Metrics/History/HNumberOfAuthors.qhelp @@ -0,0 +1,48 @@ + + + +

    +This metric measures the number of different authors (by examining the +version control history) +for files below this location in the tree. (This is a better version +of the metric that counts the number of different authors using Javadoc +tags.) +

    + +

    +Files that have been changed by a large number of different authors are +by definition the product of many minds. New authors working on a file +may be less familiar with the design and implementation of the code than +the original authors, which can be a potential source of bugs. Furthermore, +code that has been worked on by many people, if not carefully maintained, +often ends up lacking conceptual integrity. For both of these reasons, any +code that has been worked on by an unusually high number of different people +merits careful inspection in code reviews. +

    + +
    + + +

    +There is clearly no way to reduce the number of authors that have worked +on a file - it is impossible to rewrite history. However, files highlighted +by this metric should be given special attention in a code review, and may +ultimately be good candidates for refactoring/rewriting by an individual, +experienced developer. +

    + + + +
    + + + +
  • +F. P. Brooks Jr. The Mythical Man-Month, Chapter 4. Addison-Wesley, 1974. +
  • + + +
    +
    diff --git a/cpp/ql/src/Metrics/History/HNumberOfAuthors.ql b/cpp/ql/src/Metrics/History/HNumberOfAuthors.ql new file mode 100644 index 000000000000..1da244ff31aa --- /dev/null +++ b/cpp/ql/src/Metrics/History/HNumberOfAuthors.ql @@ -0,0 +1,16 @@ +/** + * @name Number of authors + * @description Number of distinct authors for each file. + * @kind treemap + * @id cpp/historical-number-of-authors + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg min max + */ +import cpp +import external.VCS + +from File f +where exists(f.getMetrics().getNumberOfLinesOfCode()) +select f, count(Author author | author.getAnEditedFile() = f) + diff --git a/cpp/ql/src/Metrics/History/HNumberOfChanges.qhelp b/cpp/ql/src/Metrics/History/HNumberOfChanges.qhelp new file mode 100644 index 000000000000..8625b20bee9d --- /dev/null +++ b/cpp/ql/src/Metrics/History/HNumberOfChanges.qhelp @@ -0,0 +1,30 @@ + + + +

    +This metric measures the total number of file-level changes made to files +below this location in the tree. For an individual file, it measures the +number of commits that have affected that file. For a directory of files, it +measures the sum of the file-level changes for each of the files in the +directory. +

    + +

    +For example, suppose we have a directory containing two files, A and B. If the +number of file-level changes to A is 100, and the number of +file-level changes to B is 80, then the total number of +file-level changes to the directory is 180. Note that this is +likely to be different (in some cases very different) from the number of +commits that affected any file in the directory, since more than one file can +be changed by a single commit. (Note what would happen if we performed +80 commits on A and B, followed by another 20 +commits on A alone - the total number of file-level changes would be +180, but the number of commits involved would be +100.) +

    + + +
    +
    diff --git a/cpp/ql/src/Metrics/History/HNumberOfChanges.ql b/cpp/ql/src/Metrics/History/HNumberOfChanges.ql new file mode 100644 index 000000000000..e3b2396662c8 --- /dev/null +++ b/cpp/ql/src/Metrics/History/HNumberOfChanges.ql @@ -0,0 +1,17 @@ +/** + * @name Number of file-level changes + * @description The number of file-level changes made (by version + * control history). + * @kind treemap + * @id cpp/historical-number-of-changes + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg min max sum + */ +import cpp +import external.VCS + +from File f +where exists(f.getMetrics().getNumberOfLinesOfCode()) +select f, count(Commit svn | + f = svn.getAnAffectedFile() and not artificialChange(svn)) diff --git a/cpp/ql/src/Metrics/History/HNumberOfCoCommits.qhelp b/cpp/ql/src/Metrics/History/HNumberOfCoCommits.qhelp new file mode 100644 index 000000000000..6a767d576589 --- /dev/null +++ b/cpp/ql/src/Metrics/History/HNumberOfCoCommits.qhelp @@ -0,0 +1,51 @@ + + + +

    +This metric measures the average number of co-committed files for the files +below this location in the tree. +

    + +

    +A co-committed file is one that is committed at the same time as a given file. +For instance, if you commit files A, B and C together, then B and C would be +the co-committed files of A for that commit. The value of the metric for an +individual file is the average number of such co-committed files over all +commits. The value of the metric for a directory is the aggregation of these +averages - for instance, if we are using max as our aggregation +function, the value would be the maximum of the average number of co-commits +over all files in the directory. +

    + +

    +An unusually high value for this metric may indicate that the file in question +is too tightly-coupled to other files, and it is difficult to change it in +isolation. Alternatively, it may just be an indication that you commit lots of +unrelated changes at the same time. +

    + +
    + + +

    +Examine the file in question to see what the problem is. +

    + +
      +
    • +If the file is too tightly coupled, it will have high values for its afferent +and/or efferent coupling metrics, and you should apply the advice given there. +
    • + +
    • +If the file is not tightly coupled, but you find that you are committing lots +of unrelated changes at the same time, then you may want to revisit your commit +practices. +
    • +
    + + +
    +
    diff --git a/cpp/ql/src/Metrics/History/HNumberOfCoCommits.ql b/cpp/ql/src/Metrics/History/HNumberOfCoCommits.ql new file mode 100644 index 000000000000..66dd16071630 --- /dev/null +++ b/cpp/ql/src/Metrics/History/HNumberOfCoCommits.ql @@ -0,0 +1,23 @@ +/** + * @name Number of co-committed files + * @description The average number of other files that are touched + * whenever a file is affected by a commit. + * @kind treemap + * @id cpp/historical-number-of-co-commits + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg min max + */ +import cpp +import external.VCS + +int committedFiles(Commit commit) { + result = count(commit.getAnAffectedFile()) +} + +from File f +where exists(f.getMetrics().getNumberOfLinesOfCode()) +select f, avg(Commit commit | + commit.getAnAffectedFile() = f | + committedFiles(commit) - 1) + diff --git a/cpp/ql/src/Metrics/History/HNumberOfReCommits.qhelp b/cpp/ql/src/Metrics/History/HNumberOfReCommits.qhelp new file mode 100644 index 000000000000..37edc9aecf15 --- /dev/null +++ b/cpp/ql/src/Metrics/History/HNumberOfReCommits.qhelp @@ -0,0 +1,53 @@ + + + +

    +This metric measures the number of file re-commits that have occurred below +this location in the tree. A re-commit is taken to mean a commit to a file +that was touched less than five days ago. +

    + +

    +In a system that is being developed using a controlled change process (where +changes are not committed until they are in some sense 'complete'), re-commits +can be (but are not always) an indication that an initial change was not +successful and had to be revisited within a short time period. The intuition +is that the original change may have been difficult to get right, and hence +the code in the file may be more than usually defect-prone. The concept is +somewhat similar to that of 'change bursts', as described in [Nagappan]. +

    + +
    + + +

    +High numbers of re-commits can be addressed on two levels: preventative and +corrective. +

    + +
      +
    • +On the preventative side, a high number of re-commits may be an indication +that your code review process needs an overhaul. +
    • + +
    • +On the corrective side, code that has experienced a high number of re-commits +should be vigorously code reviewed and tested. +
    • +
    + + +
    + + + +
  • +N. Nagappan et al. Change Bursts as Defect Predictors. In Proceedings of the 21st IEEE International Symposium on Software Reliability Engineering, 2010. +
  • + + +
    +
    diff --git a/cpp/ql/src/Metrics/History/HNumberOfReCommits.ql b/cpp/ql/src/Metrics/History/HNumberOfReCommits.ql new file mode 100644 index 000000000000..de958ad32b5e --- /dev/null +++ b/cpp/ql/src/Metrics/History/HNumberOfReCommits.ql @@ -0,0 +1,30 @@ +/** + * @name Number of re-commits for each file + * @description A re-commit is taken to mean a commit to a file that + * was touched less than five days ago. + * @kind treemap + * @id cpp/historical-number-of-re-commits + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg min max + */ +import cpp +import external.VCS + +predicate inRange(Commit first, Commit second) { + first.getAnAffectedFile() = second.getAnAffectedFile() and + first != second and + exists(int n | n = first.getDate().daysTo(second.getDate()) and + n >= 0 and n < 5) +} + +int recommitsForFile(File f) { + result = count(Commit recommit | + f = recommit.getAnAffectedFile() and + exists(Commit prev | inRange(prev, recommit))) +} + +from File f +where exists(f.getMetrics().getNumberOfLinesOfCode()) +select f, recommitsForFile(f) + diff --git a/cpp/ql/src/Metrics/History/HNumberOfRecentAuthors.ql b/cpp/ql/src/Metrics/History/HNumberOfRecentAuthors.ql new file mode 100644 index 000000000000..c912897f4047 --- /dev/null +++ b/cpp/ql/src/Metrics/History/HNumberOfRecentAuthors.ql @@ -0,0 +1,22 @@ +/** + * @name Number of recent authors + * @description Number of distinct authors that have recently made + * changes. + * @kind treemap + * @id cpp/historical-number-of-recent-authors + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg min max + */ +import cpp +import external.VCS + +from File f +where exists(f.getMetrics().getNumberOfLinesOfCode()) +select f, count(Author author | + exists(Commit e | + e = author.getACommit() and + f = e.getAnAffectedFile() and + e.daysToNow() <= 180 and + not artificialChange(e))) + diff --git a/cpp/ql/src/Metrics/History/HNumberOfRecentChangedFiles.ql b/cpp/ql/src/Metrics/History/HNumberOfRecentChangedFiles.ql new file mode 100644 index 000000000000..8009356cde98 --- /dev/null +++ b/cpp/ql/src/Metrics/History/HNumberOfRecentChangedFiles.ql @@ -0,0 +1,20 @@ +/** + * @name Recently changed files + * @description Number of files recently edited. + * @kind treemap + * @id cpp/historical-number-of-recent-changed-files + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg min max sum + */ +import cpp +import external.VCS + +from File f +where exists(Commit e | + e.getAnAffectedFile() = f and + e.daysToNow() <= 180 and + not artificialChange(e)) + and exists(f.getMetrics().getNumberOfLinesOfCode()) +select f, 1 + diff --git a/cpp/ql/src/Metrics/History/HNumberOfRecentChanges.qhelp b/cpp/ql/src/Metrics/History/HNumberOfRecentChanges.qhelp new file mode 100644 index 000000000000..fa03a183d8e4 --- /dev/null +++ b/cpp/ql/src/Metrics/History/HNumberOfRecentChanges.qhelp @@ -0,0 +1,63 @@ + + + +

    +This metric measures the number of recent changes to files that have occurred +below this location in the tree. A recent change is taken to mean a change +that has occurred in the last 180 days. +

    + +

    +All code that has changed a great deal may be more than usually prone to +defects, but this is particularly true of code that has been changing +dramatically in the recent past, because it has not yet had a chance to be +properly field-tested in order to iron out the bugs. +

    + +
    + + +

    +There is more than one reason why a file may have been changing a lot +recently: +

    + +
      +
    • +The file may be part of a new subsystem that is being written. New code is +always going to change a lot in a short period of time, but it is important +to ensure that it is properly code reviewed and unit tested before integrating +it into a working product. +
    • + +
    • +The file may be being heavily refactored. Large refactorings are sometimes +essential, but they are also quite risky. You should write proper regression +tests before starting on a major refactoring, and check that they still pass +once you're done. +
    • + +
    • +The same bit of code may be being changed repeatedly because it is difficult +to get right. Aside from vigorous code reviewing and testing, it may be a good +idea to rethink the system design - if something is that hard +to get right (and it's not an inherently difficult concept), you might be making life unnecessarily hard for yourself and +risking introducing insidious defects. +
    • +
    + + + +
    + + + +
  • +N. Nagappan et al. Change Bursts as Defect Predictors. In Proceedings of the 21st IEEE International Symposium on Software Reliability Engineering, 2010. +
  • + + +
    +
    diff --git a/cpp/ql/src/Metrics/History/HNumberOfRecentChanges.ql b/cpp/ql/src/Metrics/History/HNumberOfRecentChanges.ql new file mode 100644 index 000000000000..2ce984f40a11 --- /dev/null +++ b/cpp/ql/src/Metrics/History/HNumberOfRecentChanges.ql @@ -0,0 +1,21 @@ +/** + * @name Recent changes + * @description Number of recent commits to this file. + * @kind treemap + * @id cpp/historical-number-of-recent-changes + * @treemap.warnOn highValues + * @metricType file + * @metricAggregate avg min max sum + */ +import cpp +import external.VCS + +from File f, int n +where n = count(Commit e | + e.getAnAffectedFile() = f and + e.daysToNow() <= 180 and + not artificialChange(e)) + and exists(f.getMetrics().getNumberOfLinesOfCode()) +select f, n +order by n desc + diff --git a/cpp/ql/src/Metrics/Internal/CallableDisplayStrings.ql b/cpp/ql/src/Metrics/Internal/CallableDisplayStrings.ql new file mode 100644 index 000000000000..d02e7f1165dc --- /dev/null +++ b/cpp/ql/src/Metrics/Internal/CallableDisplayStrings.ql @@ -0,0 +1,11 @@ +/** + * @name Display strings of functions + * @kind display-string + * @id cpp/callable-display-strings + * @metricType callable + */ +import cpp + +from Function f +where f.fromSource() +select f, f.getName() diff --git a/cpp/ql/src/Metrics/Internal/CallableExtents.ql b/cpp/ql/src/Metrics/Internal/CallableExtents.ql new file mode 100644 index 000000000000..b8ac8803734f --- /dev/null +++ b/cpp/ql/src/Metrics/Internal/CallableExtents.ql @@ -0,0 +1,34 @@ +/** + * @name Extents of functions + * @kind extent + * @id cpp/callable-extents + * @metricType callable + */ +import cpp + +/** + * A Function with location overridden to cover its entire range, + * including the body (if any), as opposed to the location of its name + * only. + */ +class RangeFunction extends Function { + /** + * Holds if this function is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [LGTM locations](https://lgtm.com/help/ql/locations). + */ + predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { + super.getLocation().hasLocationInfo(path, sl, sc, _, _) + and + ( + this.getBlock().getLocation().hasLocationInfo(path, _, _, el, ec) or + (not exists(this.getBlock()) and el = sl+1 and ec=1) + ) + } +} + +from RangeFunction f +where f.fromSource() +select f.getLocation(), f diff --git a/cpp/ql/src/Metrics/Internal/CallableSourceLinks.ql b/cpp/ql/src/Metrics/Internal/CallableSourceLinks.ql new file mode 100644 index 000000000000..f6d1667b0192 --- /dev/null +++ b/cpp/ql/src/Metrics/Internal/CallableSourceLinks.ql @@ -0,0 +1,11 @@ +/** + * @name Source links of functions + * @kind source-link + * @id cpp/callable-source-links + * @metricType callable + */ +import cpp + +from Function f +where f.fromSource() +select f, f.getFile() diff --git a/cpp/ql/src/Metrics/Internal/ReftypeDisplayStrings.ql b/cpp/ql/src/Metrics/Internal/ReftypeDisplayStrings.ql new file mode 100644 index 000000000000..e11fdda6e628 --- /dev/null +++ b/cpp/ql/src/Metrics/Internal/ReftypeDisplayStrings.ql @@ -0,0 +1,11 @@ +/** + * @name Display strings of classes + * @kind display-string + * @id cpp/reference-type-display-strings + * @metricType reftype + */ +import cpp + +from Class c +where c.fromSource() +select c, c.getName() diff --git a/cpp/ql/src/Metrics/Internal/ReftypeSourceLinks.ql b/cpp/ql/src/Metrics/Internal/ReftypeSourceLinks.ql new file mode 100644 index 000000000000..d5918eaeb1f7 --- /dev/null +++ b/cpp/ql/src/Metrics/Internal/ReftypeSourceLinks.ql @@ -0,0 +1,11 @@ +/** + * @name Source links of classes + * @kind source-link + * @id cpp/reference-type-source-links + * @metricType reftype + */ +import cpp + +from Class c +where c.fromSource() +select c, c.getFile() diff --git a/cpp/ql/src/Metrics/Namespaces/AbstractNamespaces.qhelp b/cpp/ql/src/Metrics/Namespaces/AbstractNamespaces.qhelp new file mode 100644 index 000000000000..41cc622b84e0 --- /dev/null +++ b/cpp/ql/src/Metrics/Namespaces/AbstractNamespaces.qhelp @@ -0,0 +1,29 @@ + + + + + +

    This query finds namespaces that have an abstractness greater than 0.20.

    + +

    Abstractness measures the proportion of abstract types in + a package relative to the total number of types in that package. + A highly abstract package (where the metric value is close 1) + that is furthermore instable is likely to be useless: the + class hierarchy has been over-engineered, and all those + abstract types are not heavily used.

    + +
    +
    +

    Try to simplify the class hierarchy.

    + + + + + +
    + + + +
    diff --git a/cpp/ql/src/Metrics/Namespaces/AbstractNamespaces.ql b/cpp/ql/src/Metrics/Namespaces/AbstractNamespaces.ql new file mode 100644 index 000000000000..a39e7dcb76a0 --- /dev/null +++ b/cpp/ql/src/Metrics/Namespaces/AbstractNamespaces.ql @@ -0,0 +1,15 @@ +/** + * @name Abstract namespaces + * @description Finds namespaces that have an abstractness greater than 0.20. + * @kind chart + * @id cpp/abstract-namespaces + * @chart.type bar + */ +import cpp + +from Namespace n, float c +where n.fromSource() + and c = n.getMetrics().getAbstractness() + and c > 0.20 +select n as Namespace, c as Abstractness +order by Abstractness desc diff --git a/cpp/ql/src/Metrics/Namespaces/ConcreteNamespaces.qhelp b/cpp/ql/src/Metrics/Namespaces/ConcreteNamespaces.qhelp new file mode 100644 index 000000000000..24cf0f06eb72 --- /dev/null +++ b/cpp/ql/src/Metrics/Namespaces/ConcreteNamespaces.qhelp @@ -0,0 +1,22 @@ + + + + + +

    This query finds namespaces that have an abstractness equal to 0. These namesspaces do not contain any abstract classes.

    + +
    +
    +

    + + + + + +

    + + + +
    diff --git a/cpp/ql/src/Metrics/Namespaces/ConcreteNamespaces.ql b/cpp/ql/src/Metrics/Namespaces/ConcreteNamespaces.ql new file mode 100644 index 000000000000..5cf91aa490c8 --- /dev/null +++ b/cpp/ql/src/Metrics/Namespaces/ConcreteNamespaces.ql @@ -0,0 +1,14 @@ +/** + * @name Concrete namespaces + * @description Finds namespaces that have an abstractness equal to 0. + * @kind tree + * @id cpp/concrete-namespaces + */ +import cpp + +from Namespace n, float c +where n.fromSource() + and c = n.getMetrics().getAbstractness() + and c = 0 +select n as Namespace, c as Abstractness +order by Abstractness desc diff --git a/cpp/ql/src/Metrics/Namespaces/HighAfferentCouplingNamespaces.qhelp b/cpp/ql/src/Metrics/Namespaces/HighAfferentCouplingNamespaces.qhelp new file mode 100644 index 000000000000..15b284d875e6 --- /dev/null +++ b/cpp/ql/src/Metrics/Namespaces/HighAfferentCouplingNamespaces.qhelp @@ -0,0 +1,25 @@ + + + + + +

    This query finds namespaces that have an afferent coupling greater than 20.

    + +

    The query counts number of namespaces depending on a namespace and +displays those that have more than 20 namespaces depending on them.

    + +
    +
    +

    + + + + + +

    + + + +
    diff --git a/cpp/ql/src/Metrics/Namespaces/HighAfferentCouplingNamespaces.ql b/cpp/ql/src/Metrics/Namespaces/HighAfferentCouplingNamespaces.ql new file mode 100644 index 000000000000..2c0db9c7e441 --- /dev/null +++ b/cpp/ql/src/Metrics/Namespaces/HighAfferentCouplingNamespaces.ql @@ -0,0 +1,16 @@ +/** + * @name High afferent coupling namespaces + * @description Finds namespaces that have an afferent coupling greater + * than 20. + * @kind chart + * @id cpp/high-afferent-coupling-namespaces + * @chart.type bar + */ +import cpp + +from Namespace n, int c +where n.fromSource() + and c = n.getMetrics().getAfferentCoupling() + and c > 20 +select n as Namespace, c as AfferentCoupling +order by AfferentCoupling desc diff --git a/cpp/ql/src/Metrics/Namespaces/HighDistanceFromMainLineNamespaces.qhelp b/cpp/ql/src/Metrics/Namespaces/HighDistanceFromMainLineNamespaces.qhelp new file mode 100644 index 000000000000..fb067f485693 --- /dev/null +++ b/cpp/ql/src/Metrics/Namespaces/HighDistanceFromMainLineNamespaces.qhelp @@ -0,0 +1,29 @@ + + + + + +

    This query finds namespaces that do not have a good balance between abstractness and stability.

    + +

    This measure intends to capture the trade-off between abstractness + and instability: the ideal situation occurs when the sum of + abstractness and instability is one. That is, a package is + completely abstract and stable (abstractness=1 and instability=0) + or it is concrete and instable (abstractness=0 and instability=1). + We thus measure the distance from that ideal situation.

    + +
    +
    +

    + + + + + +

    + + + +
    diff --git a/cpp/ql/src/Metrics/Namespaces/HighDistanceFromMainLineNamespaces.ql b/cpp/ql/src/Metrics/Namespaces/HighDistanceFromMainLineNamespaces.ql new file mode 100644 index 000000000000..0fdb40a7e7ad --- /dev/null +++ b/cpp/ql/src/Metrics/Namespaces/HighDistanceFromMainLineNamespaces.ql @@ -0,0 +1,16 @@ +/** + * @name Namespaces far from main line + * @description Finds namespaces that do not have a good balance between + * abstractness and stability. + * @kind chart + * @id cpp/high-distance-from-main-line-namespaces + * @chart.type bar + */ +import cpp + +from Namespace n, float c +where n.fromSource() + and c = n.getMetrics().getDistanceFromMain() + and c > 0.7 +select n as Namespace, c as DistanceFromMainline +order by DistanceFromMainline desc diff --git a/cpp/ql/src/Metrics/Namespaces/HighEfferentCouplingNamespaces.qhelp b/cpp/ql/src/Metrics/Namespaces/HighEfferentCouplingNamespaces.qhelp new file mode 100644 index 000000000000..d97373f689a9 --- /dev/null +++ b/cpp/ql/src/Metrics/Namespaces/HighEfferentCouplingNamespaces.qhelp @@ -0,0 +1,22 @@ + + + + + +

    This query finds namespaces that have an efferent coupling (that is, the number of namespaces they depend on) greater than 20.

    + +
    +
    +

    + + + + + +

    + + + +
    diff --git a/cpp/ql/src/Metrics/Namespaces/HighEfferentCouplingNamespaces.ql b/cpp/ql/src/Metrics/Namespaces/HighEfferentCouplingNamespaces.ql new file mode 100644 index 000000000000..fb5d8614b4f5 --- /dev/null +++ b/cpp/ql/src/Metrics/Namespaces/HighEfferentCouplingNamespaces.ql @@ -0,0 +1,15 @@ +/** + * @name High efferent coupling namespaces + * @description Finds namespaces that have an efferent coupling greater than 20. + * @kind chart + * @id cpp/high-efferent-coupling-namespaces + * @chart.type bar + */ +import cpp + +from Namespace n, int c +where n.fromSource() + and c = n.getMetrics().getEfferentCoupling() + and c > 20 +select n as Namespace, c as EfferentCoupling +order by EfferentCoupling desc diff --git a/cpp/ql/src/Metrics/Namespaces/StableNamespaces.qhelp b/cpp/ql/src/Metrics/Namespaces/StableNamespaces.qhelp new file mode 100644 index 000000000000..6d3c1bb70673 --- /dev/null +++ b/cpp/ql/src/Metrics/Namespaces/StableNamespaces.qhelp @@ -0,0 +1,28 @@ + + + + + +

    This query finds namespaces that have an instability lower than 0.2.

    + +

    Instability is a measure of how likely a package is to be influenced + by changes to other packages. If this metric value is high, it is easily + influenced, if it is low, the impact is likely to be minimal. Instability + is estimated as the number of outgoing dependencies relative to the total + number of depencies.

    + +
    +
    +

    + + + + + +

    + + + +
    diff --git a/cpp/ql/src/Metrics/Namespaces/StableNamespaces.ql b/cpp/ql/src/Metrics/Namespaces/StableNamespaces.ql new file mode 100644 index 000000000000..060eb31e5a65 --- /dev/null +++ b/cpp/ql/src/Metrics/Namespaces/StableNamespaces.ql @@ -0,0 +1,15 @@ +/** + * @name Stable namespaces + * @description Finds namespaces that have an instability lower than 0.2. + * @kind chart + * @id cpp/stable-namespaces + * @chart.type bar + */ +import cpp + +from Namespace n, float c +where n.fromSource() + and c = n.getMetrics().getInstability() + and c < 0.2 +select n as Namespace, c as Instability +order by Instability desc diff --git a/cpp/ql/src/Metrics/Namespaces/UnstableNamespaces.qhelp b/cpp/ql/src/Metrics/Namespaces/UnstableNamespaces.qhelp new file mode 100644 index 000000000000..45377a202e4a --- /dev/null +++ b/cpp/ql/src/Metrics/Namespaces/UnstableNamespaces.qhelp @@ -0,0 +1,28 @@ + + + + + +

    This query finds namespaces that have an instability higher than 0.8.

    + +

    Instability is a measure of how likely a package is to be influenced + by changes to other packages. If this metric value is high, it is easily + influenced, if it is low, the impact is likely to be minimal. Instability + is estimated as the number of outgoing dependencies relative to the total + number of depencies.

    + +
    +
    +

    + + + + + +

    + + + +
    diff --git a/cpp/ql/src/Metrics/Namespaces/UnstableNamespaces.ql b/cpp/ql/src/Metrics/Namespaces/UnstableNamespaces.ql new file mode 100644 index 000000000000..4279dde9e7a5 --- /dev/null +++ b/cpp/ql/src/Metrics/Namespaces/UnstableNamespaces.ql @@ -0,0 +1,15 @@ +/** + * @name Unstable namespaces + * @description Finds namespaces that have an instability higher than 0.8. + * @kind chart + * @id cpp/unstable-namespaces + * @chart.type bar + */ +import cpp + +from Namespace n, float c +where n.fromSource() + and c = n.getMetrics().getInstability() + and c > 0.8 +select n as Package, c as Instability +order by Instability desc diff --git a/cpp/ql/src/Metrics/queries.xml b/cpp/ql/src/Metrics/queries.xml new file mode 100644 index 000000000000..99f4a7278c21 --- /dev/null +++ b/cpp/ql/src/Metrics/queries.xml @@ -0,0 +1 @@ + diff --git a/cpp/ql/src/Microsoft/CallWithNullSAL.ql b/cpp/ql/src/Microsoft/CallWithNullSAL.ql new file mode 100644 index 000000000000..a3ba5fd15e0e --- /dev/null +++ b/cpp/ql/src/Microsoft/CallWithNullSAL.ql @@ -0,0 +1,21 @@ +/** + * @name SAL requires non-null argument + * @description When null is passed to a function that is SAL-annotated to + * forbid this, undefined behavior may result. + * @kind problem + * @id cpp/call-with-null-sal + * @problem.severity warning + * @tags reliability + */ +import cpp +import SAL + +from Parameter p, Call c, Expr arg +where any(SALNotNull a).getDeclaration() = p + and c.getTarget() = p.getFunction() + and arg = c.getArgument(p.getIndex()) + and nullValue(arg) +select arg, "Argument ("+ arg.toString() + +") for parameter $@ in call to "+ c.getTarget().getName() + + " may be null, but a SAL annotation forbids this.", + p, p.getName() diff --git a/cpp/ql/src/Microsoft/IgnoreReturnValueSAL.ql b/cpp/ql/src/Microsoft/IgnoreReturnValueSAL.ql new file mode 100644 index 000000000000..b8b06b9146c5 --- /dev/null +++ b/cpp/ql/src/Microsoft/IgnoreReturnValueSAL.ql @@ -0,0 +1,19 @@ +/** + * @name SAL requires inspecting return value + * @description When a return value is discarded even though the SAL annotation + * requires inspecting it, a recoverable error may turn into a + * whole-program crash. + * @kind problem + * @id cpp/ignore-return-value-sal + * @problem.severity warning + * @tags reliability + */ +import SAL + +from Function f, FunctionCall call +where call.getTarget() = f + and call instanceof ExprInVoidContext + and any(SALCheckReturn a).getDeclaration() = f + and not getOptions().okToIgnoreReturnValue(call) +select call, "Return value of $@ discarded although a SAL annotation "+ + "requires inspecting it.", f, f.getName() diff --git a/cpp/ql/src/Microsoft/InconsistentSAL.ql b/cpp/ql/src/Microsoft/InconsistentSAL.ql new file mode 100644 index 000000000000..081c7439adba --- /dev/null +++ b/cpp/ql/src/Microsoft/InconsistentSAL.ql @@ -0,0 +1,40 @@ +/** + * @name Inconsistent SAL annotation + * @description Annotations are different between declaration and definition. + * @kind problem + * @id cpp/inconsistent-sal + * @problem.severity warning + * @tags reliability + */ +import SAL + +/** Holds if `e` has SAL annotation `name`. */ +predicate hasAnnotation(DeclarationEntry e, string name) { + exists(SALAnnotation a | + a.getMacro().getName() = name and + a.getDeclarationEntry() = e + ) +} + +/** Holds if `e` is annotated to take its annotation from its declaration. */ +predicate inheritsDeclAnnotations(DeclarationEntry e) { + // Is directly annotated + e.isDefinition() and + exists(SALAnnotation a | a.getMacro().getName() = "_Use_decl_annotations_" | + a.getDeclarationEntry() = e + ) + or + // or is a parameter of a function with such an annotation + inheritsDeclAnnotations(e.(ParameterDeclarationEntry) + .getFunctionDeclarationEntry()) +} + +from DeclarationEntry e1, DeclarationEntry e2, string name +where e1.getDeclaration() = e2.getDeclaration() + and hasAnnotation(e1, name) + and not hasAnnotation(e2, name) + and not name = "_Use_decl_annotations_" + and not inheritsDeclAnnotations(e2) +select e2, "Missing SAL annotation "+ name + + " in "+ e2.toString() +" although it is present on $@.", + e1, e1.toString() diff --git a/cpp/ql/src/Microsoft/SAL.qll b/cpp/ql/src/Microsoft/SAL.qll new file mode 100644 index 000000000000..42b86fe0caf5 --- /dev/null +++ b/cpp/ql/src/Microsoft/SAL.qll @@ -0,0 +1,105 @@ +import cpp + +class SALMacro extends Macro { + SALMacro() { + this.getFile().getBaseName() = "sal.h" or + this.getFile().getBaseName() = "specstrings_strict.h" or + this.getFile().getBaseName() = "specstrings.h" + } +} + +class SALAnnotation extends MacroInvocation { + SALAnnotation() { + this.getMacro() instanceof SALMacro and + not exists(this.getParentInvocation()) + } + + /** Returns the `Declaration` annotated by `this`. */ + Declaration getDeclaration() { + annotatesAt(this, result.getADeclarationEntry(), _, _) + } + + /** Returns the `DeclarationEntry` annotated by `this`. */ + DeclarationEntry getDeclarationEntry() { + annotatesAt(this, result, _, _) + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Particular SAL annotations of interest + +class SALCheckReturn extends SALAnnotation { + SALCheckReturn() { + exists(SALMacro m | m = this.getMacro() | + m.getName() = "_Check_return_" or + m.getName() = "_Must_inspect_result_" + ) + } +} + +class SALNotNull extends SALAnnotation { + SALNotNull() { + exists(SALMacro m | m = this.getMacro() | + not m.getName().matches("%\\_opt\\_%") and + ( + m.getName().matches("\\_In%") or + m.getName().matches("\\_Out%") or + m.getName() = "_Ret_notnull_" + ) + ) + and + exists(Type t + | t = this.getDeclaration().(Variable).getType() or + t = this.getDeclaration().(Function).getType() + | t.getUnspecifiedType() instanceof PointerType + ) + } +} + +class SALMaybeNull extends SALAnnotation { + SALMaybeNull() { + exists(SALMacro m | m = this.getMacro() | + m.getName().matches("%\\_opt\\_%") or + m.getName().matches("\\_Ret_maybenull\\_%") or + m.getName() = "_Result_nullonfailure_" + ) + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Implementation details + +private predicate annotatesAt(SALAnnotation a, DeclarationEntry e, + File file, int idx) { + a = salElementAt(file, idx) and + ( + // Base case: `a` right before `e` + e = salElementAt(file, idx + 1) + or + // Recursive case: `a` right before some annotation on `e` + annotatesAt(_, e, file, idx + 1) + ) +} + +library class SALElement extends Element { + SALElement() { + this instanceof DeclarationEntry or + this instanceof SALAnnotation + } +} + +/** Gets the `idx`th `SALElement` in `file`. */ +private SALElement salElementAt(File file, int idx) { + interestingLoc(file, result, interestingStartPos(file, idx)) +} + +/** Holds if an SALElement element at character `result` comes at + * position `idx` in `file`. */ +private int interestingStartPos(File file, int idx) { + result = rank[idx](int otherStart | interestingLoc(file, _, otherStart)) +} + +/** Holds if `element` in `file` is at character `startPos`. */ +private predicate interestingLoc(File file, SALElement element, int startPos) { + element.getLocation().charLoc(file, startPos, _) +} diff --git a/cpp/ql/src/Options.qll b/cpp/ql/src/Options.qll new file mode 100644 index 000000000000..bc56cf3488d8 --- /dev/null +++ b/cpp/ql/src/Options.qll @@ -0,0 +1,185 @@ +/** + * Provides custom predicates that specify information about + * the behavior of the program being analyzed. + * + * By default they fall back to the reasonable defaults provided in + * `DefaultOptions.qll`, but by modifying this file, you can customize + * the standard Semmle analyses to give better results for your project. + */ + +import cpp + +/** + * Customizable predicates that specify information about + * the behavior of the program being analyzed. + */ +class CustomOptions extends Options +{ + /** + * Holds if we wish to override the "may return NULL" inference for this + * call. If this holds, then rather than trying to infer whether this + * call might return NULL, we will instead test whether `returnsNull` + * holds for it. + * + * By default, this handles the `Xstrdup` function used by the CVS + * project. + */ + override predicate overrideReturnsNull(Call call) { + Options.super.overrideReturnsNull(call) + } + + /** + * Holds if this call may return NULL. This is only used if + * `overrideReturnsNull` holds for the call. + * + * By default, this handles the `Xstrdup` function used by the CVS + * project. + */ + override predicate returnsNull(Call call) { + Options.super.returnsNull(call) + } + + /** + * Holds if a call to this function will never return. + * + * By default, this holds for `exit`, `_exit`, `abort`, `__assert_fail`, + * `longjmp`, `error`, `__builtin_unreachable` and any function with a + * `noreturn` attribute. + */ + override predicate exits(Function f) { + Options.super.exits(f) + } + + /** + * Holds if evaluating expression `e` will never return, or can be assumed + * to never return. For example: + * ``` + * __assume(0); + * ``` + * (note that in this case if the hint is wrong and the expression is reached at + * runtime, the program's behaviour is undefined) + */ + override predicate exprExits(Expr e) { + Options.super.exprExits(e) + } + + /** + * Holds if function `f` should always have its return value checked. + * + * By default holds only for `fgets`. + */ + override predicate alwaysCheckReturnValue(Function f) { + Options.super.alwaysCheckReturnValue(f) + } + + /** + * Holds if it is reasonable to ignore the return value of function + * call `fc`. + * + * By default holds for calls to `select` where the first argument is + * `0` (commonly used as a way of sleeping), and any call inside a + * macro expansion. + */ + override predicate okToIgnoreReturnValue(FunctionCall fc) { + Options.super.okToIgnoreReturnValue(fc) + } +} + +/** + * A type that acts as a mutex. + */ +class CustomMutexType extends MutexType { + CustomMutexType() { + none() + } + + /** + * Holds if `fc` is a call that always locks mutex `arg` + * of this type. + */ + override predicate mustlockAccess(FunctionCall fc, Expr arg) { + none() + } + + /** + * Holds if `fc` is a call that tries to lock mutex `arg` + * of this type, but may return without success. + */ + override predicate trylockAccess(FunctionCall fc, Expr arg) { + none() + } + + /** + * Holds if `fc` is a call that unlocks mutex `arg` + * of this type. + */ + override predicate unlockAccess(FunctionCall fc, Expr arg) { + none() + } +} + +/** + * DEPRECATED: customize `CustomOptions.overrideReturnsNull` instead. + * + * This predicate is required to support backwards compatibility for + * older `Options.qll` files. It should not be removed or modified by + * end users. + */ +predicate overrideReturnsNull(Call call) { + none() +} + +/** + * DEPRECATED: customize `CustomOptions.returnsNull` instead. + * + * This predicate is required to support backwards compatibility for + * older `Options.qll` files. It should not be removed or modified by + * end users. + */ +predicate returnsNull(Call call) { + none() +} + +/** + * DEPRECATED: customize `CustomOptions.exits` instead. + * + * This predicate is required to support backwards compatibility for + * older `Options.qll` files. It should not be removed or modified by + * end users. + */ +predicate exits(Function f) { + none() +} + +/** + * DEPRECATED: customize `CustomOptions.exprExits` instead. + * + * This predicate is required to support backwards compatibility for + * older `Options.qll` files. It should not be removed or modified by + * end users. + */ +predicate exprExits(Expr e) { + none() +} + +/** + * DEPRECATED: customize `CustomOptions.alwaysCheckReturnValue` instead. + * + * This predicate is required to support backwards compatibility for + * older `Options.qll` files. It should not be removed or modified by + * end users. + */ +predicate alwaysCheckReturnValue(Function f) { + none() +} + +/** + * DEPRECATED: customize `CustomOptions.okToIgnoreReturnValue` instead. + * + * This predicate is required to support backwards compatibility for + * older `Options.qll` files. It should not be removed or modified by + * end users. + */ +predicate okToIgnoreReturnValue(FunctionCall fc) { + none() +} diff --git a/cpp/ql/src/PointsTo/Debug.ql b/cpp/ql/src/PointsTo/Debug.ql new file mode 100644 index 000000000000..b3de13751f9b --- /dev/null +++ b/cpp/ql/src/PointsTo/Debug.ql @@ -0,0 +1,17 @@ +/** + * @name Debug - find out what a particular function-pointer points to + * @description Query to help investigate mysterious results with ReturnStackAllocatedObject + * @kind table + * @id cpp/points-to/debug + */ +import cpp +import semmle.code.cpp.pointsto.PointsTo + +class FieldAccessPT extends PointsToExpr +{ override predicate interesting() { this instanceof FieldAccess } } + +from Function outer, FieldAccessPT fa +where outer.hasName("rtLnDeliverableMayContainDividends") + and fa.(FieldAccess).getTarget().hasName("pfFunction") + and fa.getEnclosingFunction() = outer +select fa, fa.pointsTo() diff --git a/cpp/ql/src/PointsTo/PreparedStagedPointsTo.ql b/cpp/ql/src/PointsTo/PreparedStagedPointsTo.ql new file mode 100644 index 000000000000..42ff979233ca --- /dev/null +++ b/cpp/ql/src/PointsTo/PreparedStagedPointsTo.ql @@ -0,0 +1,12 @@ +/** + * @name PrepareStagedPointsTo + * @description Query to force evaluation of staged points-to predicates + * @kind table + * @id cpp/points-to/prepared-staged-points-to + */ + + import semmle.code.cpp.pointsto.PointsTo + + select count(int set, Element location | setlocations(set, location)), + count(int set, Element element | pointstosets(set, element)) + diff --git a/cpp/ql/src/PointsTo/Stats.ql b/cpp/ql/src/PointsTo/Stats.ql new file mode 100644 index 000000000000..c77793ec5f23 --- /dev/null +++ b/cpp/ql/src/PointsTo/Stats.ql @@ -0,0 +1,20 @@ +/** + * @name Flow Statistics + * @description Count the number points-to sets with 0 or 1 incoming flow edges, and the total number of points-to sets + * @kind table + * @id cpp/points-to/stats + */ +import cpp +import semmle.code.cpp.pointsto.PointsTo + +predicate inc(int set, int cnt) +{ + (setflow(set,_) or setflow(_, set)) and + cnt = count(int i | setflow(i, set) and i != set) +} + +select + count(int set | inc(set, _)) as total, + count(int set | inc(set, 0)) as nullary, + count(int set | inc(set, 1)) as unary, + total - nullary - unary as rest diff --git a/cpp/ql/src/PointsTo/TaintedFormatStrings.ql b/cpp/ql/src/PointsTo/TaintedFormatStrings.ql new file mode 100644 index 000000000000..bb9977d46f5f --- /dev/null +++ b/cpp/ql/src/PointsTo/TaintedFormatStrings.ql @@ -0,0 +1,89 @@ +/** + * @name Taint test + * @kind table + * @id cpp/points-to/tainted-format-strings + */ +import cpp +import semmle.code.cpp.pointsto.PointsTo +import semmle.code.cpp.pointsto.CallGraph + +predicate inputArgument(string function, int arg) +{ + (function = "read" and arg = 1) or + (function = "fread" and arg = 0) or + (function = "fgets" and arg = 0) + // ... add more +} + +predicate inputBuffer(Expr e) { + exists(FunctionCall fc, string fname, int i | + fc.getTarget().getName() = fname and + inputArgument(fname, i) and + e = fc.getArgument(i)) +} + +class InputBuffer extends PointsToExpr +{ + InputBuffer() { inputBuffer(this) } + override predicate interesting() { inputBuffer(this) } +} + +predicate formatArgument(string function, int i) +{ + (function = "printf" and i = 0) or + (function = "fprintf" and i = 1) or + (function = "sprintf" and i = 1) or + (function = "snprintf" and i = 2) or + (function = "d_printf" and i = 0) or + (function = "talloc_asprintf" and i = 1) or + (function = "fstr_sprintf" and i = 1) or + (function = "talloc_asprintf_append" and i = 1) or + (function = "d_fprintf" and i = 1) or + (function = "asprintf" and i = 1) or + (function = "talloc_asprintf_append_buffer" and i = 1) or + (function = "fdprintf" and i = 1) or + (function = "d_vfprintf" and i = 1) or + (function = "smb_xvasprintf" and i = 1) or + (function = "asprintf_strupper_m" and i = 1) or + (function = "talloc_asprintf_strupper_m" and i = 1) or + (function = "sprintf_append" and i = 4) or + (function = "x_vfprintf" and i = 1) or + (function = "x_fprintf" and i = 1) or + (function = "vasprintf" and i = 1) or + (function = "ldb_asprintf_errstring" and i = 1) or + (function = "talloc_vasprintf" and i = 1) or + (function = "talloc_vasprintf" and i = 1) or + (function = "fprintf_file" and i = 1) or + (function = "vsnprintf" and i = 2) or + (function = "talloc_vasprintf_append" and i = 1) or + (function = "__talloc_vaslenprintf_append" and i = 2) or + (function = "talloc_vasprintf_append_buffer" and i = 1) or + (function = "fprintf_attr" and i = 2) or + (function = "vprintf" and i = 0) or + (function = "vsprintf" and i = 1) +} + +predicate formatBuffer(Expr e) +{ + exists(FunctionCall fc, string fname, int i | + fc.getTarget().getName() = fname and + formatArgument(fname, i) and + fc.getArgument(i) = e + ) +} + +class FormatBuffer extends PointsToExpr +{ + FormatBuffer() { formatBuffer(this) } + override predicate interesting() { formatBuffer(this) } +} + +predicate potentialViolation(InputBuffer source, FormatBuffer dest) +{ + source.pointsTo() = dest.pointsTo() and + not exists(FunctionCall fc | fc = dest and fc.getTarget().hasName("lang_msg_rotate") and fc.getArgument(1) instanceof Literal) +} + +from InputBuffer source, FormatBuffer dest +where potentialViolation(source, dest) +select dest.getFile() as File, dest as FormatString diff --git a/cpp/ql/src/Power of 10/Rule 1/UseOfGoto.c b/cpp/ql/src/Power of 10/Rule 1/UseOfGoto.c new file mode 100644 index 000000000000..ce9eb2aab3df --- /dev/null +++ b/cpp/ql/src/Power of 10/Rule 1/UseOfGoto.c @@ -0,0 +1,9 @@ +int matching; int i; +for (i = 0; i < numItems; i++) { + if (items[i] == item) { + matching = i; + goto found; // Problematic 'goto': Use 'break' instead. + } +} +found: +// ... use 'matching' ... diff --git a/cpp/ql/src/Power of 10/Rule 1/UseOfGoto.qhelp b/cpp/ql/src/Power of 10/Rule 1/UseOfGoto.qhelp new file mode 100644 index 000000000000..b58bfe2c3037 --- /dev/null +++ b/cpp/ql/src/Power of 10/Rule 1/UseOfGoto.qhelp @@ -0,0 +1,27 @@ + + + + + +

    Code should be restricted to very simple control flow constructs; +in particular, the goto statement should not be used. +Simpler control flow translates into stronger capabilities for +analysis and often results in improved code clarity.

    + +
    + +

    Usually it is clearer to re-write uses of the goto statement +in terms of the more structured control-flow constructs like +if/else, switch statements or loops in +conjunction with break or continue.

    + +
    + + + + + + +
    diff --git a/cpp/ql/src/Power of 10/Rule 1/UseOfGoto.ql b/cpp/ql/src/Power of 10/Rule 1/UseOfGoto.ql new file mode 100644 index 000000000000..bd2b9311af62 --- /dev/null +++ b/cpp/ql/src/Power of 10/Rule 1/UseOfGoto.ql @@ -0,0 +1,13 @@ +/** + * @name Use of goto + * @description Using the goto statement complicates function control flow and hinders program understanding. + * @kind problem + * @id cpp/power-of-10/use-of-goto + * @problem.severity warning + * @tags maintainability + */ + +import cpp + +from GotoStmt goto +select goto, "The goto statement should not be used." diff --git a/cpp/ql/src/Power of 10/Rule 1/UseOfJmp.ql b/cpp/ql/src/Power of 10/Rule 1/UseOfJmp.ql new file mode 100644 index 000000000000..18b739f792ba --- /dev/null +++ b/cpp/ql/src/Power of 10/Rule 1/UseOfJmp.ql @@ -0,0 +1,21 @@ +/** + * @name Use of setjmp or longjmp + * @description Using the setjmp and longjmp functions complicates control flow and hinders program understanding. + * @kind problem + * @id cpp/power-of-10/use-of-jmp + * @problem.severity warning + */ + +import cpp + +class ForbiddenFunction extends Function { + ForbiddenFunction() { + exists(string name | name = this.getName() | + name = "setjmp" or name = "longjmp" or + name = "sigsetjmp" or name = "siglongjmp") + } +} + +from FunctionCall call +where call.getTarget() instanceof ForbiddenFunction +select call, "The " + call.getTarget().getName() + " function should not be used." diff --git a/cpp/ql/src/Power of 10/Rule 1/UseOfRecursion.ql b/cpp/ql/src/Power of 10/Rule 1/UseOfRecursion.ql new file mode 100644 index 000000000000..24ccbdfae9c7 --- /dev/null +++ b/cpp/ql/src/Power of 10/Rule 1/UseOfRecursion.ql @@ -0,0 +1,23 @@ +/** + * @name Use of recursion + * @description Recursion makes the program call graph cyclic and hinders + * program understanding. + * @kind problem + * @id cpp/power-of-10/use-of-recursion + * @problem.severity warning + */ + +import cpp + +class RecursiveCall extends FunctionCall { + RecursiveCall() { + this.getTarget().calls*(this.getEnclosingFunction()) + } +} + +from RecursiveCall call, string msg +where if (call.getTarget() = call.getEnclosingFunction()) then + msg = "This call directly invokes its containing function $@." + else + msg = "The function " + call.getEnclosingFunction() + " is indirectly recursive via this call to $@." +select call, msg, call.getTarget(), call.getTarget().getName() diff --git a/cpp/ql/src/Power of 10/Rule 2/BoundedLoopIterations.ql b/cpp/ql/src/Power of 10/Rule 2/BoundedLoopIterations.ql new file mode 100644 index 000000000000..7a34b4d9e34d --- /dev/null +++ b/cpp/ql/src/Power of 10/Rule 2/BoundedLoopIterations.ql @@ -0,0 +1,116 @@ +/** + * @name Unbounded loop + * @description All loops should have a fixed upper bound; the counter should also be incremented along all paths within the loop. + This check excludes loops that are meant to be nonterminating (like schedulers). + * @kind problem + * @id cpp/power-of-10/bounded-loop-iterations + * @problem.severity warning + */ + +import cpp + +predicate validVarForBound(Loop loop, Variable var) { + // The variable is read in the loop controlling expression + var.getAnAccess().getParent*() = loop.getControllingExpr() and + // The variable is not assigned in the loop body + not inScope(loop, var.getAnAssignment().getEnclosingStmt()) and + // The variable is not incremented/decremented in the loop body + not inScope(loop, var.getAnAccess().getParent().(CrementOperation).getEnclosingStmt()) +} + +predicate upperBoundCheck(Loop loop, VariableAccess checked) { + exists(RelationalOperation rop | loop.getControllingExpr().getAChild*() = rop | + checked = rop.getLesserOperand() and + // The RHS is something "valid", i.e. a constant or + // a variable that isn't assigned in the loop body + ( + exists(rop.getGreaterOperand().getValue()) or + rop.getGreaterOperand().(VariableAccess).getTarget().isConst() or + validVarForBound(loop, rop.getGreaterOperand().(VariableAccess).getTarget()) + ) and + not rop.getGreaterOperand() instanceof CharLiteral) +} + +predicate lowerBoundCheck(Loop loop, VariableAccess checked) { + exists(RelationalOperation rop | loop.getControllingExpr().getAChild*() = rop | + checked = rop.getGreaterOperand() and + // The RHS is something "valid", i.e. a constant or + // a variable that isn't assigned in the loop body + ( + exists(rop.getLesserOperand().getValue()) or + rop.getLesserOperand().(VariableAccess).getTarget().isConst() or + validVarForBound(loop, rop.getLesserOperand().(VariableAccess).getTarget()) + ) and + not rop.getLesserOperand() instanceof CharLiteral) +} + +VariableAccess getAnIncrement(Variable var) { + result.getTarget() = var and + ( + result.getParent() instanceof IncrementOperation + or + exists(AssignAddExpr a | a.getLValue() = result and a.getRValue().getValue().toInt() > 0) + or + exists(AssignExpr a | a.getLValue() = result | + a.getRValue() = + any(AddExpr ae | ae.getAnOperand() = var.getAnAccess() and + ae.getAnOperand().getValue().toInt() > 0)) + ) +} + +VariableAccess getADecrement(Variable var) { + result.getTarget() = var and + ( + result.getParent() instanceof DecrementOperation + or + exists(AssignSubExpr a | a.getLValue() = result and a.getRValue().getValue().toInt() > 0) + or + exists(AssignExpr a | a.getLValue() = result | + a.getRValue() = + any(SubExpr ae | ae.getLeftOperand() = var.getAnAccess() and + ae.getRightOperand().getValue().toInt() > 0)) + ) +} + +predicate inScope(Loop l, Stmt s) { + l.getAChild*() = s +} + +predicate reachesNoInc(VariableAccess source, ControlFlowNode target) { + (upperBoundCheck(_, source) and source.getASuccessor() = target) or + exists(ControlFlowNode mid | reachesNoInc(source, mid) and not mid = getAnIncrement(source.getTarget()) | + target = mid.getASuccessor() and + inScope(source.getEnclosingStmt(), target.getEnclosingStmt())) +} + +predicate reachesNoDec(VariableAccess source, ControlFlowNode target) { + (lowerBoundCheck(_, source) and source.getASuccessor() = target) or + exists(ControlFlowNode mid | reachesNoDec(source, mid) and not mid = getADecrement(source.getTarget()) | + target = mid.getASuccessor() and + inScope(source.getEnclosingStmt(), target.getEnclosingStmt())) +} + +predicate hasSafeBound(Loop l) { + exists(VariableAccess bound | upperBoundCheck(l, bound) | + not reachesNoInc(bound, bound) + ) or exists(VariableAccess bound | lowerBoundCheck(l, bound) | + not reachesNoDec(bound, bound) + ) or exists(l.getControllingExpr().getValue()) +} + +from Loop loop, string msg +where not hasSafeBound(loop) and + ( + ( + not upperBoundCheck(loop, _) and + not lowerBoundCheck(loop, _) and + msg = "This loop does not have a fixed bound." + ) or exists(VariableAccess bound | upperBoundCheck(loop, bound) and + reachesNoInc(bound, bound) and + msg = "The loop counter " + bound.getTarget().getName() + " is not always incremented in the loop body." + ) or exists(VariableAccess bound | lowerBoundCheck(loop, bound) and + reachesNoDec(bound, bound) and + msg = "The loop counter " + bound.getTarget().getName() + " is not always decremented in the loop body." + ) + ) +select loop, msg diff --git a/cpp/ql/src/Power of 10/Rule 2/ExitPermanentLoop.ql b/cpp/ql/src/Power of 10/Rule 2/ExitPermanentLoop.ql new file mode 100644 index 000000000000..ebb8a67d51ee --- /dev/null +++ b/cpp/ql/src/Power of 10/Rule 2/ExitPermanentLoop.ql @@ -0,0 +1,22 @@ +/** + * @name Exit from permanent loop + * @description Permanent loops (like "while(1) {..}") are typically meant to be non-terminating and should not be terminated by other means. + * @kind problem + * @id cpp/power-of-10/exit-permanent-loop + * @problem.severity warning + */ + +import cpp + +Stmt exitFrom(Loop l) { + l.getAChild+() = result and + (result instanceof ReturnStmt or + exists(BreakStmt break | break = result | + not l.getAChild*() = break.getTarget()) + ) +} + +from Loop l, Stmt exit +where l.getControllingExpr().getValue().toInt() != 0 and + exit = exitFrom(l) +select exit, "$@ should not be exited.", l, "This permanent loop" diff --git a/cpp/ql/src/Power of 10/Rule 3/DynamicAllocAfterInit.ql b/cpp/ql/src/Power of 10/Rule 3/DynamicAllocAfterInit.ql new file mode 100644 index 000000000000..80ddc2f367f9 --- /dev/null +++ b/cpp/ql/src/Power of 10/Rule 3/DynamicAllocAfterInit.ql @@ -0,0 +1,31 @@ +/** + * @name Dynamic allocation after initialization + * @description Dynamic memory allocation (using malloc() or calloc()) should be confined to the initialization routines of a program. + * @kind problem + * @id cpp/power-of-10/dynamic-alloc-after-init + * @problem.severity warning + */ + +import cpp + +class Initialization extends Function { + Initialization() { + // Adapt this query to your codebase by changing this predicate to match + // precisely what functions count as "initialization", and are, hence, + // allowed to perform dynamic memory allocation. + this.getName().toLowerCase().matches("init%") or + this.getName().matches("%\\_init") + } +} + +class Allocation extends FunctionCall { + Allocation() { + exists(string name | name = this.getTarget().getName() | + name = "malloc" or name = "calloc") + } +} + +from Function f, Allocation a +where not f instanceof Initialization and + a.getEnclosingFunction() = f +select a, "Dynamic memory allocation is only allowed during initialization." diff --git a/cpp/ql/src/Power of 10/Rule 4/FunctionTooLong.ql b/cpp/ql/src/Power of 10/Rule 4/FunctionTooLong.ql new file mode 100644 index 000000000000..3431990dacd4 --- /dev/null +++ b/cpp/ql/src/Power of 10/Rule 4/FunctionTooLong.ql @@ -0,0 +1,33 @@ +/** + * @name Function too long + * @description Function length should be limited to what can be printed on a single sheet of paper (60 logical lines). + * @kind problem + * @id cpp/power-of-10/function-too-long + * @problem.severity warning + */ + +import cpp + +class MacroFunctionCall extends MacroInvocation { + MacroFunctionCall() { + not exists(getParentInvocation()) and + this.getMacro().getHead().matches("%(%") + } + + FunctionDeclarationEntry getFunction() { + result.getFunction() = this.getAGeneratedElement().(Stmt).getEnclosingFunction() + } +} + +int logicalLength(FunctionDeclarationEntry f) { + result = count(Stmt s | s.getEnclosingFunction() = f.getFunction() and + s.getFile() = f.getFile() and + not s instanceof Block and + not s instanceof EmptyStmt and + not exists(ForStmt for | s = for.getInitialization()) and + not s.isAffectedByMacro()) + count(MacroFunctionCall mf | mf.getFunction() = f) +} + +from FunctionDeclarationEntry f, int n +where logicalLength(f) = n and n > 60 +select f.getFunction(), "Function " + f.getName() + " has too many logical lines (" + n + ", while 60 are allowed)." diff --git a/cpp/ql/src/Power of 10/Rule 4/OneStmtPerLine.ql b/cpp/ql/src/Power of 10/Rule 4/OneStmtPerLine.ql new file mode 100644 index 000000000000..32a85cc7f258 --- /dev/null +++ b/cpp/ql/src/Power of 10/Rule 4/OneStmtPerLine.ql @@ -0,0 +1,38 @@ +/** + * @name More than one statement per line + * @description Putting more than one statement on a single line hinders program understanding. + * @kind problem + * @id cpp/power-of-10/one-stmt-per-line + * @problem.severity warning + */ + +import cpp + +class OneLineStmt extends Stmt { + OneLineStmt() { + this.getLocation().getStartLine() = this.getLocation().getEndLine() and + not this instanceof Block and + not exists(ForStmt for | this = for.getInitialization()) and + ( + // Either this statement is not touched by a macro at all... + not this.isAffectedByMacro() or + // ... or it's the top-level statement generated by a macro invocation. + exists(MacroInvocation mi | this = mi.getAGeneratedElement() | + not this.getAChild+() = mi.getAGeneratedElement() + ) + ) + } + + predicate onLine(File f, int line) { + f = this.getFile() and line = this.getLocation().getStartLine() + } +} + +int numStmt(File f, int line) { + result = strictcount(OneLineStmt o | o.onLine(f, line)) +} + +from File f, int line, OneLineStmt o, int cnt +where numStmt(f, line) = cnt and cnt > 1 and o.onLine(f, line) + and o.getLocation().getStartColumn() = min(OneLineStmt other, int toMin | (other.onLine(f, line)) and (toMin = other.getLocation().getStartColumn()) | toMin) +select o, "This line contains " + cnt + " statements; only one is allowed." diff --git a/cpp/ql/src/Power of 10/Rule 5/AssertionDensity.ql b/cpp/ql/src/Power of 10/Rule 5/AssertionDensity.ql new file mode 100644 index 000000000000..eab591534537 --- /dev/null +++ b/cpp/ql/src/Power of 10/Rule 5/AssertionDensity.ql @@ -0,0 +1,42 @@ +/** + * @name Too few assertions + * @description Each function over 20 logical lines should have at least two assertions. + * @kind problem + * @id cpp/power-of-10/assertion-density + * @problem.severity warning + */ + +import semmle.code.cpp.commons.Assertions + +class MacroFunctionCall extends MacroInvocation { + MacroFunctionCall() { + not exists(getParentInvocation()) and + this.getMacro().getHead().matches("%(%") + } + + FunctionDeclarationEntry getFunction() { + result.getFunction() = this.getAGeneratedElement().(Stmt).getEnclosingFunction() + } +} + +int logicalLength(FunctionDeclarationEntry f) { + result = count(Stmt s | s.getEnclosingFunction() = f.getFunction() and + s.getFile() = f.getFile() and + not s instanceof Block and + not s instanceof EmptyStmt and + not exists(ForStmt for | s = for.getInitialization()) and + not s.isAffectedByMacro()) + count(MacroFunctionCall mf | mf.getFunction() = f) +} + +int assertionCount(FunctionDeclarationEntry f) { + result = count(Assertion a | a.getAsserted().getEnclosingFunction() = f.getFunction() and a.getFile() = f.getFile()) +} + +from FunctionDeclarationEntry f, int numAsserts, int size, int minSize +where minSize = 20 and + numAsserts = assertionCount(f) and + numAsserts < 2 and + size = logicalLength(f) and + size > minSize +select f.getFunction(), "Function " + f.getName() + " has " + size + " logical lines, but only " + + numAsserts + " assertion(s) -- minimum is 2 (for functions over " + minSize + " logical lines)." diff --git a/cpp/ql/src/Power of 10/Rule 5/AssertionSideEffect.ql b/cpp/ql/src/Power of 10/Rule 5/AssertionSideEffect.ql new file mode 100644 index 000000000000..6af42cececa0 --- /dev/null +++ b/cpp/ql/src/Power of 10/Rule 5/AssertionSideEffect.ql @@ -0,0 +1,14 @@ +/** + * @name Assertion with side effects + * @description When an assertion has side effects, disabling assertions will + * alter program behavior. + * @kind problem + * @id cpp/power-of-10/assertion-side-effect + * @problem.severity warning + */ + +import semmle.code.cpp.commons.Assertions + +from Assertion a +where not a.getAsserted().isPure() +select a.getAsserted(), "Assertions should not have side effects." diff --git a/cpp/ql/src/Power of 10/Rule 5/ConstantAssertion.ql b/cpp/ql/src/Power of 10/Rule 5/ConstantAssertion.ql new file mode 100644 index 000000000000..8b7829e02126 --- /dev/null +++ b/cpp/ql/src/Power of 10/Rule 5/ConstantAssertion.ql @@ -0,0 +1,17 @@ +/** + * @name Constant assertion + * @description Assertions should check dynamic properties of pre-/post-conditions and invariants. Assertions that either always succeed or always fail are an error. + * @kind problem + * @id cpp/power-of-10/constant-assertion + * @problem.severity warning + */ + +import semmle.code.cpp.commons.Assertions + +from Assertion a, string value, string msg +where value = a.getAsserted().getValue() and + if value.toInt() = 0 then + msg = "This assertion is always false." + else + msg = "This assertion is always true." +select a.getAsserted(), msg diff --git a/cpp/ql/src/Power of 10/Rule 5/NonBooleanAssertion.ql b/cpp/ql/src/Power of 10/Rule 5/NonBooleanAssertion.ql new file mode 100644 index 000000000000..f3bc0a70e1ad --- /dev/null +++ b/cpp/ql/src/Power of 10/Rule 5/NonBooleanAssertion.ql @@ -0,0 +1,13 @@ +/** + * @name Non-Boolean assertion + * @description Assertions should be defined as Boolean tests, meaning "assert(p != NULL)" rather than "assert(p)". + * @kind problem + * @id cpp/power-of-10/non-boolean-assertion + * @problem.severity warning + */ + +import semmle.code.cpp.commons.Assertions + +from Assertion a +where a.getAsserted().getType() instanceof PointerType +select a.getAsserted(), "Assertions should be defined as Boolean tests." diff --git a/cpp/ql/src/Power of 10/Rule 6/GlobalCouldBeStatic.ql b/cpp/ql/src/Power of 10/Rule 6/GlobalCouldBeStatic.ql new file mode 100644 index 000000000000..ea3f09d11615 --- /dev/null +++ b/cpp/ql/src/Power of 10/Rule 6/GlobalCouldBeStatic.ql @@ -0,0 +1,16 @@ +/** + * @name Global could be static + * @description Global variables that are not accessed outside their own file could be made static to promote information hiding. + * @kind problem + * @id cpp/power-of-10/global-could-be-static + * @problem.severity warning + */ + +import cpp + +from GlobalVariable v +where forex(VariableAccess va | va.getTarget() = v | va.getFile() = v.getDefinitionLocation().getFile()) + and not v.hasSpecifier("static") + and strictcount(v.getAnAccess().getEnclosingFunction()) > 1 // If = 1, variable should be function-scope. +select v, "The global variable " + v.getName() + " is not accessed outside of " + v.getFile().getBaseName() + + " and could be made static." diff --git a/cpp/ql/src/Power of 10/Rule 6/VariableScopeTooLarge.ql b/cpp/ql/src/Power of 10/Rule 6/VariableScopeTooLarge.ql new file mode 100644 index 000000000000..b6358d41c82d --- /dev/null +++ b/cpp/ql/src/Power of 10/Rule 6/VariableScopeTooLarge.ql @@ -0,0 +1,14 @@ +/** + * @name Variable scope too large + * @description Global and file-scope variables that are accessed by only one function should be scoped within that function. + * @kind problem + * @id cpp/power-of-10/variable-scope-too-large + * @problem.severity warning + */ + +import cpp + +from GlobalVariable v, Function f +where v.getAnAccess().getEnclosingFunction() = f and + strictcount(v.getAnAccess().getEnclosingFunction()) = 1 +select v, "The variable " + v.getName() + " is only accessed in $@ and should be scoped accordingly.", f, f.getName() diff --git a/cpp/ql/src/Power of 10/Rule 7/CheckArguments.ql b/cpp/ql/src/Power of 10/Rule 7/CheckArguments.ql new file mode 100644 index 000000000000..e5d0a4d4d085 --- /dev/null +++ b/cpp/ql/src/Power of 10/Rule 7/CheckArguments.ql @@ -0,0 +1,24 @@ +/** + * @name Unchecked function argument + * @description Functions should check their arguments before their first use. + * @kind problem + * @id cpp/power-of-10/check-arguments + * @problem.severity warning + */ + +import cpp + +predicate flow(Parameter p, ControlFlowNode n) { + (exists(p.getAnAccess()) and n = p.getFunction().getBlock()) or + exists(ControlFlowNode mid | flow(p, mid) and not mid = p.getAnAccess() and n = mid.getASuccessor()) +} + +VariableAccess firstAccess(Parameter p) { + flow(p, result) and result = p.getAnAccess() +} + +from Parameter p, VariableAccess va +where va = firstAccess(p) and + not exists(Expr e | e.isCondition() | e.getAChild*() = va) +select va, "This use of parameter " + p.getName() + " has not been checked." + diff --git a/cpp/ql/src/Power of 10/Rule 7/CheckReturnValues.ql b/cpp/ql/src/Power of 10/Rule 7/CheckReturnValues.ql new file mode 100644 index 000000000000..f52d65a9228c --- /dev/null +++ b/cpp/ql/src/Power of 10/Rule 7/CheckReturnValues.ql @@ -0,0 +1,36 @@ +/** + * @name Unchecked return value + * @description The return value of each non-void function call should be checked for error conditions. + * @kind problem + * @id cpp/power-of-10/check-return-values + * @problem.severity warning + */ + +import cpp + +/** In its full generality, the rule applies to all functions that + * return non-void, including things like 'printf' and 'close', + * which are routinely not checked because the behavior on success + * is the same as the behavior on failure. The recommendation is + * to add an explicit cast to void for such functions. For code + * bases that have not been developed with this rule in mind, at + * least for such commonly ignored functions, it may be better to + * add them as exceptions to this whitelist predicate. + */ +predicate whitelist(Function f) { + // Example: + // f.hasName("printf") or f.hasName("close") or // ... + none() +} + +from FunctionCall c, string msg +where not c.getTarget().getType() instanceof VoidType + and not whitelist(c.getTarget()) + and + ( + (c instanceof ExprInVoidContext and msg = "The return value of non-void function $@ is not checked.") + or + (definition(_, c.getParent()) and not definitionUsePair(_, c.getParent(), _) and + msg = "$@'s return value is stored but not checked.") + ) +select c, msg, c.getTarget() as f, f.getName() diff --git a/cpp/ql/src/Power of 10/Rule 8/AvoidConditionalCompilation.ql b/cpp/ql/src/Power of 10/Rule 8/AvoidConditionalCompilation.ql new file mode 100644 index 000000000000..e7626bbcba08 --- /dev/null +++ b/cpp/ql/src/Power of 10/Rule 8/AvoidConditionalCompilation.ql @@ -0,0 +1,14 @@ +/** + * @name Conditional compilation + * @description The use of conditional compilation directives must be kept to a minimum -- e.g. for header guards only. + * @kind problem + * @id cpp/power-of-10/avoid-conditional-compilation + * @problem.severity warning + */ + +import cpp + +from PreprocessorDirective i +where (i instanceof PreprocessorIf or i instanceof PreprocessorIfdef or i instanceof PreprocessorIfndef) + and not i.getFile() instanceof HeaderFile +select i, "Use of conditional compilation must be kept to a minimum." diff --git a/cpp/ql/src/Power of 10/Rule 8/PartialMacro.ql b/cpp/ql/src/Power of 10/Rule 8/PartialMacro.ql new file mode 100644 index 000000000000..bcbd026ed15d --- /dev/null +++ b/cpp/ql/src/Power of 10/Rule 8/PartialMacro.ql @@ -0,0 +1,27 @@ +/** + * @name Partial macro + * @description Macros must expand to complete syntactic units -- "#define MY_IF if(" is not legal. + * @kind problem + * @id cpp/power-of-10/partial-macro + * @problem.severity warning + */ + +import cpp + +predicate incomplete(Macro m) { + exists(string body | body = m.getBody() and not m.getBody().matches("%\\") | + body.regexpMatch("[^(]*\\).*") or + body.regexpMatch("[^\\[]*].*") or + body.regexpMatch("[^{]*}.*") or + body.regexpMatch(".*\\([^)]*") or + body.regexpMatch(".*\\[[^\\]]*") or + body.regexpMatch(".*\\{[^}]*") or + count(body.indexOf("(")) != count(body.indexOf(")")) or + count(body.indexOf("[")) != count(body.indexOf("]")) or + count(body.indexOf("{")) != count(body.indexOf("}")) + ) +} + +from Macro m +where incomplete(m) +select m, "The macro " + m.getHead() + " will not expand into a syntactic unit." diff --git a/cpp/ql/src/Power of 10/Rule 8/RestrictPreprocessor.ql b/cpp/ql/src/Power of 10/Rule 8/RestrictPreprocessor.ql new file mode 100644 index 000000000000..32237426a1f5 --- /dev/null +++ b/cpp/ql/src/Power of 10/Rule 8/RestrictPreprocessor.ql @@ -0,0 +1,20 @@ +/** + * @name Disallowed preprocessor use + * @description The use of the preprocessor must be limited to inclusion of header files and simple macro definitions. + * @kind problem + * @id cpp/power-of-10/restrict-preprocessor + * @problem.severity warning + */ + +import cpp + +from PreprocessorDirective p +where not p instanceof Include and + not p instanceof Macro and + not p instanceof PreprocessorIf and + not p instanceof PreprocessorElif and + not p instanceof PreprocessorElse and + not p instanceof PreprocessorIfdef and + not p instanceof PreprocessorIfndef and + not p instanceof PreprocessorEndif +select p, "This preprocessor directive is not allowed." diff --git a/cpp/ql/src/Power of 10/Rule 8/UndisciplinedMacro.ql b/cpp/ql/src/Power of 10/Rule 8/UndisciplinedMacro.ql new file mode 100644 index 000000000000..9dcb50581714 --- /dev/null +++ b/cpp/ql/src/Power of 10/Rule 8/UndisciplinedMacro.ql @@ -0,0 +1,14 @@ +/** + * @name Undisciplined macro + * @description Macros are not allowed to use complex preprocessor features like variable argument lists and token pasting. + * @kind problem + * @id cpp/power-of-10/undisciplined-macro + * @problem.severity warning + */ + +import cpp + +from Macro m, string msg +where (m.getHead().matches("%...%") and msg = "The macro " + m.getHead() + " is variadic, and hence not allowed.") or + (m.getBody().matches("%##%") and msg = "The macro " + m.getHead() + " uses token pasting and is not allowed.") +select m, msg diff --git a/cpp/ql/src/Power of 10/Rule 9/FunctionPointer.ql b/cpp/ql/src/Power of 10/Rule 9/FunctionPointer.ql new file mode 100644 index 000000000000..ed1cf1973718 --- /dev/null +++ b/cpp/ql/src/Power of 10/Rule 9/FunctionPointer.ql @@ -0,0 +1,12 @@ +/** + * @name Function pointer call + * @description Function pointers are not permitted -- they make it impossible for a tool to prove the absence of recursion. + * @kind problem + * @id cpp/power-of-10/function-pointer + * @problem.severity warning + */ + +import cpp + +from ExprCall e +select e, "Calls through function pointers are not permitted." diff --git a/cpp/ql/src/Power of 10/Rule 9/HiddenPointerIndirection.ql b/cpp/ql/src/Power of 10/Rule 9/HiddenPointerIndirection.ql new file mode 100644 index 000000000000..5c827e3ad4af --- /dev/null +++ b/cpp/ql/src/Power of 10/Rule 9/HiddenPointerIndirection.ql @@ -0,0 +1,13 @@ +/** + * @name Hidden pointer indirection + * @description Pointer indirection may not be hidden by typedefs -- "typedef int* IntPtr;" is not allowed. + * @kind problem + * @id cpp/power-of-10/hidden-pointer-indirection + * @problem.severity warning + */ + +import cpp + +from TypedefType t +where t.getBaseType().getPointerIndirectionLevel() > 0 +select t, "The typedef " + t.getName() + " hides pointer indirection." diff --git a/cpp/ql/src/Power of 10/Rule 9/PointerNesting.ql b/cpp/ql/src/Power of 10/Rule 9/PointerNesting.ql new file mode 100644 index 000000000000..64bd364ae162 --- /dev/null +++ b/cpp/ql/src/Power of 10/Rule 9/PointerNesting.ql @@ -0,0 +1,13 @@ +/** + * @name Pointer nesting too high + * @description No more than one level of pointer nesting/dereferencing should be used. + * @kind problem + * @id cpp/power-of-10/pointer-nesting + * @problem.severity warning + */ + +import cpp + +from Variable v, int n +where n = v.getType().(PointerType).getPointerIndirectionLevel() and n > 1 +select v, "The variable " + v.getName() + " uses " + n + " levels of pointer indirection." diff --git a/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.c b/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.c new file mode 100644 index 000000000000..63856888ebbc --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.c @@ -0,0 +1,22 @@ +int main(int argc, char** argv) { + char *userAndFile = argv[2]; + + { + char fileBuffer[FILENAME_MAX] = "/home/"; + char *fileName = fileBuffer; + size_t len = strlen(fileName); + strncat(fileName+len, userAndFile, FILENAME_MAX-len-1); + // BAD: a string from the user is used in a filename + fopen(fileName, "wb+"); + } + + { + char fileBuffer[FILENAME_MAX] = "/home/"; + char *fileName = fileBuffer; + size_t len = strlen(fileName); + // GOOD: use a fixed file + char* fixed = "jim/file.txt"; + strncat(fileName+len, fixed, FILENAME_MAX-len-1); + fopen(fileName, "wb+"); + } +} diff --git a/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.qhelp b/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.qhelp new file mode 100644 index 000000000000..d7432369f0e8 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.qhelp @@ -0,0 +1,46 @@ + + + +

    Accessing paths controlled by users can allow an attacker to access unexpected resources. This +can result in sensitive information being revealed or deleted, or an attacker being able to influence +behavior by modifying unexpected files.

    + +

    Paths that are naively constructed from data controlled by a user may contain unexpected special characters, +such as "..". Such a path may potentially point to any directory on the filesystem.

    + +
    + + +

    Validate user input before using it to construct a filepath. Ideally, follow these rules:

    + +
      +
    • Do not allow more than a single "." character.
    • +
    • Do not allow directory separators such as "/" or "\" (depending on the filesystem).
    • +
    • Do not rely on simply replacing problematic sequences such as "../". For example, after applying this filter to +".../...//" the resulting string would still be "../".
    • +
    • Ideally use a whitelist of known good patterns.
    • +
    + +
    + + +

    In this example, a username and file are read from the arguments to main and then used to access a file in the +user's home directory. However, a malicious user could enter a filename which contains special +characters. For example, the string "../../etc/passwd" will result in the code reading the file located at +"/home/[user]/../../etc/passwd", which is the system's password file. This could potentially allow them to +access all the system's passwords.

    + + + +
    + + +
  • +OWASP: +Path Traversal. +
  • + +
    +
    diff --git a/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.ql b/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.ql new file mode 100644 index 000000000000..3546b2bdbc22 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.ql @@ -0,0 +1,70 @@ +/** + * @name Uncontrolled data used in path expression + * @description Accessing paths influenced by users can allow an + * attacker to access unexpected resources. + * @kind problem + * @problem.severity warning + * @precision medium + * @id cpp/path-injection + * @tags security + * external/cwe/cwe-022 + * external/cwe/cwe-023 + * external/cwe/cwe-036 + * external/cwe/cwe-073 + */ + +import cpp +import semmle.code.cpp.security.FunctionWithWrappers +import semmle.code.cpp.security.Security +import semmle.code.cpp.security.TaintTracking + +/** + * A function for opening a file. + */ +class FileFunction extends FunctionWithWrappers { + FileFunction() { + exists(string nme | this.getQualifiedName() = nme | + nme = "fopen" or + nme = "_fopen" or + nme = "_wfopen" or + nme = "open" or + nme = "_open" or + nme = "_wopen" or + + // create file function on windows + nme.matches("CreateFile%") or + + // Objective C standard library + nme.matches("NSFileHandle%::+fileHandleFor%AtPath:") + ) + or + ( + // on any of the fstream classes, or filebuf + exists(string nme | this.getDeclaringType().getSimpleName() = nme | + nme = "basic_fstream" or + nme = "basic_ifstream" or + nme = "basic_ofstream" or + nme = "basic_filebuf" + ) + and + // we look for either the open method or the constructor + (this.getName() = "open" or this instanceof Constructor) + ) + } + + // conveniently, all of these functions take the path as the first parameter! + override predicate interestingArg(int arg) { + arg = 0 + } +} + +from FileFunction fileFunction, + Expr taintedArg, Expr taintSource, string taintCause, string callChain +where + fileFunction.outermostWrapperFunctionCall(taintedArg, callChain) and + tainted(taintSource, taintedArg) and + isUserInput(taintSource, taintCause) +select + taintedArg, + "This argument to a file access function is derived from $@ and then passed to " + callChain, + taintSource, "user input (" + taintCause + ")" diff --git a/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.c b/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.c new file mode 100644 index 000000000000..da5950c5fe57 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.c @@ -0,0 +1,20 @@ +int main(int argc, char** argv) { + char *userName = argv[2]; + + { + // BAD: a string from the user is injected directly into + // a command line. + char command1[1000] = {0}; + sprintf(command1, "userinfo -v \"%s\"", userName); + system(command1); + } + + { + // GOOD: the user string is encoded by a library routine. + char userNameQuoted[1000] = {0}; + encodeShellString(userNameQuoted, 1000, userName); + char command2[1000] = {0}; + sprintf(command2, "userinfo -v %s", userNameQuoted); + system(command2); + } +} diff --git a/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.qhelp b/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.qhelp new file mode 100644 index 000000000000..2e888bf80df5 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.qhelp @@ -0,0 +1,45 @@ + + + +

    The code passes user input as part of a call to system +or popen without escaping special elements. It generates +a command line using sprintf, with the user-supplied data +directly passed as a formatting argument. This leaves the code +vulnerable to attack by command injection.

    + +
    + + +

    Use a library routine to escape characters in the user-supplied +string before passing it to a command shell.

    + +
    + +

    The following example runs an external command in two ways. The +first way uses sprintf to build a command directly out of +a user-supplied argument. As such, it is vulnerable to command +injection. The second way quotes the user-provided value before +embedding it in the command; assuming +the encodeShellString utility is correct, this code +should be safe against command injection.

    + + +
    + + +
  • CERT C Coding Standard: +STR02-C. +Sanitize data passed to complex subsystems.
  • +
  • +OWASP: +Command Injection. +
  • + + + + +
    +
    diff --git a/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.ql b/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.ql new file mode 100644 index 000000000000..17d5a62b2716 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.ql @@ -0,0 +1,26 @@ +/** + * @name Uncontrolled data used in OS command + * @description Using user-supplied data in an OS command, without + * neutralizing special elements, can make code vulnerable + * to command injection. + * @kind problem + * @problem.severity error + * @precision high + * @id cpp/command-line-injection + * @tags security + * external/cwe/cwe-078 + * external/cwe/cwe-088 + */ +import cpp +import semmle.code.cpp.security.CommandExecution +import semmle.code.cpp.security.Security +import semmle.code.cpp.security.TaintTracking + +from Expr taintedArg, Expr taintSource, string taintCause, string callChain +where shellCommand(taintedArg, callChain) + and tainted(taintSource, taintedArg) + and isUserInput(taintSource, taintCause) +select + taintedArg, + "This argument to an OS command is derived from $@ and then passed to " + callChain, + taintSource, "user input (" + taintCause + ")" diff --git a/cpp/ql/src/Security/CWE/CWE-079/CgiXss.c b/cpp/ql/src/Security/CWE/CWE-079/CgiXss.c new file mode 100644 index 000000000000..20d93703ccd7 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-079/CgiXss.c @@ -0,0 +1,20 @@ +void bad_server() { + char* query = getenv("QUERY_STRING"); + puts("

    Query results for "); + // BAD: Printing out an HTTP parameter with no escaping + puts(query); + puts("\n

    \n"); + puts(do_search(query)); +} + +void good_server() { + char* query = getenv("QUERY_STRING"); + puts("

    Query results for "); + // GOOD: Escape HTML characters before adding to a page + char* query_escaped = escape_html(query); + puts(query_escaped); + free(query_escaped); + + puts("\n

    \n"); + puts(do_search(query)); +} diff --git a/cpp/ql/src/Security/CWE/CWE-079/CgiXss.qhelp b/cpp/ql/src/Security/CWE/CWE-079/CgiXss.qhelp new file mode 100644 index 000000000000..ccd297c3b367 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-079/CgiXss.qhelp @@ -0,0 +1,48 @@ + + + +

    Directly writing an HTTP request parameter back to a web page +allows for a cross-site scripting vulnerability. The data is displayed +in a user's web browser as belonging to one site, but it is provided +by some other site that the user browses to. In effect, such an attack +allows one web site to insert content in the other one.

    + +

    For web servers implemented with the Common Gateway Interface +(CGI), HTTP parameters are supplied via the QUERY_STRING +environment variable.

    + + + + +

    To guard against cross-site scripting, consider escaping special +characters before writing the HTTP parameter back to the page.

    + +
    + + +

    In the following example, the bad_server writes a +parameter directly back to the HTML page that the user will +see. The good_server first escapes any HTML special +characters before writing to the HTML page.

    + + + +
    + + +
  • +OWASP: +XSS +(Cross Site Scripting) Prevention Cheat Sheet. +
  • +
  • +Wikipedia: Cross-site scripting. +
  • +
  • +IETF Tools: The Common Gateway Specification (CGI).
  • + + +
    + diff --git a/cpp/ql/src/Security/CWE/CWE-079/CgiXss.ql b/cpp/ql/src/Security/CWE/CWE-079/CgiXss.ql new file mode 100644 index 000000000000..217886ca8c39 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-079/CgiXss.ql @@ -0,0 +1,35 @@ +/** + * @name CGI script vulnerable to cross-site scripting + * @description Writing user input directly to a web page + * allows for a cross-site scripting vulnerability. + * @kind problem + * @problem.severity error + * @precision high + * @id cpp/cgi-xss + * @tags security + * external/cwe/cwe-079 + */ +import cpp +import semmle.code.cpp.commons.Environment +import semmle.code.cpp.security.TaintTracking + +/** A call that prints its arguments to `stdout`. */ +class PrintStdoutCall extends FunctionCall { + PrintStdoutCall() { + getTarget().hasQualifiedName("puts") or + getTarget().hasQualifiedName("printf") + } +} + +/** A read of the QUERY_STRING environment variable */ +class QueryString extends EnvironmentRead { + QueryString() { + getEnvironmentVariable() = "QUERY_STRING" + } +} + +from QueryString query, PrintStdoutCall call, Element printedArg +where call.getAnArgument() = printedArg + and tainted(query, printedArg) +select printedArg, "Cross-site scripting vulnerability due to $@.", + query, "this query data" diff --git a/cpp/ql/src/Security/CWE/CWE-089/SqlTainted.c b/cpp/ql/src/Security/CWE/CWE-089/SqlTainted.c new file mode 100644 index 000000000000..d207a2ca461c --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-089/SqlTainted.c @@ -0,0 +1,15 @@ +int main(int argc, char** argv) { + char *userName = argv[2]; + + // BAD + char query1[1000] = {0}; + sprintf(query1, "SELECT UID FROM USERS where name = \"%s\"", userName); + runSql(query1); + + // GOOD + char userNameSql[1000] = {0}; + encodeSqlString(userNameSql, 1000, userName); + char query2[1000] = {0}; + sprintf(query2, "SELECT UID FROM USERS where name = \"%s\"", userNameSql); + runSql(query2); +} diff --git a/cpp/ql/src/Security/CWE/CWE-089/SqlTainted.qhelp b/cpp/ql/src/Security/CWE/CWE-089/SqlTainted.qhelp new file mode 100644 index 000000000000..3d8f35fd483c --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-089/SqlTainted.qhelp @@ -0,0 +1,31 @@ + + + +

    The code passes user input as part of a SQL query without escaping special elements. +It generates a SQL query using sprintf, +with the user-supplied data directly passed as an argument +to sprintf. This leaves the code vulnerable to attack by SQL Injection.

    + +
    + + +

    Use a library routine to escape characters in the user-supplied +string before converting it to SQL.

    + +
    + + + + + + +
  • Microsoft Developer Network: SQL Injection.
  • + + + + +
    +
    diff --git a/cpp/ql/src/Security/CWE/CWE-089/SqlTainted.ql b/cpp/ql/src/Security/CWE/CWE-089/SqlTainted.ql new file mode 100644 index 000000000000..a2213ee084be --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-089/SqlTainted.ql @@ -0,0 +1,36 @@ +/** + * @name Uncontrolled data in SQL query + * @description Including user-supplied data in a SQL query without + * neutralizing special elements can make code vulnerable + * to SQL Injection. + * @kind problem + * @problem.severity error + * @precision high + * @id cpp/sql-injection + * @tags security + * external/cwe/cwe-089 + */ +import cpp +import semmle.code.cpp.security.Security +import semmle.code.cpp.security.FunctionWithWrappers +import semmle.code.cpp.security.TaintTracking + +class SQLLikeFunction extends FunctionWithWrappers { + SQLLikeFunction() { + sqlArgument(this.getName(), _) + } + + override predicate interestingArg(int arg) { + sqlArgument(this.getName(), arg) + } +} + +from SQLLikeFunction runSql, + Expr taintedArg, Expr taintSource, string taintCause, string callChain +where runSql.outermostWrapperFunctionCall(taintedArg, callChain) + and tainted(taintSource, taintedArg) + and isUserInput(taintSource, taintCause) +select + taintedArg, + "This argument to a SQL query function is derived from $@ and then passed to " + callChain, + taintSource, "user input (" + taintCause + ")" diff --git a/cpp/ql/src/Security/CWE/CWE-114/UncontrolledProcessOperation.c b/cpp/ql/src/Security/CWE/CWE-114/UncontrolledProcessOperation.c new file mode 100644 index 000000000000..62c7f5d4c8e4 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-114/UncontrolledProcessOperation.c @@ -0,0 +1,17 @@ +int main(int argc, char** argv) { + char *lib = argv[2]; + + // BAD: the user can cause arbitrary code to be loaded + void* handle = dlopen(lib, RTLD_LAZY); + + // GOOD: only hard-coded libraries can be loaded + void* handle2; + + if (!strcmp(lib, "inmem")) { + handle2 = dlopen("/usr/share/dbwrap/inmem", RTLD_LAZY); + } else if (!strcmp(lib, "mysql")) { + handle2 = dlopen("/usr/share/dbwrap/mysql", RTLD_LAZY); + } else { + die("Invalid library specified\n"); + } +} diff --git a/cpp/ql/src/Security/CWE/CWE-114/UncontrolledProcessOperation.qhelp b/cpp/ql/src/Security/CWE/CWE-114/UncontrolledProcessOperation.qhelp new file mode 100644 index 000000000000..fb0c7f03cb9b --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-114/UncontrolledProcessOperation.qhelp @@ -0,0 +1,34 @@ + + + +

    The code passes user input directly +to system, dlopen, LoadLibrary +or some other process or library routine. As a result, the user can +cause execution of arbitrary code.

    + +
    + + +

    If possible, use hard-coded string literals for the command to run +or library to load. Instead of passing the user input directly to the +process or library function, examine the user input and then choose +among hard-coded string literals.

    + +

    If the applicable libraries or commands cannot be determined at +compile time, then add code to verify that the user-input string is +safe before using it.

    + +
    + + + + + + + + + + +
    diff --git a/cpp/ql/src/Security/CWE/CWE-114/UncontrolledProcessOperation.ql b/cpp/ql/src/Security/CWE/CWE-114/UncontrolledProcessOperation.ql new file mode 100644 index 000000000000..408dedd943db --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-114/UncontrolledProcessOperation.ql @@ -0,0 +1,26 @@ +/** + * @name Uncontrolled process operation + * @description Using externally controlled strings in a process + * operation can allow an attacker to execute malicious + * commands. + * @kind problem + * @problem.severity warning + * @precision medium + * @id cpp/uncontrolled-process-operation + * @tags security + * external/cwe/cwe-114 + */ + +import cpp +import semmle.code.cpp.security.Security +import semmle.code.cpp.security.TaintTracking + +from string processOperation, int processOperationArg, + FunctionCall call, Expr arg, Element source +where isProcessOperationArgument(processOperation, processOperationArg) + and call.getTarget().getName() = processOperation + and call.getArgument(processOperationArg) = arg + and tainted(source, arg) +select arg, + "The value of this argument may come from $@ and is being passed to " + processOperation, + source, source.toString() diff --git a/cpp/ql/src/Security/CWE/CWE-119/OverflowBuffer.c b/cpp/ql/src/Security/CWE/CWE-119/OverflowBuffer.c new file mode 100644 index 000000000000..9ce6d74afcb9 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-119/OverflowBuffer.c @@ -0,0 +1,5 @@ +const char *message = "Hello"; +char password[32]; +char buffer[256]; + +memcpy(buffer, message, 256); diff --git a/cpp/ql/src/Security/CWE/CWE-119/OverflowBuffer.qhelp b/cpp/ql/src/Security/CWE/CWE-119/OverflowBuffer.qhelp new file mode 100644 index 000000000000..2611bae27bef --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-119/OverflowBuffer.qhelp @@ -0,0 +1,25 @@ + + + + +

    The software uses a function to access a memory buffer in a way that may read or write data past the end of that buffer. This may result in software instability, improper access to or corruption of sensitive information, or code execution by an attacker.

    +
    + + +

    When accessing buffers with functions such as memcpy, memset or strncpy, ensure that the size value for the operation is no greater than the amount of space available in the destination buffer. Failure to do this may permit a buffer overwrite to occur. Also ensure that the size value is no greater than the amount of data in the source buffer, to prevent a buffer overread from occurring.

    +
    + + +

    In the following example, memcpy is used to fill a buffer with data from a string.

    + +

    Although the size of the operation matches the destination buffer, the source is only 6 bytes long so an overread will occur. This could copy sensitive data from nearby areas of memory (such as the local variable password in this example) into the buffer as well, potentially making it visible to an attacker.

    + +

    To fix this issue, reduce the size of the memcpy to the smaller of the source and destination buffers, min(256, strlen(message) + 1). Alternatively in this case it would be more appropriate to use the strncpy function rather than memcpy.

    +
    + + + + +
    diff --git a/cpp/ql/src/Security/CWE/CWE-119/OverflowBuffer.ql b/cpp/ql/src/Security/CWE/CWE-119/OverflowBuffer.ql new file mode 100644 index 000000000000..c15552384e98 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-119/OverflowBuffer.ql @@ -0,0 +1,48 @@ +/** + * @name Call to memory access function may overflow buffer + * @description Incorrect use of a function that accesses a memory + * buffer may read or write data past the end of that + * buffer. + * @kind problem + * @id cpp/overflow-buffer + * @problem.severity recommendation + * @tags security + * external/cwe/cwe-119 + * external/cwe/cwe-121 + * external/cwe/cwe-122 + * external/cwe/cwe-126 + */ +import semmle.code.cpp.security.BufferWrite +import semmle.code.cpp.security.BufferAccess + +bindingset[num, singular, plural] +string plural(int num, string singular, string plural) { + if (num = 1) then ( + result = num + singular + ) else ( + result = num + plural + ) +} + +from BufferAccess ba, string bufferDesc, int accessSize, int accessType, + Element bufferAlloc, int bufferSize, string message +where accessSize = ba.getSize() + and bufferSize = getBufferSize(ba.getBuffer(bufferDesc, accessType), + bufferAlloc) + and accessSize > bufferSize + and if accessType = 1 then ( + message = "This '" + ba.getName() + "' operation accesses " + + plural(accessSize, " byte", " bytes") + + " but the $@ is only " + + plural(bufferSize, " byte", " bytes") + "." + ) else if accessType = 2 then ( + message = "This '" + ba.getName() + "' operation may access " + + plural(accessSize, " byte", " bytes") + + " but the $@ is only " + + plural(bufferSize, " byte", " bytes") + "." + ) else ( + message = "This array indexing operation accesses byte offset " + + (accessSize - 1) + " but the $@ is only " + + plural(bufferSize, " byte", " bytes") + "." + ) +select ba, message, bufferAlloc, bufferDesc diff --git a/cpp/ql/src/Security/CWE/CWE-120/BadlyBoundedWrite.c b/cpp/ql/src/Security/CWE/CWE-120/BadlyBoundedWrite.c new file mode 100644 index 000000000000..1bce58ea9c05 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-120/BadlyBoundedWrite.c @@ -0,0 +1,10 @@ +void congratulateUser(const char *userName) +{ + char buffer[80]; + + // BAD: even though snprintf is used, this could overflow the buffer + // because the size specified is too large. + snprintf(buffer, 256, "Congratulations, %s!", userName); + + MessageBox(hWnd, buffer, "New Message", MB_OK); +} \ No newline at end of file diff --git a/cpp/ql/src/Security/CWE/CWE-120/BadlyBoundedWrite.qhelp b/cpp/ql/src/Security/CWE/CWE-120/BadlyBoundedWrite.qhelp new file mode 100644 index 000000000000..b8ae720a6626 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-120/BadlyBoundedWrite.qhelp @@ -0,0 +1,25 @@ + + + +

    The program performs a buffer copy or write operation with an incorrect upper limit on the size of the copy. A sufficiently long input will overflow the target buffer. In addition to causing program instability, techniques exist which may allow an attacker to use this vulnerability to execute arbitrary code.

    + +
    + +

    Use preprocessor defines to specify the size of buffers, and use the same defines as arguments to strncpy, snprintf etc. This technique will ensure that buffer sizes are always specified correctly so that no overflow occurs.

    + +
    + + + +

    In this example, the developer has used snprintf to control the maximum number of characters that can be written to buffer. Unfortunately, perhaps due to modifications since the code was first written, a limited buffer overrun can still occur because the size argument to snprintf is larger than the actual size of the buffer.

    + +

    To fix the problem, either the second argument to snprintf should be changed to 80, or the buffer extended to 256 characters. A further improvement is to use a preprocessor define so that the size is only specified in one place, potentially preventing future recurrence of this issue.

    + +
    + + + + +
    diff --git a/cpp/ql/src/Security/CWE/CWE-120/BadlyBoundedWrite.ql b/cpp/ql/src/Security/CWE/CWE-120/BadlyBoundedWrite.ql new file mode 100644 index 000000000000..1638f14f15f9 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-120/BadlyBoundedWrite.ql @@ -0,0 +1,24 @@ +/** + * @name Badly bounded write + * @description Buffer write operations with a length parameter that + * does not match the size of the destination buffer may + * overflow. + * @kind problem + * @problem.severity error + * @precision high + * @id cpp/badly-bounded-write + * @tags reliability + * security + * external/cwe/cwe-120 + * external/cwe/cwe-787 + * external/cwe/cwe-805 + */ +import semmle.code.cpp.security.BufferWrite + +// see CWE-120UnboundedWrite.ql for a summary of CWE-120 violation cases + +from BufferWrite bw, int destSize +where bw.hasExplicitLimit() // has an explicit size limit + and destSize = getBufferSize(bw.getDest(), _) + and (bw.getExplicitLimit() > destSize) // but it's larger than the destination +select bw, "This '" + bw.getBWDesc() + "' operation is limited to " + bw.getExplicitLimit() + " bytes but the destination is only " + destSize + " bytes." diff --git a/cpp/ql/src/Security/CWE/CWE-120/OverrunWrite.c b/cpp/ql/src/Security/CWE/CWE-120/OverrunWrite.c new file mode 100644 index 000000000000..5ab3645753b0 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-120/OverrunWrite.c @@ -0,0 +1,9 @@ +void sayHello() +{ + char buffer[10]; + + // BAD: this message overflows the buffer + strcpy(buffer, "Hello, world!"); + + MessageBox(hWnd, buffer, "New Message", MB_OK); +} \ No newline at end of file diff --git a/cpp/ql/src/Security/CWE/CWE-120/OverrunWrite.qhelp b/cpp/ql/src/Security/CWE/CWE-120/OverrunWrite.qhelp new file mode 100644 index 000000000000..1d3bda7d97e1 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-120/OverrunWrite.qhelp @@ -0,0 +1,35 @@ + + + +

    The program performs a buffer copy or write operation with no upper limit on the size of the copy, and it appears that certain inputs will cause a buffer overflow to occur in this case. In addition to causing program instability, techniques exist which may allow an attacker to use this vulnerability to execute arbitrary code.

    + +
    + +

    Always control the length of buffer copy and buffer write operations. strncpy should be used over strcpy, snprintf over sprintf, and in other cases 'n-variant' functions should be preferred.

    + +
    + + + +

    In this example, the call to strcpy copies a message of 14 characters (including the terminating null) into a buffer with space for just 10 characters. As such, the last four characters overflow the buffer resulting in undefined behavior.

    + +

    To fix this issue three changes should be made:

    +
      +
    • Control the size of the buffer using a preprocessor define.
    • +
    • Replace the call to strcpy with strncpy, specifying the define as the maximum length to copy. This will prevent the buffer overflow.
    • +
    • Consider increasing the buffer size, say to 20 characters, so that the message is displayed correctly.
    • +
    + +
    + + +
  • CERT C Coding Standard: STR31-C. Guarantee that storage for strings has sufficient space for character data and the null terminator.
  • + + + + +
    +
    diff --git a/cpp/ql/src/Security/CWE/CWE-120/OverrunWrite.ql b/cpp/ql/src/Security/CWE/CWE-120/OverrunWrite.ql new file mode 100644 index 000000000000..d27bd80a8655 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-120/OverrunWrite.ql @@ -0,0 +1,29 @@ +/** + * @name Potentially overrunning write + * @description Buffer write operations that do not control the length + * of data written may overflow. + * @kind problem + * @problem.severity error + * @precision medium + * @id cpp/overrunning-write + * @tags reliability + * security + * external/cwe/cwe-120 + * external/cwe/cwe-787 + * external/cwe/cwe-805 + */ +import semmle.code.cpp.security.BufferWrite +import semmle.code.cpp.commons.Alloc + +// see CWE-120UnboundedWrite.ql for a summary of CWE-120 violation cases + +from BufferWrite bw, Expr dest, int destSize +where not bw.hasExplicitLimit() // has no explicit size limit + and dest = bw.getDest() + and destSize = getBufferSize(dest, _) + and // we can deduce that too much data may be copied (even without + // long '%f' conversions) + bw.getMaxDataLimited() > destSize +select bw, "This '" + bw.getBWDesc() + "' operation requires " + + bw.getMaxData() + " bytes but the destination is only " + + destSize + " bytes." diff --git a/cpp/ql/src/Security/CWE/CWE-120/OverrunWriteFloat.c b/cpp/ql/src/Security/CWE/CWE-120/OverrunWriteFloat.c new file mode 100644 index 000000000000..a93a31dfb32c --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-120/OverrunWriteFloat.c @@ -0,0 +1,9 @@ +void displayValue(double value) +{ + char buffer[256]; + + // BAD: extreme values may overflow the buffer + sprintf(buffer, "%f", value); + + MessageBox(hWnd, buffer, "A Number", MB_OK); +} \ No newline at end of file diff --git a/cpp/ql/src/Security/CWE/CWE-120/OverrunWriteFloat.qhelp b/cpp/ql/src/Security/CWE/CWE-120/OverrunWriteFloat.qhelp new file mode 100644 index 000000000000..e88259c6fb3b --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-120/OverrunWriteFloat.qhelp @@ -0,0 +1,35 @@ + + + +

    The program performs a buffer copy or write operation that includes one or more float to string conversions (i.e. the %f format specifier), which may overflow the destination buffer if extreme inputs are given. In addition to causing program instability, techniques exist which may allow an attacker to use this vulnerability to execute arbitrary code.

    + +
    + +

    Always control the length of buffer copy and buffer write operations. strncpy should be used over strcpy, snprintf over sprintf, and in other cases 'n-variant' functions should be preferred.

    + +
    + + + +

    In this example, the call to sprintf contains a %f format specifier. Though a 256 character buffer has been allowed, it is not sufficient for the most extreme floating point inputs. For example the representation of double value 1e304 (that is 1 with 304 zeroes after it) will overflow a buffer of this length.

    + +

    To fix this issue three changes should be made:

    +
      +
    • Control the size of the buffer using a preprocessor define.
    • +
    • Replace the call to sprintf with snprintf, specifying the define as the maximum length to copy. This will prevent the buffer overflow.
    • +
    • Consider using the %g format specifier instead of %f.
    • +
    + +
    + + +
  • CERT C Coding +Standard: STR31-C. Guarantee +that storage for strings has sufficient space for character data and +the null terminator.
  • + + +
    +
    diff --git a/cpp/ql/src/Security/CWE/CWE-120/OverrunWriteFloat.ql b/cpp/ql/src/Security/CWE/CWE-120/OverrunWriteFloat.ql new file mode 100644 index 000000000000..be41c88fc205 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-120/OverrunWriteFloat.ql @@ -0,0 +1,25 @@ +/** + * @name Potentially overrunning write with float to string conversion + * @description Buffer write operations that do not control the length + * of data written may overflow when floating point inputs + * take extreme values. + * @kind problem + * @problem.severity error + * @precision medium + * @id cpp/overrunning-write-with-float + * @tags reliability + * security + * external/cwe/cwe-120 + * external/cwe/cwe-787 + * external/cwe/cwe-805 + */ +import semmle.code.cpp.security.BufferWrite + +// see CWE-120UnboundedWrite.ql for a summary of CWE-120 violation cases + +from BufferWrite bw, int destSize +where (not bw.hasExplicitLimit()) // has no explicit size limit + and destSize = getBufferSize(bw.getDest(), _) + and (bw.getMaxData() > destSize) // and we can deduce that too much data may be copied + and (bw.getMaxDataLimited() <= destSize) // but it would fit without long '%f' conversions +select bw, "This '" + bw.getBWDesc() + "' operation may require " + bw.getMaxData() + " bytes because of float conversions, but the target is only " + destSize + " bytes." diff --git a/cpp/ql/src/Security/CWE/CWE-120/UnboundedWrite.c b/cpp/ql/src/Security/CWE/CWE-120/UnboundedWrite.c new file mode 100644 index 000000000000..a9825c903626 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-120/UnboundedWrite.c @@ -0,0 +1,9 @@ +void congratulateUser(const char *userName) +{ + char buffer[80]; + + // BAD: this could overflow the buffer if the UserName is long + sprintf(buffer, "Congratulations, %s!", userName); + + MessageBox(hWnd, buffer, "New Message", MB_OK); +} \ No newline at end of file diff --git a/cpp/ql/src/Security/CWE/CWE-120/UnboundedWrite.qhelp b/cpp/ql/src/Security/CWE/CWE-120/UnboundedWrite.qhelp new file mode 100644 index 000000000000..d6bd626a08e3 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-120/UnboundedWrite.qhelp @@ -0,0 +1,30 @@ + + + +

    The program performs a buffer copy or write operation with no upper limit on the size of the copy. An unexpectedly long input that reaches this code will cause the buffer to overflow. In addition to causing program instability, techniques exist which may allow an attacker to use this vulnerability to execute arbitrary code.

    + +
    + +

    Always control the length of buffer copy and buffer write operations. strncpy should be used over strcpy, snprintf over sprintf etc. In general 'n-variant' functions should be preferred.

    + +
    + + + +

    In this example, the call to sprintf may overflow buffer. This occurs if the argument userName is very long, such that the resulting string is more than the 80 characters allowed.

    + +

    To fix the problem the call to sprintf should be replaced with snprintf, specifying a maximum length of 80 characters.

    + +
    + + +
  • CERT C++ Coding Standard: STR50-CPP. Guarantee that storage for strings has sufficient space for character data and the null terminator.
  • + + + + +
    +
    diff --git a/cpp/ql/src/Security/CWE/CWE-120/UnboundedWrite.ql b/cpp/ql/src/Security/CWE/CWE-120/UnboundedWrite.ql new file mode 100644 index 000000000000..322669b3b3b8 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-120/UnboundedWrite.ql @@ -0,0 +1,72 @@ +/** + * @name Unbounded write + * @description Buffer write operations that do not control the length + * of data written may overflow. + * @kind problem + * @problem.severity error + * @precision medium + * @id cpp/unbounded-write + * @tags reliability + * security + * external/cwe/cwe-120 + * external/cwe/cwe-787 + * external/cwe/cwe-805 + */ +import semmle.code.cpp.security.BufferWrite +import semmle.code.cpp.security.Security +import semmle.code.cpp.security.TaintTracking + +// --- Summary of CWE-120 violations --- +// +// The essence of CWE-120 is that string / buffer copies that are +// potentially unbounded, e.g. null terminated string copy, +// should be controlled e.g. by using strncpy instead of strcpy. +// In practice this is divided into several queries that +// handle slightly different sub-cases, exclude some acceptable uses, +// and produce reasonable messages to fit each issue. +// +// cases: +// hasExplicitLimit() exists(getMaxData()) exists(getBufferSize(bw.getDest(), _))) handled by +// NO NO either UnboundedWrite.ql isUnboundedWrite() +// NO YES NO UnboundedWrite.ql isMaybeUnboundedWrite() +// NO YES YES OverrunWrite.ql, OverrunWriteFloat.ql +// YES either YES BadlyBoundedWrite.ql +// YES either NO (assumed OK) + +// --- CWE-120UnboundedWrite --- + +predicate isUnboundedWrite(BufferWrite bw) { + not bw.hasExplicitLimit() // has no explicit size limit + and (not exists(bw.getMaxData())) // and we can't deduce an upper bound to the amount copied +} + +/*predicate isMaybeUnboundedWrite(BufferWrite bw) +{ + not bw.hasExplicitLimit() // has no explicit size limit + and exists(bw.getMaxData()) // and we can deduce an upper bound to the amount copied + and (not exists(getBufferSize(bw.getDest(), _))) // but we can't work out the size of the destination to be sure +}*/ + +// --- user input reach --- + +/** + * Identifies expressions that are potentially tainted with user + * input. Most of the work for this is actually done by the + * TaintTracking library. + */ +predicate tainted2(Expr expr, Expr inputSource, string inputCause) { + ( + taintedIncludingGlobalVars(inputSource, expr, _) and + inputCause = inputSource.toString() + ) or exists(Expr e | tainted2(e, inputSource, inputCause) | + // field accesses of a tainted struct are tainted + e = expr.(FieldAccess).getQualifier() + ) +} + +// --- put it together --- + +from BufferWrite bw, Expr inputSource, string inputCause +where isUnboundedWrite(bw) + and tainted2(bw.getASource(), inputSource, inputCause) +select bw, "This '" + bw.getBWDesc() + "' with input from $@ may overflow the destination.", inputSource, inputCause diff --git a/cpp/ql/src/Security/CWE/CWE-121/UnterminatedVarargsCall.cpp b/cpp/ql/src/Security/CWE/CWE-121/UnterminatedVarargsCall.cpp new file mode 100644 index 000000000000..96b013ba4b64 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-121/UnterminatedVarargsCall.cpp @@ -0,0 +1,31 @@ +#include + +void pushStrings(char *firstString, ...) +{ + va_list args; + char *arg; + + va_start(args, firstString); + + // process inputs, beginning with firstString, ending when NULL is reached + arg = firstString; + while (arg != NULL) + { + // push the string + pushString(arg); + + // move on to the next input + arg = va_arg(args, char *); + } + + va_end(args); +} + +void badFunction() +{ + pushStrings("hello", "world", NULL); // OK + + pushStrings("apple", "pear", "banana", NULL); // OK + + pushStrings("car", "bus", "train"); // BAD, not terminated with the expected NULL +} \ No newline at end of file diff --git a/cpp/ql/src/Security/CWE/CWE-121/UnterminatedVarargsCall.qhelp b/cpp/ql/src/Security/CWE/CWE-121/UnterminatedVarargsCall.qhelp new file mode 100644 index 000000000000..fbbc425500e5 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-121/UnterminatedVarargsCall.qhelp @@ -0,0 +1,23 @@ + + + +

    The program calls a function that expects the variable argument list to be terminated with a sentinel value (typically NULL, 0 or -1). In this case, the sentinel value has been omitted as a final argument. This defect may result in incorrect behavior of the function and unintended stack memory access, leading to incorrect program results, instability, and even vulnerability to buffer overflow style attacks.

    + +
    + +

    Each description of a defect highlighted by this rule includes a suggested value for the terminator. Check that this value is correct, then add it to the end of the call.

    + +
    + + + +

    In this example, the third call to pushStrings is not correctly terminated. This call should be updated to include NULL as the fourth and final argument to this call.

    + +
    + + + + +
    diff --git a/cpp/ql/src/Security/CWE/CWE-121/UnterminatedVarargsCall.ql b/cpp/ql/src/Security/CWE/CWE-121/UnterminatedVarargsCall.ql new file mode 100644 index 000000000000..30872663b005 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-121/UnterminatedVarargsCall.ql @@ -0,0 +1,88 @@ +/** + * @name Unterminated variadic call + * @description Calling a variadic function without a sentinel value + * may result in a buffer overflow if the function expects + * a specific value to terminate the argument list. + * @kind problem + * @problem.severity warning + * @precision medium + * @id cpp/unterminated-variadic-call + * @tags reliability + * security + * external/cwe/cwe-121 + */ + +import cpp + +/** + * Gets a normalized textual representation of `e`'s value. + * The result is the same as `Expr.getValue()`, except if there is a + * trailing `".0"` then it is removed. This means that, for example, + * the values of `-1` and `-1.0` would be considered the same. + */ +string normalisedExprValue(Expr e) { + result = e.getValue().regexpReplaceAll("\\.0$", "") +} + +/** + * A variadic function which is not a formatting function. + */ +class VarargsFunction extends Function { + VarargsFunction() { + this.isVarargs() and + not this instanceof FormattingFunction + } + + Expr trailingArgumentIn(FunctionCall fc) { + fc = this.getACallToThisFunction() and + result = fc.getArgument(fc.getNumberOfArguments() - 1) + } + + string trailingArgValue(FunctionCall fc) { + result = normalisedExprValue(this.trailingArgumentIn(fc)) + } + + private + int trailingArgValueCount(string value) { + result = strictcount(FunctionCall fc | trailingArgValue(fc) = value) + } + + string nonTrailingVarArgValue(FunctionCall fc, int index) { + fc = this.getACallToThisFunction() and + index >= this.getNumberOfParameters() and + index < fc.getNumberOfArguments() - 1 and + result = normalisedExprValue(fc.getArgument(index)) + } + + private + int totalCount() { + result = strictcount(FunctionCall fc | fc = this.getACallToThisFunction()) + } + + string normalTerminator(int cnt) { + ( + result = "0" or result = "-1" + ) and ( + cnt = trailingArgValueCount(result) + ) and ( + 2 * cnt > totalCount() + ) and not exists(FunctionCall fc, int index | + // terminator value is used in a non-terminating position + nonTrailingVarArgValue(fc, index) = result + ) + } + + predicate isWhitelisted() { + this.hasQualifiedName("open") or + this.hasQualifiedName("fcntl") or + this.hasQualifiedName("ptrace") + } +} + +from VarargsFunction f, FunctionCall fc, string terminator, int cnt +where terminator = f.normalTerminator(cnt) + and fc = f.getACallToThisFunction() + and not normalisedExprValue(f.trailingArgumentIn(fc)) = terminator + and not f.isWhitelisted() +select fc, "Calls to $@ should use the value " + terminator + + " as a terminator (" + cnt + " calls do).", f, f.getQualifiedName() diff --git a/cpp/ql/src/Security/CWE/CWE-129/ImproperArrayIndexValidation.qhelp b/cpp/ql/src/Security/CWE/CWE-129/ImproperArrayIndexValidation.qhelp new file mode 100644 index 000000000000..9cf4426231c5 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-129/ImproperArrayIndexValidation.qhelp @@ -0,0 +1,37 @@ + + + +

    C and C++ do not have built-in bounds checking for array indexing +expressions such as x[i]. If i is out of +bounds then the program will read/write whatever data happens to be at +that address. An attacker who is able to control the value +of i might be able to read or modify data which they are +not authorized to access. +

    +
    + + +

    Always check the bounds of array indexing expressions, especially +if the index value is derived from user-controlled data.

    +
    + + + +

    In this example, a string is read from a socket and converted to +an int. This int is then used to index +the data array. Because the index value is a +user-controlled value, it could be out of bounds.

    + + + +

    Below, the problem has been fixed by adding a guard:

    + + + +
    + + + +
    diff --git a/cpp/ql/src/Security/CWE/CWE-129/ImproperArrayIndexValidation.ql b/cpp/ql/src/Security/CWE/CWE-129/ImproperArrayIndexValidation.ql new file mode 100644 index 000000000000..a50e164e8af5 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-129/ImproperArrayIndexValidation.ql @@ -0,0 +1,40 @@ +/** + * @name Unclear validation of array index + * @description Accessing an array without first checking + * that the index is within the bounds of the array can + * cause undefined behavior and can also be a security risk. + * @kind problem + * @id cpp/unclear-array-index-validation + * @problem.severity warning + * @tags security + * external/cwe/cwe-129 + */ +import cpp +import semmle.code.cpp.controlflow.Guards +private import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils +import semmle.code.cpp.security.TaintTracking + +predicate hasUpperBound(VariableAccess offsetExpr) { + exists( + BasicBlock controlled, LocalScopeVariable offsetVar, SsaDefinition def + | controlled.contains(offsetExpr) and + linearBoundControls(controlled, def, offsetVar) and + offsetExpr = def.getAUse(offsetVar)) +} + +pragma[noinline] +predicate linearBoundControls(BasicBlock controlled, SsaDefinition def, LocalScopeVariable offsetVar) { + exists(GuardCondition guard, boolean branch | + guard.controls(controlled, branch) and + cmpWithLinearBound(guard, def.getAUse(offsetVar), Lesser(), branch) + ) +} + +from Expr origin, ArrayExpr arrayExpr, VariableAccess offsetExpr +where tainted(origin, offsetExpr) + and offsetExpr = arrayExpr.getArrayOffset() + and not hasUpperBound(offsetExpr) +select + offsetExpr, + "$@ flows to here and is used in an array indexing expression, potentially causing an invalid access.", + origin, "User-provided value" diff --git a/cpp/ql/src/Security/CWE/CWE-129/ImproperArrayIndexValidationBad.c b/cpp/ql/src/Security/CWE/CWE-129/ImproperArrayIndexValidationBad.c new file mode 100644 index 000000000000..76fbc588bf09 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-129/ImproperArrayIndexValidationBad.c @@ -0,0 +1,17 @@ +int example(int socket, int data[]) { + char inputBuffer[CHAR_ARRAY_SIZE]; + int recvResult; + int i; + + recvResult = recv(socket, inputBuffer, CHAR_ARRAY_SIZE - 1, 0); + if (recvResult < 0) + { + return -1; + } + + inputBuffer[recvResult] = '\0'; + i = atoi(inputBuffer); + + // BAD: i has not been validated. + return data[i]; +} diff --git a/cpp/ql/src/Security/CWE/CWE-129/ImproperArrayIndexValidationGood.c b/cpp/ql/src/Security/CWE/CWE-129/ImproperArrayIndexValidationGood.c new file mode 100644 index 000000000000..9e69d5986b32 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-129/ImproperArrayIndexValidationGood.c @@ -0,0 +1,21 @@ +int example(int socket, int data[], int ndata) { + char inputBuffer[CHAR_ARRAY_SIZE]; + int recvResult; + int i; + + recvResult = recv(socket, inputBuffer, CHAR_ARRAY_SIZE - 1, 0); + if (recvResult < 0) + { + return -1; + } + + inputBuffer[recvResult] = '\0'; + i = atoi(inputBuffer); + + if (i < 0 || ndata <= i) { + return -1; + } + + // GOOD: i has been validated. + return data[i]; +} diff --git a/cpp/ql/src/Security/CWE/CWE-131/NoSpaceForZeroTerminator.c b/cpp/ql/src/Security/CWE/CWE-131/NoSpaceForZeroTerminator.c new file mode 100644 index 000000000000..0dc3255da6b9 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-131/NoSpaceForZeroTerminator.c @@ -0,0 +1,11 @@ + +void flawed_strdup(const char *input) +{ + char *copy; + + /* Fail to allocate space for terminating '\0' */ + copy = (char *)malloc(strlen(input)); + strcpy(copy, input); + return copy; +} + diff --git a/cpp/ql/src/Security/CWE/CWE-131/NoSpaceForZeroTerminator.qhelp b/cpp/ql/src/Security/CWE/CWE-131/NoSpaceForZeroTerminator.qhelp new file mode 100644 index 000000000000..1bed6a59f523 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-131/NoSpaceForZeroTerminator.qhelp @@ -0,0 +1,37 @@ + + + + +

    +This rule identifies calls to malloc that call strlen to determine +the required buffer size, but do not allocate space for the zero terminator. +

    + +
    + +

    +The expression highlighted by this rule creates a buffer that is of insufficient size to contain +the data being copied. This makes the code vulnerable to buffer overflow which can result in anything from a segmentation fault to a security vulnerability (particularly if the array is on stack-allocated memory). +

    + +

    +Increase the size of the buffer being allocated by one or +replace malloc, strcpy pairs with a call to strdup +

    + +
    + + + + + +
  • CERT C Coding +Standard: MEM35-C. Allocate +sufficient memory for an object.
  • + + + +
    +
    diff --git a/cpp/ql/src/Security/CWE/CWE-131/NoSpaceForZeroTerminator.ql b/cpp/ql/src/Security/CWE/CWE-131/NoSpaceForZeroTerminator.ql new file mode 100644 index 000000000000..20a90caa761c --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-131/NoSpaceForZeroTerminator.ql @@ -0,0 +1,38 @@ +/** + * @name No space for zero terminator + * @description Allocating a buffer using 'malloc' without ensuring that + * there is always space for the entire string and a zero + * terminator can cause a buffer overrun. + * @kind problem + * @problem.severity error + * @precision high + * @id cpp/no-space-for-terminator + * @tags reliability + * security + * external/cwe/cwe-131 + * external/cwe/cwe-120 + * external/cwe/cwe-122 + */ +import cpp + +class MallocCall extends FunctionCall { + MallocCall() { this.getTarget().hasGlobalName("malloc") } + + Expr getAllocatedSize() { + if this.getArgument(0) instanceof VariableAccess then + exists(LocalScopeVariable v, ControlFlowNode def | + definitionUsePair(v, def, this.getArgument(0)) and + exprDefinition(v, def, result)) + else + result = this.getArgument(0) + } +} + +predicate terminationProblem(MallocCall malloc, string msg) { + malloc.getAllocatedSize() instanceof StrlenCall and + msg = "This allocation does not include space to null-terminate the string." +} + +from Expr problem, string msg +where terminationProblem(problem, msg) +select problem, msg diff --git a/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatString.c b/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatString.c new file mode 100644 index 000000000000..c3733120fa0f --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatString.c @@ -0,0 +1,16 @@ +#include + +void printWrapper(char *str) { + printf(str); +} + +int main(int argc, char **argv) { + // This should be avoided + printf(argv[1]); + + // This should be avoided too, because it has the same effect + printWrapper(argv[1]); + + // This is fine + printf("%s", argv[1]); +} \ No newline at end of file diff --git a/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatString.qhelp b/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatString.qhelp new file mode 100644 index 000000000000..aec75e161fb4 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatString.qhelp @@ -0,0 +1,27 @@ + + + +

    The program uses input from the user as a format string for printf style functions. This can lead to buffer overflows or data representation problems. An attacker can exploit this weakness to crash the program, disclose information or even execute arbitrary code.

    + +

    The results of this rule do not include inputs from the user that are transferred through global variables. +Those can be found in the related rule "Uncontrolled format string (through global variable)".

    + +
    + +

    Use constant expressions as the format strings. If you need to print a value from the user, use printf("%s", value_from_user).

    + +
    + + + + + + +
  • CERT C Coding +Standard: FIO30-C. Exclude user input from format strings.
  • + + +
    +
    diff --git a/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatString.ql b/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatString.ql new file mode 100644 index 000000000000..4fa6efb2d916 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatString.ql @@ -0,0 +1,28 @@ +/** + * @name Uncontrolled format string + * @description Using externally-controlled format strings in + * printf-style functions can lead to buffer overflows + * or data representation problems. + * @kind problem + * @problem.severity warning + * @precision medium + * @id cpp/tainted-format-string + * @tags reliability + * security + * external/cwe/cwe-134 + */ + +import cpp +import semmle.code.cpp.security.Security +import semmle.code.cpp.security.FunctionWithWrappers +import semmle.code.cpp.security.TaintTracking + +from PrintfLikeFunction printf, Expr arg, string printfFunction, + Expr userValue, string cause +where + printf.outermostWrapperFunctionCall(arg, printfFunction) + and tainted(userValue, arg) + and isUserInput(userValue, cause) +select arg, + "The value of this argument may come from $@ and is being used as a formatting argument to " + printfFunction, + userValue, cause diff --git a/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatStringThroughGlobalVar.c b/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatStringThroughGlobalVar.c new file mode 100644 index 000000000000..2700109a586c --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatStringThroughGlobalVar.c @@ -0,0 +1,24 @@ +#include + +char *copy; + +void copyArgv(char **argv) { + copy = argv[1]; +} + +void printWrapper(char *str) { + printf(str); +} + +int main(int argc, char **argv) { + copyArgv(argv); + + // This should be avoided + printf(copy); + + // This should be avoided too, because it has the same effect + printWrapper(copy); + + // This is fine + printf("%s", copy); +} diff --git a/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatStringThroughGlobalVar.qhelp b/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatStringThroughGlobalVar.qhelp new file mode 100644 index 000000000000..80b84580a57f --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatStringThroughGlobalVar.qhelp @@ -0,0 +1,36 @@ + + + +

    The program uses input from the user, propagated via a global variable, as a format string for printf style functions. +This can lead to buffer overflows or data representation problems. An attacker can exploit this weakness to crash the program, +disclose information or even execute arbitrary code.

    + +

    This rule only identifies inputs from the user that are transferred through global variables before being used in printf style functions. +Analyzing the flow of data through global variables is more prone to errors and so this rule may identify some examples of code where +the input is not really from the user. For example, when a global variable is set in two places, one that comes from the user and one that does not. +In this case we would mark all usages of the global variable as input from the user, but the input from the user may always came after the call to the +printf style functions.

    + +

    The results of this rule should be considered alongside the related rule "Uncontrolled format string" which tracks the flow of the +values input by a user, excluding global variables, until the values are used as the format argument for a printf like function call.

    + +
    + +

    Use constant expressions as the format strings. If you need to print a value from the user, use printf("%s", value_from_user).

    + +
    + + + + + + +
  • CERT C Coding +Standard: FIO30-C. Exclude +user input from format strings.
  • + + +
    +
    diff --git a/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatStringThroughGlobalVar.ql b/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatStringThroughGlobalVar.ql new file mode 100644 index 000000000000..e4beeb8c24b4 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatStringThroughGlobalVar.ql @@ -0,0 +1,28 @@ +/** + * @name Uncontrolled format string (through global variable) + * @description Using externally-controlled format strings in + * printf-style functions can lead to buffer overflows + * or data representation problems. + * @kind problem + * @problem.severity warning + * @precision medium + * @id cpp/tainted-format-string-through-global + * @tags reliability + * security + * external/cwe/cwe-134 + */ + +import cpp +import semmle.code.cpp.security.FunctionWithWrappers +import semmle.code.cpp.security.Security +import semmle.code.cpp.security.TaintTracking + +from PrintfLikeFunction printf, Expr arg, string printfFunction, + Expr userValue, string cause, string globalVar +where printf.outermostWrapperFunctionCall(arg, printfFunction) + and not tainted(_, arg) + and taintedIncludingGlobalVars(userValue, arg, globalVar) + and isUserInput(userValue, cause) +select arg, + "This value may flow through $@, originating from $@, and is a formatting argument to " + printfFunction + ".", + globalVarFromId(globalVar), globalVar, userValue, cause diff --git a/cpp/ql/src/Security/CWE/CWE-170/ImproperNullTerminationTainted.qhelp b/cpp/ql/src/Security/CWE/CWE-170/ImproperNullTerminationTainted.qhelp new file mode 100644 index 000000000000..42c6719b8380 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-170/ImproperNullTerminationTainted.qhelp @@ -0,0 +1,49 @@ + + + + + +

    Built-in C string functions such as strcpy require that their +input string arguments are null terminated. If the input string arguments are +not null terminated, these functions will read/write beyond the length of the +buffer containing the string, resulting in either buffer over-read or buffer +overflow, respectively. +

    + +
    + + +

    Always guard against user-controlled strings that may not be null terminated. +Otherwise, an attacker may be able to supply input without null termination. +

    + +
    + +

    In this example, a string is read from a file using the read +function. Because the value may be user-controlled, the string may not be +null terminated, in which case the call to strcpy will perform +a buffer overwrite on cpy, potentially resulting in a program +crash. +

    + + + +

    In the revised example, the number of bytes read is recorded in the variable +count. In addition to checking that the read succeeds (the condition +count >= 0), a null terminator is inserted before the call to +strcpy. +

    + + + + +
    + + +
  • B. Chess and J. West, Secure Programming with Static Analysis, 6.2 Maintaining the Null Terminator. Addison-Wesley. 2007.
  • +
  • Linux Programmer's Manual: READ(2), STRCPY(3).
  • + +
    +
    diff --git a/cpp/ql/src/Security/CWE/CWE-170/ImproperNullTerminationTainted.ql b/cpp/ql/src/Security/CWE/CWE-170/ImproperNullTerminationTainted.ql new file mode 100644 index 000000000000..0f93593cfd28 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-170/ImproperNullTerminationTainted.ql @@ -0,0 +1,93 @@ +/** + * @name User-controlled data may not be null terminated + * @description String operations on user-controlled strings can result in + * buffer overflow or buffer over-read. + * @kind problem + * @id cpp/user-controlled-null-termination-tainted + * @problem.severity warning + * @tags security + * external/cwe/cwe-170 + */ + +import cpp +import semmle.code.cpp.commons.NullTermination +import semmle.code.cpp.security.TaintTracking + +/** A user-controlled expression that may not be null terminated. */ +class TaintSource extends VariableAccess { + TaintSource() { + exists(SecurityOptions x, string cause | + this.getTarget() instanceof SemanticStackVariable and + x.isUserInput(this, cause) | + cause = "read" or + cause = "fread" or + cause = "recv" or + cause = "recvfrom" or + cause = "recvmsg" + ) + } + + /** + * Holds if `sink` is a tainted variable access that must be null + * terminated. + */ + private predicate isSink(VariableAccess sink) { + tainted(this, sink) and + variableMustBeNullTerminated(sink) + } + + /** + * Holds if this source can reach `va`, possibly using intermediate + * reassignments. + */ + private predicate sourceReaches(VariableAccess va) { + definitionUsePair(_, this, va) + or + exists(VariableAccess mid, Expr def | + sourceReaches(mid) and + exprDefinition(_, def, mid) and + definitionUsePair(_, def, va) + ) + } + + /** + * Holds if the sink `sink` is reachable both from this source and + * from `va`, possibly using intermediate reassignments. + */ + private predicate reachesSink(VariableAccess va, VariableAccess sink) { + isSink(sink) and + va = sink + or + exists(VariableAccess mid, Expr def | + reachesSink(mid, sink) and + exprDefinition(_, def, va) and + definitionUsePair(_, def, mid) + ) + } + + /** + * Holds if `sink` is a tainted variable access that must be null + * terminated, and no access which null terminates its contents can + * either reach the sink or be reached from the source. (Ideally, + * we should instead look for such accesses only on the path from + * this source to `sink` found via `tainted(source, sink)`.) + * */ + predicate reaches(VariableAccess sink) { + isSink(sink) and + not exists(VariableAccess va | + va != this and + va != sink and + mayAddNullTerminator(_, va) | + sourceReaches(va) + or + reachesSink(va, sink) + ) + } +} + +from TaintSource source, VariableAccess sink +where source.reaches(sink) +select + sink, + "$@ flows to here and may not be null terminated.", + source, "User-provided value" diff --git a/cpp/ql/src/Security/CWE/CWE-170/ImproperNullTerminationTaintedBad.cpp b/cpp/ql/src/Security/CWE/CWE-170/ImproperNullTerminationTaintedBad.cpp new file mode 100644 index 000000000000..094f9ceb9ac3 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-170/ImproperNullTerminationTaintedBad.cpp @@ -0,0 +1,4 @@ +char buf[BUF_SIZE]; +read(fd, buf, BUF_SIZE); +char cpy[BUF_SIZE]; +strcpy(cpy, buf); \ No newline at end of file diff --git a/cpp/ql/src/Security/CWE/CWE-170/ImproperNullTerminationTaintedGood.cpp b/cpp/ql/src/Security/CWE/CWE-170/ImproperNullTerminationTaintedGood.cpp new file mode 100644 index 000000000000..8bf081029d6c --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-170/ImproperNullTerminationTaintedGood.cpp @@ -0,0 +1,7 @@ +char buf[BUF_SIZE]; +int count = read(fd, buf, BUF_SIZE); +if (count >= 0) { + buf[count < BUF_SIZE ? count : BUF_SIZE - 1] = '\0'; + char cpy[BUF_SIZE]; + strcpy(cpy, buf); +} \ No newline at end of file diff --git a/cpp/ql/src/Security/CWE/CWE-190/ArithmeticTainted.c b/cpp/ql/src/Security/CWE/CWE-190/ArithmeticTainted.c new file mode 100644 index 000000000000..2a108fbc6460 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-190/ArithmeticTainted.c @@ -0,0 +1,18 @@ +int main(int argc, char** argv) { + char buffer[20]; + fgets(buffer, 20, stdin); + + int num = atoi(buffer); + // BAD: may overflow if input is very large + int scaled = num + 1000; + + // ... + + int num2 = atoi(buffer); + int scaled2; + // GOOD: use a guard to prevent overflow + if (num2 < INT_MAX-1000) + scaled2 = num2 + 1000; + else + scaled2 = INT_MAX; +} diff --git a/cpp/ql/src/Security/CWE/CWE-190/ArithmeticTainted.qhelp b/cpp/ql/src/Security/CWE/CWE-190/ArithmeticTainted.qhelp new file mode 100644 index 000000000000..b09c84f7cfe8 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-190/ArithmeticTainted.qhelp @@ -0,0 +1,44 @@ + + + +

    Performing calculations on user-controlled data can result in integer overflows +unless the input is validated.

    + +

    If the user is free to enter very large numbers, even arithmetic operations that would usually +result in a small change in magnitude may result in overflows.

    + +
    + + +

    Always guard against overflow in arithmetic operations on user-controlled data by doing one of the +following:

    + +
      +
    • Validate the user input.
    • +
    • Define a guard on the arithmetic expression, so that the operation is performed only if the +result can be known to be less than, or equal to, the maximum value for the type, for example INT_MAX.
    • +
    • Use a wider type, so that larger input values do not cause overflow.
    • +
    + +
    + + +

    In this example, a value is read from standard input into an int. Because the value +is a user-controlled value, it could be extremely large. Performing arithmetic operations on this +value could therefore cause an overflow. To avoid this happening, the example shows how to perform +a check before performing a multiplication.

    + + + +
    + + + + + + + +
    diff --git a/cpp/ql/src/Security/CWE/CWE-190/ArithmeticTainted.ql b/cpp/ql/src/Security/CWE/CWE-190/ArithmeticTainted.ql new file mode 100644 index 000000000000..16382cb86a77 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-190/ArithmeticTainted.ql @@ -0,0 +1,33 @@ +/** + * @name User-controlled data in arithmetic expression + * @description Arithmetic operations on user-controlled data that is + * not validated can cause overflows. + * @kind problem + * @problem.severity warning + * @precision medium + * @id cpp/tainted-arithmetic + * @tags security + * external/cwe/cwe-190 + * external/cwe/cwe-191 + */ +import cpp + +import semmle.code.cpp.security.Overflow +import semmle.code.cpp.security.Security +import semmle.code.cpp.security.TaintTracking + +predicate taintedVarAccess(Expr origin, VariableAccess va) { + isUserInput(origin, _) and + tainted(origin, va) +} + +from Expr origin, BinaryArithmeticOperation op, VariableAccess va, string effect +where taintedVarAccess(origin, va) + and op.getAnOperand() = va + and + ( + (missingGuardAgainstUnderflow(op, va) and effect = "underflow") or + (missingGuardAgainstOverflow(op, va) and effect = "overflow") + ) +select va, "$@ flows to here and is used in arithmetic, potentially causing an " + effect + ".", + origin, "User-provided value" diff --git a/cpp/ql/src/Security/CWE/CWE-190/ArithmeticUncontrolled.c b/cpp/ql/src/Security/CWE/CWE-190/ArithmeticUncontrolled.c new file mode 100644 index 000000000000..fee75d549188 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-190/ArithmeticUncontrolled.c @@ -0,0 +1,15 @@ +int main(int argc, char** argv) { + int i = rand(); + // BAD: potential overflow + int j = i + 1000; + + // ... + + int n = rand(); + int k; + // GOOD: use a guard to prevent overflow + if (n < INT_MAX-1000) + k = n + 1000; + else + k = INT_MAX; +} diff --git a/cpp/ql/src/Security/CWE/CWE-190/ArithmeticUncontrolled.qhelp b/cpp/ql/src/Security/CWE/CWE-190/ArithmeticUncontrolled.qhelp new file mode 100644 index 000000000000..265c6172efce --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-190/ArithmeticUncontrolled.qhelp @@ -0,0 +1,44 @@ + + + +

    Performing calculations on uncontrolled data can result in integer overflows +unless the input is validated.

    + +

    If the data is not under your control, and can take extremely large values, +even arithmetic operations that would usually result in a small change in magnitude may result in overflows.

    + +
    + + +

    Always guard against overflow in arithmetic operations on uncontrolled data by doing one of the +following:

    + +
      +
    • Validate the data.
    • +
    • Define a guard on the arithmetic expression, so that the operation is performed only if the +result can be known to be less than, or equal to, the maximum value for the type, for example INT_MAX.
    • +
    • Use a wider type, so that larger input values do not cause overflow.
    • +
    + +
    + + +

    In this example, a random integer is generated. Because the value +is not controlled by the programmer, it could be extremely large. Performing arithmetic operations on this +value could therefore cause an overflow. To avoid this happening, the example shows how to perform +a check before performing an arithmetic operation.

    + + + +
    + + + + + + + +
    diff --git a/cpp/ql/src/Security/CWE/CWE-190/ArithmeticUncontrolled.ql b/cpp/ql/src/Security/CWE/CWE-190/ArithmeticUncontrolled.ql new file mode 100644 index 000000000000..a6d03dfe2d58 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-190/ArithmeticUncontrolled.ql @@ -0,0 +1,59 @@ +/** + * @name Uncontrolled data in arithmetic expression + * @description Arithmetic operations on uncontrolled data that is not + * validated can cause overflows. + * @kind problem + * @problem.severity warning + * @precision medium + * @id cpp/uncontrolled-arithmetic + * @tags security + * external/cwe/cwe-190 + * external/cwe/cwe-191 + */ +import cpp + +import semmle.code.cpp.security.Overflow +import semmle.code.cpp.security.Security +import semmle.code.cpp.security.TaintTracking + +predicate isRandValue(Expr e) { + e.(FunctionCall).getTarget().getName() = "rand" or + exists(FunctionCall fc | + fc = e.(MacroInvocationExpr).getInvocation().getExpr().getAChild*() + | fc.getTarget().getName() = "rand" + ) +} + +class SecurityOptionsArith extends SecurityOptions { + override predicate isUserInput(Expr expr, string cause) { + isRandValue(expr) and cause = "rand" + and not expr.getParent*() instanceof DivExpr + } +} + +predicate taintedVarAccess(Expr origin, VariableAccess va) { + isUserInput(origin, _) and + tainted(origin, va) +} + +/** + * A value that undergoes division is likely to be bounded within a safe + * range. + */ +predicate guardedByAssignDiv(Expr origin) { + isUserInput(origin, _) and + exists(AssignDivExpr div, VariableAccess va | + tainted(origin, va) and div.getLValue() = va) +} + +from Expr origin, BinaryArithmeticOperation op, VariableAccess va, string effect +where taintedVarAccess(origin, va) + and op.getAnOperand() = va + and + ( + (missingGuardAgainstUnderflow(op, va) and effect = "underflow") or + (missingGuardAgainstOverflow(op, va) and effect = "overflow") + ) + and not guardedByAssignDiv(origin) +select va, "$@ flows to here and is used in arithmetic, potentially causing an " + effect + ".", + origin, "Uncontrolled value" diff --git a/cpp/ql/src/Security/CWE/CWE-190/ArithmeticWithExtremeValues.c b/cpp/ql/src/Security/CWE/CWE-190/ArithmeticWithExtremeValues.c new file mode 100644 index 000000000000..69e22d2fc109 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-190/ArithmeticWithExtremeValues.c @@ -0,0 +1,11 @@ +int main(int argc, char** argv) { + int i = INT_MAX; + // BAD: overflow + int j = i + 1; + + // ... + + int l = INT_MAX; + // GOOD: no overflow + long k = (long)l + 1; +} diff --git a/cpp/ql/src/Security/CWE/CWE-190/ArithmeticWithExtremeValues.qhelp b/cpp/ql/src/Security/CWE/CWE-190/ArithmeticWithExtremeValues.qhelp new file mode 100644 index 000000000000..b5b490591fde --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-190/ArithmeticWithExtremeValues.qhelp @@ -0,0 +1,33 @@ + + + +

    Assigning the maximum or minimum value for a type to a variable of that type and then using the +variable in calculations may cause overflows.

    + +
    + + +

    Before using the variable, ensure that it is reassigned a value that does not cause an overflow, +or use a wider type to do the arithmetic.

    + +
    + + +

    In this example, assigning INT_MAX to a variable and adding one causes +an overflow. However, casting to a long beforehand ensures that the arithmetic +is done in the wider type, and so does not overflow.

    + + + +
    + + + + + + + +
    diff --git a/cpp/ql/src/Security/CWE/CWE-190/ArithmeticWithExtremeValues.ql b/cpp/ql/src/Security/CWE/CWE-190/ArithmeticWithExtremeValues.ql new file mode 100644 index 000000000000..f218328a19ea --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-190/ArithmeticWithExtremeValues.ql @@ -0,0 +1,57 @@ +/** + * @name Use of extreme values in arithmetic expression + * @description If a variable is assigned the maximum or minimum value + * for that variable's type and is then used in an + * arithmetic expression, this may result in an overflow. + * @kind problem + * @id cpp/arithmetic-with-extreme-values + * @problem.severity warning + * @precision low + * @tags security + * reliability + * external/cwe/cwe-190 + * external/cwe/cwe-191 + */ +import cpp + +import semmle.code.cpp.security.Overflow +import semmle.code.cpp.security.Security +import semmle.code.cpp.security.TaintTracking + +predicate isMaxValue(MacroInvocationExpr mie) { + mie.getMacroName() = "CHAR_MAX" or + mie.getMacroName() = "LLONG_MAX" or + mie.getMacroName() = "INT_MAX" or + mie.getMacroName() = "SHRT_MAX" or + mie.getMacroName() = "UINT_MAX" +} + +predicate isMinValue(MacroInvocationExpr mie) { + mie.getMacroName() = "CHAR_MIN" or + mie.getMacroName() = "LLONG_MIN" or + mie.getMacroName() = "INT_MIN" or + mie.getMacroName() = "SHRT_MIN" +} + +class SecurityOptionsArith extends SecurityOptions { + override predicate isUserInput(Expr expr, string cause) { + (isMaxValue(expr) and cause = "max value") or + (isMinValue(expr) and cause = "min value") + } +} + +predicate taintedVarAccess(Expr origin, VariableAccess va) { + isUserInput(origin, _) and + tainted(origin, va) +} + +from Expr origin, BinaryArithmeticOperation op, VariableAccess va, string effect +where taintedVarAccess(origin, va) + and op.getAnOperand() = va + and + ( + (missingGuardAgainstUnderflow(op, va) and effect = "underflow") or + (missingGuardAgainstOverflow(op, va) and effect = "overflow") + ) +select va, "$@ flows to here and is used in arithmetic, potentially causing an " + effect + ".", + origin, "Extreme value" diff --git a/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.c b/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.c new file mode 100644 index 000000000000..4f3b0dd2cf49 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.c @@ -0,0 +1,28 @@ +void main(int argc, char **argv) { + uint32_t big_num = INT32_MAX; + char buf[big_num]; + int16_t bytes_received = 0; + int max_get = INT16_MAX + 1; + + // BAD: 'bytes_received' is compared with a value of a wider type. + // 'bytes_received' overflows before reaching 'max_get', + // causing an infinite loop + while (bytes_received < max_get) + bytes_received += get_from_input(buf, bytes_received); + } + + uint32_t bytes_received = 0; + + // GOOD: 'bytes_received2' has a type at least as wide as 'max_get' + while (bytes_received < max_get) { + bytes_received += get_from_input(buf, bytes_received); + } + +} + + +int getFromInput(char *buf, short pos) { + // write to buf + // ... + return 1; +} diff --git a/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.qhelp b/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.qhelp new file mode 100644 index 000000000000..4506eb053cef --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.qhelp @@ -0,0 +1,41 @@ + + + +

    In a loop condition, comparison of a value of a narrow type with a value of a wide type may +result in unexpected behavior if the wider value is sufficiently large (or small). This is because +the narrower value may overflow. This can lead to an infinite loop.

    + +
    + + +

    Change the types of the compared values so that the value on the narrower side of the +comparison is at least as wide as the value it is being compared with.

    + +
    + + +

    In this example, bytes_received is compared against max_get in a +while loop. However, bytes_received is an int16_t, and +max_get is an int32_t. Because max_get is larger than +INT16_MAX, the loop condition is always true, so the loop never +terminates.

    + +

    This problem is avoided in the 'GOOD' case because bytes_received2 is an +int32_t, which is as wide as the type of max_get.

    + + + +
    + + +
  • + Data type ranges +
  • + +
  • + INT18-C. Evaluate integer expressions in a larger size before comparing or assigning to that size +
  • +
    +
    \ No newline at end of file diff --git a/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql b/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql new file mode 100644 index 000000000000..af343fc991f8 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql @@ -0,0 +1,69 @@ +/** + * @name Comparison of narrow type with wide type in loop condition + * @description Comparisons between types of different widths in a loop + * condition can cause the loop to behave unexpectedly. + * @id cpp/comparison-with-wider-type + * @kind problem + * @problem.severity warning + * @precision medium + * @tags reliability + * security + * external/cwe/cwe-190 + * external/cwe/cwe-197 + * external/cwe/cwe-835 + * +*/ + +import cpp +import semmle.code.cpp.controlflow.Dominance +import semmle.code.cpp.controlflow.SSA +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis + +/** + * C++ references are all pointer width, but the comparison takes place with + * the pointed-to value + */ +int getComparisonSize(Expr e) { + if e.getType() instanceof ReferenceType + then result = e.getType().(ReferenceType).getBaseType().getSize() + else result = e.getType().getSize() +} + + +predicate loopVariant(VariableAccess e, Loop loop) { + exists (SsaDefinition d + | d.getAUse(e.getTarget()) = e + | d.getAnUltimateDefiningValue(e.getTarget()) = loop.getCondition().getAChild*() + or d.getAnUltimateDefiningValue(e.getTarget()).getEnclosingStmt().getParent*() + = loop.getStmt() + or d.getAnUltimateDefiningValue(e.getTarget()) + = loop.(ForStmt).getUpdate().getAChild*()) +} + +Element friendlyLoc(Expr e) { + result = e.(Access).getTarget() or + result = e.(Call).getTarget() or + not e instanceof Access and not e instanceof Call and result = e +} + +from Loop l, RelationalOperation rel, Expr small, Expr large +where small = rel.getLesserOperand() + and large = rel.getGreaterOperand() + and rel = l.getCondition().getAChild*() + and upperBound(large).log2() > getComparisonSize(small) * 8 + // Ignore cases where the smaller type is int or larger + // These are still bugs, but you should need a very large string or array to + // trigger them. We will want to disable this for some applications, but it's + // very noisy on codebases that started as 32-bit + and small.getExplicitlyConverted().getType().getSize() < 4 + // Ignore cases where integer promotion has occurred on /, -, or >> expressions. + and not getComparisonSize(large.(DivExpr).getLeftOperand() + .getExplicitlyConverted()) <= getComparisonSize(small) + and not getComparisonSize(large.(SubExpr).getLeftOperand() + .getExplicitlyConverted()) <= getComparisonSize(small) + and not getComparisonSize(large.(RShiftExpr).getLeftOperand() + .getExplicitlyConverted()) <= getComparisonSize(small) + // ignore loop-invariant smaller variables + and loopVariant(small.getAChild*(), l) +select rel, "Comparison between $@ of type " + small.getType().getName() + " and $@ of wider type " + large.getType().getName() + ".", + friendlyLoc(small), small.toString(), friendlyLoc(large), large.toString() diff --git a/cpp/ql/src/Security/CWE/CWE-190/IntegerOverflowTainted.qhelp b/cpp/ql/src/Security/CWE/CWE-190/IntegerOverflowTainted.qhelp new file mode 100644 index 000000000000..01bb78be887b --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-190/IntegerOverflowTainted.qhelp @@ -0,0 +1,55 @@ + + + +

    Performing calculations on user-controlled data can result in +integer overflows unless the input is validated.

    + +

    Integer overflow occurs when the result of an arithmetic expression +is too large to be represented by the (integer) output type of the +expression. For example, if the result of the expression is 200, but +the output type is a signed 8-bit integer, then overflow occurs +because the largest value that can be represented is 127. The behavior +of overflow is implementation defined, but the most common +implementation is two's complement arithmetic, in which case the +result is -56. Overflow can cause unexpected results, particularly +when a large value overflows and the result is negative. It can also +pose a security risk if the value of the expression is controllable by +user, because it could enable an attacker to deliberately cause an +overflow.

    + +

    Negative integer overflow is another form of integer overflow, +in which a negative result cannot be represented in the output type.

    +
    + + + +

    Always guard against overflow in arithmetic operations on +user-controlled data by doing one of the following:

    + +
      +
    • Validate the user input.
    • +
    • Define a guard on the arithmetic expression, so that the operation +is performed only if the result can be known to be less than, or equal +to, the maximum value for the type, for +example INT_MAX.
    • +
    • Use a wider type, so that larger input values do not cause +overflow.
    • +
    + +
    + + +

    In this example, a value is read from standard input into an int. Because the value +is a user-controlled value, it could be extremely large. Performing arithmetic operations on this +value could therefore cause an overflow. To avoid this happening, the example shows how to perform +a check before performing a multiplication.

    + + + +
    + + + +
    diff --git a/cpp/ql/src/Security/CWE/CWE-190/IntegerOverflowTainted.ql b/cpp/ql/src/Security/CWE/CWE-190/IntegerOverflowTainted.ql new file mode 100644 index 000000000000..3bb064086d81 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-190/IntegerOverflowTainted.ql @@ -0,0 +1,35 @@ +/** + * @name Potential integer arithmetic overflow + * @description A user-controlled integer arithmetic expression + * that is not validated can cause overflows. + * @kind problem + * @id cpp/integer-overflow-tainted + * @problem.severity warning + * @tags security + * external/cwe/cwe-190 + * external/cwe/cwe-197 + * external/cwe/cwe-681 + */ +import cpp +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis +import semmle.code.cpp.security.TaintTracking + +/** Holds if `expr` might overflow. */ +predicate outOfBoundsExpr(Expr expr, string kind) { + if convertedExprMightOverflowPositively(expr) then + kind = "overflow" + else if convertedExprMightOverflowNegatively(expr) then + kind = "overflow negatively" + else + none() +} + +from Expr use, Expr origin, string kind +where outOfBoundsExpr(use, kind) and tainted(origin, use) and origin != use +and not inSystemMacroExpansion(use) +// Avoid double-counting: don't include all the conversions of `use`. +and not (use instanceof Conversion) +select + use, + "$@ flows to here and is used in an expression which might " + kind + ".", + origin, "User-provided value" diff --git a/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.c b/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.c new file mode 100644 index 000000000000..7a84a1aad13c --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.c @@ -0,0 +1,11 @@ +int factor = atoi(getenv("BRANCHING_FACTOR")); + +// GOOD: Prevent overflow by checking the input +if (factor < 0 || factor > 1000) { + log("Factor out of range (%d)\n", factor); + return -1; +} + +// This line can allocate too little memory if factor +// is very large. +char **root_node = (char **) malloc(factor * sizeof(char *)); diff --git a/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.qhelp b/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.qhelp new file mode 100644 index 000000000000..3b8524608871 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.qhelp @@ -0,0 +1,46 @@ + + + +

    This code calculates an allocation size by multiplying a user input +by a sizeof expression. Since the user input has no +apparent guard on its magnitude, this multiplication can +overflow. When an integer multiply overflows in C, the result can wrap +around and be much smaller than intended. A later attempt to put data +into the allocated buffer can then overflow.

    + +
    + + +

    Guard all integer parameters that come from an external +user. Implement a guard with the expected range for the parameter and +make sure that the input value meets both the minimum and maximum +requirements for this range. If the input value fails this guard then +reject the request before proceeding further. If the input value +passes the guard then subsequent calculations should not overflow.

    + + +
    + + + +

    This code shows one way to guard that an input value is within the +expected range. If factor fails the guard, then an error +is returned, and the value is not used as an argument to the +subsequent call to malloc. Without this guard, the +allocated buffer might be too small to hold the data intended for it.

    + +
    + + +
  • The CERT Oracle Secure Coding Standard for C: + INT04-C. Enforce + limits on integer values originating from tainted sources.
  • + + + + +
    +
    diff --git a/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql b/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql new file mode 100644 index 000000000000..e91daca6a1e0 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql @@ -0,0 +1,28 @@ +/** + * @name Overflow in uncontrolled allocation size + * @description Allocating memory with a size controlled by an external + * user can result in integer overflow. + * @kind problem + * @problem.severity error + * @precision high + * @id cpp/uncontrolled-allocation-size + * @tags reliability + * security + * external/cwe/cwe-190 + */ + +import cpp +import semmle.code.cpp.security.TaintTracking + +from Expr source, Expr tainted, BinaryArithmeticOperation oper, + SizeofOperator sizeof, string taintCause +where tainted(source, tainted) + and oper.getAnOperand() = tainted + and oper.getOperator() = "*" + and oper.getAnOperand() = sizeof + and oper != tainted + and sizeof.getValue().toInt() > 1 + and isUserInput(source, taintCause) +select + oper, "This allocation size is derived from $@ and might overflow", + source, "user input (" + taintCause + ")" diff --git a/cpp/ql/src/Security/CWE/CWE-290/AuthenticationBypass.cpp b/cpp/ql/src/Security/CWE/CWE-290/AuthenticationBypass.cpp new file mode 100644 index 000000000000..825793f2f0c1 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-290/AuthenticationBypass.cpp @@ -0,0 +1,32 @@ + +#define BUFFER_SIZE (4 * 1024) + +void receiveData() +{ + int sock; + sockaddr_in addr, addr_from; + char buffer[BUFFER_SIZE]; + int msg_size; + socklen_t addr_from_len; + + // configure addr + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(1234); + addr.sin_addr.s_addr = INADDR_ANY; + + // create and bind the socket + sock = socket(AF_INET, SOCK_DGRAM, 0); + bind(sock, (sockaddr *)&addr, sizeof(addr)); + + // receive message + addr_from_len = sizeof(addr_from); + msg_size = recvfrom(sock, buffer, BUFFER_SIZE, 0, (sockaddr *)&addr_from, &addr_from_len); + + // BAD: the address is controllable by the user, so it + // could be spoofed to bypass the security check below. + if ((msg_size > 0) && (strcmp("127.0.0.1", inet_ntoa(addr_from.sin_addr)) == 0)) + { + // ... + } +} diff --git a/cpp/ql/src/Security/CWE/CWE-290/AuthenticationBypass.qhelp b/cpp/ql/src/Security/CWE/CWE-290/AuthenticationBypass.qhelp new file mode 100644 index 000000000000..9ba69aaf005e --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-290/AuthenticationBypass.qhelp @@ -0,0 +1,30 @@ + + + +

    Code which relies on an IP address or domain name for authentication can be exploited +by an attacker who spoofs their address.

    + +
    + + +

    IP address verification can be a useful part of an authentication scheme, +but it should not be the single factor required for authentication. Make sure +that other authentication methods are also in place.

    + +
    + + +

    In this example (taken from +CWE-290: Authentication Bypass by Spoofing), +the client is authenticated by checking that its IP address is 127.0.0.1. An attacker might be able to +bypass this authentication by spoofing their IP address.

    + + + +
    + + + +
    diff --git a/cpp/ql/src/Security/CWE/CWE-290/AuthenticationBypass.ql b/cpp/ql/src/Security/CWE/CWE-290/AuthenticationBypass.ql new file mode 100644 index 000000000000..b744095bfe07 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-290/AuthenticationBypass.ql @@ -0,0 +1,124 @@ +/** + * @name Authentication bypass by spoofing + * @description Authentication by checking that the peer's address + * matches a known IP or web address is unsafe as it is + * vulnerable to spoofing attacks. + * @kind problem + * @problem.severity warning + * @precision medium + * @id cpp/user-controlled-bypass + * @tags security + * external/cwe/cwe-290 + */ + +import semmle.code.cpp.security.TaintTracking + +predicate hardCodedAddressOrIP(StringLiteral txt) { + exists(string s + | s = txt.getValueText() + | // Hard-coded ip addresses, such as 127.0.0.1 + s.regexpMatch("\"[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+\"") or + + // Hard-coded addresses such as www.mycompany.com + s.matches("\"www.%\"") or + s.matches("\"http:%\"") or + s.matches("\"https:%\"") or + s.matches("\"%.com\"") or + s.matches("\"%.ru\"") or + s.matches("\"%.net\"") or + s.matches("\"%.org\"") or + s.matches("\"%.de\"") or + s.matches("\"%.jp\"") or + s.matches("\"%.uk\"") or + s.matches("\"%.br\"") or + s.matches("\"%.pl\"") or + s.matches("\"%.in\"") or + s.matches("\"%.it\"") or + s.matches("\"%.fr\"") or + s.matches("\"%.au\"") or + s.matches("\"%.info\"") or + s.matches("\"%.nl\"") or + s.matches("\"%.cn\"") or + s.matches("\"%.ir\"") or + s.matches("\"%.es\"") or + s.matches("\"%.cz\"") or + s.matches("\"%.biz\"") or + s.matches("\"%.ca\"") or + s.matches("\"%.eu\"") or + s.matches("\"%.ua\"") or + s.matches("\"%.kr\"") or + s.matches("\"%.za\"") or + s.matches("\"%.co\"") or + s.matches("\"%.gr\"") or + s.matches("\"%.ro\"") or + s.matches("\"%.se\"") or + s.matches("\"%.tw\"") or + s.matches("\"%.vn\"") or + s.matches("\"%.mx\"") or + s.matches("\"%.ch\"") or + s.matches("\"%.tr\"") or + s.matches("\"%.at\"") or + s.matches("\"%.be\"") or + s.matches("\"%.hu\"") or + s.matches("\"%.tv\"") or + s.matches("\"%.dk\"") or + s.matches("\"%.me\"") or + s.matches("\"%.ar\"") or + s.matches("\"%.us\"") or + s.matches("\"%.no\"") or + s.matches("\"%.sk\"") or + s.matches("\"%.fi\"") or + s.matches("\"%.id\"") or + s.matches("\"%.cl\"") or + s.matches("\"%.nz\"") or + s.matches("\"%.by\"") or + s.matches("\"%.xyz\"") or + s.matches("\"%.pt\"") or + s.matches("\"%.ie\"") or + s.matches("\"%.il\"") or + s.matches("\"%.kz\"") or + s.matches("\"%.my\"") or + s.matches("\"%.hk\"") or + s.matches("\"%.lt\"") or + s.matches("\"%.cc\"") or + s.matches("\"%.sg\"") or + s.matches("\"%.io\"") or + s.matches("\"%.edu\"") or + s.matches("\"%.gov\"")) +} + +predicate useOfHardCodedAddressOrIP(Expr use) { + hardCodedAddressOrIP(use) or + exists(Expr def, Expr src, Variable v + | useOfHardCodedAddressOrIP(src) and + exprDefinition(v, def, src) and + definitionUsePair(v, def, use)) +} + +/* + * Find IfStmts that have a hard-coded IP or web address in + * their condition. If the condition also depends on an + * untrusted input then it might be vulnerable to a spoofing + * attack. + */ +predicate hardCodedAddressInCondition(Expr source, Expr condition) { + // One of the sub-expressions of the condition is tainted. + exists(Expr taintedExpr + | taintedExpr.getParent+() = condition + | tainted(source, taintedExpr)) and + + // One of the sub-expressions of the condition is a hard-coded + // IP or web-address. + exists(Expr use + | use.getParent+() = condition + | useOfHardCodedAddressOrIP(use)) and + + condition = any(IfStmt ifStmt).getCondition() +} + +from Expr source, Expr condition +where hardCodedAddressInCondition(source, condition) +select + condition, + "Untrusted input $@ might be vulnerable to a spoofing attack.", + source, source.toString() diff --git a/cpp/ql/src/Security/CWE/CWE-311/CleartextBufferWrite.qhelp b/cpp/ql/src/Security/CWE/CWE-311/CleartextBufferWrite.qhelp new file mode 100644 index 000000000000..884b1dbdd4b4 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-311/CleartextBufferWrite.qhelp @@ -0,0 +1,5 @@ + + + diff --git a/cpp/ql/src/Security/CWE/CWE-311/CleartextBufferWrite.ql b/cpp/ql/src/Security/CWE/CWE-311/CleartextBufferWrite.ql new file mode 100644 index 000000000000..d8002212824a --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-311/CleartextBufferWrite.ql @@ -0,0 +1,27 @@ +/** + * @name Cleartext storage of sensitive information in buffer + * @description Storing sensitive information in cleartext can expose it + * to an attacker. + * @kind problem + * @problem.severity warning + * @precision medium + * @id cpp/cleartext-storage-buffer + * @tags security + * external/cwe/cwe-312 + */ + +import cpp +import semmle.code.cpp.security.BufferWrite +import semmle.code.cpp.security.TaintTracking +import semmle.code.cpp.security.SensitiveExprs + +from BufferWrite w, + Expr taintedArg, Expr taintSource, string taintCause, + SensitiveExpr dest +where tainted(taintSource, taintedArg) + and isUserInput(taintSource, taintCause) + and w.getASource() = taintedArg + and dest = w.getDest() +select w, "This write into buffer '" + dest.toString() + + "' may contain unencrypted data from $@", + taintSource, "user input (" + taintCause + ")" diff --git a/cpp/ql/src/Security/CWE/CWE-311/CleartextFileWrite.qhelp b/cpp/ql/src/Security/CWE/CWE-311/CleartextFileWrite.qhelp new file mode 100644 index 000000000000..884b1dbdd4b4 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-311/CleartextFileWrite.qhelp @@ -0,0 +1,5 @@ + + + diff --git a/cpp/ql/src/Security/CWE/CWE-311/CleartextFileWrite.ql b/cpp/ql/src/Security/CWE/CWE-311/CleartextFileWrite.ql new file mode 100644 index 000000000000..400088e2be0b --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-311/CleartextFileWrite.ql @@ -0,0 +1,22 @@ +/** + * @name Cleartext storage of sensitive information in file + * @description Storing sensitive information in cleartext can expose it + * to an attacker. + * @kind problem + * @problem.severity warning + * @precision medium + * @id cpp/cleartext-storage-file + * @tags security + * external/cwe/cwe-313 + */ + +import cpp +import semmle.code.cpp.security.SensitiveExprs +import semmle.code.cpp.security.FileWrite + +from FileWrite w, SensitiveExpr source, Expr dest +where source = w.getASource() + and dest = w.getDest() +select w, "This write into file '" + dest.toString() + + "' may contain unencrypted data from $@", + source, "this source." diff --git a/cpp/ql/src/Security/CWE/CWE-311/CleartextStorage.c b/cpp/ql/src/Security/CWE/CWE-311/CleartextStorage.c new file mode 100644 index 000000000000..e0600cc4d2f2 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-311/CleartextStorage.c @@ -0,0 +1,12 @@ +void writeCredentials() { + char *password = "cleartext password"; + FILE* file = fopen("credentials.txt", "w"); + + // BAD: write password to disk in cleartext + fputs(password, file); + + // GOOD: encrypt password first + char *encrypted = encrypt(password); + fputs(encrypted, file); +} + diff --git a/cpp/ql/src/Security/CWE/CWE-311/CleartextStorage.qhelp b/cpp/ql/src/Security/CWE/CWE-311/CleartextStorage.qhelp new file mode 100644 index 000000000000..eb9a6f8adce6 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-311/CleartextStorage.qhelp @@ -0,0 +1,39 @@ + + + +

    Sensitive information that is stored unencrypted is accessible to an attacker who gains access to the +storage.

    + +
    + + +

    Ensure that sensitive information is always encrypted before being stored, especially before writing to a file. +It may be wise to encrypt information before it is put into a buffer that may be readable in memory.

    + +

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

    + +
    + + +

    The following example shows two ways of storing user credentials in a file. In the 'BAD' case, +the credentials are simply stored in cleartext. In the 'GOOD' case, the credentials are encrypted before +storing them.

    + + + +
    + + +
  • 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/Security/CWE/CWE-313/CleartextSqliteDatabase.c b/cpp/ql/src/Security/CWE/CWE-313/CleartextSqliteDatabase.c new file mode 100644 index 000000000000..f92aa8c38684 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-313/CleartextSqliteDatabase.c @@ -0,0 +1,36 @@ + +void bad(void) { + char *password = "cleartext password"; + sqlite3 *credentialsDB; + sqlite3_stmt *stmt; + + if (sqlite3_open("credentials.db", &credentialsDB) == SQLITE_OK) { + // BAD: database opened without encryption being enabled + sqlite3_exec(credentialsDB, "CREATE TABLE IF NOT EXISTS creds (password TEXT);", NULL, NULL, NULL); + if (sqlite3_prepare_v2(credentialsDB, "INSERT INTO creds(password) VALUES(?)", -1, &stmt, NULL) == SQLITE_OK) { + sqlite3_bind_text(stmt, 1, password, -1, SQLITE_TRANSIENT); + sqlite3_step(stmt); + sqlite3_finalize(stmt); + sqlite3_close(credentialsDB); + } + } +} + +void good(void) { + char *password = "cleartext password"; + sqlite3 *credentialsDB; + sqlite3_stmt *stmt; + + if (sqlite3_open("credentials.db", &credentialsDB) == SQLITE_OK) { + // GOOD: database encryption enabled: + sqlite3_exec(credentialsDB, "PRAGMA key = 'secretKey!'", NULL, NULL, NULL); + sqlite3_exec(credentialsDB, "CREATE TABLE IF NOT EXISTS creds (password TEXT);", NULL, NULL, NULL); + if (sqlite3_prepare_v2(credentialsDB, "INSERT INTO creds(password) VALUES(?)", -1, &stmt, NULL) == SQLITE_OK) { + sqlite3_bind_text(stmt, 1, password, -1, SQLITE_TRANSIENT); + sqlite3_step(stmt); + sqlite3_finalize(stmt); + sqlite3_close(credentialsDB); + } + } +} + diff --git a/cpp/ql/src/Security/CWE/CWE-313/CleartextSqliteDatabase.qhelp b/cpp/ql/src/Security/CWE/CWE-313/CleartextSqliteDatabase.qhelp new file mode 100644 index 000000000000..c8edcccb92f5 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-313/CleartextSqliteDatabase.qhelp @@ -0,0 +1,35 @@ + + + +

    Sensitive information that is stored in an unencrypted SQLite database is accessible to an attacker who gains access to the +database.

    + +
    + + +

    Ensure that if sensitive information is stored in a database then the database is always encrypted.

    + +
    + + +

    The following example shows two ways of storing information in an SQLite +database. In the 'BAD' case, the credentials are simply stored in cleartext. +In the 'GOOD' case, the database (and thus the credentials) are encrypted.

    + + + +
    + + +
  • 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/Security/CWE/CWE-313/CleartextSqliteDatabase.ql b/cpp/ql/src/Security/CWE/CWE-313/CleartextSqliteDatabase.ql new file mode 100644 index 000000000000..c5f3ae71a576 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-313/CleartextSqliteDatabase.ql @@ -0,0 +1,45 @@ +/** + * @name Cleartext storage of sensitive information in an SQLite database + * @description Storing sensitive information in a non-encrypted + * database can expose it to an attacker. + * @kind problem + * @problem.severity warning + * @precision medium + * @id cpp/cleartext-storage-database + * @tags security + * external/cwe/cwe-313 + */ + +import cpp +import semmle.code.cpp.security.SensitiveExprs +import semmle.code.cpp.security.TaintTracking + +class UserInputIsSensitiveExpr extends SecurityOptions { + override predicate isUserInput(Expr expr, string cause) { + expr instanceof SensitiveExpr and cause = "sensitive information" + } +} + +class SqliteFunctionCall extends FunctionCall { + SqliteFunctionCall() { + this.getTarget().getName().matches("sqlite%") + } + + Expr getASource() { + result = this.getAnArgument() + } +} + +predicate sqlite_encryption_used() { + any(StringLiteral l).getValue().toLowerCase().regexpMatch("pragma key.*") or + any(StringLiteral l).getValue().toLowerCase().matches("%attach%database%key%") or + any(FunctionCall fc).getTarget().getName().matches("sqlite%\\_key\\_%") +} + +from SensitiveExpr taintSource, Expr taintedArg, SqliteFunctionCall sqliteCall +where tainted(taintSource, taintedArg) + and taintedArg = sqliteCall.getASource() + and not sqlite_encryption_used() +select sqliteCall, "This SQLite call may store $@ in a non-encrypted SQLite database", + taintSource, "sensitive information" + diff --git a/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.c b/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.c new file mode 100644 index 000000000000..a804bf8becbc --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.c @@ -0,0 +1,13 @@ +void advapi() { + HCRYPTPROV hCryptProv; + HCRYPTKEY hKey; + HCRYPTHASH hHash; + // other preparation goes here + + // BAD: use 3DES for key + CryptDeriveKey(hCryptProv, CALG_3DES, hHash, 0, &hKey); + + // GOOD: use AES + CryptDeriveKey(hCryptProv, CALG_AES_256, hHash, 0, &hKey); +} + diff --git a/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.qhelp b/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.qhelp new file mode 100644 index 000000000000..0fcbd89d8921 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.qhelp @@ -0,0 +1,41 @@ + + + +

    Using broken or weak cryptographic algorithms can leave data vulnerable to being decrypted.

    + +

    Many cryptographic algorithms provided by cryptography libraries are known to be weak, or +flawed. Using such an algorithm means that an attacker may be able to easily decrypt the encrypted +data.

    + +
    + + +

    Ensure that you use a strong, modern cryptographic algorithm. Use at least AES-128 or RSA-2048.

    + +
    + + +

    The following code shows an example of using the advapi windows API to decrypt some data. +When creating a key, you must specify which algorithm to use. The first example uses DES +which is an older algorithm that is now considered weak. The second example uses AES, which +is a strong modern algorithm.

    + + + +
    + + +
  • NIST, FIPS 140 Annex a: +Approved Security Functions.
  • +
  • NIST, SP 800-131A: +Transitions: Recommendation for Transitioning the Use of Cryptographic Algorithms and Key Lengths.
  • + + + + + +
    +
    diff --git a/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql b/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql new file mode 100644 index 000000000000..53d6a7411529 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql @@ -0,0 +1,42 @@ +/** + * @name Use of a broken or risky cryptographic algorithm + * @description Using broken or weak cryptographic algorithms can allow + * an attacker to compromise security. + * @kind problem + * @problem.severity error + * @precision medium + * @id cpp/weak-cryptographic-algorithm + * @tags security + * external/cwe/cwe-327 + */ +import cpp +import semmle.code.cpp.security.Encryption + +abstract class InsecureCryptoSpec extends Locatable { + abstract string description(); +} + +class InsecureFunctionCall extends InsecureCryptoSpec, FunctionCall { + InsecureFunctionCall() { + this.getTarget().getName().regexpMatch(algorithmBlacklistRegex()) + } + + override string description() { result = "function call" } + + override string toString() { result = FunctionCall.super.toString() } + override Location getLocation() { result = FunctionCall.super.getLocation() } +} + +class InsecureMacroSpec extends InsecureCryptoSpec, MacroInvocation { + InsecureMacroSpec() { + this.getMacro().getName().regexpMatch(algorithmBlacklistRegex()) + } + + override string description() { result = "macro invocation" } + + override string toString() { result = MacroInvocation.super.toString() } + override Location getLocation() { result = MacroInvocation.super.getLocation() } +} + +from InsecureCryptoSpec c +select c, "This " + c.description() + " specifies a broken or weak cryptographic algorithm." diff --git a/cpp/ql/src/Security/CWE/CWE-327/OpenSslHeartbleed.c b/cpp/ql/src/Security/CWE/CWE-327/OpenSslHeartbleed.c new file mode 100644 index 000000000000..b78036b3f463 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-327/OpenSslHeartbleed.c @@ -0,0 +1,26 @@ +int +tls1_process_heartbeat(SSL *s) + { + unsigned char *p = &s->s3->rrec.data[0], *pl; + unsigned short hbtype; + unsigned int payload; + + /* ... */ + + hbtype = *p++; + n2s(p, payload); + pl = p; + + /* ... */ + + if (hbtype == TLS1_HB_REQUEST) + { + /* ... */ + memcpy(bp, pl, payload); // BAD: overflow here + /* ... */ + } + + + /* ... */ + + } diff --git a/cpp/ql/src/Security/CWE/CWE-327/OpenSslHeartbleed.qhelp b/cpp/ql/src/Security/CWE/CWE-327/OpenSslHeartbleed.qhelp new file mode 100644 index 000000000000..4ffee9708cf2 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-327/OpenSslHeartbleed.qhelp @@ -0,0 +1,46 @@ + + + +

    Earlier versions of the popular OpenSSL library suffer from a +buffer overflow in its "heartbeat" code. Because of the location of +the problematic code, this vulnerability is often called +"Heartbleed".

    + +

    Software that includes a copy of OpenSSL should be sure to use a +current version of the library. If it uses an older version, it will +be vulnerable to any network site it connects with.

    + +
    + + +

    Upgrade to the latest version of OpenSSL. This problem was fixed in +version 1.0.1g.

    + +
    + + +

    The following code is present in earlier versions of +OpenSSL. The payload variable is the number of bytes that +should be copied from the request back into the response. The call +to memcpy does this copy. The problem is +that payload is supplied as part of the remote request, +and there is no code that checks the size of it. If the caller +supplies a very large value, then the memcpy call will +copy memory that is outside the request packet.

    + + + +
    + + +
  • Common Vulnerabilities and Exposures: + CVE-2014-0160.
  • +
  • OpenSSL News: + OpenSSL + Security Advisory [07 Apr 2014].
  • + + +
    +
    diff --git a/cpp/ql/src/Security/CWE/CWE-327/OpenSslHeartbleed.ql b/cpp/ql/src/Security/CWE/CWE-327/OpenSslHeartbleed.ql new file mode 100644 index 000000000000..a8c4336aa6ec --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-327/OpenSslHeartbleed.ql @@ -0,0 +1,58 @@ +/** + * @name Use of a version of OpenSSL with Heartbleed + * @description Using an old version of OpenSSL can allow remote + * attackers to retrieve portions of memory. + * @kind problem + * @problem.severity error + * @precision very-high + * @id cpp/openssl-heartbleed + * @tags security + * external/cwe/cwe-327 + * external/cwe/cwe-788 + */ + +import cpp + +/** + * Holds if `v` and `w` are ever compared to each other. + */ +predicate comparedTo(Variable v, Variable w) { + v.getAnAssignedValue() = w.getAnAccess() or + exists (ComparisonOperation comp | + comp = v.getAnAccess().getParent+() and + comp = w.getAnAccess().getParent+() + ) +} + +class DataVariable extends Variable { + DataVariable() { + exists(Struct ssl3_record_st | + ssl3_record_st.hasName("ssl3_record_st") and + this = ssl3_record_st.getAField() and + this.hasName("data")) + } +} + +/** + * Holds if expression `e` might evaluate to a pointer + * into the memory region pointed to by `v`. + */ +predicate pointsInto(Expr e, DataVariable v) { + e = v.getAnAccess() or + e.(AddressOfExpr).getOperand().(ArrayExpr).getArrayBase() = v.getAnAccess() or + varPointsInto(e.(VariableAccess).getTarget(), v) +} + +pragma[nomagic] +predicate varPointsInto(Variable tainted, DataVariable src) { + pointsInto(tainted.getAnAssignedValue(), src) +} + +from FunctionCall fc, Struct ssl3_record_st, Field data, Field length +where fc.getTarget().getName().matches("%memcpy%") and + ssl3_record_st.hasName("ssl3_record_st") and + data = ssl3_record_st.getAField() and data.hasName("data") and + length = ssl3_record_st.getAField() and length.hasName("length") and + pointsInto(fc.getArgument(1), data) and + not comparedTo(fc.getArgument(2).(VariableAccess).getTarget(), length) +select fc, "This call to memcpy is insecure (Heartbleed vulnerability)." diff --git a/cpp/ql/src/Security/CWE/CWE-367/TOCTOUFilesystemRace.qhelp b/cpp/ql/src/Security/CWE/CWE-367/TOCTOUFilesystemRace.qhelp new file mode 100644 index 000000000000..84f349ccf2ca --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-367/TOCTOUFilesystemRace.qhelp @@ -0,0 +1,62 @@ + + + + +

    +Often it is necessary to check the state of a file before using it. These checks usually take a file name +to be checked, and if the check returns positively, then the file is opened or otherwise operated upon. +

    + +

    +However, in the time between the check and the operation, the underlying file referenced by the +file name could be changed by an attacker, causing unexpected behavior. +

    + +
    + + +

    +Wherever possible, use functions that operate on file descriptors rather than file names (for example, fchmod +rather than chmod). +

    + +

    +For access checks, you can temporarily change the UID and GID to that of the user whose permissions are being checked, and then +perform the operation. This has the effect of "atomically" combining a permissions check with the operation. +

    + +

    +If file-system locking tools are available on your platform, then locking the file before the check can prevent an unexpected update. However, +note that on some platforms (for example, Unix) file-system locks are typically advisory, and so can be ignored by an attacker. +

    +
    + + +

    +The following example shows a case where a file is opened and then, if the opening was successful, its permissions are changed with +chmod. However, an attacker might change the target of the file name between the initial opening and the permissions change, +potentially changing the permissions of a different file. +

    + + + +

    +This can be avoided by using fchmod with the file descriptor that was received from opening the file. This ensures that the +permissions change is applied to the very same file that was opened. +

    + + + +
    + + +
  • The CERT Oracle Secure Coding Standard for C: + + FIO01-C. Be careful using functions that use file names for identification + . +
  • + +
    +
    diff --git a/cpp/ql/src/Security/CWE/CWE-367/TOCTOUFilesystemRace.ql b/cpp/ql/src/Security/CWE/CWE-367/TOCTOUFilesystemRace.ql new file mode 100644 index 000000000000..f8764b7358aa --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-367/TOCTOUFilesystemRace.ql @@ -0,0 +1,128 @@ +/** + * @name Time-of-check time-of-use filesystem race condition + * @description Separately checking the state of a file before operating + * on it may allow an attacker to modify the file between + * the two operations. + * @kind problem + * @problem.severity warning + * @precision medium + * @id cpp/toctou-race-condition + * @tags security + * external/cwe/cwe-367 + */ +import cpp +import semmle.code.cpp.controlflow.Guards + +/** + * An operation on a filename. + * + * Note: we're not interested in operations on file descriptors, as they + * are better behaved. + */ +FunctionCall filenameOperation(Expr path) { + exists(string name | name = result.getTarget().getName() | + ( + ( + name = "remove" or + name = "unlink" or + name = "rmdir" or + name = "rename" or + name = "chmod" or + name = "chown" or + name = "fopen" or + name = "open" or + name = "freopen" or + name = "_open" or + name = "_wopen" or + name = "_wfopen" + ) + and + result.getArgument(0) = path + ) + or + ( + ( + name = "fopen_s" or + name = "wfopen_s" + ) + and + result.getArgument(1) = path + ) + ) +} + +/** + * A use of `access` (or similar) on a filename. + */ +FunctionCall accessCheck(Expr path) { + exists(string name | name = result.getTarget().getName() | + name = "access" or + name = "_access" or + name = "_waccess" or + name = "_access_s" or + name = "_waccess_s" + ) + and + path = result.getArgument(0) +} + +/** + * A use of `stat` (or similar) on a filename. + */ +FunctionCall stat(Expr path, Expr buf) { + exists(string name | name = result.getTarget().getName() | + name = "stat" or + name = "lstat" or + name = "fstat" or + name.matches("\\_stat%") or + name.matches("\\_wstat%") + ) + and + path = result.getArgument(0) + and + buf = result.getArgument(1) +} + +/** + * Holds if `use` points to `source`, either by being the same or by + * one step of variable indirection. + */ +predicate referenceTo(Expr source, Expr use) { + source = use + or + exists(SsaDefinition def, LocalScopeVariable v | + def.getAnUltimateDefiningValue(v) = source and def.getAUse(v) = use) +} + +from FunctionCall fc, Expr check, Expr checkUse, Expr opUse +where // checkUse looks like a check on a filename + ( + // either: + // an access check + check = accessCheck(checkUse) + or + // a stat + check = stat(checkUse, _) + or + // another filename operation (null pointers can indicate errors) + check = filenameOperation(checkUse) + or + // access to a member variable on the stat buf + // (morally, this should be a use-use pair, but it seems unlikely + // that this variable will get reused in practice) + exists(Variable buf | + exists(stat(checkUse, buf.getAnAccess())) | + check.(VariableAccess).getQualifier() = buf.getAnAccess()) + ) + and // checkUse and opUse refer to the same SSA variable + exists(SsaDefinition def, LocalScopeVariable v | + def.getAUse(v) = checkUse and def.getAUse(v) = opUse) + and // opUse looks like an operation on a filename + fc = filenameOperation(opUse) + and // the return value of check is used (possibly with one step of + // variable indirection) in a guard which controls fc + exists(GuardCondition guard | + referenceTo(check, guard.getAChild*()) | + guard.controls(fc.(ControlFlowNode).getBasicBlock(), _) + ) +select fc, "The $@ being operated upon was previously $@, but the underlying file may have been changed since then.", opUse, "filename", check, "checked" diff --git a/cpp/ql/src/Security/CWE/CWE-367/TOCTOUFilesystemRaceBad.c b/cpp/ql/src/Security/CWE/CWE-367/TOCTOUFilesystemRaceBad.c new file mode 100644 index 000000000000..03e8a82a38c6 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-367/TOCTOUFilesystemRaceBad.c @@ -0,0 +1,15 @@ +char *file_name; +FILE *f_ptr; + +/* Initialize file_name */ + +f_ptr = fopen(file_name, "w"); +if (f_ptr == NULL) { + /* Handle error */ +} + +/* ... */ + +if (chmod(file_name, S_IRUSR) == -1) { + /* Handle error */ +} \ No newline at end of file diff --git a/cpp/ql/src/Security/CWE/CWE-367/TOCTOUFilesystemRaceGood.c b/cpp/ql/src/Security/CWE/CWE-367/TOCTOUFilesystemRaceGood.c new file mode 100644 index 000000000000..bf14cffbd4d3 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-367/TOCTOUFilesystemRaceGood.c @@ -0,0 +1,19 @@ +char *file_name; +int fd; + +/* Initialize file_name */ + +fd = open( + file_name, + O_WRONLY | O_CREAT | O_EXCL, + S_IRWXU +); +if (fd == -1) { + /* Handle error */ +} + +/* ... */ + +if (fchmod(fd, S_IRUSR) == -1) { + /* Handle error */ +} \ No newline at end of file diff --git a/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScaling.cpp b/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScaling.cpp new file mode 100644 index 000000000000..c0724bc1926f --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScaling.cpp @@ -0,0 +1,14 @@ +int example1(int i) { + int intArray[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int *intPointer = intArray; + // BAD: the offset is already automatically scaled by sizeof(int), + // so this code will compute the wrong offset. + return *(intPointer + (i * sizeof(int))); +} + +int example2(int i) { + int intArray[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int *intPointer = intArray; + // GOOD: the offset is automatically scaled by sizeof(int). + return *(intPointer + i); +} diff --git a/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScaling.qhelp b/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScaling.qhelp new file mode 100644 index 000000000000..16052fc0ed8c --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScaling.qhelp @@ -0,0 +1,44 @@ + + + +

    Pointer arithmetic in C and C++ is automatically scaled according +to the size of the data type. For example, if the type of p +is T* and sizeof(T) == 4 then the +expression p+1 adds 4 bytes to p. This can cause a +buffer overflow condition if the programmer forgets that they are +adding a multiple of sizeof(T), rather than a number of +bytes. +

    + +

    +This query finds pointer arithmetic expressions where it appears +likely that the programmer has forgotten that the offset is +automatically scaled. +

    + +
    + + +
      +
    1. Whenever possible, use the array subscript operator rather than +pointer arithmetic. For example, replace *(p+k) +with p[k].
    2. +
    3. Cast to the correct type before using pointer arithmetic. For +example, if the type of p is int* but it really +points to an array of type double[] then use the syntax +(double*)p + k to get a pointer to the k'th element +of the array.
    4. +
    + +
    + + + + + + + + +
    diff --git a/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScaling.ql b/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScaling.ql new file mode 100644 index 000000000000..6417d025e6cf --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScaling.ql @@ -0,0 +1,155 @@ +/** + * @name Suspicious pointer scaling + * @description Implicit scaling of pointer arithmetic expressions + * can cause buffer overflow conditions. + * @kind problem + * @problem.severity warning + * @precision high + * @id cpp/suspicious-pointer-scaling + * @tags security + * external/cwe/cwe-468 + */ +import cpp +import semmle.code.cpp.controlflow.SSA +import IncorrectPointerScalingCommon + +private predicate isPointerType(Type t) { + t instanceof PointerType or + t instanceof ArrayType +} + +private Type baseType(Type t) { + ( + exists (PointerType dt + | dt = t.getUnspecifiedType() and + result = dt.getBaseType().getUnspecifiedType()) or + exists (ArrayType at + | at = t.getUnspecifiedType() and + (not at.getBaseType().getUnspecifiedType() instanceof ArrayType) and + result = at.getBaseType().getUnspecifiedType()) or + exists (ArrayType at, ArrayType at2 + | at = t.getUnspecifiedType() and + at2 = at.getBaseType().getUnspecifiedType() and + result = baseType(at2)) + ) + // Make sure that the type has a size and that it isn't ambiguous. + and strictcount(result.getSize()) = 1 + +} + +/** + * Holds if there is a pointer expression with type `sourceType` at + * location `sourceLoc` which might be the source expression for `use`. + * + * For example, with + * ``` + * int intArray[5] = { 1, 2, 3, 4, 5 }; + * char *charPointer = (char *)intArray; + * return *(charPointer + i); + * ``` + * the array initializer on the first line is a source expression + * for the use of `charPointer` on the third line. + * + * The source will either be an `Expr` or a `Parameter`. + */ +private +predicate exprSourceType(Expr use, Type sourceType, Location sourceLoc) { + // Reaching definitions. + if exists (SsaDefinition def, LocalScopeVariable v | use = def.getAUse(v)) then + exists (SsaDefinition def, LocalScopeVariable v + | use = def.getAUse(v) + | defSourceType(def, v, sourceType, sourceLoc)) + + // Pointer arithmetic + else if use instanceof PointerAddExpr then + exprSourceType(use.(PointerAddExpr).getLeftOperand(), sourceType, sourceLoc) + else if use instanceof PointerSubExpr then + exprSourceType(use.(PointerSubExpr).getLeftOperand(), sourceType, sourceLoc) + else if use instanceof AddExpr then + exprSourceType(use.(AddExpr).getAnOperand(), sourceType, sourceLoc) + else if use instanceof SubExpr then + exprSourceType(use.(SubExpr).getAnOperand(), sourceType, sourceLoc) + else if use instanceof CrementOperation then + exprSourceType(use.(CrementOperation).getOperand(), sourceType, sourceLoc) + + // Conversions are not in the AST, so ignore them. + else if use instanceof Conversion then + none() + + // Source expressions + else + (sourceType = use.getType().getUnspecifiedType() and + isPointerType(sourceType) and + sourceLoc = use.getLocation()) +} + +/** + * Holds if there is a pointer expression with type `sourceType` at + * location `sourceLoc` which might define the value of `v` at `def`. + */ +private +predicate defSourceType(SsaDefinition def, LocalScopeVariable v, + Type sourceType, Location sourceLoc) { + exprSourceType(def.getDefiningValue(v), sourceType, sourceLoc) + or + defSourceType(def.getAPhiInput(v), v, sourceType, sourceLoc) + or + exists (Parameter p + | p = v and + def.definedByParameter(p) and + sourceType = p.getType().getUnspecifiedType() and + isPointerType(sourceType) and + sourceLoc = p.getLocation()) +} + +/** + * Gets the pointer arithmetic expression that `e` is (directly) used + * in, if any. + * + * For example, in `(char*)(p + 1)`, for `p`, ths result is `p + 1`. + */ +private Expr pointerArithmeticParent(Expr e) { + e = result.(PointerAddExpr).getLeftOperand() or + e = result.(PointerSubExpr).getLeftOperand() or + e = result.(PointerDiffExpr).getAnOperand() +} + +from Expr dest, Type destType, Type sourceType, Type sourceBase, + Type destBase, Location sourceLoc +where exists(pointerArithmeticParent(dest)) + and exprSourceType(dest, sourceType, sourceLoc) + and sourceBase = baseType(sourceType) + and destType = dest.getFullyConverted().getType() + and destBase = baseType(destType) + and destBase.getSize() != sourceBase.getSize() + and not dest.isInMacroExpansion() + + // If the source type is a char* or void* then don't + // produce a result, because it is likely to be a false + // positive. + and not (sourceBase instanceof CharType) + and not (sourceBase instanceof VoidType) + + // Low-level pointer tricks often involve casting a struct pointer to a + // char pointer, then accessing it at byte offsets. For example, this can + // be necessary in order to resume an interrupted `write(2)`. + and not (destBase instanceof CharType) + + // Similarly, gcc and compilers emulating it will allow void pointer + // arithmetic as if void were a 1-byte type + and not (destBase instanceof VoidType) + + // Don't produce an alert if the root expression computes + // an offset, rather than a pointer. For example: + // ``` + // (p + 1) - q + // ``` + and forall(Expr parent | + parent = pointerArithmeticParent+(dest) | + parent.getFullyConverted().getType().getUnspecifiedType() instanceof PointerType) +select + dest, + "This pointer might have type $@ (size " + sourceBase.getSize() + + "), but the pointer arithmetic here is done with type " + + destType + " (size " + destBase.getSize() + ").", + sourceLoc, sourceBase.toString() diff --git a/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingChar.cpp b/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingChar.cpp new file mode 100644 index 000000000000..b7e82e02dfb1 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingChar.cpp @@ -0,0 +1,14 @@ +char example1(int i) { + int intArray[5] = { 1, 2, 3, 4, 5 }; + char *charPointer = (char *)intArray; + // BAD: the pointer arithmetic uses type char*, so the offset + // is not scaled by sizeof(int). + return *(charPointer + i); +} + +int example2(int i) { + int intArray[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int *intPointer = intArray; + // GOOD: the offset is automatically scaled by sizeof(int). + return *(intPointer + i); +} diff --git a/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingChar.qhelp b/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingChar.qhelp new file mode 100644 index 000000000000..b81174b85220 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingChar.qhelp @@ -0,0 +1,38 @@ + + + +

    Casting arbitrary pointers into char* and then accessing their +contents should be done with care. The results may not be portable.

    + +

    +This query finds pointer arithmetic expressions where a pointer to +char (or similar) is dereferenced even though the underlying value is +of a type larger than char. +

    + +
    + + +
      +
    1. Whenever possible, use the array subscript operator rather than +pointer arithmetic. For example, replace *(p+k) +with p[k].
    2. +
    3. Cast to the correct type before using pointer arithmetic. For +example, if the type of p is char* but it really +points to an array of type double[] then use the syntax +(double*)p + k to get a pointer to the k'th element +of the array.
    4. +
    + +
    + + + + + + + + +
    diff --git a/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingChar.ql b/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingChar.ql new file mode 100644 index 000000000000..fa921e2beb42 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingChar.ql @@ -0,0 +1,153 @@ +/** + * @name Suspicious pointer scaling to char + * @description Implicit scaling of pointer arithmetic expressions + * can cause buffer overflow conditions. + * @kind problem + * @id cpp/incorrect-pointer-scaling-char + * @problem.severity warning + * @precision low + * @tags security + * external/cwe/cwe-468 + */ +import cpp +import semmle.code.cpp.controlflow.SSA +import IncorrectPointerScalingCommon + +private predicate isPointerType(Type t) { + t instanceof PointerType or + t instanceof ArrayType +} + +private Type baseType(Type t) { + exists (DerivedType dt + | dt = t.getUnspecifiedType() and + isPointerType(dt) and + result = dt.getBaseType().getUnspecifiedType()) + + // Make sure that the type has a size and that it isn't ambiguous. + and strictcount(result.getSize()) = 1 +} + +/** + * Holds if there is a pointer expression with type `sourceType` at + * location `sourceLoc` which might be the source expression for `use`. + * + * For example, with + * ``` + * int intArray[5] = { 1, 2, 3, 4, 5 }; + * char *charPointer = (char *)intArray; + * return *(charPointer + i); + * ``` + * the array initializer on the first line is a source expression + * for the use of `charPointer` on the third line. + * + * The source will either be an `Expr` or a `Parameter`. + */ +private +predicate exprSourceType(Expr use, Type sourceType, Location sourceLoc) { + // Reaching definitions. + if exists (SsaDefinition def, LocalScopeVariable v | use = def.getAUse(v)) then + exists (SsaDefinition def, LocalScopeVariable v + | use = def.getAUse(v) + | defSourceType(def, v, sourceType, sourceLoc)) + + // Pointer arithmetic + else if use instanceof PointerAddExpr then + exprSourceType(use.(PointerAddExpr).getLeftOperand(), sourceType, sourceLoc) + else if use instanceof PointerSubExpr then + exprSourceType(use.(PointerSubExpr).getLeftOperand(), sourceType, sourceLoc) + else if use instanceof AddExpr then + exprSourceType(use.(AddExpr).getAnOperand(), sourceType, sourceLoc) + else if use instanceof SubExpr then + exprSourceType(use.(SubExpr).getAnOperand(), sourceType, sourceLoc) + else if use instanceof CrementOperation then + exprSourceType(use.(CrementOperation).getOperand(), sourceType, sourceLoc) + + // Conversions are not in the AST, so ignore them. + else if use instanceof Conversion then + none() + + // Source expressions + else + (sourceType = use.getType().getUnspecifiedType() and + isPointerType(sourceType) and + sourceLoc = use.getLocation()) +} + +/** + * Holds if there is a pointer expression with type `sourceType` at + * location `sourceLoc` which might define the value of `v` at `def`. + */ +private +predicate defSourceType(SsaDefinition def, LocalScopeVariable v, + Type sourceType, Location sourceLoc) { + exprSourceType(def.getDefiningValue(v), sourceType, sourceLoc) + or + defSourceType(def.getAPhiInput(v), v, sourceType, sourceLoc) + or + exists (Parameter p + | p = v and + def.definedByParameter(p) and + sourceType = p.getType().getUnspecifiedType() and + isPointerType(sourceType) and + sourceLoc = p.getLocation()) +} + +/** + * Gets the pointer arithmetic expression that `e` is (directly) used + * in, if any. + * + * For example, in `(char*)(p + 1)`, for `p`, ths result is `p + 1`. + */ +private Expr pointerArithmeticParent(Expr e) { + e = result.(PointerAddExpr).getLeftOperand() or + e = result.(PointerSubExpr).getLeftOperand() or + e = result.(PointerDiffExpr).getAnOperand() +} + +from Expr dest, Type destType, Type sourceType, Type sourceBase, + Type destBase, Location sourceLoc +where exists(pointerArithmeticParent(dest)) + and exprSourceType(dest, sourceType, sourceLoc) + and sourceBase = baseType(sourceType) + and destType = dest.getFullyConverted().getType() + and destBase = baseType(destType) + and destBase.getSize() != sourceBase.getSize() + and not dest.isInMacroExpansion() + + // If the source type is a char* or void* then don't + // produce a result, because it is likely to be a false + // positive. + and not (sourceBase instanceof CharType) + and not (sourceBase instanceof VoidType) + + // Don't produce an alert if the dest type is `char *` but the + // expression contains a `sizeof`, which is probably correct. For + // example: + // ``` + // int x[3] = {1,2,3}; + // char* p = (char*)x; + // return *(int*)(p + (2 * sizeof(int))) + // ``` + and not ( + destBase instanceof CharType and + dest.getParent().(Expr).getAChild*() instanceof SizeofOperator + ) + + // Don't produce an alert if the root expression computes + // an offset, rather than a pointer. For example: + // ``` + // (p + 1) - q + // ``` + and forall(Expr parent | + parent = pointerArithmeticParent+(dest) | + parent.getFullyConverted().getType().getUnspecifiedType() instanceof PointerType) + + // Only produce alerts that are not produced by `IncorrectPointerScaling.ql`. + and (destBase instanceof CharType) +select + dest, + "This pointer might have type $@ (size " + sourceBase.getSize() + + "), but the pointer arithmetic here is done with type " + + destType + " (size " + destBase.getSize() + ").", + sourceLoc, sourceBase.toString() diff --git a/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingCommon.qll b/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingCommon.qll new file mode 100644 index 000000000000..1d3f20ea946f --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingCommon.qll @@ -0,0 +1,48 @@ +/** + * Shared utilities for the CWE-468 queries. + */ +import cpp + +/** + * Gets the type parameter of `sizeof` expression `e`. + */ +private +Type sizeofParam(Expr e) { + result = e.(SizeofExprOperator).getExprOperand().getFullyConverted().getType() + or + result = e.(SizeofTypeOperator).getTypeOperand() +} + +/** + * Holds if `e` is `sizeof` expression `sizeofExpr`, possibly multiplied + * by another expression, and `sizeofParam` is `sizeofExpr`'s type + * parameter. + * + * For example, if `e` is `4 * sizeof(T)` then `sizeofExpr` is + * `sizeof(T)` and `sizeofParam` is `T`. + */ +private +predicate multiplyWithSizeof(Expr e, Expr sizeofExpr, Type sizeofParam) { + (e = sizeofExpr and sizeofParam = sizeofParam(e).getUnspecifiedType()) + or + multiplyWithSizeof(e.(MulExpr).getAnOperand(), sizeofExpr, sizeofParam) +} + +/** + * Holds if the pointer `e` is added to the `sizeof` expression + * `sizeofExpr` (which may first be multiplied by another expression), + * and `sizeofParam` is `sizeofExpr`'s type parameter. + + * For example, if the program contains the expression + * `p - (i * sizeof(T))` then `e` would be `p`, `sizeofExpr` would be + * `sizeof(T)`, and `sizeofParam` would be `T`. + */ +predicate addWithSizeof(Expr e, Expr sizeofExpr, Type sizeofParam) { + exists (PointerAddExpr addExpr + | e = addExpr.getLeftOperand() and + multiplyWithSizeof(addExpr.getRightOperand(), sizeofExpr, sizeofParam)) + or + exists (PointerSubExpr subExpr + | e = subExpr.getLeftOperand() and + multiplyWithSizeof(subExpr.getRightOperand(), sizeofExpr, sizeofParam)) +} diff --git a/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingVoid.cpp b/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingVoid.cpp new file mode 100644 index 000000000000..321a5f287fe4 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingVoid.cpp @@ -0,0 +1,14 @@ +char example1(int i) { + int intArray[5] = { 1, 2, 3, 4, 5 }; + void *voidPointer = (void *)intArray; + // BAD: the pointer arithmetic uses type void*, so the offset + // is not scaled by sizeof(int). + return *(voidPointer + i); +} + +int example2(int i) { + int intArray[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int *intPointer = intArray; + // GOOD: the offset is automatically scaled by sizeof(int). + return *(intPointer + i); +} diff --git a/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingVoid.qhelp b/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingVoid.qhelp new file mode 100644 index 000000000000..358cf538f3c1 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingVoid.qhelp @@ -0,0 +1,40 @@ + + + +

    Casting arbitrary pointers into void* and then accessing their +contents should be done with care. The results may not be portable.

    + +

    +This query finds pointer arithmetic expressions where a pointer to +void (or similar) is then cast to another type and dereferenced. +

    + +
    + + +
      +
    1. Whenever possible, use the array subscript operator rather than +pointer arithmetic. For example, replace *(p+k) +with p[k].
    2. +
    3. Cast to the correct type before using pointer arithmetic. For +example, if the type of p is void* but it really +points to an array of type double[] then use the syntax +(double*)p + k to get a pointer to the k'th element +of the array.
    4. +
    5. If pointer arithmetic must be done with a single-byte width, prefer +char * to void *, as pointer arithmetic on +void * is a nonstandard GNU extension.
    6. +
    + +
    + + + + + + + + +
    diff --git a/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingVoid.ql b/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingVoid.ql new file mode 100644 index 000000000000..c91c9a83e9f1 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingVoid.ql @@ -0,0 +1,124 @@ +/** + * @name Suspicious pointer scaling to void + * @description Implicit scaling of pointer arithmetic expressions + * can cause buffer overflow conditions. + * @kind problem + * @problem.severity warning + * @precision medium + * @id cpp/suspicious-pointer-scaling-void + * @tags security + * external/cwe/cwe-468 + */ +import cpp +import semmle.code.cpp.controlflow.SSA +import IncorrectPointerScalingCommon + +private predicate isPointerType(Type t) { + t instanceof PointerType or + t instanceof ArrayType +} + +private Type baseType(Type t) { + exists (DerivedType dt + | dt = t.getUnspecifiedType() and + isPointerType(dt) and + result = dt.getBaseType().getUnspecifiedType()) + + // Make sure that the type has a size and that it isn't ambiguous. + and strictcount(result.getSize()) = 1 +} + +/** + * Holds if there is a pointer expression with type `sourceType` at + * location `sourceLoc` which might be the source expression for `use`. + * + * For example, with + * ``` + * int intArray[5] = { 1, 2, 3, 4, 5 }; + * char *charPointer = (char *)intArray; + * return *(charPointer + i); + * ``` + * the array initializer on the first line is a source expression + * for the use of `charPointer` on the third line. + * + * The source will either be an `Expr` or a `Parameter`. + */ +private +predicate exprSourceType(Expr use, Type sourceType, Location sourceLoc) { + // Reaching definitions. + if exists (SsaDefinition def, LocalScopeVariable v | use = def.getAUse(v)) then + exists (SsaDefinition def, LocalScopeVariable v + | use = def.getAUse(v) + | defSourceType(def, v, sourceType, sourceLoc)) + + // Pointer arithmetic + else if use instanceof PointerAddExpr then + exprSourceType(use.(PointerAddExpr).getLeftOperand(), sourceType, sourceLoc) + else if use instanceof PointerSubExpr then + exprSourceType(use.(PointerSubExpr).getLeftOperand(), sourceType, sourceLoc) + else if use instanceof AddExpr then + exprSourceType(use.(AddExpr).getAnOperand(), sourceType, sourceLoc) + else if use instanceof SubExpr then + exprSourceType(use.(SubExpr).getAnOperand(), sourceType, sourceLoc) + else if use instanceof CrementOperation then + exprSourceType(use.(CrementOperation).getOperand(), sourceType, sourceLoc) + + // Conversions are not in the AST, so ignore them. + else if use instanceof Conversion then + none() + + // Source expressions + else + (sourceType = use.getType().getUnspecifiedType() and + isPointerType(sourceType) and + sourceLoc = use.getLocation()) +} + +/** + * Holds if there is a pointer expression with type `sourceType` at + * location `sourceLoc` which might define the value of `v` at `def`. + */ +private +predicate defSourceType(SsaDefinition def, LocalScopeVariable v, + Type sourceType, Location sourceLoc) { + exprSourceType(def.getDefiningValue(v), sourceType, sourceLoc) + or + defSourceType(def.getAPhiInput(v), v, sourceType, sourceLoc) + or + exists (Parameter p + | p = v and + def.definedByParameter(p) and + sourceType = p.getType().getUnspecifiedType() and + isPointerType(sourceType) and + sourceLoc = p.getLocation()) +} + +/** + * Gets the pointer arithmetic expression that `e` is (directly) used + * in, if any. + * + * For example, in `(char*)(p + 1)`, for `p`, ths result is `p + 1`. + */ +private Expr pointerArithmeticParent(Expr e) { + e = result.(PointerAddExpr).getLeftOperand() or + e = result.(PointerSubExpr).getLeftOperand() or + e = result.(PointerDiffExpr).getAnOperand() +} + +from Expr dest, Type destType, Type sourceType, Type sourceBase, + Type destBase, Location sourceLoc +where exists(pointerArithmeticParent(dest)) + and exprSourceType(dest, sourceType, sourceLoc) + and sourceBase = baseType(sourceType) + and destType = dest.getFullyConverted().getType() + and destBase = baseType(destType) + and destBase.getSize() != sourceBase.getSize() + and not dest.isInMacroExpansion() + + // Only produce alerts that are not produced by `IncorrectPointerScaling.ql`. + and (destBase instanceof VoidType) +select + dest, + "This pointer might have type $@ (size " + sourceBase.getSize() + + "), but the pointer arithmetic here is done with type void", + sourceLoc, sourceBase.toString() diff --git a/cpp/ql/src/Security/CWE/CWE-468/SuspiciousAddWithSizeof.cpp b/cpp/ql/src/Security/CWE/CWE-468/SuspiciousAddWithSizeof.cpp new file mode 100644 index 000000000000..c0724bc1926f --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-468/SuspiciousAddWithSizeof.cpp @@ -0,0 +1,14 @@ +int example1(int i) { + int intArray[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int *intPointer = intArray; + // BAD: the offset is already automatically scaled by sizeof(int), + // so this code will compute the wrong offset. + return *(intPointer + (i * sizeof(int))); +} + +int example2(int i) { + int intArray[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int *intPointer = intArray; + // GOOD: the offset is automatically scaled by sizeof(int). + return *(intPointer + i); +} diff --git a/cpp/ql/src/Security/CWE/CWE-468/SuspiciousAddWithSizeof.qhelp b/cpp/ql/src/Security/CWE/CWE-468/SuspiciousAddWithSizeof.qhelp new file mode 100644 index 000000000000..f88dfd1bda9a --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-468/SuspiciousAddWithSizeof.qhelp @@ -0,0 +1,41 @@ + + + +

    Pointer arithmetic in C and C++ is automatically scaled according +to the size of the data type. For example, if the type of p +is T* and sizeof(T) == 4 then the +expression p+1 adds 4 bytes to p. +

    + +

    +This query finds code of the form p + k*sizeof(T). Such code +is usually a mistake because there is no need to manually scale the +offset by sizeof(T). +

    + +
    + + +
      +
    1. Whenever possible, use the array subscript operator rather than +pointer arithmetic. For example, replace *(p+k) +with p[k].
    2. +
    3. Cast to the correct type before using pointer arithmetic. For +example, if the type of p is char* but it really +points to an array of type double[] then use the syntax +(double*)p + k to get a pointer to the k'th element +of the array.
    4. +
    + +
    + + + + + + + + +
    diff --git a/cpp/ql/src/Security/CWE/CWE-468/SuspiciousAddWithSizeof.ql b/cpp/ql/src/Security/CWE/CWE-468/SuspiciousAddWithSizeof.ql new file mode 100644 index 000000000000..75c02b5e8c7f --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-468/SuspiciousAddWithSizeof.ql @@ -0,0 +1,32 @@ +/** + * @name Suspicious add with sizeof + * @description Explicitly scaled pointer arithmetic expressions + * can cause buffer overflow conditions if the offset is also + * implicitly scaled. + * @kind problem + * @problem.severity warning + * @precision high + * @id cpp/suspicious-add-sizeof + * @tags security + * external/cwe/cwe-468 + */ +import cpp +import IncorrectPointerScalingCommon + +private predicate isCharPtrExpr(Expr e) { + exists (PointerType pt + | pt = e.getFullyConverted().getUnderlyingType() + | pt.getBaseType().getUnspecifiedType() instanceof CharType) +} + +from Expr sizeofExpr, Expr e +where + // If we see an addWithSizeof then we expect the type of + // the pointer expression to be char*. Otherwise it is probably + // a mistake. + addWithSizeof(e, sizeofExpr, _) and not isCharPtrExpr(e) +select + sizeofExpr, + "Suspicious sizeof offset in a pointer arithmetic expression. " + + "The type of the pointer is " + + e.getFullyConverted().getType().toString() + "." diff --git a/cpp/ql/src/Security/CWE/CWE-497/ExposedSystemData.qhelp b/cpp/ql/src/Security/CWE/CWE-497/ExposedSystemData.qhelp new file mode 100644 index 000000000000..2daa683f4839 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-497/ExposedSystemData.qhelp @@ -0,0 +1,28 @@ + + + +

    Exposing system data or debugging information may help an adversary to learn about the system and form an attack plan. An attacker can use error messages that reveal technologies, operating systems, and product versions to tune their attack against known vulnerabilities in these technologies.

    + +

    This query finds locations where system configuration information might be revealed to a user.

    +
    + + +

    Do not expose system configuration information to users. Be wary of the difference between information that could be helpful to users, and unnecessary details that could be useful to an adversary.

    +
    + + +

    In this example the value of the PATH environment variable is revealed in full to the user when a particular error occurs. This might reveal information such as the software installed on your system to an adversary who does not have legitimate access to that information.

    + + + +

    The message should be rephrased without this information, for example:

    + + +
    + + + + +
    diff --git a/cpp/ql/src/Security/CWE/CWE-497/ExposedSystemData.ql b/cpp/ql/src/Security/CWE/CWE-497/ExposedSystemData.ql new file mode 100644 index 000000000000..ef804061e975 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-497/ExposedSystemData.ql @@ -0,0 +1,492 @@ +/** + * @name Exposure of system data to an unauthorized control sphere + * @description Exposing system data or debugging information helps + * an adversary learn about the system and form an + * attack plan. + * @kind problem + * @problem.severity warning + * @precision medium + * @id cpp/system-data-exposure + * @tags security + * external/cwe/cwe-497 + */ + +import cpp +import semmle.code.cpp.commons.Environment +import semmle.code.cpp.security.OutputWrite + +/** + * An element that should not be exposed to an adversary. + */ +abstract class SystemData extends Element { + /** + * Gets an expression that is part of this `SystemData`. + */ + abstract Expr getAnExpr(); + + /** + * Gets an expression whose value originates from, or is used by, + * this `SystemData`. + */ + Expr getAnExprIndirect() { + // direct SystemData + result = getAnExpr() or + + // flow via global or member variable (conservative approximation) + exists(Variable var | + ( + var.getAnAssignedValue() = getAnExprIndirect() or + var.getAnAccess() = getAnExprIndirect() + ) and + result = var.getAnAccess() and + not var instanceof LocalScopeVariable + ) or + + // flow via stack variable + definitionUsePair(_, getAnExprIndirect(), result) or + useUsePair(_, getAnExprIndirect(), result) or + useUsePair(_, result, getAnExprIndirect()) or + + // flow from assigned value to assignment expression + result.(AssignExpr).getRValue() = getAnExprIndirect() + } +} + +/** + * Data originating from the environment. + */ +class EnvData extends SystemData { + EnvData() { + this instanceof EnvironmentRead + } + + override Expr getAnExpr() { + result = this + } +} + +/** + * Data originating from a call to `mysql_get_client_info()`. + */ +class SQLClientInfo extends SystemData { + SQLClientInfo() { + this.(FunctionCall).getTarget().hasName("mysql_get_client_info") + } + + override Expr getAnExpr() { + result = this + } +} + +private predicate sqlConnectInfo(FunctionCall source, VariableAccess use) { + ( + source.getTarget().hasName("mysql_connect") or + source.getTarget().hasName("mysql_real_connect") + ) and ( + use = source.getAnArgument() + ) +} + +/** + * Data passed into an SQL connect function. + */ +class SQLConnectInfo extends SystemData { + SQLConnectInfo() { + sqlConnectInfo(this, _) + } + + override Expr getAnExpr() { + sqlConnectInfo(this, result) + } +} + +private predicate posixSystemInfo(FunctionCall source, Element use) { + ( + // long sysconf(int name) + // - various OS / system values and limits + source.getTarget().hasName("sysconf") and + use = source + ) or ( + // size_t confstr(int name, char *buf, size_t len) + // - various OS / system strings, such as the libc version + // int statvfs(const char *__path, struct statvfs *__buf) + // int fstatvfs(int __fd, struct statvfs *__buf) + // - various filesystem parameters + // int uname(struct utsname *buf) + // - OS name and version + ( + source.getTarget().hasName("confstr") or + source.getTarget().hasName("statvfs") or + source.getTarget().hasName("fstatvfs") or + source.getTarget().hasName("uname") + ) and ( + use = source.getArgument(1) + ) + ) +} + +/** + * Data obtained from a POSIX system information call. + */ +class PosixSystemInfo extends SystemData { + PosixSystemInfo() { + posixSystemInfo(this, _) + } + + override Expr getAnExpr() { + posixSystemInfo(this, result) + } +} + +private predicate posixPWInfo(FunctionCall source, Element use) { + ( + // struct passwd *getpwnam(const char *name); + // struct passwd *getpwuid(uid_t uid); + // struct passwd *getpwent(void); + // struct group *getgrnam(const char *name); + // struct group *getgrgid(gid_t); + // struct group *getgrent(void); + ( + source.getTarget().hasName("getpwnam") or + source.getTarget().hasName("getpwuid") or + source.getTarget().hasName("getpwent") or + source.getTarget().hasName("getgrnam") or + source.getTarget().hasName("getgrgid") or + source.getTarget().hasName("getgrent") + ) and + use = source + ) or ( + // int getpwnam_r(const char *name, struct passwd *pwd, + // char *buf, size_t buflen, struct passwd **result); + // int getpwuid_r(uid_t uid, struct passwd *pwd, + // char *buf, size_t buflen, struct passwd **result); + // int getgrgid_r(gid_t gid, struct group *grp, + // char *buf, size_t buflen, struct group **result); + // int getgrnam_r(const char *name, struct group *grp, + // char *buf, size_t buflen, struct group **result); + ( + source.getTarget().hasName("getpwnam_r") or + source.getTarget().hasName("getpwuid_r") or + source.getTarget().hasName("getgrgid_r") or + source.getTarget().hasName("getgrnam_r") + ) and ( + use = source.getArgument(1) or + use = source.getArgument(2) or + use = source.getArgument(4) + ) + ) or ( + // int getpwent_r(struct passwd *pwd, char *buffer, size_t bufsize, + // struct passwd **result); + // int getgrent_r(struct group *gbuf, char *buf, + // size_t buflen, struct group **gbufp); + ( + source.getTarget().hasName("getpwent_r") or + source.getTarget().hasName("getgrent_r") + ) and ( + use = source.getArgument(0) or + use = source.getArgument(1) or + use = source.getArgument(3) + ) + ) +} + +/** + * Data obtained from a POSIX user/password/group database information call. + */ +class PosixPWInfo extends SystemData { + PosixPWInfo() { + posixPWInfo(this, _) + } + + override Expr getAnExpr() { + posixPWInfo(this, result) + } +} + +private predicate windowsSystemInfo(FunctionCall source, Element use) { + ( + // DWORD WINAPI GetVersion(void); + ( + source.getTarget().hasGlobalName("GetVersion") + ) and + use = source + ) or ( + // BOOL WINAPI GetVersionEx(_Inout_ LPOSVERSIONINFO lpVersionInfo); + // void WINAPI GetSystemInfo(_Out_ LPSYSTEM_INFO lpSystemInfo); + // void WINAPI GetNativeSystemInfo(_Out_ LPSYSTEM_INFO lpSystemInfo); + ( + source.getTarget().hasName("GetVersionEx") or + source.getTarget().hasName("GetVersionExA") or + source.getTarget().hasName("GetVersionExW") or + source.getTarget().hasName("GetSystemInfo") or + source.getTarget().hasName("GetNativeSystemInfo") + ) and + use = source.getArgument(0) + ) +} + +/** + * Data obtained from a Windows system information call. + */ +class WindowsSystemInfo extends SystemData { + WindowsSystemInfo() { + windowsSystemInfo(this, _) + } + + override Expr getAnExpr() { + windowsSystemInfo(this, result) + } +} + +private predicate windowsFolderPath(FunctionCall source, Element use) { + ( + // BOOL SHGetSpecialFolderPath( + // HWND hwndOwner, + // _Out_ LPTSTR lpszPath, + // _In_ int csidl, + // _In_ BOOL fCreate + // ); + ( + source.getTarget().hasName("SHGetSpecialFolderPath") or + source.getTarget().hasName("SHGetSpecialFolderPathA") or + source.getTarget().hasName("SHGetSpecialFolderPathW") + ) and + use = source.getArgument(1) + ) or ( + // HRESULT SHGetKnownFolderPath( + // _In_ REFKNOWNFOLDERID rfid, + // _In_ DWORD dwFlags, + // _In_opt_ HANDLE hToken, + // _Out_ PWSTR *ppszPath + // ); + source.getTarget().hasName("SHGetKnownFolderPath") and + use = source.getArgument(3) + ) or ( + // HRESULT SHGetFolderPath( + // _In_ HWND hwndOwner, + // _In_ int nFolder, + // _In_ HANDLE hToken, + // _In_ DWORD dwFlags, + // _Out_ LPTSTR pszPath + // ); + ( + source.getTarget().hasName("SHGetFolderPath") or + source.getTarget().hasName("SHGetFolderPathA") or + source.getTarget().hasName("SHGetFolderPathW") + ) and + use = source.getArgument(4) + ) or ( + // HRESULT SHGetFolderPathAndSubDir( + // _In_ HWND hwnd, + // _In_ int csidl, + // _In_ HANDLE hToken, + // _In_ DWORD dwFlags, + // _In_ LPCTSTR pszSubDir, + // _Out_ LPTSTR pszPath + // ); + ( + source.getTarget().hasName("SHGetFolderPathAndSubDir") or + source.getTarget().hasName("SHGetFolderPathAndSubDirA") or + source.getTarget().hasName("SHGetFolderPathAndSubDirW") + ) and + use = source.getArgument(5) + ) +} + +/** + * Data obtained about Windows special paths (for example, the + * location of `System32`). + */ +class WindowsFolderPath extends SystemData { + WindowsFolderPath() { + windowsFolderPath(this, _) + } + + override Expr getAnExpr() { + windowsFolderPath(this, result) + } +} + +private predicate logonUser(FunctionCall source, VariableAccess use) { + ( + source.getTarget().hasName("LogonUser") or + source.getTarget().hasName("LogonUserW") or + source.getTarget().hasName("LogonUserA") + ) and ( + use = source.getAnArgument() + ) +} + +/** + * Data passed into a `LogonUser` (Windows) function. + */ +class LogonUser extends SystemData { + LogonUser() { + logonUser(this, _) + } + + override Expr getAnExpr() { + logonUser(this, result) + } +} + +private predicate regQuery(FunctionCall source, VariableAccess use) { + ( + // LONG WINAPI RegQueryValue( + // _In_ HKEY hKey, + // _In_opt_ LPCTSTR lpSubKey, + // _Out_opt_ LPTSTR lpValue, + // _Inout_opt_ PLONG lpcbValue + // ); + ( + source.getTarget().hasName("RegQueryValue") or + source.getTarget().hasName("RegQueryValueA") or + source.getTarget().hasName("RegQueryValueW") + ) and ( + use = source.getArgument(2) + ) + ) or ( + // LONG WINAPI RegQueryMultipleValues( + // _In_ HKEY hKey, + // _Out_ PVALENT val_list, + // _In_ DWORD num_vals, + // _Out_opt_ LPTSTR lpValueBuf, + // _Inout_opt_ LPDWORD ldwTotsize + // ); + ( + source.getTarget().hasName("RegQueryMultipleValues") or + source.getTarget().hasName("RegQueryMultipleValuesA") or + source.getTarget().hasName("RegQueryMultipleValuesW") + ) and ( + use = source.getArgument(3) + ) + ) or ( + // LONG WINAPI RegQueryValueEx( + // _In_ HKEY hKey, + // _In_opt_ LPCTSTR lpValueName, + // _Reserved_ LPDWORD lpReserved, + // _Out_opt_ LPDWORD lpType, + // _Out_opt_ LPBYTE lpData, + // _Inout_opt_ LPDWORD lpcbData + // ); + ( + source.getTarget().hasName("RegQueryValueEx") or + source.getTarget().hasName("RegQueryValueExA") or + source.getTarget().hasName("RegQueryValueExW") + ) and ( + use = source.getArgument(4) + ) + ) or ( + // LONG WINAPI RegGetValue( + // _In_ HKEY hkey, + // _In_opt_ LPCTSTR lpSubKey, + // _In_opt_ LPCTSTR lpValue, + // _In_opt_ DWORD dwFlags, + // _Out_opt_ LPDWORD pdwType, + // _Out_opt_ PVOID pvData, + // _Inout_opt_ LPDWORD pcbData + // ); + ( + source.getTarget().hasName("RegGetValue") or + source.getTarget().hasName("RegGetValueA") or + source.getTarget().hasName("RegGetValueW") + ) and ( + use = source.getArgument(5) + ) + ) +} + +/** + * Data read from the Windows registry. + */ +class RegQuery extends SystemData { + RegQuery() { + regQuery(this, _) + } + + override Expr getAnExpr() { + regQuery(this, result) + } +} +/** + * Somewhere data is output. + */ +abstract class DataOutput extends Element { + /** + * Get an expression containing data that is output. + */ + abstract Expr getASource(); +} + +/** + * Data that is output via standard output or standard error. + */ +class StandardOutput extends DataOutput { + StandardOutput() { + this instanceof OutputWrite + } + + override Expr getASource() { + result = this.(OutputWrite).getASource() + } +} + +private predicate socketCallOrIndirect(FunctionCall call) { + ( + // direct socket call + // int socket(int domain, int type, int protocol); + call.getTarget().getName() = "socket" + ) or exists(ReturnStmt rtn | + // indirect socket call + call.getTarget() = rtn.getEnclosingFunction() and + ( + socketCallOrIndirect(rtn.getExpr()) or + socketCallOrIndirect(rtn.getExpr().(VariableAccess).getTarget().getAnAssignedValue()) + ) + ) +} + +private predicate socketFileDescriptor(Expr e) { + exists(Variable var, FunctionCall socket | + socketCallOrIndirect(socket) and + var.getAnAssignedValue() = socket and + e = var.getAnAccess() + ) +} + +private predicate socketOutput(FunctionCall call, Expr data) { + ( + // ssize_t send(int sockfd, const void *buf, size_t len, int flags); + // ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, + // const struct sockaddr *dest_addr, socklen_t addrlen); + // ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags); + // int write(int handle, void *buffer, int nbyte); + ( + call.getTarget().hasGlobalName("send") or + call.getTarget().hasGlobalName("sendto") or + call.getTarget().hasGlobalName("sendmsg") or + call.getTarget().hasGlobalName("write") + ) and + data = call.getArgument(1) and + socketFileDescriptor(call.getArgument(0)) + ) +} + +/** + * Data that is output via a socket. + */ +class SocketOutput extends DataOutput { + SocketOutput() { + socketOutput(this, _) + } + + override Expr getASource() { + socketOutput(this, result) + } +} + +from SystemData sd, DataOutput ow +where + sd.getAnExprIndirect() = ow.getASource() or + sd.getAnExprIndirect() = ow.getASource().(Expr).getAChild*() +select ow, "This operation exposes system data from $@.", sd, sd.toString() diff --git a/cpp/ql/src/Security/CWE/CWE-497/ExposedSystemDataCorrect.cpp b/cpp/ql/src/Security/CWE/CWE-497/ExposedSystemDataCorrect.cpp new file mode 100644 index 000000000000..ac885f425dcf --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-497/ExposedSystemDataCorrect.cpp @@ -0,0 +1,7 @@ +char* path = getenv("PATH"); + +//... + +fprintf(stderr, "A required executable file could not be found. " \ + "Please ensure that the software has been installed " \ + "correctly or contact a system administrator.\n"); \ No newline at end of file diff --git a/cpp/ql/src/Security/CWE/CWE-497/ExposedSystemDataIncorrect.cpp b/cpp/ql/src/Security/CWE/CWE-497/ExposedSystemDataIncorrect.cpp new file mode 100644 index 000000000000..fec622a5f7f4 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-497/ExposedSystemDataIncorrect.cpp @@ -0,0 +1,5 @@ +char* path = getenv("PATH"); + +//... + +fprintf(stderr, "cannot find exe on path %s\n", path); \ No newline at end of file diff --git a/cpp/ql/src/Security/CWE/CWE-676/DangerousUseOfCin.cpp b/cpp/ql/src/Security/CWE/CWE-676/DangerousUseOfCin.cpp new file mode 100644 index 000000000000..59435c531236 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-676/DangerousUseOfCin.cpp @@ -0,0 +1,18 @@ +#define BUFFER_SIZE 20 + +void bad() +{ + char buffer[BUFFER_SIZE]; + // BAD: Use of 'cin' without specifying the length of the input. + cin >> buffer; + buffer[BUFFER_SIZE-1] = '\0'; +} + +void good() +{ + char buffer[BUFFER_SIZE]; + // GOOD: Specifying the length of the input before using 'cin'. + cin.width(BUFFER_SIZE); + cin >> buffer; + buffer[BUFFER_SIZE-1] = '\0'; +} diff --git a/cpp/ql/src/Security/CWE/CWE-676/DangerousUseOfCin.qhelp b/cpp/ql/src/Security/CWE/CWE-676/DangerousUseOfCin.qhelp new file mode 100644 index 000000000000..9cfa4cd79214 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-676/DangerousUseOfCin.qhelp @@ -0,0 +1,29 @@ + + + +

    This rule finds calls to std::istream::operator>> on +std::cin without a preceding call to cin.width. +Consuming input from cin without specifying the length +of the input is dangerous due to the possibility of buffer overflows. + +

    + + +

    Always specify the length of any input expected from cin +by calling cin.width before consuming the input. + +

    + +

    The following example shows both a dangerous and a safe way to consume +input from cin.

    + + +
    + + + + + +
    diff --git a/cpp/ql/src/Security/CWE/CWE-676/DangerousUseOfCin.ql b/cpp/ql/src/Security/CWE/CWE-676/DangerousUseOfCin.ql new file mode 100644 index 000000000000..43ff0e6b8a34 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-676/DangerousUseOfCin.ql @@ -0,0 +1,259 @@ +/** + * @name Dangerous use of 'cin' + * @description Using `cin` without specifying the length of the input + * may be dangerous. + * @kind problem + * @problem.severity error + * @precision high + * @id cpp/dangerous-cin + * @tags reliability + * security + * external/cwe/cwe-676 + * external/cwe/cwe-242 + */ +import cpp + +/** + * A C/C++ `char*` or `wchar_t*` type. + */ +class AnyCharPointerType extends PointerType { + AnyCharPointerType() { + this.getBaseType().getUnderlyingType() instanceof CharType or + this.getBaseType().getUnderlyingType() instanceof WideCharType + } +} + +/** + * A C/C++ `char[]` or `wchar_t[]` type. + */ +class AnyCharArrayType extends ArrayType { + AnyCharArrayType() { + this.getBaseType().getUnderlyingType() instanceof CharType or + this.getBaseType().getUnderlyingType() instanceof WideCharType + } +} + +/** + * A C++ `std::basic_string` type (the underlying type of `std::string` + * and `std::wstring`). + */ +class AnyStdStringType extends Type { + AnyStdStringType() { + exists(Namespace std | + std.getName() = "std" and + std.getADeclaration() = this + ) and + this.getName().matches("basic\\_string<%") + } +} + +/** + * A C++ `std::basic_ifstream` type (the underlying type of + * `std::ifstream` and `std::wifstream`). + */ +class IFStream extends Type { + IFStream() { + exists(Namespace std | + std.getName() = "std" and + std.getADeclaration() = this + ) and + this.getName().matches("basic\\_ifstream<%") + } +} + +/** + * The variable `std::cin` or `std::wcin`. + */ +class CinVariable extends NamespaceVariable { + CinVariable() { + ( + getName() = "cin" or + getName() = "wcin" + ) and + getNamespace().getName() = "std" + } +} + +/** A call to `std::operator>>`. */ +class OperatorRShiftCall extends FunctionCall { + OperatorRShiftCall() { + getTarget().getNamespace().getName() = "std" and + getTarget().hasName("operator>>") + } + + /* + This is complicated by the fact this overload can be made + in two ways: + - as a member of the `std::istream` class, with one parameter. + - as an independent function, with two parameters. + */ + + Expr getSource() { + if (getTarget() instanceof MemberFunction) then ( + result = getQualifier() + ) else ( + result = getArgument(0) + ) + } + + Expr getDest() { + if (getTarget() instanceof MemberFunction) then ( + result = getArgument(0) + ) else ( + result = getArgument(1) + ) + } +} + +/** + * A potentially dangerous `std::istream` or `std::wistream`, for + * example, an access to `std::cin`. + */ +abstract class PotentiallyDangerousInput extends Expr { + /** + * Gets the variable that is the source of this input stream, if + * it can be determined. + */ + abstract Variable getStreamVariable(); + + /** + * Gets the previous access to the same input stream, if any. + */ + abstract PotentiallyDangerousInput getPreviousAccess(); + + /** + * Gets the width restriction that applies to the input stream + * for this expression, if any. + */ + Expr getWidth() { + result = getPreviousAccess().getWidthAfter() + } + + private Expr getWidthSetHere() { + exists(FunctionCall widthCall | + // std::istream.width or std::wistream.width + widthCall.getQualifier() = this and + widthCall.getTarget().getName() = "width" and + result = widthCall.getArgument(0) + ) or exists(FunctionCall setwCall, Function setw | + // >> std::setw + setwCall = this.(OperatorRShiftCall).getDest() and + setw = setwCall.getTarget() and + setw.getNamespace().getName() = "std" and + setw.hasName("setw") and + result = setwCall.getArgument(0) + ) + } + + private predicate isWidthConsumedHere() { + // std::cin >> s, where s is a char*, char[] or std::string type + // or wide character equivalent + exists(Type t | + t = this.(OperatorRShiftCall).getDest().getUnderlyingType() | + t instanceof AnyCharPointerType or + t instanceof AnyCharArrayType or + t instanceof AnyStdStringType) + } + + /** + * Gets the width restriction that applies to the input stream + * after this expression, if any. + */ + Expr getWidthAfter() { + result = getWidthSetHere() + or + ( + not exists(getWidthSetHere()) and + not isWidthConsumedHere() and + result = getWidth() + ) + } +} + +predicate nextPotentiallyDangerousInput(ControlFlowNode cfn, PotentiallyDangerousInput next, Variable streamVariable) { + ( + // this node + next = cfn and + next.getStreamVariable() = streamVariable + ) or ( + // flow + not cfn.(PotentiallyDangerousInput).getStreamVariable() = streamVariable and + nextPotentiallyDangerousInput(cfn.getASuccessor(), next, streamVariable) + ) +} + +/** + * A direct access to `std::cin` or `std::wcin`. + */ +class CinAccess extends PotentiallyDangerousInput { + CinAccess() { + this.(VariableAccess).getTarget() instanceof CinVariable + } + + override Variable getStreamVariable() { + result = this.(VariableAccess).getTarget() + } + + override PotentiallyDangerousInput getPreviousAccess() { + nextPotentiallyDangerousInput(result.getASuccessor(), this, result.getStreamVariable()) + } +} + +/** + * A direct access to a variable of type `std::ifstream` or `std::wifstream`. + */ +class IFStreamAccess extends PotentiallyDangerousInput { + IFStreamAccess() { + this.(VariableAccess).getTarget().getUnderlyingType() instanceof IFStream + } + + override Variable getStreamVariable() { + result = this.(VariableAccess).getTarget() + } + + override PotentiallyDangerousInput getPreviousAccess() { + nextPotentiallyDangerousInput(result.getASuccessor(), this, result.getStreamVariable()) + } +} + +/** + * A chained call to `std::operator>>` on a potentially dangerous input. + */ +class ChainedInput extends PotentiallyDangerousInput { + ChainedInput() { + this.(OperatorRShiftCall).getSource() instanceof PotentiallyDangerousInput + } + + override Variable getStreamVariable() { + result = this.(OperatorRShiftCall).getSource().(PotentiallyDangerousInput).getStreamVariable() + } + + override PotentiallyDangerousInput getPreviousAccess() { + result = this.(OperatorRShiftCall).getSource() + } +} + +from PotentiallyDangerousInput input, OperatorRShiftCall rshift, Expr dest +where + // a call to operator>> on a potentially dangerous input + input = rshift.getSource() and + dest = rshift.getDest() and + ( + ( + // destination is char* or wchar_t* + dest.getUnderlyingType() instanceof AnyCharPointerType and + + // assume any width setting makes this safe + not exists(input.getWidthAfter()) + ) or exists(int arraySize | + // destination is char[] or wchar_t* or a wide character equivalent. + arraySize = dest.getUnderlyingType().(AnyCharArrayType).getArraySize() and + + // assume any width setting makes this safe, unless we know + // it to be larger than the array. + forall(Expr w | w = input.getWidthAfter() | + w.getValue().toInt() > arraySize + ) + ) + ) +select rshift, "Use of 'cin' without specifying the length of the input may be dangerous." diff --git a/cpp/ql/src/Security/CWE/CWE-676/PotentiallyDangerousFunction.c b/cpp/ql/src/Security/CWE/CWE-676/PotentiallyDangerousFunction.c new file mode 100644 index 000000000000..d455bb102b08 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-676/PotentiallyDangerousFunction.c @@ -0,0 +1,12 @@ +// BAD: using gmtime +int is_morning_bad() { + struct tm *now = gmtime(time(NULL)); + return (now->tm_hour < 12); +} + +// GOOD: using gmtime_r +int is_morning_good() { + struct tm now; + gmtime_r(time(NULL), &now); + return (now.tm_hour < 12); +} diff --git a/cpp/ql/src/Security/CWE/CWE-676/PotentiallyDangerousFunction.qhelp b/cpp/ql/src/Security/CWE/CWE-676/PotentiallyDangerousFunction.qhelp new file mode 100644 index 000000000000..40b82e9310ac --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-676/PotentiallyDangerousFunction.qhelp @@ -0,0 +1,54 @@ + + + +

    This rule finds calls to functions that are dangerous to +use. Currently, it checks for calls +to gmtime. See Related rules +below for rules that identify other dangerous functions.

    + +

    The gmtime function fills data into a tm +struct in shared memory and then returns a pointer to that struct. If +the function is called from multiple places in the same program, and +especially if it is called from multiple threads in the same program, +then the calls will overwrite each other's data.

    + +
    + + +

    It is safer to use gmtime_r. +With gmtime_r, the application code manages allocation of +the tm struct. That way, separate calls to the function +can use their own storage.

    + +
    + +

    The following example checks the local time in two ways:

    + + +

    The first version uses gmtime, so it is vulnerable to +its data being overwritten by another thread. Even if this code is not +used in a multi-threaded context right now, future changes may +make the program multi-threaded. The second version of the code +uses gmtime_r. Since it allocates a new tm +struct on every call, it is immune to other calls to gmtime +or gmtime_r.

    + +
    +
    +

    Other dangerous functions identified by CWE-676 ("Use of +Potentially Dangerous Function") include strcpy +and strcat. Use of these functions is highlighted by +rules for the following CWEs:

    +
      +
    • CWE-120 Classic Buffer Overflow +
    • CWE-131 Incorrect Calculation of Buffer Size +
    + +
    + + + + +
    diff --git a/cpp/ql/src/Security/CWE/CWE-676/PotentiallyDangerousFunction.ql b/cpp/ql/src/Security/CWE/CWE-676/PotentiallyDangerousFunction.ql new file mode 100644 index 000000000000..d529adc278c1 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-676/PotentiallyDangerousFunction.ql @@ -0,0 +1,24 @@ +/** + * @name Use of potentially dangerous function + * @description Certain standard library functions are dangerous to call. + * @kind problem + * @problem.severity error + * @precision high + * @id cpp/potentially-dangerous-function + * @tags reliability + * security + * external/cwe/cwe-676 + */ +import cpp + + +predicate dangerousFunction(Function function) { + exists (string name | name = function.getQualifiedName() | + name = "gmtime") +} + + +from FunctionCall call, Function target +where call.getTarget() = target + and dangerousFunction(target) +select call, "Call to " + target.getQualifiedName() + " is potentially dangerous" diff --git a/cpp/ql/src/Security/CWE/CWE-732/DoNotCreateWorldWritable.c b/cpp/ql/src/Security/CWE/CWE-732/DoNotCreateWorldWritable.c new file mode 100644 index 000000000000..3c984aa6a62e --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-732/DoNotCreateWorldWritable.c @@ -0,0 +1,11 @@ +int write_default_config_bad() { + // BAD - this is world-writable so any user can overwrite the config + FILE* out = creat(OUTFILE, 0666); + fprintf(out, DEFAULT_CONFIG); +} + +int write_default_config_good() { + // GOOD - this allows only the current user to modify the file + FILE* out = creat(OUTFILE, S_IWUSR | S_IRUSR); + fprintf(out, DEFAULT_CONFIG); +} diff --git a/cpp/ql/src/Security/CWE/CWE-732/DoNotCreateWorldWritable.qhelp b/cpp/ql/src/Security/CWE/CWE-732/DoNotCreateWorldWritable.qhelp new file mode 100644 index 000000000000..6ee951490414 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-732/DoNotCreateWorldWritable.qhelp @@ -0,0 +1,50 @@ + + + +

    +When you create a file, take care to give it the most restrictive permissions possible. A typical +mistake is to create the file with world-writable permissions. This can allow an attacker to write to the file, which can give them +unexpected control over the program. +

    +
    + +

    +Files should usually be created with write permissions only for the current user. If broader permissions are needed, including +the users' group should be sufficient. It is very rare that a file needs to be world-writable, and care should be taken not +to make assumptions about the contents of any such file. +

    + +

    +On Unix systems, it is possible for the user who runs the program to restrict file creation permissions using umask. However, +a program should not assume that the user will set an umask, and should still set restrictive permissions by default. +

    +
    + +

    +This example shows two ways of writing a default configuration file. Software often does this to provide the user with a convenient +starting point for defining their own configuration. However, configuration files can also control important aspects of the software's behavior, +so it is important that they cannot be controlled by an attacker. +

    + +

    +The first example creates the default configuration file with the usual "default" Unix permissions, 0666. This makes the +file world-writable, so that an attacker could write in their own configuration that would be read by the program. The second example uses +more restrictive permissions: a combination of the standard Unix constants S_IWUSR and S_IRUSR which means that +only the current user will have read and write access to the file. +

    + + + +
    + + +
  • The CERT Oracle Secure Coding Standard for C: + + FIO06-C. Create files with appropriate access permissions + . +
  • + +
    +
    diff --git a/cpp/ql/src/Security/CWE/CWE-732/DoNotCreateWorldWritable.ql b/cpp/ql/src/Security/CWE/CWE-732/DoNotCreateWorldWritable.ql new file mode 100644 index 000000000000..5d1480f59c5f --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-732/DoNotCreateWorldWritable.ql @@ -0,0 +1,40 @@ +/** + * @name File created without restricting permissions + * @description Creating a file that is world-writable can allow an attacker to write to the file. + * @kind problem + * @problem.severity warning + * @precision medium + * @id cpp/world-writable-file-creation + * @tags security + * external/cwe/cwe-732 + */ + +import cpp +import FilePermissions +import semmle.code.cpp.commons.unix.Constants + +predicate worldWritableCreation(FileCreationExpr fc, int mode) { + mode = localUmask(fc).mask(fc.getMode()) + and + sets(mode, s_iwoth()) +} + +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 + mode = fc.getArgument(1).getValue().toInt() + and + sets(mode, s_iwoth()) +} + +from Expr fc, int mode, string message +where + worldWritableCreation(fc, mode) and message = "A file may be created here with mode "+octalFileMode(mode)+", which would make it world-writable." + or + setWorldWritable(fc, mode) and message = "This sets a file's permissions to "+octalFileMode(mode)+", which would make it world-writable." +select fc, message diff --git a/cpp/ql/src/Security/CWE/CWE-732/FilePermissions.qll b/cpp/ql/src/Security/CWE/CWE-732/FilePermissions.qll new file mode 100644 index 000000000000..3f89093fb046 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-732/FilePermissions.qll @@ -0,0 +1,181 @@ +import cpp +import semmle.code.cpp.commons.unix.Constants + +bindingset[n, digit] +private string octalDigit(int n, int digit) { + result = n.bitShiftRight(digit * 3).bitAnd(7).toString() +} + +bindingset[n, digit] +private string octalDigitOpt(int n, int digit) { + exists(string s | s = octalDigit(n, digit) | + if s = "0" then result = "" else result = s + ) +} + +bindingset[mode] +string octalFileMode(int mode) { + if mode >= 0 and mode <= 4095 /* octal 07777 */ then + result = + "0" + + octalDigitOpt(mode, 3) + + octalDigit(mode, 2) + + octalDigit(mode, 1) + + octalDigit(mode, 0) + else + result = "[non-standard mode: decimal "+ mode +"]" +} + +/** + * Holds if the bitmask `mask` sets any of the bit fields in `fields`. + */ +bindingset[mask, fields] +predicate sets(int mask, int fields) { + mask.bitAnd(fields) != 0 +} + +/** + * Gets the value that `fc` sets the umask to, if `fc` is a call to + * 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 + result = fc.getArgument(0).getValue().toInt() +} + +class Umask extends int { + Umask() { this = 0 or this = umask(_) } + + bindingset[mode,this] + int mask(int mode) { + result = mode.bitAnd(this.bitNot()) + } +} + +Umask defaultUmask() { result = 0 } + +/** + * Gets the last umask set in `block`. + */ +private Umask lastUmask(BasicBlock block) { + exists(int i | result = umask(block.getNode(i)) + and not exists(int j | j > i | exists(umask(block.getNode(j))))) +} + +private Umask umaskStrictlyReaches(BasicBlock block) { + exists(BasicBlock pred | pred = block.getAPredecessor() | + if exists(umask(pred.getNode(_))) + then + result = lastUmask(pred) + else + result = umaskStrictlyReaches(pred) + ) +} + +private Umask localDefinedUmask(FileCreationExpr e) { + exists(BasicBlock b, int i | e = b.getNode(i) | + (not exists(umask(b.getNode(_))) and result = umaskStrictlyReaches(b)) + or + exists(Expr um, int j | um = b.getNode(j) and j <= i | + result = umask(um) + and + not exists(int k | k in [j+1 .. i] | exists(umask(b.getNode(k)))) + ) + ) +} + +Umask localUmask(FileCreationExpr e) { + if exists(localDefinedUmask(e)) + then + result = localDefinedUmask(e) + else + result = defaultUmask() +} + +abstract class FileCreationExpr extends FunctionCall { + abstract Expr getPath(); + abstract int getMode(); +} + +class OpenCreationExpr extends FileCreationExpr { + OpenCreationExpr() { + exists(string name | + name = this.getTarget().getName() | + name = "open" or + name = "_open" or + name = "_wopen") + and + sets(this.getArgument(1).getValue().toInt(), o_creat()) + } + override Expr getPath() { result = this.getArgument(0) } + override int getMode() { + if exists(this.getArgument(2)) + then result = this.getArgument(2).getValue().toInt() + else // assume anything is permitted + result = 0.bitNot() + } +} + +class CreatCreationExpr extends FileCreationExpr { + CreatCreationExpr() { this.getTarget().getName() = "creat" } + override Expr getPath() { result = this.getArgument(0) } + override int getMode() { result = this.getArgument(1).getValue().toInt() } +} + +class OpenatCreationExpr extends FileCreationExpr { + OpenatCreationExpr() { + this.getTarget().getName() = "openat" and + this.getNumberOfArguments() = 4 + } + override Expr getPath() { result = this.getArgument(1) } + override int getMode() { result = this.getArgument(3).getValue().toInt() } +} + +private int fopenMode() { + result = s_irusr().bitOr(s_irgrp()).bitOr(s_iroth()).bitOr(s_iwusr()).bitOr(s_iwgrp()).bitOr(s_iwoth()) +} + +class FopenCreationExpr extends FileCreationExpr { + FopenCreationExpr() { + exists(string name | + name = this.getTarget().getName() | + name = "fopen" or + name = "_wfopen" or + name = "fsopen" or + name = "_wfsopen") + and + exists(string mode | + (mode = "w" or mode = "a") + and + this.getArgument(1).getValue().matches(mode+"%")) + } + override Expr getPath() { result = this.getArgument(0) } + override int getMode() { result = fopenMode() } +} + +class FopensCreationExpr extends FileCreationExpr { + FopensCreationExpr() { + exists(string name | + name = this.getTarget().getName() | + name = "fopen_s" or + name = "_wfopen_s") + and + exists(string mode | + (mode = "w" or mode = "a") + and + this.getArgument(2).getValue().matches(mode+"%") + ) + } + override Expr getPath() { result = this.getArgument(1) } + override int getMode() { + // fopen_s has restrictive permissions unless you have "u" in the mode + if this.getArgument(2).getValue().charAt(_) = "u" + then result = fopenMode() + else result = s_irusr().bitOr(s_iwusr()) + } +} diff --git a/cpp/ql/src/Security/CWE/CWE-764/LockFlow.qll b/cpp/ql/src/Security/CWE/CWE-764/LockFlow.qll new file mode 100644 index 000000000000..a370b2a83f50 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-764/LockFlow.qll @@ -0,0 +1,151 @@ +/** + * Provides classes and predicates for analyzing mutexes and the control + * flow regions where they might be locked. + */ +import cpp +import semmle.code.cpp.commons.Synchronization + +/** + * Holds if `cond` is a test for whether locking `access` succeeded, + * and `failNode` is the control flow node we continue with if it did + * not. Suitable locking statements may look like `if(tryLock(m))` or + * like `if(lock(m) != 0)`. + */ +cached +predicate tryLockCondition(VariableAccess access, + ControlFlowNode cond, + ControlFlowNode failNode) { + exists (FunctionCall call + | lockCall(access, call) + | (cond = call and call.getAFalseSuccessor() = failNode) + + // Look for code like this: + // + // if (pthread_mutex_lock(mtx) != 0) return -1; + // + or + (cond = call.getParent*() and + cond.isCondition() and + failNode = cond.getASuccessor() and + failNode instanceof BasicBlockWithReturn)) +} + +/** + * A basic block that ends with a return statement. + */ +class BasicBlockWithReturn extends BasicBlock { + BasicBlockWithReturn() { + this.contains(any(ReturnStmt r)) + } +} + +/** + * Holds if mutex variable `v` might be locked or unlocked during + * function call `call`? + */ +private predicate lockedOrUnlockedInCall(Variable v, FunctionCall call) { + lockCall(v.getAnAccess(), call) or + unlockCall(v.getAnAccess(), call) or + + // Interprocedural analysis: look for mutexes which are locked or + // unlocked in the body of the callee. + exists (Function fcn, Variable x + | fcn = call.getTarget() and + lockedOrUnlockedInFunction(x, fcn) + | // If `x` is one of the function's parameters, then map it to the + // corresponding argument. + if x = fcn.getAParameter() + then exists (int i | + x = fcn.getParameter(i) | + v.getAnAccess() = call.getArgument(i)) + else v = x) +} + +/** + * Holds if mutex variable `v` might be locked or unlocked by this + * function, either directly or indirectly (through a call to another + * function). + */ +private predicate lockedOrUnlockedInFunction(Variable v, Function fcn) { + exists (FunctionCall call | + lockedOrUnlockedInCall(v, call) and + call.getEnclosingFunction() = fcn) +} + +/** + * Holds if the mutex locked at `access` might still be locked after + * control flow node `node` has executed. That is, the lock which was + * obtained at `access` has not been canceled by a matching unlock or + * superseded by a more recent call to the lock method. + */ +predicate lockedOnExit(VariableAccess access, ControlFlowNode node) { + lockCall(access, node) + or + (lockedOnEntry(access, node) and + // Remove mutexes which are either unlocked by this statement or + // superseded by a another call to the lock method. + not lockedOrUnlockedInCall(access.getTarget(), node)) + or + // Interprocedural analysis: if the node is a function call and a mutex + // is still locked at the end of the function body, then it is also + // locked after the function returns. Note that the Function object is + // used to represent the exit node in the control flow graph. + exists (Function fcn, Variable x, VariableAccess xAccess | + fcn = node.(FunctionCall).getTarget() and + lockedOnEntry(xAccess, fcn) and + x = xAccess.getTarget() | + // If `x` is one of the function's parameters, then map it to the + // corresponding argument. + if x = fcn.getAParameter() + then exists (int i | + x = fcn.getParameter(i) | + access = node.(FunctionCall).getArgument(i)) + else access = xAccess) +} + +/** + * Holds if the mutex locked at `access` might still be locked before + * control flow node `node` executes. That is, if it might be locked + * after a predecessor of `node` has executed. + */ +predicate lockedOnEntry(VariableAccess access, ControlFlowNode node) { + exists (ControlFlowNode prev + | lockedOnExit(access, prev) and + node = prev.getASuccessor() and + // If we are on the false branch of a call to `try_lock` then the + // mutex is not locked. + not tryLockCondition(access, prev, node)) +} + +/** + * Holds if mutex `access` is locked either directly or indirectly by + * this function call. This is a generalization of `lockCall`. + */ +predicate lockedInCall(VariableAccess access, FunctionCall call) { + lockCall(access, call) + or + // Interprocedural analysis: look for mutexes which are locked in the + // body of the callee. + exists (Function fcn, Variable x, VariableAccess xAccess + | fcn = call.getTarget() and + pathToLock(xAccess, fcn.getEntryPoint()) and + x = xAccess.getTarget() + | // If `x` is one of the function's parameters, then map it to the + // corresponding argument. + if x = fcn.getAParameter() + then exists (int i + | x = fcn.getParameter(i) + | access = call.getArgument(i)) + else access = xAccess) +} + +/** + * Holds if mutex `access` might be locked at `node` or one of its + * successors. + */ +predicate pathToLock(VariableAccess access, ControlFlowNode node) { + lockedInCall(access, node) + or + (pathToLock(access, node.getASuccessor()) and + not lockedOrUnlockedInCall(access.getTarget(), node)) +} diff --git a/cpp/ql/src/Security/CWE/CWE-764/LockOrderCycle.qhelp b/cpp/ql/src/Security/CWE/CWE-764/LockOrderCycle.qhelp new file mode 100644 index 000000000000..bdeae6731f4b --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-764/LockOrderCycle.qhelp @@ -0,0 +1,64 @@ + + + + +

    +Deadlock can occur when two or more threads each attempt to lock +multiple mutexes, but not in the same order. A famous example of this +is the "Dining Philosophers Problem" (see references below). In this +example, deadlock occurs when all five philosophers simultaneously +pick up the fork to their left because then none of them can pick up +the fork to their right. A simple solution is to assign an order to +the mutexes. If the philosophers always pick up the lower numbered +fork first, rather than the fork to their left, then deadlock cannot +occur. +

    + +

    +In general, we can build a graph of the order in which the mutexes are +locked. For example, if one of the threads locks mutex A first and B +second (while A is still locked), then we add a graph edge from A to +B. If the resulting graph contains a cycle then there is a possibility +of deadlock. The graph for the dining philosophers example contains a +cycle because each philosopher creates a graph edge from their left +fork to their right fork. +

    +
    + + +

    +If multiple mutexes need to be locked simultaneously, then make sure +that all threads lock them in the same order. +

    +
    + + + +

    +In this example, f1 locks mtx1 first and +mtx2 second, but f2 locks them in the +opposite order. If f1 and f2 are run +simultaneously in separate threads then they could cause a deadlock. +

    + + + +
    + + +
  • Dijkstra, E. W., EWD-310: +Hierarchical +Ordering of Sequential Processes. E. W. Dijkstra +Archive. Center for American History, University of Texas at Austin. +
  • + +
  • Hoare, C. A. R., +Communicating +Sequential Processes, +Section 2.5 - 'Example: The Dining Philosophers,' p. 55, +2004 (originally published in 1985 by Prentice Hall International).
  • + +
    +
    diff --git a/cpp/ql/src/Security/CWE/CWE-764/LockOrderCycle.ql b/cpp/ql/src/Security/CWE/CWE-764/LockOrderCycle.ql new file mode 100644 index 000000000000..964eb1a7563f --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-764/LockOrderCycle.ql @@ -0,0 +1,39 @@ +/** + * @name Cyclic lock order dependency + * @description Locking mutexes in different orders in different + * threads can cause deadlock. + * @kind problem + * @id cpp/lock-order-cycle + * @problem.severity error + * @tags security + * external/cwe/cwe-764 + * external/cwe/cwe-833 + */ +import cpp +import semmle.code.cpp.commons.Synchronization +import LockFlow + +/** + * Gets a variable that might be locked while a lock on `v` is held. + * + * For example, with + * ``` + * x.lock() + * y.lock() + * x.unlock() + * y.unlock() + *``` + * `x` is already locked when `y.lock()` is called, so `y` is a result + * of `lockSuccessor(x)`. If you consider this an edge from `x` to `y` + * in a directed graph, then a cycle in the graph indicates a potential + * source of deadlock. The dining philosophers are the classic example. + */ +Variable lockSuccessor(Variable v) { + exists (FunctionCall call + | lockedOnEntry(v.getAnAccess(), call) and + lockedInCall(result.getAnAccess(), call)) +} + +from Variable v1, Variable v2 +where v1 != v2 and lockSuccessor+(v1) = v2 and lockSuccessor+(v2) = v1 +select v1, "Mutex " + v1 + " has a cyclic lock order dependency with $@.", v2, "mutex " + v2 diff --git a/cpp/ql/src/Security/CWE/CWE-764/LockOrderCycleExample.cpp b/cpp/ql/src/Security/CWE/CWE-764/LockOrderCycleExample.cpp new file mode 100644 index 000000000000..13067b06061c --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-764/LockOrderCycleExample.cpp @@ -0,0 +1,20 @@ +std::mutex mtx1; +std::mutex mtx2; + +void f1() { + // GOOD: lock mtx1 before mtx2. + mtx1.lock(); + mtx2.lock(); + printf("f1"); + mtx2.unlock(); + mtx1.unlock(); +} + +void f2() { + // BAD: lock mtx2 before mtx1. + mtx2.lock(); + mtx1.lock(); + printf("f2"); + mtx1.unlock(); + mtx2.unlock(); +} diff --git a/cpp/ql/src/Security/CWE/CWE-764/TwiceLocked.qhelp b/cpp/ql/src/Security/CWE/CWE-764/TwiceLocked.qhelp new file mode 100644 index 000000000000..2c09c4fffe09 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-764/TwiceLocked.qhelp @@ -0,0 +1,49 @@ + + + + +

    +Mutexes come in two flavors: recursive and non-recursive. For example, +the C++ mutex library provides both std::mutex +and std::recursive_mutex. A non-recursive mutex cannot +be locked until it has been unlocked by its previous owner, even if it +is already owned by the current thread. Deadlock is often caused by a +thread attempting to lock the same mutex twice, usually in a recursive +algorithm. +

    +
    + + +

    +If a recursive method needs to acquire a lock, then split it into two +methods. The first method is public and is responsible for locking and +unlocking the mutex. It delegates the rest of the work to a second +private method. The second method does not need to lock or unlock the +mutex because that is done by the first method. +

    +
    + + + +

    +In this example, the method f is recursive, so it causes +a deadlock by attempting to lock the mutex twice. +

    + + + +

    +In this second example, the recursion is delegated to an internal +method so the mutex is only locked once. +

    + + + +
    + + + + +
    diff --git a/cpp/ql/src/Security/CWE/CWE-764/TwiceLocked.ql b/cpp/ql/src/Security/CWE/CWE-764/TwiceLocked.ql new file mode 100644 index 000000000000..6e33fb0d5f42 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-764/TwiceLocked.ql @@ -0,0 +1,54 @@ +/** + * @name Mutex locked twice + * @description Calling the lock method of a mutex twice in succession + * might cause a deadlock. + * @kind problem + * @id cpp/twice-locked + * @problem.severity error + * @precision low + * @tags security + * external/cwe/cwe-764 + * external/cwe/cwe-833 + */ +import cpp +import semmle.code.cpp.commons.Synchronization +import LockFlow + +/** + * Holds if `call` locks `v`, via the access `a`, but `v` might already + * be locked when we reach `call`. The access `a` might be in a function + * which is called indirectly from `call`. + */ +cached private predicate twiceLocked( + FunctionCall call, Variable v, VariableAccess a) { + lockedOnEntry(v.getAnAccess(), call) and + lockedInCall(a, call) +} + +/* + * When this query finds a result, there are often multiple call sites + * associated with one instance of the problem. For this reason, we do not + * include `call` in the result. However, it is sometimes helpful to + * include `call.getLocation()` in the result, because it can help to find + * the control flow path which might be responsible. + */ +from FunctionCall call, Variable v, VariableAccess access2 +where + twiceLocked(call, v, access2) and + v = access2.getTarget() and + + // If the second lock is a `try_lock` then it won't cause a deadlock. + // We want to be extra sure that the second lock is not a `try_lock` + // to make sure that we don't generate too many false positives, so + // we use three heuristics: + // + // 1. The call is to a function named "try_lock". + // 2. The result of the call is used in a condition. For example: + // if (pthread_mutex_lock(mtx) != 0) return -1; + // 3. The call is a condition. Because the analysis is interprocedural, + // `call` might be an indirect call to `lock`, so this heuristic + // catches some cases which the second heuristic does not. + not (trylockCall(access2, _) or + tryLockCondition(access2, _, _) or + call.isCondition()) +select access2, "Mutex " + v + " might be locked already, which could cause a deadlock." diff --git a/cpp/ql/src/Security/CWE/CWE-764/TwiceLockedBad.cpp b/cpp/ql/src/Security/CWE/CWE-764/TwiceLockedBad.cpp new file mode 100644 index 000000000000..718bfdda8a03 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-764/TwiceLockedBad.cpp @@ -0,0 +1,11 @@ +class C { + std::mutex mutex; +public: + // BAD: recursion causes deadlock. + int f(int n) { + mutex.lock(); + int result = (n == 0) ? 1 : n*f(n-1); + mutex.unlock(); + return result; + } +}; diff --git a/cpp/ql/src/Security/CWE/CWE-764/TwiceLockedGood.cpp b/cpp/ql/src/Security/CWE/CWE-764/TwiceLockedGood.cpp new file mode 100644 index 000000000000..ff6868a00774 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-764/TwiceLockedGood.cpp @@ -0,0 +1,14 @@ +class C { + std::mutex mutex; + int f_impl(int n) { + return (n == 0) ? 1 : n*f_impl(n-1); + } +public: + // GOOD: recursion is delegated to f_impl. + int f(int n) { + mutex.lock(); + int result = f_impl(n); + mutex.unlock(); + return result; + } +}; diff --git a/cpp/ql/src/Security/CWE/CWE-764/UnreleasedLock.qhelp b/cpp/ql/src/Security/CWE/CWE-764/UnreleasedLock.qhelp new file mode 100644 index 000000000000..5d91f18932eb --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-764/UnreleasedLock.qhelp @@ -0,0 +1,45 @@ + + + + +

    +When a thread acquires a lock it must make sure to unlock it again; +failing to do so can lead to deadlocks. If a lock allows a thread to acquire +it multiple times, for example std::recursive_mutex, +then the number of locks must match the number of unlocks in order to fully +release the lock. +

    +
    + + +

    +The best way to ensure that locks are always unlocked is to use RAII (Resource Acquisition Is Initialization). +That means acquiring the lock in the constructor of a class, and releasing it in its destructor. +A local-scoped instance of that class will then be destroyed when it leaves scope, even if an exception is thrown, +ensuring that the lock is released. +

    +
    + + + +

    +In this example, the mutex may not be unlocked if the function returns early. +

    + + + +

    +In this second example, we show a simple RAII wrapper class for std::mutex. Using this ensures +that even in the case of the early return, the mutex is released. +

    + + + +
    + + + + +
    \ No newline at end of file diff --git a/cpp/ql/src/Security/CWE/CWE-764/UnreleasedLock.ql b/cpp/ql/src/Security/CWE/CWE-764/UnreleasedLock.ql new file mode 100644 index 000000000000..27486e2f7334 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-764/UnreleasedLock.ql @@ -0,0 +1,79 @@ +/** + * @name Lock may not be released + * @description A lock that is acquired one or more times without a + * matching number of unlocks may cause a deadlock. + * @kind problem + * @id cpp/unreleased-lock + * @problem.severity error + * @precision low + * @tags security + * external/cwe/cwe-764 + * external/cwe/cwe-833 + */ +import cpp +import semmle.code.cpp.commons.Synchronization + +predicate lockBlock(MutexType t, BasicBlock b, int locks) { + locks = strictcount(int i | b.getNode(i) = t.getLockAccess()) +} + +predicate unlockBlock(MutexType t, BasicBlock b, int unlocks) { + unlocks = strictcount(int i | b.getNode(i) = t.getUnlockAccess()) +} + +/** + * Holds if there is a call to `lock` or `tryLock` on `t` in + * `lockblock`, and `failblock` is the successor if it fails. + */ +predicate failedLock(MutexType t, BasicBlock lockblock, BasicBlock failblock) { + exists (ControlFlowNode lock | + lock = lockblock.getEnd() and + lock = t.getLockAccess() and + lock.getAFalseSuccessor() = failblock + ) +} + +/** + * Holds if `b` locks `t` a net `netlocks` times. For example, if `b` + * locks `t` twice and unlocks `t` four times, then `netlocks` will be + * `-2`. + */ +predicate lockUnlockBlock(MutexType t, BasicBlock b, int netlocks) { + lockBlock(t, b, netlocks) and not unlockBlock(t, b, _) or + exists (int unlocks | + not lockBlock(t, b, _) and unlockBlock(t, b, unlocks) and netlocks = -unlocks + ) or + exists (int locks, int unlocks | + lockBlock(t, b, locks) and unlockBlock(t, b, unlocks) and netlocks = locks - unlocks + ) +} + +/** + * Holds if there is a control flow path from `src` to `b` such that + * on that path the net number of locks is `locks`, and `locks` is + * positive. + */ +predicate blockIsLocked(MutexType t, BasicBlock src, BasicBlock b, int locks) { + lockUnlockBlock(t, b, locks) and src = b and locks > 0 + or + exists (BasicBlock pred, int predlocks, int curlocks, int failedlock | + pred = b.getAPredecessor() | + blockIsLocked(t, src, pred, predlocks) and + ( if failedLock(t, pred, b) then failedlock = 1 else failedlock = 0 ) and // count a failed lock as an unlock so the net is zero + ( not lockUnlockBlock(t, b, _) and curlocks = 0 or + lockUnlockBlock(t, b, curlocks) + ) and + locks = predlocks + curlocks -failedlock and locks > 0 and + locks < 10 // arbitrary bound to fail gracefully in case of locking in a loop + ) +} + +from Function c, MutexType t, BasicBlock src, BasicBlock exit, FunctionCall lock +where // restrict results to those methods that actually attempt to unlock + t.getUnlockAccess().getEnclosingFunction() = c + and blockIsLocked(t, src, exit, _) + and exit.getEnd() = c + and lock = src.getANode() + and lock = t.getLockAccess() +select + lock, "This lock might not be unlocked or might be locked more times than it is unlocked." diff --git a/cpp/ql/src/Security/CWE/CWE-764/UnreleasedLockBad.cpp b/cpp/ql/src/Security/CWE/CWE-764/UnreleasedLockBad.cpp new file mode 100644 index 000000000000..4a97eb8028a7 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-764/UnreleasedLockBad.cpp @@ -0,0 +1,11 @@ +std::mutex mutex; +int fun() { + mutex.lock(); + bool succeeded = doWork(); + if (!succeeded) { + // BAD: this does not release the mutex + return -1 + } + mutex.unlock(); + return 1; +} diff --git a/cpp/ql/src/Security/CWE/CWE-764/UnreleasedLockGood.cpp b/cpp/ql/src/Security/CWE/CWE-764/UnreleasedLockGood.cpp new file mode 100644 index 000000000000..41f0469ba8b6 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-764/UnreleasedLockGood.cpp @@ -0,0 +1,28 @@ +class RAII_Mutex +{ + std::mutex lock; +public: + RAII_Mutex(mutex m) : lock(m) + { + lock.lock(); + } + + ~RAII_Mutex() + { + lock.unlock(); + } +}; + + +std::mutex mutex; +int fun() { + RAII_Mutex(mutex); + + bool succeeded = doWork(); + if (!succeeded) { + // GOOD: the RAII_Mutex is destroyed, releasing the lock + return -1 + } + + return 1; +} \ No newline at end of file diff --git a/cpp/ql/src/Security/CWE/CWE-807/TaintedCondition.c b/cpp/ql/src/Security/CWE/CWE-807/TaintedCondition.c new file mode 100644 index 000000000000..8664d491d950 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-807/TaintedCondition.c @@ -0,0 +1,10 @@ +struct hostent *hp;struct in_addr myaddr; +char* tHost = "trustme.example.com"; +myaddr.s_addr=inet_addr(ip_addr_string); + +hp = gethostbyaddr((char *) &myaddr, sizeof(struct in_addr), AF_INET); +if (hp && !strncmp(hp->h_name, tHost, sizeof(tHost))) { + trusted = true; +} else { + trusted = false; +} diff --git a/cpp/ql/src/Security/CWE/CWE-807/TaintedCondition.qhelp b/cpp/ql/src/Security/CWE/CWE-807/TaintedCondition.qhelp new file mode 100644 index 000000000000..80e51ec08a30 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-807/TaintedCondition.qhelp @@ -0,0 +1,47 @@ + + + +

    This rule finds code where untrusted inputs are used in +an if statement, and the body of that statement makes a +security decision. This is an example of CWE-807 and makes the program +vulnerable to attack. An attacker might be able to gain unauthorized +access to the system by manipulating external inputs to the system.

    + +
    + +

    In most cases, you need to add or strengthen the checks made on the +user-supplied data to ensure its integrity. The user-supplied data can +then be used as a trusted input to the security decision. For example, +instead of checking an HTTP cookie against a predictable fixed string, +check a cookie against a randomly generated session key.

    + +

    This rule may highlight a few conditions where user-supplied +data has been checked and can be trusted. It is not always possible +to determine if the checks applied to data are enough to ensure security.

    + +
    + +

    The following example is included in CWE 807.

    + + +

    In this example, the result of a reverse DNS query is compared +against a fixed string. An attacker can return an incorrect reverse +DNS entry for the requesting IP and thus gain the same access as a +legitimate user from trustme.example.com.

    + +

    To fix the problem in this example, you need to add an additional +mechanism to test the user-supplied data. For example, +numeric IP addresses could be used.

    + + +
    + + + + + + +
    diff --git a/cpp/ql/src/Security/CWE/CWE-807/TaintedCondition.ql b/cpp/ql/src/Security/CWE/CWE-807/TaintedCondition.ql new file mode 100644 index 000000000000..68a9c7d5a53c --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-807/TaintedCondition.ql @@ -0,0 +1,36 @@ +/** + * @name Untrusted input for a condition + * @description Using untrusted inputs in a statement that makes a + * security decision makes code vulnerable to + * attack. + * @kind problem + * @problem.severity warning + * @precision medium + * @id cpp/tainted-permissions-check + * @tags security + * external/cwe/cwe-807 + */ + +import semmle.code.cpp.security.TaintTracking + + +/** + * Holds if there is an 'if' statement whose condition `condition` + * is influenced by tainted data `source`, and the body contains + * `raise` which escalates privilege. + */ +predicate cwe807violation(Expr source, Expr condition, Expr raise) { + tainted(source, condition) + and raisesPrivilege(raise) + and exists (IfStmt ifstmt | + ifstmt.getCondition() = condition + and raise.getEnclosingStmt().getParentStmt*() = ifstmt) +} + +from Expr source, Expr condition, Expr raise +where cwe807violation(source, condition, raise) +select + condition, + "Reliance on untrusted input $@ to raise privilege at $@", + source, source.toString(), + raise, raise.toString() diff --git a/cpp/ql/src/Security/CWE/CWE-835/InfiniteLoopBad.c b/cpp/ql/src/Security/CWE/CWE-835/InfiniteLoopBad.c new file mode 100644 index 000000000000..6ab1c6d375f8 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-835/InfiniteLoopBad.c @@ -0,0 +1,12 @@ +void test(int n) { + int i = 0; + if (n <= 0) { + return; + } + while (1) { + // BAD: condition is never true, so loop will not terminate. + if (i == n) { + break; + } + } +} diff --git a/cpp/ql/src/Security/CWE/CWE-835/InfiniteLoopGood.c b/cpp/ql/src/Security/CWE/CWE-835/InfiniteLoopGood.c new file mode 100644 index 000000000000..a688ff0c2d52 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-835/InfiniteLoopGood.c @@ -0,0 +1,13 @@ +void test(int n) { + int i = 0; + if (n <= 0) { + return; + } + while (1) { + // GOOD: condition is true after n iterations. + if (i == n) { + break; + } + i++; + } +} diff --git a/cpp/ql/src/Security/CWE/CWE-835/InfiniteLoopWithUnsatisfiableExitCondition.qhelp b/cpp/ql/src/Security/CWE/CWE-835/InfiniteLoopWithUnsatisfiableExitCondition.qhelp new file mode 100644 index 000000000000..0ab7d1c334b5 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-835/InfiniteLoopWithUnsatisfiableExitCondition.qhelp @@ -0,0 +1,44 @@ + + + + +

    +Loops can contain multiple exit conditions, either directly in the loop +condition or as guards around break or return +statements. If none of the exit conditions can ever be satisfied, then +the loop will never terminate. A program containing an infinite loop +could be vulnerable to a denial of service attack if it is possible for +an attacker to trigger the loop. +

    +
    + + +

    +When writing a loop that is intended to terminate, make sure that all the +necessary exit conditions can be satisfied and that loop termination is clear. +

    +
    + + + +

    +The following example shows an infinite loop. The value of +variable i is always zero, so the condition guarding the +break is always false. +

    + + + +

    The error has been fixed below by incrementing i in +the body of the loop. +

    + + + +
    + + + +
    diff --git a/cpp/ql/src/Security/CWE/CWE-835/InfiniteLoopWithUnsatisfiableExitCondition.ql b/cpp/ql/src/Security/CWE/CWE-835/InfiniteLoopWithUnsatisfiableExitCondition.ql new file mode 100644 index 000000000000..df13779f8c11 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-835/InfiniteLoopWithUnsatisfiableExitCondition.ql @@ -0,0 +1,64 @@ +/** + * @name Infinite loop with unsatisfiable exit condition + * @description A loop with an unsatisfiable exit condition could + * prevent the program from terminating, making it + * vulnerable to a denial of service attack. + * @kind problem + * @id cpp/infinite-loop-with-unsatisfiable-exit-condition + * @problem.severity warning + * @tags security + * external/cwe/cwe-835 + */ +import cpp +import semmle.code.cpp.controlflow.BasicBlocks +private import semmle.code.cpp.rangeanalysis.PointlessComparison +import semmle.code.cpp.controlflow.internal.ConstantExprs + +/** + * Holds if there is a control flow edge from `src` to `dst`, but + * it can never be taken due to `cmp` always having value `value`. + */ +predicate impossibleEdge( + ComparisonOperation cmp, boolean value, BasicBlock src, BasicBlock dst) { + cmp = src.getEnd() and + reachablePointlessComparison(cmp, _, _, value, _) and + if value = true + then dst = src.getAFalseSuccessor() + else dst = src.getATrueSuccessor() +} + +BasicBlock enhancedSucc(BasicBlock bb) { + result = bb.getASuccessor() and not impossibleEdge(_, _, bb, result) +} + +/** + * Holds if `cmp` always has value `value`, and if that will cause + * non-termination. + * + * It only holds if the function exit is reachable using + * the standard `getASuccessor` relation, but not using + * `enhancedSucc`. This means that it does not hold for + * comparison operations which are trivially true or false, such as + * ``` + * while (1) { ... } + * ``` + * Since this loop is obviously infinite, we assume that it was written + * intentionally. + */ +predicate impossibleEdgeCausesNonTermination( + ComparisonOperation cmp, boolean value) { + exists (BasicBlock src + | impossibleEdge(cmp, value, src, _) and + src.getASuccessor+() instanceof ExitBasicBlock and + not (enhancedSucc+(src) instanceof ExitBasicBlock) and + // Make sure that the source is reachable to reduce + // false positives. + exists (EntryBasicBlock entry | src = enhancedSucc+(entry))) +} + +from ComparisonOperation cmp, boolean value +where impossibleEdgeCausesNonTermination(cmp, value) +select + cmp, + "Function exit is unreachable because this condition is always " + + value.toString() + "." diff --git a/cpp/ql/src/cpp.qll b/cpp/ql/src/cpp.qll new file mode 100644 index 000000000000..bc60dd0ec59e --- /dev/null +++ b/cpp/ql/src/cpp.qll @@ -0,0 +1,89 @@ +/** + * Provides classes and predicates for working with C/C++/ObjC/ObjC++ + * code. + * + * Where the documentation refers to the standards, it gives + * references to the freely-available drafts. + * + * For C++11, these references are of the form [N3337 5.3.2/1], and the + * corresponding draft of the standard can be downloaded from + * https://github.com/cplusplus/draft/raw/master/papers/n3337.pdf + * + * For C++14, these references are of the form [N4140 5.3.2/1], and the + * corresponding draft of the standard can be downloaded from + * https://github.com/cplusplus/draft/raw/master/papers/n4140.pdf + */ + +import semmle.code.cpp.File +import semmle.code.cpp.Linkage +import semmle.code.cpp.Location + +import semmle.code.cpp.Compilation +import semmle.code.cpp.Element +import semmle.code.cpp.Namespace +import semmle.code.cpp.Specifier +import semmle.code.cpp.Declaration +import semmle.code.cpp.Include +import semmle.code.cpp.Macro +import semmle.code.cpp.Type +import semmle.code.cpp.TypedefType +import semmle.code.cpp.Class +import semmle.code.cpp.Struct +import semmle.code.cpp.Union +import semmle.code.cpp.Enum +import semmle.code.cpp.Member +import semmle.code.cpp.Field +import semmle.code.cpp.Function +import semmle.code.cpp.Parameter +import semmle.code.cpp.Variable +import semmle.code.cpp.Initializer + +import semmle.code.cpp.FriendDecl + +import semmle.code.cpp.exprs.Expr +import semmle.code.cpp.exprs.ArithmeticOperation +import semmle.code.cpp.exprs.BitwiseOperation +import semmle.code.cpp.exprs.LogicalOperation +import semmle.code.cpp.exprs.ComparisonOperation +import semmle.code.cpp.exprs.Assignment +import semmle.code.cpp.exprs.Cast +import semmle.code.cpp.exprs.Access +import semmle.code.cpp.exprs.Call +import semmle.code.cpp.exprs.Lambda +import semmle.code.cpp.exprs.Literal +import semmle.code.cpp.exprs.BuiltInOperations + +import semmle.code.cpp.stmts.Stmt +import semmle.code.cpp.stmts.Block + +import semmle.code.cpp.metrics.MetricNamespace +import semmle.code.cpp.metrics.MetricClass +import semmle.code.cpp.metrics.MetricFile +import semmle.code.cpp.metrics.MetricFunction + +import semmle.code.cpp.commons.CommonType +import semmle.code.cpp.commons.Printf +import semmle.code.cpp.commons.VoidContext +import semmle.code.cpp.commons.NULL +import semmle.code.cpp.commons.PolymorphicClass +import semmle.code.cpp.commons.Alloc +import semmle.code.cpp.commons.StructLikeClass + +import semmle.code.cpp.controlflow.ControlFlowGraph + +import semmle.code.cpp.XML + +import semmle.code.cpp.Diagnostics + +import semmle.code.cpp.Comments +import semmle.code.cpp.Preprocessor + +import semmle.code.cpp.Iteration + +import semmle.code.cpp.NameQualifiers + +import semmle.code.cpp.ObjectiveC +import semmle.code.cpp.exprs.ObjectiveC + +import DefaultOptions + diff --git a/cpp/ql/src/default.qll b/cpp/ql/src/default.qll new file mode 100644 index 000000000000..4996ace84523 --- /dev/null +++ b/cpp/ql/src/default.qll @@ -0,0 +1 @@ +import cpp diff --git a/cpp/ql/src/definitions.ql b/cpp/ql/src/definitions.ql new file mode 100644 index 000000000000..117e7512a68a --- /dev/null +++ b/cpp/ql/src/definitions.ql @@ -0,0 +1,13 @@ +/** + * @name Jump-to-definition links + * @description Generates use-definition pairs that provide the data + * for jump-to-definition in the code viewer. + * @kind definitions + * @id cpp/jump-to-definition + */ + +import definitions + +from Top e, Top def, string kind +where def = definitionOf(e, kind) +select e, def, kind diff --git a/cpp/ql/src/definitions.qll b/cpp/ql/src/definitions.qll new file mode 100644 index 000000000000..754037953123 --- /dev/null +++ b/cpp/ql/src/definitions.qll @@ -0,0 +1,212 @@ +/** + * Provides classes and predicates related to jump-to-definition links + * in the code viewer. + */ + +import cpp + +/** + * Any element that might be the source or target of a jump-to-definition + * link. + * + * In some cases it is preferable to modify locations (the + * `hasLocationInfo()` predicate) so that they are short, and + * non-overlapping with other locations that might be highlighted in + * the LGTM interface. + * + * We need to give locations that may not be in the database, so + * we use `hasLocationInfo()` rather than `getLocation()`. + */ +class Top extends Element { + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [LGTM locations](https://lgtm.com/help/ql/locations). + */ + predicate hasLocationInfo(string filepath, + int startline, int startcolumn, + int endline, int endcolumn) { + exists(Location l | l = this.getLocation() + and filepath = l.getFile().getAbsolutePath() + and startline = l.getStartLine() + and startcolumn = l.getStartColumn() + and endline = l.getEndLine() + and endcolumn = l.getEndColumn()) + } +} + +/** + * A `MacroAccess` with a `hasLocationInfo` predicate. + * + * This has a location that covers only the name of the accessed + * macro, not its arguments (which are included by `MacroAccess`'s + * `getLocation()`). + */ +class MacroAccessWithHasLocationInfo extends Top { + MacroAccessWithHasLocationInfo() { + this instanceof MacroAccess + } + + override + predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { + exists(MacroAccess ma, Location l | + ma = this + and l = ma.getLocation() + and path = l.getFile().getAbsolutePath() + and sl = l.getStartLine() + and sc = l.getStartColumn() + and el = sl + and ec = sc + ma.getMacroName().length() - 1) + } +} + +/** + * An `Include` with a `hasLocationInfo` predicate. + * + * This has a location that covers only the name of the included + * file, not the `#include` text or whitespace before it. + */ +class IncludeWithHasLocationInfo extends Top { + IncludeWithHasLocationInfo() { + this instanceof Include + } + + override + predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { + exists(Include i, Location l | + i = this and + l = i.getLocation() and + path = l.getFile().getAbsolutePath() and + sl = l.getEndLine() and + sc = l.getEndColumn() + 1 - i.getIncludeText().length() and + el = l.getEndLine() and + ec = l.getEndColumn() + ) + } +} + +/** + * Holds if `f`, `line`, `column` indicate the start character + * of `cc`. + */ +private predicate constructorCallStartLoc(ConstructorCall cc, File f, int line, int column) { + exists(Location l | + l = cc.getLocation() and + l.getFile() = f and + l.getStartLine() = line and + l.getStartColumn() = column + ) +} + +/** + * Holds if `f`, `line`, `column` indicate the start character + * of `tm`. + */ +private predicate typeMentionStartLoc(TypeMention tm, File f, int line, int column) { + exists(Location l | + l = tm.getLocation() and + l.getFile() = f and + l.getStartLine() = line and + l.getStartColumn() = column + ) +} + +/** + * Holds if `cc` and `tm` begin at the same character. + */ +private cached predicate constructorCallTypeMention(ConstructorCall cc, TypeMention tm) { + exists(File f, int line, int column | + constructorCallStartLoc(cc, f, line, column) and + typeMentionStartLoc(tm, f, line, column) + ) +} + +/** + * Gets an element, of kind `kind`, that element `e` uses, if any. + * + * The `kind` is a string representing what kind of use it is: + * - `"M"` for function and method calls + * - `"T"` for uses of types + * - `"V"` for variable accesses + * - `"X"` for macro accesses + * - `"I"` for import / include directives + */ +Top definitionOf(Top e, string kind) { + ( + ( + // call -> function called + kind = "M" and + result = e.(Call).getTarget() and + not e.(Expr).isCompilerGenerated() and + not e instanceof ConstructorCall // handled elsewhere + ) or ( + // access -> function, variable or enum constant accessed + kind = "V" and + result = e.(Access).getTarget() and + not e.(Expr).isCompilerGenerated() + ) or ( + // macro access -> macro accessed + kind = "X" and + result = e.(MacroAccess).getMacro() + ) or ( + // type mention -> type + kind = "T" and + e.(TypeMention).getMentionedType() = result and + not constructorCallTypeMention(_, e) and // handled elsewhere + + // multiple mentions can be generated when a typedef is used. Exclude + // all but the originating typedef. + not exists(TypeMention tm, File f, int startline, int startcol | + typeMentionStartLoc(e, f, startline, startcol) and + typeMentionStartLoc(tm, f, startline, startcol) and + ( + e.(TypeMention).getMentionedType() = tm.getMentionedType().(TypedefType).getBaseType() or + e.(TypeMention).getMentionedType() = tm.getMentionedType().(TypedefType).getBaseType().(SpecifiedType).getBaseType() + ) + ) + ) or ( + // constructor call -> function called + // - but only if there is a corresponding type mention, since + // we don't want links for implicit conversions. + // - using the location of the type mention, since it's + // tighter that the location of the function call. + kind = "M" and + exists(ConstructorCall cc | + constructorCallTypeMention(cc, e) and + result = cc.getTarget() + ) + ) or ( + // include -> included file + kind = "I" and + result = e.(Include).getIncludedFile() and + + // exclude `#include` directives containing macros + not exists(MacroInvocation mi, Location l1, Location l2 | + l1 = e.(Include).getLocation() and + l2 = mi.getLocation() and + l1.getFile() = l2.getFile() and + l1.getStartLine() = l2.getStartLine() + // (an #include directive must be always on it's own line) + ) + ) + ) and ( + // exclude things inside macro invocations, as they will overlap + // with the macro invocation. + not e.(Element).isInMacroExpansion() and + + // exclude nested macro invocations, as they will overlap with + // the top macro invocation. + not exists(e.(MacroAccess).getParentInvocation()) and + + // exclude results from template instantiations, as: + // (1) these dependencies will often be caused by a choice of + // template parameter, which is non-local to this part of code; and + // (2) overlapping results pointing to different locations will + // be very common. + // It's possible we could allow a subset of these dependencies + // in future, if we're careful to ensure the above don't apply. + not e.isFromTemplateInstantiation(_) + ) +} diff --git a/cpp/ql/src/external/CodeDuplication.qll b/cpp/ql/src/external/CodeDuplication.qll new file mode 100644 index 000000000000..7815751dccce --- /dev/null +++ b/cpp/ql/src/external/CodeDuplication.qll @@ -0,0 +1,279 @@ +import cpp + +private +string relativePath(File file) { + result = file.getRelativePath().replaceAll("\\", "/") +} + +private cached +predicate tokenLocation(string path, int sl, int sc, int ec, int el, Copy copy, int index) { + path = copy.sourceFile().getAbsolutePath() and + tokens(copy, index, sl, sc, ec, el) +} + +class Copy extends @duplication_or_similarity +{ + private + int lastToken() { + result = max(int i | tokens(this, i, _, _, _, _) | i) + } + + int tokenStartingAt(Location loc) { + exists(string filepath, int startline, int startcol | + loc.hasLocationInfo(filepath, startline, startcol, _, _) and + tokenLocation(filepath, startline, startcol, _, _, this, result) + ) + } + + int tokenEndingAt(Location loc) { + exists(string filepath, int endline, int endcol | + loc.hasLocationInfo(filepath, _, _, endline, endcol) and + tokenLocation(filepath, _, _, endline, endcol, this, result) + ) + } + + int sourceStartLine() { + tokens(this, 0, result, _, _, _) + } + + int sourceStartColumn() { + tokens(this, 0, _, result, _, _) + } + + int sourceEndLine() { + tokens(this, lastToken(), _, _, result, _) + } + + int sourceEndColumn() { + tokens(this, lastToken(), _, _, _, result) + } + + int sourceLines() { + result = this.sourceEndLine() + 1 - this.sourceStartLine() + } + + int getEquivalenceClass() { + duplicateCode(this, _, result) or similarCode(this, _, result) + } + + File sourceFile() { + exists(string name | + duplicateCode(this, name, _) or similarCode(this, name, _) | + name.replaceAll("\\", "/") = relativePath(result)) + } + + predicate hasLocationInfo(string filepath, int startline, int startcolumn, int endline, int endcolumn) { + sourceFile().getAbsolutePath() = filepath and + startline = sourceStartLine() and + startcolumn = sourceStartColumn() and + endline = sourceEndLine() and + endcolumn = sourceEndColumn() + } + + string toString() { none() } +} + +class DuplicateBlock extends Copy, @duplication +{ + override string toString() { + result = "Duplicate code: " + sourceLines() + " duplicated lines." + } +} + +class SimilarBlock extends Copy, @similarity +{ + override string toString() { + result = "Similar code: " + sourceLines() + " almost duplicated lines." + } +} + + + +FunctionDeclarationEntry sourceMethod() { + result.isDefinition() and + exists(result.getLocation()) and numlines(result.getFunction(),_,_,_) +} + +int numberOfSourceMethods(Class c) { + result = count(FunctionDeclarationEntry m | + m = sourceMethod() and + m.getFunction().getDeclaringType() = c) +} + +private +predicate blockCoversStatement(int equivClass, int first, int last, Stmt stmt) { + exists(DuplicateBlock b, Location loc | + stmt.getLocation() = loc and + first = b.tokenStartingAt(loc) and + last = b.tokenEndingAt(loc) and + b.getEquivalenceClass() = equivClass) +} + +private +Stmt statementInMethod(FunctionDeclarationEntry m) { + result.getParent+() = m.getBlock() and + not result.getLocation() instanceof UnknownStmtLocation and + not result instanceof Block +} + +private +predicate duplicateStatement(FunctionDeclarationEntry m1, FunctionDeclarationEntry m2, Stmt s1, Stmt s2) { + exists(int equivClass, int first, int last | + s1 = statementInMethod(m1) and + s2 = statementInMethod(m2) and + blockCoversStatement(equivClass, first, last, s1) and + blockCoversStatement(equivClass, first, last, s2) and + s1 != s2 and m1 != m2 + ) +} + +predicate duplicateStatements(FunctionDeclarationEntry m1, FunctionDeclarationEntry m2, int duplicate, int total) { + duplicate = strictcount(Stmt s | duplicateStatement(m1, m2, s, _)) and + total = strictcount(statementInMethod(m1)) +} + +/** + * Find pairs of methods are identical + */ +predicate duplicateMethod(FunctionDeclarationEntry m, FunctionDeclarationEntry other) { + exists(int total | duplicateStatements(m, other, total, total)) +} + +predicate similarLines(File f, int line) { + exists(SimilarBlock b | + b.sourceFile() = f and line in [b.sourceStartLine() .. b.sourceEndLine()] + ) +} + +private +predicate similarLinesPerEquivalenceClass(int equivClass, int lines, File f) +{ + lines = strictsum(SimilarBlock b, int toSum | (b.sourceFile() = f and b.getEquivalenceClass() = equivClass) and (toSum = b.sourceLines()) | toSum) +} + +private predicate similarLinesCoveredFiles(File f, File otherFile) { + exists(int numLines | numLines = f.getMetrics().getNumberOfLines() | + exists(int coveredApprox | + coveredApprox = strictsum(int num | + exists(int equivClass | + similarLinesPerEquivalenceClass(equivClass, num, f) and + similarLinesPerEquivalenceClass(equivClass, num, otherFile) and + f != otherFile + ) + ) and + ((coveredApprox * 100) / numLines > 75) + ) + ) +} + +predicate similarLinesCovered(File f, int coveredLines, File otherFile) { + exists(int numLines | numLines = f.getMetrics().getNumberOfLines() | + similarLinesCoveredFiles(f, otherFile) and + exists(int notCovered | + notCovered = count(int j | + j in [1 .. numLines] and + not similarLines(f, j) + ) and + coveredLines = numLines - notCovered + ) + ) +} + +predicate duplicateLines(File f, int line) { + exists(DuplicateBlock b | + b.sourceFile() = f and line in [b.sourceStartLine() .. b.sourceEndLine()] + ) +} + +private +predicate duplicateLinesPerEquivalenceClass(int equivClass, int lines, File f) +{ + lines = strictsum(DuplicateBlock b, int toSum | (b.sourceFile() = f and b.getEquivalenceClass() = equivClass) and (toSum = b.sourceLines()) | toSum) +} + +predicate duplicateLinesCovered(File f, int coveredLines, File otherFile) { + exists(int numLines | numLines = f.getMetrics().getNumberOfLines() | + exists(int coveredApprox | + coveredApprox = strictsum(int num | + exists(int equivClass | + duplicateLinesPerEquivalenceClass(equivClass, num, f) and + duplicateLinesPerEquivalenceClass(equivClass, num, otherFile) and + f != otherFile + ) + ) and + ((coveredApprox * 100) / numLines > 75) + ) and + exists(int notCovered | + notCovered = count(int j | + j in [1 .. numLines] and + not duplicateLines(f, j) + ) and + coveredLines = numLines - notCovered + ) + ) +} + +predicate similarFiles(File f, File other, int percent) { + exists(int covered, int total | + similarLinesCovered(f, covered, other) and + total = f.getMetrics().getNumberOfLines() and + covered * 100 / total = percent and + percent > 80 + ) and + not duplicateFiles(f, other, _) +} + +predicate duplicateFiles(File f, File other, int percent) { + exists(int covered, int total | + duplicateLinesCovered(f, covered, other) and + total = f.getMetrics().getNumberOfLines() and + covered * 100 / total = percent and + percent > 70 + ) +} + +predicate mostlyDuplicateClassBase(Class c, Class other, int numDup, int total) { + numDup = strictcount(FunctionDeclarationEntry m1 | + exists(FunctionDeclarationEntry m2 | + duplicateMethod(m1, m2) and + m1 = sourceMethod() and + exists(Function f | f = m1.getFunction() and f.getDeclaringType() = c) and + exists(Function f | f = m2.getFunction() and f.getDeclaringType() = other) and + c != other + ) + ) and + total = numberOfSourceMethods(c) and + ((numDup * 100) / total > 80) +} + +predicate mostlyDuplicateClass(Class c, Class other, string message) { + exists(int numDup, int total | + mostlyDuplicateClassBase(c,other,numDup,total) and + ( + (total != numDup + and exists(string s1, string s2, string s3, string name | s1 = " out of " and s2 = " methods in " and s3 = " are duplicated in $@." and name = c.getName() | + message = numDup + s1 + total + s2 + name + s3 + ) + ) + or + (total = numDup + and exists(string s1, string s2, string name | s1 = "All methods in " and s2 = " are identical in $@." and name = c.getName() | + message = s1 + name + s2 + ) + ) + ) + ) +} + +predicate fileLevelDuplication(File f, File other) { + similarFiles(f, other, _) or duplicateFiles(f, other, _) +} + +predicate classLevelDuplication(Class c, Class other) { + mostlyDuplicateClass(c, other, _) +} + +predicate whitelistedLineForDuplication(File f, int line) { + exists(Include i | i.getFile() = f and i.getLocation().getStartLine() = line) +} + diff --git a/cpp/ql/src/external/DefectFilter.qll b/cpp/ql/src/external/DefectFilter.qll new file mode 100644 index 000000000000..54182636a0ca --- /dev/null +++ b/cpp/ql/src/external/DefectFilter.qll @@ -0,0 +1,27 @@ +import cpp + +external predicate defectResults(int id, string queryPath, string file, int startline, int startcol, int endline, int endcol, string message); + +class DefectResult extends int { + + DefectResult() { defectResults(this, _, _, _, _, _, _, _) } + + string getQueryPath() { defectResults(this, result, _, _, _, _, _, _) } + + File getFile() { exists(string path | defectResults(this, _, path, _, _, _, _, _) and result.getAbsolutePath() = path) } + + int getStartLine() { defectResults(this, _, _, result, _, _, _, _) } + + int getStartColumn() { defectResults(this, _, _, _, result, _, _, _) } + + int getEndLine() { defectResults(this, _, _, _, _, result, _, _) } + + int getEndColumn() { defectResults(this, _, _, _, _, _, result, _) } + + string getMessage() { defectResults(this, _, _, _, _, _, _, result) } + + string getURL() { + result = "file://" + getFile().getAbsolutePath() + ":" + getStartLine() + ":" + getStartColumn() + ":" + getEndLine() + ":" + getEndColumn() + } + +} diff --git a/cpp/ql/src/external/DuplicateBlock.ql b/cpp/ql/src/external/DuplicateBlock.ql new file mode 100644 index 000000000000..a4d418315432 --- /dev/null +++ b/cpp/ql/src/external/DuplicateBlock.ql @@ -0,0 +1,19 @@ +/** + * @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 + * @id cpp/duplicate-block + * @problem.severity recommendation + */ +import CodeDuplication + +from DuplicateBlock d, DuplicateBlock other, int lines, File otherFile, int otherLine +where lines = d.sourceLines() and + lines > 10 and + other.getEquivalenceClass() = d.getEquivalenceClass() and + other != d and + otherFile = other.sourceFile() and + otherLine = other.sourceStartLine() +select + d, + "Duplicate code: " + lines + " lines are duplicated at " + otherFile.getBaseName() + ":" + otherLine diff --git a/cpp/ql/src/external/DuplicateFunction.qhelp b/cpp/ql/src/external/DuplicateFunction.qhelp new file mode 100644 index 000000000000..8e81c11e5e67 --- /dev/null +++ b/cpp/ql/src/external/DuplicateFunction.qhelp @@ -0,0 +1,43 @@ + + + + + +

    A function should never be duplicated verbatim in several places in the code. Of course +the severity of this anti-pattern is higher for longer functions than for extremely short +functions of one or two statements, but there are usually better ways of achieving the same +effect.

    + +
    + +

    Code duplication in general is highly undesirable for a range of reasons: The artificially +inflated amount of code hinders comprehension, and ranges of similar but subtly different lines +can mask the real purpose or intention behind a function. There is also a risk of +update anomalies, where only one of several copies of the code is updated to address a defect or +add a feature.

    + +

    In the case of function duplication, how to address the issue depends on the functions themselves +and on the precise classes in which the duplication occurs. At its simplest, the duplication can +be addressed by removing all but one of the duplicate function definitions and making +callers of the removed functions refer to the (now canonical) single remaining definition +instead.

    + +

    This may not be possible for reasons of visibility or accessibility. A common example might +be where two classes implement the same functionality but neither is a subtype of the other, +so it is not possible to inherit a single function definition. In such cases, introducing a +common superclass to share the duplicated code is a viable option. Alternatively, if the functions +don't need access to private object state, they can be moved to a shared utility class that +just provides the functionality itself.

    + +
    + + +
  • Elmar Juergens, Florian Deissenboeck, Benjamin Hummel, and Stefan Wagner. 2009. +Do code clones matter? In Proceedings of the 31st International Conference on +Software Engineering (ICSE '09). IEEE Computer Society, Washington, DC, USA, +485-495.
  • + +
    +
    diff --git a/cpp/ql/src/external/DuplicateFunction.ql b/cpp/ql/src/external/DuplicateFunction.ql new file mode 100644 index 000000000000..ceaac8d3cf6d --- /dev/null +++ b/cpp/ql/src/external/DuplicateFunction.ql @@ -0,0 +1,36 @@ +/** + * @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 + * @id cpp/duplicate-function + * @problem.severity recommendation + * @precision medium + * @tags testability + * maintainability + * duplicate-code + * non-attributable + */ +import cpp +import CodeDuplication + +predicate relevant(FunctionDeclarationEntry m) { + exists(Location loc | + loc = m.getBlock().getLocation() and + ( + loc.getStartLine() + 5 < loc.getEndLine() and not m.getName().matches("get%") + or + loc.getStartLine() + 10 < loc.getEndLine() + ) + ) +} + +from FunctionDeclarationEntry m, FunctionDeclarationEntry other +where duplicateMethod(m, other) + and relevant(m) + and not m.getFunction().isConstructedFrom(_) + and not other.getFunction().isConstructedFrom(_) + and not fileLevelDuplication(m.getFile(), other.getFile()) + and not classLevelDuplication(m.getFunction().getDeclaringType(), other.getFunction().getDeclaringType()) +select m, "Function " + m.getName() + " is duplicated at $@.", + other, + other.getFile().getBaseName() + ":" + other.getLocation().getStartLine().toString() diff --git a/cpp/ql/src/external/ExternalArtifact.qll b/cpp/ql/src/external/ExternalArtifact.qll new file mode 100644 index 000000000000..be6730948986 --- /dev/null +++ b/cpp/ql/src/external/ExternalArtifact.qll @@ -0,0 +1,44 @@ +import cpp + +class ExternalData extends @externalDataElement { + + string getDataPath() { externalData(this, result, _, _) } + + string getQueryPath() { result = getDataPath().regexpReplaceAll("\\.[^.]*$", ".ql") } + + int getNumFields() { result = 1 + max(int i | externalData(this, _, i, _) | i) } + + string getField(int index) { externalData(this, _, index, result) } + + int getFieldAsInt(int index) { result = getField(index).toInt() } + + float getFieldAsFloat(int index) { result = getField(index).toFloat() } + + date getFieldAsDate(int index) { result = getField(index).toDate() } + + string toString() { + result = getQueryPath() + ": " + buildTupleString(0) + } + + private string buildTupleString(int start) { + (start = getNumFields() - 1 and result = getField(start)) + or + (start < getNumFields() - 1 and result = getField(start) + "," + buildTupleString(start+1)) + } + +} + +/** + * External data with a location, and a message, as produced by tools that used to produce QLDs. + */ +class DefectExternalData extends ExternalData { + DefectExternalData() { + this.getField(0).regexpMatch("\\w+://.*:[0-9]+:[0-9]+:[0-9]+:[0-9]+$") and + this.getNumFields() = 2 + } + + string getURL() { result = getField(0) } + + string getMessage() { result = getField(1) } +} + diff --git a/cpp/ql/src/external/MetricFilter.qll b/cpp/ql/src/external/MetricFilter.qll new file mode 100644 index 000000000000..68cd2eba962e --- /dev/null +++ b/cpp/ql/src/external/MetricFilter.qll @@ -0,0 +1,37 @@ +import cpp + +external predicate metricResults(int id, string queryPath, string file, int startline, int startcol, int endline, int endcol, float value); + +class MetricResult extends int { + + MetricResult() { metricResults(this, _, _, _, _, _, _, _) } + + string getQueryPath() { metricResults(this, result, _, _, _, _, _, _) } + + File getFile() { exists(string path | metricResults(this, _, path, _, _, _, _, _) and result.getAbsolutePath() = path) } + + int getStartLine() { metricResults(this, _, _, result, _, _, _, _) } + + int getStartColumn() { metricResults(this, _, _, _, result, _, _, _) } + + int getEndLine() { metricResults(this, _, _, _, _, result, _, _) } + + int getEndColumn() { metricResults(this, _, _, _, _, _, result, _) } + + predicate hasMatchingLocation() { exists(this.getMatchingLocation()) } + + Location getMatchingLocation() { + result.getFile() = this.getFile() and + result.getStartLine() = this.getStartLine() and + result.getEndLine() = this.getEndLine() and + result.getStartColumn() = this.getStartColumn() and + result.getEndColumn() = this.getEndColumn() + } + + float getValue() { metricResults(this, _, _, _, _, _, _, result) } + + string getURL() { + result = "file://" + getFile().getAbsolutePath() + ":" + getStartLine() + ":" + getStartColumn() + ":" + getEndLine() + ":" + getEndColumn() + } + +} diff --git a/cpp/ql/src/external/MostlyDuplicateClass.qhelp b/cpp/ql/src/external/MostlyDuplicateClass.qhelp new file mode 100644 index 000000000000..113eb6bf243f --- /dev/null +++ b/cpp/ql/src/external/MostlyDuplicateClass.qhelp @@ -0,0 +1,45 @@ + + + + + +

    In cases where several methods are duplicated between two (or more) classes, +the classes themselves are highlighted as "mostly duplicate", rather than creating a large +number of method-level warnings. The same caveats apply here, too.

    + +
    + +

    Code duplication in general is highly undesirable for a range of reasons: The artificially +inflated amount of code hinders comprehension, and ranges of similar but subtly different lines +can mask the real purpose or intention behind a method. There's also an omnipresent risk of +update anomalies, where only one of several copies of the code is updated to address a defect or +add a feature.

    + +

    While completely duplicated classes are rare, they are usually a sign of a simple +oversight (or deliberate copy/paste) on a developer's part. Usually the required remedy +action is to remove all but one of them.

    + +

    It is far more common to see duplication of many methods between two classes, leaving just +a few that are actually different. Such situations warrant close inspection. Are the differences +deliberate or a result of an inconsistent update to one of the clones? If the latter, then +treating the classes as completely duplicate and eliminating all but one (while preserving any +corrections or new features that may have been introduced) is the best course. If two classes serve +genuinely different purposes but almost all of their methods are the same, that can be a sign +that there is a missing level of abstraction. Introducing a common superclass to define the +common methods and sharing the code is likely to prevent many problems in the long term.

    + +

    Modern IDEs may provide refactoring support for this sort of transformation, usually +under the names of "Pull up" or "Extract supertype".

    + +
    + + +
  • Elmar Juergens, Florian Deissenboeck, Benjamin Hummel, and Stefan Wagner. 2009. +Do code clones matter? In Proceedings of the 31st International Conference on +Software Engineering (ICSE '09). IEEE Computer Society, Washington, DC, USA, +485-495.
  • + +
    +
    diff --git a/cpp/ql/src/external/MostlyDuplicateClass.ql b/cpp/ql/src/external/MostlyDuplicateClass.ql new file mode 100644 index 000000000000..a1420f3e6050 --- /dev/null +++ b/cpp/ql/src/external/MostlyDuplicateClass.ql @@ -0,0 +1,21 @@ +/** + * @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 + * @id cpp/duplicate-class + * @problem.severity recommendation + * @precision medium + * @tags testability + * maintainability + * duplicate-code + * non-attributable + */ +import cpp +import CodeDuplication + +from Class c, Class other, string message +where mostlyDuplicateClass(c, other, message) + and not c.isConstructedFrom(_) + and not other.isConstructedFrom(_) + and not fileLevelDuplication(c.getFile(), _) +select c, message, other, other.getQualifiedName() diff --git a/cpp/ql/src/external/MostlyDuplicateFile.qhelp b/cpp/ql/src/external/MostlyDuplicateFile.qhelp new file mode 100644 index 000000000000..b3b319f346b9 --- /dev/null +++ b/cpp/ql/src/external/MostlyDuplicateFile.qhelp @@ -0,0 +1,46 @@ + + + + + +

    In cases where most of a file's lines have been duplicated in one or more other +files, the files themselves are highlighted as "mostly duplicate", rather than +creating a large number of method-level or class-level warnings. The same caveats +apply here, too.

    + +
    + +

    Code duplication in general is highly undesirable for a range of reasons: The artificially +inflated amount of code hinders comprehension, and ranges of similar but subtly different lines +can mask the real purpose or intention behind a method. There's also an omnipresent risk of +update anomalies, where only one of several copies of the code is updated to address a defect or +add a feature.

    + +

    While completely duplicated files are rare, they are usually a sign of a simple +oversight (or deliberate copy/paste) on a developer's part. Usually the required remedy +action is to remove all but one of them. A common exception may arise from generated code +that simply occurs in several places in the source tree; the check can be adapted to +exclude such results.

    + +

    It is far more common to see duplication of many lines between two files, leaving just +a few that are actually different. Such situations warrant close inspection. Are the differences +deliberate or a result of an inconsistent update to one of the clones? If the latter, then +treating the files as completely duplicate and eliminating all but one (while preserving any +corrections or new features that may have been introduced) is the best course. If two files serve +genuinely different purposes but almost all of their lines are the same, that can be a sign +that there is a missing level of abstraction. Look for ways to share the functionality, either +by extracting a utility class for parts of it or by encapsulating the common parts into a new +super class of any classes involved.

    + +
    + + +
  • Elmar Juergens, Florian Deissenboeck, Benjamin Hummel, and Stefan Wagner. 2009. +Do code clones matter? In Proceedings of the 31st International Conference on +Software Engineering (ICSE '09). IEEE Computer Society, Washington, DC, USA, +485-495.
  • + +
    +
    diff --git a/cpp/ql/src/external/MostlyDuplicateFile.ql b/cpp/ql/src/external/MostlyDuplicateFile.ql new file mode 100644 index 000000000000..55016a88e912 --- /dev/null +++ b/cpp/ql/src/external/MostlyDuplicateFile.ql @@ -0,0 +1,18 @@ +/** + * @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 + * @id cpp/duplicate-file + * @problem.severity recommendation + * @precision medium + * @tags testability + * maintainability + * duplicate-code + * non-attributable + */ +import cpp +import CodeDuplication + +from File f, File other, int percent +where duplicateFiles(f, other, percent) +select f, percent + "% of the lines in " + f.getBaseName() + " are copies of lines in $@.", other, other.getBaseName() diff --git a/cpp/ql/src/external/MostlyDuplicateFunction.qhelp b/cpp/ql/src/external/MostlyDuplicateFunction.qhelp new file mode 100644 index 000000000000..a981eef6e87b --- /dev/null +++ b/cpp/ql/src/external/MostlyDuplicateFunction.qhelp @@ -0,0 +1,55 @@ + + + + +

    A "mostly duplicate function" is a function for which there is at least one +almost exact duplicate somewhere else in the code, but for which there are no +exact duplicates. There will be minor typographical differences between this +function and any "mostly duplicate function" to which it corresponds (for +example, comments and small code changes), preventing an exact match. Pairs of +such functions are sometimes referred to as "similar".

    + +

    This class of problem can often be more insidious than mere duplication, because the two +implementations have diverged. This may be on purpose (when a function is copy-and-pasted +and adapted to a new context) or accidentally (when a correction is only introduced in one of +several identical pieces of code), and to address the problem one needs to understand which +of the two situations applies.

    + +
    + +

    Code duplication in general is highly undesirable for a range of reasons: The artificially +inflated amount of code hinders comprehension, and ranges of similar but subtly different lines +can mask the real purpose or intention behind a function. There's also a risk of +update anomalies, where only one of several copies of the code is updated to address a defect or +add a feature.

    + +

    In the case of function similarity, how to address the issue depends on the functions +themselves and on the precise classes in which they occur. At its simplest, if the differences +are accidental, the problem can be addressed by unifying the functions to behave identically. +Then, we can remove all but one of the duplicate function definitions and make +callers of the removed functions refer to the (now canonical) single remaining definition +instead.

    + +

    In more complex cases, look for ways of encapsulating the commonality and sharing it while +retaining the differences in functionality. Perhaps the function can be moved to a single place +and given an additional parameter, allowing it to cover all use cases? Alternatively, there +may be a common preprocessing or postprocessing step which can be extracted to its own (shared) +function, leaving only the specific parts in the existing functions.

    + +

    Modern IDEs may provide refactoring support for this sort of transformation. Relevant +refactorings might be "Extract function", "Change function signature", "Pull up" or "Extract +supertype".

    + +
    + + +
  • Elmar Juergens, Florian Deissenboeck, Benjamin Hummel, and Stefan Wagner. 2009. +Do code clones matter? In Proceedings of the 31st International Conference on +Software Engineering (ICSE '09). IEEE Computer Society, Washington, DC, USA, +485-495.
  • + + +
    +
    diff --git a/cpp/ql/src/external/MostlyDuplicateFunction.ql b/cpp/ql/src/external/MostlyDuplicateFunction.ql new file mode 100644 index 000000000000..f991b0692ed5 --- /dev/null +++ b/cpp/ql/src/external/MostlyDuplicateFunction.ql @@ -0,0 +1,27 @@ +/** + * @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 + * @id cpp/mostly-duplicate-function + * @problem.severity recommendation + * @precision medium + * @tags testability + * duplicate-code + * non-attributable + */ +import cpp +import CodeDuplication + +from FunctionDeclarationEntry m, int covered, int total, FunctionDeclarationEntry other, int percent +where + duplicateStatements(m, other, covered, total) and + covered != total and + total > 5 and + covered * 100 / total = percent and + percent > 80 and + not m.getFunction().isConstructedFrom(_) and + not other.getFunction().isConstructedFrom(_) and + not duplicateMethod(m, other) and + not classLevelDuplication(m.getFunction().getDeclaringType(), other.getFunction().getDeclaringType()) and + not fileLevelDuplication(m.getFile(), other.getFile()) +select m, percent + "% of the statements in " + m.getName() + " are duplicated in $@.", other, other.getFunction().getQualifiedName() diff --git a/cpp/ql/src/external/MostlySimilarFile.qhelp b/cpp/ql/src/external/MostlySimilarFile.qhelp new file mode 100644 index 000000000000..e1a41a16321f --- /dev/null +++ b/cpp/ql/src/external/MostlySimilarFile.qhelp @@ -0,0 +1,39 @@ + + + + + +

    Two lines are defined as similar if they are either identical or contain only very minor +differences. In cases where most of the lines in a file have corresponding "similar" lines in another file, +the files themselves are highlighted as "mostly similar", instead of creating a large number of method-level or class-level +warnings. The same caveats apply here, too.

    + +
    + +

    Code duplication in general is highly undesirable for a range of reasons: The artificially +inflated amount of code hinders comprehension, and ranges of similar but subtly different lines +can mask the real purpose or intention behind a method. There's also an omnipresent risk of +update anomalies, where only one of several copies of the code is updated to address a defect or +add a feature.

    + +

    With files that are marked as mostly similar, special care should be taken. Why are +they almost the same, and why are there differences? If the differences are accidental (for +example from corrections that were only applied to one copy), then unifying the files and removing +all but one is the best thing to do. If the files have genuinely different tasks, it is worth +thinking about the reasons for the similarity. Can some of the shared code be extracted into +methods (perhaps with additional parameters, to cover the differences in behavior)? Should it +be moved into a utility class or file that is accessible to all current implementations, or +should a new level of abstraction be introduced?

    + +
    + + +
  • Elmar Juergens, Florian Deissenboeck, Benjamin Hummel, and Stefan Wagner. 2009. +Do code clones matter? In Proceedings of the 31st International Conference on +Software Engineering (ICSE '09). IEEE Computer Society, Washington, DC, USA, +485-495.
  • + +
    +
    diff --git a/cpp/ql/src/external/MostlySimilarFile.ql b/cpp/ql/src/external/MostlySimilarFile.ql new file mode 100644 index 000000000000..64338c71d5d5 --- /dev/null +++ b/cpp/ql/src/external/MostlySimilarFile.ql @@ -0,0 +1,18 @@ +/** + * @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 + * @id cpp/similar-file + * @problem.severity recommendation + * @precision medium + * @tags testability + * maintainability + * duplicate-code + * non-attributable + */ +import cpp +import CodeDuplication + +from File f, File other, int percent +where similarFiles(f, other, percent) +select f, percent + "% of the lines in " + f.getBaseName() + " are similar to lines in $@.", other, other.getBaseName() diff --git a/cpp/ql/src/external/VCS.qll b/cpp/ql/src/external/VCS.qll new file mode 100644 index 000000000000..cae2379cab51 --- /dev/null +++ b/cpp/ql/src/external/VCS.qll @@ -0,0 +1,104 @@ +import cpp + +class Commit extends @svnentry { + + Commit() { + svnaffectedfiles(this, _, _) and + exists(date svnDate, date snapshotDate | + svnentries(this, _, _, svnDate, _) and + snapshotDate(snapshotDate) and + svnDate <= snapshotDate + ) + } + + string toString() { result = this.getRevisionName() } + + string getRevisionName() { svnentries(this, result, _, _, _) } + + string getAuthor() { svnentries(this, _, result, _, _) } + + date getDate() { svnentries(this, _, _, result, _) } + + int getChangeSize() { svnentries(this, _, _, _, result) } + + string getMessage() { svnentrymsg(this, result) } + + string getAnAffectedFilePath(string action) { + exists(File rawFile | svnaffectedfiles(this, rawFile, action) | + result = rawFile.getAbsolutePath() + ) + } + + string getAnAffectedFilePath() { result = getAnAffectedFilePath(_) } + + File getAnAffectedFile(string action) { + // Workaround for incorrect keys in SVN data + exists(File svnFile | svnFile.getAbsolutePath() = result.getAbsolutePath() | + svnaffectedfiles(this,svnFile,action) + ) + and exists(result.getMetrics().getNumberOfLinesOfCode()) + } + + File getAnAffectedFile() { exists(string action | result = this.getAnAffectedFile(action)) } + + private predicate churnForFile(File f, int added, int deleted) { + // Workaround for incorrect keys in SVN data + exists(File svnFile | svnFile.getAbsolutePath() = f.getAbsolutePath() | + svnchurn(this,svnFile,added,deleted) + ) + and exists(f.getMetrics().getNumberOfLinesOfCode()) + } + + int getRecentChurnForFile(File f) { + exists (int added, int deleted | churnForFile(f, added, deleted) and result = added + deleted) + } + + int getRecentAdditionsForFile(File f) { + churnForFile(f, result, _) + } + + int getRecentDeletionsForFile(File f) { + churnForFile(f, _, result) + } + + predicate isRecent() { recentCommit(this) } + + int daysToNow() { + exists(date now | snapshotDate(now) | + result = getDate().daysTo(now) and result >= 0 + ) + } + +} + +class Author extends string { + Author() { exists(Commit e | this = e.getAuthor()) } + + Commit getACommit() { result.getAuthor() = this } + + File getAnEditedFile() { result = this.getACommit().getAnAffectedFile() } + +} + +predicate recentCommit(Commit e) { + exists(date snapshotDate, date commitDate, int days | + snapshotDate(snapshotDate) and + e.getDate() = commitDate and + days = commitDate.daysTo(snapshotDate) and + days >= 0 and days <= 60 + ) +} + +date firstChange(File f) { + result = min(Commit e, date toMin | (f = e.getAnAffectedFile()) and (toMin = e.getDate()) | toMin) +} + +predicate firstCommit(Commit e) { + not exists(File f | f = e.getAnAffectedFile() | + firstChange(f) < e.getDate() + ) +} + +predicate artificialChange(Commit e) { + firstCommit(e) or e.getChangeSize() >= 50000 +} diff --git a/cpp/ql/src/external/examples/filters/BumpMetricBy10.ql b/cpp/ql/src/external/examples/filters/BumpMetricBy10.ql new file mode 100644 index 000000000000..b0939153a323 --- /dev/null +++ b/cpp/ql/src/external/examples/filters/BumpMetricBy10.ql @@ -0,0 +1,12 @@ +/** + * @name Metric filter: increase the value of a metric by 10 + * @description This filter demonstrates how to change the value + * computed by the metric that it is filtering. In this + * example the value is increased by 10. + */ +import cpp +import external.MetricFilter + +from MetricResult res +select res, + res.getValue() + 10 diff --git a/cpp/ql/src/external/examples/filters/EditDefectMessage.ql b/cpp/ql/src/external/examples/filters/EditDefectMessage.ql new file mode 100644 index 000000000000..01c10f6443d9 --- /dev/null +++ b/cpp/ql/src/external/examples/filters/EditDefectMessage.ql @@ -0,0 +1,13 @@ +/** + * @name Filter: alter the message generated by a query + * @description This filter demonstrates how to edit the message + * generated by the query that it is filtering. In this + * example the string `Filtered query result: ` is + * prepended to the message. + */ +import cpp +import external.DefectFilter + +from DefectResult res +select res, + "Filtered query result: " + res.getMessage() diff --git a/cpp/ql/src/external/examples/filters/ExcludeGeneratedCode.ql b/cpp/ql/src/external/examples/filters/ExcludeGeneratedCode.ql new file mode 100644 index 000000000000..4a12003f8090 --- /dev/null +++ b/cpp/ql/src/external/examples/filters/ExcludeGeneratedCode.ql @@ -0,0 +1,18 @@ +/** + * @name Filter: exclude results from generated code + * @description This filter demonstrates how to return results only if + * they meet certain criteria. In this example, results are + * only returned if they do not come from a file which + * contains 'generated' anywhere in its path. + */ +import cpp +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/cpp/ql/src/external/tests/DefectFilter.ql b/cpp/ql/src/external/tests/DefectFilter.ql new file mode 100644 index 000000000000..7c9459f64ced --- /dev/null +++ b/cpp/ql/src/external/tests/DefectFilter.ql @@ -0,0 +1,11 @@ +/** + * @name Defect filter + * @description Only include results in large files (200) lines of code, and change the message. + */ +import cpp +import external.DefectFilter +import external.VCS + +from DefectResult res +where res.getFile().getMetrics().getNumberOfLinesOfCode() > 200 +select res, "Large files: " + res.getMessage() diff --git a/cpp/ql/src/external/tests/DefectFromExternalData.ql b/cpp/ql/src/external/tests/DefectFromExternalData.ql new file mode 100644 index 000000000000..1164d4498d93 --- /dev/null +++ b/cpp/ql/src/external/tests/DefectFromExternalData.ql @@ -0,0 +1,15 @@ +/** + * @name Defect from external data + * @description Insert description here... + * @kind problem + * @problem.severity warning + */ + +import cpp + +import external.ExternalArtifact + +from ExternalData d, File u +where d.getQueryPath() = "external-data.ql" + and u.getShortName() = 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/cpp/ql/src/external/tests/DefectFromSVN.ql b/cpp/ql/src/external/tests/DefectFromSVN.ql new file mode 100644 index 000000000000..f58a6482ebc8 --- /dev/null +++ b/cpp/ql/src/external/tests/DefectFromSVN.ql @@ -0,0 +1,26 @@ +/** + * @name Defect from SVN + * @description A test case for creating a defect from SVN data. + * @kind problem + * @problem.severity warning + */ + +import cpp + +import external.ExternalArtifact +import external.VCS + +predicate numCommits(File f, int i) { + i = count(Commit e | e.getAnAffectedFile() = f) +} + +predicate maxCommits(int i) { + i = max(File f, int j | numCommits(f, j) | j) +} + +from File f, int i +where numCommits(f,i) and maxCommits(i) +select f, "This file has " + i + " commits." + + + diff --git a/cpp/ql/src/external/tests/MetricFilter.ql b/cpp/ql/src/external/tests/MetricFilter.ql new file mode 100644 index 000000000000..99dbd16f0ecb --- /dev/null +++ b/cpp/ql/src/external/tests/MetricFilter.ql @@ -0,0 +1,10 @@ +/** + * @name Metric filter + * @description Only include results in large files (200) lines of code. + */ +import cpp +import external.MetricFilter + +from MetricResult res +where res.getFile().getMetrics().getNumberOfLinesOfCode() > 200 +select res, res.getValue() diff --git a/cpp/ql/src/external/tests/MetricFromSVN.ql b/cpp/ql/src/external/tests/MetricFromSVN.ql new file mode 100644 index 000000000000..32d27c7deafc --- /dev/null +++ b/cpp/ql/src/external/tests/MetricFromSVN.ql @@ -0,0 +1,18 @@ +/** + * @name Metric from SVN + * @description Find number of commits for a file + * @treemap.warnOn lowValues + * @metricType file + */ + +import cpp + +import external.VCS + +predicate numCommits(File f, int i) { + i = count(Commit e | e.getAnAffectedFile() = f) +} + +from File f, int i +where numCommits(f, i) +select f, i diff --git a/cpp/ql/src/filters/ClassifyFiles.ql b/cpp/ql/src/filters/ClassifyFiles.ql new file mode 100644 index 000000000000..8f486e63fa7b --- /dev/null +++ b/cpp/ql/src/filters/ClassifyFiles.ql @@ -0,0 +1,25 @@ +/** + * @name Classify files + * @description This query produces a list of all files in a snapshot + * that are classified as generated code or test code. + * @kind file-classifier + * @id cpp/file-classifier + */ + +import cpp +import semmle.code.cpp.AutogeneratedFile +import semmle.code.cpp.TestFile + +predicate classify(File f, string tag) { + ( + f instanceof AutogeneratedFile and + tag = "generated" + ) or ( + f instanceof TestFile and + tag = "test" + ) +} + +from File f, string tag +where classify(f, tag) +select f, tag diff --git a/cpp/ql/src/filters/FilterAutogenerated.ql b/cpp/ql/src/filters/FilterAutogenerated.ql new file mode 100644 index 000000000000..a6577aa7c115 --- /dev/null +++ b/cpp/ql/src/filters/FilterAutogenerated.ql @@ -0,0 +1,16 @@ +/** + * @name Filter: exclude results from files that are autogenerated + * @description Use this filter to return results only if they are + * located in files that are maintained manually. + * @kind problem + * @id cpp/autogenerated-filter + */ + +import cpp +import semmle.code.cpp.AutogeneratedFile +import external.DefectFilter + +from DefectResult res +where not res.getFile() instanceof AutogeneratedFile +select res, + res.getMessage() diff --git a/cpp/ql/src/filters/FilterAutogeneratedForMetric.ql b/cpp/ql/src/filters/FilterAutogeneratedForMetric.ql new file mode 100644 index 000000000000..2b0ff58e0655 --- /dev/null +++ b/cpp/ql/src/filters/FilterAutogeneratedForMetric.ql @@ -0,0 +1,16 @@ +/** + * @name Metric filter: exclude results from files that are autogenerated + * @description Use this filter to return results only if they are + * located in files that are maintained manually. + * @kind treemap + * @id cpp/autogenerated-for-metric-filter + */ + +import cpp +import semmle.code.cpp.AutogeneratedFile +import external.MetricFilter + +from MetricResult res +where not res.getFile() instanceof AutogeneratedFile +select res, + res.getValue() diff --git a/cpp/ql/src/filters/FromSource.ql b/cpp/ql/src/filters/FromSource.ql new file mode 100644 index 000000000000..6323d3c9ec40 --- /dev/null +++ b/cpp/ql/src/filters/FromSource.ql @@ -0,0 +1,16 @@ +/** + * @name Filter: exclude results from files for which we do not have + * source code + * @description Use this filter to return results only if they are + * located in files for which we have source code. + * @kind problem + * @id cpp/from-source-filter + */ + +import cpp +import external.DefectFilter + +from DefectResult res +where res.getFile().fromSource() +select res, + res.getMessage() diff --git a/cpp/ql/src/filters/ImportAdditionalLibraries.ql b/cpp/ql/src/filters/ImportAdditionalLibraries.ql new file mode 100644 index 000000000000..1d54da5f6e75 --- /dev/null +++ b/cpp/ql/src/filters/ImportAdditionalLibraries.ql @@ -0,0 +1,20 @@ +/** + * @name (Import additional libraries) + * @description This query produces no results but imports some libraries we + * would like to make available in the LGTM query console even + * if they are not used by any queries. + * @kind file-classifier + * @id cpp/lgtm/import-additional-libraries + */ + +import cpp + +import semmle.code.cpp.dataflow.DataFlow +import semmle.code.cpp.dataflow.DataFlow2 +import semmle.code.cpp.dataflow.DataFlow3 +import semmle.code.cpp.dataflow.DataFlow4 +import semmle.code.cpp.dataflow.TaintTracking + +from File f, string tag +where none() +select f, tag diff --git a/cpp/ql/src/filters/Macros.ql b/cpp/ql/src/filters/Macros.ql new file mode 100644 index 000000000000..567461fb9117 --- /dev/null +++ b/cpp/ql/src/filters/Macros.ql @@ -0,0 +1,35 @@ +/** + * @name Filter: exclude results on lines covered by a macro expansion + * @description Use this filter to return results only when there is no + * macro expansion whose location spans all the lines of + * the result's location. + * @kind problem + * @id cpp/macros-filter + */ +import cpp +import external.DefectFilter + +predicate macroLocation(File f, int startLine, int endLine) { + exists(MacroInvocation mi, Location l | + l = mi.getLocation() and + l.getFile() = f and + l.getStartLine() = startLine and + l.getEndLine() = endLine + ) +} + +predicate macroCovering(DefectResult r) { + exists(File f, int macroStart, int macroEnd, int defectStart, int defectEnd | + f = r.getFile() and + defectStart = r.getStartLine() and + defectEnd = r.getEndLine() and + macroLocation(f, macroStart, macroEnd) and + macroStart <= defectStart and + macroEnd >= defectEnd + ) +} + +from DefectResult res +where not macroCovering(res) +select res, + res.getMessage() diff --git a/cpp/ql/src/filters/RecentDefects.ql b/cpp/ql/src/filters/RecentDefects.ql new file mode 100644 index 000000000000..cf904d602ad1 --- /dev/null +++ b/cpp/ql/src/filters/RecentDefects.ql @@ -0,0 +1,23 @@ +/** + * @name Filter: exclude results from files that have not recently been + * edited + * @description Use this filter to return results only if they are + * located in files that have been modified in the 60 days + * before the date of the snapshot. + * @kind problem + * @id cpp/recent-defects-filter + */ +import cpp +import external.DefectFilter +import external.VCS + +private pragma[noopt] +predicate recent(File file) { + exists(Commit e | file = e.getAnAffectedFile() | + e.isRecent() and not artificialChange(e) + ) +} + +from DefectResult res +where recent(res.getFile()) +select res, res.getMessage() diff --git a/cpp/ql/src/filters/RecentDefectsForMetric.ql b/cpp/ql/src/filters/RecentDefectsForMetric.ql new file mode 100644 index 000000000000..295155383880 --- /dev/null +++ b/cpp/ql/src/filters/RecentDefectsForMetric.ql @@ -0,0 +1,23 @@ +/** + * @name Metric filter: exclude results from files that have not + * recently been edited + * @description Use this filter to return results only if they are + * located in files that have been modified in the 60 days + * before the snapshot. + * @kind treemap + * @id cpp/recent-defects-for-metric-filter + */ +import cpp +import external.MetricFilter +import external.VCS + +private pragma[noopt] +predicate recent(File file) { + exists(Commit e | file = e.getAnAffectedFile() | + e.isRecent() and not artificialChange(e) + ) +} + +from MetricResult res +where recent(res.getFile()) +select res, res.getValue() diff --git a/cpp/ql/src/jsf/3.02 Code Size and Complexity/AV Rule 1.ql b/cpp/ql/src/jsf/3.02 Code Size and Complexity/AV Rule 1.ql new file mode 100644 index 000000000000..d0ce8aff05b3 --- /dev/null +++ b/cpp/ql/src/jsf/3.02 Code Size and Complexity/AV Rule 1.ql @@ -0,0 +1,12 @@ +/** + * @name AV Rule 1 + * @description Any one function (or method) will contain no more than 200 logical source lines of code. + * @kind problem + * @id cpp/jsf/av-rule-1 + * @problem.severity warning + */ +import cpp + +from Function f +where f.getMetrics().getNumberOfLinesOfCode() > 200 +select f, "AV Rule 1: any one function (or method) will contain no more than 200 logical source lines of code." \ No newline at end of file diff --git a/cpp/ql/src/jsf/3.02 Code Size and Complexity/AV Rule 2.ql b/cpp/ql/src/jsf/3.02 Code Size and Complexity/AV Rule 2.ql new file mode 100644 index 000000000000..81441a548fc3 --- /dev/null +++ b/cpp/ql/src/jsf/3.02 Code Size and Complexity/AV Rule 2.ql @@ -0,0 +1,25 @@ +/** + * @name AV Rule 2 + * @description There shall not be any self-modifying code. + * @kind problem + * @id cpp/jsf/av-rule-2 + * @problem.severity error + */ +import cpp + +/* We look for code that converts between function pointers and non-function, non-void + pointers. This will obviously not catch code that uses inline assembly to achieve + self-modification, nor will it spot the use of OS mechanisms to write into process + memory (such as WriteProcessMemory under Windows). */ +predicate maybeSMCConversion(Type t1, Type t2) { + t1 instanceof FunctionPointerType and + t2 instanceof PointerType and + not t2 instanceof FunctionPointerType and + not t2 instanceof VoidPointerType + or maybeSMCConversion(t2, t1) +} + +from Expr e +where e.fromSource() and + maybeSMCConversion(e.getUnderlyingType(), e.getActualType()) +select e, "AV Rule 2: There shall not be any self-modifying code." \ No newline at end of file diff --git a/cpp/ql/src/jsf/3.02 Code Size and Complexity/AV Rule 3.ql b/cpp/ql/src/jsf/3.02 Code Size and Complexity/AV Rule 3.ql new file mode 100644 index 000000000000..41aea8836d79 --- /dev/null +++ b/cpp/ql/src/jsf/3.02 Code Size and Complexity/AV Rule 3.ql @@ -0,0 +1,14 @@ +/** + * @name AV Rule 3 + * @description All functions shall have a cyclomatic complexity number of 20 or less. + * @kind problem + * @id cpp/jsf/av-rule-3 + * @problem.severity error + */ +import cpp + +from Function f, int c +where c = f.getMetrics().getCyclomaticComplexity() and + c > 20 +select f, c as CyclomaticComplexity, + "AV Rule 3: All functions shall have a cyclomatic complexity number of 20 or less." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.04 Environment/AV Rule 11.ql b/cpp/ql/src/jsf/4.04 Environment/AV Rule 11.ql new file mode 100644 index 000000000000..c7ff2e29cd55 --- /dev/null +++ b/cpp/ql/src/jsf/4.04 Environment/AV Rule 11.ql @@ -0,0 +1,13 @@ +/** + * @name AV Rule 11 + * @description Trigraphs will not be used. + * @kind problem + * @id cpp/jsf/av-rule-11 + * @problem.severity warning + */ +import cpp +import external.ExternalArtifact + +from DefectExternalData d +where d.getQueryPath() = "jsf/4.04 Environment/AV Rule 11.ql" +select d, d.getMessage() \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.04 Environment/AV Rule 12.ql b/cpp/ql/src/jsf/4.04 Environment/AV Rule 12.ql new file mode 100644 index 000000000000..704a605f7ad3 --- /dev/null +++ b/cpp/ql/src/jsf/4.04 Environment/AV Rule 12.ql @@ -0,0 +1,14 @@ +/** + * @name AV Rule 12 + * @description Digraphs will not be used. + * @kind problem + * @id cpp/jsf/av-rule-12 + * @problem.severity warning + */ + +import cpp +import external.ExternalArtifact + +from DefectExternalData d +where d.getQueryPath() = "jsf/4.04 Environment/AV Rule 12.ql" +select d, d.getMessage() \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.04 Environment/AV Rule 13.ql b/cpp/ql/src/jsf/4.04 Environment/AV Rule 13.ql new file mode 100644 index 000000000000..1eeb9f0ef3e0 --- /dev/null +++ b/cpp/ql/src/jsf/4.04 Environment/AV Rule 13.ql @@ -0,0 +1,13 @@ +/** + * @name AV Rule 13 + * @description Multi-byte characters and wide string literals will not be used. + * @kind problem + * @id cpp/jsf/av-rule-13 + * @problem.severity error + */ +import cpp + +from Literal l +where l.getType() instanceof Wchar_t or + l.getType().(ArrayType).getBaseType().getUnspecifiedType() instanceof Wchar_t +select l, "AV Rule 13: Multi-byte characters and wide string literals will not be used." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.04 Environment/AV Rule 14.ql b/cpp/ql/src/jsf/4.04 Environment/AV Rule 14.ql new file mode 100644 index 000000000000..f9da62380f66 --- /dev/null +++ b/cpp/ql/src/jsf/4.04 Environment/AV Rule 14.ql @@ -0,0 +1,13 @@ +/** + * @name AV Rule 14 + * @description Literal suffixes shall use uppercase rather than lowercase letters. + * @kind problem + * @id cpp/jsf/av-rule-14 + * @problem.severity error + */ +import cpp + +from Literal l +where l.fromSource() and + l.getValueText().regexpMatch(".*[ul][uUlL]*\\s*") +select l, "AV Rule 14: Literal suffixes shall use uppercase rather than lowercase letters." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.04 Environment/AV Rule 9.ql b/cpp/ql/src/jsf/4.04 Environment/AV Rule 9.ql new file mode 100644 index 000000000000..5ac20cfc5a65 --- /dev/null +++ b/cpp/ql/src/jsf/4.04 Environment/AV Rule 9.ql @@ -0,0 +1,14 @@ +/** + * @name AV Rule 9 + * @description Only those characters specified in the C++ basic source character set will be used. + * @kind problem + * @id cpp/jsf/av-rule-9 + * @problem.severity warning + */ + +import cpp +import external.ExternalArtifact + +from DefectExternalData d +where d.getQueryPath() = "jsf/4.04 Environment/AV Rule 9.ql" +select d, d.getMessage() diff --git a/cpp/ql/src/jsf/4.05 Libraries/AV Rule 17.ql b/cpp/ql/src/jsf/4.05 Libraries/AV Rule 17.ql new file mode 100644 index 000000000000..30a36d0ad960 --- /dev/null +++ b/cpp/ql/src/jsf/4.05 Libraries/AV Rule 17.ql @@ -0,0 +1,14 @@ +/** + * @name AV Rule 17 + * @description The error indicator errno shall not be used. + * @kind problem + * @id cpp/jsf/av-rule-17 + * @problem.severity error + */ +import cpp + +from Locatable errno, Locatable use +where ( errno.(Macro).getHead() = "errno" and use = errno.(Macro).getAnInvocation() + or errno.(Variable).hasName("errno") and use = errno.(Variable).getAnAccess()) and + errno.getFile().getAbsolutePath().matches("%errno.h") +select use, "AV Rule 17: The error indicator errno shall not be used." diff --git a/cpp/ql/src/jsf/4.05 Libraries/AV Rule 18.ql b/cpp/ql/src/jsf/4.05 Libraries/AV Rule 18.ql new file mode 100644 index 000000000000..8045b2bb604d --- /dev/null +++ b/cpp/ql/src/jsf/4.05 Libraries/AV Rule 18.ql @@ -0,0 +1,13 @@ +/** + * @name AV Rule 18 + * @description The macro offsetof, in library , shall not be used. + * @kind problem + * @id cpp/jsf/av-rule-18 + * @problem.severity error + */ +import cpp + +from Macro offsetof +where offsetof.getHead().matches("offsetof(%,%)") and + offsetof.getFile().getAbsolutePath().matches("%stddef.h") +select offsetof.getAnInvocation(), "AV Rule 18: The macro offsetof, in library , shall not be used." diff --git a/cpp/ql/src/jsf/4.05 Libraries/AV Rule 19.ql b/cpp/ql/src/jsf/4.05 Libraries/AV Rule 19.ql new file mode 100644 index 000000000000..ea4885b13a33 --- /dev/null +++ b/cpp/ql/src/jsf/4.05 Libraries/AV Rule 19.ql @@ -0,0 +1,12 @@ +/** + * @name AV Rule 19 + * @description and the setlocale function shall not be used. + * @kind problem + * @id cpp/jsf/av-rule-19 + * @problem.severity error + */ +import cpp + +from Include incl +where incl.getIncludedFile().getAbsolutePath().matches("%locale.h") +select incl, "AV Rule 19: and the setlocale function shall not be used." diff --git a/cpp/ql/src/jsf/4.05 Libraries/AV Rule 20.ql b/cpp/ql/src/jsf/4.05 Libraries/AV Rule 20.ql new file mode 100644 index 000000000000..f5658511c13f --- /dev/null +++ b/cpp/ql/src/jsf/4.05 Libraries/AV Rule 20.ql @@ -0,0 +1,28 @@ +/** + * @name AV Rule 20 + * @description The setjmp macro and the longjmp function shall not be used. + * @kind problem + * @id cpp/jsf/av-rule-20 + * @problem.severity error + */ +import cpp + +class Setjmp extends Macro { + Setjmp() { + super.getHead().matches("setjmp(%)") and + super.getFile().getAbsolutePath().matches("%setjmp.h") + } +} + +class Longjmp extends Function { + Longjmp() { + super.hasName("longjmp") and + super.getNumberOfParameters() = 2 and + super.getFile().getAbsolutePath().matches("%setjmp.h") + } +} + +from Setjmp setjmp, Longjmp longjmp, Locatable use +where use = setjmp.getAnInvocation() + or use = longjmp.getACallToThisFunction() +select use, "AV Rule 20: The setjmp macro and the longjmp function shall not be used." diff --git a/cpp/ql/src/jsf/4.05 Libraries/AV Rule 21.ql b/cpp/ql/src/jsf/4.05 Libraries/AV Rule 21.ql new file mode 100644 index 000000000000..388a717eebbf --- /dev/null +++ b/cpp/ql/src/jsf/4.05 Libraries/AV Rule 21.ql @@ -0,0 +1,12 @@ +/** + * @name AV Rule 21 + * @description The signal handling facilities of shall not be used. + * @kind problem + * @id cpp/jsf/av-rule-21 + * @problem.severity error + */ +import cpp + +from Include incl +where incl.getIncludedFile().getAbsolutePath().matches("%signal.h") +select incl, "AV Rule 21: The signal handling facilities of shall not be used." diff --git a/cpp/ql/src/jsf/4.05 Libraries/AV Rule 22.ql b/cpp/ql/src/jsf/4.05 Libraries/AV Rule 22.ql new file mode 100644 index 000000000000..4a8f075a7bd8 --- /dev/null +++ b/cpp/ql/src/jsf/4.05 Libraries/AV Rule 22.ql @@ -0,0 +1,12 @@ +/** + * @name AV Rule 22 + * @description The input/output library shall not be used. + * @kind problem + * @id cpp/jsf/av-rule-22 + * @problem.severity error + */ +import cpp + +from Include incl +where incl.getIncludedFile().getAbsolutePath().matches("%stdio.h") +select incl, "AV Rule 22: The input/output library shall not be used." diff --git a/cpp/ql/src/jsf/4.05 Libraries/AV Rule 23.ql b/cpp/ql/src/jsf/4.05 Libraries/AV Rule 23.ql new file mode 100644 index 000000000000..62b3b4c6539e --- /dev/null +++ b/cpp/ql/src/jsf/4.05 Libraries/AV Rule 23.ql @@ -0,0 +1,13 @@ +/** + * @name AV Rule 23 + * @description The library functions atof, atoi and atol from library shall not be used. + * @kind problem + * @id cpp/jsf/av-rule-23 + * @problem.severity error + */ +import cpp + +from Function f +where f.getName().regexpMatch("atof|atoi|atol") and + f.getFile().getAbsolutePath().matches("%stdlib.h") +select f.getACallToThisFunction(), "AV Rule 23: The library functions atof, atoi and atol from library shall not be used." diff --git a/cpp/ql/src/jsf/4.05 Libraries/AV Rule 24.cpp b/cpp/ql/src/jsf/4.05 Libraries/AV Rule 24.cpp new file mode 100644 index 000000000000..dd6d0bce8bd4 --- /dev/null +++ b/cpp/ql/src/jsf/4.05 Libraries/AV Rule 24.cpp @@ -0,0 +1,19 @@ +class LibraryClass { +public: + void f() { + ... + if (error) { + abort(); //immediately terminates program, especially + //bad since the class is in a library and not in the main + //logic of the application. + } + } + + char* diff(string file1, string file2) { + string command; + command = "diff " + file1 + " " + file2; + system(command); //call to system, may not be supported in all platforms + ... + return cmd_out; + } +}; 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 new file mode 100644 index 000000000000..1a6404486ec9 --- /dev/null +++ b/cpp/ql/src/jsf/4.05 Libraries/AV Rule 24.qhelp @@ -0,0 +1,35 @@ + + + + + +

    +This rule finds calls to the standard library functions abort, exit, getenv and system. +The functions abort and exit should not be called as they immediately terminate the program +and will bypass all the normal error and exception handling routines in the software. This is especially important in +software which is run on systems without an interactive OS, as restarting the software may require a complete reboot +of the system. getenv and system will usually not work at all on systems that do not have a +command processor. +

    + + +
    + +

    +Do not use any of the functions mentioned above. Use the error/exception handling mechanism of the software system you are using +instead of exit or abort, or write your own functions to emulate the functionality provided +by running OS commands through system and getenv. +

    + +
    + + + + + + + +
  • AV Rule 24, Joint Strike Fighter Air Vehicle C++ Coding Standards. Lockheed Martin Corporation, 2005. +
  • diff --git a/cpp/ql/src/jsf/4.05 Libraries/AV Rule 24.ql b/cpp/ql/src/jsf/4.05 Libraries/AV Rule 24.ql new file mode 100644 index 000000000000..1cec97180f7e --- /dev/null +++ b/cpp/ql/src/jsf/4.05 Libraries/AV Rule 24.ql @@ -0,0 +1,14 @@ +/** + * @name Dangerous system functions + * @description The library functions abort, exit, getenv and system from library should not be used. + * @kind problem + * @id cpp/jsf/av-rule-24 + * @problem.severity warning + * @tags portability + */ +import cpp + +from Function f +where f.getName().regexpMatch("abort|exit|getenv|system") and + f.getFile().getAbsolutePath().matches("%stdlib.h") +select f.getACallToThisFunction(), "The library functions abort, exit, getenv and system from library should not be used." diff --git a/cpp/ql/src/jsf/4.05 Libraries/AV Rule 25.ql b/cpp/ql/src/jsf/4.05 Libraries/AV Rule 25.ql new file mode 100644 index 000000000000..659afa17cf48 --- /dev/null +++ b/cpp/ql/src/jsf/4.05 Libraries/AV Rule 25.ql @@ -0,0 +1,12 @@ +/** + * @name AV Rule 25 + * @description The time handling functions of library shall not be used. + * @kind problem + * @id cpp/jsf/av-rule-25 + * @problem.severity error + */ +import cpp + +from Include incl +where incl.getIncludedFile().getAbsolutePath().matches("%time.h") +select incl, "AV Rule 25: The time handling functions of library shall not be used." diff --git a/cpp/ql/src/jsf/4.06 Pre-Processing Directives/AV Rule 26.ql b/cpp/ql/src/jsf/4.06 Pre-Processing Directives/AV Rule 26.ql new file mode 100644 index 000000000000..095b7ecb80ab --- /dev/null +++ b/cpp/ql/src/jsf/4.06 Pre-Processing Directives/AV Rule 26.ql @@ -0,0 +1,15 @@ +/** + * @name AV Rule 26 + * @description Only the #ifndef, #define, #endif and #include preprocessor directives shall be used. + * @kind problem + * @id cpp/jsf/av-rule-26 + * @problem.severity error + */ +import cpp + +from PreprocessorDirective directive +where not directive instanceof PreprocessorIfndef and + not directive instanceof PreprocessorEndif and + not directive instanceof Macro and + not directive instanceof Include +select directive, "AV Rule 26: only the #ifndef, #endif, #define and #include directives shall be used." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.06 Pre-Processing Directives/AV Rule 27.ql b/cpp/ql/src/jsf/4.06 Pre-Processing Directives/AV Rule 27.ql new file mode 100644 index 000000000000..43b483caa48b --- /dev/null +++ b/cpp/ql/src/jsf/4.06 Pre-Processing Directives/AV Rule 27.ql @@ -0,0 +1,12 @@ +/** + * @name AV Rule 27 + * @description The #ifndef, #define and #endif directives will be used to prevent multiple inclusions of the same header files. Other techniques to prevent multiple inclusion will not be used. + * @kind problem + * @id cpp/jsf/av-rule-27 + * @problem.severity warning + */ +import cpp +import semmle.code.cpp.headers.MultipleInclusion + +from BadIncludeGuard bad +select bad.blame(), "AV Rule 27: techniques other than #ifndef/#define/#endif will not be used to prevent multiple inclusions of header files." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.06 Pre-Processing Directives/AV Rule 28.ql b/cpp/ql/src/jsf/4.06 Pre-Processing Directives/AV Rule 28.ql new file mode 100644 index 000000000000..8b044fdfc0ed --- /dev/null +++ b/cpp/ql/src/jsf/4.06 Pre-Processing Directives/AV Rule 28.ql @@ -0,0 +1,14 @@ +/** + * @name AV Rule 28 + * @description The #ifndef and #endif directives will only be used as defined in AV Rule 27 to prevent multiple inclusions of the same header file. + * @kind problem + * @id cpp/jsf/av-rule-28 + * @problem.severity warning + */ +import cpp +import semmle.code.cpp.headers.MultipleInclusion + +from PreprocessorDirective directive +where (directive instanceof PreprocessorIfndef or directive instanceof PreprocessorEndif) and + not exists(CorrectIncludeGuard cig | directive = cig.getIfndef() or directive = cig.getEndif()) +select directive, "AV Rule 28: the #ifndef and #endif directives will only be used as defined in AV Rule 27." diff --git a/cpp/ql/src/jsf/4.06 Pre-Processing Directives/AV Rule 29.ql b/cpp/ql/src/jsf/4.06 Pre-Processing Directives/AV Rule 29.ql new file mode 100644 index 000000000000..c6229e118b66 --- /dev/null +++ b/cpp/ql/src/jsf/4.06 Pre-Processing Directives/AV Rule 29.ql @@ -0,0 +1,12 @@ +/** + * @name AV Rule 29 + * @description The #define pre-processor directive shall not be used to create inline macros. Inline functions shall be used instead. + * @kind problem + * @id cpp/jsf/av-rule-29 + * @problem.severity error + */ +import cpp + +from Macro m +where m.getHead().matches("%(%") // Macro functions are simply macros with brackets in the head +select m, "The #define pre-processor directive shall not be used to create inline macros" \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.06 Pre-Processing Directives/AV Rule 30.ql b/cpp/ql/src/jsf/4.06 Pre-Processing Directives/AV Rule 30.ql new file mode 100644 index 000000000000..dff8e58367b7 --- /dev/null +++ b/cpp/ql/src/jsf/4.06 Pre-Processing Directives/AV Rule 30.ql @@ -0,0 +1,33 @@ +/** + * @name AV Rule 30 + * @description The #define pre-processor directive shall not be used to define constant values. Instead, the const qualifier shall be applied to variable declarations to specify constant values. + * @kind problem + * @id cpp/jsf/av-rule-30 + * @problem.severity error + */ +import cpp + +/** A macro defining a simple constant. */ +class ConstantDefMacro extends Macro { + ConstantDefMacro() { + // Exclude functions + not (this.getHead().matches("%(%")) and + exists(string body | body = this.getBody() and + // Empty defines are allowed (rule 31 restricts their use though) + body != "" and + // No special characters in the body + not (body.matches("%(%")) and + not (body.matches("%{%")) + ) + } +} + +/** List macros that are 'common' and should be excluded */ +predicate commonMacro(string name) { + name = "NULL" // TODO +} + +from ConstantDefMacro m +where not commonMacro(m.getHead()) + and m.fromSource() +select m, "The #define pre-processor directive shall not be used to define constant values." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.06 Pre-Processing Directives/AV Rule 31.ql b/cpp/ql/src/jsf/4.06 Pre-Processing Directives/AV Rule 31.ql new file mode 100644 index 000000000000..74b5eb9697b0 --- /dev/null +++ b/cpp/ql/src/jsf/4.06 Pre-Processing Directives/AV Rule 31.ql @@ -0,0 +1,13 @@ +/** + * @name AV Rule 31 + * @description The #define directive will only be used as part of the technique to prevent multiple inclusions of the same header file. + * @kind problem + * @id cpp/jsf/av-rule-31 + * @problem.severity warning + */ +import cpp +import semmle.code.cpp.headers.MultipleInclusion + +from Macro macro +where not exists(CorrectIncludeGuard cig | macro = cig.getDefine()) +select macro, "AV Rule 31: The #define directive will only be used as part of the technique to prevent multiple inclusions of the same header file." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.06 Pre-Processing Directives/AV Rule 32.cpp b/cpp/ql/src/jsf/4.06 Pre-Processing Directives/AV Rule 32.cpp new file mode 100644 index 000000000000..9f5573ab44cf --- /dev/null +++ b/cpp/ql/src/jsf/4.06 Pre-Processing Directives/AV Rule 32.cpp @@ -0,0 +1,4 @@ +#include //correct: iostream.h is a header file +#include "implementation.c" //wrong: include header files only +... + diff --git a/cpp/ql/src/jsf/4.06 Pre-Processing Directives/AV Rule 32.qhelp b/cpp/ql/src/jsf/4.06 Pre-Processing Directives/AV Rule 32.qhelp new file mode 100644 index 000000000000..01df6ec28e2e --- /dev/null +++ b/cpp/ql/src/jsf/4.06 Pre-Processing Directives/AV Rule 32.qhelp @@ -0,0 +1,46 @@ + + + + + +

    +This rule finds #include pre-processor directives that include a non-header (*.h, *.hpp) file. The exception is +header files that define template classes or functions. These header files can include the implementation files, which are then +considered to be part of the header file. Including source files can create unnecessarily large object files, and would also expose +the entirety of the implementation instead of just its interface (especially in C with its limited set of scoping keywords). +

    + +

    +An exception to these rules are files that contain template definitions. These files can include .c and .cpp +files that contain the implementation of the template. No code is actually generated for templates until they are used, and so their +definition (not just the declaration) has to be included in any file that uses the template. +

    + +
    + +

    +Only include header files. Refactor the code to put class declarations and function prototypes into header files, and their +definitions in source files (*.c, *.cpp) files. +

    + +
    + + + + + +
  • + AV Rule 32, Joint Strike Fighter Air Vehicle C++ Coding Standards. Lockheed Martin Corporation, 2005. +
  • +
  • + Scott Meyers. Item 38, Effective C++: 50 Specific Ways to Improve Your Programs and Design, 2nd Edition. Addison-Wesley, 1998. +
  • +
  • + Header files +
  • + + +
    +
    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 new file mode 100644 index 000000000000..9dd2e5bfc223 --- /dev/null +++ b/cpp/ql/src/jsf/4.06 Pre-Processing Directives/AV Rule 32.ql @@ -0,0 +1,23 @@ +/** + * @name Include header files only + * @description The #include pre-processor directive should only be used to include header files. + * @kind problem + * @problem.severity recommendation + * @precision high + * @id cpp/include-non-header + * @tags maintainability + * modularity + */ +import cpp +import semmle.code.cpp.AutogeneratedFile + +from Include i, File f, string extension +where f = i.getIncludedFile() and extension = f.getExtension().toLowerCase() + and extension != "inl" + and extension != "tcc" + and extension != "tpp" + and extension != "txx" + and extension != "xpm" + and not (f instanceof AutogeneratedFile) + and not (f instanceof HeaderFile) +select i, "The #include pre-processor directive should only be used to include header files." diff --git a/cpp/ql/src/jsf/4.07 Header Files/AV Rule 33.ql b/cpp/ql/src/jsf/4.07 Header Files/AV Rule 33.ql new file mode 100644 index 000000000000..bdf802a85f78 --- /dev/null +++ b/cpp/ql/src/jsf/4.07 Header Files/AV Rule 33.ql @@ -0,0 +1,12 @@ +/** + * @name AV Rule 33 + * @description The #include directive shall use the notation to include header files. + * @kind problem + * @id cpp/jsf/av-rule-33 + * @problem.severity error + */ +import cpp + +from Include i +where i.getIncludeText().matches("<%") +select i, "AV Rule 33: the #include notation shall be used." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.07 Header Files/AV Rule 35.qhelp b/cpp/ql/src/jsf/4.07 Header Files/AV Rule 35.qhelp new file mode 100644 index 000000000000..23f4e62e8f4c --- /dev/null +++ b/cpp/ql/src/jsf/4.07 Header Files/AV Rule 35.qhelp @@ -0,0 +1,74 @@ + + + +

    +Some header files, such as those which define structures or classes, cannot be included more than once within a translation unit, as doing so would +cause a redefinition error. Such headers must be guarded to prevent ill-effects from multiple inclusion. Simlarly, if header files include other +header files, and this inclusion graph contains a cycle, then at least one file within the cycle must contain header guards in order to break the +cycle. Because of cases like these, all headers should be guarded as a matter of good practice, even if they do not strictly need to be. +

    +

    +Furthermore, most modern compilers contain optimisations which are triggered by header guards. If the header guard strictly conforms to the pattern +that compilers expect, then inclusions of that header other than the first have absolutely no effect: the file isn't re-read from disk, nor is it +re-tokenised or re-preprocessed. This can result in a noticeable, albeit minor, improvement to compilation time. +

    + +
    + +

    +Add one of the following forms of header guard to the file (where HEADER_NAME is a unique identifier derived from the name of the file): +

    +
      +
    1. #ifndef HEADER_NAME followed by #define HEADER_NAME at the very start of the header, and a matching #endif at the very end.
    2. +
    3. #if !defined(HEADER_NAME) followed by #define HEADER_NAME at the very start of the header, and a matching #endif at the very end.
    4. +
    5. #pragma once anywhere within the header.
    6. +
    +

    +Note that if you are updating code to match the Joint Strike Fighter Air Vehicle coding standard, then the first option is the only appropriate form. +

    + +
    + +

    The author of the following header tried to use header guards, but made a typo: +

    +

    In scenarios like this, MY_HAEDER_H should be replaced by MY_HEADER_H (note the transposed A and E): +

    + +
    + +

    The following header would seem to be guarded, but doesn't strictly abide to the rules: +

    +

    Although the preprocessor directives in the preceding header will prevent errors from repeated inclusion, not all compilers are intelligent enough +to recognise it as being guarded. Consequently compiler optimization will be limited. To ensure that the guard is recognized by compilers, change the +header so that the guard is the outermost directive: +

    + +
    + +

    The following header evolved over time, with different authors adding function declarations in different places: +

    +

    Unfortunately, the result is that some declarations are before the initial #ifndef, some are between the #ifndef and the #define, and +some are after the final #endif. All three of these things must be addressed to turn the file into a correctly guarded header: +

    + +
    + + +
  • + AV Rules 27 and 35, Joint Strike Fighter Air Vehicle C++ Coding Standards. Lockheed Martin Corporation, 2005. +
  • +
  • + PRE06-C. Enclose header files in an inclusion guard +
  • +
  • + Header files +
  • +
  • + Headers and Includes: Why and How +
  • + + +
    +
    diff --git a/cpp/ql/src/jsf/4.07 Header Files/AV Rule 35.ql b/cpp/ql/src/jsf/4.07 Header Files/AV Rule 35.ql new file mode 100644 index 000000000000..b4eabe1f175f --- /dev/null +++ b/cpp/ql/src/jsf/4.07 Header Files/AV Rule 35.ql @@ -0,0 +1,143 @@ +/** + * @name Missing header guard + * @description Header files should contain header guards (#defines to prevent + * the file from being included twice). This prevents errors and + * inefficiencies caused by repeated inclusion. + * @kind problem + * @problem.severity warning + * @precision high + * @id cpp/missing-header-guard + * @tags efficiency + * maintainability + * modularity + */ +import cpp +import semmle.code.cpp.headers.MultipleInclusion + +string possibleGuard(HeaderFile hf, string body) { + exists(Macro m | m.getFile() = hf and m.getBody() = body | result = m.getHead()) +} + +/** + * Option type for preprocessor directives so we can produce a variable number + * of links in the result + */ +newtype TMaybePreprocessorDirective = + TSomePreprocessorDirective(PreprocessorDirective pd) or + TNoPreprocessorDirective() + +abstract class MaybePreprocessorDirective extends TMaybePreprocessorDirective { + abstract string toString(); + abstract Location getLocation(); +} + +class NoPreprocessorDirective extends TNoPreprocessorDirective, MaybePreprocessorDirective { + override string toString() { + result = "" + } + + override Location getLocation() { + result instanceof UnknownDefaultLocation + } +} + +class SomePreprocessorDirective extends TSomePreprocessorDirective, MaybePreprocessorDirective { + PreprocessorDirective pd; + + SomePreprocessorDirective() { + this = TSomePreprocessorDirective(pd) + } + + override string toString() { + result = pd.toString() + } + + override Location getLocation() { + result = pd.getLocation() + } + + PreprocessorDirective getPreprocessorDirective() { + result = pd + } +} + +/** + * Provides additional detail when there is an incorrect header guard. + * The second and third parameters are option typed, and are only present + * when there are additional links in the detail string. + */ +string extraDetail(HeaderFile hf, SomePreprocessorDirective detail1, SomePreprocessorDirective detail2) { + exists(string s, PreprocessorEndif endif, PreprocessorDirective ifndef | startsWithIfndef(hf, ifndef, s) and endif.getIf() = ifndef | + detail1.getPreprocessorDirective() = endif and + detail2.getPreprocessorDirective() = ifndef and + if not endsWithEndif(hf, endif) then + result = " ($@ matching $@ occurs before the end of the file)." + else if exists(Macro m | m.getFile() = hf and m.getHead() = s) then + result = " (#define " + s + " needs to appear immediately after #ifndef " + s + ")." + else if strictcount(possibleGuard(hf, _)) = 1 then + result = " (" + possibleGuard(hf, _) + " should appear in the #ifndef rather than " + s + ")." + else if strictcount(possibleGuard(hf, "")) = 1 then + result = " (" + possibleGuard(hf, "") + " should appear in the #ifndef rather than " + s + ")." + else + result = " (the macro " + s + " is checked for, but is not defined)." + ) +} + +/** + * Header file `hf` uses a macro called `macroName`. + */ +predicate usesMacro(HeaderFile hf, string macroName) { + exists(MacroAccess ma | + ma.getFile() = hf and + ma.getMacro().getName() = macroName + ) +} + +/** + * File `f` both defines and un-defines a macro called `macroName`. + */ +predicate defUndef(File f, string macroName) { + exists(Macro m | + m.getFile() = f and + m.getName() = macroName + ) and exists(PreprocessorUndef ud | + ud.getFile() = f and + ud.getName() = macroName + ) +} + +/** + * Header file `hf` looks like it contains an x-macro called + * `macroName`. That is, a macro that is used to interpret the + * data in `hf`, usually defined just before including that file + * and undefined immediately afterwards. + */ +predicate hasXMacro(HeaderFile hf, string macroName) { + usesMacro(hf, macroName) and + forex(Include i | i.getIncludedFile() = hf | + defUndef(i.getFile(), macroName) + ) +} + +from HeaderFile hf, string detail, MaybePreprocessorDirective detail1, MaybePreprocessorDirective detail2 +where not hf instanceof IncludeGuardedHeader + and (if exists(extraDetail(hf, _, _)) + then detail = extraDetail(hf, detail1, detail2) + else (detail = "." and + detail1 instanceof NoPreprocessorDirective and + detail2 instanceof NoPreprocessorDirective)) + // Exclude files which contain no declaration entries or top level + // declarations (e.g. just preprocessor directives; or non-top level + // code). + and ( + exists(DeclarationEntry de | de.getFile() = hf) or + exists(Declaration d | d.getFile() = hf and d.isTopLevel()) or + exists(UsingEntry ue | ue.getFile() = hf) + ) + // Exclude files which look like they contain 'x-macros' + and not hasXMacro(hf, _) + // Exclude files which are always #imported. + and not forex(Include i | i.getIncludedFile() = hf | i instanceof Import) + // Exclude files which are only included once. + and not strictcount(Include i | i.getIncludedFile() = hf) = 1 +select hf, "This header file should contain a header guard to prevent multiple inclusion" + detail, detail1, detail1.toString(), detail2, detail2.toString() diff --git a/cpp/ql/src/jsf/4.07 Header Files/AV Rule 39.ql b/cpp/ql/src/jsf/4.07 Header Files/AV Rule 39.ql new file mode 100644 index 000000000000..4a38b3807b61 --- /dev/null +++ b/cpp/ql/src/jsf/4.07 Header Files/AV Rule 39.ql @@ -0,0 +1,18 @@ +/** + * @name AV Rule 39 + * @description Header files (*.h) will not contain non-const variable definitions or function definitions. + * @kind problem + * @id cpp/jsf/av-rule-39 + * @problem.severity warning + */ +import cpp + +predicate forbidden(Declaration d) { + d instanceof Variable and not d.(Variable).isConst() + or (d instanceof Function and not d.hasSpecifier("inline")) +} + +from Declaration d +where d.getDefinitionLocation().getFile() instanceof HeaderFile + and forbidden(d) +select d.getDefinitionLocation(), "AV Rule 38: header files will not contain non-const variable definitions or function definitions." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.07 Header Files/JSF35-encompassing-bad.cpp b/cpp/ql/src/jsf/4.07 Header Files/JSF35-encompassing-bad.cpp new file mode 100644 index 000000000000..541b7eb0a9c3 --- /dev/null +++ b/cpp/ql/src/jsf/4.07 Header Files/JSF35-encompassing-bad.cpp @@ -0,0 +1,15 @@ +void alpha(); + +#ifndef MY_FUNCTIONS_H + +void beta(); + +#define MY_FUNCTIONS_H + +void gamma(); +void delta(); +void epsilon(); + +#endif + +void omega(); \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.07 Header Files/JSF35-encompassing-good.cpp b/cpp/ql/src/jsf/4.07 Header Files/JSF35-encompassing-good.cpp new file mode 100644 index 000000000000..ecfe71122e88 --- /dev/null +++ b/cpp/ql/src/jsf/4.07 Header Files/JSF35-encompassing-good.cpp @@ -0,0 +1,11 @@ +#ifndef MY_FUNCTIONS_H +#define MY_FUNCTIONS_H + +void alpha(); +void beta(); +void gamma(); +void delta(); +void epsilon(); +void omega(); + +#endif \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.07 Header Files/JSF35-outermost-bad.cpp b/cpp/ql/src/jsf/4.07 Header Files/JSF35-outermost-bad.cpp new file mode 100644 index 000000000000..ea5eab30994c --- /dev/null +++ b/cpp/ql/src/jsf/4.07 Header Files/JSF35-outermost-bad.cpp @@ -0,0 +1,8 @@ +#ifdef __linux__ +#ifndef MY_LINUX_ONLY_HEADER_H +#define MY_LINUX_ONLY_HEADER_H + +/* ... contents of my_linux_only_header.h ... */ + +#endif // MY_LINUX_ONLY_HEADER_H +#endif // __linux__ \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.07 Header Files/JSF35-outermost-good.cpp b/cpp/ql/src/jsf/4.07 Header Files/JSF35-outermost-good.cpp new file mode 100644 index 000000000000..d592bbffa63d --- /dev/null +++ b/cpp/ql/src/jsf/4.07 Header Files/JSF35-outermost-good.cpp @@ -0,0 +1,8 @@ +#ifndef MY_LINUX_ONLY_HEADER_H +#define MY_LINUX_ONLY_HEADER_H +#ifdef __linux__ + +/* ... contents of my_linux_only_header.h ... */ + +#endif // __linux__ +#endif // MY_LINUX_ONLY_HEADER_H \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.07 Header Files/JSF35-typo-bad.cpp b/cpp/ql/src/jsf/4.07 Header Files/JSF35-typo-bad.cpp new file mode 100644 index 000000000000..cb56ee195ca2 --- /dev/null +++ b/cpp/ql/src/jsf/4.07 Header Files/JSF35-typo-bad.cpp @@ -0,0 +1,6 @@ +#ifndef MY_HAEDER_H +#define MY_HEADER_H + +/* ... contents of my_header.h ... */ + +#endif \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.07 Header Files/JSF35-typo-good.cpp b/cpp/ql/src/jsf/4.07 Header Files/JSF35-typo-good.cpp new file mode 100644 index 000000000000..7bb2b4bf590f --- /dev/null +++ b/cpp/ql/src/jsf/4.07 Header Files/JSF35-typo-good.cpp @@ -0,0 +1,6 @@ +#ifndef MY_HEADER_H +#define MY_HEADER_H + +/* ... contents of my_header.h ... */ + +#endif \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.08 Implementation Files/AV Rule 40.ql b/cpp/ql/src/jsf/4.08 Implementation Files/AV Rule 40.ql new file mode 100644 index 000000000000..fbc245545477 --- /dev/null +++ b/cpp/ql/src/jsf/4.08 Implementation Files/AV Rule 40.ql @@ -0,0 +1,47 @@ +/** + * @name AV Rule 40 + * @description Every implementation file shall include the header files that uniquely define the inline functions, types, and templates used. + * @kind problem + * @id cpp/jsf/av-rule-40 + * @problem.severity error + */ +import cpp + +/* + * The rule appears a bit under-specified, but looking at the rationale and examples + * it is quite straightforward: the rule is intended to require the one-definition rule. + * We check for types, inline functions and templates: + * - The definition is in a header file (not an implementation file) + * - There is only one definition + * + * Note: non-defining declarations are allowed to be repeated. + * + * LIMITATION: currently templates are not checked (extractor limitation) + */ + +predicate relevant(Declaration d) { + d instanceof UserType or + d.(Function).hasSpecifier("inline") +} + +predicate hasTwoDefinitions(Declaration d) { + exists(Location l1, Location l2 | + l1 = d.getDefinitionLocation() and + l2 = d.getDefinitionLocation() and + l1 != l2 + ) +} + +predicate definedInImplementationFile(Declaration d) { + d.getDefinitionLocation().getFile() instanceof CppFile or + d.getDefinitionLocation().getFile() instanceof CFile +} + +from Declaration d, string message +where relevant(d) and + ((hasTwoDefinitions(d) and message = " should not have several definitions.") + or definedInImplementationFile(d) and message = " should be defined in a header file.") + // Don't count member functions - the only way they can match this rule is by + // being in a class definition that already matches, so it would be redundant + and not d instanceof MemberFunction +select d.getDefinitionLocation(), "AV Rule 40: " + d.getName() + message \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.09 Style/AV Rule 41.ql b/cpp/ql/src/jsf/4.09 Style/AV Rule 41.ql new file mode 100644 index 000000000000..e3f6ce995c1e --- /dev/null +++ b/cpp/ql/src/jsf/4.09 Style/AV Rule 41.ql @@ -0,0 +1,14 @@ +/** + * @name AV Rule 41 + * @description Source lines will be kept to a length of 120 characters or less. + * @kind problem + * @id cpp/jsf/av-rule-41 + * @problem.severity warning + */ + +import cpp +import external.ExternalArtifact + +from DefectExternalData d +where d.getQueryPath() = "jsf/4.09 Style/AV Rule 41.ql" +select d, d.getMessage() \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.09 Style/AV Rule 42.ql b/cpp/ql/src/jsf/4.09 Style/AV Rule 42.ql new file mode 100644 index 000000000000..170ed0a44588 --- /dev/null +++ b/cpp/ql/src/jsf/4.09 Style/AV Rule 42.ql @@ -0,0 +1,28 @@ +/** + * @name AV Rule 42 + * @description Each expression-statement will be on a separate line. + * @kind problem + * @id cpp/jsf/av-rule-42 + * @problem.severity warning + */ +import cpp + +predicate exprInStmtContext(Expr e, Location l, File f) { + e.getParent() instanceof ExprStmt + and l = e.getLocation() + and f = l.getFile() +} + +predicate overlappingExprs(Expr e1, Expr e2) { + exists(Location l1, File f, Location l2 | + exprInStmtContext(e1, l1, f) and + exprInStmtContext(e2, l2, f) and + e1 != e2 and + (l1.getEndLine() >= l2.getStartLine()) and + (l1.getStartLine() <= l2.getEndLine()) + ) +} + +from Expr e +where overlappingExprs(e, _) and not e.isInMacroExpansion() +select e, "AV Rule 42: Each expression-statement will be on a separate line." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.09 Style/AV Rule 43.ql b/cpp/ql/src/jsf/4.09 Style/AV Rule 43.ql new file mode 100644 index 000000000000..c2094226871f --- /dev/null +++ b/cpp/ql/src/jsf/4.09 Style/AV Rule 43.ql @@ -0,0 +1,14 @@ +/** + * @name AV Rule 43 + * @description Tabs should be avoided. + * @kind problem + * @id cpp/jsf/av-rule-43 + * @problem.severity recommendation + */ + +import cpp +import external.ExternalArtifact + +from DefectExternalData d +where d.getQueryPath() = "jsf/4.09 Style/AV Rule 43.ql" +select d, d.getMessage() \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.09 Style/AV Rule 44.ql b/cpp/ql/src/jsf/4.09 Style/AV Rule 44.ql new file mode 100644 index 000000000000..7b842e4edd72 --- /dev/null +++ b/cpp/ql/src/jsf/4.09 Style/AV Rule 44.ql @@ -0,0 +1,14 @@ +/** + * @name AV Rule 44 + * @description All indentations will be at least two spaces and be consistent within the same source file. + * @kind problem + * @id cpp/jsf/av-rule-44 + * @problem.severity warning + */ + +import cpp +import external.ExternalArtifact + +from DefectExternalData d +where d.getQueryPath() = "jsf/4.09 Style/AV Rule 44.ql" +select d, d.getMessage() \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.09 Style/AV Rule 45.ql b/cpp/ql/src/jsf/4.09 Style/AV Rule 45.ql new file mode 100644 index 000000000000..79bdad085b8f --- /dev/null +++ b/cpp/ql/src/jsf/4.09 Style/AV Rule 45.ql @@ -0,0 +1,20 @@ +/** + * @name AV Rule 45 + * @description All words in an identifier will be separated by the underscore character. + * @kind problem + * @id cpp/jsf/av-rule-45 + * @problem.severity warning + */ +import cpp + +// INTERPRETATION: just check for the absence of camel-case, ie +// forbid 'aB' in identifier names + +from Declaration d, string name, string lowerCase, string upperCase, int pos +where name = d.getName() and + d.fromSource() and + lowerCase = name.charAt(pos) and + upperCase = name.charAt(pos+1) and + lowerCase.regexpMatch("[a-z]") and + upperCase.regexpMatch("[A-Z]") +select d, "AV Rule 45: All words in an identifier will be separated by the underscore character. Camel-case is not allowed." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.09 Style/AV Rule 46.ql b/cpp/ql/src/jsf/4.09 Style/AV Rule 46.ql new file mode 100644 index 000000000000..4511cbfe1f04 --- /dev/null +++ b/cpp/ql/src/jsf/4.09 Style/AV Rule 46.ql @@ -0,0 +1,39 @@ +/** + * @name AV Rule 46 + * @description User-specified identifiers (internal and external) will not rely on significance of more than 64 characters. + * @kind problem + * @id cpp/jsf/av-rule-46 + * @problem.severity warning + */ +import cpp + +// This is a bit off, because the two nameables could be in different scopes +// or in different name ranges entirely. However it is infrequent enough to +// require > 64 chars significance, and it would be bad practice in any case + +predicate longNameSignificance(Element e, string significance) { + exists(string name | + elementName(e, name) and + name.length() > 64 and + significance = name.substring(0, 64) + ) +} + +predicate elementName(Element e, string name) { + name = e.(Declaration).getName() or name = e.(Namespace).getName() + } + +predicate clash(Element e1, Element e2) { + exists(string significance, string n1, string n2 | + longNameSignificance(e1, significance) and + longNameSignificance(e2, significance) and + elementName(e1, n1) and + elementName(e2, n2) and + n1 != n2 + ) +} + +from Element e1, Element e2, string name +where clash(e1, e2) + and elementName(e2, name) +select e1, "AV Rule 46: relies on more than 64 characters to separate from " + name \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.09 Style/AV Rule 47.ql b/cpp/ql/src/jsf/4.09 Style/AV Rule 47.ql new file mode 100644 index 000000000000..49255f8347f8 --- /dev/null +++ b/cpp/ql/src/jsf/4.09 Style/AV Rule 47.ql @@ -0,0 +1,13 @@ +/** + * @name AV Rule 47 + * @description Identifiers will not begin with the underscore character. + * @kind problem + * @id cpp/jsf/av-rule-47 + * @problem.severity warning + */ +import cpp + +from Declaration d +where d.fromSource() and + d.getName().matches("\\_%") +select d, "Identifiers will not begin with the underscore character." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.09 Style/AV Rule 48.ql b/cpp/ql/src/jsf/4.09 Style/AV Rule 48.ql new file mode 100644 index 000000000000..890245aafcda --- /dev/null +++ b/cpp/ql/src/jsf/4.09 Style/AV Rule 48.ql @@ -0,0 +1,86 @@ +/** + * @name AV Rule 48 + * @description Indentifiers will not differ by: (a) only case, (b) only underscores, (c) O vs 0 and D, (d) I vs 1 and l, (e) S vs 5, (f) Z vs 2, (g) n vs h + * @kind problem + * @id cpp/jsf/av-rule-48 + * @problem.severity warning + */ +import cpp + +// The rule applies in an almost completely global way since that is the way it appears to be phrased, and +// it may be sufficiently rare to be okay that way. We make a small exception: two locals in separate scopes +// cannot trigger the rule + +// Note: rule (a) interferes with the others, since H=h by (a) and h=n by (g) but surely H != n +// For implementation it's important that this can be viewed as canonicalisation of names to avoid a quadratic +// test but that would imply transitivity +// To resolve this we interpret the rule as: it is a violation if +// - a combination of (a)-(b) makes the names identical OR +// - a combination of (b)-(g) makes the names identical + +// Implement rules (a) and (b) +predicate canonicalName1(Declaration d, string canonical) { + canonical = d.getName().replaceAll("_", "").toLowerCase() +} + +predicate canonicalName2(Declaration d, string canonical) { + canonical = d.getName() + .replaceAll("_", "") + .replaceAll("0", "O") + .replaceAll("D", "O") + .replaceAll("1", "I") + .replaceAll("l", "I") + .replaceAll("S", "5") + .replaceAll("Z", "2") + .replaceAll("n", "h") +} + +predicate same(Declaration d1, Declaration d2) { + exists (string common | canonicalName1(d1, common) and canonicalName1(d2, common)) + or + exists (string common | canonicalName2(d1, common) and canonicalName2(d2, common)) +} + +/** Is the declaration local to that function? */ +predicate local(Declaration d, Function f) { + d.(Parameter).getFunction() = f + or exists(DeclStmt ds | d = ds.getADeclaration().(LocalVariable) and ds.getEnclosingFunction() = f) +} + +/** Is the declaration local to that struct? Reduce false warning by taking into + * account struct lookup rules - since a struct members can never be referred to + * other than in a qualified way it doesn't really count.*/ +predicate structLocal(Declaration d, Struct s) { + d = s.getAMemberVariable() +} + +predicate compatibleScopes(Declaration d1, Declaration d2) { + // Either they're both local to the same struct, or + exists (Struct s | structLocal(d1,s) and structLocal(d2,s)) + or + ( + // Neither of them is a struct member and ... + not(structLocal(d1,_)) and not(structLocal(d2,_)) and + same(d1, d2) and + ( + // d2 is global and d1 is either, or + not local(d2, _) or + // both are local to the same function + exists (Function f | local(d1,f) and local(d2,f)) + ) + ) +} + +from Declaration d1, Declaration d2 +where d1.fromSource() and d2.fromSource() and + // Test that the names are confusing according to the above criteria + same(d1, d2) and + d1.getName() != d2.getName() and + ( + // either they are both type names, or + (d1 instanceof UserType and d2 instanceof UserType) + or + // they are both variable names in close enough scopes for the confusion to matter + (d1 instanceof Variable and d2 instanceof Variable and compatibleScopes(d1, d2)) + ) +select d1, "AV Rule 48: this identifier is too close to another identifier (" + d2.getName() + ")" \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.09 Style/AV Rule 49.ql b/cpp/ql/src/jsf/4.09 Style/AV Rule 49.ql new file mode 100644 index 000000000000..68301d12c279 --- /dev/null +++ b/cpp/ql/src/jsf/4.09 Style/AV Rule 49.ql @@ -0,0 +1,21 @@ +/** + * @name AV Rule 49 + * @description All acronyms in an identifier will be composed of uppercase letters. + * @kind problem + * @id cpp/jsf/av-rule-49 + * @problem.severity warning + */ +import cpp +import Naming + +// // +// // CUSTOMIZATION: see Naming to add custom acronyms +// // + +from Declaration d, Name n, Word w +where d.fromSource() and + n = d.getName() and + w = n.getAWord() and + w.isDefiniteAcronym() and + not w.isUppercase() +select d, "AV Rule 49: acronyms in identifiers will be uppercase. Incorrect case for acronym " + w.toString() + "." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.09 Style/AV Rule 50.ql b/cpp/ql/src/jsf/4.09 Style/AV Rule 50.ql new file mode 100644 index 000000000000..5ead6fe0236d --- /dev/null +++ b/cpp/ql/src/jsf/4.09 Style/AV Rule 50.ql @@ -0,0 +1,27 @@ +/** + * @name AV Rule 50 + * @description The first word of the name of a class, structure, namespace, enumeration, or type created with typedef will begin with an uppercase letter. All other letters will be lowercase. + * @kind problem + * @id cpp/jsf/av-rule-50 + * @problem.severity warning + */ +import cpp +import Naming + +predicate relevant(Element elem, Element blame, string kind, string name) { + exists(Class c | elem = c and blame = c and kind = "class or struct" and name = c.getName()) or // includes struct + exists(Namespace n | elem = n and blame = n.getADeclarationEntry() and kind = "namespace" and name = n.getName()) or + exists(Enum e | elem = e and blame = e and kind = "enumeration" and name = e.getName()) or + exists(TypedefType t | elem = t and blame = t and kind = "typedef" and name = t.getName()) +} + +from Element d, Element blame, Word w, int pos, string kind, string name +where relevant(d, blame, kind, name) and + w = name.(Name).getWord(pos) and + not (w.couldBeUppercaseAcronym() and w != name) and + ( + (pos = 0 and not w.isCapitalized()) + or + (pos > 0 and not w.isLowercase()) + ) +select blame, "AV Rule 50: The first word of a " + kind + " will begin with an uppercase letter, and all other letters will be lowercase." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.09 Style/AV Rule 51.ql b/cpp/ql/src/jsf/4.09 Style/AV Rule 51.ql new file mode 100644 index 000000000000..346e9c3ee4c2 --- /dev/null +++ b/cpp/ql/src/jsf/4.09 Style/AV Rule 51.ql @@ -0,0 +1,21 @@ +/** + * @name AV Rule 51 + * @description All letters contained in function and variable names will be lowercase. + * @kind problem + * @id cpp/jsf/av-rule-51 + * @problem.severity warning + */ +import cpp +import Naming + +predicate relevant(Declaration d, string kind) { + (d instanceof Function and kind = "function") or + (d instanceof Variable and kind = "variable") +} + +from Declaration d, Word w, string kind +where relevant(d, kind) and + w = d.getName().(Name).getAWord() and + not (w.couldBeUppercaseAcronym() and w != d.getName()) and + not w.isLowercase() +select d, "AV Rule 51: All letters contained in " + kind + " names will be lowercase." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.09 Style/AV Rule 52.ql b/cpp/ql/src/jsf/4.09 Style/AV Rule 52.ql new file mode 100644 index 000000000000..93218392e92a --- /dev/null +++ b/cpp/ql/src/jsf/4.09 Style/AV Rule 52.ql @@ -0,0 +1,21 @@ +/** + * @name AV Rule 52 + * @description Identifiers for constant and enumerator values shall be lowercase. + * @kind problem + * @id cpp/jsf/av-rule-52 + * @problem.severity error + */ +import cpp +import Naming + +predicate relevant(Declaration d, string kind) { + (d instanceof EnumConstant and kind = "enumerator") or + (d.(Variable).isConst() and kind = "constant") +} + +from Declaration d, Word w, string kind +where relevant(d, kind) and + w = d.getName().(Name).getAWord() and + not (w.couldBeUppercaseAcronym() and w != d.getName()) and + not w.isLowercase() +select d, "AV Rule 52: identifiers for " + kind + " values shall be lowercase." diff --git a/cpp/ql/src/jsf/4.09 Style/AV Rule 53.1.ql b/cpp/ql/src/jsf/4.09 Style/AV Rule 53.1.ql new file mode 100644 index 000000000000..77953c40c6d8 --- /dev/null +++ b/cpp/ql/src/jsf/4.09 Style/AV Rule 53.1.ql @@ -0,0 +1,18 @@ +/** + * @name AV Rule 53.1 + * @description The following character sequences shall not appear in header file names: ', \, /*, //, or ". + * @kind problem + * @id cpp/jsf/av-rule-53-1 + * @problem.severity error + */ +import cpp + +from Include i, string name +where name = i.getIncludeText() + and (name.matches("%'%") or + name.matches("%\\\\%") or + name.matches("%/*%") or + name.matches("%//%") or + name.matches("%\"%\"%\"%") or + name.matches("%<%\"%>%")) +select i, "AV Rule 53.1: Invalid character sequence in header file name '" + name + "'" 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 new file mode 100644 index 000000000000..862d9028de3b --- /dev/null +++ b/cpp/ql/src/jsf/4.09 Style/AV Rule 53.ql @@ -0,0 +1,15 @@ +/** + * @name AV Rule 53 + * @description Header files will always have a file name extension of .h. + * @kind problem + * @id cpp/jsf/av-rule-53 + * @problem.severity warning + */ +import cpp + +// Interpretation: use .h, never .H, .hpp or other variants. What else could be meant by 'header file'? + +from File f +where (f.getExtension().toLowerCase() = "h" or f.getExtension().toLowerCase() = "hpp") + and f.getExtension() != "h" +select f, "AV Rule 53: Header files will always have a file name extension of .h." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.09 Style/AV Rule 54.ql b/cpp/ql/src/jsf/4.09 Style/AV Rule 54.ql new file mode 100644 index 000000000000..cb40d30d51db --- /dev/null +++ b/cpp/ql/src/jsf/4.09 Style/AV Rule 54.ql @@ -0,0 +1,12 @@ +/** + * @name AV Rule 54 + * @description Implementation files will always have a file name extension of .cpp. + * @kind problem + * @id cpp/jsf/av-rule-54 + * @problem.severity warning + */ +import cpp + +from CppFile f +where f.getExtension() != "cpp" +select f, "AV Rule 53: Implementation files will always have a file name extension of .cpp." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.09 Style/AV Rule 57.ql b/cpp/ql/src/jsf/4.09 Style/AV Rule 57.ql new file mode 100644 index 000000000000..2c205cc009f0 --- /dev/null +++ b/cpp/ql/src/jsf/4.09 Style/AV Rule 57.ql @@ -0,0 +1,17 @@ +/** + * @name AV Rule 57 + * @description The public, protected, and private sections of a class will be declared in that order. + * @kind problem + * @id cpp/jsf/av-rule-57 + * @problem.severity warning + */ +import cpp + +from Class c, Declaration m1, Declaration m2, int pos1, int pos2 +where m1 = c.getCanonicalMember(pos1) and + m2 = c.getCanonicalMember(pos2) and + pos1 < pos2 and + ( (m1.hasSpecifier("private") and m2.hasSpecifier("protected")) + or (m1.hasSpecifier("private") and m2.hasSpecifier("public")) + or (m1.hasSpecifier("protected") and m2.hasSpecifier("public"))) +select c, "AV Rule 57: The public, protected, and private sections of a class will be declared in that order." diff --git a/cpp/ql/src/jsf/4.09 Style/AV Rule 58.ql b/cpp/ql/src/jsf/4.09 Style/AV Rule 58.ql new file mode 100644 index 000000000000..08a6e2c81b3b --- /dev/null +++ b/cpp/ql/src/jsf/4.09 Style/AV Rule 58.ql @@ -0,0 +1,31 @@ +/** + * @name AV Rule 58 + * @description When declaring and defining functions with more than two parameters, the leading parenthesis and the first argument will be written on the same line as the function name. Each additional argument will be written on a separate line (with the closing parenthesis directly after the last argument). + * @kind problem + * @id cpp/jsf/av-rule-58 + * @problem.severity warning + */ +import cpp + +class ParamLoc extends Parameter { + int getValidLine() { result = this.getLocation().getStartLine() and result = this.getLocation().getEndLine() } +} + +predicate valid(Function f) { + // Check that the first parameter is okay + f.getParameter(0).(ParamLoc).getValidLine() = f.getLocation().getStartLine() and + // Check that each subsequent parameter is on its own line + not exists(ParamLoc p1, ParamLoc p2 | + p1 = f.getAParameter() and p2 = f.getAParameter() and + p1 != p2 and + p1.getValidLine() = p2.getValidLine() + ) and + // Check that there are no parameters on two lines + forall (ParamLoc p | p = f.getAParameter() | exists(p.getValidLine())) +} + +from Function f +where f.getNumberOfParameters() > 2 + and f.hasDefinition() + and not valid(f) +select f, "AV Rule 58: functions with more than two parameters will conform to style rules for declaring parameters" \ No newline at end of file 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 new file mode 100644 index 000000000000..ca598d20b2e4 --- /dev/null +++ b/cpp/ql/src/jsf/4.09 Style/AV Rule 59.ql @@ -0,0 +1,19 @@ +/** + * @name AV Rule 59 + * @description The statements forming the body of an if, else if, else, while, do-while or for statement shall always be enclosed in braces, even if the braces form an empty block. + * @kind problem + * @id cpp/jsf/av-rule-59 + * @problem.severity error + */ +import cpp + +from Stmt parent, Stmt child +where not child instanceof Block + and + (child = parent.(IfStmt).getThen() or + child = parent.(WhileStmt).getStmt() or + child = parent.(DoStmt).getStmt() or + child = parent.(ForStmt).getStmt() or + (child = parent.(IfStmt).getElse() and not child instanceof IfStmt) + ) +select child.findRootCause(), "The statements forming the body of an if, else if, else, while, do...while or for statement shall always be enclosed in braces, even if the braces form an empty block." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.09 Style/AV Rule 60.ql b/cpp/ql/src/jsf/4.09 Style/AV Rule 60.ql new file mode 100644 index 000000000000..206eb7c5d99e --- /dev/null +++ b/cpp/ql/src/jsf/4.09 Style/AV Rule 60.ql @@ -0,0 +1,14 @@ +/** + * @name AV Rule 60 + * @description Braces which enclose a block will be placed in the same column, on separate lines directly before and after the block. + * @kind problem + * @id cpp/jsf/av-rule-60 + * @problem.severity warning + */ + +import cpp +import external.ExternalArtifact + +from DefectExternalData d +where d.getQueryPath() = "jsf/4.09 Style/AV Rule 60.ql" +select d, d.getMessage() \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.09 Style/AV Rule 61.ql b/cpp/ql/src/jsf/4.09 Style/AV Rule 61.ql new file mode 100644 index 000000000000..e31e844a3a53 --- /dev/null +++ b/cpp/ql/src/jsf/4.09 Style/AV Rule 61.ql @@ -0,0 +1,14 @@ +/** + * @name AV Rule 61 + * @description Braces which enclose a block will have nothing else on the line except comments (if necessary). + * @kind problem + * @id cpp/jsf/av-rule-61 + * @problem.severity warning + */ + +import cpp +import external.ExternalArtifact + +from DefectExternalData d +where d.getQueryPath() = "jsf/4.09 Style/AV Rule 61.ql" +select d, d.getMessage() \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.09 Style/AV Rule 63.ql b/cpp/ql/src/jsf/4.09 Style/AV Rule 63.ql new file mode 100644 index 000000000000..2f65ce9ec834 --- /dev/null +++ b/cpp/ql/src/jsf/4.09 Style/AV Rule 63.ql @@ -0,0 +1,87 @@ +/** + * @name AV Rule 63 + * @description Spaces will not be used around '.' or '->', nor between unary operators and operands. + * @kind problem + * @id cpp/jsf/av-rule-63 + * @problem.severity error + */ +import cpp + +predicate hasLocation(Expr e) { + e.getLocation().getStartLine() != 0 +} + +predicate outermostConvWithLocation(Expr e, Expr res) { + if(exists(Expr p | e.getConversion+() = p and hasLocation(p))) then + outermostConvWithLocation(e.getConversion(), res) + else + res = e +} + +predicate diffEndBegin(Expr lhs, Expr rhs, int length) { + exists(Location l, Location r | + l = lhs.getLocation() and r = rhs.getLocation() + and length = r.getStartColumn() - l.getEndColumn() + and l.getEndLine() = r.getStartLine() + ) +} +predicate diffEndEnd(Expr lhs, Expr rhs, int length) { + exists(Location l, Location r | + l = lhs.getLocation() and r = rhs.getLocation() + and length = r.getEndColumn() - l.getEndColumn() + and l.getEndLine() = r.getEndLine() + ) +} +predicate diffBeginBegin(Expr lhs, Expr rhs, int length) { + exists(Location l, Location r | + l = lhs.getLocation() and r = rhs.getLocation() + and length = r.getStartColumn() - l.getStartColumn() + and l.getStartLine() = r.getStartLine() + ) +} + + +/* + * Unary postfix operations: PostfixDecrExpr, PostfixIncrExpr + * Unary prefix operations: PrefixIncrExpr, PrefixDecrExpr, ComplementExpr, NotExpr, + UnaryMinusExpr, UnaryPlusExpr, AddressOfExpr, PointerDereferenceExpr + */ + +from Expr err +where + not err.isInMacroExpansion() and hasLocation(err) + and + ( + exists(Call c, Expr e | c = err and outermostConvWithLocation(c.getQualifier(), e) + and e.getType() instanceof PointerType and not diffEndBegin(e, c, 3)) + or + exists(Call c, Expr e | c = err and outermostConvWithLocation(c.getQualifier(), e) + and not e.getType() instanceof PointerType and not diffEndBegin(e, c, 2)) + or + exists(VariableAccess c, Expr e | c = err and outermostConvWithLocation(c.getQualifier(), e) + and e.getType() instanceof PointerType and not diffEndBegin(e, c, 3)) + or + exists(VariableAccess c, Expr e | c = err and outermostConvWithLocation(c.getQualifier(), e) + and not e.getType() instanceof PointerType and not diffEndBegin(e, c, 2)) + or + exists(UnaryOperation c, Expr e | c = err and outermostConvWithLocation(c.getOperand(), e) + and (c instanceof ComplementExpr or c instanceof NotExpr or c instanceof UnaryMinusExpr + or c instanceof UnaryPlusExpr or c instanceof AddressOfExpr) + and not diffBeginBegin(c, e, 1)) + or + exists(PrefixIncrExpr c, Expr e | c = err and outermostConvWithLocation(c.getOperand(), e) + and not diffBeginBegin(c, e, 2)) + or + exists(PrefixDecrExpr c, Expr e | c = err and outermostConvWithLocation(c.getOperand(), e) + and not diffBeginBegin(c, e, 2)) + or + exists(PointerDereferenceExpr c, Expr e | c = err and outermostConvWithLocation(c.getChild(0), e) + and not diffBeginBegin(c, e, 1)) + or + exists(PostfixIncrExpr c, Expr e | c = err and outermostConvWithLocation(c.getOperand(), e) + and not diffEndEnd(e, c, 2)) + or + exists(PostfixDecrExpr c, Expr e | c = err and outermostConvWithLocation(c.getOperand(), e) + and not diffEndEnd(e, c, 2)) + ) + select err, "AV Rule 63: Spaces will not be used around '.' or '->', nor between unary operators and operands." diff --git a/cpp/ql/src/jsf/4.09 Style/Naming.qll b/cpp/ql/src/jsf/4.09 Style/Naming.qll new file mode 100644 index 000000000000..4fa283259695 --- /dev/null +++ b/cpp/ql/src/jsf/4.09 Style/Naming.qll @@ -0,0 +1,113 @@ +/* + Common functions for implementing naming conventions + + Naming rules are the following: + + [45] All words in an ident will be separated by '_' + [46] Idents will not rely on significance of more than 64 characters + [47] Idents will not begin with '_' + [48] Idents will not differ in certain confusing ways (listed) + [49] All acronyms in an ident will be uppercase + [50] Classes, namespaces, enums, structs, typedefs: + begin first word with uppercase, all other lowercase + [51] Functions and variables: + lowercase + [52] Constants and enum values: + lowercase + +The tricky rules are: 45, 49, 50, 51, 52. There are two reasons: + + - Reference to 'words'. We ignore this (beyond the scope). For 45, + detect camel-case and any other bad conventions. For 50 this just + means the first letter should be uppercase. + - Acronyms. [49] has a comment that it applies to *all* identifiers, + even if some other rule specified that they should be lowercase or + differently capitalized. + +The strategy is as follows: + - apart from 45, 'words' are just _-separated parts of the identifier + - apart from 49, always allow a 'word' to be entirely in uppercase (URL), + but make sure that the whole identifier is not in uppercase. + - 49: check common acronyms and allow extensibility to check that they are + uppercase. +*/ + +import cpp + +/** The name of an identifier, for the purpose of JSF naming conventions */ +class Name extends string { + + Name() { + exists(Declaration d | this = d.getName() ) + or exists(Namespace n | this = n.getName() ) + } + + /** + * Gets a word in this identifier. Words are just portions separated by underscores. + */ + Word getAWord() { exists(int index | result = this.splitAt("_", index)) } + + /** + * Gets the `index`th word (starting at zero) in an identifier. Words are + * just portions separated by underscores. + */ + Word getWord(int index) { result = this.splitAt("_", index) } + +} + +/** A (nonempty) word in an identifier, for JSF naming conventions */ +class Word extends string { + + Word() { exists(Name n | this = n.splitAt("_") and this != "") } + + /** + * Gets the 0-based position of this word in the identifier. + */ + int getIndex() { exists (Name n | this = n.getWord(result)) } + + /** + * Holds if this word is capitalized (for example 'Word', not 'word' + * or 'WORD'). + */ + predicate isCapitalized() { + this.prefix(1).isUppercase() and + this.suffix(1).isLowercase() + } + + /** + * Holds if this word looks like an acronym, and is written entirely + * in uppercase. This is a permissive heuristic; anything that could + * just possibly be an acronym is included. + * + * It is there for the purpose of excluding acronyms from other rules. + */ + predicate couldBeUppercaseAcronym() { + // 1-letter acronyms do not make sense + this.length() > 1 and + // few acronyms are more than 5 characters long... + this.length() <= 5 and + // Must be uppercase + this.isUppercase() + } + + /** + * Holds if this word is definitely an acronym. This is the dual of + * `couldBeUppercaseAcronym` - it underestimates rather than overestimates. + */ + predicate isDefiniteAcronym() { + // Don't look at case + exists(string s | s = this.toLowerCase() | + // A few standard acronyms + s = "url" + or s = "http" + or s = "html" + + // // + // // CUSTOMIZATION: + // // ANY ACRONYMS TO ENFORCE IN A PROJECT CAN BE ADDED HERE + // // + // eg. 'or s = "myacronym"' + ) + } + +} \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 68.ql b/cpp/ql/src/jsf/4.10 Classes/AV Rule 68.ql new file mode 100644 index 000000000000..1ef9a49bb23a --- /dev/null +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 68.ql @@ -0,0 +1,67 @@ +/** + * @name AV Rule 68 + * @description Unneeded implicitly generated member functions shall be explicitly disallowed. + * @kind problem + * @id cpp/jsf/av-rule-68 + * @problem.severity error + */ +import cpp + +/* + * Implicitly generated member functions are: + * - default constructor, created if there is no explicit constructor and + * the class is instantiated (using new or by declaring a variable of that type) + * - copy constructor, created if there is no explicit copy constructor and + * the class is copied + * - copy assignment operator if there is no explicit copy assignment operator + * and the class is assigned from + * - destructor if no destructor is defined and the object is destroyed (goes out + * of scope as an automatic variable or delete is called + */ + +/* + * Now for disallowing generated member functions. There are two ways to do this: + * define them as private, or inherit from a class that defines them as private. + * It's okay if the class actually defines those members as public; so long as they + * are not implicitly generated + */ + +predicate definesDefaultConstructor(Class c) { + exists(Constructor constr | + constr.getDeclaringType() = c and constr.isDefault()) + or definesDefaultConstructor(c.getABaseClass()) +} + +predicate definesCopyConstructor(Class c) { + exists(CopyConstructor constr | + constr.getDeclaringType() = c) + or definesCopyConstructor(c.getABaseClass()) +} + +predicate definesCopyAssignmentOperator(Class c) { + exists(CopyAssignmentOperator op | + op.getDeclaringType() = c) + or definesCopyAssignmentOperator(c.getABaseClass()) +} + +predicate definesDestructor(Class c) { + exists(Destructor op | + op.getDeclaringType() = c) + or definesDestructor(c.getABaseClass()) +} + +class ProperClass extends Class { + ProperClass() { not this instanceof Struct } +} + +/* + * The query now checks for each case whether: (a) it is not needed, (b) it is not already defined, + * and (c) it will be generated (ie for default constructors, if there is any constructor at all then default + * constructors will not be generated) + */ +from ProperClass c, string msg +where (not definesDefaultConstructor(c) and not c.hasConstructor() and msg="AV Rule 68: class " + c.getName() + " does not need a default constructor and should explicitly disallow it.") or + (not definesCopyConstructor(c) and msg="AV Rule 68: class " + c.getName() + " does not need a copy constructor and should explicitly disallow it.") or + (not definesCopyAssignmentOperator(c) and msg="AV Rule 68: class " + c.getName() + " does not need a copy assignment operator and should explicitly disallow it.") or + (not definesDestructor(c) and msg="AV Rule 68: class " + c.getName() + " does not need a destructor and should explicitly disallow it.") +select c, msg \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 69.ql b/cpp/ql/src/jsf/4.10 Classes/AV Rule 69.ql new file mode 100644 index 000000000000..1daec9c9cecc --- /dev/null +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 69.ql @@ -0,0 +1,21 @@ +/** + * @name AV Rule 69 + * @description A member function that does not affect the state of an object will be + * declared const. + * @kind problem + * @id cpp/jsf/av-rule-69 + * @problem.severity warning + */ +import cpp + +from MemberFunction mf +where mf.fromSource() and + mf.hasDefinition() and + not mf instanceof Constructor and + not exists(VariableAccess va | va.isLValue() and + va.getEnclosingFunction() = mf and + (va.getTarget() instanceof MemberVariable + or va.getTarget() instanceof GlobalVariable)) and + forall(Call c | c.getEnclosingFunction() = mf | c.isPure()) and + not mf.hasSpecifier("const") +select mf, "AV Rule 69: A member function that does not affect the state of an object will be declared const." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 70.ql b/cpp/ql/src/jsf/4.10 Classes/AV Rule 70.ql new file mode 100644 index 000000000000..af544ae46109 --- /dev/null +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 70.ql @@ -0,0 +1,36 @@ +/** + * @name AV Rule 70 + * @description A class will have friends only when a function or object requires access to + * the private elements of the class, but is unable to be a member of the class + * for logical or efficiency reasons. + * @kind problem + * @id cpp/jsf/av-rule-70 + * @problem.severity warning + */ +import cpp + +// whether b is the same as a, or b is "const a&" +predicate isTypeOrConstRef(Type a, Type b) { + a=b + or exists(ReferenceType r, SpecifiedType s | b=r and s=r.getBaseType() and s.hasSpecifier("const") and a=s.getUnspecifiedType()) +} + +// whether the first parameter of f may be subject to implicit conversions +predicate implicitConvOnFirstParm(Function f) { + exists(ImplicitConversionFunction conv | + isTypeOrConstRef(conv.getDestType(), f.getParameter(0).getUnderlyingType())) +} + +// whether f is declared as a friend by all its parameter types +predicate multiFriend(Function f) { + f.getNumberOfParameters() > 1 and + forall(Parameter p | p = f.getAParameter() | + exists(FriendDecl fd | + fd.getFriend() = f and + p.getType().refersTo(fd.getDeclaringClass()))) +} + +from FriendDecl fd +where not implicitConvOnFirstParm(fd.getFriend()) and + not multiFriend(fd.getFriend()) +select fd, "AV Rule 70: Friend declarations will only be used if the friend is unable to be a member of the class for logical or efficiency reasons." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 71.1.cpp b/cpp/ql/src/jsf/4.10 Classes/AV Rule 71.1.cpp new file mode 100644 index 000000000000..8c2fb3a4e172 --- /dev/null +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 71.1.cpp @@ -0,0 +1,35 @@ +class Base { +protected: + Resource* resource; +public: + virtual void init() { + resource = createResource(); + } + virtual void release() { + freeResource(resource); + } +}; + +class Derived: public Base { + virtual void init() { + resource = createResourceV2(); + } + virtual void release() { + freeResourceV2(resource); + } +}; + +Base::Base() { + this->init(); +} +Base::~Base() { + this->release(); +} + +int f() { + // this will call Base::Base() and then Derived::Derived(), but this->init() + // inBase::Base() will resolve to Base::init(), not Derived::init() + // The reason for this is that when Base::Base is called, the object being + // created is still of type Base (including the vtable) + Derived* d = new Derived(); +} diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 71.1.qhelp b/cpp/ql/src/jsf/4.10 Classes/AV Rule 71.1.qhelp new file mode 100644 index 000000000000..9467b5dbbb22 --- /dev/null +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 71.1.qhelp @@ -0,0 +1,57 @@ + + + + + +

    +This rule finds calls to virtual functions from a constructor or destructor +that may resolve to a different function than was intended. When instantiating +a derived class, the resolution of a virtual function call depends on the type +that defines the constructor/destructor that is currently running, not +the class that is being instantiated. This is to prevent the calling of +functions in the derived class that rely on fields declared in the derived +class. The values of such fields are undefined until the constructor of the +derived class is invoked after the constructor of the base class. Values +declared in the derived class are likewise destructed prior to +invocation of the destructor of the base class. +

    + +

    +The indicated function call is a call to a virtual function in a constructor or destructor, which will most +likely not call the intended function, or if correct would be difficult to interpret without knowledge of the class' +inheritance graph. +

    + +
    + +

    +Do not call virtual functions from the constructor or destructor. Change the virtual function in the base into a non-virtual function and pass +any required parameters from the derived classes, or simply perform initialization that requires a virtual function after construction/before destruction. +

    + +
    + + + + + + + +
  • + AV Rule 71.1, Joint Strike Fighter Air Vehicle C++ Coding Standards. Lockheed Martin Corporation, 2005. +
  • +
  • + S. Meyers. Effective C++ 3d ed. pp 48-52. Addison-Wesley Professional, 2005. +
  • +
  • + OOP50-CPP. Do not invoke virtual functions from constructors or destructors +
  • + + + + + +
    +
    diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 71.1.ql b/cpp/ql/src/jsf/4.10 Classes/AV Rule 71.1.ql new file mode 100644 index 000000000000..354c165a5e9f --- /dev/null +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 71.1.ql @@ -0,0 +1,69 @@ +/** + * @name Virtual call from constructor or destructor + * @description Virtual functions should not be invoked from a constructor or destructor of the same class. Confusingly, virtual functions are resolved statically (not dynamically) in constructors and destructors for the same class. The call should be made explicitly static by qualifying it using the scope resolution operator. + * @kind problem + * @problem.severity warning + * @precision high + * @id cpp/virtual-call-in-constructor + * @tags reliability + * readability + * language-features + */ +import cpp + +predicate thisCall(FunctionCall c) { + c.getQualifier() instanceof ThisExpr or + c.getQualifier().(PointerDereferenceExpr).getChild(0) instanceof ThisExpr +} + +predicate virtualThisCall(FunctionCall c, Function overridingFunction) { + c.isVirtual() and + thisCall(c) and + overridingFunction = c.getTarget().(VirtualFunction).getAnOverridingFunction() +} + +// Catch most cases: go into functions in the same class, but only catch direct references to "this" + +predicate nonVirtualMember(MemberFunction mf, Class c) { + mf = c.getAMemberFunction() and + not (mf instanceof Constructor) and not (mf instanceof Destructor) and + not mf.isVirtual() +} + +predicate callFromNonVirtual(MemberFunction source, Class c, MemberFunction targ) { + exists (FunctionCall fc | fc.getEnclosingFunction() = source and fc.getTarget() = targ and thisCall(fc)) and + targ = c.getAMemberFunction() and + nonVirtualMember(source, c) +} + +pragma[noopt] +predicate indirectlyCallsVirtualFunction(MemberFunction caller, Function target, Class c) { + exists (FunctionCall fc | + virtualThisCall(fc,_) and + fc.getEnclosingFunction() = caller and + fc.getTarget() = target and + nonVirtualMember(caller, c) + ) or + exists (MemberFunction mid | + indirectlyCallsVirtualFunction(mid, target, c) and + callFromNonVirtual(caller, c, mid) + ) +} + +from FunctionCall call, string explanation, Function virtFunction, Function overridingFunction +where (call.getEnclosingFunction() instanceof Constructor or call.getEnclosingFunction() instanceof Destructor) and + ( + (virtualThisCall(call, overridingFunction) and explanation = "Call to virtual function $@ which is overridden in $@. If you intend to statically call this virtual function, it should be qualified with " + virtFunction.getDeclaringType().toString() + "::.") + and virtFunction = call.getTarget() + and overridingFunction.getDeclaringType().getABaseClass+() = call.getEnclosingFunction().getDeclaringType() + or + exists(VirtualFunction target | thisCall(call) and indirectlyCallsVirtualFunction(call.getTarget(), target, _) | + explanation = "Call to function " + call.getTarget().getName() + " that calls virtual function $@ (overridden in $@)." + and virtFunction = target + and overridingFunction = target.getAnOverridingFunction() + and overridingFunction.getDeclaringType().getABaseClass+() = call.getEnclosingFunction().getDeclaringType() + ) + ) +select call, explanation, + virtFunction, virtFunction.getName(), + overridingFunction, overridingFunction.getDeclaringType().getName() diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 71.ql b/cpp/ql/src/jsf/4.10 Classes/AV Rule 71.ql new file mode 100644 index 000000000000..8a1cc05eb728 --- /dev/null +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 71.ql @@ -0,0 +1,114 @@ +/** + * @name AV Rule 71 + * @description Calls to an externally visible operation of an object, other than its constructors, shall not be allowed until the object has been fully initialized. + * @kind problem + * @id cpp/jsf/av-rule-71 + * @problem.severity error + */ +import cpp + +/* + * Implementation of the rule: partial, bug-finding approach based on the cases listed as + * examples in the JSF spec. Of those, + * + * (1) is checked by 71.1 (no virtual calls in ctors) + * (2) is approximated. We cannot know about preconditions of methods, but we make sure + * that no public or protected method that reads a member M is called before M is + * initialised in the constructor + * (3) make sure that constructors initialise all fields + * + * Incomplete on (2): take transitive chains only through public member functions. Context-insensitive + * except inside the constructor itself + */ + +// Part 1: call chains of public/protected member functions and the variables they need + +predicate memberDirectlyNeedsVariable(MemberFunction mf, Class c, MemberVariable mv) { + (mf.isPublic() or mf.isProtected()) and + c = mf.getDeclaringType() and + c = mv.getDeclaringType() and + exists(VariableAccess va | va = mv.getAnAccess() and + not exists(Assignment a | va = a.getLValue()) and + va.getEnclosingFunction() = mf) +} + +predicate memberNeedsVariable(MemberFunction mf, Class c, MemberVariable mv) { + memberDirectlyNeedsVariable(mf, c, mv) or + exists(MemberFunction mf2 | + memberNeedsVariable(mf2, c, mv) and + mf.getDeclaringType() = c and + (mf.isPublic() or mf.isProtected()) and + mf.calls(mf2) + ) +} + +// Part 2: call chains of members of the class and the variables they initialise (include private members) + +predicate memberDirectlyInitialisesVariable(MemberFunction mf, Class c, MemberVariable mv) { + c = mf.getDeclaringType() and + c = mv.getDeclaringType() and + mv.getAnAssignedValue().getEnclosingFunction() = mf +} + +predicate memberInitialisesVariable(MemberFunction mf, Class c, MemberVariable mv) { + memberDirectlyInitialisesVariable(mf, c, mv) or + exists(MemberFunction mf2 | + memberInitialisesVariable(mf2, c, mv) and + mf.getDeclaringType() = c and + mf.calls(mf2) + ) +} + +// Part 3: which variable a constructor initialises through assignment, calls or initialisers + +predicate preInitialises(Constructor c, MemberVariable mv) { + exists(ConstructorFieldInit cfi | cfi = c.getAnInitializer() | cfi.getTarget() = mv) +} + +predicate exprInitialises(Constructor c, ControlFlowNode cf, MemberVariable mv) { + cf.getControlFlowScope() = c and + ( + cf.(Assignment).getLValue() = mv.getAnAccess() + or memberInitialisesVariable(cf.(FunctionCall).getTarget(), c.getDeclaringType(), mv) + ) +} + +predicate initialises(Constructor c, MemberVariable mv) { + exprInitialises(c, _, mv) + or preInitialises(c, mv) +} + +predicate doesNotInitialise(Constructor c, MemberVariable mv) { + mv.getDeclaringType() = c.getDeclaringType() and + not initialises(c, mv) +} + +// Part 4: flow-sensitive analysis of the constructor (only) to check the order: only make public/protected +// calls that require the value of a variable after it has been initialised. + +predicate reachableWithoutInitialising(Constructor c, ControlFlowNode cf, MemberVariable mv) { + not (preInitialises(c, mv)) + and + ( + cf = c.getBlock() or + exists(ControlFlowNode mid | + reachableWithoutInitialising(c, mid, mv) and + cf = mid.getASuccessor() and + not exprInitialises(c, cf, mv) + ) + ) +} + +predicate badCall(Constructor c, FunctionCall call, MemberVariable mv) { + reachableWithoutInitialising(c, call, mv) and + memberNeedsVariable(call.getTarget(), c.getDeclaringType(), mv) +} + +// +// Query +// + +from Element e, MemberVariable mv, string message +where (doesNotInitialise(e, mv) and message = "Constructor does not initialize member variable " + mv.getName() + ".") or + (badCall(_, e, mv) and message = "Constructor calls function using " + mv.getName() + " before it is initialized.") +select e, "AV Rule 71: " + message \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 73.ql b/cpp/ql/src/jsf/4.10 Classes/AV Rule 73.ql new file mode 100644 index 000000000000..2cce8b266461 --- /dev/null +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 73.ql @@ -0,0 +1,36 @@ +/** + * @name AV Rule 73 + * @description Unnecessary default constructors shall not be defined. + * @kind problem + * @id cpp/jsf/av-rule-73 + * @problem.severity error + * @precision low + */ +import cpp + +/* bug finding approach: look for no-argument constructors that set a certain field x, + such that every member function reading x also contains a comparison involving x as + part of an if statement; the host class should not be used in an array type or as + part of an array new, it should not be used in a template instantiation, and it should + not be a virtual base class */ + +from Constructor c, MemberVariable f +where c.fromSource() and + c.isDefault() and + not c.isCompilerGenerated() and + f = c.getAWrittenVariable() and + forall(MemberFunction m, VariableAccess va | + va = f.getAnAccess() and + m = va.getEnclosingFunction() and + not m instanceof Constructor and + not va.getEnclosingStmt() instanceof IfStmt | + exists(VariableAccess va2 | va2 = f.getAnAccess() and m = va2.getEnclosingFunction() | + va != va2 and + va2.getEnclosingStmt() instanceof IfStmt)) and + not (exists(ArrayType at | at.getBaseType+() = c.getDeclaringType()) + or + exists(NewArrayExpr nae | nae.getType().(ArrayType).getBaseType+() = c.getDeclaringType()) + or + exists(ClassDerivation cd | cd.hasSpecifier("virtual") and cd.getBaseClass() = c.getDeclaringType())) +select c, "This default constructor possibly doesn't put the object in a usable state, indicated by the member variable $@.", + f, f.toString() diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 74.ql b/cpp/ql/src/jsf/4.10 Classes/AV Rule 74.ql new file mode 100644 index 000000000000..7828fc028499 --- /dev/null +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 74.ql @@ -0,0 +1,19 @@ +/** + * @name AV Rule 74 + * @description Initialization of nonstatic class members will be performed through the + * member initialization list rather than through assignment in the body of + * a constructor. + * @kind problem + * @id cpp/jsf/av-rule-74 + * @problem.severity warning + */ +import cpp + +// find assignment to non-static member variable in constructor, where that variable is +// not also initialised through the member initialisation list +from Assignment ass, Field mv, Constructor ctor +where ass.getEnclosingFunction() = ctor and + mv.getDeclaringType() = ctor.getDeclaringType() and + ass.getLValue().(Access).getTarget() = mv and + not exists(ConstructorFieldInit cfi | cfi = ctor.getAnInitializer() | cfi.getTarget() = mv) +select ass, "AV Rule 74: Nonstatic members will be initialized through the member initialization list, not through assignment." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 75.ql b/cpp/ql/src/jsf/4.10 Classes/AV Rule 75.ql new file mode 100644 index 000000000000..ba78dec64861 --- /dev/null +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 75.ql @@ -0,0 +1,12 @@ +/** + * @name AV Rule 75 + * @description Members of the initialization list shall be listed in the order in which they are declared in the class. + * @kind problem + * @id cpp/jsf/av-rule-75 + * @problem.severity error + */ +import cpp + +from Diagnostic d +where d.hasTag("out_of_order_ctor_init") +select d, "AV Rule 75: Members of the initialization list shall be listed in the order in which they are declared in the class." diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 76.ql b/cpp/ql/src/jsf/4.10 Classes/AV Rule 76.ql new file mode 100644 index 000000000000..9ab2a892b65f --- /dev/null +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 76.ql @@ -0,0 +1,39 @@ +/** + * @name AV Rule 76 + * @description A copy constructor and an assignment operator shall be declared for classes that contain pointers to data items or nontrivial destructors. If the copy constructor and assignment operators are not required, they should be explicitly disallowed. + * @kind problem + * @id cpp/jsf/av-rule-76 + * @problem.severity error + */ +import cpp + +predicate hasPointerMember(Class c) { + // Note: any function pointers are fine + exists (MemberVariable v, Type t | + v.getDeclaringType() = c and + t = v.getType().getUnderlyingType() and + (t instanceof PointerType or t instanceof PointerToMemberType) and + not (t instanceof FunctionPointerType) and + not t.(PointerToMemberType).getBaseType() instanceof RoutineType + ) +} + +class TrivialStmt extends Stmt { + TrivialStmt() { + this instanceof EmptyStmt + or this instanceof ReturnStmt and not this.(ReturnStmt).hasExpr() + } +} + +// What is a nontrivial destructor? JSF is unclear about that. We'll just +// take any nonempty destructor as nontrivial. Exclude the generated 'return' stmt +predicate hasNontrivialDestructor(Class c) { + exists (Stmt s | s = c.getDestructor().getBlock().getAStmt() | not s instanceof TrivialStmt) +} + +from Class c +where (hasPointerMember(c) or hasNontrivialDestructor(c)) and + not (c.getAMemberFunction() instanceof CopyConstructor and + c.getAMemberFunction() instanceof CopyAssignmentOperator) and + not (c instanceof Struct) +select c, "AV Rule 76: A copy constructor and an assignment operator shall be declared for classes that contain pointers to data items or nontrivial destructors." diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 77.1.cpp b/cpp/ql/src/jsf/4.10 Classes/AV Rule 77.1.cpp new file mode 100644 index 000000000000..76a6a0772e20 --- /dev/null +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 77.1.cpp @@ -0,0 +1,14 @@ +struct X { + //This struct will have a compiler-generated copy constructor + X(const X&, int); + ... +}; + +//However, if this is declared later, it will override the compiler-generated +//constructor +X::X(const X& x, int i =0) { + this-> i = i; //uses the i parameter, instead of x.i +} + +C c(1); +C cCopy = c; //would take i to be 0, instead of just copying c(1) diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 77.1.qhelp b/cpp/ql/src/jsf/4.10 Classes/AV Rule 77.1.qhelp new file mode 100644 index 000000000000..f76c96d36d08 --- /dev/null +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 77.1.qhelp @@ -0,0 +1,35 @@ + + + + + +

    +Multi-parameter constructors with default arguments can be signature-compatible with a copy constructor +when their default arguments are taken into account. An example would be a constructor for X +of the form X(const X& rhs, int i = 0). A compiler will use such a constructor as a copy +constructor in preference to the default member-wise copy constructor that it would otherwise generate. +Since this is usually not what was intended, constructors of the form often do not provide the right +semantics for copying objects of the class, making them potentially dangerous. Even when this sort of +thing has been done intentionally, it is confusing and in bad taste, and should be avoided. +

    + +
    + + +

    +Do not declare constructors with default arguments that are signature-compatible with a copy constructor +when their default arguments are taken into account. +

    + +
    + + + + + + +
  • AV Rule 77.1, Joint Strike Fighter Air Vehicle C++ Coding Standards. Lockheed Martin Corporation, 2005.
  • +
    +
    diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 77.1.ql b/cpp/ql/src/jsf/4.10 Classes/AV Rule 77.1.ql new file mode 100644 index 000000000000..c268d3c4f7f1 --- /dev/null +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 77.1.ql @@ -0,0 +1,17 @@ +/** + * @name Constructor with default arguments will be used as a copy constructor + * @description Constructors with default arguments should not be signature-compatible with a copy constructor when their default arguments are taken into account. + * @kind problem + * @problem.severity error + * @precision high + * @id cpp/constructor-used-as-copy-constructor + * @tags reliability + * readability + * language-features + */ +import cpp + +from CopyConstructor f +where f.getNumberOfParameters() > 1 + and not f.mayNotBeCopyConstructorInInstantiation() +select f, f.getName() + " is signature-compatible with a copy constructor when its default arguments are taken into account." diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 78.cpp b/cpp/ql/src/jsf/4.10 Classes/AV Rule 78.cpp new file mode 100644 index 000000000000..dcdc264cc49a --- /dev/null +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 78.cpp @@ -0,0 +1,34 @@ +class Base { +public: + Resource *p; + Base() { + p = createResource(); + } + virtual void f() { //has virtual function + //... + } + //... + ~Base() { //wrong: is non-virtual + freeResource(p); + } +}; + +class Derived: public Base { +public: + Resource *dp; + Derived() { + dp = createResource2(); + } + ~Derived() { + freeResource2(dp); + } +}; + +int f() { + Base *b = new Derived(); //creates resources for both Base::p and Derived::dp + //... + + //will only call Base::~Base(), leaking the resource dp. + //Change both destructors to virtual to ensure they are both called. + delete b; +} diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 78.qhelp b/cpp/ql/src/jsf/4.10 Classes/AV Rule 78.qhelp new file mode 100644 index 000000000000..0d25ce90c968 --- /dev/null +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 78.qhelp @@ -0,0 +1,41 @@ + + + + + +

    +This rule finds classes with virtual functions but no virtual destructor. Deleting a class without a virtual destructor will +only call the destructor of the type of the pointer being deleted. This can cause a defect if the pointer type is a base +type while the object instance is a derived type. +

    + + +
    + +

    +Make sure that all classes with virtual functions also have a virtual destructor, especially if other classes derive from them. +

    + +
    + + + + + + + +
  • + AV Rule 78, Joint Strike Fighter Air Vehicle C++ Coding Standards. Lockheed Martin Corporation, 2005. +
  • +
  • + S. Meyers. Effective C++ 3d ed. pp 40-44. Addison-Wesley Professional, 2005. +
  • +
  • + When should your destructor be virtual? +
  • + + +
    +
    diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 78.ql b/cpp/ql/src/jsf/4.10 Classes/AV Rule 78.ql new file mode 100644 index 000000000000..945c50d037cc --- /dev/null +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 78.ql @@ -0,0 +1,24 @@ +/** + * @name No virtual destructor + * @description All base classes with a virtual function should define a virtual destructor. If an application attempts to delete a derived class object through a base class pointer, the result is undefined if the base class destructor is non-virtual. + * @kind problem + * @problem.severity warning + * @precision high + * @id cpp/virtual-destructor + * @tags reliability + * readability + * language-features + */ +import cpp + +// find classes with virtual functions that have a destructor that is not virtual and for which there exists a derived class +// when calling the destructor of a derived class the destructor in the base class may not be called + +from Class c +where exists(VirtualFunction f | f.getDeclaringType() = c) + and exists(Destructor d | d.getDeclaringType() = c and + not d.isVirtual() and + not d.isDeleted() and + not d.isCompilerGenerated()) + and exists(ClassDerivation d | d.getBaseClass() = c) +select c, "Base classes with a virtual function must define a virtual destructor." diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 79.cpp b/cpp/ql/src/jsf/4.10 Classes/AV Rule 79.cpp new file mode 100644 index 000000000000..6d47fd142480 --- /dev/null +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 79.cpp @@ -0,0 +1,54 @@ +// This class opens a file but never closes it. Even its clients +// cannot close the file +class ResourceLeak { +private: + int sockfd; + FILE* file; +public: + C() { + sockfd = socket(AF_INET, SOCK_STREAM, 0); + } + + void f() { + file = fopen("foo.txt", "r"); + ... + } +}; + +// This class relies on its client to release any stream it +// allocates. Note that this means the client must have +// intimate knowledge of the implementation of the class to +// decide whether it is safe to release the stream. +class StreamPool { +private: + Stream *instance; +public: + Stream *createStream(char *name) { + if (!instance) + instance = new Stream(name); + return instance; + } +} + +// This class handles its resources, but does not do that in +// the constructor/destructor. It can be rewritten easily to +// be safer to use. +class StreamHandler { +private: + char *_name; + Stream *stream; +public: + C(char *name) { + _name = strdup(name): + } + void open() { + stream = new Stream(); + } + void close() { + delete stream; + } + ~StreamHandler() { + free(_name); + // stream should be deleted here, not in close() + } +} \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 79.qhelp b/cpp/ql/src/jsf/4.10 Classes/AV Rule 79.qhelp new file mode 100644 index 000000000000..a5b94460d43b --- /dev/null +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 79.qhelp @@ -0,0 +1,95 @@ + + + + + +

    +This rule finds resources that are allocated by a class, but not released in the destructor of that class. +Allocating a resource includes:

    +
      +
    • Allocating memory with malloc
    • +
    • Creating objects using new
    • +
    • Opening files
    • +
    • Opening network sockets
    • +
    + +

    +Resource management can be a complex task, so a standard best practice is the pattern of Resource Acquisition is Initialization +(RAII). In an RAII class, the constructor allocates all required resources, and the destructor frees all resources. +This guarantees that simply deleting an instance of the class is enough to free resources, and benefits from C++'s +automatic object lifetime management. A well-designed RAII class cannot be a source of resource leaks as long as its +lifetime is properly managed:

    +
      +
    • If it is allocated with new it should be released with delete by the client that created it
    • +
    • If its lifetime is lexical (it is only used in one function), then it is enough to declare it as a local variable + of that function (not a pointer) and the C++ runtime will ensure it is released on exit.
    • +
    + + +

    +There are two possible messages: +

    + +
      +
    • "Resource x is acquired by class C but not released in the destructor. It is released from f, + so this function may need to be called in the destructor".
    • +
    +

    +This indicates that the resource (x) is being released, just not in the destructor of the class. Typically, +it is released in a function called close, free or something similar. This does not always +indicate a resource leak, but it shows that the class is unnecessarily difficult to use because it does not conform to +the RAII pattern. +

    + +
      +
    • "Resource x is acquired by class C but not released anywhere in this class".
    • +
    +

    +This indicates that the class is allocating resources but not responsible for releasing them. This is very error-prone: +even if a class requires an explicit close operator, it should manage any resources it allocates rather than forcing +clients to manage them. In the worst case this can be a resource leak, if client code does not free the resource. +

    + +
    + +

    +If the resource is not being released at all, ensure that the class does release the resource, normally by adding +the release to the destructor of the class. This change needs to be carefully validated: client code may be relying +on the resource outliving the class that allocated it, and must be reviewed and updated if necessary. +

    + +

    +In the other case, for instance a class that has an explicit close function, the aim is to migrate the class +to a straightforward RAII pattern. This can be achieved in several steps:

    +
      +
    1. First, ensure that the close function (or its equivalent) is safe to call twice, by releasing resources + only if they have not been released before.
    2. +
    3. Next, call the close function from the destructor. This does not require changing the client code, since + it is safe to call it twice.
    4. +
    5. Migrate client code to remove direct uses of the close function, taking the opportunity to check that + the object itself is being deleted appropriately.
    6. +
    7. Finally, when possible also migrate initialization code to the constructor of the class to make it follow the RAII + pattern precisely.
    8. +
    + +
    + + + + + +
  • + AV Rule 79, Joint Strike Fighter Air Vehicle C++ Coding Standards. Lockheed Martin Corporation, 2005. +
  • +
  • + S. Meyers. Effective C++ 3d ed. pp 61-66. Addison-Wesley Professional, 2005. +
  • +
  • + Resource Acquisition Is Initialization +
  • + + +
    +
    diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 79.ql b/cpp/ql/src/jsf/4.10 Classes/AV Rule 79.ql new file mode 100644 index 000000000000..22ee7204b91c --- /dev/null +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 79.ql @@ -0,0 +1,249 @@ +/** + * @name Resource not released in destructor + * @description All resources acquired by a class should be released by its destructor. Avoid the use of the 'open / close' pattern, since C++ constructors and destructors provide a safer way to handle resource acquisition and release. Best practice in C++ is to use the 'RAII' technique: constructors allocate resources and destructors free them. + * @kind problem + * @problem.severity warning + * @precision high + * @id cpp/resource-not-released-in-destructor + * @tags efficiency + * readability + * external/cwe/cwe-404 + */ +import cpp + +// List pairs of functions that do resource acquisition/release +// Extend this to add custom function pairs. As written the query +// will only apply if the resource is the *return value* of the +// first call and a *parameter* to the second. Other cases should +// be handled differently. +predicate resourceManagementPair(string acquire, string release) { + + (acquire = "fopen" and release = "fclose") + or + (acquire = "open" and release = "close") + or + (acquire = "socket" and release = "close") + +} + +// List functions that return malloc-allocated memory. Customize +// to list your own functions there +predicate mallocFunction(Function malloc) { + malloc.hasName("malloc") or malloc.hasName("calloc") or // Not realloc: doesn't acquire it, really + malloc.hasName("strdup") +} + +private predicate isRelease(string release) { + resourceManagementPair(_, release) or + release = "free" or + release = "delete" +} + +/** + * Gets the expression `e` or a `PointerDereferenceExpr` around + * it. + */ +Expr exprOrDereference(Expr e) { + result = e or + result.(PointerDereferenceExpr).getOperand() = e +} + +/** + * Holds if the expression `e` releases expression `released`, whether directly + * or via one or more function call(s). + */ +private predicate exprReleases(Expr e, Expr released, string releaseType) { + ( + // `e` is a call to a release function and `released` is any argument + e.(FunctionCall).getTarget().getName() = releaseType and + isRelease(releaseType) and + e.(FunctionCall).getAnArgument() = released + ) or ( + // `e` is a call to `delete` and `released` is the target + e.(DeleteExpr).getExpr() = released and + releaseType = "delete" + ) or ( + // `e` is a call to `delete[]` and `released` is the target + e.(DeleteArrayExpr).getExpr() = released and + releaseType = "delete" + ) or exists(Function f, int arg | + // `e` is a call to a function that releases one of it's parameters, + // and `released` is the corresponding argument + e.(FunctionCall).getTarget() = f and + e.(FunctionCall).getArgument(arg) = released and + exprReleases(_, exprOrDereference(f.getParameter(arg).getAnAccess()), releaseType) + ) or exists(Function f, Expr innerThis | + // `e` is a call to a method that releases `this`, and `released` + // is the object that is called + e.(FunctionCall).getTarget() = f and + e.(FunctionCall).getQualifier() = exprOrDereference(released) and + innerThis.getEnclosingFunction() = f and + exprReleases(_, innerThis, releaseType) and + innerThis instanceof ThisExpr and + releaseType = "delete" + ) +} + +class Resource extends MemberVariable { + + Resource() { not isStatic() } + + // Check that an expr is somewhere in this class - does not have to be a constructor + predicate inSameClass(Expr e) { + e.getEnclosingFunction().(MemberFunction).getDeclaringType() = this.getDeclaringType() + } + + private predicate calledFromDestructor(Function f) { + (f instanceof Destructor and f.getDeclaringType() = this.getDeclaringType()) + or + exists(Function mid, FunctionCall fc | + calledFromDestructor(mid) and + fc.getEnclosingFunction() = mid and + fc.getTarget() = f and + f.getDeclaringType() = this.getDeclaringType()) + } + + predicate inDestructor(Expr e) { + exists(Function f | f = e.getEnclosingFunction() | + calledFromDestructor(f) + ) + } + + predicate acquisitionWithRequiredRelease(Expr acquire, string releaseName) { + acquire.(Assignment).getLValue() = this.getAnAccess() and + // Should be in this class, but *any* member method will do + this.inSameClass(acquire) and + // Check that it is an acquisition function and return the corresponding free + ( + exists(Function f | f = acquire.(Assignment).getRValue().(FunctionCall).getTarget() and + (resourceManagementPair(f.getName(), releaseName) or (mallocFunction(f) and (releaseName = "free" or releaseName = "delete"))) + ) + or + (acquire = this.getANew() and releaseName = "delete") + ) + } + + private Assignment getANew() { + result.getLValue() = this.getAnAccess() and + (result.getRValue() instanceof NewExpr or result.getRValue() instanceof NewArrayExpr) and + this.inSameClass(result) + } + + Expr getAReleaseExpr(string releaseName) { + exprReleases(result, this.getAnAccess(), releaseName) + } +} + +predicate unreleasedResource(Resource r, Expr acquire, File f, int acquireLine) { + // Note: there could be several release functions, because there could be + // several functions called 'fclose' for example. We want to check that + // *none* of these functions are called to release the resource + r.acquisitionWithRequiredRelease(acquire, _) and + not exists(Expr releaseExpr, string releaseName | + r.acquisitionWithRequiredRelease(acquire, releaseName) and + releaseExpr = r.getAReleaseExpr(releaseName) and + r.inDestructor(releaseExpr) + ) + and f = acquire.getFile() + and acquireLine = acquire.getLocation().getStartLine() +} + +predicate freedInSameMethod(Resource r, Expr acquire) { + unreleasedResource(r, acquire, _, _) and + exists(Expr releaseExpr, string releaseName | + r.acquisitionWithRequiredRelease(acquire, releaseName) and + releaseExpr = r.getAReleaseExpr(releaseName) and + releaseExpr.getEnclosingFunction() = acquire.getEnclosingFunction() + ) +} + +/** + * Resource `r`, acquired by `acquire`, is passed to some external + * object in the function where it's acquired. This other object + * may have taken responsibility for freeing the resource. + */ +predicate leakedInSameMethod(Resource r, Expr acquire) { + unreleasedResource(r, acquire, _, _) and + ( + exists(FunctionCall fc | + // `r` (or something computed from it) is passed to another function + // near to where it's acquired, and might be stored elsewhere. + fc.getAnArgument().getAChild*() = r.getAnAccess() and + fc.getEnclosingFunction() = acquire.getEnclosingFunction() + ) or exists(Variable v, Expr e | + // `r` (or something computed from it) is stored in another variable + // near to where it's acquired, and might be released through that + // variable. + v.getAnAssignedValue() = e and + e.getAChild*() = r.getAnAccess() and + e.getEnclosingFunction() = acquire.getEnclosingFunction() + ) or exists(FunctionCall fc | + // `this` (i.e. the class where `r` is acquired) is passed into `r` via a + // method, or the constructor. `r` may use this to register itself with + // `this` in some way, ensuring it is later deleted. + fc.getEnclosingFunction() = acquire.getEnclosingFunction() and + fc.getAnArgument() instanceof ThisExpr and + ( + fc.getQualifier() = r.getAnAccess() or // e.g. `r->setOwner(this)` + fc = acquire.getAChild*() // e.g. `r = new MyClass(this)` + ) + ) + ) +} + +pragma[noopt] predicate badRelease(Resource r, Expr acquire, Function functionCallingRelease, int line) { + unreleasedResource(r, acquire, _, _) and + exists(Expr releaseExpr, string releaseName, + Location releaseExprLocation, Function acquireFunction | + r.acquisitionWithRequiredRelease(acquire, releaseName) and + releaseExpr = r.getAReleaseExpr(releaseName) and + releaseExpr.getEnclosingFunction() = functionCallingRelease and + functionCallingRelease.getDeclaringType() = r.getDeclaringType() and + releaseExprLocation = releaseExpr.getLocation() and + line = releaseExprLocation.getStartLine() and + acquireFunction = acquire.getEnclosingFunction() and + functionCallingRelease != acquireFunction + ) +} + +Class qtObject() { result.getABaseClass*().getQualifiedName() = "QObject" } +PointerType qtObjectReference() { result.getBaseType() = qtObject() } +Constructor qtParentConstructor() { + exists(Parameter p | + p.getName() = "parent" and + p.getType() = qtObjectReference() and + result.getAParameter() = p and + result.getDeclaringType() = qtObject() + ) +} + +predicate automaticallyReleased(Assignment acquire) +{ + // sub-types of the Qt type QObject are released by their parent (if they have one) + exists(NewExpr alloc | + alloc.getType() = qtObject() and + acquire.getRValue() = alloc and + alloc.getInitializer() = qtParentConstructor().getACallToThisFunction() + ) +} + +from Resource r, Expr acquire, File f, string message +where unreleasedResource(r, acquire, f, _) and + not freedInSameMethod(r, acquire) and + not leakedInSameMethod(r, acquire) and + ( + exists(Function releaseFunction, int releaseLine | badRelease(r, acquire, releaseFunction, releaseLine) and + message = + "Resource " + r.getName() + " is acquired by class " + r.getDeclaringType().getName() + + " but not released in the destructor. It is released from " + releaseFunction.getName() + " on line " + releaseLine + + ", so this function may need to be called from the destructor." + ) + or + ( + not badRelease(r, _, _, _) and + message = "Resource " + r.getName() + " is acquired by class " + r.getDeclaringType().getName() + " but not released anywhere in this class." + ) + ) and + not automaticallyReleased(acquire) and + not r.getDeclaringType() instanceof TemplateClass // template classes may contain insufficient information for this analysis; results from instantiations will usually suffice. +select acquire, message diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 81.ql b/cpp/ql/src/jsf/4.10 Classes/AV Rule 81.ql new file mode 100644 index 000000000000..a6bba73ebd4b --- /dev/null +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 81.ql @@ -0,0 +1,78 @@ +/** + * @name AV Rule 81 + * @description The assignment operator shall handle self-assignment correctly. + * @kind problem + * @id cpp/jsf/av-rule-81 + * @problem.severity error + */ +import cpp + +/* + * Note: it's almost impossible to implement this rule fully. We take a bug-finding + * approach: try to look for some patterns that are definitely bad, while allowing + * common patterns. Passing this query by no means implies that the implementation + * is correct; though it probably shows that some thought has been put into it. + * Hopefully correct implementations also pass, but 'cunning' implementations may not. + */ + +/** A copy assignment operator taking its parameter by reference. + * For our purposes, copy assignment operators taking parameters by + * value are likely fine, since the copy already happened + */ +class ReferenceCopyAssignmentOperator extends MemberFunction { + ReferenceCopyAssignmentOperator() { + this.getName() = "operator=" and + this.getNumberOfParameters() = 1 and + exists (ReferenceType rt | + rt = this.getParameter(0).getType() and + (rt.getBaseType() = this.getDeclaringType() or + rt.getBaseType().(SpecifiedType).getBaseType() = this.getDeclaringType()) + ) + } + + Parameter getRhs() { result = this.getParameter(0) } + + /** A self-equality test: between 'this' and the RHS parameter */ + IfStmt getASelfEqualityTest() { + result.getEnclosingFunction() = this and + exists(ComparisonOperation op | + op = result.getCondition() and + op.getAnOperand() instanceof ThisExpr and + op.getAnOperand().(AddressOfExpr).getOperand().(VariableAccess).getTarget() = this.getRhs() + ) + } + + /** A call to a function called swap. Note: could be a member, + * std::swap or a function overloading std::swap (not in std::) + * so keep it simple + */ + FunctionCall getASwapCall() { + result.getEnclosingFunction() = this and + result.getTarget().getName() = "swap" + } + + /** A call to delete on a member variable */ + DeleteExpr getADeleteExpr() { + result.getEnclosingFunction() = this and + result.getExpr().(VariableAccess).getTarget().(MemberVariable).getDeclaringType() = this.getDeclaringType() + } + +} + +/** Test whether a class has a resource that needs management. Value class types are + * okay because they get their semantics from their assignment operator. Primitive + * types are fine (no management needed). Constant and reference values are okay too + * (they can't be changed anyway). All that remains are pointer types. + */ +predicate hasResource(Class c) { + exists (MemberVariable mv | + mv.getDeclaringType() = c and + mv.getType() instanceof PointerType) +} + +from ReferenceCopyAssignmentOperator op +where hasResource(op.getDeclaringType()) + and not exists(op.getASelfEqualityTest()) + and not exists(op.getASwapCall()) + and exists(op.getADeleteExpr()) +select op \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 82.cpp b/cpp/ql/src/jsf/4.10 Classes/AV Rule 82.cpp new file mode 100644 index 000000000000..b6dc6b383d5e --- /dev/null +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 82.cpp @@ -0,0 +1,12 @@ +class B { + B& operator=(const B& other) { + ... //incorrect, does not return a reference to this + } +}; + +class C { + C& operator=(const C& other) { + ... + return *this; //correct, returns reference to this + } +}; diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 82.qhelp b/cpp/ql/src/jsf/4.10 Classes/AV Rule 82.qhelp new file mode 100644 index 000000000000..ace0d6e4f985 --- /dev/null +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 82.qhelp @@ -0,0 +1,46 @@ + + + + + +

    +This rule finds assignment operator definitions that do not return a reference to this. +The reference to this is required for an assignment expression to have a meaningful value +(i.e. so that expressions like a = b = 3 would work correctly). Not returning a reference to +this would give the assignment expression an unexpected value, most likely causing such chained assignments to fail. +Both the standard library types and the built-in types behave in this manner, and the default behavior +of the assignment operator is an almost universal assumption of all developers that it is unwise to change +it considerably. If the desired behavior is significantly different from that of the default +assignment operator, it may be better to define a separate function instead. +

    + +
    + +

    +Make sure that the assignment operator overload returns a reference to this. Define a separate +function if the desired assignment operator differs significantly from the default behavior. +

    + +
    + + + + + + + +
  • + AV Rule 82, Joint Strike Fighter Air Vehicle C++ Coding Standards. Lockheed Martin Corporation, 2005. +
  • +
  • + S. Meyers. Effective C++ 3d ed. pp 52-53. Addison-Wesley Professional, 2005. +
  • +
  • + C++ Operator Overloading Guidelines +
  • + + +
    +
    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 new file mode 100644 index 000000000000..e48827979400 --- /dev/null +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 82.ql @@ -0,0 +1,86 @@ +/** + * @name Overloaded assignment does not return 'this' + * @description An assignment operator should return a reference to *this. Both the standard library types and the built-in types behave in this manner. + * @kind problem + * @problem.severity warning + * @precision high + * @id cpp/assignment-does-not-return-this + * @tags reliability + * readability + * language-features + */ +import cpp + +predicate callOnThis(FunctionCall fc) { + // `this->f(...)` + fc.getQualifier() instanceof ThisExpr or + + // `(*this).f(...)` + fc.getQualifier().(PointerDereferenceExpr).getChild(0) instanceof ThisExpr +} + +predicate pointerThis(Expr e) { + e instanceof ThisExpr or + + // `f(...)` + // (includes `this = ...`, where `=` is overloaded so a `FunctionCall`) + exists(FunctionCall fc | fc = e and callOnThis(fc) | + exists(fc.getTarget().getBlock()) implies returnsPointerThis(fc.getTarget()) + ) or + + // `this = ...` (where `=` is not overloaded, so an `AssignExpr`) + pointerThis(e.(AssignExpr).getLValue()) +} + +predicate dereferenceThis(Expr e) { + pointerThis(e.(PointerDereferenceExpr).getChild(0)) or + + // `f(...)` + // (includes `*this = ...`, where `=` is overloaded so a `FunctionCall`) + exists(FunctionCall fc | fc = e and callOnThis(fc) | + exists(fc.getTarget().getBlock()) implies returnsDereferenceThis(fc.getTarget()) + ) or + + // `*this = ...` (where `=` is not overloaded, so an `AssignExpr`) + dereferenceThis(e.(AssignExpr).getLValue()) +} + +predicate returnsPointerThis(Function f) { + forex(ReturnStmt s | s.getEnclosingFunction() = f | + // `return this` + pointerThis(s.getExpr()) + ) +} + +predicate returnsDereferenceThis(Function f) { + forex(ReturnStmt s | s.getEnclosingFunction() = f | + // `return *this` + dereferenceThis(s.getExpr()) + ) +} + +predicate assignOperatorWithWrongType(Operator op, string msg) { + op.hasName("operator=") + and exists(op.getBlock()) + and exists(Class c | + c = op.getDeclaringType() + and op.getType() = c + and msg = "Assignment operator in class " + c.getName() + " should have return type " + c.getName() + "&. Otherwise a copy is created at each call." + ) +} + +predicate assignOperatorWithWrongResult(Operator op, string msg) { + op.hasName("operator=") + and not returnsDereferenceThis(op) + and exists(op.getBlock()) + and not op.getType() instanceof VoidType + and not assignOperatorWithWrongType(op, _) + and msg = "Assignment operator in class " + op.getDeclaringType().getName() + " does not return a reference to *this." +} + +// Applies to all assignment operators, not just a copy assignment operator + +from Operator op, string msg +where assignOperatorWithWrongType(op, msg) + or assignOperatorWithWrongResult(op, msg) +select op, msg diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 85.cpp b/cpp/ql/src/jsf/4.10 Classes/AV Rule 85.cpp new file mode 100644 index 000000000000..bacb47f69e44 --- /dev/null +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 85.cpp @@ -0,0 +1,21 @@ +class B { + int x; + int y; //new field + bool operator==(const C& other) { + return this->x % 3 == 0 && this->y % 4 ==0; //updated to include the new field y + } + bool operator!=(const C& other) { + return this->x % 3 != 0; //Wrong: forgot to update to include new field y + } +}; +class C { + int x; + int y; //new field + bool operator==(const C& other) { + return this->x % 3 == 0 && this->y % 4 == 0; //updated to include the new field + } + bool operator!=(const C& other) { + return !(*this == other); //Correct, no need to update this operator + //definition when adding the new field + } +}; 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 new file mode 100644 index 000000000000..a92a3beac088 --- /dev/null +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 85.qhelp @@ -0,0 +1,40 @@ + + + + + +

    +This rule ensures that all operators with opposites (e.g. == and !=) are both defined, and +that one of them is defined in terms of the other. This just enforces the consistency of meaning +of the operators. +

    + + +

    +The indicated operator either does not have its opposite defined, or both of the definitions are not in terms of the other. +Not defining an operator in terms of its opposite is prone to mistakes, as it requires modification of both operators when +the behavior of one changes. Deliberately defining opposite operators with behaviors that are not actual logical opposites +(e.g. defining x == y if x and y are divisible by 2 and x != y if x and y are divisible by 3) +violates the almost universal assumptions developers have on the relationship of == and != and will +lead to unnecessary confusion. +

    + +
    + +

    +Make sure that both opposite operators are defined when they are overloaded, and ensure that one of the overloads +is defined in terms of the other. +

    + +
    + + + + + + +
  • AV Rule 85, Joint Strike Fighter Air Vehicle C++ Coding Standards. Lockheed Martin Corporation, 2005.
  • +
    +
    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 new file mode 100644 index 000000000000..690dd9479d28 --- /dev/null +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 85.ql @@ -0,0 +1,41 @@ +/** + * @name Opposite operator definition + * @description When two operators are opposites, both should be defined and one should be defined in terms of the other. + * @kind problem + * @id cpp/jsf/av-rule-85 + * @problem.severity warning + * @tags reliability + */ +import cpp + +predicate oppositeOperators(string op1, string op2) { + op1="operator<" and op2="operator>=" + or op1="operator<=" and op2="operator>" + or op1="operator==" and op2="operator!=" + or oppositeOperators(op2, op1) +} + +// whether op1 is implemented as the negation of op2 +/* this match is very syntactic: we simply check that op1 is defined as + !op2(_, _) */ +predicate implementedAsNegationOf(Operator op1, Operator op2) { + exists(Block b, ReturnStmt r, NotExpr n, FunctionCall c | + b = op1.getBlock() and + b.getNumStmt() = 1 and + r = b.getStmt(0) and + n = r.getExpr() and + c = n.getOperand() and + c.getTarget() = op2) +} + +from Class c, string op, string opp, Operator rator +where c.fromSource() and + oppositeOperators(op, opp) and + rator = c.getAMember() and + rator.hasName(op) and + not exists(Operator oprator | oprator = c.getAMember() and + oprator.hasName(opp) and + ( implementedAsNegationOf(rator, oprator) + or implementedAsNegationOf(oprator, rator))) +select c, "When two operators are opposites, both should be defined and one should be defined in terms of the other. Operator " + op + + " is declared on line " + rator.getLocation().getStartLine().toString() + ", but it is not defined in terms of its opposite operator " + opp + "." diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 88.1.ql b/cpp/ql/src/jsf/4.10 Classes/AV Rule 88.1.ql new file mode 100644 index 000000000000..a96697f1e5c9 --- /dev/null +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 88.1.ql @@ -0,0 +1,24 @@ +/** + * @name AV Rule 88.1 + * @description A stateful virtual base shall be explicitly declared in each derived class that accesses it. Explicitly declaring a stateful virtual base at each level in a hierarchy (where that base is used), documents that fact that no assumptions can be made with respect to the exclusive use of the data contained within the virtual base. + * @kind problem + * @id cpp/jsf/av-rule-88-1 + * @problem.severity error + */ +import cpp + +predicate derivesVirtual(Class c, Class base) +{ + exists(ClassDerivation d | d.getDerivedClass() = c and d.getBaseClass() = base + and d.hasSpecifier("virtual")) +} + +predicate derivesVirtualStar(Class c, Class base) { + derivesVirtual(c, base) or derivesVirtualStar(c.getABaseClass(), base) +} + +from Class c, Class base +where c.getABaseClass+() = base + and derivesVirtualStar(c, base) + and not derivesVirtual(c, base) +select c, "AV Rule 88.1: The virtual base " + base.getName() + " shall be explicitly declared." diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 88.cpp b/cpp/ql/src/jsf/4.10 Classes/AV Rule 88.cpp new file mode 100644 index 000000000000..b663d04bcacc --- /dev/null +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 88.cpp @@ -0,0 +1,18 @@ +//correct: +// only one protected base, +// multiple interfaces ("pure virtual" classes), and +// multiple private implementations +class C : protected Superclass, + public InterfaceA, public InterfaceB, + private ImplementationA, private ImplementationB +{ + //implementation +}; + +//wrong: multiple protected bases +class D : protected Superclass1, protected Superclass2, + public Interface, private Implementation +{ + //implementation +}; + diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 88.qhelp b/cpp/ql/src/jsf/4.10 Classes/AV Rule 88.qhelp new file mode 100644 index 000000000000..3e59ea516407 --- /dev/null +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 88.qhelp @@ -0,0 +1,54 @@ + + + + + +

    +This rule ensures that classes conform to a restricted form of multiple inheritance. +The restricted form allows inheriting from any number of interfaces (i.e. classes which contain only pure +virtual functions and only essential data), any number of private implementations, and only one protected implementation. +Allowing only one protected base class ensures that the class is only allowed to override functions from that base class +(and not accidentally override functions from other base classes). Allowing multiple private implementations allow +reuse (but not overriding) of functions from multiple classes. +

    + +

    +This restricted form of inheritance is almost identical to Java, where a class may extend (public/protected derive) only a single class, +but may implement (public derive) multiple interfaces (pure virtual classes), with the addition of mixin-like behavior provided by +the reuse of members by privately deriving from implementation classes [Bracha and Cook]. This enforces a public class hierarchy that is predictable and easy to comprehend +while giving flexibility and opportunities for code reuse for the non-public implementation. +

    + +

    +The indicated class violates the restricted form of multiple inheritance. +

    + +
    + +

    +Change the class to conform to the restricted form of multiple inheritance. +

    + +
    + + + + + + + +
  • + AV Rule 88, Joint Strike Fighter Air Vehicle C++ Coding Standards. Lockheed Martin Corporation, 2005. +
  • +
  • + G. Bracha and W. Cook. Mixin-based inheritance. OOPSLA/ECOOP '90: Proceedings of the European conference on object-oriented programming on Object-oriented programming systems, languages, and applications, pp 303-311. 1990. +
  • +
  • + S. Meyers. Effective C++ 3d ed. pp 192-198. Addison-Wesley Professional, 2005. +
  • + + +
    +
    diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 88.ql b/cpp/ql/src/jsf/4.10 Classes/AV Rule 88.ql new file mode 100644 index 000000000000..1deeb5da9eb0 --- /dev/null +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 88.ql @@ -0,0 +1,53 @@ +/** + * @name Undisciplined multiple inheritance + * @description Multiple inheritance should only be used in the following restricted form: n interfaces plus m private implementations, plus at most one protected implementation. Multiple inheritance can lead to complicated inheritance hierarchies that are difficult to comprehend and maintain. + * @kind problem + * @problem.severity recommendation + * @precision high + * @id cpp/undisciplined-multiple-inheritance + * @tags readability + */ +import cpp + +/* +In the context of this rule, an interface is specified by a class which has the following properties: +- it is intended to be an interface, +- its public methods are pure virtual functions, and +- it does not hold any data, unless those data items are small and function as part of the interface (e.g. a unique object identifier). + +An approximation of this definition is classes with pure virtual functions and less than 3 member variables. +*/ +class InterfaceClass extends Class { + InterfaceClass() { + exists(MemberFunction m | m.getDeclaringType() = this and not compgenerated(m)) + and + forall(MemberFunction m | m.getDeclaringType() = this and not compgenerated(m) | m instanceof PureVirtualFunction) + and + count(MemberVariable v | v.getDeclaringType() = this) < 3 + } +} + +class InterfaceImplementor extends Class { + InterfaceImplementor() { + exists(ClassDerivation d | d.getDerivedClass() = this and d.getBaseClass() instanceof InterfaceClass) + } + int getNumInterfaces() { + result = count(ClassDerivation d | d.getDerivedClass() = this and d.getBaseClass() instanceof InterfaceClass) + } + int getNumProtectedImplementations() { + result = count(ClassDerivation d | d.hasSpecifier("protected") and d.getDerivedClass() = this and not d.getBaseClass() instanceof InterfaceClass) + } + int getNumPrivateImplementations() { + result = count(ClassDerivation d | d.hasSpecifier("private") and d.getDerivedClass() = this and not d.getBaseClass() instanceof InterfaceClass) + } + int getNumPublicImplementations() { + result = count(ClassDerivation d | d.hasSpecifier("public") and d.getDerivedClass() = this and not d.getBaseClass() instanceof InterfaceClass) + } +} + +from InterfaceImplementor d +where d.getNumPublicImplementations() > 0 + or d.getNumProtectedImplementations() > 1 +select d, "Multiple inheritance should not be used with " + d.getNumInterfaces().toString() + + " interfaces, " + d.getNumPrivateImplementations().toString() + " private implementations, " + d.getNumProtectedImplementations().toString() + " protected implementations, and " + + d.getNumPublicImplementations().toString() + " public implementations." diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 89.qhelp b/cpp/ql/src/jsf/4.10 Classes/AV Rule 89.qhelp new file mode 100644 index 000000000000..819d9e995cb3 --- /dev/null +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 89.qhelp @@ -0,0 +1,23 @@ + + + + +

    If a base class is both virtual and non-virtual within a single hierarchy then the hierarchy is +more difficult to understand and maintain. This rule identifies any base classes that are derived +as both virtual and non-virtual in one hierarchy as violations.

    + +
    + + +
  • + AV Rule 89, Joint Strike Fighter Air Vehicle C++ Coding Standards. Lockheed Martin Corporation, 2005. +
  • +
  • + Virtual Base Classes +
  • + + +
    +
    diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 89.ql b/cpp/ql/src/jsf/4.10 Classes/AV Rule 89.ql new file mode 100644 index 000000000000..7e39363e8389 --- /dev/null +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 89.ql @@ -0,0 +1,29 @@ +/** + * @name Inconsistent virtual inheritance + * @description A base class shall not be both virtual and non-virtual in the same hierarchy. + * @kind problem + * @problem.severity error + * @precision high + * @id cpp/inconsistent-virtual-inheritance + * @tags maintainability + * readability + */ +import cpp + +predicate derivesVirtual(Class c, Class base) { + exists(ClassDerivation d | d.getDerivedClass() = c and d.getBaseClass() = base + and d.hasSpecifier("virtual")) + or derivesVirtual(c.getABaseClass(), base) +} + +predicate derivesNonVirtual(Class c, Class base) { + exists(ClassDerivation d | d.getDerivedClass() = c and d.getBaseClass() = base + and not d.hasSpecifier("virtual")) + or derivesNonVirtual(c.getABaseClass(), base) +} + +from Class c, Class base +where c.getABaseClass+() = base + and derivesVirtual(c, base) + and derivesNonVirtual(c, base) +select c, "AV Rule 89: The base class " + base.getName() + " is derived both virtual and non-virtual." diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 94.ql b/cpp/ql/src/jsf/4.10 Classes/AV Rule 94.ql new file mode 100644 index 000000000000..1bf0465486a2 --- /dev/null +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 94.ql @@ -0,0 +1,17 @@ +/** + * @name AV Rule 94 + * @description An inherited nonvirtual function shall not be redefined in a derived class. Such definitions would hide the function in the base class. + * @kind problem + * @id cpp/jsf/av-rule-94 + * @problem.severity error + */ +import cpp + +from MemberFunction f1, MemberFunction f2 +where f1 != f2 + and not (f1 instanceof VirtualFunction) + and not (f2 instanceof VirtualFunction) + and f1.getDeclaringType().getABaseClass+() = f2.getDeclaringType() + and f1.getName() = f2.getName() + and forall(Parameter p1, Parameter p2, int i | f1.getParameter(i) = p1 and f2.getParameter(i) = p2 | p1.getType() = p2.getType() ) +select f1, "AV Rule 94: An inherited nonvirtual function shall not be redefined in a derived class." diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 95.cpp b/cpp/ql/src/jsf/4.10 Classes/AV Rule 95.cpp new file mode 100644 index 000000000000..2c9a76e676ed --- /dev/null +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 95.cpp @@ -0,0 +1,20 @@ +enum Shape_color { red, green, blue }; +class Shape +{ + public: + virtual void draw (Shape_color color = green) const; + ... +} +class Circle : public Shape +{ + public: + virtual void draw (Shape_color color = red) const; + ... +} +void fun() +{ + Shape* sp; + + sp = new Circle; + sp->draw (); // Invokes Circle::draw(green) even though the default +} // parameter for Circle is red. diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 95.qhelp b/cpp/ql/src/jsf/4.10 Classes/AV Rule 95.qhelp new file mode 100644 index 000000000000..a38089c450fe --- /dev/null +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 95.qhelp @@ -0,0 +1,26 @@ + + + + +

    Inherited default parameters should not be redefined because this obscures the behavior of the code. Default values are statically bound and when they are redefined in dynamically bound function calls this reduces readability of the code, increasing the risk of introducing defects.

    + +

    For example, while C++ dynamically binds virtual methods, the default parameters of those methods are statically bound. Hence, the draw() method of the derived type (Circle), if referenced through a base type pointer (Shape *), will be invoked with the default parameters of the base type (Shape).

    + + + +
    + + + +
  • + AV Rule 95, Joint Strike Fighter Air Vehicle C++ Coding Standards. Lockheed Martin Corporation, 2005. +
  • +
  • + Scott Meyers. Item 38, Effective C++: 50 Specific Ways to Improve Your Programs and Design, 2nd Edition. Addison-Wesley, 1998. +
  • + + +
    +
    diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 95.ql b/cpp/ql/src/jsf/4.10 Classes/AV Rule 95.ql new file mode 100644 index 000000000000..a43c7b56a3bb --- /dev/null +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 95.ql @@ -0,0 +1,27 @@ +/** + * @name Redefined default parameter + * @description An inherited default parameter shall never be redefined. Default values are bound statically which is confusing when combined with dynamically bound function calls. + * @kind problem + * @problem.severity error + * @precision high + * @id cpp/redefined-default-parameter + * @tags maintainability + * readability + */ +import cpp + +from Parameter p, Parameter superP, MemberFunction subF, MemberFunction superF, int i, string subValue, string superValue +where p.hasInitializer() + and subF.getParameter(i) = p + and superP.hasInitializer() + and subF.overrides(superF) + and superF.getParameter(i) = superP + and subValue = p.getInitializer().getExpr().getValue() + and superValue = superP.getInitializer().getExpr().getValue() + and subValue != superValue +select p.getInitializer().getExpr(), + "Parameter " + p.getName() + + " redefines its default value to " + subValue + + " from the inherited default value " + superValue + + " (in " + superF.getDeclaringType().getName() + + ").\nThe default value will be resolved statically, not by dispatch, so this can cause confusion." diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 96.ql b/cpp/ql/src/jsf/4.10 Classes/AV Rule 96.ql new file mode 100644 index 000000000000..d577b649df86 --- /dev/null +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 96.ql @@ -0,0 +1,32 @@ +/** + * @name AV Rule 96 + * @description Arrays shall not be treated polymorphically. Array indexing in C/C++ is implemented as pointer arithmetic. Hence, a[i] is equivalent to a+i*SIZEOF(array element). Since derived classes are often larger than base classes, polymorphism and pointer arithmetic are not compatible techniques. + * @kind problem + * @id cpp/jsf/av-rule-96 + * @problem.severity error + */ +import cpp + +predicate compatible(Type t1, Type t2) { + t1 = t2 + or compatible(t1.(DerivedType).getBaseType().getUnspecifiedType(), t2.(DerivedType).getBaseType().getUnspecifiedType()) +} +predicate baseElement(ArrayType t, Type e) { + t.getBaseType() instanceof ArrayType and baseElement(t.getBaseType(), e) + or not t.getBaseType() instanceof ArrayType and e = t.getBaseType() +} + +from Expr e, Class cl +where e.getType() instanceof ArrayType + and exists( + FunctionCall c, int i, Function f | + c.getArgument(i) = e + and + c.getTarget() = f + and + exists(Parameter p | f.getParameter(i) = p) // varargs + and + baseElement(e.getType(), cl) // only interested in arrays with classes + and + not compatible(f.getParameter(i).getType().getUnspecifiedType(), e.getType().getUnspecifiedType())) + select e, "AV Rule 96: Arrays shall not be teated polymorphically" diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 97.1.ql b/cpp/ql/src/jsf/4.10 Classes/AV Rule 97.1.ql new file mode 100644 index 000000000000..455add085d5f --- /dev/null +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 97.1.ql @@ -0,0 +1,13 @@ +/** + * @name AV Rule 97.1 + * @description Neither operand of an equality operator (== or !=) shall be a pointer to a virtual member function. + * @kind problem + * @id cpp/jsf/av-rule-97-1 + * @problem.severity error + */ +import cpp + +from EqualityOperation e, PointerToMemberType t, Class c +where e.getAnOperand().getType() = t + and t.getClass() = c and exists(VirtualFunction f | c.getAMemberFunction() = f) +select e, "AV Rule 97.1: Neither operand of an equality operator shall be a pointer to a virtual member function." diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 97.cpp b/cpp/ql/src/jsf/4.10 Classes/AV Rule 97.cpp new file mode 100644 index 000000000000..ef3e59b02f4f --- /dev/null +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 97.cpp @@ -0,0 +1,4 @@ +void f(char buf[]) { //wrong: uses an array as a parameter type + int length = sizeof(buf); //will return sizeof(char*), not the size of the array passed + ... +} diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 97.qhelp b/cpp/ql/src/jsf/4.10 Classes/AV Rule 97.qhelp new file mode 100644 index 000000000000..102b92471d80 --- /dev/null +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 97.qhelp @@ -0,0 +1,41 @@ + + + + + + +

    +This rule finds class members (functions or data) that are or use arrays. This is particularly important for +functions with array type parameters, as these parameters are treated as pointers to the array's first element +inside the function (array decay). Assuming that it is still has the type of the array passed to the function can cause unexpected +behavior (e.g. when using the sizeof operator). +

    + + +
    + +

    +Use the Array class, or explicitly declare the variable/parameter as a pointer so there is no possibility for confusion. +

    + +
    + + + + + + + + +
  • + AV Rule 97, Joint Strike Fighter Air Vehicle C++ Coding Standards. Lockheed Martin Corporation, 2005. +
  • +
  • + comp.lang.c FAQ list · Question 6.3 +
  • + + +
    +
    diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 97.ql b/cpp/ql/src/jsf/4.10 Classes/AV Rule 97.ql new file mode 100644 index 000000000000..256b1174ccb4 --- /dev/null +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 97.ql @@ -0,0 +1,32 @@ +/** + * @name No raw arrays in interfaces + * @description Arrays should not be used in interfaces. Arrays degenerate to pointers when passed as parameters. This array decay problem has long been known to be a source of errors. Consider using std::vector or encapsulating the array in an Array class. + * @kind problem + * @problem.severity recommendation + * @precision high + * @id cpp/array-in-interface + * @tags reliability + * readability + * language-features + */ +import cpp + +predicate containsArray(Type t) { + t instanceof ArrayType + or containsArray(t.(PointerType).getBaseType()) + or containsArray(t.(SpecifiedType).getBaseType()) + or (containsArray(t.getUnderlyingType()) and not exists(TypedefType allowed | allowed = t | + allowed.hasGlobalName("jmp_buf") or + allowed.hasGlobalName("va_list") + )) +} + +predicate functionAPIViolation(MemberFunction f) { + f.isPublic() and + containsArray(f.getAParameter().getType()) +} + +from MemberFunction m +where functionAPIViolation(m) + and not m.getDeclaringType() instanceof Struct +select m, "Raw arrays should not be used in interfaces. A container class should be used instead." diff --git a/cpp/ql/src/jsf/4.11 Namespaces/AV Rule 99.ql b/cpp/ql/src/jsf/4.11 Namespaces/AV Rule 99.ql new file mode 100644 index 000000000000..920853145fcd --- /dev/null +++ b/cpp/ql/src/jsf/4.11 Namespaces/AV Rule 99.ql @@ -0,0 +1,19 @@ +/** + * @name AV Rule 99 + * @description Namespaces will not be nested more than two levels deep + * @kind problem + * @id cpp/jsf/av-rule-99 + * @problem.severity warning + */ +import cpp + +// Pick a representative file for a namespace - more than a bit dodgy, but otherwise +// the results don't show up anywhere which is less than helpful +predicate namespaceRepresentative(Namespace ns, File rep) { + rep.getAbsolutePath() = min(File f | exists(Declaration d | d = ns.getADeclaration() | d.getFile() = f) | f.getAbsolutePath()) +} + +from Namespace ns, File rep +where exists(ns.getParentNamespace().getParentNamespace().getParentNamespace()) + and namespaceRepresentative(ns, rep) +select rep, "AV Rule 99: namespace " + ns.toString() + " is nested more than two levels deep" 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 new file mode 100644 index 000000000000..2c4037770ba8 --- /dev/null +++ b/cpp/ql/src/jsf/4.12 Templates/AV Rule 104.ql @@ -0,0 +1,27 @@ +/** + * @name AV Rule 104 + * @description A template specialization shall be declared before its use. + * @kind problem + * @id cpp/jsf/av-rule-104 + * @problem.severity error + */ +import cpp + +/** A compiler warning that a template specialization occurs after + * what would have been its use. In C++ a template specialization + * only applies after it is defined; if it would have applied had + * it been defined earlier this warning is triggered. + * + * The warning is also triggered if the specialization would have + * made a use ambiguous had it occurred earlier. */ +class WarningLateTemplateSpecialization extends CompilerWarning { + + WarningLateTemplateSpecialization() { + this.getTag() = "partial_spec_after_instantiation" or + this.getTag() = "partial_spec_after_instantiation_ambiguous" + } + +} + +from WarningLateTemplateSpecialization warning +select warning, "AV Rule 104: A template specialization shall be declared before its use; " + warning.getMessage() + "." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.13 Functions/AV Rule 107.cpp b/cpp/ql/src/jsf/4.13 Functions/AV Rule 107.cpp new file mode 100644 index 000000000000..252c9967076e --- /dev/null +++ b/cpp/ql/src/jsf/4.13 Functions/AV Rule 107.cpp @@ -0,0 +1,10 @@ +int f() { + extern int other(); //scope of externs is the entire file, not just the + //block where it is declared + ... + other() +} + +int g() { + other(); //this will use the other() function declared inside f() +} diff --git a/cpp/ql/src/jsf/4.13 Functions/AV Rule 107.qhelp b/cpp/ql/src/jsf/4.13 Functions/AV Rule 107.qhelp new file mode 100644 index 000000000000..46463d413ef3 --- /dev/null +++ b/cpp/ql/src/jsf/4.13 Functions/AV Rule 107.qhelp @@ -0,0 +1,39 @@ + + + + + +

    +This rule finds functions that are declared in a block. +It is confusing to declare a function at block scope, and the visibility of the function is not what would be expected. +extern function declarations inside a block, which are allowed in C, are particularly confusing, as the scope +of the declaration is the entire file, not just the block where it was declared. +

    + + +
    + +

    +Declare the function in file scope to prevent any confusion as to its visibility. +

    + +
    + + + + + + + +
  • + AV Rule 107, Joint Strike Fighter Air Vehicle C++ Coding Standards. Lockheed Martin Corporation, 2005. +
  • +
  • + MISRA C++ Rule 3-1-2, Guidelines for the use of the C++ language in critical systems. The Motor Industry Software Reliability Associate, 2008. +
  • + + +
    +
    diff --git a/cpp/ql/src/jsf/4.13 Functions/AV Rule 107.ql b/cpp/ql/src/jsf/4.13 Functions/AV Rule 107.ql new file mode 100644 index 000000000000..223413301507 --- /dev/null +++ b/cpp/ql/src/jsf/4.13 Functions/AV Rule 107.ql @@ -0,0 +1,17 @@ +/** + * @name Function declared in block + * @description Functions should always be declared at file scope. It is confusing to declare a function at block scope, and the visibility of the function is not what would be expected. + * @kind problem + * @problem.severity warning + * @precision high + * @id cpp/function-in-block + * @tags maintainability + * readability + */ +import cpp + +from DeclStmt ds +where ds.getADeclaration() instanceof Function + and not ds.isInMacroExpansion() + and not exists(MacroInvocation mi | mi.getLocation() = ds.getADeclarationEntry().getLocation()) +select ds, "Functions should be declared at file scope, not inside blocks." diff --git a/cpp/ql/src/jsf/4.13 Functions/AV Rule 108.ql b/cpp/ql/src/jsf/4.13 Functions/AV Rule 108.ql new file mode 100644 index 000000000000..1ece9be6fe80 --- /dev/null +++ b/cpp/ql/src/jsf/4.13 Functions/AV Rule 108.ql @@ -0,0 +1,12 @@ +/** + * @name AV Rule 108 + * @description Functions with variable number of arguments shall not be used. + * @kind problem + * @id cpp/jsf/av-rule-108 + * @problem.severity error + */ +import cpp + +from Function f +where f.fromSource() and f.hasSpecifier("varargs") +select f, "AV Rule 108: Functions with variable number of arguments shall not be used." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.13 Functions/AV Rule 110.ql b/cpp/ql/src/jsf/4.13 Functions/AV Rule 110.ql new file mode 100644 index 000000000000..16e24b9697b3 --- /dev/null +++ b/cpp/ql/src/jsf/4.13 Functions/AV Rule 110.ql @@ -0,0 +1,13 @@ +/** + * @name AV Rule 110 + * @description Functions with more than 7 arguments will not be used. + * @kind problem + * @id cpp/jsf/av-rule-110 + * @problem.severity warning + */ +import cpp + +from Function f +where f.getNumberOfParameters() > 7 + and f.fromSource() +select f, "AV Rule 110: Functions with more than 7 arguments will not be used." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.13 Functions/AV Rule 111.cpp b/cpp/ql/src/jsf/4.13 Functions/AV Rule 111.cpp new file mode 100644 index 000000000000..9b229f857060 --- /dev/null +++ b/cpp/ql/src/jsf/4.13 Functions/AV Rule 111.cpp @@ -0,0 +1,7 @@ +Record* fixRecord(Record* r) { + Record myRecord = *r; + delete r; + + myRecord.fix(); + return &myRecord; //returns pointer to myRecord, which is a stack-allocated object +} 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 new file mode 100644 index 000000000000..577784582cc7 --- /dev/null +++ b/cpp/ql/src/jsf/4.13 Functions/AV Rule 111.qhelp @@ -0,0 +1,35 @@ + + + + + +

    +This rule finds return statements that return pointers to an object allocated on the stack. The lifetime +of a stack allocated memory location only lasts until the function returns, , and +the contents of that memory become undefined after that. Clearly, using a pointer to stack +memory after the function has already returned will have undefined results. +

    + + + + + +
    + +

    +Do not return pointers to stack memory locations. Instead, create an output parameter, or create a heap-allocated +buffer, copy the contents of the stack allocated memory to that buffer and return that instead. +

    + +
    + + + + + + +
  • AV Rule 111, Joint Strike Fighter Air Vehicle C++ Coding Standards. Lockheed Martin Corporation, 2005.
  • +
    +
    diff --git a/cpp/ql/src/jsf/4.13 Functions/AV Rule 111.ql b/cpp/ql/src/jsf/4.13 Functions/AV Rule 111.ql new file mode 100644 index 000000000000..edc3f8c646dd --- /dev/null +++ b/cpp/ql/src/jsf/4.13 Functions/AV Rule 111.ql @@ -0,0 +1,25 @@ +/** + * @name Return stack-allocated object + * @description A function must not return a pointer or reference to a non-static local object. + * @kind problem + * @id cpp/jsf/av-rule-111 + * @problem.severity error + * @tags reliability + */ +import semmle.code.cpp.pointsto.PointsTo + +class ReturnPointsToExpr extends PointsToExpr +{ + override predicate interesting() { + exists(ReturnStmt ret | ret.getExpr() = this) + and pointerValue(this) + } + + ReturnStmt getReturnStmt() { result.getExpr() = this } +} + +from ReturnPointsToExpr ret, LocalVariable dest +where ret.pointsTo() = dest + and ret.getReturnStmt().getParentStmt().getEnclosingFunction() = dest.getFunction() + and not(dest.isStatic()) +select ret.getReturnStmt(), "AV Rule 111: A function shall not return a pointer or reference to a non-static local object." diff --git a/cpp/ql/src/jsf/4.13 Functions/AV Rule 113.ql b/cpp/ql/src/jsf/4.13 Functions/AV Rule 113.ql new file mode 100644 index 000000000000..938e8b3a81e6 --- /dev/null +++ b/cpp/ql/src/jsf/4.13 Functions/AV Rule 113.ql @@ -0,0 +1,14 @@ +/** + * @name AV Rule 113 + * @description Functions will have a single exit point. + * @kind problem + * @id cpp/jsf/av-rule-113 + * @problem.severity warning + */ +import cpp + +from Function f, Stmt s1, Stmt s2 +where s1 = f.getAPredecessor().getEnclosingStmt() and + s2 = f.getAPredecessor().getEnclosingStmt() and + s1 != s2 +select f, "AV Rule 113: Functions will have a single exit point." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.13 Functions/AV Rule 114.cpp b/cpp/ql/src/jsf/4.13 Functions/AV Rule 114.cpp new file mode 100644 index 000000000000..101736499c5c --- /dev/null +++ b/cpp/ql/src/jsf/4.13 Functions/AV Rule 114.cpp @@ -0,0 +1,8 @@ +int f() { + ... + if (error) { + return -1; + } + ... + //wrong: no explicit return here, value returned is undefined +} 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 new file mode 100644 index 000000000000..e47368f5d664 --- /dev/null +++ b/cpp/ql/src/jsf/4.13 Functions/AV Rule 114.qhelp @@ -0,0 +1,45 @@ + + + + + +

    +This rule finds non-void functions with an execution path that does not return through an explicit +return statement. The return value in such a case is undefined. For example, in the cdecl +calling convention for x86, it would be whatever value was in the AX/EAX register when the function returned, +assuming the function had a non-float return type that can fit in a machine word. +

    + + + + + +
    + +

    Make sure that all execution paths in the function exit through an explicit return statement.

    + + +
    + + + + + + + + +
  • + AV Rule 114, Joint Strike Fighter Air Vehicle C++ Coding Standards. Lockheed Martin Corporation, 2005. +
  • +
  • + MISRA C++ Rule 8-4-3, Guidelines for the use of the C++ language in critical systems. The Motor Industry Software Reliability Associate, 2008. +
  • +
  • + The return Statement +
  • + + +
    +
    diff --git a/cpp/ql/src/jsf/4.13 Functions/AV Rule 114.ql b/cpp/ql/src/jsf/4.13 Functions/AV Rule 114.ql new file mode 100644 index 000000000000..71bab6e01be7 --- /dev/null +++ b/cpp/ql/src/jsf/4.13 Functions/AV Rule 114.ql @@ -0,0 +1,44 @@ +/** + * @name Missing return statement + * @description All functions that are not void should return a value on every exit path. + * @kind problem + * @problem.severity error + * @precision medium + * @id cpp/missing-return + * @tags reliability + * readability + * language-features + */ +import cpp + +/* This is slightly subtle: The extractor adds a dummy 'return;' statement for control paths + that fall off the end of a function. So we can simply look for non-void functions containing + a non-value carrying return. If the predecessor is a return statement it means that + the return did not return a value. (If that return was not added by the extractor but by the + programmer, we can flag it anyway, since this is arguably a bug.) */ + +predicate functionsMissingReturnStmt(Function f, ControlFlowNode blame) { + f.fromSource() and + not f.getType().getUnderlyingType().getUnspecifiedType() instanceof VoidType and + exists(ReturnStmt s | f.getAPredecessor() = s | blame = s.getAPredecessor())} + +/* If a function has a value-carrying return statement, but the extractor hit a snag + whilst parsing the value, then the control flow graph will not include the value. + As such, to avoid embarrassing false positives, we exclude any function which + wasn't perfectly extracted. */ +predicate functionImperfectlyExtracted(Function f) { + exists(CompilerError e | f.getBlock().getLocation().subsumes(e.getLocation())) + or + exists(ErrorExpr ee | ee.getEnclosingFunction() = f) +} + +from Stmt stmt, string msg +where + exists(Function f, ControlFlowNode blame | + functionsMissingReturnStmt(f, blame) and + reachable(blame) and + not functionImperfectlyExtracted(f) and + (blame = stmt or blame.(Expr).getEnclosingStmt() = stmt) and + msg = "Function " + f.getName() + " should return a value of type " + f.getType().getName() + " but does not return a value here" + ) +select stmt, msg diff --git a/cpp/ql/src/jsf/4.13 Functions/AV Rule 115.ql b/cpp/ql/src/jsf/4.13 Functions/AV Rule 115.ql new file mode 100644 index 000000000000..af81266e1d86 --- /dev/null +++ b/cpp/ql/src/jsf/4.13 Functions/AV Rule 115.ql @@ -0,0 +1,39 @@ +/** + * @name AV Rule 115 + * @description If a function returns error information, then that + * information will be tested. + * @kind problem + * @id cpp/jsf/av-rule-115 + * @problem.severity warning + */ +import cpp + +// a return statement that looks like it returns an error code +// we use an extremely simple approximation: any return statement that returns +// a literal or a symbolic constant is considered to return an error code; we +// make an exception for integral constant zero, which often indicates success +// (unlike, of course, the null pointer) +class ErrorReturn extends ReturnStmt { + ErrorReturn() { + exists(Expr e | + e = super.getExpr() and + (e instanceof Literal + or e.(VariableAccess).getTarget().isConst()) and + not (e.getValue() = "0" and e.getActualType() instanceof IntegralType)) + } +} + +// a function that has both a return statement returning an error code, and one that doesn't +class FunctionReturningErrorCode extends Function { + FunctionReturningErrorCode() { + exists(ErrorReturn er, ReturnStmt nr | + er.getEnclosingFunction() = this and + nr.getEnclosingFunction() = this and + not nr instanceof ErrorReturn) + } +} + +from FunctionReturningErrorCode frec, Call c +where c = frec.getACallToThisFunction() and + c instanceof ExprInVoidContext +select c, "AV Rule 115: If a function returns error information, it will be tested." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.13 Functions/AV Rule 119.ql b/cpp/ql/src/jsf/4.13 Functions/AV Rule 119.ql new file mode 100644 index 000000000000..e45afd248802 --- /dev/null +++ b/cpp/ql/src/jsf/4.13 Functions/AV Rule 119.ql @@ -0,0 +1,13 @@ +/** + * @name AV Rule 119 + * @description Functions shall not call themselves, either directly or indirectly (i.e. recursion shall not be allowed). + * @kind problem + * @id cpp/jsf/av-rule-119 + * @problem.severity error + */ +import cpp + +from Function f +where f.fromSource() and + f.calls+(f) +select f, "Functions shall not call theselves, either directly or indirectly" \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.14 Comments/AV Rule 126.ql b/cpp/ql/src/jsf/4.14 Comments/AV Rule 126.ql new file mode 100644 index 000000000000..975489f30f28 --- /dev/null +++ b/cpp/ql/src/jsf/4.14 Comments/AV Rule 126.ql @@ -0,0 +1,13 @@ +/** + * @name AV Rule 126 + * @description Only valid C++ style comments shall be used. + * @kind problem + * @id cpp/jsf/av-rule-126 + * @problem.severity error + */ +import cpp + +from Comment c +where c.fromSource() and + not c.getContents().regexpMatch("\\s*//.*") +select c, "AV Rule 126: Only valid C++ style comments shall be used." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.14 Comments/AV Rule 127.ql b/cpp/ql/src/jsf/4.14 Comments/AV Rule 127.ql new file mode 100644 index 000000000000..e8dfa01e54db --- /dev/null +++ b/cpp/ql/src/jsf/4.14 Comments/AV Rule 127.ql @@ -0,0 +1,14 @@ +/** + * @name AV Rule 127 + * @description Code that is not used (commented out) shall be deleted. + * @kind problem + * @id cpp/jsf/av-rule-127 + * @problem.severity error + */ + +import cpp +import external.ExternalArtifact + +from DefectExternalData d +where d.getQueryPath() = "jsf/4.14 Comments/AV Rule 127.ql" +select d, d.getMessage() diff --git a/cpp/ql/src/jsf/4.14 Comments/AV Rule 133.ql b/cpp/ql/src/jsf/4.14 Comments/AV Rule 133.ql new file mode 100644 index 000000000000..6de8e990e3e2 --- /dev/null +++ b/cpp/ql/src/jsf/4.14 Comments/AV Rule 133.ql @@ -0,0 +1,58 @@ +/** + * @name AV Rule 133 + * @description Every source file will be documented with an introductory comment that provides information on the file name, its contents, and any program-required information (eg. legal statements, copyright information, etc) + * @kind problem + * @id cpp/jsf/av-rule-133 + * @problem.severity warning + */ +import cpp + +class FirstComment extends Comment { + + FirstComment() { + not exists(Locatable l | l != this and shouldNotBeBefore(l) and l.getFile() = this.getFile() and l.getLocation().getEndLine() <= this.getLocation().getStartLine()) + } + + /* + * Test whether the comment is a reasonable start-of-file comment. + * CUSTOMIZATION POINT: INSERT STYLE RULES FOR THE START-OF-FILE COMMENT HERE + * Simple checks only - if there is a comment at the beginning of the file, that + * is pretty much enough + */ + predicate isValid() { + // At least 3 lines long: make sure it's a proper comment + this.getLocation().getEndLine() >= this.getLocation().getStartLine() + 2 + and + exists (string contents | contents = this.getContents() | + // Make sure the name of the file is included + contents.matches("%" + this.getFile().getShortName() + "%") + // Other checks could go here; for instance containing a standard copyright notice + ) + } + +} + +/** Elements that should not appear before the 'first' comment */ +predicate shouldNotBeBefore(Locatable l) { + l instanceof Comment or + l instanceof Declaration or + l instanceof PreprocessorDirective +} + +from File f, Element blame, string message +where f.fromSource() and + ( + ( + not exists(FirstComment comment | comment.getFile() = f) + and blame = f + and message = "" + ) + + or + + ( + exists(FirstComment comment | comment.getFile() = f and not comment.isValid() and blame = comment ) + and message = "The introductory comment does not match the required style rules." + ) + ) +select blame, "AV Rule 133: every source file will be documented with an introductory comment. " + message \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 135.cpp b/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 135.cpp new file mode 100644 index 000000000000..7cfe4617035d --- /dev/null +++ b/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 135.cpp @@ -0,0 +1,12 @@ +void f() { + int i = 10; + + for (int i = 0; i < 10; i++) { //the loop counter hides the variable + ... + } + + { + int i = 12; //this variable hides the variable in the outer block + ... + } +} 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 new file mode 100644 index 000000000000..33de5f3b5812 --- /dev/null +++ b/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 135.qhelp @@ -0,0 +1,29 @@ + + + + + +

    +This rule finds identifiers in an inner scope that hide (have the same name as) an identifier in an outer scope. +This should be avoided as it can cause confusion about the actual variable being used in an expression. +

    + + +
    + +

    +Change the name of the identifier so it does not hide another on an outer scope. +

    + +
    + + + + + + + +
  • AV Rule 135, Joint Strike Fighter Air Vehicle C++ Coding Standards. Lockheed Martin Corporation, 2005. +
  • 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 new file mode 100644 index 000000000000..d5fb20d4883a --- /dev/null +++ b/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 135.ql @@ -0,0 +1,39 @@ +/** + * @name Hiding identifiers + * @description Identifiers in an inner scope should not use the same name as an identifier in an outer scope, and therefore hide that identifier. + * @kind problem + * @id cpp/jsf/av-rule-135 + * @problem.severity recommendation + * @tags maintainability + */ +import cpp +import Best_Practices.Hiding.Shadowing + +// Shadowing globals by locals or parameters. Only in the same file; +// otherwise the rule is violated too often + +class LocalVariableOrParameter extends Variable { + LocalVariableOrParameter() { + this instanceof LocalVariable or this instanceof Parameter + } + + predicate shadowsGlobal(GlobalVariable gv) { + this.getName() = gv.getName() and this.getFile() = gv.getFile() + } +} + +// Shadowing parameters by locals + +predicate localShadowsParameter(LocalVariable lv, Parameter p) { + p.getName() = lv.getName() and + p.getFunction() = lv.getFunction() +} + +from Variable v, Variable shadowed +where not v.getParentScope().(Block).isInMacroExpansion() and + ( + v.(LocalVariableOrParameter).shadowsGlobal((GlobalVariable)shadowed) + or localShadowsParameter(v, shadowed) + or shadowing(v, shadowed) + ) +select v, "Identifiers in an inner scope should not hide identifiers in an outer scope" diff --git a/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 138.ql b/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 138.ql new file mode 100644 index 000000000000..0a6744aed2ce --- /dev/null +++ b/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 138.ql @@ -0,0 +1,42 @@ +/** + * @name AV Rule 138 + * @description Identifiers shall not simultaneously have both internal and external linkage in the same translation unit. + * @kind problem + * @id cpp/jsf/av-rule-138 + * @problem.severity error + */ +import cpp + +/* + * Interpretation: the standards doc is a bit unclear on this rule, making it sound like it is + * about name shadowing when rule 135 prevents shadowing anyway. This is supported by the example, + * which incorrectly points to a variable with static storage but no linkage as having internal + * linkage. + * + * We interpret it as: do not have two declarations of the same identifier *anywhere* in the same + * translation unit if one has internal linkage and the other external linkage. + * + * RESTRICTION: variable names only. + * NOTE: only applies to C++; rules for C are different. + */ + +// FOR FUTURE REFERENCE ONLY - CURRENTLY USELESS BECAUSE OF POPULATOR LIMITATIONS +// We need to have all the declarations of a variable to make this work; the extractor +// does not currently provide that. + +predicate externalLinkage(Variable v) { + v.getADeclarationEntry().hasSpecifier("extern") + or v instanceof GlobalVariable and + not v.isConst() and + not v.isStatic() +} + +predicate internalLinkage(GlobalVariable v) { + v.isStatic() + or v.isConst() and + not v.hasSpecifier("extern") +} + +from Variable v +where externalLinkage(v) and internalLinkage(v) +select v, "AV Rule 138: Identifiers shall not simultaneously have both internal and external linkage in the same translation unit." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 139.ql b/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 139.ql new file mode 100644 index 000000000000..1d8a8849ac1b --- /dev/null +++ b/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 139.ql @@ -0,0 +1,40 @@ +/** + * @name AV Rule 139 + * @description External objects will not be declared in more than one file + * @kind problem + * @id cpp/jsf/av-rule-139 + * @problem.severity warning + */ +import cpp + +predicate twoDeclarations(Declaration element, Location l1, Location l2) { + l1 = element.getADeclarationLocation() and + l2 = element.getADeclarationLocation() and + l1.getFile() != l2.getFile() and + l1 != element.getDefinitionLocation() and + l2 != element.getDefinitionLocation() +} + +predicate twoDeclarationFilesWithDifferentNames(Declaration d, string f1, string f2) { + f1 = min(string s, File f, Location l | twoDeclarations(d, l, _) and l.getFile() = f and s = f.getBaseName() | s) and + f2 = min(string s, File f, Location l | twoDeclarations(d, l, _) and l.getFile() = f and s = f.getBaseName() and s != f1 | s) +} + +predicate twoDeclarationFilesWithSameNames(Declaration d, string f1, string f2) { + f1 = min(string s, File f, Location l | twoDeclarations(d, l, _) and l.getFile() = f and s = f.toString() | s) and + f2 = min(string s, File f, Location l | twoDeclarations(d, l, _) and l.getFile() = f and s = f.toString() and s != f1 | s) +} + + +predicate twoDeclarationFilesMessage(Declaration d, string msg) { + // If we can get two files with different names, all the better for the error message + if twoDeclarationFilesWithDifferentNames(d,_,_) then + exists(string f1, string f2 | twoDeclarationFilesWithDifferentNames(d, f1, f2) and msg = "Declared in " + f1 + " and " + f2) + else + exists(string f1, string f2 | twoDeclarationFilesWithSameNames(d, f1, f2) and msg = "Declared in " + f1 + " and " + f2) +} + +from Declaration d, string msg +where twoDeclarations(d, _, _) and + twoDeclarationFilesMessage(d, msg) +select d, "AV Rule 139: external objects will not be declared in more than one file. " + msg diff --git a/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 140.cpp b/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 140.cpp new file mode 100644 index 000000000000..ecb89ff7ff15 --- /dev/null +++ b/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 140.cpp @@ -0,0 +1,16 @@ +int f() { + //wrong: register storage specifier used + register int i; //these register definitions can crowd out the + //variables x1 to x5 that are used in the complex + //operation in the inner loop, leading to slower + //performance + register int j; + + for (i = 0; i < HUGE_NUM; i++) { + for (j = 0; j < HUGE_NUM; j++) { + int x1, x2, x3, x4, x5; + //complex CPU-intensive operation that accesses + //x1 to x5 a large number of times + } + } +} 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 new file mode 100644 index 000000000000..50354e8788cd --- /dev/null +++ b/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 140.qhelp @@ -0,0 +1,31 @@ + + + + + +

    +This rule finds variables with the register storage class specifier. Modern compilers are now capable of +optimal register placement, and overriding it could lead to worse performance. +

    + + +
    + +

    +Remove the register storage class specifier. +

    + +
    + + + + + + + +
  • AV Rule 140, Joint Strike Fighter Air Vehicle C++ Coding Standards. Lockheed Martin Corporation, 2005.
  • +
  • M. Banahan, D. Brady, M. Doram. The C Book. Section 8.2.1.1. http://publications.gbdirect.co.uk/c_book/
  • +
    +
    diff --git a/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 140.ql b/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 140.ql new file mode 100644 index 000000000000..7a0e6f58661a --- /dev/null +++ b/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 140.ql @@ -0,0 +1,13 @@ +/** + * @name Register variables + * @description The register storage class specifier shall not be used. It is better and more portable to rely on the compiler to allocate registers automatically. + * @kind problem + * @id cpp/jsf/av-rule-140 + * @problem.severity warning + * @tags portability + */ +import cpp + +from Declaration d +where d.hasSpecifier("register") +select d, "The register storage class specifier should not be used; compilers can be trusted to decide whether to store a variable in a register." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.16 Initialization/AV Rule 142.ql b/cpp/ql/src/jsf/4.16 Initialization/AV Rule 142.ql new file mode 100644 index 000000000000..570a3566d4a1 --- /dev/null +++ b/cpp/ql/src/jsf/4.16 Initialization/AV Rule 142.ql @@ -0,0 +1,54 @@ +/** + * @name AV Rule 142 + * @description All variables shall be initialized before use. + * @kind problem + * @id cpp/jsf/av-rule-142 + * @problem.severity error + */ +import cpp + +// whether s defines variable v (conservative) +predicate defines(ControlFlowNode s, Variable lv) { + exists(VariableAccess va | va = s and va.getTarget() = lv and va.isLValue()) +} + +// whether s uses variable v (conservative) +predicate uses(ControlFlowNode s, Variable lv) { + exists(VariableAccess va | va = s and va.getTarget() = lv and va.isRValue() + and not va.getParent+() instanceof SizeofOperator) +} + +// whether there is a path from the declaration of lv to n such that lv is definitely not defined before n +predicate noDefPath(LocalVariable lv, ControlFlowNode n) { + n.(DeclStmt).getADeclaration() = lv and not exists(lv.getInitializer()) + or exists(ControlFlowNode p | noDefPath(lv, p) and n = p.getASuccessor() and not defines(p, lv)) +} + +predicate isAggregateType(Type t) { + t instanceof Class or t instanceof ArrayType +} + +// whether va is a use of a local variable that has not been previously defined +predicate undefinedLocalUse(VariableAccess va) { + exists(LocalVariable lv | + // it is hard to tell when a struct or array has been initialised, so we ignore them + not isAggregateType(lv.getUnderlyingType()) and + not lv.getType().hasName("va_list") and + va = lv.getAnAccess() and + noDefPath(lv, va) and + uses(va, lv)) +} + +// whether gv is a potentially uninitialised global variable +predicate uninitialisedGlobal(GlobalVariable gv) { + exists(VariableAccess va | + not isAggregateType(gv.getUnderlyingType()) and + va = gv.getAnAccess() and + va.isRValue() and + not gv.hasInitializer() and + not gv.hasSpecifier("extern")) +} + +from Element elt +where undefinedLocalUse(elt) or uninitialisedGlobal(elt) +select elt, "AV Rule 142: All variables shall be initialized before use." \ No newline at end of file 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 new file mode 100644 index 000000000000..595b414ee9be --- /dev/null +++ b/cpp/ql/src/jsf/4.16 Initialization/AV Rule 143.ql @@ -0,0 +1,45 @@ +/** + * @name AV Rule 143 + * @description Variables will not be introduced until they can be initialized + * with meaningful values. + * @kind problem + * @id cpp/jsf/av-rule-143 + * @problem.severity warning + */ +import cpp + +// whether s defines variable v (conservative) +predicate defines(VariableAccess va, Variable lv) { + va.getTarget() = lv and va.isLValue() +} + +// whether s uses variable v (conservative) +predicate uses(ControlFlowNode s, Variable lv) { + exists(VariableAccess va | va = s and va.getTarget() = lv and va.isRValue() + and not va.getParent+() instanceof SizeofOperator) +} + +// whether there is a path from the declaration of lv to n such that lv is definitely not used or defined +// before n +predicate noDefUsePath(LocalVariable lv, ControlFlowNode n) { + n.(DeclStmt).getADeclaration() = lv + or exists(ControlFlowNode p | noDefUsePath(lv, p) and + n = p.getASuccessor() and + not defines(p, lv) and + not uses(p, lv)) +} + +predicate neighbouringStmts(Stmt s1, Stmt s2) { + exists(Block b, int i | + i in [0..b.getNumStmt()-2] and + s1 = b.getStmt(i) and + s2 = b.getStmt(i+1)) +} + +from LocalVariable lv, VariableAccess def, DeclStmt d +where lv.fromSource() and + d.getADeclaration() = lv and + noDefUsePath(lv, def) and defines(def, lv) and + not neighbouringStmts(d, def.getEnclosingStmt()) and + not exists(ControlFlowNode use | noDefUsePath(lv, use) and uses(use, lv)) +select def, "AV Rule 143: Variables will not be introduced until they can be initialized with meaningful values." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.16 Initialization/AV Rule 145.cpp b/cpp/ql/src/jsf/4.16 Initialization/AV Rule 145.cpp new file mode 100644 index 000000000000..dda71c28946b --- /dev/null +++ b/cpp/ql/src/jsf/4.16 Initialization/AV Rule 145.cpp @@ -0,0 +1,23 @@ +//wrong: inconsistent initialization, only the first should be initialized, +//or all should be initialized +enum { + VALUE_SHOULD_BE_10 = 10, + VALUE_SHOULD_BE_11, + VALUE_SHOULD_BE_12, + VALUE_SHOULD_BE_20, //newly added member, but initialization was forgotten + //(would have a value of 13 instead of 20). + VALUE_SHOULD_BE_30 = 30, + VALUE_SHOULD_BE_40 = 40 +} bad_values; + +//correct: all enum values initialized +enum { + VALUE_SHOULD_BE_10 = 10, + VALUE_SHOULD_BE_11 = 11, + VALUE_SHOULD_BE_12 = 12, + VALUE_SHOULD_BE_20 = 20, //newly added member, it is less likely to forget + //putting in initialization since everything else + //is initialized + VALUE_SHOULD_BE_30 = 30, + VALUE_SHOULD_BE_40 = 40 +} good_values; diff --git a/cpp/ql/src/jsf/4.16 Initialization/AV Rule 145.qhelp b/cpp/ql/src/jsf/4.16 Initialization/AV Rule 145.qhelp new file mode 100644 index 000000000000..e207680edc43 --- /dev/null +++ b/cpp/ql/src/jsf/4.16 Initialization/AV Rule 145.qhelp @@ -0,0 +1,45 @@ + + + + + +

    +This rule finds enumerations that initialize their members inconsistently. Only the first enumeration member should be +initialized using '=', or all of them should be initialized. Inconsistent initialization of members in enumerations +can easily cause defects, especially when adding or removing members, or if the code relies on the actual integer +values of the enumeration members. +

    + + +
    + +

    +Change the enumeration so that only the first member is initialized, or initialize all the members of the enumeration. +In general, initialize all enumeration members if you actually care about their integer values. If not, it is +better to let the compiler assign values at compile time. +

    + +
    + + + + + + + + +
  • + AV Rule 145, Joint Strike Fighter Air Vehicle C++ Coding Standards. Lockheed Martin Corporation, 2005. +
  • +
  • + MISRA C++ Rule 8-5-3, Guidelines for the use of the C++ language in critical systems. The Motor Industry Software Reliability Associate, 2008. +
  • +
  • + C++ Enumeration Declarations +
  • + + +
    +
    diff --git a/cpp/ql/src/jsf/4.16 Initialization/AV Rule 145.ql b/cpp/ql/src/jsf/4.16 Initialization/AV Rule 145.ql new file mode 100644 index 000000000000..858fbf9e1234 --- /dev/null +++ b/cpp/ql/src/jsf/4.16 Initialization/AV Rule 145.ql @@ -0,0 +1,63 @@ +/** + * @name Irregular enum initialization + * @description In an enumerator list, the = construct should not be used to explicitly initialize members other than the first, unless all items are explicitly initialized. An exception is the pattern to use the last element of an enumerator list to get the number of possible values. + * @kind problem + * @problem.severity recommendation + * @precision high + * @id cpp/irregular-enum-init + * @tags reliability + * readability + * language-features + */ +import cpp + +predicate hasInitializer(EnumConstant c) { + c.getInitializer().fromSource() +} + +/** Does this have an initializer that is not just a ref to another constant in the same enum? */ +predicate hasNonReferenceInitializer(EnumConstant c) { + exists (Initializer init | + init = c.getInitializer() and + init.fromSource() and + not init.getExpr().(EnumConstantAccess).getTarget().getDeclaringEnum() = c.getDeclaringEnum() + ) +} + +predicate hasReferenceInitializer(EnumConstant c) { + exists (Initializer init | + init = c.getInitializer() and + init.fromSource() and + init.getExpr().(EnumConstantAccess).getTarget().getDeclaringEnum() = c.getDeclaringEnum() + ) +} + + +// There exists another constant whose value is implicit, but it's +// not the last one: the last value is okay to use to get the highest +// enum value automatically. It can be followed by aliases though. +predicate enumThatHasConstantWithImplicitValue(Enum e) { + exists(EnumConstant ec, int pos | + ec = e.getEnumConstant(pos) and + not hasInitializer(ec) and + exists(EnumConstant ec2, int pos2 | + ec2 = e.getEnumConstant(pos2) and + pos2 > pos and + not hasReferenceInitializer(ec2) + ) + ) +} + +from Enum e, int i +where // e is at position i, and has an explicit value in the source - but + // not just a reference to another enum constant + hasNonReferenceInitializer(e.getEnumConstant(i)) and + // but e is not the first or the last constant of the enum + i != 0 and + exists(e.getEnumConstant(i+1)) and + // and there exists another constant whose value is implicit, but it's + // not the last one: the last value is okay to use to get the highest + // enum value automatically. It can be followed by aliases though. + enumThatHasConstantWithImplicitValue(e) + +select e, "In an enumerator list, the = construct should not be used to explicitly initialize members other than the first, unless all items are explicitly initialized." diff --git a/cpp/ql/src/jsf/4.17 Types/AV Rule 147.cpp b/cpp/ql/src/jsf/4.17 Types/AV Rule 147.cpp new file mode 100644 index 000000000000..af6e84b67715 --- /dev/null +++ b/cpp/ql/src/jsf/4.17 Types/AV Rule 147.cpp @@ -0,0 +1,12 @@ +// Wrong: floating point bit implementation exposed by union with bit field. +// Endianness and different floating-point implementations across architectures +// as well as different packing methods across compilers could make this behave +// incorrectly. +typedef union { + float float_num; + struct { + unsigned sign : 1; + unsigned exp : 8; + unsigned fraction : 23; + } bits; +} floatbits; 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 new file mode 100644 index 000000000000..489ab0fe575e --- /dev/null +++ b/cpp/ql/src/jsf/4.17 Types/AV Rule 147.qhelp @@ -0,0 +1,40 @@ + + + + + +

    +This rule finds portions of code that can expose the floating point implementation of the underlying +machine. Manually manipulating the bits in the float is prone to mistakes and is unportable. Floating point +implementations can vary across architectures, and bit-field packing can differ across compilers, +making manual bit-manipulation of floats inadvisable. +

    + +

    +The bits of a floating point could be exposed by: +

    +
      +
    • casting a float pointer to a pointer of another type
    • +
    • casting a float array to a non-float pointer type
    • +
    • using a float in a union with another type
    • +
    + + +
    + +

    +Do not expose the bit contents of a float. +

    + +
    + + + + + + + +
  • AV Rule 147, Joint Strike Fighter Air Vehicle C++ Coding Standards. Lockheed Martin Corporation, 2005. +
  • diff --git a/cpp/ql/src/jsf/4.17 Types/AV Rule 147.ql b/cpp/ql/src/jsf/4.17 Types/AV Rule 147.ql new file mode 100644 index 000000000000..072076080b17 --- /dev/null +++ b/cpp/ql/src/jsf/4.17 Types/AV Rule 147.ql @@ -0,0 +1,50 @@ +/** + * @name Do not expose float representation + * @description The underlying bit representation of floating point numbers should not be used in any way by the programmer. This leads to non-portable and hard to maintain code. + * @kind problem + * @id cpp/jsf/av-rule-147 + * @problem.severity error + * @tags reliability + */ +import cpp + +/* + * The bit representation of a float can be exposed by: + * - casting a float pointer to another pointer type + * - casting a float array to a non-float pointer type + * - using a float in a union with at least one non-float member + */ + +class GeneralPointerType extends DerivedType { + GeneralPointerType() { this instanceof PointerType or this instanceof ArrayType } +} + +class InvalidFloatCastExpr extends Expr { + + InvalidFloatCastExpr() { + exists(Type src, Type dst | + src = this.getUnderlyingType().getUnspecifiedType() and + dst = this.getActualType().getUnderlyingType().getUnspecifiedType() and + src.(GeneralPointerType).getBaseType() instanceof FloatingPointType and + src.(GeneralPointerType).getBaseType() != dst.(GeneralPointerType).getBaseType() + ) + } +} + +class FloatUnion extends Union { + + FloatUnion() { + exists (MemberVariable mv | this.getAMemberVariable() = mv and mv.getType().getUnderlyingType() instanceof FloatingPointType) + and exists (MemberVariable mv | this.getAMemberVariable() = mv and not mv.getType().getUnderlyingType() instanceof FloatingPointType) + } + + MemberVariable getAFloatMember() { result = this.getAMemberVariable() and result.getType().getUnderlyingType() instanceof FloatingPointType } + +} + +from Element e, string message +where + (e instanceof InvalidFloatCastExpr and message = "Casting a float pointer to another pointer type exposes the bit representation of the float, leading to unportable code.") + or + (exists (FloatUnion fu | e = fu.getAFloatMember()) and message = "Defining a union with a float member exposes the bit representation of the float, leading to unportable code.") +select e, message diff --git a/cpp/ql/src/jsf/4.17 Types/AV Rule 148.cpp b/cpp/ql/src/jsf/4.17 Types/AV Rule 148.cpp new file mode 100644 index 000000000000..dfef08e607a5 --- /dev/null +++ b/cpp/ql/src/jsf/4.17 Types/AV Rule 148.cpp @@ -0,0 +1,28 @@ +typedef enum { + CASE_VAL1, + CASE_VAL2 +} caseVals; + +void f() { + int caseVal; + //Wrong: switch statement uses an integer + switch(caseVal) { + case 1: + //... + case 0xFF: + //... + default: + //... + } + + //Correct: switch statement uses enum. It is easier to see if a case + //has been left out, and that all cases are valid values + caseVals caseVal2; + switch (caseVal2) { + case CASE_VAL1: + //... + case CASE_VAL2: + //... + default: + } +} diff --git a/cpp/ql/src/jsf/4.17 Types/AV Rule 148.qhelp b/cpp/ql/src/jsf/4.17 Types/AV Rule 148.qhelp new file mode 100644 index 000000000000..3546970390be --- /dev/null +++ b/cpp/ql/src/jsf/4.17 Types/AV Rule 148.qhelp @@ -0,0 +1,38 @@ + + + + + +

    +This rule finds switch statements that use an integer instead of an enumeration. +Enumerations are preferred when dealing with a limited number of choices as they makes it easier +to see if a case has been left out. +

    + + +
    + +

    +Use an enumeration instead of an integer to represent a limited set of choices. +

    + +
    + + + + + + + +
  • + AV Rule 148, Joint Strike Fighter Air Vehicle C++ Coding Standards. Lockheed Martin Corporation, 2005. +
  • +
  • + C++ Switch statement +
  • + + +
    +
    diff --git a/cpp/ql/src/jsf/4.17 Types/AV Rule 148.ql b/cpp/ql/src/jsf/4.17 Types/AV Rule 148.ql new file mode 100644 index 000000000000..e76a52ce3cdb --- /dev/null +++ b/cpp/ql/src/jsf/4.17 Types/AV Rule 148.ql @@ -0,0 +1,20 @@ +/** + * @name Use of integer where enum is preferred + * @description Enumeration types should be used instead of integer types (and constants) to select from a limited series of choices. + * @kind problem + * @problem.severity warning + * @precision medium + * @id cpp/integer-used-for-enum + * @tags maintainability + * readability + * language-features + */ +import cpp + +// flag switch statements where every non-default case dispatches on an integer constant +from SwitchStmt s +where forex(SwitchCase sc | sc = s.getASwitchCase() and not sc instanceof DefaultCase | + sc.getExpr().(VariableAccess).getTarget().isConst()) + // Allow switch on character types + and not (s.getExpr().getUnderlyingType().getUnspecifiedType() instanceof CharType) +select s, "Enumeration types should be used instead of integers to select from a limited series of choices." diff --git a/cpp/ql/src/jsf/4.18 Constants/AV Rule 149.ql b/cpp/ql/src/jsf/4.18 Constants/AV Rule 149.ql new file mode 100644 index 000000000000..d09fd2de3dc2 --- /dev/null +++ b/cpp/ql/src/jsf/4.18 Constants/AV Rule 149.ql @@ -0,0 +1,13 @@ +/** + * @name AV Rule 149 + * @description Octal constants (other than zero) shall not be used. + * @kind problem + * @id cpp/jsf/av-rule-149 + * @problem.severity error + */ +import cpp + +from OctalLiteral l +where l.fromSource() and + l.getValue() != "0" +select l, "AV Rule 149: Octal constants (other than zero) shall not be used." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.18 Constants/AV Rule 150.ql b/cpp/ql/src/jsf/4.18 Constants/AV Rule 150.ql new file mode 100644 index 000000000000..542d241721a6 --- /dev/null +++ b/cpp/ql/src/jsf/4.18 Constants/AV Rule 150.ql @@ -0,0 +1,13 @@ +/** + * @name AV Rule 150 + * @description Hexadecimal constants will be represented using all uppercase letters. + * @kind problem + * @id cpp/jsf/av-rule-150 + * @problem.severity warning + */ +import cpp + +from HexLiteral l +where l.fromSource() and + l.getValueText().regexpMatch(".*[a-z].*") +select l, "AV Rule 150: Hexadecimal constants will be represented using all uppercase letters." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.18 Constants/AV Rule 151.1.cpp b/cpp/ql/src/jsf/4.18 Constants/AV Rule 151.1.cpp new file mode 100644 index 000000000000..609bfa23d6a9 --- /dev/null +++ b/cpp/ql/src/jsf/4.18 Constants/AV Rule 151.1.cpp @@ -0,0 +1,9 @@ +void f() { + char *s = "String"; //wrong: literal assigned to non-const + //this will cause a write error or corrupt other data in the data section + strcpy(s, "Another string"); + + const char* cs ="String"; //correct: literal assigned to a const + //this will cause a compile error (trying to write to a const) + strcpy(cs, "Another string"); +} 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 new file mode 100644 index 000000000000..8e3770c0b451 --- /dev/null +++ b/cpp/ql/src/jsf/4.18 Constants/AV Rule 151.1.qhelp @@ -0,0 +1,31 @@ + + + + + +

    +This rule finds string literals that are assigned to a non-const variable. String literals +should not be changed, since they are usually stored in the data section, and depending on the architecture, +writing to the data section will cause undefined behavior, such as memory corruption or memory write error. +

    + +
    + +

    +Only assign string literals to const variables. In general, using const to indicate values +that do not change is good practice, as it provides a compile-time check and when used on function parameters gives an +indication of the function's expected behavior. +

    + +
    + + + + + + + +
  • AV Rule 151.1, Joint Strike Fighter Air Vehicle C++ Coding Standards. Lockheed Martin Corporation, 2005. +
  • diff --git a/cpp/ql/src/jsf/4.18 Constants/AV Rule 151.1.ql b/cpp/ql/src/jsf/4.18 Constants/AV Rule 151.1.ql new file mode 100644 index 000000000000..8411c8ebf3fd --- /dev/null +++ b/cpp/ql/src/jsf/4.18 Constants/AV Rule 151.1.ql @@ -0,0 +1,27 @@ +/** + * @name Constant string literals + * @description A string literal must not be modified, as the result is undefined. To ensure this, only variables of type const char * or const char [] can hold string literals. + * @kind problem + * @id cpp/jsf/av-rule-151-1 + * @problem.severity error + * @tags correctness + */ +import cpp + +// Interpretation (from the example in AV151.1): +// rather than doing points-to to find writes into string literals, the +// check forbids assigning to non-const string variables, which prevents it. +// Casting the const-ness of the variable away is still possible; ideally it +// should be prevented but it doesn't seem worth the effort since it will likely +// flag another rule. + +class NonConstStringType extends DerivedType { + NonConstStringType() { + this.(ArrayType).getBaseType() instanceof CharType or + this.(PointerType).getBaseType() instanceof CharType + } +} + +from StringLiteral l +where l.getFullyConverted().getType().getUnderlyingType() instanceof NonConstStringType +select l, "A string literal must not be used as a non-const value: modifying string literals leads to undefined behavior." diff --git a/cpp/ql/src/jsf/4.18 Constants/AV Rule 151.ql b/cpp/ql/src/jsf/4.18 Constants/AV Rule 151.ql new file mode 100644 index 000000000000..e4dad7e2e001 --- /dev/null +++ b/cpp/ql/src/jsf/4.18 Constants/AV Rule 151.ql @@ -0,0 +1,21 @@ +/** + * @name AV Rule 151 + * @description Numeric values in code will not be used; symbolic values will be used instead. + * @kind problem + * @id cpp/jsf/av-rule-151 + * @problem.severity warning + */ +import cpp + +from Literal l +where l.fromSource() and + l.getType() instanceof ArithmeticType and + not l.isInMacroExpansion() and + not l.getParent() instanceof ArrayExpr and + not exists(Variable v | v.getInitializer().getExpr() = l) and + not exists(Assignment a | a.getLValue() instanceof ArrayExpr and + a.getRValue() = l and + a.getControlFlowScope() instanceof Constructor) and + not l.getValue() = "0" and + not l.getValue() = "1" +select l, "AV Rule 151: Numeric values in code will not be used; symbolic values will be used instead." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.19 Variables/AV Rule 152.ql b/cpp/ql/src/jsf/4.19 Variables/AV Rule 152.ql new file mode 100644 index 000000000000..d57a9eb79b02 --- /dev/null +++ b/cpp/ql/src/jsf/4.19 Variables/AV Rule 152.ql @@ -0,0 +1,31 @@ +/** + * @name AV Rule 152 + * @description Multiple variable declarations shall not be allowed on the same line. + * @kind problem + * @id cpp/jsf/av-rule-152 + * @problem.severity error + */ +import cpp + +predicate isNonSolitary(Declaration d) { + exists(DeclStmt ds, Variable v | + ds.fromSource() and + d = ds.getADeclaration() and + d instanceof Variable and + v = ds.getADeclaration() and + v != d) + or + exists(GlobalVariable g | g.fromSource() and + g.getLocation().getStartLine() = d.(GlobalVariable).getLocation().getStartLine() and + g.getLocation().getFile() = d.getLocation().getFile() and + g != d) + or + exists(Field f | f.fromSource() and + f.getLocation().getStartLine() = d.(Field).getLocation().getStartLine() and + f.getLocation().getFile() = d.getLocation().getFile() and + f != d) +} + +from Declaration d +where isNonSolitary(d) +select d, "AV Rule 152: Multiple variable declarations shall not be allowed on the same line." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.20 Unions and Bit Fields/AV Rule 153.ql b/cpp/ql/src/jsf/4.20 Unions and Bit Fields/AV Rule 153.ql new file mode 100644 index 000000000000..7f0298c3558e --- /dev/null +++ b/cpp/ql/src/jsf/4.20 Unions and Bit Fields/AV Rule 153.ql @@ -0,0 +1,14 @@ +/** + * @name AV Rule 153 + * @description Unions shall not be used. + * @kind problem + * @id cpp/jsf/av-rule-153 + * @problem.severity error + */ +import cpp + +// see MISRA Rule 9-5-1 + +from Union u +where u.fromSource() +select u, "AV Rule 153: Unions shall not be used." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.20 Unions and Bit Fields/AV Rule 154.cpp b/cpp/ql/src/jsf/4.20 Unions and Bit Fields/AV Rule 154.cpp new file mode 100644 index 000000000000..a95e99d10d66 --- /dev/null +++ b/cpp/ql/src/jsf/4.20 Unions and Bit Fields/AV Rule 154.cpp @@ -0,0 +1,4 @@ +struct { + short s : 4; //wrong: behavior of signed bit-field members vary across compilers + unsigned int : 24; //correct: unsigned +} bits; 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 new file mode 100644 index 000000000000..604a44b5e70d --- /dev/null +++ b/cpp/ql/src/jsf/4.20 Unions and Bit Fields/AV Rule 154.qhelp @@ -0,0 +1,39 @@ + + + + + +

    +This rule finds bit fields with members that are not explicitly declared to be unsigned. +The sign of plain char, short, int, or long bit field is implementation-specific, and declaring +them all to be unsigned removes the ambiguity and ensures portability. +

    + + +
    + +

    +Declare all members of the bit field to be unsigned. +

    + +
    + + + + + + + + +
  • + AV Rule 154, Joint Strike Fighter Air Vehicle C++ Coding Standards. Lockheed Martin Corporation, 2005. +
  • +
  • + C++ Bit Fields +
  • + + +
    +
    diff --git a/cpp/ql/src/jsf/4.20 Unions and Bit Fields/AV Rule 154.ql b/cpp/ql/src/jsf/4.20 Unions and Bit Fields/AV Rule 154.ql new file mode 100644 index 000000000000..c5cf42641961 --- /dev/null +++ b/cpp/ql/src/jsf/4.20 Unions and Bit Fields/AV Rule 154.ql @@ -0,0 +1,26 @@ +/** + * @name Possible signed bit-field member + * @description Bit fields should have explicitly unsigned integral or + * enumeration types only. For example, use `unsigned int` rather + * than `int`. It is implementation specific whether an + * `int`-typed bit field is signed, so there could be unexpected + * sign extension or overflow. + * @kind problem + * @problem.severity warning + * @precision low + * @id cpp/signed-bit-field + * @tags reliability + * readability + * language-features + * external/cwe/cwe-190 + */ +import cpp + +from BitField bf +where not bf.getType().getUnspecifiedType().(IntegralType).isUnsigned() + and not bf.getUnderlyingType() instanceof Enum + and not bf.getUnderlyingType().getUnspecifiedType() instanceof BoolType + and not bf.getType().hasName("BOOL") // At least for C programs on Windows, BOOL is a common typedef for a type representing BoolType. + and not bf.getDeclaredNumBits() = bf.getType().getSize() * 8 // If this is true, then there cannot be unsigned sign extension or overflow. + and not bf.isAnonymous() +select bf, "Bit field " + bf.getName() + " of type " + bf.getUnderlyingType().getName() + " should have explicitly unsigned integral or enumeration type." diff --git a/cpp/ql/src/jsf/4.20 Unions and Bit Fields/AV Rule 155.ql b/cpp/ql/src/jsf/4.20 Unions and Bit Fields/AV Rule 155.ql new file mode 100644 index 000000000000..ba48fe3539ab --- /dev/null +++ b/cpp/ql/src/jsf/4.20 Unions and Bit Fields/AV Rule 155.ql @@ -0,0 +1,14 @@ +/** + * @name AV Rule 155 + * @description Bit fields will not be used to pack data into a word for the + * sole purpose of saving space. + * @kind problem + * @id cpp/jsf/av-rule-155 + * @problem.severity warning + */ +import cpp + +from BitField bf +where not bf.getUnderlyingType() instanceof Enum and + not bf.getDeclaredNumBits() = 0 +select bf, "AV Rule 155: Bit fields will not be used purely to save space." diff --git a/cpp/ql/src/jsf/4.20 Unions and Bit Fields/AV Rule 156.ql b/cpp/ql/src/jsf/4.20 Unions and Bit Fields/AV Rule 156.ql new file mode 100644 index 000000000000..2dcabc930989 --- /dev/null +++ b/cpp/ql/src/jsf/4.20 Unions and Bit Fields/AV Rule 156.ql @@ -0,0 +1,18 @@ +/** + * @name AV Rule 156 + * @description All the members of a structure (or class) shall be named + * and shall only be accessed via their names. + * @kind problem + * @id cpp/jsf/av-rule-156 + * @problem.severity error + */ +import cpp + +// TODO: what about the "shall only be accessed" part? +// TODO: implement better check for namelessness (at the moment we rely on the fact +// that the frontend creates dummy names of the form "(unnamed X)" for nameless members) + +from Declaration m +where m.isMember() and m.getName().matches("(%") and + not m.(BitField).getDeclaredNumBits() = 0 +select m, "AV Rule 156: All the members of a structure (or class) shall be named and shall only be accessed via their names." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.21 Operators/AV Rule 157.ql b/cpp/ql/src/jsf/4.21 Operators/AV Rule 157.ql new file mode 100644 index 000000000000..18cbc5118a14 --- /dev/null +++ b/cpp/ql/src/jsf/4.21 Operators/AV Rule 157.ql @@ -0,0 +1,16 @@ +/** + * @name AV Rule 157 + * @description The right hand operand of a && or || operator shall not + * contain side effects. + * @kind problem + * @id cpp/jsf/av-rule-157 + * @problem.severity error + */ +import cpp + +// see MISRA Rule 5-14-1 + +from BinaryLogicalOperation blo +where blo.fromSource() and + not blo.getRightOperand().isPure() +select blo, "AV Rule 157: The right hand operand of a && or || operator shall not contain side effects." diff --git a/cpp/ql/src/jsf/4.21 Operators/AV Rule 158.ql b/cpp/ql/src/jsf/4.21 Operators/AV Rule 158.ql new file mode 100644 index 000000000000..6063f45073d3 --- /dev/null +++ b/cpp/ql/src/jsf/4.21 Operators/AV Rule 158.ql @@ -0,0 +1,17 @@ +/** + * @name AV Rule 158 + * @description The operands of a logical && or || operator shall be parenthesized + * if the operands contain binary operators. + * @kind problem + * @id cpp/jsf/av-rule-158 + * @problem.severity error + */ +import cpp + +// NB: we only check if the immediate operands are (unparenthesised) binary operations + +from BinaryLogicalOperation blo, BinaryOperation bo +where blo.fromSource() and + bo = blo.getAnOperand() and + not bo.isParenthesised() +select blo, "AV Rule 158: The operands of a logical && or || operator shall be parenthesized if the operands contain binary operators." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.21 Operators/AV Rule 159.ql b/cpp/ql/src/jsf/4.21 Operators/AV Rule 159.ql new file mode 100644 index 000000000000..ae3350aeecec --- /dev/null +++ b/cpp/ql/src/jsf/4.21 Operators/AV Rule 159.ql @@ -0,0 +1,18 @@ +/** + * @name AV Rule 159 + * @description Operators ||, &&, and unary & shall not be overloaded + * @kind problem + * @id cpp/jsf/av-rule-159 + * @problem.severity error + */ +// See More Effective C++ item 7 +// Note: Meyers allows unary & to be overloaded but not comma + +import cpp + +from Function o, string message +where (o.getName() = "operator&&" and message = "AV Rule 159: the && operator shall not be overloaded as short-circuit semantics cannot be obtained.")or + (o.getName() = "operator||" and message = "AV Rule 159: the || operator shall not be overloaded as short-circuit semantics cannot be obtained.")or + (o.getName() = "operator&" and o.getNumberOfParameters() = 1 and + message = "AV Rule 159: the unary & operator shall not be overloaded because of undefined behavior.") +select o, message \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.21 Operators/AV Rule 160.ql b/cpp/ql/src/jsf/4.21 Operators/AV Rule 160.ql new file mode 100644 index 000000000000..e34673b90706 --- /dev/null +++ b/cpp/ql/src/jsf/4.21 Operators/AV Rule 160.ql @@ -0,0 +1,39 @@ +/** + * @name AV Rule 160 + * @description An assignment expression shall be used only as the expression in an expression statement. + * @kind problem + * @id cpp/jsf/av-rule-160 + * @problem.severity error + */ +import cpp + +predicate multipleAssignExpr(Expr e) { + e instanceof AssignExpr + or exists(CommaExpr ce | ce = e | + multipleAssignExpr(ce.getLeftOperand()) and + multipleAssignExpr(ce.getRightOperand()) + ) + +} + +class MultipleAssignExpr extends Expr { + MultipleAssignExpr() { multipleAssignExpr(this) } + + Assignment getAnAssignment() { result = this.getAChild*() } + +} + +class ForStmtSideEffectExpr extends Expr { + ForStmtSideEffectExpr() { + exists (ForStmt stmt | + this = stmt.getUpdate() or + this = stmt.getInitialization().getAChild().(MultipleAssignExpr).getAnAssignment() + ) + } +} + +from AssignExpr ae +where ae.fromSource() and + not ae.getParent() instanceof ExprStmt and + not ae instanceof ForStmtSideEffectExpr +select ae, "AV Rule 160: An assignment expression shall be used only as the exprression in an expression statement." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.21 Operators/AV Rule 162.ql b/cpp/ql/src/jsf/4.21 Operators/AV Rule 162.ql new file mode 100644 index 000000000000..e9f1fa4f08f9 --- /dev/null +++ b/cpp/ql/src/jsf/4.21 Operators/AV Rule 162.ql @@ -0,0 +1,28 @@ +/** + * @name AV Rule 162 + * @description Signed and unsigned values shall not be mixed in arithmetic or comparison operations. Mixing signed and unsigned values is error prone as it subjects operations to numerous arithmetic conversion and integral promotion rules. + * @kind problem + * @id cpp/jsf/av-rule-162 + * @problem.severity error + */ +import cpp + +predicate excluded(Expr e) { + // Don't bother with literals - signed/unsigned is less meaningful there + e instanceof Literal +} + +predicate isSignedOperand(Expr e) { + e.getExplicitlyConverted().getUnderlyingType().(IntegralType).isSigned() + and not excluded(e) +} +predicate isUnsignedOperand(Expr e) { + e.getExplicitlyConverted().getUnderlyingType().(IntegralType).isUnsigned() + and not excluded(e) +} + +from BinaryOperation op +where (op instanceof BinaryArithmeticOperation or op instanceof ComparisonOperation) + and isSignedOperand(op.getAnOperand()) + and isUnsignedOperand(op.getAnOperand()) +select op, "AV Rule 162: signed and unsigned values shall not be mixed in arithmetic or comparison operations" \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.21 Operators/AV Rule 163.ql b/cpp/ql/src/jsf/4.21 Operators/AV Rule 163.ql new file mode 100644 index 000000000000..d8a5a8930a9f --- /dev/null +++ b/cpp/ql/src/jsf/4.21 Operators/AV Rule 163.ql @@ -0,0 +1,24 @@ +/** + * @name AV Rule 163 + * @description Unsigned arithmetic shall not be used. + * @kind problem + * @id cpp/jsf/av-rule-163 + * @problem.severity error + */ +import cpp + +predicate excluded(Expr e) { + // Don't bother with literals - signed/unsigned is less meaningful there + e instanceof Literal +} + +predicate isUnsignedOperand(Expr e) { + e.getUnderlyingType().(IntegralType).isUnsigned() + and not excluded(e) +} + +from BinaryOperation op +where op instanceof BinaryArithmeticOperation + and isUnsignedOperand(op.getChild(0)) + and isUnsignedOperand(op.getChild(1)) +select op, "AV Rule 163: unsigned arithmetic shall not be used" \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.21 Operators/AV Rule 164.ql b/cpp/ql/src/jsf/4.21 Operators/AV Rule 164.ql new file mode 100644 index 000000000000..5531028f4663 --- /dev/null +++ b/cpp/ql/src/jsf/4.21 Operators/AV Rule 164.ql @@ -0,0 +1,45 @@ +/** + * @name AV Rule 164 + * @description The right-hand operand of a shift operator shall lie between zero and one less than the width in bits of the left-hand operand. + * @kind problem + * @id cpp/jsf/av-rule-164 + * @problem.severity error + */ +import cpp + +/* + * We'll check the cases where the value of the RHS can be easily determined only. + * Include: + * - constants and exprs that evaluate to constants, and + * - variables that are only assigned constants + */ + +predicate possibleValue(Variable v, Expr value) { + v.getInitializer().getExpr() = value or + value = v.getAnAssignedValue() +} + +predicate constantValue(Expr e, int value) { + e.getType().getUnderlyingType().getUnspecifiedType() instanceof IntegralType and + ( + // Either the expr has a constant value + value = e.getValue().toInt() or + // The expr is a variable access and all values of the variable are constant + exists(VariableAccess va | va = e and + forall(Expr init | possibleValue(va.getTarget(), init) | constantValue(init,_)) and + exists(Expr init | possibleValue(va.getTarget(), init) | constantValue(init,value)) + ) + ) +} + +predicate violation(BinaryBitwiseOperation op, int lhsBytes, int value) { + (op instanceof LShiftExpr or op instanceof RShiftExpr) and + constantValue(op.getRightOperand(), value) and + lhsBytes = op.getLeftOperand().getType().getSize() and + (value < 0 or value >= lhsBytes * 8) +} + + +from BinaryBitwiseOperation op, int lhsBytes, int canonicalValue +where canonicalValue = min(int v | violation(op, lhsBytes, v)) +select op, "AV Rule 164: The right-hand operand (here a value is " + canonicalValue.toString() + ") of this shift shall lie between 0 and " + (lhsBytes * 8 - 1).toString() + "." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.21 Operators/AV Rule 165.cpp b/cpp/ql/src/jsf/4.21 Operators/AV Rule 165.cpp new file mode 100644 index 000000000000..7ac57c159de5 --- /dev/null +++ b/cpp/ql/src/jsf/4.21 Operators/AV Rule 165.cpp @@ -0,0 +1,8 @@ +//for this example, sizeof(short) == 2 bytes +short f(unsigned short c) { + return -c; //unsigned value being negated +} + +cout << f(100); //as expected, returns -100 +cout << f(40000U); //returns 25536 +// (negation finds the two's complement of the bit representation of 40000) 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 new file mode 100644 index 000000000000..48d385848021 --- /dev/null +++ b/cpp/ql/src/jsf/4.21 Operators/AV Rule 165.qhelp @@ -0,0 +1,31 @@ + + + + + +

    +This rule finds unsigned values that are being negated. Behavior is undefined in such cases. +Negating integer values produces the two's complement of that number, which cannot represent negative +values of large unsigned values (values where the sign bit is used) and are most likely to be interpreted +as a smaller positive integer instead. +

    + + +
    + +

    +Do not negate unsigned values. +

    + +
    + + + + + + + +
  • AV Rule 165, Joint Strike Fighter Air Vehicle C++ Coding Standards. Lockheed Martin Corporation, 2005. +
  • diff --git a/cpp/ql/src/jsf/4.21 Operators/AV Rule 165.ql b/cpp/ql/src/jsf/4.21 Operators/AV Rule 165.ql new file mode 100644 index 000000000000..ba2e452eade8 --- /dev/null +++ b/cpp/ql/src/jsf/4.21 Operators/AV Rule 165.ql @@ -0,0 +1,16 @@ +/** + * @name Negation of unsigned value + * @description The unary minus operator should not be applied to unsigned expressions - cast the expression to a signed type to avoid unexpected behavior. + * @kind problem + * @id cpp/jsf/av-rule-165 + * @problem.severity warning + * @tags reliability + */ +import cpp + +// see MISRA Rule 5-3-2 + +from UnaryMinusExpr ume +where ume.getOperand().getUnderlyingType().(IntegralType).isUnsigned() + and not ume.getOperand() instanceof Literal +select ume, "The unary minus operator should not be applied to an unsigned expression." diff --git a/cpp/ql/src/jsf/4.21 Operators/AV Rule 166.cpp b/cpp/ql/src/jsf/4.21 Operators/AV Rule 166.cpp new file mode 100644 index 000000000000..706f27224e97 --- /dev/null +++ b/cpp/ql/src/jsf/4.21 Operators/AV Rule 166.cpp @@ -0,0 +1,6 @@ +int f(void){ + int i = 0; + char arr[20]; + int size = sizeof(arr[i++]); //wrong: sizeof expression has side effect + cout << i; //would output 0 instead of 1 +} diff --git a/cpp/ql/src/jsf/4.21 Operators/AV Rule 166.qhelp b/cpp/ql/src/jsf/4.21 Operators/AV Rule 166.qhelp new file mode 100644 index 000000000000..3524312aacbd --- /dev/null +++ b/cpp/ql/src/jsf/4.21 Operators/AV Rule 166.qhelp @@ -0,0 +1,35 @@ + + + + + +

    +This rule finds sizeof expressions that have a parameter with side-effects. sizeof only +uses the type of the parameter, so the parameter will not be evaluated. In the C99 standard, using sizeof on +expressions with dynamic arrays may or may not evaluate the side-effect, so it is better to avoid it completely. +

    + +
    + +

    +Simplify the sizeof parameter to use only the subexpression that is of the type you need. +

    + +
    + + + + + +
  • + AV Rule 166, Joint Strike Fighter Air Vehicle C++ Coding Standards. Lockheed Martin Corporation, 2005. +
  • +
  • + Tutorialspoint - The C++ Programming Language: C++ sizeof Operator +
  • + + +
    +
    diff --git a/cpp/ql/src/jsf/4.21 Operators/AV Rule 166.ql b/cpp/ql/src/jsf/4.21 Operators/AV Rule 166.ql new file mode 100644 index 000000000000..019e52e0e9f8 --- /dev/null +++ b/cpp/ql/src/jsf/4.21 Operators/AV Rule 166.ql @@ -0,0 +1,15 @@ +/** + * @name Sizeof with side effects + * @description The sizeof operator should not be used on expressions that contain side effects. It is subtle whether the side effects will occur or not. + * @kind problem + * @problem.severity recommendation + * @precision high + * @id cpp/sizeof-side-effect + * @tags reliability + * correctness + */ +import cpp +import jsf.lib.section_4_21_Operators.AV_Rule_166 + +from SizeofImpureExprOperator sz +select sz, "A sizeof operator should not be used on expressions that contain side effects as the effect is confusing." diff --git a/cpp/ql/src/jsf/4.21 Operators/AV Rule 168.ql b/cpp/ql/src/jsf/4.21 Operators/AV Rule 168.ql new file mode 100644 index 000000000000..6a47217fd1ad --- /dev/null +++ b/cpp/ql/src/jsf/4.21 Operators/AV Rule 168.ql @@ -0,0 +1,15 @@ +/** + * @name AV Rule 168 + * @description The comma operator shall not be used. + * @kind problem + * @id cpp/jsf/av-rule-168 + * @problem.severity error + */ +import cpp + +// see MISRA Rule 5-18-1 + +from CommaExpr ce +where ce.fromSource() + and not exists (MacroInvocation me | ce = me.getAnAffectedElement()) +select ce, "AV Rule 168: The comma operator shall not be used." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.22 Pointers and References/AV Rule 170.ql b/cpp/ql/src/jsf/4.22 Pointers and References/AV Rule 170.ql new file mode 100644 index 000000000000..1cd0a5a5a68c --- /dev/null +++ b/cpp/ql/src/jsf/4.22 Pointers and References/AV Rule 170.ql @@ -0,0 +1,13 @@ +/** + * @name AV Rule 170 + * @description More than two levels of pointer indirection shall not be used. + * @kind problem + * @id cpp/jsf/av-rule-170 + * @problem.severity error + */ +import cpp + +from PointerType t, Element e +where t.getPointerIndirectionLevel() > 2 and + t.getATypeNameUse() = e +select e, "AV Rule 170: More than two levels of pointer indirection shall not be used." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.22 Pointers and References/AV Rule 171.ql b/cpp/ql/src/jsf/4.22 Pointers and References/AV Rule 171.ql new file mode 100644 index 000000000000..a5a282bd280b --- /dev/null +++ b/cpp/ql/src/jsf/4.22 Pointers and References/AV Rule 171.ql @@ -0,0 +1,34 @@ +/** + * @name AV Rule 171 + * @description Relational operations shall not be applied to pointer types except where + * both operands are of the same type and point to or into the same object. + * @kind problem + * @id cpp/jsf/av-rule-171 + * @problem.severity error + */ +import cpp +import semmle.code.cpp.pointsto.PointsTo + +class PointerInComparison extends PointsToExpr { + override predicate interesting() { + exists(ComparisonOperation comp | comp.getAChild() = this) and + pointerValue(this) + } + + ComparisonOperation getComparison() { + result.getAChild() = this + } +} + +predicate mayBeCompared(PointerInComparison p, PointerInComparison q) { + p.getUnderlyingType() = q.getUnderlyingType() and + p.pointsTo() = q.pointsTo() + // TODO: should handle null pointers (p and q can only be compared if either both or none can be null) + // for now, just allow comparisons with null + or p.getValue() = "0" or q.getValue() = "0" +} + +from PointerInComparison p, PointerInComparison q +where p.getComparison() = q.getComparison() and + not mayBeCompared(p, q) +select p.getComparison(), "AV Rule 171: Relational operators shall not be applied to pointer types except in very specific circumstances." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.22 Pointers and References/AV Rule 173.ql b/cpp/ql/src/jsf/4.22 Pointers and References/AV Rule 173.ql new file mode 100644 index 000000000000..9bbc4f563886 --- /dev/null +++ b/cpp/ql/src/jsf/4.22 Pointers and References/AV Rule 173.ql @@ -0,0 +1,29 @@ +/** + * @name AV Rule 173 + * @description The address of an object with automatic storage shall not be + * assigned to an object which persists after the object has ceased + * to exist. + * @kind problem + * @id cpp/jsf/av-rule-173 + * @problem.severity error + */ +import cpp + +// see MISRA Rule 7-5-2 +// TODO: this catches only the most obvious cases + +// Current caught cases: assignment x = &y (literally) where +// - y is a local +// - EITHER x is a nonlocal +// - OR x is a local defined in an enclosing scope +// - OR x has static storage duration + +from Assignment a, Variable global, Variable local +where a.fromSource() and + global.getAnAccess() = a.getLValue().(VariableAccess) and + local.getAnAccess() = a.getRValue().(AddressOfExpr).getOperand() and + local.hasSpecifier("auto") and + (not global instanceof LocalVariable + or global.getParentScope() = local.getParentScope().getParentScope+() + or global.hasSpecifier("static")) +select a, "AV Rule 173: The address of an object with automatic storage shall not be assigned to another object that may persist after the first object has ceased to exist" \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.22 Pointers and References/AV Rule 175.ql b/cpp/ql/src/jsf/4.22 Pointers and References/AV Rule 175.ql new file mode 100644 index 000000000000..b9b19ca12369 --- /dev/null +++ b/cpp/ql/src/jsf/4.22 Pointers and References/AV Rule 175.ql @@ -0,0 +1,13 @@ +/** + * @name AV Rule 175 + * @description A pointer shall not be compared to NULL or be assigned NULL; use plain 0 instead. + * @kind problem + * @id cpp/jsf/av-rule-175 + * @problem.severity error + */ +import cpp + +from NULL null +where exists (Assignment a | null = a.getRValue()) + or exists (ComparisonOperation op | null = op.getAnOperand()) +select null, "A pointer shall not be compared to NULL or be assigned NULL; use plain 0 instead." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.22 Pointers and References/AV Rule 176.ql b/cpp/ql/src/jsf/4.22 Pointers and References/AV Rule 176.ql new file mode 100644 index 000000000000..3b3369dff21d --- /dev/null +++ b/cpp/ql/src/jsf/4.22 Pointers and References/AV Rule 176.ql @@ -0,0 +1,18 @@ +/** + * @name AV Rule 176 + * @description A typedef will be used to simplify program syntax when declaring function pointers. + * @kind problem + * @id cpp/jsf/av-rule-176 + * @problem.severity warning + */ +import cpp + +predicate allowed(TypeDeclarationEntry tde, FunctionPointerType t) +{ + tde.getDeclaration().(TypedefType).getBaseType() = t +} + +from FunctionPointerType t, Locatable l +where t.getATypeNameUse() = l and + not allowed(l, t) +select l, "AV Rule 176: A typedef will be used when declaring function pointers." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.23 Type Conversions/AV Rule 178.ql b/cpp/ql/src/jsf/4.23 Type Conversions/AV Rule 178.ql new file mode 100644 index 000000000000..62bb5ac7207c --- /dev/null +++ b/cpp/ql/src/jsf/4.23 Type Conversions/AV Rule 178.ql @@ -0,0 +1,22 @@ +/** + * @name AV Rule 178 + * @description Down casting (casting from base to derived class) shall only be + * allowed through virtual functions. + * @kind problem + * @id cpp/jsf/av-rule-178 + * @problem.severity error + */ +import cpp + +predicate subtype(Type sub, Type sup) { + sub.getUnderlyingType().(Class).getABaseClass+() = sup.getUnderlyingType() +} + +from Expr e, Type declared, Type converted +where e.fromSource() and + declared = e.getUnderlyingType() and + converted = e.getActualType() and + (subtype(converted.(ReferenceType).getBaseType(), declared.(ReferenceType).getBaseType()) + or + subtype(converted.(PointerType).getBaseType(), declared.(PointerType).getBaseType())) +select e, "AV Rule 178: Down casting shall only be allowed through virtual functions." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.23 Type Conversions/AV Rule 179.ql b/cpp/ql/src/jsf/4.23 Type Conversions/AV Rule 179.ql new file mode 100644 index 000000000000..007f6c9c80a9 --- /dev/null +++ b/cpp/ql/src/jsf/4.23 Type Conversions/AV Rule 179.ql @@ -0,0 +1,18 @@ +/** + * @name AV Rule 179 + * @description A pointer to a virtual base class shall not be converted + * to a pointer to a derived class. + * @kind problem + * @id cpp/jsf/av-rule-179 + * @problem.severity error + */ +import cpp + +from Expr e, Class declared, Class converted, Class sub, Class sup +where e.fromSource() and + declared = e.getUnderlyingType().(PointerType).getBaseType().getUnderlyingType() and + converted = e.getActualType().(PointerType).getBaseType().getUnderlyingType() and + converted.getABaseClass*() = sub and + sub.hasVirtualBaseClass(sup) and + sup.getABaseClass*() = declared +select e, "AV Rule 179: A pointer to a virtual base class shall not be converted to a pointer to a derived class." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.23 Type Conversions/AV Rule 180.ql b/cpp/ql/src/jsf/4.23 Type Conversions/AV Rule 180.ql new file mode 100644 index 000000000000..7f3ecd38c998 --- /dev/null +++ b/cpp/ql/src/jsf/4.23 Type Conversions/AV Rule 180.ql @@ -0,0 +1,103 @@ +/** + * @name AV Rule 180 + * @description Implicit conversions that may result in a loss of information + * shall not be used. + * @kind problem + * @id cpp/jsf/av-rule-180 + * @problem.severity error + */ +import cpp + +class IntConstantExpr extends Expr { + + IntConstantExpr() { + this.getType() instanceof IntegralType + and exists (this.getValue()) + } + + int getIntValue() { + result = this.getValue().toInt() + } + + predicate isNegative() { + this.getIntValue() < 0 + } + + /** Find out how many (8, 16, 32, 64) bits are required. + FIXME Currently only works up to 32 bits */ + int getRequiredSizeBits() { + exists (int value | + value = this.getIntValue() and + ( + (value < -32768 and result = 32) or + (value >= -32768 and value < -128 and result = 16) or + (value >= -128 and value < 0 and result = 8) or + (value >= 0 and value < 128 and result = 7) or + (value >= 128 and value < 256 and result = 8) or + (value >= 256 and value < 32768 and result = 15) or + (value >= 32768 and value < 65536 and result = 16) or + (value >= 65536 and value <= 2147483647 and result = 31) or + (value > 2147483647 and result = 32) + ) + ) + } + +} + +class ImplicitConversion extends Expr { + ImplicitConversion() { + super.hasImplicitConversion() + } + + Type getUnderlyingSourceType() { + result = this.getUnderlyingType() + } + + Type getUnderlyingTargetType() { + result = this.getConversion().getUnderlyingType() + } + + int getSourceSize() { + if this instanceof IntConstantExpr then + result = this.(IntConstantExpr).getRequiredSizeBits() + else + result = this.getUnderlyingSourceType().getSize() * 8 + } + + int getTargetSize() { + result = this.getUnderlyingTargetType().getSize() * 8 + } + + predicate isSourceSignedInt() { + if this instanceof IntConstantExpr then + this.(IntConstantExpr).isNegative() + else + this.getUnderlyingSourceType().(IntegralType).isSigned() + } + predicate isTargetSignedInt() { + this.getUnderlyingTargetType().(IntegralType).isSigned() + } +} + +class LossyImplicitConversion extends ImplicitConversion { + LossyImplicitConversion() { + // conversion to smaller type + super.getTargetSize() < super.getSourceSize() + // signed to unsigned conversion + or super.isSourceSignedInt() and not super.isTargetSignedInt() + // unsigned to signed conversion without increasing size + or (not super.isSourceSignedInt() and super.isTargetSignedInt() + and super.getTargetSize() = super.getSourceSize()) + // floating-integral conversion + or super.getUnderlyingSourceType() instanceof FloatingPointType and + super.getUnderlyingTargetType() instanceof IntegralType + // integral-floating conversion + or super.getUnderlyingSourceType() instanceof IntegralType and + super.getUnderlyingTargetType() instanceof FloatingPointType + } +} + +from LossyImplicitConversion lic +where // Conversions to bool are always fine + not lic.getUnderlyingTargetType() instanceof BoolType +select lic, "AV Rule 180: implicit conversion from " + lic.getUnderlyingSourceType().toString() + " to " + lic.getUnderlyingTargetType().toString() + " may lose information" \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.23 Type Conversions/AV Rule 181.ql b/cpp/ql/src/jsf/4.23 Type Conversions/AV Rule 181.ql new file mode 100644 index 000000000000..cd32f6f4208d --- /dev/null +++ b/cpp/ql/src/jsf/4.23 Type Conversions/AV Rule 181.ql @@ -0,0 +1,16 @@ +/** + * @name AV Rule 181 + * @description Redundant explicit casts will not be used. + * @kind problem + * @id cpp/jsf/av-rule-181 + * @problem.severity warning + */ +import cpp + +from Expr e, Conversion c +where e.fromSource() and + c = e.getConversion() and + not c instanceof ParenthesisExpr and + not c.isCompilerGenerated() and + c.getUnderlyingType() = e.getUnderlyingType() +select c.findRootCause(), "AV Rule 181: Redundant explicit casts will not be used." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.23 Type Conversions/AV Rule 182.ql b/cpp/ql/src/jsf/4.23 Type Conversions/AV Rule 182.ql new file mode 100644 index 000000000000..0e5a345c1981 --- /dev/null +++ b/cpp/ql/src/jsf/4.23 Type Conversions/AV Rule 182.ql @@ -0,0 +1,30 @@ +/** + * @name AV Rule 182 + * @description Type casting from any type to or from pointers shall not be used. + * @kind problem + * @id cpp/jsf/av-rule-182 + * @problem.severity error + */ +import cpp + +class NullPointer extends Expr { + NullPointer() { + this.getValue() = "0" and this.getType() instanceof IntegralType + or + this instanceof NULL + } +} + +from Expr e, Type t1, Type t2 +where t1 = e.getUnderlyingType().getUnspecifiedType() and + t2 = e.getActualType().getUnderlyingType().getUnspecifiedType() and + t1 != t2 and + (t1 instanceof PointerType or t2 instanceof PointerType) and + not (t2 instanceof VoidPointerType and t1 instanceof PointerType) and + // Conversion to bool type is always fine + not (t2 instanceof BoolType) and + // Ignore assigning NULL to a pointer + not e instanceof NullPointer and + // Allow array -> pointer conversion + not t1.(ArrayType).getBaseType() = t2.(PointerType).getBaseType() +select e, "AV Rule 182: illegal cast from type " + t1.toString() + " to type " + t2.toString() + ". Casting to or from pointers shall not be used" \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.23 Type Conversions/AV Rule 184.ql b/cpp/ql/src/jsf/4.23 Type Conversions/AV Rule 184.ql new file mode 100644 index 000000000000..b72c3803f062 --- /dev/null +++ b/cpp/ql/src/jsf/4.23 Type Conversions/AV Rule 184.ql @@ -0,0 +1,31 @@ +/** + * @name AV Rule 184 + * @description Floating point numbers shall not be converted to integers, + * unless such a conversion is a specified algorithmic + * requirement or is necessary for a hardware interface. + * @kind problem + * @id cpp/jsf/av-rule-184 + * @problem.severity error + */ +import cpp + +// extend to allow certain conversions +predicate necessaryFloatToIntConversion(Expr e) { + none() +} + +predicate badConversion(Expr e) { + e.fromSource() and + e.getUnderlyingType() instanceof FloatingPointType and + e.getActualType() instanceof IntegralType and + not necessaryFloatToIntConversion(e) +} + +from Expr e +where badConversion(e) and + // Only include outermost matches; no need to be transitive + // (should report an expr if a distant parent is a violation + // but the exprs in between are fine). Exclude brackets + not badConversion(e.getParent()) and + not e instanceof ParenthesisExpr +select e, "AV Rule 184: Floating point numbers shall not be converted to integers." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.23 Type Conversions/AV Rule 185.ql b/cpp/ql/src/jsf/4.23 Type Conversions/AV Rule 185.ql new file mode 100644 index 000000000000..5a276d498c7e --- /dev/null +++ b/cpp/ql/src/jsf/4.23 Type Conversions/AV Rule 185.ql @@ -0,0 +1,17 @@ +/** + * @name AV Rule 185 + * @description C++ style casts (const_cast, reinterpret_cast, and static_cast) shall be used + * instead of the traditional C-style cast. + * @kind problem + * @id cpp/jsf/av-rule-185 + * @problem.severity error + */ +import cpp + +from Cast c +where c.fromSource() and + c.getFile() instanceof CppFile and // Ignore C-style casts in C files + not c instanceof ConstCast and + not c instanceof ReinterpretCast and + not c instanceof StaticCast +select c.findRootCause(), "AV Rule 184: C++ style casts shall be used instead of the traditional C-style casts." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 186.ql b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 186.ql new file mode 100644 index 000000000000..2ee5c35140ae --- /dev/null +++ b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 186.ql @@ -0,0 +1,29 @@ +/** + * @name AV Rule 186 + * @description There shall be no unreachable code. + * @kind problem + * @id cpp/jsf/av-rule-186 + * @problem.severity error + */ +import cpp + +// whether f is to be considered an API entry point, and hence reachable by default +predicate isAPI(Function f) { + f.hasName("main") + or f.(MemberFunction).hasSpecifier("public") +} + +predicate unusedFunction(Function f) { + not isAPI(f) and + not exists(FunctionCall c | c.getTarget() = f) and + not exists(Access acc | acc.getTarget() = f) and + f.hasDefinition() +} + +predicate unreachableStmt(Stmt s) { + not s.getControlFlowScope().getBlock().getASuccessor*() = s +} + +from ControlFlowNode n +where unreachableStmt(n) or unusedFunction(n) +select n, "AV Rule 186: There shall be no unreachable code." \ No newline at end of file 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 new file mode 100644 index 000000000000..6033c421a4f4 --- /dev/null +++ b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 187.ql @@ -0,0 +1,23 @@ +/** + * @name AV Rule 187 + * @description All non-null statements shall potentially have a side-effect. + * @kind problem + * @id cpp/jsf/av-rule-187 + * @problem.severity error + */ +import cpp + +from Stmt s +where s.fromSource() and + // Exclude empty statements + not s instanceof EmptyStmt and + // Exclude control statements + not s instanceof LabelStmt and + not s instanceof JumpStmt and + not s instanceof ReturnStmt and + 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 + s.isPure() +select s, "AV Rule 187: All non-null statements shall potentially have a side-effect." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 188.ql b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 188.ql new file mode 100644 index 000000000000..f01c15a9a0e0 --- /dev/null +++ b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 188.ql @@ -0,0 +1,12 @@ +/** + * @name AV Rule 188 + * @description Labels will not be used, except in switch statements. + * @kind problem + * @id cpp/jsf/av-rule-188 + * @problem.severity warning + */ +import cpp + +from LabelStmt l +where not exists(GotoStmt g | g.breaksFromNestedLoops() and g.getTarget() = l) +select l, "AV Rule 188: Labels will not be used, except in switch statements." \ No newline at end of file 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 new file mode 100644 index 000000000000..211a694ae16d --- /dev/null +++ b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 189.qhelp @@ -0,0 +1,40 @@ + + + + +

    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. +This rule identifies any goto statements that are called directly or from a single nested loop as violations.

    + +
    + +

    +In most cases you should replace the goto statement in the highlighted code with an alternative jump statement +(break, continue or return). In deeply nested loops you may need to use a goto statement because +the break statement only exits from one level of the loop.

    + +
    + + +
  • + AV Rule 189, Joint Strike Fighter Air Vehicle C++ Coding Standards. Lockheed Martin Corporation, 2005. +
  • +
  • + A Case against the GO TO Statement (EWD-215). +
  • +
  • + MSDN Library: The goto Statement +
  • +
  • + Mats Henricson and Erik Nyquist, Industrial Strength C++, published by Prentice Hall PTR (1997). + Chapter 4: Control Flow, Rule 4.6 (PDF). +
  • +
  • + www.cplusplus.com Control Structures +
  • + + +
    +
    diff --git a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 189.ql b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 189.ql new file mode 100644 index 000000000000..fbf0a1b0b117 --- /dev/null +++ b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 189.ql @@ -0,0 +1,14 @@ +/** + * @name AV Rule 189 + * @description The goto statement shall not be used. + * @kind problem + * @id cpp/jsf/av-rule-189 + * @problem.severity error + * @tags maintainability + */ +import cpp + +from GotoStmt s +where s.fromSource() and + not s.breaksFromNestedLoops() +select s, "AV Rule 189: The goto statement shall not be used." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 190.ql b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 190.ql new file mode 100644 index 000000000000..8e794081c2ea --- /dev/null +++ b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 190.ql @@ -0,0 +1,12 @@ +/** + * @name AV Rule 190 + * @description The continue statement shall not be used. + * @kind problem + * @id cpp/jsf/av-rule-190 + * @problem.severity error + */ +import cpp + +from ContinueStmt s +where s.fromSource() +select s, "AV Rule 190: The continue statement shall not be used." \ No newline at end of file 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 new file mode 100644 index 000000000000..866dbcd2a229 --- /dev/null +++ b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 191.ql @@ -0,0 +1,23 @@ +/** + * @name AV Rule 191 + * @description The break statement shall not be used. + * @kind problem + * @id cpp/jsf/av-rule-191 + * @problem.severity error + */ +import cpp + +/* TODO: "The break statement may be used to break out of a single loop provided + the alternative would obscure or otherwise significantly complicate the + control logic." */ + +// 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) +} + +from BreakStmt s +where s.fromSource() and + // exclude break statements that terminate switch cases + not exists(SwitchCase sc | isTerminatingStmt(sc.getLastStmt(), s)) +select s, "AV Rule 191: The break statement shall not be used." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 192.ql b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 192.ql new file mode 100644 index 000000000000..8553d0a005af --- /dev/null +++ b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 192.ql @@ -0,0 +1,26 @@ +/** + * @name AV Rule 192 + * @description All if, else if constructs will contain either a final else clause + * or a comment indicating why a final else clause is not necessary. + * @kind problem + * @id cpp/jsf/av-rule-192 + * @problem.severity warning + */ +import cpp + +// the extractor associates comments with the statement immediately following them +// additionally, we also want to count comments that come right after the if statement, +// as this seems a likely place to put a comment explaining the absence of an else +predicate isCommented(IfStmt i) { + exists(Comment c | + c.getCommentedElement() = i + or i.getLocation().getEndLine() = c.getLocation().getStartLine()) +} + +from IfStmt i +where i.fromSource() and + // only applies if there are one or more else-ifs + exists(IfStmt i2 | i2.getElse() = i) and + not i.hasElse() and + not isCommented(i) +select i, "AV Rule 192: All if-else if chains will have an else clause or a comment explaining its absence." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 193.ql b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 193.ql new file mode 100644 index 000000000000..6e589bb0fab1 --- /dev/null +++ b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 193.ql @@ -0,0 +1,28 @@ +/** + * @name AV Rule 193 + * @description Every non-empty case clause in a switch statement + * shall be terminated with a break statement. + * @kind problem + * @id cpp/jsf/av-rule-193 + * @problem.severity error + */ +import cpp + +/* Interpretation: + * - return statements should be okay too; if not desired another rule should catch it + * - the default case should be excluded, as long as it is at the end + * We don't allow the last case to omit the break/return, because that seems to violate the + * spirit of the rule (what if another case was added?) + */ + +predicate lastDefaultCase(SwitchCase sc) { + sc.isDefault() and not exists(SwitchCase sc2 | sc2.getSwitchStmt() = sc.getSwitchStmt() and sc2.getChildNum() > sc.getChildNum()) +} + +from SwitchCase sc +where sc.fromSource() and + exists(sc.getAStmt()) and + not lastDefaultCase(sc) and + not sc.terminatesInBreakStmt() and + not sc.terminatesInReturnStmt() +select sc, "AV Rule 193: Every non-empty case clause in a switch statement shall be terminated with a break statement." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 194.ql b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 194.ql new file mode 100644 index 000000000000..4aa98eb85e60 --- /dev/null +++ b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 194.ql @@ -0,0 +1,18 @@ +/** + * @name AV Rule 194 + * @description All switch statements that do not intend to test for every enumeration value shall contain a final default clause. + * @kind problem + * @id cpp/jsf/av-rule-194 + * @problem.severity error + */ +import cpp + +from EnumSwitch es, EnumConstant ec +where // A switch without a default case ... + not es.hasDefaultCase() and + // ... that misses at least one value ... + ec = es.getAMissingCase() and + // ... and make sure we pick a single missed value; choose the first one + not exists (EnumConstant ec2, int i, int j | + ec2 = es.getAMissingCase() and ec2 = ec2.getDeclaringEnum().getEnumConstant(i) and ec = ec.getDeclaringEnum().getEnumConstant(j) and i < j) +select es, "AV Rule 195: all switch statements that do not intend to test for every enumeration value shall contain a final default clause. This statement is missing a case for " + ec.getName() \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 195.ql b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 195.ql new file mode 100644 index 000000000000..8a8fa4b58e54 --- /dev/null +++ b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 195.ql @@ -0,0 +1,13 @@ +/** + * @name AV Rule 195 + * @description A switch expression will not represent a Boolean value. + * @kind problem + * @id cpp/jsf/av-rule-195 + * @problem.severity warning + */ +import cpp + +from SwitchStmt s +where s.fromSource() and + s.getExpr().getUnderlyingType() instanceof BoolType +select s, "AV Rule 195: A switch expression will not represent a Boolean value." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 196.cpp b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 196.cpp new file mode 100644 index 000000000000..115f3d212dac --- /dev/null +++ b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 196.cpp @@ -0,0 +1,18 @@ +int f() { + int val = 0; + switch(val) { //wrong, use an if instead + case 0: + //... + default: + //... + } + + switch(val) { //correct, has 2 cases and a default + case 0: + //... + case 1: + //... + default: + //... + } +} diff --git a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 196.qhelp b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 196.qhelp new file mode 100644 index 000000000000..ccf1548d28d0 --- /dev/null +++ b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 196.qhelp @@ -0,0 +1,42 @@ + + + +

    +The following forms of switch statement are considered trivial: +

    +
      +
    1. No cases at all.
    2. +
    3. Just a default case.
    4. +
    5. Just one non-default case.
    6. +
    7. A default case and one non-default case.
    8. +
    + +
    + +

    +Either the switch statement should be replaced with a simpler control flow structure, or it should be extended +to handle more cases. Each trivial form has a different replacement: +

    +
      +
    1. If there are no cases, the switch statement can be removed.
    2. +
    3. If there is just one default case, the switch keyword, the default keyword, and the subsequent colon can all be removed.
    4. +
    5. If there is just one non-default case, the switch statement can be turned into an if statement.
    6. +
    7. If there is one default case and one non-default case, the switch statement can be turned into an if/else statement.
    8. +
    + +
    + + + + + + +
  • +AV Rule 196, Joint Strike Fighter Air Vehicle C++ Coding Standards. Lockheed Martin Corporation, 2005. +
  • + + +
    +
    diff --git a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 196.ql b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 196.ql new file mode 100644 index 000000000000..b0f2627e6556 --- /dev/null +++ b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 196.ql @@ -0,0 +1,17 @@ +/** + * @name No trivial switch statements + * @description Using a switch statement when there are fewer than two non-default cases leads to unclear code. + * @kind problem + * @problem.severity recommendation + * @precision high + * @id cpp/trivial-switch + * @tags maintainability + * readability + */ +import cpp + +from SwitchStmt s +where s.fromSource() and + count(SwitchCase sc | sc.getSwitchStmt() = s and not sc instanceof DefaultCase) < 2 and + not exists(s.getGeneratingMacro()) +select s, "This switch statement should either handle more cases, or be rewritten as an if statement." diff --git a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 197.cpp b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 197.cpp new file mode 100644 index 000000000000..b0f5d9f68e71 --- /dev/null +++ b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 197.cpp @@ -0,0 +1,12 @@ +void f() { + float i = 0.0f; + //wrong: float used as loop counter + for (i = 0; i < 1000000.0f; i++) { //may execute 1000000 +x/-x times + //... + } + for (i = 0; i < 100000000.0f; i++) { //may never terminate, as rounding errors + //cancel out the addition of 1.0 once + //i becomes large enough + //... + } +} diff --git a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 197.qhelp b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 197.qhelp new file mode 100644 index 000000000000..b9e8387ec320 --- /dev/null +++ b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 197.qhelp @@ -0,0 +1,40 @@ + + + + + +

    +This rule finds float variables being used as loop counter. float values +are prone to rounding and truncation. In particular, very large and very small +float values are prone to rounding errors and could lead to unexpected loop behavior. +

    + +
    + +

    +Use an integral variable instead of a float variable for the loop counter. +

    + +
    + + + + + + + +
  • + AV Rule 197, Joint Strike Fighter Air Vehicle C++ Coding Standards. Lockheed Martin Corporation, 2005. +
  • +
  • + MISRA C++ Rule 6-5-1, Guidelines for the use of the C++ language in critical systems. The Motor Industry Software Reliability Associate, 2008. +
  • +
  • + FLP30-C. Do not use floating-point variables as loop counters +
  • + + +
    +
    diff --git a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 197.ql b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 197.ql new file mode 100644 index 000000000000..37fc05b58338 --- /dev/null +++ b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 197.ql @@ -0,0 +1,15 @@ +/** + * @name Avoid floats in for loops + * @description Floating point variables should not be used as loop counters. For loops are best suited to simple increments and termination conditions; while loops are preferable for more complex uses. + * @kind problem + * @problem.severity recommendation + * @precision high + * @id cpp/loop-variable-float + * @tags reliability + * readability + */ +import cpp + +from LoopCounter lc +where lc.getUnderlyingType() instanceof FloatingPointType +select lc, "Floating point variables should not be used as loop counters." diff --git a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 198.ql b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 198.ql new file mode 100644 index 000000000000..9b973d40264f --- /dev/null +++ b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 198.ql @@ -0,0 +1,19 @@ +/** + * @name AV Rule 198 + * @description The initialization statement in a for loop will only + * initialize a single loop parameter. + * @kind problem + * @id cpp/jsf/av-rule-198 + * @problem.severity warning + */ +import cpp + +predicate isValidLoopInitialization(Stmt s) { + s instanceof DeclStmt + or s.(ExprStmt).getExpr() instanceof AssignExpr +} + +from ForStmt for +where for.fromSource() and + not isValidLoopInitialization(for.getInitialization()) +select for, "AV Rule 198: The initialization statement in a for loop will only initialize a single loop parameter." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 199.ql b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 199.ql new file mode 100644 index 000000000000..258a6c1e2bfa --- /dev/null +++ b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 199.ql @@ -0,0 +1,20 @@ +/** + * @name AV Rule 199 + * @description The increment expression in a for loop will only update + * a single loop parameter. + * @kind problem + * @id cpp/jsf/av-rule-199 + * @problem.severity warning + */ +import cpp + +predicate isValidLoopUpdate(Expr e) { + e instanceof CrementOperation + or e instanceof AssignExpr + or e instanceof FunctionCall // this is to account for overloaded operators +} + +from ForStmt for +where for.fromSource() and + not isValidLoopUpdate(for.getUpdate()) +select for, "AV Rule 199: The increment expression in a for loop will only update a single for loop parameter." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 200.ql b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 200.ql new file mode 100644 index 000000000000..cbabf4852659 --- /dev/null +++ b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 200.ql @@ -0,0 +1,14 @@ +/** + * @name AV Rule 200 + * @description Null initialize or increment expressions in for loops will not be used; + * a while loop will be used instead. + * @kind problem + * @id cpp/jsf/av-rule-200 + * @problem.severity warning + */ +import cpp + +from ForStmt f +where f.fromSource() and + (not exists(f.getInitialization()) or not exists(f.getUpdate())) +select f, "AV Rule 200: Every for loop will have non-null initialize and increment expressions." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 201.cpp b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 201.cpp new file mode 100644 index 000000000000..329e65cd8ae8 --- /dev/null +++ b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 201.cpp @@ -0,0 +1,12 @@ +void f() { + int i = 0; + for (i = 0; i < 10; i++) { + //... + if (special_case) { + i += 5; //Wrong: loop variable changed in body, which is contrary + //to the usual expectation of for loop behavior. + //Use a while loop instead. + continue; + } + } +} diff --git a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 201.qhelp b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 201.qhelp new file mode 100644 index 000000000000..c165fe59ba8c --- /dev/null +++ b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 201.qhelp @@ -0,0 +1,47 @@ + + + + + +

    +This rule finds for loop variables that are being modified in the loop body. for loops should +be reserved for simple iteration, use while loops for more complicated cases. Most developers assume that +the for loop iteration statement is the only thing that changes the value of the loop counter, so changing it +inside the loop body can lead to confusion. +

    + + +
    + +

    +Use only the increment expression in the for loop to modify loop variables, or change it to a while +loop if the loop requires more complicated variable iteration. +

    + +
    + + + + + + + +
  • + AV Rule 201, Joint Strike Fighter Air Vehicle C++ Coding Standards. Lockheed Martin Corporation, 2005. +
  • +
  • + MISRA C++ Rule 6-5-3, Guidelines for the use of the C++ language in critical systems. The Motor Industry Software Reliability Associate, 2008. +
  • +
  • + For statements +
  • +
  • + Mats Henricson and Erik Nyquist, Industrial Strength C++, published by Prentice Hall PTR (1997). + Chapter 4: Control Flow, Rule 4.1 (PDF). +
  • + + +
    +
    diff --git a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 201.ql b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 201.ql new file mode 100644 index 000000000000..060734ab0eca --- /dev/null +++ b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 201.ql @@ -0,0 +1,51 @@ +/** + * @name For loop variable changed in body + * @description Numeric variables being used within a for loop for iteration counting should not be modified in the body of the loop. Reserve for loops for straightforward iterations, and use a while loop instead for more complex cases. + * @kind problem + * @problem.severity recommendation + * @precision high + * @id cpp/loop-variable-changed + * @tags reliability + * readability + */ +import cpp +import Likely_Bugs.NestedLoopSameVar + +pragma[noopt] +predicate loopModification(ForStmt for, Variable loopVariable, VariableAccess acc) { + loopVariable = for.getAnIterationVariable() and + acc = loopVariable.getAnAccess() and + acc.isModified() and + exists(Stmt stmt | acc.getEnclosingStmt() = stmt and stmtInForBody(stmt, for)) +} + +pragma[noopt] +predicate stmtInForBody(Stmt stmt, ForStmt forStmt) { + (forStmt.getStmt() = stmt or exists(StmtParent parent | parent = stmt.getParent() | stmtInForBody(parent, forStmt))) + and forStmt instanceof ForStmt +} + +from ForStmt for, Variable loopVariable, VariableAccess acc +where + loopModification(for, loopVariable, acc) and + + // field accesses must have the same object + ( + loopVariable instanceof Field implies + exists(Variable obj | + simpleFieldAccess(obj, loopVariable, acc) and + simpleFieldAccess(obj, loopVariable, for.getCondition().getAChild*()) + ) + ) and + + // don't duplicate results from NestedLoopSameVar.ql + not exists(ForStmt inner | + nestedForViolation(inner, loopVariable, for) and + ( + acc.getParent*() = inner or + acc.getParent*() = inner.getInitialization() + ) + ) +select + acc, "Loop counters should not be modified in the body of the $@.", + for.getStmt(), "loop" diff --git a/cpp/ql/src/jsf/4.25 Expressions/AV Rule 202.ql b/cpp/ql/src/jsf/4.25 Expressions/AV Rule 202.ql new file mode 100644 index 000000000000..352993f2c17a --- /dev/null +++ b/cpp/ql/src/jsf/4.25 Expressions/AV Rule 202.ql @@ -0,0 +1,15 @@ +/** + * @name AV Rule 202 + * @description Floating point variables shall not be tested for exact equality or inequality. + * @kind problem + * @id cpp/jsf/av-rule-202 + * @problem.severity error + */ +import cpp + +// see MISRA 6-2-2 + +from EqualityOperation e +where e.fromSource() and + e.getAnOperand().getType() instanceof FloatingPointType +select e, "AV Rule 202: Floating point variables shall not be tested for exact equality or inequality." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.25 Expressions/AV Rule 204.1.ql b/cpp/ql/src/jsf/4.25 Expressions/AV Rule 204.1.ql new file mode 100644 index 000000000000..d1c53d781eda --- /dev/null +++ b/cpp/ql/src/jsf/4.25 Expressions/AV Rule 204.1.ql @@ -0,0 +1,31 @@ +/** + * @name AV Rule 204.1 + * @description The value of an expression shall be the same under any order of evaluation that the standard permits. Except where noted, the order in which operators and subexpression are evaluated, as well as the order in which side effects take place, is unspecified. + * @kind problem + * @id cpp/jsf/av-rule-204-1 + * @problem.severity error + */ +import cpp + +class SideEffectVariableExpr extends VariableAccess { + SideEffectVariableExpr() { + exists(CrementOperation o | o.getOperand() = this) + or exists(AssignExpr e | e.getLValue() = this) + } +} + +predicate characteristicSequencePointExpr(Expr e, Expr c) { + if exists(Expr p | p = e.getParent() and not p instanceof BinaryLogicalOperation + and not p instanceof CommaExpr and not p instanceof ConditionalExpr) then + characteristicSequencePointExpr(e.getParent(), c) + else + e = c +} + +from SideEffectVariableExpr e, Expr c, Variable v, VariableAccess va +where e.getTarget() = v and va.getTarget() = v and + characteristicSequencePointExpr(e, c) + and characteristicSequencePointExpr(va, c) + and va != e + and not e.getParent().(AssignExpr).getLValue() = e +select c, "AV Rule 204.1: The value of an expression shall be the same under any order of evaluation that the standard permits" diff --git a/cpp/ql/src/jsf/4.25 Expressions/AV Rule 204.ql b/cpp/ql/src/jsf/4.25 Expressions/AV Rule 204.ql new file mode 100644 index 000000000000..04a8806c8309 --- /dev/null +++ b/cpp/ql/src/jsf/4.25 Expressions/AV Rule 204.ql @@ -0,0 +1,87 @@ +/** + * @name AV Rule 204 + * @description A single operation with side-effects shall only be used by itself, + * to the right of an assignment, in a condition, as the only argument + * with side-effects in a function call, as a loop condition, as a switch + * condition, or as a part of a chained operation. + * @kind problem + * @id cpp/jsf/av-rule-204 + * @problem.severity error + */ +import cpp + +// whether the main operation of e is pure (not considering its operands) +predicate isPureOperation(Expr e) { + e instanceof Operation and + not e instanceof Assignment and + not e instanceof CrementOperation +} + +// f is the smallest expression containing e composed entirely of +// pure operators such that e is the only non-constant subexpression +// e.g., in x = g(y) + 1, if e is g(y), then f is g(y) + 1 +predicate stripOffConstants(Expr e, Expr f) { + (not isPureOperation(e.getParent()) and f=e) + + or exists(Operation p | + p = e.getParent() and + isPureOperation(p) and + (if forall(Expr g | g = p.getAChild() and e != g | g.isConstant()) then + stripOffConstants(p, f) + else f=e)) +} + +// whether e occurs by itself as a statement +predicate occursByItself(Expr e) { + exists(ExprStmt s | e = s.getExpr()) + or exists(ForStmt s | s.getUpdate() = e) +} + +// whether e is the source of an assignment or an initializer +predicate isOnRightOfAssignment(Expr e) { + exists(Assignment a | a.getRValue() = e) + or exists(Initializer i | i.getExpr() = e) +} + +// whether e is a loop condition, an if condition, or a switch expression +predicate isControllingExpr(Expr e) { + exists(ControlStructure c | c.getControllingExpr() = e) +} + +// whether e is the only impure argument expression of a function call +predicate isSoleNonConstFunArg(Expr e) { + exists(FunctionCall fc | fc.getAnArgument() = e and forall(Expr g | g = fc.getAnArgument() and g!=e | g.isPure())) +} + +// whether e occurs as part of a chain of qualifiers +predicate isPartOfChain(Expr e) { + qualifies(e, _) or qualifies(_, e) +} + +// whether q qualifies e +predicate qualifies(Expr q, Expr e) { + q = e.(Call).getQualifier() or q = e.(VariableAccess).getQualifier() +} + +predicate impureExprInDisallowedContext(Expr e) { + exists(Expr f | + e.fromSource() and + not e.isPure() and + stripOffConstants(e, f) and + not occursByItself(f) and + not isOnRightOfAssignment(f) and + not isControllingExpr(f) and + not isSoleNonConstFunArg(f) and + not isPartOfChain(f)) +} + +from Expr e +where impureExprInDisallowedContext(e) and + not e.isCompilerGenerated() and + // A few cases that are always ok + not e instanceof Conversion and + not exists(@ctorinit ci | e = ci) and + not exists(@dtordestruct dd | e = dd) and + // Avoid flagging nested expressions + not impureExprInDisallowedContext(e.getParent+()) +select e.findRootCause(), "AV Rule 204: A single operation with side-effects shall only be used in certain contexts." diff --git a/cpp/ql/src/jsf/4.25 Expressions/AV Rule 205.ql b/cpp/ql/src/jsf/4.25 Expressions/AV Rule 205.ql new file mode 100644 index 000000000000..a269959daa26 --- /dev/null +++ b/cpp/ql/src/jsf/4.25 Expressions/AV Rule 205.ql @@ -0,0 +1,19 @@ +/** + * @name AV Rule 205 + * @description The volatile keyword shall not be used unless directly interfacing with hardware. + * @kind problem + * @id cpp/jsf/av-rule-205 + * @problem.severity error + */ +import cpp + +// whether it is acceptable for s to be declared volatile +// by default, we accept volatile specifiers on external global variables +predicate acceptableVolatile(Variable v) { + v instanceof GlobalVariable and v.hasSpecifier("extern") +} + +from Variable v +where v.getType().hasSpecifier("volatile") and + not acceptableVolatile(v) +select v, "AV Rule 205: The volatile keyword shall not be used unless directly interfacing with hardware." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.26 Memory Allocation/AV Rule 206.ql b/cpp/ql/src/jsf/4.26 Memory Allocation/AV Rule 206.ql new file mode 100644 index 000000000000..30254488556e --- /dev/null +++ b/cpp/ql/src/jsf/4.26 Memory Allocation/AV Rule 206.ql @@ -0,0 +1,18 @@ +/** + * @name AV Rule 206 + * @description Allocation/deallocation from/to the free store (heap) shall not + * occur after initialization. + * @kind problem + * @id cpp/jsf/av-rule-206 + * @problem.severity error + */ +import cpp + +predicate occursAfterInitialization(Expr e) { + not e.getEnclosingFunction() instanceof Constructor and + not e.getEnclosingFunction() instanceof Destructor +} + +from Expr e +where isMemoryManagementExpr(e) and occursAfterInitialization(e) +select e, "AV Rule 206: Memory management shall not occur after initialization." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.26 Memory Allocation/AV Rule 207.ql b/cpp/ql/src/jsf/4.26 Memory Allocation/AV Rule 207.ql new file mode 100644 index 000000000000..6628234ff0c0 --- /dev/null +++ b/cpp/ql/src/jsf/4.26 Memory Allocation/AV Rule 207.ql @@ -0,0 +1,28 @@ +/** + * @name AV Rule 207 + * @description Unencapsulated global data will be avoided. Global non-constant variables should be objects of encapsulated types - not basic types, structs or struct-like classes. + * @kind problem + * @id cpp/jsf/av-rule-207 + * @problem.severity warning + */ +import cpp + +predicate unencapsulated(Type t) { + exists(Type base | base = t.getUnderlyingType() and + ( + base instanceof ArithmeticType or + base instanceof Enum or + base instanceof StructLikeClass or + unencapsulated(base.(PointerType).getBaseType()) or + unencapsulated(base.(SpecifiedType).getBaseType()) or + unencapsulated(base.(ReferenceType).getBaseType()) + ) + ) +} + + +from GlobalVariable gv +where unencapsulated(gv.getType()) + // Allow immutable global constants + and not gv.getType().isDeeplyConst() +select gv, "AV Rule 207: Unencapsulated global data will be avoided." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.27 Fault Handling/AV Rule 208.ql b/cpp/ql/src/jsf/4.27 Fault Handling/AV Rule 208.ql new file mode 100644 index 000000000000..172f8db58a86 --- /dev/null +++ b/cpp/ql/src/jsf/4.27 Fault Handling/AV Rule 208.ql @@ -0,0 +1,12 @@ +/** + * @name AV Rule 208 + * @description C++ exceptions shall not be used. + * @kind problem + * @id cpp/jsf/av-rule-208 + * @problem.severity error + */ +import cpp + +from Element e +where e instanceof TryStmt or e instanceof ThrowExpr +select e, "AV Rule 208: C++ exceptions shall not be used." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.28 Portable Code/AV Rule 209.ql b/cpp/ql/src/jsf/4.28 Portable Code/AV Rule 209.ql new file mode 100644 index 000000000000..991026f4fa60 --- /dev/null +++ b/cpp/ql/src/jsf/4.28 Portable Code/AV Rule 209.ql @@ -0,0 +1,17 @@ +/** + * @name AV Rule 209 + * @description The basic types of int, short, long, float and double shall not be used, + * but specific-length equivalents should be typedef'd accordingly for + * each compiler, and these type names used in the code. + * @kind problem + * @id cpp/jsf/av-rule-209 + * @problem.severity error + */ +import cpp + +from Element u, ArithmeticType at +where (at.hasName("int") or at.hasName("short") or at.hasName("long") + or at.hasName("float") or at.hasName("double")) and + u = at.getATypeNameUse() and + not at instanceof WideCharType +select u, "AV Rule 209: The basic types of int, short, long, float and double shall not be used." diff --git a/cpp/ql/src/jsf/4.28 Portable Code/AV Rule 210.ql b/cpp/ql/src/jsf/4.28 Portable Code/AV Rule 210.ql new file mode 100644 index 000000000000..c8fb016284dc --- /dev/null +++ b/cpp/ql/src/jsf/4.28 Portable Code/AV Rule 210.ql @@ -0,0 +1,60 @@ +/** + * @name AV Rule 210 + * @description Algorithms shall not make assumptions concerning how + * data is represented in memory. + * @kind problem + * @id cpp/jsf/av-rule-210 + * @problem.severity error + * @precision low + */ +import cpp + +/* The standard lists three things that are disallowed in particular: + * - relying on big vs. little endian representation + * - relying on base class subobject ordering in derived classes + * - relying on nonstatic data member ordering across access specifiers + * + * We currently only check for violations of the first one, in a similar way + * to AV Rule 147: No casts from pointers to/arrays of integrals to pointers to + * integrals of a different size, no unions that both contain an integral and + * an array of smaller integrals. */ + +class PointerOrArrayType extends DerivedType { + PointerOrArrayType() { this instanceof PointerType or this instanceof ArrayType } +} + +// cast from pointer to integral type to pointer to a different integral type +class ExposingIntegralCastExpr extends Expr { + ExposingIntegralCastExpr() { + exists(PointerOrArrayType src, PointerOrArrayType dst, IntegralType srcbase, IntegralType dstbase | + src = this.getUnderlyingType() and + srcbase = src.getBaseType().getUnderlyingType() and + dst = this.getActualType() and + dstbase = dst.getBaseType().getUnderlyingType() and + srcbase != dstbase) + } +} + +class ExposingIntegralUnion extends Union { + ExposingIntegralUnion() { + exists (MemberVariable mv1, MemberVariable mv2, IntegralType mv1tp, IntegralType mv2tp | + mv1 = this.getAMemberVariable() and + mv2 = this.getAMemberVariable() and + mv1tp = mv1.getUnderlyingType().(IntegralType) and + (mv2tp = mv2.getUnderlyingType().(IntegralType) + or + mv2tp = mv2.getUnderlyingType().(ArrayType).getBaseType().getUnderlyingType().(IntegralType)) and + mv1tp.getSize() > mv2tp.getSize()) + } +} + +from Element e, string message +where + ( + e instanceof ExposingIntegralCastExpr and + message = "AV Rule 210: This cast makes assumptions concerning data representation in memory." + ) or ( + e instanceof ExposingIntegralUnion and + message = "AV Rule 210: This union may make assumptions concerning data representation in memory." + ) +select e, message diff --git a/cpp/ql/src/jsf/4.28 Portable Code/AV Rule 212.ql b/cpp/ql/src/jsf/4.28 Portable Code/AV Rule 212.ql new file mode 100644 index 000000000000..5a05826fa191 --- /dev/null +++ b/cpp/ql/src/jsf/4.28 Portable Code/AV Rule 212.ql @@ -0,0 +1,37 @@ +/** + * @name AV Rule 212 + * @description Underflow or overflow functioning shall not be depended on + * in any special way. + * @kind problem + * @id cpp/jsf/av-rule-212 + * @problem.severity error + */ +import cpp + +/* We check for the use of the "a + b < a" idiom. */ + +predicate isNonNegative(Expr e) { + e.getUnderlyingType().(IntegralType).isUnsigned() + or e.getValue().toInt() >= 0 +} + +predicate sameExpr(Expr e, Expr f) { + e.(VariableAccess).getTarget() = f.(VariableAccess).getTarget() + // adding the following disjunct OOMs on non-trivial databases + //or e.getValue() = f.getValue() +} + +class UnreliableOverflowTest extends LTExpr { + UnreliableOverflowTest() { + exists(AddExpr l, Expr a, Expr b, Expr r | + l = super.getLeftOperand() and + a = l.getLeftOperand() and + b = l.getRightOperand() and + r = super.getRightOperand() and + ( sameExpr(a, r) and isNonNegative(b) + or sameExpr(b, r) and isNonNegative(a))) + } +} + +from UnreliableOverflowTest t +select t, "AV Rule 212: Underflow or overflow functioning shall not be depended on." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.28 Portable Code/AV Rule 213.ql b/cpp/ql/src/jsf/4.28 Portable Code/AV Rule 213.ql new file mode 100644 index 000000000000..7cdcc1e9e24a --- /dev/null +++ b/cpp/ql/src/jsf/4.28 Portable Code/AV Rule 213.ql @@ -0,0 +1,32 @@ +/** + * @name AV Rule 213 + * @description No dependence shall be placed on C++'s operator precedence rules, + * below arithmetic operators, in expressions. + * @kind problem + * @id cpp/jsf/av-rule-213 + * @problem.severity error + */ +import cpp + +// Interpretation and deviations: +// - if the higher operator has precedence > arithmetic then it is fine +// RATIONALE: exprs like f(), *x, &x are easily understood to bind tightly +// - if the higher operator is the RHS of an assign then it is fine +// RATIONALE: cf. MISRA, too many cases excluded otherwise +// - comparison operators can be mixed with arithmetic +// RATIONALE: x==y+z is common and unambiguous + +predicate arithmeticPrecedence(int p) { p = 12 or p = 13 } +predicate comparisonPrecedence(int p) { p = 9 or p = 10 } + +from Expr e1, Expr e2, int p1, int p2 +where e1.getAChild() = e2 and + p1 = e1.getPrecedence() and + p2 = e2.getPrecedence() and + p1 < p2 and + not e2.isParenthesised() and + p1 <= 11 and + p2 <= 13 and // allow > arithmetic operators + not (arithmeticPrecedence(p2) and comparisonPrecedence(p1)) and // arith-compare deviation + not e2 = e1.(Assignment).getRValue() // assignment deviation +select e1, "AV Rule 213: Limited dependence shall be placed on operator precedence rules." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.28 Portable Code/AV Rule 214.ql b/cpp/ql/src/jsf/4.28 Portable Code/AV Rule 214.ql new file mode 100644 index 000000000000..9b873f8e716c --- /dev/null +++ b/cpp/ql/src/jsf/4.28 Portable Code/AV Rule 214.ql @@ -0,0 +1,39 @@ +/** + * @name AV Rule 214 + * @description Assuming that non-local static objects, in separate translation units, + * are initialized in a special order shall not be done. + * @kind problem + * @id cpp/jsf/av-rule-214 + * @problem.severity error + */ +import cpp + +// whether e is part of an initializer of a global variable in file f +predicate inGlobalInitializer(Expr e, File f) { + exists(GlobalVariable gv | gv.getInitializer().getExpr() = e.getParent*() and + f = gv.getFile()) +} + +// whether c is called from within a global initializer in f +// TODO: this should be transitive, but maybe it's not worth the extra hassle +predicate calledFromGlobalInitializer(Function fn, File f) { + exists(FunctionCall c | inGlobalInitializer(c, f) and fn = c.getTarget()) +} + +predicate evaluatedBeforeMain(Expr e, File f) { + inGlobalInitializer(e, f) + or exists(Function fn | calledFromGlobalInitializer(fn, f) and fn = e.getControlFlowScope()) +} + +// whether f1 and f2 belong to the same translation unit +predicate sameTranslationUnit(File f1, File f2) { + exists(File f | f.getAnIncludedFile*() = f1 and f.getAnIncludedFile*() = f2) +} + +from VariableAccess v, File f1, File f2 +where v.fromSource() and + v.isRValue() and + evaluatedBeforeMain(v, f1) and + v.getTarget().getFile() = f2 and + not sameTranslationUnit(f1, f2) +select v, "AV Rule 214: It shall not be assumed that objects in separated translation units are initialized in a special order." \ No newline at end of file diff --git a/cpp/ql/src/jsf/4.28 Portable Code/AV Rule 215.ql b/cpp/ql/src/jsf/4.28 Portable Code/AV Rule 215.ql new file mode 100644 index 000000000000..201ea073fd99 --- /dev/null +++ b/cpp/ql/src/jsf/4.28 Portable Code/AV Rule 215.ql @@ -0,0 +1,12 @@ +/** + * @name AV Rule 215 + * @description Pointer arithmetic will not be used. + * @kind problem + * @id cpp/jsf/av-rule-215 + * @problem.severity warning + */ +import cpp + +from PointerArithmeticOperation pao +where pao.fromSource() +select pao, "AV Rule 215: Pointer arithmetic will not be used." \ No newline at end of file diff --git a/cpp/ql/src/jsf/lib/section_4_21_Operators/AV_Rule_166.qll b/cpp/ql/src/jsf/lib/section_4_21_Operators/AV_Rule_166.qll new file mode 100644 index 000000000000..6e7176fb86db --- /dev/null +++ b/cpp/ql/src/jsf/lib/section_4_21_Operators/AV_Rule_166.qll @@ -0,0 +1,19 @@ +import cpp + +class IgnoreAllVolatileSpecifiersEverywhere extends Specifier { + override string getName() { result = super.getName() and result != "volatile" } +} + +class SizeofImpureExprOperator extends SizeofExprOperator { + SizeofImpureExprOperator () { + exists (Expr e | + e = this.getExprOperand() + and not e.isPure() + and not e.isAffectedByMacro() + and not e.(OverloadedPointerDereferenceExpr).getExpr().isPure() + and not exists(OverloadedArrayExpr op | op = e | + op.getArrayBase().isPure() + and op.getArrayOffset().isPure())) + } +} + diff --git a/cpp/ql/src/objc.qll b/cpp/ql/src/objc.qll new file mode 100644 index 000000000000..4996ace84523 --- /dev/null +++ b/cpp/ql/src/objc.qll @@ -0,0 +1 @@ +import cpp diff --git a/cpp/ql/src/plugin.xml b/cpp/ql/src/plugin.xml new file mode 100644 index 000000000000..e08a29947a6c --- /dev/null +++ b/cpp/ql/src/plugin.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/cpp/ql/src/queries.xml b/cpp/ql/src/queries.xml new file mode 100644 index 000000000000..99f4a7278c21 --- /dev/null +++ b/cpp/ql/src/queries.xml @@ -0,0 +1 @@ + diff --git a/cpp/ql/src/semmle/code/cpp/ASTSanity.ql b/cpp/ql/src/semmle/code/cpp/ASTSanity.ql new file mode 100644 index 000000000000..758a2a26169c --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ASTSanity.ql @@ -0,0 +1,9 @@ +/** + * @name AST Sanity Check + * @description Performs sanity checks on the Abstract Syntax Tree. This query should have no results. + * @kind problem + * @id cpp/ast-sanity-check + */ + +import cpp +import CastSanity diff --git a/cpp/ql/src/semmle/code/cpp/AutogeneratedFile.qll b/cpp/ql/src/semmle/code/cpp/AutogeneratedFile.qll new file mode 100644 index 000000000000..6cc5f28c8f22 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/AutogeneratedFile.qll @@ -0,0 +1,44 @@ +import semmle.code.cpp.Comments +import semmle.code.cpp.File +import semmle.code.cpp.Preprocessor + +/** + * Holds if `c` is a comment which is usually seen in autogenerated files. + * For example, comments containing 'autogenerated' or 'generated by'. + */ +predicate isAutogeneratedComment(Comment c) { + c.getContents().regexpMatch("(?si).*(?:auto[ -]?generated|generated (?:by|file)|changes made in this file will be lost).*") +} + +/** + * Holds if the file contains `#line` pragmas that refer to a different file. + * For example, in `parser.c` a pragma `#line 1 "parser.rl"`. + * Such pragmas usually indicate that the file was automatically generated. + */ +predicate hasPragmaDifferentFile(File f) { + exists (PreprocessorLine pl, string s | + pl.getFile() = f and + pl.getHead().splitAt(" ", 1) = s and /* Zero index is line number, one index is file reference */ + not ("\"" + f.getAbsolutePath() + "\"" = s)) +} + +/** + * Holds if the file is probably an autogenerated file. + * + * A file is probably autogenerated if either of the following heuristics + * hold: + * 1. There is a comment in the start of the file that matches + * 'autogenerated', 'generated by', or a similar phrase. + * 2. There is a `#line` directive referring to a different file. + */ +class AutogeneratedFile extends File { + cached AutogeneratedFile() { + exists(int limit, int head | + head <= 5 and + limit = max(int line | locations_default(_, this, head, _, line, _)) + 5 + | + exists (Comment c | c.getFile() = this and c.getLocation().getStartLine() <= limit and isAutogeneratedComment(c)) + ) + or hasPragmaDifferentFile(this) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/Class.qll b/cpp/ql/src/semmle/code/cpp/Class.qll new file mode 100644 index 000000000000..8c001a6574e5 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/Class.qll @@ -0,0 +1,1019 @@ +import semmle.code.cpp.Type +import semmle.code.cpp.UserType +import semmle.code.cpp.metrics.MetricClass +import semmle.code.cpp.Linkage +private import semmle.code.cpp.internal.Type + +/** + * A class type [N4140 9]. + * + * While this does include types declared with the `class` keyword, it also + * includes types declared with the `struct` and `union` keywords. + */ +class Class extends UserType { + Class() { + isClass(this) and this = resolve(_) + } + + /** Gets a child declaration of this class. */ + override Declaration getADeclaration() { result = this.getAMember() } + + /** Gets a type declared in this class. */ + UserType getANestedType() { result = this.getAMember() } + + /** + * Gets a function declared in this class. + * For template member functions, results include both the template + * and the instantiations of that template. If you only want the + * template, then use `getACanonicalMemberFunction()` instead. + */ + MemberFunction getAMemberFunction() { result = this.getAMember() } + + /** + * Gets a function declared in this class. + * For template member functions, results include only the template. + * If you also want instantiations of the template, then use + * `getAMemberFunction()` instead. + */ + MemberFunction getACanonicalMemberFunction() { + result = this.getACanonicalMember() + } + + /** + * Gets a member variable declared in this class. + * For template member variables, results include both the template + * and the instantiations of that template. If you only want the + * template, then use `getACanonicalMemberVariable()` instead. + */ + MemberVariable getAMemberVariable() { result = this.getAMember() } + + /** + * Gets a member variable declared in this class. + * For template member variables, results include only the template. + * If you also want instantiations of the template, then use + * `getAMemberVariable()` instead. + */ + MemberVariable getACanonicalMemberVariable() { result = this.getAMember() } + + /** + * Gets a member declared in this class. For template members, this + * may be either the template or an instantiation of that template. + * If you only want the template, see + * `getACanonicalMember()`. + */ + Declaration getAMember() { result = this.getAMember(_) } + + /** + * Gets a member declared in this class. + * If you also want template instantiations of results, see + * `getAMember()`. + */ + Declaration getACanonicalMember() { result = this.getCanonicalMember(_) } + + /** + * Gets the (zero-based) `index`th member declared in this class. + * If you also want template instantiations of results, see + * `getAMember(int)`. + */ + Declaration getCanonicalMember(int index) { member(this,index,result) } + + /** + * Gets the (zero-based) `index`th canonical member declared in this + * class and, if that member is a template, all instantiations of that + * template. If you only want the canonical member, see + * `getCanonicalMember(int)`. + */ + Declaration getAMember(int index) { + result = this.getCanonicalMember(index) or + result = this.getCanonicalMember(index).(TemplateClass).getAnInstantiation() or + result = this.getCanonicalMember(index).(TemplateFunction).getAnInstantiation() or + result = this.getCanonicalMember(index).(TemplateVariable).getAnInstantiation() + } + + /** + * DEPRECATED: Use `getCanonicalMember(int)` or `getAMember(int)` instead. + * Gets the `index`th member of this class. + */ + deprecated Declaration getMember(int index) { member(this,index,result) } + + /** + * DEPRECATED: As this includes a somewhat arbitrary number of + * template instantiations, it is unlikely to do what + * you need. + * Gets the number of members that this class has. This includes both + * templates that are in this class, and instantiations of those + * templates. + */ + deprecated int getNumMember() { result = count(this.getAMember()) } + + /** + * Gets a private member declared in this class. + * For template members, this may be either the template or an + * instantiation of that template. For just the template, use + * `getAPrivateCanonicalMember()`. + */ + Declaration getAPrivateMember() { + result = this.getAMember() and result.hasSpecifier("private") + } + + /** + * Gets a private canonical member declared in this class. + * If you also want template instantiations of results, see + * `getAPrivateMember()`. + */ + Declaration getAPrivateCanonicalMember() { + result = this.getACanonicalMember() and result.hasSpecifier("private") + } + + /** + * Gets a protected member declared in this class. + * For template members, this may be either the template or an + * instantiation of that template. For just the template, use + * `getAProtectedCanonicalMember()`. + */ + Declaration getAProtectedMember() { + result = this.getAMember() and result.hasSpecifier("protected") + } + + /** + * Gets a protected canonical member declared in this class. + * If you also want template instantiations of results, see + * `getAProtectedMember()`. + */ + Declaration getAProtectedCanonicalMember() { + result = this.getACanonicalMember() and result.hasSpecifier("protected") + } + + /** + * Gets a public member declared in this class. + * For template members, this may be either the template or an + * instantiation of that template. For just the template, use + * `getAPublicCanonicalMember()`. + */ + Declaration getAPublicMember() { + result = this.getAMember() and result.hasSpecifier("public") + } + + /** + * Gets a public canonical member declared in this class. + * If you also want template instantiations of results, see + * `getAPublicMember()`. + */ + Declaration getAPublicCanonicalMember() { + result = this.getACanonicalMember() and result.hasSpecifier("public") + } + + /** Gets a static member declared in this class. */ + Declaration getAStaticMember() { + result = this.getAMember() and result.isStatic() + } + + /** Gets a field of this class. */ + Field getAField() { result = this.getAMemberVariable() } + + /** Gets a constructor of this class. */ + Constructor getAConstructor() { result = this.getAMemberFunction() } + + /** Holds if this class has a constructor. */ + predicate hasConstructor() { exists(this.getAConstructor()) } + + /** + * Holds if this class has a copy constructor that is either explicitly + * declared (though possibly `= delete`) or is auto-generated, non-trivial + * and called from somewhere. + * + * DEPRECATED: There is more than one reasonable definition of what it means + * to have a copy constructor, and we do not want to promote one particular + * definition by naming it with this predicate. Having a copy constructor + * could mean that such a member is declared or defined in the source or that + * it is callable by a particular caller. For C++11, there's also a question + * of whether to include members that are defaulted or deleted. + */ + deprecated predicate hasCopyConstructor() { + exists(CopyConstructor cc | cc = this.getAMemberFunction()) + } + + /** + * Holds if this class has a copy assignment operator that is either + * explicitly declared (though possibly `= delete`) or is auto-generated, + * non-trivial and called from somewhere. + * + * DEPRECATED: There is more than one reasonable definition of what it means + * to have a copy assignment operator, and we do not want to promote one + * particular definition by naming it with this predicate. Having a copy + * assignment operator could mean that such a member is declared or defined + * in the source or that it is callable by a particular caller. For C++11, + * there's also a question of whether to include members that are defaulted + * or deleted. + */ + deprecated predicate hasCopyAssignmentOperator() { + exists(CopyAssignmentOperator coa | coa = this.getAMemberFunction()) + } + + /** + * Like accessOfBaseMember but returns multiple results if there are multiple + * paths to `base` through the inheritance graph. + */ + private AccessSpecifier accessOfBaseMemberMulti(Class base, + AccessSpecifier fieldInBase) + { + (this = base and result = fieldInBase) + or + exists(ClassDerivation cd | cd.getBaseClass() = base | + result = this.accessOfBaseMemberMulti( + cd.getDerivedClass(), + fieldInBase.accessInDirectDerived(cd.getASpecifier().(AccessSpecifier)) + ) + ) + } + + /** + * Gets the access specifier, if any, that a hypothetical member of `base` + * would have when viewed as a member of `this`, given that this member had + * access specifier `fieldInBase`. Encodes the rules of C++14 11.2/1 and + * 11.6/1, except that this predicate includes the case of `base` = `this`. + */ + AccessSpecifier accessOfBaseMember(Class base, AccessSpecifier fieldInBase) { + // If there are multiple paths through the inheritance graph, we take the + // most permissive one (C++14 11.6/1). This implementation relies on the + // alphabetical order of "private", "protected", "public". + result.hasName( + max(this.accessOfBaseMemberMulti(base, fieldInBase).getName()) + ) + } + + /** + * Gets the access specifier, if any, that `member` has when viewed as a + * member of `this`, where `member` may come from a base class of `this`. + * Encodes the rules of C++14 11.2/1 and 11.6/1, except that this predicate + * includes the case of `base` = `this`. + */ + AccessSpecifier accessOfBaseMember(Declaration member) { + result = this.accessOfBaseMember( + member.getDeclaringType(), + member.getASpecifier().(AccessSpecifier) + ) + } + + /** + * DEPRECATED: name changed to `hasImplicitCopyConstructor` to reflect that + * `= default` members are no longer included. + */ + deprecated predicate hasGeneratedCopyConstructor() { + hasImplicitCopyConstructor() + } + + /** + * DEPRECATED: name changed to `hasImplicitCopyAssignmentOperator` to + * reflect that `= default` members are no longer included. + */ + deprecated predicate hasGeneratedCopyAssignmentOperator() { + hasImplicitCopyConstructor() + } + + /** + * Holds if this class has an implicitly-declared copy constructor that is + * not _deleted_. This predicate is more accurate than checking + * if this class has a `CopyConstructor cc` where `cc.isCompilerGenerated()` + * since such a `CopyConstructor` may not exist in the database if (1) it is + * never called or (2) it is _trivial_, meaning that it is equivalent to + * `memcpy`. + */ + predicate hasImplicitCopyConstructor() { + not this.implicitCopyConstructorDeleted() and + forall(CopyConstructor cc | cc = this.getAMemberFunction() | + cc.isCompilerGenerated() and not cc.isDeleted() + ) + } + + /** + * Holds if this class has an implicitly-declared copy assignment operator + * that is not _deleted_. This predicate is more accurate than checking + * if this class has a `CopyAssignmentOperator ca` where + * `ca.isCompilerGenerated()` since such a `CopyAssignmentOperator` may not + * exist in the database if (1) it is never called or (2) it is _trivial_, + * meaning that it is equivalent to `memcpy`. + */ + predicate hasImplicitCopyAssignmentOperator() { + not this.implicitCopyAssignmentOperatorDeleted() and + forall(CopyAssignmentOperator ca | ca = this.getAMemberFunction() | + ca.isCompilerGenerated() and not ca.isDeleted() + ) + } + + /** + * Holds if the compiler would be unable to generate a copy constructor for + * this class. This predicate implements the rules listed here: + * http://en.cppreference.com/w/cpp/language/copy_constructor#Deleted_implicitly-declared_copy_constructor + */ + predicate implicitCopyConstructorDeleted() { + // - T has non-static data members that cannot be copied (have deleted, + // inaccessible, or ambiguous copy constructors); + exists(Type t | t = this.getAFieldSubobjectType().getUnspecifiedType() | + // Note: Overload resolution is not implemented -- all copy + // constructors are considered equal. + this.cannotAccessCopyConstructorOnAny(t.(Class)) + ) + or + // - T has direct or virtual base class that cannot be copied (has deleted, + // inaccessible, or ambiguous copy constructors); + exists(Class c | c = this.getADirectOrVirtualBase() | + // Note: Overload resolution is not implemented -- all copy + // constructors are considered equal. + this.cannotAccessCopyConstructorOnThis(c) + ) + or + // - T has direct or virtual base class with a deleted or inaccessible + // destructor; + exists(Class base | base = this.getADirectOrVirtualBase() | + this.cannotAccessDestructor(base, this) + ) + or + // - T has a user-defined move constructor or move assignment operator; + exists(MoveConstructor mc | mc = this.getAMemberFunction() | + not mc.isCompilerGenerated()) + or + exists(MoveAssignmentOperator ma | ma = this.getAMemberFunction() | + not ma.isCompilerGenerated()) + or + // - T is a union and has a variant member with non-trivial copy + // constructor (since C++11) + none() // Not implemented + or + // - T has a data member of rvalue reference type. + exists (Type t | t = this.getAFieldSubobjectType() | + t instanceof RValueReferenceType + ) + } + + /** + * Holds if the compiler would be unable to generate a copy assignment + * operator for this class. This predicate implements the rules listed here: + * http://en.cppreference.com/w/cpp/language/copy_assignment#Deleted_implicitly-declared_copy_assignment_operator + */ + predicate implicitCopyAssignmentOperatorDeleted() { + // - T has a user-declared move constructor; + exists(MoveConstructor mc | mc = this.getAMemberFunction() | + not mc.isCompilerGenerated()) + or + // - T has a user-declared move assignment operator. + exists(MoveAssignmentOperator ma | ma = this.getAMemberFunction() | + not ma.isCompilerGenerated()) + or + + // - T has a non-static data member of non-class type (or array thereof) + // that is const; + exists(Type t | t = this.getAFieldSubobjectType() | + // The rule for this case refers only to non-class types only, but our + // implementation extends it to cover class types too. Class types are + // supposed to be covered by the rule below on data members that + // cannot be copy-assigned. Copy-assigning a const class-typed member + // would call an overload of type + // `const C& operator=(const C&) const;`. Such an overload is unlikely + // to exist because it contradicts the intention of "const": it allows + // assigning to a const object. But since we have not implemented the + // ability to distinguish between overloads, we cannot distinguish that + // overload from the ordinary `C& operator=(const C&);`. Instead, we + // require class types to be non-const in this clause. + /* not t instanceof Class and */ t.isConst() + ) + or + // - T has a non-static data member of a reference type; + exists (Type t | t = this.getAFieldSubobjectType() | + t instanceof ReferenceType + ) + or + // - T has a non-static data member or a direct or virtual base class that + // cannot be copy-assigned (overload resolution for the copy assignment + // fails, or selects a deleted or inaccessible function); + exists(Type t | t = this.getAFieldSubobjectType().getUnspecifiedType() | + // Note: Overload resolution is not implemented -- all copy assignment + // operators are considered equal. + this.cannotAccessCopyAssignmentOperatorOnAny(t.(Class)) + ) + or + exists(Class c | c = this.getADirectOrVirtualBase() | + // Note: Overload resolution is not implemented -- all copy assignment + // operators are considered equal. + this.cannotAccessCopyAssignmentOperatorOnThis(c) + ) + // - T is a union-like class, and has a variant member whose corresponding + // assignment operator is non-trivial. + // Not implemented + } + + /** Gets the destructor of this class, if any. */ + Destructor getDestructor() { result = this.getAMemberFunction() } + + /** Holds if this class has a destructor. */ + predicate hasDestructor() { exists(this.getDestructor()) } + + /** + * Holds if this class is a POD (Plain Old Data) class [N4140 9(10)]. + * + * The definition of POD changed between C++03 and C++11, so whether + * a class is POD can depend on which version of the language it was + * compiled for. For this reason, the `is_pod_class` predicate is + * generated by the extractor. + */ + predicate isPOD() { is_pod_class(this) } + + /** + * Holds if this class is abstract, in other words whether it declares one + * or more pure virtual member functions. + */ + predicate isAbstract() { this.getAMemberFunction() instanceof PureVirtualFunction } + + /** Gets a direct base class of this class [N4140 10]. */ + Class getABaseClass() { this.getADerivation().getBaseClass() = result } + + /** Gets a class that is directly derived from this class [N4140 10]. */ + Class getADerivedClass() { result.getABaseClass() = this } + + /** Holds if this class derives directly from that. */ + predicate derivesFrom(Class that) { + this.getABaseClass() = that + } + + override predicate refersToDirectly(Type t) { + t = this.getATemplateArgument() or + this.isConstructedFrom(t) + } + + /** + * Gets a class derivation of this class, for example the "public B" + * in "class D : public B { ... };". + */ + ClassDerivation getADerivation() { + exists(ClassDerivation d | d.getDerivedClass() = this and d = result) + } + ClassDerivation getDerivation(int index) { + exists(ClassDerivation d + | d.getDerivedClass() = this and d.getIndex() = index and d = result) + } + + /** + * Gets the byte offset within `this` of each base class subobject of type + * `baseClass`, or zero if `baseClass` and `this` are the same type. Both + * direct and indirect base classes are included. + * Does not hold for base class subobjects for virtual base classes, nor does + * it hold for further base class subobjects of virtual base classes. + */ + private int getANonVirtualBaseClassByteOffset(Class baseClass) { + baseClass = this and result = 0 or // `baseClass` is the most-derived type + exists(ClassDerivation cd | + // Add the offset of the direct base class and the offset of `baseClass` + // within that direct base class. + cd = getADerivation() and + result = cd.getBaseClass().getANonVirtualBaseClassByteOffset(baseClass) + + cd.getByteOffset() + ) + } + + /** + * Gets the byte offset within `this` of each base class subobject of type + * `baseClass`, or zero if `baseClass` and `this` are the same type. Both + * direct and indirect base classes are included. + * Note that for virtual base classes, and non-virtual base classes thereof, + * this predicate assumes that `this` is the type of the complete most-derived + * object. + */ + int getABaseClassByteOffset(Class baseClass) { + // Handle the non-virtual case. + result = getANonVirtualBaseClassByteOffset(baseClass) or + exists(Class virtualBaseClass, int virtualBaseOffset, + int offsetFromVirtualBase | + // Look for the base class as a non-virtual base of a direct or indirect + // virtual base, adding the two offsets. + getVirtualBaseClassByteOffset(virtualBaseClass) = virtualBaseOffset and + offsetFromVirtualBase = + virtualBaseClass.getANonVirtualBaseClassByteOffset(baseClass) and + result = virtualBaseOffset + offsetFromVirtualBase + ) + } + + /** + * Holds if this class has a virtual class derivation, for example the + * "virtual public B" in "class D : virtual public B { ... };". + */ + predicate hasVirtualBaseClass(Class base) { + exists(ClassDerivation cd | + this.getADerivation() = cd and + cd.getBaseClass() = base and + cd.hasSpecifier("virtual") + ) + } + + /** + * Gets the byte offset of virtual base class subobject `base` within a + * most-derived object of class `this`. The virtual base can be a direct or + * indirect virtual base of `this`. Does not hold if `this` is an + * uninstantiated template. + * See `ClassDerivation.getByteOffset` for offsets of non-virtual base + * classes. + */ + int getVirtualBaseClassByteOffset(Class base) { + virtual_base_offsets(this, base, result) + } + + /** + * Holds if this class has a private class derivation, for example the + * "private B" in "class D : private B { ... };". + */ + predicate hasPrivateBaseClass(Class base) { + exists(ClassDerivation cd | + this.getADerivation() = cd and + cd.getBaseClass() = base and + cd.hasSpecifier("private") + ) + } + + /** + * Holds if this class has a public class derivation, for example the + * "public B" in "class D : public B { ... };". + */ + predicate hasPublicBaseClass(Class base) { + exists(ClassDerivation cd | + this.getADerivation() = cd and + cd.getBaseClass() = base and + cd.hasSpecifier("public") + ) + } + + /** + * Holds if this class has a protected class derivation, for example the + * "protected B" in "class D : protected B { ... };". + */ + predicate hasProtectedBaseClass(Class base) { + exists(ClassDerivation cd | + this.getADerivation() = cd and + cd.getBaseClass() = base and + cd.hasSpecifier("protected") + ) + } + + /** Gets the metric class. */ + MetricClass getMetrics() { result = this } + + /** Gets a friend declaration in this class. */ + FriendDecl getAFriendDecl() { result.getDeclaringClass() = this } + + override string explain() { result = "class " + this.getName() } + + override predicate isDeeplyConstBelow() { any() } // No subparts + + /** + * The alignment of this type in bytes (on the machine where facts were + * extracted). + */ + int getAlignment() { usertypesize(this,_,result) } + + /** + * Holds if this class is constructed from another class as a result of + * template instantiation. It originates either from a class template or + * from a class nested in a class template. + */ + predicate isConstructedFrom(Class c) { + class_instantiation(this, c) + } + + /** + * Gets a template argument used to instantiate this class from a class + * template. When called on a class template, this will return a template + * parameter. + */ + Type getATemplateArgument() { + exists(int i | this.getTemplateArgument(i) = result ) + } + + /** Gets the number of template arguments for this class. */ + int getNumberOfTemplateArguments() { + result = count(int i | exists(getTemplateArgument(i))) + } + + /** + * Gets the `i`th template argument used to instantiate this class from a + * class template. When called on a class template, this will return the + * `i`th template parameter. + */ + Type getTemplateArgument(int i) { + class_template_argument(this,i,unresolve(result)) + } + + /** + * Holds if or not this class is polymorphic (has a virtual function, or + * inherits one). + */ + predicate isPolymorphic() { + exists(MemberFunction f | f.getDeclaringType() = getABaseClass*() and f.isVirtual()) + } + + override predicate involvesTemplateParameter() { + getATemplateArgument().involvesTemplateParameter() + } + + /** Holds if this class was declared 'final'. */ + predicate isFinal() { + usertype_final(this) + } + + /** Gets a link target which references this class. */ + LinkTarget getALinkTarget() { + this = result.getAClass() + } + + /** + * Gets the UUID that associated with this class via the `__declspec(uuid)` + * attribute. + * + * Regardless of the format of the UUID string in source code, the returned + * value is normalized to the standard "registry format", without braces, and + * using lowercase letters (e.g. "01234567-89ab-cdef-0123-456789abcdef"). + */ + string getUuid() { + usertype_uuid(this, result) + } + + private Type getAFieldSubobjectType() { + result = stripArrayTypes(this.getAField().getUnderlyingType()) + } + + private Class getADirectOrVirtualBase() { + // `result` is a direct base of `this` + result.getADerivedClass() = this + or + // `result` is an indirect virtual base of `this`. The case where `result` + // is a direct virtual base of `this` is included in the above clause, and + // therefore we can use "+"-closure instead of "*"-closure here. + result.(VirtualBaseClass) + .getAVirtuallyDerivedClass() + .getADerivedClass+() = this + } + + /** + * Holds if `this` can NOT access the destructor of class `c` on an object of + * type `objectClass`. Note: this implementation is incomplete but will be + * correct in most cases; it errs on the side of claiming that the destructor + * is accessible. + */ + pragma[inline] + private predicate cannotAccessDestructor(Class c, Class objectClass) { + // The destructor in our db, if any, is accessible. If there is no + // destructor in our db, it usually means that there is a default + // public one. + exists(Destructor d | d = c.getAMemberFunction() | + not this.canAccessMember(d, objectClass)) + + // The extractor doesn't seem to support the case of a deleted destructor, + // so we ignore that. It is very much a corner case. + + // To implement this properly, there should be a predicate about whether + // the implicit destructor is deleted, similar to + // `implicitCopyConstructorDeleted`. See + // http://en.cppreference.com/w/cpp/language/destructor#Deleted_implicitly-declared_destructor + } + + private predicate cannotAccessCopyConstructorOnThis(Class c) { + this.cannotAccessCopyConstructor(c, this) + } + + private predicate cannotAccessCopyConstructorOnAny(Class c) { + this.cannotAccessCopyConstructor(c, c) + } + + /** + * Holds if `this` can NOT access the copy constructor of class `c` in order + * to construct an object of class `objectClass`. In practice, set + * `objectClass` to `this` when access-checking a base subobject + * initialization (like `class D : C { D(D& that) : C(that) { ... } }`). Set + * `objectClass` to `c` for any other purpose (like `C y = x;`). + */ + pragma[inline] + private predicate cannotAccessCopyConstructor(Class c, Class objectClass) { + // Pseudocode: + // if c has CopyConstructor cc + // then this.cannotAccess(cc) + // else this.implicitCopyConstructorDeleted() + exists(CopyConstructor cc | cc = c.getAMemberFunction() | + not this.canAccessMember(cc, objectClass)) + or + ( + not exists(CopyConstructor cc | cc = c.getAMemberFunction() and not cc.isDeleted()) and + c.implicitCopyConstructorDeleted() // mutual recursion here + // no access check in this case since the implicit member is always + // public. + ) + } + + private predicate cannotAccessCopyAssignmentOperatorOnThis(Class c) { + this.cannotAccessCopyAssignmentOperator(c, this) + } + + private predicate cannotAccessCopyAssignmentOperatorOnAny(Class c) { + this.cannotAccessCopyAssignmentOperator(c, c) + } + + /** + * Holds if `this` can NOT access the copy assignment operator of class `c` on + * an object of type `objectClass`, where `objectClass` is derived from or + * equal to `c`. That is, whether the call `x.C::operator=(...)` is forbidden + * when the type of `x` is `objectClass`, and `c` has the name `C`. + */ + pragma[inline] + private predicate cannotAccessCopyAssignmentOperator(Class c, Class objectClass) { + // Pseudocode: + // if c has CopyAssignmentOperator ca + // then this.cannotAccess(ca) + // else this.implicitCopyAssignmentOperatorDeleted() + exists(CopyAssignmentOperator ca | ca = c.getAMemberFunction() | + not this.canAccessMember(ca, objectClass)) + or + ( + not exists(CopyAssignmentOperator ca | ca = c.getAMemberFunction() and not ca.isDeleted()) and + c.implicitCopyAssignmentOperatorDeleted() // mutual recursion here + // no access check in this case since the implicit member is always + // public. + ) + } +} + +/** + * A class derivation, for example the "public B" in + * "class D : public B { ... };". + */ +class ClassDerivation extends Locatable, @derivation { + /** + * Gets the class/struct from which we are actually deriving, resolving a + * typedef if necessary. For example, the base class in the following + * would be B: + * + * struct B {}; + * typedef B T; + * struct D : T {}; + */ + Class getBaseClass() { + result = getBaseType().getUnderlyingType() + } + + /** + * Gets the type from which we are deriving, without resolving any + * typedef. For example, the base type in the following would be T: + * + * struct B {}; + * typedef B T; + * struct D : T {}; + */ + Type getBaseType() { + derivations(this,_,_,unresolve(result),_) + } + + /** + * Gets the class that is doing the deriving. For example, the derived + * class in the following would be D: + * + * struct B {}; + * struct D : B {}; + */ + Class getDerivedClass() { + derivations(this,unresolve(result),_,_,_) + } + + /** + * Gets the index of the derivation in the derivation list for the + * derived class (indexed from 0). For example, the index of the + * derivation of B2 in "struct D : B1, B2 { ... };" would be 1. + */ + int getIndex() { + derivations(this,_,result,_,_) + } + + /** Gets a specifier (for example "public") applied to the derivation. */ + Specifier getASpecifier() { + derspecifiers(this,result) + } + + /** Holds if the derivation has specifier `s`. */ + predicate hasSpecifier(string s) { + this.getASpecifier().hasName(s) + } + + /** Holds if the derivation is for a virtual base class. */ + predicate isVirtual() { + hasSpecifier("virtual") + } + + /** Gets the location of the derivation. */ + override Location getLocation() { + derivations(this,_,_,_,result) + } + + /** + * Gets the byte offset of the base class subobject relative to the start of + * the derived class object. Only holds for non-virtual bases, since the + * offset of a virtual base class is not a constant. Does not hold if the + * derived class is an uninstantiated template. + * See `Class.getVirtualBaseClassByteOffset` for offsets of virtual base + * classes. + */ + int getByteOffset() { + direct_base_offsets(this, result) + } + + override string toString() { + result = "derivation" + } +} + +/** A class that is directly enclosed by a function. */ +class LocalClass extends Class { + LocalClass() { + isLocal() + } + + override Function getEnclosingAccessHolder() { + result = this.getEnclosingFunction() + } +} + +/** + * A nested class [4140 9.7]. + */ +class NestedClass extends Class { + NestedClass() { + this.isMember() + } + + /** Holds if this member is private. */ + predicate isPrivate() { this.hasSpecifier("private") } + + /** Holds if this member is public. */ + predicate isProtected() { this.hasSpecifier("protected") } + + /** Holds if this member is public. */ + predicate isPublic() { this.hasSpecifier("public") } + +} + +/** + * An "abstract class", in other words a class that contains at least one + * pure virtual function. + */ +class AbstractClass extends Class { + AbstractClass() { + exists(PureVirtualFunction f| this.getAMemberFunction() = f) + } +} + +/** + * A class template. (This class also finds partial specializations + * of class templates). + */ +class TemplateClass extends Class { + TemplateClass() { usertypes(this,_,6) } + Class getAnInstantiation() { + result.isConstructedFrom(this) and + exists(result.getATemplateArgument()) + } +} + +/** + * A class that is an instantiation of a template. + */ +class ClassTemplateInstantiation extends Class { + ClassTemplateInstantiation() { + exists(TemplateClass tc | tc.getAnInstantiation() = this) + } +} + +/** + * A specialization of a class template. + */ +abstract class ClassTemplateSpecialization extends Class { + /** + * Gets the primary template for the specialization, for example + * S<T,int> -> S<T,U>. + */ + TemplateClass getPrimaryTemplate() { + // Ignoring template arguments, the primary template has the same name + // as each of its specializations. + result.getSimpleName() = getSimpleName() + + // It is in the same namespace as its specializations. + and result.getNamespace() = getNamespace() + + // It is distinguished by the fact that each of its template arguments + // is a distinct template parameter. + and count(TemplateParameter tp | tp = result.getATemplateArgument()) = + count(int i | exists(result.getTemplateArgument(i))) + } +} + +/** + * A full specialization of a class template. + */ +class FullClassTemplateSpecialization extends ClassTemplateSpecialization { + FullClassTemplateSpecialization() { + // This class has template arguments, but none of them involves a template parameter. + exists(getATemplateArgument()) + and not exists(Type ta | ta = getATemplateArgument() and ta.involvesTemplateParameter()) + + // This class does not have any instantiations. + and not exists(this.(TemplateClass).getAnInstantiation()) + + // This class is not an instantiation of a class template. + and not this instanceof ClassTemplateInstantiation + } +} + +/** + * A partial specialization of a class template. + */ +class PartialClassTemplateSpecialization extends ClassTemplateSpecialization { + PartialClassTemplateSpecialization() { + /* + * (a) At least one of this class's template arguments involves a + * template parameter in some respect, for example T, T*, etc. + * + * (b) It is not the case that the n template arguments of this class + * are a set of n distinct template parameters. + * + * template class X {}; // class template + * template class X {}; // partial class template specialization + * template class X {}; // partial class template specialization + * template class Y {}; // class template + * template class Y {}; // partial class template specialization + */ + exists(Type ta | ta = getATemplateArgument() and ta.involvesTemplateParameter()) + and count(TemplateParameter tp | tp = getATemplateArgument()) != + count(int i | exists(getTemplateArgument(i))) + } +} + +/** + * An "interface", in other words a class that only contains pure virtual + * functions. + */ +class Interface extends Class { + Interface() { + forex(Declaration m | m.getDeclaringType() = this.getABaseClass*() and not compgenerated(m) | m instanceof PureVirtualFunction) + } +} + +/** + * A class derivation that is virtual, for example + * "class X : --> virtual public Y <--." + */ +class VirtualClassDerivation extends ClassDerivation { + VirtualClassDerivation() { + hasSpecifier("virtual") + } +} + +/** + * A class that is the base of some virtual class derivation. + */ +class VirtualBaseClass extends Class { + VirtualBaseClass() { + exists(VirtualClassDerivation cd | cd.getBaseClass() = this) + } + + /** A virtual class derivation of which this class is the base. */ + VirtualClassDerivation getAVirtualDerivation() { + result.getBaseClass() = this + } + + /** A class that is derived from this one using virtual inheritance. */ + Class getAVirtuallyDerivedClass() { + result = getAVirtualDerivation().getDerivedClass() + } +} + +/** + * The proxy class (where needed) associated with a template parameter, as + * in the following code: + * + * template <typename T> + * struct S : T // the type of this T is a proxy class + * {}; + */ +class ProxyClass extends UserType { + ProxyClass() { + usertypes(this,_,9) + } + + /** Gets the location of the proxy class. */ + override Location getLocation() { + result = getTemplateParameter().getDefinitionLocation() + } + + /** Gets the template parameter for which this is the proxy class. */ + TemplateParameter getTemplateParameter() { + is_proxy_class_for(this,result) + } +} + +// Unpacks "array of ... of array of t" into t. +private Type stripArrayTypes(Type t) { + not t instanceof ArrayType and result = t or + result = stripArrayTypes(t.(ArrayType).getBaseType()) +} diff --git a/cpp/ql/src/semmle/code/cpp/Comments.qll b/cpp/ql/src/semmle/code/cpp/Comments.qll new file mode 100644 index 000000000000..1176ccfaf785 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/Comments.qll @@ -0,0 +1,30 @@ +import semmle.code.cpp.Location +import semmle.code.cpp.Element + +/** + * A C/C++ comment. + */ +class Comment extends Locatable, @comment { + override string toString() { result = this.getContents() } + override Location getLocation() { comments(this,_,result) } + string getContents() { comments(this,result,_) } + Element getCommentedElement() { commentbinding(this,result) } +} + +/** + * A C style comment (one which starts with `/*`). + */ +class CStyleComment extends Comment { + CStyleComment() { + this.getContents().matches("/*%") + } +} + +/** + * A CPP style comment (one which starts with `//`). + */ +class CppStyleComment extends Comment { + CppStyleComment() { + this.getContents().prefix(2) = "//" + } +} diff --git a/cpp/ql/src/semmle/code/cpp/Compilation.qll b/cpp/ql/src/semmle/code/cpp/Compilation.qll new file mode 100644 index 000000000000..339cea244c6c --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/Compilation.qll @@ -0,0 +1,124 @@ +import semmle.code.cpp.File + +/** + * These two helper predicates are used to associate a unique integer with + * each `@compilation`, for use in the `toString` method of `Compilation`. + * These integers are not stable across trap imports, but stable across + * runs with the same database. + */ +private predicate id(@compilation x, @compilation y) { x = y } +private predicate idOf(@compilation x, int y) = equivalenceRelation(id/2)(x, y) + +/** + * 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 + * + * Three things happen to each file during a compilation: + * + * 1. The file is compiled by a real compiler, such as gcc or VC. + * 2. The file is parsed by Semmle's C++ front-end. + * 3. The parsed representation is converted to database tables by + * Semmle's extractor. + * + * This class provides CPU and elapsed time information for steps 2 and 3, + * but not for step 1. + */ +class Compilation extends @compilation { + /** Gets a textual representation of this element. */ + string toString() { + exists(int i + | idOf(this, i) and + result = "") + } + + /** Gets a file compiled during this invocation. */ + File getAFileCompiled() { result = getFileCompiled(_) } + File getFileCompiled(int i) { + compilation_compiling_files(this, i, result) + } + + /** + * Gets the amount of CPU time spent processing file number `i` in the C++ + * front-end. + */ + float getFrontendCpuSeconds(int i) { + compilation_time(this, i, 1, result) + } + + /** + * Gets the amount of elapsed time while processing file number `i` in the + * C++ front-end. + */ + float getFrontendElapsedSeconds(int i) { + compilation_time(this, i, 2, result) + } + + /** + * Gets the amount of CPU time spent processing file number `i` in the + * extractor. + */ + float getExtractorCpuSeconds(int i) { + compilation_time(this, i, 3, result) + } + + /** + * Gets the amount of elapsed time while processing file number `i` in the + * extractor. + */ + float getExtractorElapsedSeconds(int i) { + compilation_time(this, i, 4, result) + } + + /** + * Gets an argument passed to the extractor on this invocation. + */ + string getAnArgument() { result = getArgument(_) } + + /** + * Gets the `i`th argument passed to the extractor on this invocation. + * + * If the compiler was invoked as `gcc -c f1.c f2.c f3.c` then this + * will typically hold for + * + * i | result + * - | --- + * 0 | *path to extractor* + * 1 | `--mimic` + * 2 | `/usr/bin/gcc` + * 3 | `-c` + * 4 | f1.c + * 5 | f2.c + * 6 | f3.c + */ + string getArgument(int i) { + compilation_args(this, i, result) + } + + /** + * Gets the total amount of CPU time spent processing all the files in the + * front-end and extractor. + */ + float getTotalCpuSeconds() { + compilation_finished(this, result, _) + } + + /** + * Gets the total amount of elapsed time while processing all the files in + * the front-end and extractor. + */ + float getTotalElapsedSeconds() { + compilation_finished(this, _, result) + } + + /** + * Holds if the extractor terminated normally. Terminating with an exit + * code indicating that an error occurred is considered normal + * termination, but crashing due to something like a segfault is not. + */ + predicate normalTermination() { + compilation_finished(this, _, _) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/Declaration.qll b/cpp/ql/src/semmle/code/cpp/Declaration.qll new file mode 100644 index 000000000000..45fc1ec2d6ab --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/Declaration.qll @@ -0,0 +1,573 @@ +import semmle.code.cpp.Element +import semmle.code.cpp.Specifier +import semmle.code.cpp.Namespace + +/** + * A C/C++ declaration: for example, a variable declaration, a type + * declaration, or a function declaration. + * + * This file defines two closely related classes: `Declaration` and + * `DeclarationEntry`. Some declarations do not correspond to a unique + * location in the source code. For example, a global variable might + * be declared in multiple source files: + * + * extern int myglobal; + * + * Each of these declarations is given its own distinct `DeclarationEntry`, + * but they all share the same `Declaration`. + * + * Some derived class of `Declaration` do not have a corresponding + * `DeclarationEntry`, because they always have a unique source location. + * `EnumConstant` and `FriendDecl` are both examples of this. + */ +abstract class Declaration extends Locatable, @declaration { + /** + * Gets the innermost namespace which contains this declaration. + * + * The result will either be GlobalNamespace, or the tightest lexically + * enclosing namespace block. In particular, note that for declarations + * within structures, the namespace of the declaration is the same as the + * namespace of the structure. + */ + Namespace getNamespace() { + // Top level declaration in a namespace ... + result.getADeclaration() = this + + // ... or nested in another structure. + or + exists (Declaration m + | m = this and result = m.getDeclaringType().getNamespace()) + or + exists (EnumConstant c + | c = this and result = c.getDeclaringEnum().getNamespace()) + or + exists (Parameter p + | p = this and result = p.getFunction().getNamespace()) + or + exists (LocalVariable v + | v = this and result = v.getFunction().getNamespace()) + } + + /** + * Gets the name of the declaration, fully qualified with its + * namespace. For example: "A::B::C::myfcn". + */ + string getQualifiedName() { + // MemberFunction, MemberVariable, MemberType + exists (Declaration m + | m = this and + result = m.getDeclaringType().getQualifiedName() + "::" + m.getName()) + or + exists (EnumConstant c + | c = this and + result = c.getDeclaringEnum().getQualifiedName() + "::" + c.getName()) + or + exists (GlobalVariable v, string s1, string s2 + | v = this and + s2 = v.getNamespace().getQualifiedName() and + s1 = v.getName() + | (s2 != "" and result = s2 + "::" + s1) or (s2 = "" and result = s1)) + or + exists (Function f, string s1, string s2 + | f = this and f.isTopLevel() and + s2 = f.getNamespace().getQualifiedName() and + s1 = f.getName() + | (s2 != "" and result = s2 + "::" + s1) or (s2 = "" and result = s1)) + or + exists (UserType t, string s1, string s2 + | t = this and t.isTopLevel() and + s2 = t.getNamespace().getQualifiedName() and + s1 = t.getName() + | (s2 != "" and result = s2 + "::" + s1) or (s2 = "" and result = s1)) + } + + predicate hasQualifiedName(string name) { + this.getQualifiedName() = name + } + + override string toString() { result = this.getName() } + + /** Gets the name of this declaration. */ + abstract string getName(); + predicate hasName(string name) { name = this.getName() } + + /** Holds if this element has the given name in the global namespace. */ + predicate hasGlobalName(string name) { + hasName(name) + and getNamespace() instanceof GlobalNamespace + } + + /** Gets a specifier of this declaration. */ + abstract Specifier getASpecifier(); + + /** Holds if this declaration has a specifier with the given name. */ + predicate hasSpecifier(string name) { + this.getASpecifier().hasName(name) + } + + /** + * Gets a declaration entry corresponding to this declaration. See the + * comment above this class for an explanation of the relationship + * between `Declaration` and `DeclarationEntry`. + */ + DeclarationEntry getADeclarationEntry() { + none() + } + + /** + * Gets the location of a declaration entry corresponding to this + * declaration. + */ + abstract Location getADeclarationLocation(); + + /** + * Gets the declaration entry corresponding to this declaration that is a + * definition, if any. + */ + DeclarationEntry getDefinition() { + none() + } + + /** Gets the location of the definition, if any. */ + abstract Location getDefinitionLocation(); + + /** Holds if the declaration has a definition. */ + predicate hasDefinition() { exists(this.getDefinition()) } + predicate isDefined() { hasDefinition() } + + /** Gets the preferred location of this declaration, if any. */ + override Location getLocation() { + none() + } + + /** Gets a file where this element occurs. */ + File getAFile() { result = this.getADeclarationLocation().getFile() } + + /** Holds if this declaration is a top-level declaration. */ + predicate isTopLevel() { + not (this.isMember() or + this instanceof EnumConstant or + this instanceof Parameter or + this instanceof ProxyClass or + this instanceof LocalVariable or + this instanceof TemplateParameter or + this.(UserType).isLocal()) + } + + /** Holds if this declaration is static. */ + predicate isStatic() { this.hasSpecifier("static") } + + /** Holds if this declaration is a member of a class/struct/union. */ + predicate isMember() { hasDeclaringType() } + + /** Holds if this declaration is a member of a class/struct/union. */ + predicate hasDeclaringType() { + exists(this.getDeclaringType()) + } + + /** + * Gets the class where this member is declared, if it is a member. + * For templates, both the template itself and all instantiations of + * the template are considered to have the same declaring class. + */ + Class getDeclaringType() { + this = result.getAMember() + } +} + +/** + * A C/C++ declaration entry. See the comment above `Declaration` for an + * explanation of the relationship between `Declaration` and + * `DeclarationEntry`. + */ +abstract class DeclarationEntry extends Locatable { + /** a specifier associated with this declaration entry */ + abstract string getASpecifier(); + + /** + * Gets the name associated with the corresponding definition (where + * available), or the name declared by this entry otherwise. + */ + string getCanonicalName() { + if getDeclaration().isDefined() then + result = getDeclaration().getDefinition().getName() + else + result = getName() + } + + /** + * Gets the declaration for which this is a declaration entry. + * + * Note that this is *not* always the inverse of + * Declaration.getADeclarationEntry(), for example if C is a + * TemplateClass, I is an instantiation of C, and D is a Declaration of + * C, then: + * C.getADeclarationEntry() returns D + * I.getADeclarationEntry() returns D + * but D.getDeclaration() only returns C + */ + abstract Declaration getDeclaration(); + + /** Gets the name associated with this declaration entry, if any. */ + abstract string getName(); + + /** + * Gets the type associated with this declaration entry. + * + * For variable declarations, get the type of the variable. + * For function declarations, get the return type of the function. + * For type declarations, get the type being declared. + */ + abstract Type getType(); + + /** + * Holds if this declaration entry has a specifier with the given name. + */ + predicate hasSpecifier(string specifier) { + getASpecifier() = specifier + } + + /** Holds if this declaration entry is a definition. */ + abstract predicate isDefinition(); + + override string toString() { + if isDefinition() then + result = "definition of " + getName() + else if getName() = getCanonicalName() then + result = "declaration of " + getName() + else + result = "declaration of " + getCanonicalName() + " as " + getName() + } +} + + +/** + * A declaration that can potentially have more C++ access rights than its + * enclosing element. This comprises `Class` (they have access to their own + * private members) along with other `UserType`s and `Function` (they can be + * the target of `friend` declarations). + * + * In the C++ standard (N4140 11.2), rules for access control revolve around + * the informal phrase "_R_ occurs in a member or friend of class C", where + * `AccessHolder` corresponds to this _R_. + */ +abstract class AccessHolder extends Declaration { + /** + * Holds if `this` can access private members of class `c`. + * + * This predicate encodes the phrase "occurs in a member or friend" that is + * repeated many times in the C++14 standard, section 11.2. + */ + predicate inMemberOrFriendOf(Class c) { + ( + this.getEnclosingAccessHolder*() = c + ) or ( + exists(FriendDecl fd | fd.getDeclaringClass() = c | + this.getEnclosingAccessHolder*() = fd.getFriend() + ) + ) + } + + /** + * Gets the nearest enclosing AccessHolder. + */ + abstract AccessHolder getEnclosingAccessHolder(); + + /** + * Holds if a base class `base` of `derived` _is accessible at_ `this` (N4140 + * 11.2/4). When this holds, and `derived` has only one base subobject of + * type `base`, code in `this` can implicitly convert a pointer to `derived` + * into a pointer to `base`. Conversely, if such a conversion is possible + * then this predicate holds. + * + * For the sake of generality, this predicate also holds whenever `base` = + * `derived`. + * + * This predicate is `pragma[inline]` because it is infeasible to fully + * compute it on large code bases: all classes `derived` can be converted to + * their public bases `base` from everywhere (`this`), so this predicate + * could yield a number of tuples that is quadratic in the size of the + * program. To avoid this combinatorial explosion, only use this predicate in + * a context where `this` together with `base` or `derived` are sufficiently + * restricted. + */ + pragma[inline] + predicate canAccessClass(Class base, Class derived) { + // This predicate is marked `inline` and implemented in a very particular + // way. If we allowed this predicate to be fully computed, it would relate + // all `AccessHolder`s to all classes, which would be too much. + + // There are four rules in N4140 11.2/4. Only the one named (4.4) is + // recursive, and it describes a transitive closure: intuitively, if A can + // be converted to B, and B can be converted to C, then A can be converted + // to C. To limit the number of tuples in the non-inline helper predicates, + // we first separate the derivation of 11.2/4 into two cases: + + // Derivations using only (4.1) and (4.4). Note that these derivations are + // independent of `this`, which is why users of this predicate must take + // care to avoid a combinatorial explosion. + isDirectPublicBaseOf*(base, derived) + or + exists(DirectAccessHolder n | + this.getEnclosingAccessHolder*() = n and + // Derivations using (4.2) or (4.3) at least once. + n.thisCanAccessClassTrans(base, derived) + ) + } + + /** + * Holds if a non-static member `member` _is accessible at_ `this` when named + * in a class `derived` that is derived from or equal to the declaring class + * of `member` (N4140 11.2/5 and 11.4). + * + * This predicate determines whether an expression `x.member` would be + * allowed in `this` when `x` has type `derived`. The more general syntax + * `x.N::member`, where `N` may be a base class of `derived`, is not + * supported. This should only affect very rare edge cases of 11.4. This + * predicate concerns only _access_ and thus does not determine whether + * `member` can be unambiguously named at `this`: multiple overloads may + * apply, or `member` may be declared in an ambiguous base class. + * + * This predicate is `pragma[inline]` because it is infeasible to fully + * compute it on large code bases: all public members `member` are accessible + * from everywhere (`this`), so this predicate could yield a number of tuples + * that is quadratic in the size of the program. To avoid this combinatorial + * explosion, only use this predicate in a context where `this` and `member` + * are sufficiently restricted when `member` is public. + */ + pragma[inline] + predicate canAccessMember(Declaration member, Class derived) { + this.couldAccessMember( + member.getDeclaringType(), + member.getASpecifier().(AccessSpecifier), + derived + ) + } + + /** + * Holds if a hypothetical non-static member of `memberClass` with access + * specifier `memberAccess` _is accessible at_ `this` when named in a class + * `derived` that is derived from or equal to `memberClass` (N4140 11.2/5 and + * 11.4). + * + * This predicate determines whether an expression `x.m` would be + * allowed in `this` when `x` has type `derived` and `m` has `memberAccess` + * in `memberClass`. The more general syntax `x.N::n`, where `N` may be a + * base class of `derived`, is not supported. This should only affect very + * rare edge cases of 11.4. + * + * This predicate is `pragma[inline]` because it is infeasible to fully + * compute it on large code bases: all classes `memberClass` have their + * public members accessible from everywhere (`this`), so this predicate + * could yield a number of tuples that is quadratic in the size of the + * program. To avoid this combinatorial explosion, only use this predicate in + * a context where `this` and `memberClass` are sufficiently restricted when + * `memberAccess` is public. + */ + pragma[inline] + predicate couldAccessMember(Class memberClass, AccessSpecifier memberAccess, + Class derived) + { + // There are four rules in N4140 11.2/5. To limit the number of tuples in + // the non-inline helper predicates, we first separate the derivation of + // 11.2/5 into two cases: + + // Rule (5.1) directly: the member is public, and `derived` uses public + // inheritance all the way up to `memberClass`. Note that these derivations + // are independent of `this`, which is why users of this predicate must + // take care to avoid a combinatorial explosion. + everyoneCouldAccessMember(memberClass, memberAccess, derived) + or + exists(DirectAccessHolder n | + this.getEnclosingAccessHolder*() = n and + // Any other derivation. + n.thisCouldAccessMember(memberClass, memberAccess, derived) + ) + } +} + +/** + * A declaration that very likely has more C++ access rights than its + * enclosing element. This comprises `Class` (they have access to their own + * private members) along with any target of a `friend` declaration. + * + * Most access rights are computed for `DirectAccessHolder` instead of + * `AccessHolder` -- that's more efficient because there are fewer + * `DirectAccessHolder`s. If a `DirectAccessHolder` contains an `AccessHolder`, + * then the contained `AccessHolder` inherits its access rights. + */ +private class DirectAccessHolder extends @declaration { + DirectAccessHolder() { + this instanceof Class + or + exists(FriendDecl fd | fd.getFriend() = this) + } + + /** + * Holds if a base class `base` of `derived` _is accessible at_ `this` when + * the derivation of that fact uses rule (4.2) and (4.3) of N4140 11.2/4 at + * least once. In other words, the `this` parameter is not ignored. This + * restriction makes it feasible to fully enumerate this predicate even on + * large code bases. + */ + predicate thisCanAccessClassTrans(Class base, Class derived) { + // This implementation relies on the following property of our predicates: + // if `this.thisCanAccessClassStep(b, d)` and + // `isDirectPublicBaseOf(b2, b)`, then + // `this.thisCanAccessClassStep(b2, d)`. In other words, if a derivation + // uses (4.2) or (4.3) somewhere and uses (4.1) directly above that in the + // transitive chain, then the use of (4.1) is redundant. This means we only + // need to consider derivations that use (4.2) or (4.3) as the "first" + // step, that is, towards `base`, so this implementation is essentially a + // transitive closure with a restricted base case. + this.thisCanAccessClassStep(base, derived) + or + exists(Class between | thisCanAccessClassTrans(base, between) | + isDirectPublicBaseOf(between, derived) or + this.thisCanAccessClassStep(between, derived) + ) + + // It is possible that this predicate could be computed faster for deep + // hierarchies if we can prove and utilize that all derivations of 11.2/4 + // can be broken down into steps where `base` is a _direct_ base of + // `derived` in each step. + } + + /** + * Holds if a base class `base` of `derived` _is accessible at_ `this` using + * only a single application of rule (4.2) and (4.3) of N4140 11.2/4. + */ + private predicate thisCanAccessClassStep(Class base, Class derived) { + exists(AccessSpecifier public | public.hasName("public") | + // Rules (4.2) and (4.3) are implemented together as one here with + // reflexive-transitive inheritance, where (4.3) is the transitive case, + // and (4.2) is the reflexive case. + exists(Class p | p = derived.getADerivedClass*() | + this.isFriendOfOrEqualTo(p) and + // Note: it's crucial that this is `!=` rather than `not =` since + // accessOfBaseMember does not have a result when the member would be + // inaccessible. + p.accessOfBaseMember(base, public) != public + ) + ) and + // This is the only case that doesn't in itself guarantee that + // `derived` < `base`, so we add the check here. The standard suggests + // computing `canAccessClass` only for derived classes, but that seems + // incompatible with the execution model of QL, so we instead construct + // every case to guarantee `derived` < `base`. + derived = base.getADerivedClass+() + } + + /** + * Like `couldAccessMember` but only contains derivations in which either + * (5.2), (5.3) or (5.4) must be invoked. In other words, the `this` + * parameter is not ignored. This restriction makes it feasible to fully + * enumerate this predicate even on large code bases. We check for 11.4 as + * part of (5.3), since this further limits the number of tuples produced by + * this predicate. + */ + predicate thisCouldAccessMember(Class memberClass, + AccessSpecifier memberAccess, + Class derived) + { + // Only (5.4) is recursive, and chains of invocations of (5.4) can always + // be collapsed to one invocation by the transitivity of 11.2/4. + // Derivations not using (5.4) can always be rewritten to have a (5.4) rule + // in front because our encoding of 11.2/4 in `canAccessClass` is + // reflexive. Thus, we only need to consider three cases: rule (5.4) + // followed by either (5.1), (5.2) or (5.3). + + // Rule (5.4), using a non-trivial derivation of 11.2/4, followed by (5.1). + // If the derivation of 11.2/4 is trivial (only uses (4.1) and (4.4)), this + // case can be replaced with purely (5.1) and thus does not need to be in + // this predicate. + exists(Class between | this.thisCanAccessClassTrans(between, derived) | + everyoneCouldAccessMember(memberClass, memberAccess, between) + ) + or + // Rule (5.4) followed by Rule (5.2) + exists(Class between | this.(AccessHolder).canAccessClass(between, derived) | + between.accessOfBaseMember(memberClass, memberAccess) + .hasName("private") and + this.isFriendOfOrEqualTo(between) + ) + or + // Rule (5.4) followed by Rule (5.3), integrating 11.4. We integrate 11.4 + // here because we would otherwise generate too many tuples. This code is + // very performance-sensitive, and any changes should be benchmarked on + // LibreOffice. + + // Rule (5.4) requires that `this.canAccessClass(between, derived)` + // (implying that `derived <= between` in the class hierarchy) and that + // `p <= between`. Rule 11.4 additionally requires `derived <= p`, but + // all these rules together result in too much freedom and overlap between + // cases. Therefore, for performance, we split into three cases for how + // `between` as a base of `derived` is accessible at `this`, where `this` + // is the implementation of `p`: + // 1. `between` is an accessible base of `derived` by going through `p` as + // an intermediate step. + // 2. `this` is part of the implementation of `derived` because it's a + // member or a friend. In this case, we do not need `p` to perform this + // derivation, so we can set `p = derived` and proceed as in case 1. + // 3. `derived` has an alternative inheritance path up to `between` that + // bypasses `p`. Then that path must be public, or we are in case 2. + exists(AccessSpecifier public | public.hasName("public") | + exists(Class between, Class p | + between.accessOfBaseMember(memberClass, memberAccess) + .hasName("protected") and + this.isFriendOfOrEqualTo(p) and + ( + // This is case 1 from above. If `p` derives privately from `between` + // then the member we're trying to access is private or inaccessible + // in `derived`, so either rule (5.2) applies instead, or the member + // is inaccessible. Therefore, in this case, `p` must derive at least + // protected from `between`. Further, since the access of `derived` + // to its base `between` must pass through `p` in this case, we know + // that `derived` must derived publicly from `p` unless we are in + // case 2; there are no other cases of 11.2/4 where the + // implementation of a base class can access itself as a base. + p.accessOfBaseMember(between, public).getName() >= "protected" and + derived.accessOfBaseMember(p, public) = public + or + // This is case 3 above. + derived.accessOfBaseMember(between, public) = public and + derived = p.getADerivedClass*() and + exists(p.accessOfBaseMember(between, memberAccess)) + ) + ) + ) + } + + private predicate isFriendOfOrEqualTo(Class c) { + exists(FriendDecl fd | fd.getDeclaringClass() = c | this = fd.getFriend()) + or + this = c + } + + string toString() { result = this.(Declaration).toString() } +} + +/** + * Holds if `base` is a direct public base of `derived`, possibly virtual and + * possibly through typedefs. The transitive closure of this predicate encodes + * derivations of N4140 11.2/4 that use only (4.1) and (4.4). + */ +private predicate isDirectPublicBaseOf(Class base, Class derived) { + exists(ClassDerivation cd | + cd.getBaseClass() = base and + cd.getDerivedClass() = derived and + cd.getASpecifier().hasName("public") + ) +} + +/** + * Holds if a hypothetical member of `memberClass` with access specifier + * `memberAccess` would be public when named as a member of `derived`. + * This encodes N4140 11.2/5 case (5.1). + */ +private predicate everyoneCouldAccessMember(Class memberClass, + AccessSpecifier memberAccess, + Class derived) +{ + derived.accessOfBaseMember(memberClass, memberAccess).hasName("public") +} diff --git a/cpp/ql/src/semmle/code/cpp/Diagnostics.qll b/cpp/ql/src/semmle/code/cpp/Diagnostics.qll new file mode 100644 index 000000000000..692da6d39f8b --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/Diagnostics.qll @@ -0,0 +1,66 @@ +import semmle.code.cpp.Location + +/** A compiler-generated error, warning or remark. */ +class Diagnostic extends Locatable, @diagnostic { + + /** + * Gets the severity of the message, on a range from 1 to 5: 1=remark, + * 2=warning, 3=discretionary error, 4=error, 5=catastrophic error. + */ + int getSeverity() { diagnostics(this, result, _, _, _, _) } + + /** Gets the error code for this compiler message. */ + string getTag() { diagnostics(this, _, result, _, _, _) } + predicate hasTag(string s) { this.getTag() = s } + + /** + * Gets the error message text associated with this compiler + * diagnostic. + */ + string getMessage() { diagnostics(this, _, _, result, _, _) } + + /** + * Gets the full error message text associated with this compiler + * diagnostic. + */ + string getFullMessage() { diagnostics(this, _, _, _, result, _) } + + /** Gets the source location corresponding to the compiler message. */ + override Location getLocation() { diagnostics(this, _, _, _, _, result) } + + override string toString() { result = this.getMessage() } + +} + +/** A compiler-generated remark (milder than a warning). */ +class CompilerRemark extends Diagnostic { + + CompilerRemark() { this.getSeverity() = 1 } +} + +/** A compiler-generated warning. */ +class CompilerWarning extends Diagnostic { + + CompilerWarning() { this.getSeverity() = 2 } +} + +/** + * A compiler-generated discretionary error (a compile-time error that may + * be suppressed). + */ +class CompilerDiscretionaryError extends Diagnostic { + + CompilerDiscretionaryError() { this.getSeverity() = 3 } +} + +/** A compiler error message. */ +class CompilerError extends Diagnostic { + + CompilerError() { this.getSeverity() = 4 } +} + +/** A compiler error that prevents compilation from continuing. */ +class CompilerCatastrophe extends Diagnostic { + + CompilerCatastrophe() { this.getSeverity() = 5 } +} diff --git a/cpp/ql/src/semmle/code/cpp/Element.qll b/cpp/ql/src/semmle/code/cpp/Element.qll new file mode 100644 index 000000000000..a4e6eb44fda7 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/Element.qll @@ -0,0 +1,254 @@ +import semmle.code.cpp.Location +private import semmle.code.cpp.Enclosing +private import semmle.code.cpp.internal.Type + +/** + * A C/C++ element. This class is the base class for all C/C++ + * elements, such as functions, classes, expressions, and so on. + */ +class Element extends @element { + Element() { + isElement(this) + } + + /** Gets a textual representation of this element. */ + string toString() { none() } + + /** Gets the primary file where this element occurs. */ + File getFile() { result = this.getLocation().getFile() } + + /** + * Holds if this element may be from source. + * + * Note: this predicate is provided for consistency with the libraries + * for other languages, such as Java and Python. In C++, all files are + * classified as source files, so this predicate is always true. + */ + predicate fromSource() { this.getFile().fromSource() } + + /** + * Holds if this element may be from a library. + * + * DEPRECATED: always true. + */ + deprecated + predicate fromLibrary() { this.getFile().fromLibrary() } + + /** Gets the primary location of this element. */ + Location getLocation() { + none() + } + + /** + * Gets the source of this element: either itself or a macro that expanded + * to this element. + * + * If the element is not in a macro expansion, then the "root" is just + * the element itself. Otherwise, it is the definition of the innermost + * macro whose expansion the element is in. + * + * This method is useful for filtering macro results in checks: simply + * blame `e.findRootCause` rather than `e`. This will report only bugs + * that are not in macros, and in addition report macros that (somewhere) + * expand to a bug. + */ + Element findRootCause() { + if (exists(MacroInvocation mi | this = mi.getAGeneratedElement())) then + exists(MacroInvocation mi | + this = mi.getAGeneratedElement() and + not exists(MacroInvocation closer | + this = closer.getAGeneratedElement() and + mi = closer.getParentInvocation+() + ) and + result = mi.getMacro() + ) + else + result = this + } + + /** + * Gets the parent scope of this `Element`, if any. + * A scope is a `Type` (`Class` / `Enum`), a `Namespace`, a `Block`, a `Function`, + * or certain kinds of `Statement`. + */ + Element getParentScope() { + // result instanceof class + exists (Declaration m + | m = this and + result = m.getDeclaringType() and + not this instanceof EnumConstant) + or + exists (TemplateClass tc + | this = tc.getATemplateArgument() and result = tc) + + // result instanceof namespace + or + exists (Namespace n + | result = n and n.getADeclaration() = this) + or + exists (FriendDecl d, Namespace n + | this = d and n.getADeclaration() = d and result = n) + or + exists (Namespace n + | this = n and result = n.getParentNamespace()) + + // result instanceof stmt + or + exists (LocalVariable v + | this = v and + exists (DeclStmt ds + | ds.getADeclaration() = v and result = ds.getParent())) + or + exists (Parameter p + | this = p and result = p.getFunction()) + or + exists (GlobalVariable g, Namespace n + | this = g and n.getADeclaration() = g and result = n) + or + exists (EnumConstant e + | this = e and result = e.getDeclaringEnum()) + + // result instanceof block|function + or + exists (Block b + | this = b and blockscope(b, result)) + or + exists (TemplateFunction tf + | this = tf.getATemplateArgument() and result = tf) + + // result instanceof stmt + or + exists (ControlStructure s + | this = s and result = s.getParent()) + + or + using_container(result, this) + } + + /** + * Holds if this element comes from a macro expansion. Only elements that + * are entirely generated by a macro are included - for elements that + * partially come from a macro, see `isAffectedByMacro`. + */ + predicate isInMacroExpansion() { + inMacroExpansion(this) + } + + /** + * Holds if this element is affected in any way by a macro. All elements + * that are totally or partially generated by a macro are included, so + * this is a super-set of `isInMacroExpansion`. + */ + predicate isAffectedByMacro() { + affectedByMacro(this) + } + + private Element getEnclosingElementPref() { + enclosingfunction(this, result) or + result.(Function) = stmtEnclosingElement(this) or + this.(LocalScopeVariable).getFunction() = result or + enumconstants(this, result, _, _, _, _) or + derivations(this, result, _, _, _) or + stmtparents(this, _, result) or + exprparents(this, _, result) or + namequalifiers(this, result, _, _) or + initialisers(this, result, _, _) or + exprconv(result, this) or + this = result.(MacroAccess).getParentInvocation() or + result = this.(MacroInvocation).getExpr() or // macroinvocation -> outer Expr + param_decl_bind(this,_,result) + } + + /** Gets the closest `Element` enclosing this one. */ + cached Element getEnclosingElement() { + result = getEnclosingElementPref() or + ( + not exists(getEnclosingElementPref()) and + ( + // macroinvocation -> all enclosed elements + inmacroexpansion(result, this) + or + macrolocationbind( + this.(MacroInvocation), + result.(VariableDeclarationEntry).getLocation()) + or + macrolocationbind( + this.(MacroInvocation), + result.(FunctionDeclarationEntry).getLocation()) + or + this = result.(Class).getAMember() + or + result = exprEnclosingElement(this) + or + var_decls(this, result, _, _, _) + ) + ) + } + + /** + * Holds if this `Element` is a part of a template instantiation (but not + * the template itself). + */ + predicate isFromTemplateInstantiation(Element instantiation) { + exists(Element e | + isFromTemplateInstantiationRec(e, instantiation) | + this = e or + this.(DeclarationEntry).getDeclaration() = e + ) + } + + /** + * Holds if this `Element` is part of a template `template` (not if it is + * part of an instantiation of `template`). This means it is represented in + * the database purely as syntax and without guarantees on the presence or + * correctness of type-based operations such as implicit conversions. + * + * If an element is nested within several templates, this predicate holds with + * a value of `template` for each containing template. + */ + predicate isFromUninstantiatedTemplate(Element template) { + exists(Element e | + isFromUninstantiatedTemplateRec(e, template) | + this = e or + this.(DeclarationEntry).getDeclaration() = e + ) + } +} + +private predicate isFromTemplateInstantiationRec(Element e, Element instantiation) { + instantiation.(Function).isConstructedFrom(_) and + e = instantiation + or + instantiation.(Class).isConstructedFrom(_) and + e = instantiation + or + instantiation.(Variable).isConstructedFrom(_) and + e = instantiation + or + isFromTemplateInstantiationRec(e.getEnclosingElement(), instantiation) and + not e instanceof MacroAccess +} + +private predicate isFromUninstantiatedTemplateRec(Element e, Element template) { + is_class_template(template) and + e = template + or + is_function_template(template) and + e = template + or + is_variable_template(template) and + e = template + or + isFromUninstantiatedTemplateRec(e.getEnclosingElement(), template) and + not e instanceof MacroAccess +} + +/** + * A C++11 `static_assert` or C11 `_Static_assert` construct. + */ +class StaticAssert extends Locatable, @static_assert { + override string toString() { result = "static_assert(..., \"" + getMessage() + "\")" } + Expr getCondition() { static_asserts(this, result, _, _) } + string getMessage() { static_asserts(this, _, result, _) } + override Location getLocation() { static_asserts(this, _, _, result) } +} diff --git a/cpp/ql/src/semmle/code/cpp/Enclosing.qll b/cpp/ql/src/semmle/code/cpp/Enclosing.qll new file mode 100644 index 000000000000..525b04a79f74 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/Enclosing.qll @@ -0,0 +1,36 @@ +import cpp + +/** + * Gets the enclosing element of statement `s`. + */ +cached Element stmtEnclosingElement(Stmt s) { + result.(Function).getEntryPoint() = s or + result = stmtEnclosingElement(s.getParent()) or + result = exprEnclosingElement(s.getParent()) +} + +/** + * Gets the enclosing element of expression `e`. + */ +cached Element exprEnclosingElement(Expr e) { + result = exprEnclosingElement(e.getParent()) or + result = stmtEnclosingElement(e.getParent()) or + result.(Function) = e.getParent() or + result = exprEnclosingElement(e.(Conversion).getExpr()) or + exists(Initializer i | i.getExpr() = e and + if exists(i.getEnclosingStmt()) + then result = stmtEnclosingElement(i.getEnclosingStmt()) + else if i.getDeclaration() instanceof Parameter + then result = i.getDeclaration().(Parameter).getFunction() + else result = i.getDeclaration()) or + exists(Expr anc | expr_ancestor(e, anc) and result = exprEnclosingElement(anc)) or + exists(Stmt anc | expr_ancestor(e, anc) and result = stmtEnclosingElement(anc)) or + exists(DeclarationEntry de | + expr_ancestor(e, de) and + if exists(DeclStmt ds | de = ds.getADeclarationEntry()) + then exists(DeclStmt ds | + de = ds.getADeclarationEntry() and + result = stmtEnclosingElement(ds)) + else result = de.getDeclaration()) +} + diff --git a/cpp/ql/src/semmle/code/cpp/Enum.qll b/cpp/ql/src/semmle/code/cpp/Enum.qll new file mode 100644 index 000000000000..c699803fbeaa --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/Enum.qll @@ -0,0 +1,138 @@ +import semmle.code.cpp.Type +private import semmle.code.cpp.internal.Type + +/** + * A C/C++ enum [N4140 7.2]. + */ +class Enum extends UserType, IntegralOrEnumType { + /** Gets an enumerator of this enumeration. */ + EnumConstant getAnEnumConstant() { result.getDeclaringEnum() = this } + EnumConstant getEnumConstant(int index) { enumconstants(result,this,index,_,_,_) } + + /** + * Gets a descriptive string for the enum. This method is only intended to + * be used for debugging purposes. For more information, see the comment + * for `Type.explain`. + */ + override string explain() { result = "enum " + this.getName() } + + /** See `Type.isDeeplyConst` and `Type.isDeeplyConstBelow`. Internal. */ + override predicate isDeeplyConstBelow() { any() } // No subparts + + /** + * Holds if this enum has an enum-base [N4140 7.2]. + * For example: `enum E : int`. + */ + predicate hasExplicitUnderlyingType() { + derivations(_, this, _, _, _) + } + + /** + * The type of the enum-base [N4140 7.2], if it is specified. + * For example: `int` in `enum E : int`. + */ + Type getExplicitUnderlyingType() { + derivations(_, this, _, result, _) + } +} + +/** + * A C++ enum that is directly enclosed by a function. + */ +class LocalEnum extends Enum { + LocalEnum() { + isLocal() + } +} + +/** + * A C++ enum that is declared within a class. + */ +class NestedEnum extends Enum { + + NestedEnum() { + this.isMember() + } + + /** 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") } + +} + +/** + * A C++ scoped enum. + * + * For example, `enum class Color { red, blue }`. + */ +class ScopedEnum extends Enum { + ScopedEnum() { + usertypes(this,_,13) + } +} + +/** + * A C/C++ enumerator [N4140 7.2]. + * + * For example: `green` in `enum { red, green, blue }`. + * + * Enumerators are also knowns as enumeration constants. + */ +class EnumConstant extends @enumconstant, Declaration { + /** + * Gets the enumeration of which this enumerator is a member. + */ + Enum getDeclaringEnum() { enumconstants(this,result,_,_,_,_) } + + override Class getDeclaringType() { + result = this.getDeclaringEnum().getDeclaringType() + } + + /** + * Gets the name of this enumerator. + */ + override string getName() { enumconstants(this,_,_,_,result,_) } + + /** + * Gets the value that this enumerator is initialized to, as a + * string. This can be a value explicitly given to the enumerator, or an + * automatically assigned value. + */ + string getValue() { result = this.getInitializer().getExpr().getValue() } + + /** Gets the type of this enumerator. */ + Type getType() { enumconstants(this,_,_,unresolve(result),_,_) } + + /** Gets the location of a declaration of this enumerator. */ + override Location getADeclarationLocation() { result = this.getDefinitionLocation() } + + /** Gets the location of the definition of this enumerator. */ + override Location getDefinitionLocation() { enumconstants(this,_,_,_,_,result) } + + /** Gets the location of the definition of this enumerator. */ + override Location getLocation() { result = this.getDefinitionLocation() } + + /** Gets the initializer of this enumerator, if any. */ + Initializer getInitializer() { result.getDeclaration() = this } + + /** Gets an access of this enumerator. */ + EnumConstantAccess getAnAccess() { result.getTarget() = this } + + /** Gets a specifier of this enumerator. */ + override Specifier getASpecifier() { varspecifiers(this,result) } + + /** + * An attribute of this enumerator. + * + * Note that allowing attributes on enumerators is a language extension + * which is only supported by Clang. + */ + Attribute getAnAttribute() { + varattributes(this, result) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/Field.qll b/cpp/ql/src/semmle/code/cpp/Field.qll new file mode 100644 index 000000000000..87b07e9b2909 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/Field.qll @@ -0,0 +1,107 @@ +import semmle.code.cpp.Variable +import semmle.code.cpp.Enum +import semmle.code.cpp.exprs.Access + +/** + * A C structure member or C++ non-static member variable. + */ +class Field extends MemberVariable { + + Field() { + fieldoffsets(this,_,_) + } + + /** + * Gets the offset of this field in bytes from the start of its declaring + * type (on the machine where facts were extracted). + */ + int getByteOffset() { fieldoffsets(this,result,_) } + + /** + * Gets the byte offset within `mostDerivedClass` of each occurence of this + * field within `mostDerivedClass` itself or a base class subobject of + * `mostDerivedClass`. + * Note that for fields of virtual base classes, and non-virtual base classes + * thereof, this predicate assumes that `mostDerivedClass` is the type of the + * complete most-derived object. + */ + int getAByteOffsetIn(Class mostDerivedClass) { + result = mostDerivedClass.getABaseClassByteOffset(getDeclaringType()) + + getByteOffset() + } + + /** + * Holds if the field can be initialized as part of an initializer list. For + * example, in: + * + * struct S { + * unsigned int a : 5; + * unsigned int : 5; + * unsigned int b : 5; + * }; + * + * Fields `a` and `b` are initializable, but the unnamed bitfield is not. + */ + predicate isInitializable() { + // All non-bitfield fields are initializable. This predicate is overridden + // in `BitField` to handle the anonymous bitfield case. + any() + } + + /** + * Gets the zero-based index of the specified field within its enclosing + * class, counting only fields that can be initialized. This is the order in + * which the field will be initialized, whether by an initializer list or in a + * constructor. + */ + final int getInitializationOrder() { + exists(Class cls, int memberIndex | + this = cls.getCanonicalMember(memberIndex) and + memberIndex = rank[result + 1](int index | + cls.getCanonicalMember(index).(Field).isInitializable() + ) + ) + } +} + +/** + * A C structure member or C++ member variable declared with an explicit size in bits. + * + * Syntactically, this looks like `int x : 3` in `struct S { int x : 3; };`. + */ +class BitField extends Field { + BitField() { bitfield(this,_,_) } + + /** + * Gets the size of this bitfield in bits (on the machine where facts + * were extracted). + */ + int getNumBits() { bitfield(this,result,_) } + + /** + * Gets the value which appeared after the colon in the bitfield + * declaration. + * + * In most cases, this will give the same value as `getNumBits`. It will + * only differ when the value after the colon is larger than the size of + * the variable's type. For example, given `int32_t x : 1234`, + * `getNumBits` will give 32, whereas `getDeclaredNumBits` will give + * 1234. + */ + int getDeclaredNumBits() { bitfield(this,_,result) } + + /** + * Gets the offset of this bitfield in bits from the byte identified by + * getByteOffset (on the machine where facts were extracted). + */ + int getBitOffset() { fieldoffsets(this,_,result) } + + predicate isAnonymous() { + hasName("(unnamed bitfield)") + } + + override predicate isInitializable() { + // Anonymous bitfields are not initializable. + not isAnonymous() + } +} diff --git a/cpp/ql/src/semmle/code/cpp/File.qll b/cpp/ql/src/semmle/code/cpp/File.qll new file mode 100644 index 000000000000..3526739d5b46 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/File.qll @@ -0,0 +1,507 @@ +import semmle.code.cpp.Element +import semmle.code.cpp.Declaration +import semmle.code.cpp.metrics.MetricFile + +/** A file or folder. */ +abstract class Container extends Locatable, @container { + /** + * Gets the absolute, canonical path of this container, using forward slashes + * as path separator. + * + * The path starts with a _root prefix_ followed by zero or more _path + * segments_ separated by forward slashes. + * + * The root prefix is of one of the following forms: + * + * 1. A single forward slash `/` (Unix-style) + * 2. An upper-case drive letter followed by a colon and a forward slash, + * such as `C:/` (Windows-style) + * 3. Two forward slashes, a computer name, and then another forward slash, + * such as `//FileServer/` (UNC-style) + * + * Path segments are never empty (that is, absolute paths never contain two + * contiguous slashes, except as part of a UNC-style root prefix). Also, path + * segments never contain forward slashes, and no path segment is of the + * form `.` (one dot) or `..` (two dots). + * + * Note that an absolute path never ends with a forward slash, except if it is + * a bare root prefix, that is, the path has no path segments. A container + * whose absolute path has no segments is always a `Folder`, not a `File`. + */ + abstract string getAbsolutePath(); + + /** + * DEPRECATED: Use `getLocation` instead. + * Gets a URL representing the location of this container. + * + * For more information see https://lgtm.com/help/ql/locations#providing-urls. + */ + deprecated abstract string getURL(); + + /** + * Gets the relative path of this file or folder from the root folder of the + * analyzed source location. The relative path of the root folder itself is + * the empty string. + * + * This has no result if the container is outside the source root, that is, + * if the root folder is not a reflexive, transitive parent of this container. + */ + string getRelativePath() { + exists (string absPath, string pref | + absPath = getAbsolutePath() and sourceLocationPrefix(pref) | + absPath = pref and result = "" + or + absPath = pref.regexpReplaceAll("/$", "") + "/" + result and + not result.matches("/%") + ) + } + + /** + * Gets the base name of this container including extension, that is, the last + * segment of its absolute path, or the empty string if it has no segments. + * + * Here are some examples of absolute paths and the corresponding base names + * (surrounded with quotes to avoid ambiguity): + * + * + * + * + * + * + * + * + * + *
    Absolute pathBase name
    "/tmp/tst.js""tst.js"
    "C:/Program Files (x86)""Program Files (x86)"
    "/"""
    "C:/"""
    "D:/"""
    "//FileServer/"""
    + */ + string getBaseName() { + result = getAbsolutePath().regexpCapture(".*/(([^/]*?)(?:\\.([^.]*))?)", 1) + } + + /** + * Gets the extension of this container, that is, the suffix of its base name + * after the last dot character, if any. + * + * In particular, + * + * - if the name does not include a dot, there is no extension, so this + * predicate has no result; + * - if the name ends in a dot, the extension is the empty string; + * - if the name contains multiple dots, the extension follows the last dot. + * + * Here are some examples of absolute paths and the corresponding extensions + * (surrounded with quotes to avoid ambiguity): + * + * + * + * + * + * + * + * + *
    Absolute pathExtension
    "/tmp/tst.js""js"
    "/tmp/.classpath""classpath"
    "/bin/bash"not defined
    "/tmp/tst2."""
    "/tmp/x.tar.gz""gz"
    + */ + string getExtension() { + result = getAbsolutePath().regexpCapture(".*/([^/]*?)(\\.([^.]*))?", 3) + } + + /** + * Gets the stem of this container, that is, the prefix of its base name up to + * (but not including) the last dot character if there is one, or the entire + * base name if there is not. + * + * Here are some examples of absolute paths and the corresponding stems + * (surrounded with quotes to avoid ambiguity): + * + * + * + * + * + * + * + * + *
    Absolute pathStem
    "/tmp/tst.js""tst"
    "/tmp/.classpath"""
    "/bin/bash""bash"
    "/tmp/tst2.""tst2"
    "/tmp/x.tar.gz""x.tar"
    + */ + string getStem() { + result = getAbsolutePath().regexpCapture(".*/([^/]*?)(?:\\.([^.]*))?", 1) + } + + /** Gets the parent container of this file or folder, if any. */ + Container getParentContainer() { + containerparent(result, this) + } + + /** Gets a file or sub-folder in this container. */ + Container getAChildContainer() { + this = result.getParentContainer() + } + + /** Gets a file in this container. */ + File getAFile() { + result = getAChildContainer() + } + + /** Gets the file in this container that has the given `baseName`, if any. */ + File getFile(string baseName) { + result = getAFile() and + result.getBaseName() = baseName + } + + /** Gets a sub-folder in this container. */ + Folder getAFolder() { + result = getAChildContainer() + } + + /** Gets the sub-folder in this container that has the given `baseName`, if any. */ + Folder getFolder(string baseName) { + result = getAFolder() and + result.getBaseName() = baseName + } + + /** + * Gets a textual representation of the path of this container. + * + * This is the absolute path of the container. + */ + string toString() { + result = getAbsolutePath() + } +} + +/** + * A folder that was observed on disk during the build process. + * + * For the example folder name of "/usr/home/me", the path decomposes to: + * + * 1. "/usr/home" - see `getParentContainer`. + * 2. "me" - see `getBaseName`. + * + * To get the full path, use `getAbsolutePath`. + */ +class Folder extends Container, @folder { + override string getAbsolutePath() { + folders(this, result, _) + } + + override Location getLocation() { + result.getContainer() = this and + result.hasLocationInfo(_, 0, 0, 0, 0) + } + + /** + * DEPRECATED: Use `getLocation` instead. + * Gets the URL of this folder. + */ + deprecated override string getURL() { + result = "folder://" + getAbsolutePath() + } + + /** + * DEPRECATED: use `getAbsolutePath` instead. + * Gets the name of this folder. + */ + deprecated + string getName() { folders(this,result,_) } + + /** + * DEPRECATED: use `getAbsolutePath` instead. + * Holds if this element is named `name`. + */ + deprecated + predicate hasName(string name) { name = this.getName() } + + /** + * DEPRECATED: use `getAbsolutePath` instead. + * Gets the full name of this folder. + */ + deprecated + string getFullName() { result = this.getName() } + + /** + * DEPRECATED: use `getBaseName` instead. + * Gets the last part of the folder name. + */ + deprecated + string getShortName() { + exists (string longnameRaw, string longname + | folders(this,_,longnameRaw) and + longname = longnameRaw.replaceAll("\\", "/") + | exists (int index + | result = longname.splitAt("/", index) and + not exists (longname.splitAt("/", index+1)))) + } + + /** + * DEPRECATED: use `getParentContainer` instead. + * Gets the parent folder. + */ + deprecated + Folder getParent() { containerparent(result,this) } +} + +/** + * A file that was observed on disk during the build process. + * + * For the example filename of "/usr/home/me/myprogram.c", the filename + * decomposes to: + * + * 1. "/usr/home/me" - see `getParentContainer`. + * 2. "myprogram.c" - see `getBaseName`. + * + * The base name further decomposes into the _stem_ and _extension_ -- see + * `getStem` and `getExtension`. To get the full path, use `getAbsolutePath`. + */ +class File extends Container, @file { + override string getAbsolutePath() { + files(this, result, _, _, _) + } + + override string toString() { + result = Container.super.toString() + } + + override Location getLocation() { + result.getContainer() = this and + result.hasLocationInfo(_, 0, 0, 0, 0) + } + + /** + * DEPRECATED: Use `getLocation` instead. + * Gets the URL of this file. + */ + deprecated override string getURL() { + result = "file://" + this.getAbsolutePath() + ":0:0:0:0" + } + + /** Holds if this file was compiled as C (at any point). */ + predicate compiledAsC() { + fileannotations(this,1,"compiled as c","1") + } + + /** Holds if this file was compiled as C++ (at any point). */ + predicate compiledAsCpp() { + fileannotations(this,1,"compiled as c++","1") + } + + /** + * DEPRECATED: Objective-C is no longer supported. + * Holds if this file was compiled as Objective C (at any point). + */ + deprecated predicate compiledAsObjC() { + none() + } + + /** + * DEPRECATED: Objective-C is no longer supported. + * Holds if this file was compiled as Objective C++ (at any point). + */ + deprecated predicate compiledAsObjCpp() { + none() + } + + /** Holds if this file was compiled by a Microsoft compiler (at any point). */ + predicate compiledAsMicrosoft() { + exists(Compilation c | + c.getAFileCompiled() = this and + c.getAnArgument() = "--microsoft" + ) + } + + /** Gets a top-level element declared in this file. */ + Declaration getATopLevelDeclaration() { + result.getAFile() = this and result.isTopLevel() + } + + /** Gets a declaration in this file. */ + Declaration getADeclaration() { result.getAFile() = this } + + /** Holds if this file uses the given macro. */ + predicate usesMacro(Macro m) { + exists(MacroInvocation mi | mi.getFile() = this and + mi.getMacro() = m) + } + + /** + * Gets a file that is directly included from this file (using a + * pre-processor directive like `#include`). + */ + File getAnIncludedFile() { + exists(Include i | i.getFile() = this and i.getIncludedFile() = result) + } + + /** + * DEPRECATED: use `getParentContainer` instead. + * Gets the folder which contains this file. + */ + deprecated + Folder getParent() { containerparent(result,this) } + + /** + * Holds if this file may be from source. This predicate holds for all files + * except the dummy file, whose name is the empty string, which contains + * declarations that are built into the compiler. + */ + override predicate fromSource() { numlines(this,_,_,_) } + + /** + * Holds if this file may be from a library. + * + * DEPRECATED: For historical reasons this is true for any file. + */ + deprecated override + predicate fromLibrary() { any() } + + /** Gets the metric file. */ + MetricFile getMetrics() { result = this } + + /** + * DEPRECATED: Use `getAbsolutePath` instead. + * Gets the full name of this file, for example: + * "/usr/home/me/myprogram.c". + */ + deprecated + string getName() { files(this,result,_,_,_) } + + /** + * DEPRECATED: Use `getAbsolutePath` instead. + * Holds if this file has the specified full name. + * + * Example usage: `f.hasName("/usr/home/me/myprogram.c")`. + */ + deprecated + predicate hasName(string name) { name = this.getName() } + + /** + * DEPRECATED: Use `getAbsolutePath` instead. + * Gets the full name of this file, for example + * "/usr/home/me/myprogram.c". + */ + deprecated + string getFullName() { result = this.getName() } + + /** + * Gets the remainder of the base name after the first dot character. Note + * that the name of this predicate is in plural form, unlike `getExtension`, + * which gets the remainder of the base name after the _last_ dot character. + * + * Predicates `getStem` and `getExtension` should be preferred over + * `getShortName` and `getExtensions` since the former pair is compatible + * with the file libraries of other languages. + * Note the slight difference between this predicate and `getStem`: + * for example, for "file.tar.gz", this predicate will have the result + * "tar.gz", while `getExtension` will have the result "gz". + */ + string getExtensions() { + files(this,_,_,result,_) + } + + /** + * DEPRECATED: Use `getBaseName` instead. + * Gets the name and extension(s), but not path, of a file. For example, + * if the full name is "/path/to/filename.a.bcd" then the filename is + * "filename.a.bcd". + */ + deprecated + string getFileName() { + // [a/b.c/d/]fileName + // ^ beginAfter + exists(string fullName, int beginAfter | + fullName = this.getName() and + beginAfter = max(int i | i = -1 or fullName.charAt(i) = "/" | i) and + result = fullName.suffix(beginAfter + 1) + ) + } + + /** + * Gets the short name of this file, that is, the prefix of its base name up + * to (but not including) the first dot character if there is one, or the + * entire base name if there is not. For example, if the full name is + * "/path/to/filename.a.bcd" then the short name is "filename". + * + * Predicates `getStem` and `getExtension` should be preferred over + * `getShortName` and `getExtensions` since the former pair is compatible + * with the file libraries of other languages. + * Note the slight difference between this predicate and `getStem`: + * for example, for "file.tar.gz", this predicate will have the result + * "file", while `getStem` will have the result "file.tar". + */ + string getShortName() { files(this,_,result,_,_) } +} + + +/** + * A C/C++ header file, as determined (mainly) by file extension. + * + * For the related notion of whether a file is included anywhere (using a + * pre-processor directive like `#include`), use `Include.getIncludedFile`. + */ +class HeaderFile extends File { + + HeaderFile() { + exists(string ext | ext = this.getExtension().toLowerCase() | + ext = "h" or ext = "r" + /* --- */ or ext = "hpp" or ext = "hxx" or ext = "h++" or ext = "hh" or ext = "hp" + or ext = "tcc" or ext = "tpp" or ext = "txx" or ext = "t++" /* --- --- */ + ) + or + not exists(this.getExtension()) and + exists(Include i | i.getIncludedFile() = this) + } + +} + +/** + * A C source file, as determined by file extension. + * + * For the related notion of whether a file is compiled as C code, use + * `File.compiledAsC`. + */ +class CFile extends File { + + CFile() { + exists(string ext | ext = this.getExtension().toLowerCase() | + ext = "c" or ext = "i" + ) + } + +} + +/** + * A C++ source file, as determined by file extension. + * + * For the related notion of whether a file is compiled as C++ code, use + * `File.compiledAsCpp`. + */ +class CppFile extends File { + + CppFile() { + exists(string ext | ext = this.getExtension().toLowerCase() | + /* --- */ ext = "cpp" or ext = "cxx" or ext = "c++" or ext = "cc" or ext = "cp" + or ext = "icc" or ext = "ipp" or ext = "ixx" or ext = "i++" or ext = "ii" /* --- */ + // Note: .C files are indistinguishable from .c files on some + // file systems, so we just treat them as CFile's. + ) + } + +} + +/** + * DEPRECATED: Objective-C is no longer supported. + * An Objective C source file, as determined by file extension. + * + * For the related notion of whether a file is compiled as Objective C + * code, use `File.compiledAsObjC`. + */ +deprecated class ObjCFile extends File { + ObjCFile() { none() } +} + +/** + * DEPRECATED: Objective-C is no longer supported. + * An Objective C++ source file, as determined by file extension. + * + * For the related notion of whether a file is compiled as Objective C++ + * code, use `File.compiledAsObjCpp`. + */ +deprecated class ObjCppFile extends File { + ObjCppFile() { none() } +} diff --git a/cpp/ql/src/semmle/code/cpp/FriendDecl.qll b/cpp/ql/src/semmle/code/cpp/FriendDecl.qll new file mode 100644 index 000000000000..0919a6c0f4cb --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/FriendDecl.qll @@ -0,0 +1,64 @@ +import semmle.code.cpp.Declaration +private import semmle.code.cpp.internal.Type + +/** + * A C++ friend declaration [N4140 11.3]. + * For example: + * + * class A { + * friend void f(int); + * friend class X; + * }; + * + * void f(int x) { ... } + * class X { ... }; + */ +class FriendDecl extends Declaration, @frienddecl { + /** + * Gets the location of this friend declaration. The result is the + * location of the friend declaration itself, not the class or function + * that it refers to. Note: to get the target of the friend declaration, + * use `getFriend`. + */ + override Location getADeclarationLocation() { result = this.getLocation() } + + /** + * Implements the abstract method `Declaration.getDefinitionLocation`. A + * friend declaration cannot be a definition because it is only a link to + * another class or function. But we have to provide an implementation of + * this method, so we use the location of the declaration as the location + * of the definition. Note: to get the target of the friend declaration, + * use `getFriend`. + */ + override Location getDefinitionLocation() { result = this.getLocation() } + + /** Gets the location of this friend declaration. */ + override Location getLocation() { frienddecls(this,_,_,result) } + + /** Gets a descriptive string for this friend declaration. */ + override string getName() { + result = this.getDeclaringClass().getName() + "'s friend" + } + + /** + * Friend declarations do not have specifiers. It makes no difference + * whether they are declared in a public, protected or private section of + * the class. + */ + override Specifier getASpecifier() { none() } + + /** + * Gets the target of this friend declaration. + * For example: `X` in `class A { friend class X }`. + */ + AccessHolder getFriend() { frienddecls(this,_,result,_) } + + /** + * Gets the declaring class (also known as the befriending class). + * For example: `A` in `class A { friend class X }`. + */ + Class getDeclaringClass() { frienddecls(this,unresolve(result),_,_) } + + /* Holds if this declaration is a top-level declaration. */ + override predicate isTopLevel() { none() } +} diff --git a/cpp/ql/src/semmle/code/cpp/Function.qll b/cpp/ql/src/semmle/code/cpp/Function.qll new file mode 100644 index 000000000000..52a9111c737e --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/Function.qll @@ -0,0 +1,1159 @@ +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 +import semmle.code.cpp.metrics.MetricFunction +import semmle.code.cpp.Linkage +private import semmle.code.cpp.internal.Type + +/** + * A C/C++ function [N4140 8.3.5]. Both member functions and non-member + * functions are included. + * + * Function has a one-to-many relationship with FunctionDeclarationEntry, + * because the same function can be declared in multiple locations. This + * relationship between `Declaration` and `DeclarationEntry` is explained + * in more detail in `Declaration.qll`. + */ +class Function extends Declaration, ControlFlowNode, AccessHolder, @function { + + /** + * Gets the name of this function. + * + * This name doesn't include a namespace or any argument types, so both + * `::open()` and `::std::ifstream::open(...)` have the same name. + * + * To get the name including the namespace, use `getQualifiedName` or + * `hasQualifiedName`. + * + * To test whether a function has a particular name in the global + * namespace, use `hasGlobalName`. + */ + override string getName() { functions(this,result,_) } + + /** + * Gets the full signature of this function, including return type, parameter + * types, and template arguments. + * + * For example, in the following code: + * ``` + * template T min(T x, T y); + * int z = min(5, 7); + * ``` + * The full signature of the function called on the last line would be + * "min(int, int) -> int", and the full signature of the uninstantiated + * template on the first line would be "min(T, T) -> T". + */ + string getFullSignature() { + exists(string name, string templateArgs, string args | + result = name + templateArgs + args + " -> " + getType().toString() and + name = getQualifiedName() and + ( + if exists(getATemplateArgument()) then ( + templateArgs = "<" + + concat(int i | + exists(getTemplateArgument(i)) | + getTemplateArgument(i).toString(), ", " order by i + ) + ">" + ) + else + templateArgs = "" + ) and + args = "(" + + concat(int i | + exists(getParameter(i)) | + getParameter(i).getType().toString(), ", " order by i + ) + ")" + ) + } + + /** Gets a specifier of this function. */ + override Specifier getASpecifier() { + funspecifiers(this,result) + or result.hasName(getADeclarationEntry().getASpecifier()) + } + + /** Gets an attribute of this function. */ + Attribute getAnAttribute() { funcattributes(this, result) } + + /** Holds if this function is generated by the compiler. */ + predicate isCompilerGenerated() { + compgenerated(this) + } + + /** Holds if this function is inline. */ + predicate isInline() { + this.hasSpecifier("inline") + } + + /** Holds if this function is virtual. */ + predicate isVirtual() { + this.hasSpecifier("virtual") + } + + /** + * Holds if this function is deleted. + * This may be because it was explicitly deleted with an `= delete` + * definition, or because the compiler was unable to auto-generate a + * definition for it. + * + * Most implicitly deleted functions are omitted from the database. + * `Class.implicitCopyConstructorDeleted` and + * `Class.implicitCopyAssignmentOperatorDeleted` can be used to find + * whether a class would have had those members implicitly deleted. + */ + predicate isDeleted() { + function_deleted(this) + } + + /** + * Holds if this function is explicitly defaulted with the `= default` + * specifier. + */ + predicate isDefaulted() { + function_defaulted(this) + } + + /** + * Holds if this function is declared with `__attribute__((naked))` or + * `__declspec(naked)`. + */ + predicate isNaked() { + getAnAttribute().hasName("naked") + } + + /** Gets the return type of this function. */ + Type getType() { function_return_type(this,unresolve(result)) } + + /** Gets the nth parameter of this function. */ + Parameter getParameter(int n) { params(result,this,n,_) } + + /** Gets a parameter of this function. */ + Parameter getAParameter() { params(result,this,_,_) } + + /** + * Gets the number of parameters of this function, _not_ including any + * implicit `this` parameter or any `...` varargs pseudo-parameter. + */ + int getNumberOfParameters() { + result = count(this.getAParameter()) + } + + /** + * Gets the number of parameters of this function, _including_ any implicit + * `this` parameter but _not_ including any `...` varargs pseudo-parameter. + */ + int getEffectiveNumberOfParameters() { + // This method is overridden in `MemberFunction`, where the result is + // adjusted to account for the implicit `this` parameter. + result = getNumberOfParameters() + } + + /** + * Gets a string representing the parameters of this function. + * + * For example: for a function `int Foo(int p1, int p2)` this would + * return `int p1, int p2`. + */ + string getParameterString() { + result = getParameterStringFrom(0) + } + + private string getParameterStringFrom(int index) { + ( + index = getNumberOfParameters() and + result = "" + ) or ( + index = getNumberOfParameters() - 1 and + result = getParameter(index).getTypedName() + ) or ( + index < getNumberOfParameters() - 1 and + result = getParameter(index).getTypedName() + ", " + getParameterStringFrom(index + 1) + ) + } + + FunctionCall getACallToThisFunction() { + result.getTarget() = this + } + + /** + * Gets a declaration entry corresponding to this declaration. The + * relationship between `Declaration` and `DeclarationEntry` is explained + * in `Declaration.qll`. + */ + override FunctionDeclarationEntry getADeclarationEntry() { + if fun_decls(_,this,_,_,_) then + declEntry(result) + else + exists(Function f | this.isConstructedFrom(f) and fun_decls(result,f,_,_,_)) + } + + private predicate declEntry(FunctionDeclarationEntry fde) { + fun_decls(fde,this,_,_,_) and + // If one .cpp file specializes a function, and another calls the + // specialized function, then when extracting the second we only see an + // instantiation, not the specialization. We Therefore need to ignore + // any non-specialized declarations if there are any specialized ones. + (this.isSpecialization() implies fde.isSpecialization()) + } + + /** + * Gets the location of a `FunctionDeclarationEntry` corresponding to this + * declaration. + */ + override Location getADeclarationLocation() { + result = getADeclarationEntry().getLocation() + } + + /** Holds if this Function is a Template specialization. */ + predicate isSpecialization() { + exists(FunctionDeclarationEntry fde | fun_decls(fde,this,_,_,_) + and fde.isSpecialization()) + } + + /** + * Gets the declaration entry corresponding to this declaration that is a + * definition, if any. + */ + override FunctionDeclarationEntry getDefinition() { + result = getADeclarationEntry() and + result.isDefinition() + } + + /** Gets the location of the definition, if any. */ + override Location getDefinitionLocation() { + if exists(getDefinition()) then + result = getDefinition().getLocation() + else + exists(Function f | this.isConstructedFrom(f) and result = f.getDefinition().getLocation()) + } + + /** + * Gets the preferred location of this declaration. (The location of the + * definition, if possible.) + */ + override Location getLocation() { + if exists(getDefinition()) then + result = this.getDefinitionLocation() + else + result = this.getADeclarationLocation() + } + + /** Gets a child declaration of this function. */ + Declaration getADeclaration() { result = this.getAParameter() } + + /** + * Gets the block that is the function body. + * + * For C++ functions whose body is a function try statement rather than a + * block, this gives the block guarded by the try statement. See + * `FunctionTryStmt` for further information. + */ + Block getBlock() { result.getParentScope() = this } + + /** Holds if this function has an entry point. */ + predicate hasEntryPoint() { exists(getEntryPoint()) } + + /** + * Gets the first node in this function's control flow graph. + * + * For most functions, this first node will be the `Block` returned by + * `getBlock`. However in C++, the first node can also be a + * `FunctionTryStmt`. + */ + Stmt getEntryPoint() { + function_entry_point(this, result) + } + + /** + * Gets the metric class. `MetricFunction` has methods for computing + * various metrics, such as "number of lines of code" and "number of + * function calls". + */ + MetricFunction getMetrics() { result = this } + + /** Holds if this function calls the function `f`. */ + predicate calls(Function f) { + exists(Locatable l | this.calls(f, l)) + } + + /** + * Holds if this function calls the function `f` in the `FunctionCall` + * expression `l`. + */ + predicate calls(Function f, Locatable l) { + exists(FunctionCall call | call.getEnclosingFunction() = this and call.getTarget() = f and call = l) + or exists(DestructorCall call | call.getEnclosingFunction() = this and call.getTarget() = f and call = l) + } + + /** Holds if this function accesses a function or variable or enumerator `a`. */ + predicate accesses(Declaration a) { + exists(Locatable l | this.accesses(a, l)) + } + + /** + * Holds if this function accesses a function or variable or enumerator `a` + * in the `Access` expression `l`. + */ + predicate accesses(Declaration a, Locatable l) { + exists(Access access | access.getEnclosingFunction() = this and + a = access.getTarget() and access = l) + } + + /** Gets a variable that is written-to in this function. */ + Variable getAWrittenVariable() { + exists(ConstructorFieldInit cfi | cfi.getEnclosingFunction() = this and result = cfi.getTarget()) or + exists(VariableAccess va | va = result.getAnAccess() and + va.isUsedAsLValue() and + va.getEnclosingFunction() = this) + } + + /** + * Implements `ControlFlowNode.getControlFlowScope`. The `Function` is + * used to represent the exit node of the control flow graph, so it is + * its own scope. + */ + override Function getControlFlowScope() { + result = this + } + + /** + * Implements `ControlFlowNode.getEnclosingStmt`. The `Function` is + * used to represent the exit node of the control flow graph, so it + * has no enclosing statement. + */ + override Stmt getEnclosingStmt() { + none() + } + + /** + * Holds if this function has C linkage, as specified by one of its + * declaration entries. For example: `extern "C" void foo();`. + */ + predicate hasCLinkage() { + getADeclarationEntry().hasCLinkage() + } + + /** + * Holds if this function is constructed from `f` as a result + * of template instantiation. If so, it originates either from a template + * function or from a function nested in a template class. + */ + predicate isConstructedFrom(Function f) { + function_instantiation(this, f) + } + + /** + * Gets an argument used to instantiate this class from a template + * class. + */ + Type getATemplateArgument() { + exists(int i | this.getTemplateArgument(i) = result ) + } + + /** + * Gets a particular argument used to instantiate this class from a + * template class. + */ + Type getTemplateArgument(int index) { + function_template_argument(this,index,unresolve(result)) + } + + /** + * Holds if this function is defined in several files. This is illegal in + * C (though possible in some C++ compilers), and likely indicates that + * several functions that are not linked together have been compiled. An + * example would be a project with many 'main' functions. + */ + predicate isMultiplyDefined() { + strictcount(getFile()) > 1 + } + + /** Holds if this function is a varargs function. */ + predicate isVarargs() { + hasSpecifier("varargs") + } + + /** Gets a type that is specified to be thrown by the function. */ + Type getAThrownType() { + result = getADeclarationEntry().getAThrownType() + } + + /** + * Gets the `i`th type specified to be thrown by the function. + */ + Type getThrownType(int i) { + result = getADeclarationEntry().getThrownType(i) + } + + /** Holds if the function has an exception specification. */ + predicate hasExceptionSpecification() { + getADeclarationEntry().hasExceptionSpecification() + } + + /** Holds if this function has a `throw()` exception specification. */ + predicate isNoThrow() { + getADeclarationEntry().isNoThrow() + } + + /** Holds if this function has a `noexcept` exception specification. */ + predicate isNoExcept() { + getADeclarationEntry().isNoExcept() + } + + /** Gets a function that overloads this one. */ + Function getAnOverload() { + result.getName() = getName() + and result.getNamespace() = getNamespace() + and result != this + + // If this function is declared in a class, only consider other + // functions from the same class. Conversely, if this function is not + // declared in a class, only consider other functions not declared in a + // class. + and + ( + if exists(getDeclaringType()) then + result.getDeclaringType() = getDeclaringType() + else + not exists(result.getDeclaringType()) + ) + + // Instantiations and specializations don't participate in overload + // resolution. + and not (this instanceof FunctionTemplateInstantiation or + result instanceof FunctionTemplateInstantiation) + and not (this instanceof FunctionTemplateSpecialization or + result instanceof FunctionTemplateSpecialization) + } + + /** Gets a link target which compiled or referenced this function. */ + LinkTarget getALinkTarget() { + this = result.getAFunction() + } + + /** + * Holds if this function is side-effect free (conservative + * approximation). + */ + predicate isSideEffectFree() { + not this.mayHaveSideEffects() + } + + /** + * Holds if this function may have side-effects; if in doubt, we assume it + * may. + */ + predicate mayHaveSideEffects() { + // If we cannot see the definition then we assume that it may have + // side-effects. + if exists(this.getEntryPoint()) then ( + // If it might be globally impure (we don't care about it modifying + // temporaries) then it may have side-effects. + this.getEntryPoint().mayBeGloballyImpure() or + // Constructor initializers are separate from the entry point ... + this.(Constructor).getAnInitializer().mayBeGloballyImpure() or + // ... and likewise for destructors. + this.(Destructor).getADestruction().mayBeGloballyImpure() + ) else not exists(string name | this.hasGlobalName(name) | + // Unless it's a function that we know is side-effect-free, it may + // have side-effects. + name = "strcmp" or name = "wcscmp" or name = "_mbscmp" or + name = "strlen" or name = "wcslen" or + name = "_mbslen" or name = "_mbslen_l" or + name = "_mbstrlen" or name = "_mbstrlen_l" or + name = "strnlen" or name = "strnlen_s" or + name = "wcsnlen" or name = "wcsnlen_s" or + name = "_mbsnlen" or name = "_mbsnlen_l" or + name = "_mbstrnlen" or name = "_mbstrnlen_l" or + name = "strncmp" or name = "wcsncmp" or + name = "_mbsncmp" or name = "_mbsncmp_l" or + name = "strchr" or name = "memchr" or name = "wmemchr" or + name = "memcmp" or name = "wmemcmp" or + name = "_memicmp" or name = "_memicmp_l" or + name = "feof" or + name = "isdigit" or name = "isxdigit" or + name = "abs" or name = "fabs" or name = "labs" or + name = "floor" or name = "ceil" or + name = "atoi" or name = "atol" or name = "atoll" or name = "atof" + ) + } + + /** + * Gets the nearest enclosing AccessHolder. + */ + override AccessHolder getEnclosingAccessHolder() { + result = this.getDeclaringType() + } +} + +/** + * A particular declaration or definition of a C/C++ function. + */ +class FunctionDeclarationEntry extends DeclarationEntry, @fun_decl { + /** Gets the function which is being declared or defined. */ + override Function getDeclaration() { result = getFunction() } + + /** Gets the function which is being declared or defined. */ + Function getFunction() { fun_decls(this,result,_,_,_) } + + /** Gets the name of the function. */ + override string getName() { fun_decls(this,_,_,result,_) } + + /** + * Gets the return type of the function which is being declared or + * defined. + */ + override Type getType() { fun_decls(this,_,unresolve(result),_,_) } + + /** Gets the location of this declaration entry. */ + override Location getLocation() { fun_decls(this,_,_,_,result) } + + /** Gets a specifier associated with this declaration entry. */ + override string getASpecifier() { fun_decl_specifiers(this,result) } + + /** + * Implements `Element.getEnclosingElement`. A function declaration does + * not have an enclosing element. + */ + override Element getEnclosingElement() { none() } + + /** + * Gets the typedef type (if any) used for this function declaration. As + * an example, the typedef type in the declaration of function foo in the + * following is Foo: + * + * typedef int Foo(); + * static Foo foo; + */ + TypedefType getTypedefType() { + fun_decl_typedef_type(this,result) + } + + /** + * Gets the cyclomatic complexity of this function: + * + * The number of branching statements (if, while, do, for, switch, + * case, catch) plus the number of branching expressions (`?`, `&&`, + * `||`) plus one. + */ + int getCyclomaticComplexity() { + result = 1 + cyclomaticComplexityBranches(getBlock()) + } + + /** + * If this is a function definition, get the block containing the + * function body. + */ + Block getBlock() { + this.isDefinition() and + result = getFunction().getBlock() and result.getFile() = this.getFile() + } + + /** + * If this is a function definition, get the number of lines of code + * associated with it. + */ + pragma[noopt] int getNumberOfLines() { + exists(Block b, Location l, int start, int end, int diff| b = getBlock() | + l = b.getLocation() and + start = l.getStartLine() and + end = l.getEndLine() and + diff = end - start and + result = diff + 1 + ) + } + + /** + * Gets the declaration entry for a parameter of this function + * declaration. + */ + ParameterDeclarationEntry getAParameterDeclarationEntry() { + result = getParameterDeclarationEntry(_) + } + + /** + * Gets the declaration entry for the nth parameter of this function + * declaration. + */ + ParameterDeclarationEntry getParameterDeclarationEntry(int n) { + param_decl_bind(result,n,this) + } + + /** Gets the number of parameters of this function declaration. */ + int getNumberOfParameters() { + result = count(this.getAParameterDeclarationEntry()) + } + + /** + * Gets a string representing the parameters of this function declaration. + * + * For example: for a function 'int Foo(int p1, int p2)' this would + * return 'int p1, int p2'. + */ + string getParameterString() { + result = getParameterStringFrom(0) + } + + private string getParameterStringFrom(int index) { + ( + index = getNumberOfParameters() and + result = "" + ) or ( + index = getNumberOfParameters() - 1 and + result = getParameterDeclarationEntry(index).getTypedName() + ) or ( + index < getNumberOfParameters() - 1 and + result = getParameterDeclarationEntry(index).getTypedName() + ", " + getParameterStringFrom(index + 1) + ) + } + + /** Holds if this declaration entry specifies C linkage: + * + * `extern "C" void foo();` + */ + predicate hasCLinkage() { + getASpecifier() = "c_linkage" + } + + /** Holds if this declaration entry has a void parameter list. */ + predicate hasVoidParamList() { + getASpecifier() = "void_param_list" + } + + /** Holds if this declaration is also a definition of its function. */ + override predicate isDefinition() { + fun_def(this) + } + + /** Holds if this declaration is a Template specialization. */ + predicate isSpecialization() { + fun_specialized(this) + } + + /** + * Holds if this declaration is an implicit function declaration, that is, + * where a function is used before it is declared (under older C standards). + */ + predicate isImplicit() { + fun_implicit(this) + } + + /** Gets a type that is specified to be thrown by the declared function. */ + Type getAThrownType() { + result = getThrownType(_) + } + + /** + * Gets the `i`th type specified to be thrown by the declared function + * (where `i` is indexed from 0). For example, if a function is declared + * to `throw(int,float)`, then the thrown type with index 0 would be + * `int`, and that with index 1 would be `float`. + */ + Type getThrownType(int i) { + fun_decl_throws(this,i,unresolve(result)) + } + + /** + * If this declaration has a noexcept-specification [N4140 15.4], then + * this predicate returns the argument to `noexcept` if one was given. + */ + Expr getNoExceptExpr() { + fun_decl_noexcept(this,result) + } + + /** + * Holds if the declared function has an exception specification [N4140 + * 15.4]. + */ + predicate hasExceptionSpecification() { + fun_decl_throws(this,_,_) or + fun_decl_noexcept(this,_) or + isNoThrow() or + isNoExcept() + } + + /** + * Holds if the declared function has a `throw()` exception specification. + */ + predicate isNoThrow() { + fun_decl_empty_throws(this) + } + + /** + * Holds if the declared function has an empty `noexcept` exception + * specification. + */ + predicate isNoExcept() { + fun_decl_empty_noexcept(this) + } +} + + +/** + * A C/C++ non-member function (a function that is not a member of any + * class). + */ +class TopLevelFunction extends Function { + TopLevelFunction() { + not this.isMember() + } +} + +/** + * A C++ function declared as a member of a class [N4140 9.3]. This includes + * static member functions. + */ +class MemberFunction extends Function { + MemberFunction() { + this.isMember() + } + + /** + * 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(this,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. + */ +class VirtualFunction extends MemberFunction { + + VirtualFunction() { + this.hasSpecifier("virtual") or purefunctions(this) + } + + /** 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]. + */ +class PureVirtualFunction extends VirtualFunction { + + PureVirtualFunction() { purefunctions(this) } + +} + +/** + * A const C++ member function [N4140 9.3.1/4]. A const function does not + * modify the state of its class. + * For example: `int day() const { return d; }` + */ +class ConstMemberFunction extends MemberFunction { + + ConstMemberFunction() { this.hasSpecifier("const") } + +} + +/** + * A C++ constructor [N4140 12.1]. + */ +class Constructor extends MemberFunction { + + Constructor() { functions(this,_,2) } + + /** + * 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(result, i, this) + } +} + +/** A function that defines an implicit conversion. */ +abstract class ImplicitConversionFunction extends MemberFunction { + abstract Type getSourceType(); + abstract Type getDestType(); +} + +/** A C++ constructor that also defines an implicit conversion. */ +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) + } + + /** 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).getType() + .getUnderlyingType() // resolve typedefs + .(LValueReferenceType).getBaseType() // step through lvalue reference type + .getUnspecifiedType() = // resolve typedefs, strip const/volatile + f.getDeclaringType() +} + +private predicate hasMoveSignature(MemberFunction f) { + f.getParameter(0).getType() + .getUnderlyingType() // resolve typedefs + .(RValueReferenceType).getBaseType() // step through rvalue reference type + .getUnspecifiedType() = // resolve typedefs, strip const/volatile + f.getDeclaringType() +} + +/** + * A C++ copy constructor, such as `T::T(const T&)` [N4140 12.8]. + * + * 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()) + } + + /** + * 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, such as `T::T(T&&)` [N4140 12.8]. + * + * 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()) + } + + /** + * 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. + */ +class NoArgConstructor extends Constructor { + NoArgConstructor() { + this.getNumberOfParameters() = 0 + } +} + +/** + * A C++ destructor [N4140 12.4]. + */ +class Destructor extends MemberFunction { + Destructor() { functions(this,_,3) } + + /** + * 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(result, i, this) + } +} + +/** + * A C++ conversion operator [N4140 12.3.2]. + */ +class ConversionOperator extends MemberFunction, ImplicitConversionFunction { + + ConversionOperator() { functions(this,_,4) } + + override Type getSourceType() { result = this.getDeclaringType() } + override Type getDestType() { result = this.getType() } + +} + +/** + * A C++ user-defined operator [N4140 13.5]. + */ +class Operator extends Function { + + Operator() { functions(this,_,5) } + +} + +/** + * A C++ copy assignment operator, such as `T& T::operator=(const T&)` + * [N4140 12.8]. + * + * 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).getType().getUnspecifiedType() = getDeclaringType() + ) and + not exists(this.getParameter(1)) and + not exists(getATemplateArgument()) + } +} + + +/** + * A C++ move assignment operator, such as `T& T::operator=(T&&)` [N4140 + * 12.8]. + * + * 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()) + } +} + + +/** + * A C++ function which has a non-empty template argument list. + * + * This includes function declarations which are immediately preceded by + * `template <...>`, where the "..." part is not empty, and therefore it + * does not include: + * + * 1. Full specializations of template functions, as they have an empty + * template argument list. + * 2. Instantiations of template functions, as they don't have an + * explicit template argument list. + * 3. Member functions of template classes - unless they have their own + * (non-empty) template argument list. + */ +class TemplateFunction extends Function { + TemplateFunction() { is_function_template(this) and exists(getATemplateArgument()) } + + /** + * Gets a compiler-generated instantiation of this function template. + */ + Function getAnInstantiation() { + result.isConstructedFrom(this) + and not result.isSpecialization() + } + + /** + * Gets a full specialization of this function template. + * + * Note that unlike classes, functions overload rather than specialize + * partially. Therefore this does not include things which "look like" + * partial specializations, nor does it include full specializations of + * such things -- see FunctionTemplateSpecialization for further details. + */ + FunctionTemplateSpecialization getASpecialization() { + result.getPrimaryTemplate() = this + } +} + +/** + * A function that is an instantiation of a template. + */ +class FunctionTemplateInstantiation extends Function { + FunctionTemplateInstantiation() { + exists(TemplateFunction tf | tf.getAnInstantiation() = this) + } +} + +/** + * An explicit specialization of a C++ function template. + * For example: `template <> void f(int *)`. + * + * Note that unlike classes, functions overload rather than specialize + * partially. Therefore this only includes the last two of the following + * four definitions, and in particular does not include the second one: + * + * ``` + * template void f(T) {...} + * template void f(T*) {...} + * template <> void f(int *) {...} + * template <> void f(int *) {...} + * ``` + * + * Furthermore, this does not include compiler-generated instantiations of + * function templates. + * + * For further reference on function template specializations, see: + * http://www.gotw.ca/publications/mill17.htm + */ +class FunctionTemplateSpecialization extends Function { + FunctionTemplateSpecialization() { + this.isSpecialization() + } + + /** + * Gets the primary template for the specialization (the function template + * this specializes). + */ + TemplateFunction getPrimaryTemplate() { + this.isConstructedFrom(result) + } +} + +/** + * A GCC built-in function. For example: `__builtin___memcpy_chk`. + */ +class BuiltInFunction extends Function { + BuiltInFunction() { + functions(this,_,6) + } + + /** Gets a dummy location for the built-in function. */ + override Location getLocation() { + suppressUnusedThis(this) and + result instanceof UnknownDefaultLocation + } +} + +private predicate suppressUnusedThis(Function f) { any() } diff --git a/cpp/ql/src/semmle/code/cpp/Include.qll b/cpp/ql/src/semmle/code/cpp/Include.qll new file mode 100644 index 000000000000..b29278a35ac1 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/Include.qll @@ -0,0 +1,56 @@ +import semmle.code.cpp.Preprocessor + +/** + * A C/C++ `#include`, `#include_next`, or `#import` preprocessor + * directive. + */ +class Include extends PreprocessorDirective, @ppd_include { + override string toString() { result = "#include " + this.getIncludeText() } + + /** + * Gets the token which occurs after `#include`, for example `"filename"` + * or `<filename>`. + */ + string getIncludeText() { result = getHead() } + + /** Gets the file directly included by this `#include`. */ + File getIncludedFile() { includes(this, result) } + + /** + * Gets a file which might be transitively included by this `#include`. + * + * Note that as this isn't computed within the context of a particular + * translation unit, it is often a slight over-approximation. + */ + predicate provides(File l) { + exists(Include i | this.getAnInclude*() = i and i.getIncludedFile() = l) + } + + /** + * A `#include` which appears in the file directly included by this + * `#include`. + */ + Include getAnInclude() { + this.getIncludedFile() = result.getFile() + } +} + +/** + * A `#include_next` preprocessor directive (a non-standard extension to + * C/C++). + */ +class IncludeNext extends Include, @ppd_include_next { + override string toString() { + result = "#include_next " + getIncludeText() + } +} + +/** + * A `#import` preprocessor directive (used heavily in Objective C, and + * supported by GCC as an extension in C). + */ +class Import extends Include, @ppd_objc_import { + override string toString() { + result = "#import " + getIncludeText() + } +} diff --git a/cpp/ql/src/semmle/code/cpp/Initializer.qll b/cpp/ql/src/semmle/code/cpp/Initializer.qll new file mode 100644 index 000000000000..d31d54b8987a --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/Initializer.qll @@ -0,0 +1,36 @@ +import semmle.code.cpp.controlflow.ControlFlowGraph + +/** + * A C/C++ declaration initializer. + */ +class Initializer extends @initialiser, ControlFlowNode { + override Location getLocation() { initialisers(this,_,_,result) } + + /** Holds if this initializer is explicit in the source. */ + override predicate fromSource() { + not (this.getLocation() instanceof UnknownLocation) + } + + override string toString() { + if exists(getDeclaration()) then ( + result = "initializer for " + max(getDeclaration().getName()) + ) else ( + result = "initializer" + ) + } + + /** Gets the variable or enum constant being initialized. */ + Declaration getDeclaration() { initialisers(this,result,_,_) } + + /** Gets the initializing expression. */ + Expr getExpr() { initialisers(this,_,result,_) } + + /** Gets the function containing this control-flow node. */ + override Function getControlFlowScope() { + result = this.getExpr().getEnclosingFunction() + } + + override Stmt getEnclosingStmt() { + result = this.getExpr().getEnclosingStmt() + } +} diff --git a/cpp/ql/src/semmle/code/cpp/Iteration.qll b/cpp/ql/src/semmle/code/cpp/Iteration.qll new file mode 100644 index 000000000000..5a63b5ff1bd0 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/Iteration.qll @@ -0,0 +1,56 @@ +import semmle.code.cpp.Variable + +/** + * A C/C++ variable which is used within the condition of a 'for' loop, and + * mutated within the update expression of the same 'for' loop. + */ +class LoopCounter extends Variable { + LoopCounter() { + exists(ForStmt f | f.getAnIterationVariable() = this) + } + + // 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. + ForStmt getALoop() { + result.getAnIterationVariable() = this + } +} + +/** + * A C/C++ variable which is used within the initialization, condition, or + * update expression of a 'for' loop. + */ +class LoopControlVariable extends Variable { + LoopControlVariable() { + this = loopControlVariable(_) + } + + // 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. + ForStmt getALoop() { + this = loopControlVariable(result) + } +} + +/** + * Gets a control variable of loop `f`. + */ +private Variable loopControlVariable(ForStmt f) { + exists(Expr e + | result.getAnAccess().getParent*() = e + | e = f.getControllingExpr() or + e = f.getInitialization().(ExprStmt).getExpr() or + e = f.getUpdate()) +} diff --git a/cpp/ql/src/semmle/code/cpp/Linkage.qll b/cpp/ql/src/semmle/code/cpp/Linkage.qll new file mode 100644 index 000000000000..c1d7ea7b54fa --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/Linkage.qll @@ -0,0 +1,58 @@ +import semmle.code.cpp.Class +import semmle.code.cpp.File +import semmle.code.cpp.Function + +/** + * A linker call during the build process, typically resulting in an + * executable or a shared library. + * + * Note that if linkage information isn't captured as part of the snapshot, + * then everything is grouped together into a single dummy link target. + */ +class LinkTarget extends @link_target { + /** + * Gets the file which was built. + */ + File getBinary() { + link_targets(this, result) + } + + /** + * Holds if this is the dummy link target: if linkage information isn't + * captured as part of the snapshot, then everything is grouped together + * into a single dummy link target. + */ + predicate isDummy() { + getBinary().getAbsolutePath() = "" + } + + /** Gets a textual representation of this element. */ + string toString() { + result = getBinary().getAbsolutePath() + } + + /** + * Gets a function which was compiled into this link target, or had its + * declaration included by one of the translation units which contributed + * to this link target. + */ + Function getAFunction() { + link_parent(result, this) + } + + /** + * Gets a class which had its declaration included by one of the + * translation units which contributed to this link target. + */ + Class getAClass() { + link_parent(result, this) + } +} + +/** + * Holds if this database was created with the linker awareness feature + * switched on. + */ +cached predicate isLinkerAwareExtracted() { + exists(LinkTarget lt | not lt.isDummy()) +} diff --git a/cpp/ql/src/semmle/code/cpp/Location.qll b/cpp/ql/src/semmle/code/cpp/Location.qll new file mode 100644 index 000000000000..284299bc8493 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/Location.qll @@ -0,0 +1,197 @@ +import semmle.code.cpp.Element +import semmle.code.cpp.File + +/** + * A location of a C/C++ artifact. + */ +class Location extends @location { + + /** Gets the container corresponding to this location. */ + Container getContainer() { + locations_default(this,result,_,_,_,_) or + locations_stmt(this,result,_,_,_,_) or + locations_expr(this,result,_,_,_,_) + } + + /** Gets the file corresponding to this location, if any. */ + File getFile() { + result = this.getContainer() + } + + /** Gets the start line of this location. */ + int getStartLine() { + locations_default(this,_,result,_,_,_) or + locations_stmt(this,_,result,_,_,_) or + locations_expr(this,_,result,_,_,_) + } + + /** Gets the end line of this location. */ + int getEndLine() { + locations_default(this,_,_,_,result,_) or + locations_stmt(this,_,_,_,result,_) or + locations_expr(this,_,_,_,result,_) + } + + /** Gets the start column of this location. */ + int getStartColumn() { + locations_default(this,_,_,result,_,_) or + locations_stmt(this,_,_,result,_,_) or + locations_expr(this,_,_,result,_,_) + } + + /** Gets the end column of this location. */ + int getEndColumn() { + locations_default(this,_,_,_,_,result) or + locations_stmt(this,_,_,_,_,result) or + locations_expr(this,_,_,_,_,result) + } + + /** + * Gets a textual representation of this element. + * + * The format is "file://filePath:startLine:startColumn:endLine:endColumn". + */ + string toString() { + exists(string filepath, int startline, int startcolumn, int endline, int endcolumn + | this.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + | toUrl(filepath, startline, startcolumn, endline, endcolumn, result) + ) + } + + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [LGTM locations](https://lgtm.com/help/ql/locations). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn) { + none() + } + + /** Holds if `this` comes on a line strictly before `l`. */ + predicate isBefore(Location l) { + this.getFile() = l.getFile() and this.getEndLine() < l.getStartLine() + } + + /** Holds if location `l` is completely contained within this one. */ + predicate subsumes(Location l) { + exists(File f | f = getFile() | + exists(int thisStart, int thisEnd | charLoc(f, thisStart, thisEnd) | + exists(int lStart, int lEnd | l.charLoc(f, lStart, lEnd) | + thisStart <= lStart and lEnd <= thisEnd + ) + ) + ) + } + + /** + * Holds if this location corresponds to file `f` and character "offsets" + * `start..end`. Note that these are not real character offsets, because + * we use `maxCols` to find the length of the longest line and then pretend + * that all the lines are the same length. However, these offsets are + * convenient for comparing or sorting locations in a file. For an example, + * see `subsumes`. + */ + predicate charLoc(File f, int start, int end) { + f = getFile() and + exists(int maxCols | maxCols = maxCols(f) | + start = getStartLine() * maxCols + getStartColumn() and + end = getEndLine() * maxCols + getEndColumn() + ) + } +} + +/** + * 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 { + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn) { + exists(Container f + | locations_default(this,f,startline,startcolumn,endline,endcolumn) + | filepath = f.getAbsolutePath()) + } +} + +/** A location of a statement. */ +library class LocationStmt extends Location, @location_stmt { + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn) { + exists(Container f + | locations_stmt(this,f,startline,startcolumn,endline,endcolumn) + | filepath = f.getAbsolutePath()) + } +} + +/** A location of an expression. */ +library class LocationExpr extends Location, @location_expr { + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn) { + exists(Container f + | locations_expr(this,f,startline,startcolumn,endline,endcolumn) + | filepath = f.getAbsolutePath()) + } +} + +/** + * Gets the length of the longest line in file `f`. + */ +pragma[nomagic] +private int maxCols(File f) { + result = max(Location l | l.getFile() = f | l.getEndColumn()) +} + +/** + * A C/C++ element that has a location in a file + */ +class Locatable extends Element { + +} + +/** + * A dummy location which is used when something doesn't have a location in + * the source code but needs to have a `Location` associated with it. There + * may be several distinct kinds of unknown locations. For example: one for + * expressions, one for statements and one for other program elements. + */ +class UnknownLocation extends Location { + UnknownLocation() { + getFile().getAbsolutePath() = "" + } +} + +/** + * A dummy location which is used when something doesn't have a location in + * the source code but needs to have a `Location` associated with it. + */ +class UnknownDefaultLocation extends UnknownLocation { + UnknownDefaultLocation() { + locations_default(this, _, 0, 0, 0, 0) + } +} + +/** + * A dummy location which is used when an expression doesn't have a + * location in the source code but needs to have a `Location` associated + * with it. + */ +class UnknownExprLocation extends UnknownLocation { + UnknownExprLocation() { + locations_expr(this, _, 0, 0, 0, 0) + } +} + +/** + * A dummy location which is used when a statement doesn't have a location + * in the source code but needs to have a `Location` associated with it. + */ +class UnknownStmtLocation extends UnknownLocation { + UnknownStmtLocation() { + locations_stmt(this, _, 0, 0, 0, 0) + } +} + diff --git a/cpp/ql/src/semmle/code/cpp/Macro.qll b/cpp/ql/src/semmle/code/cpp/Macro.qll new file mode 100644 index 000000000000..febc4d4c47cb --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/Macro.qll @@ -0,0 +1,336 @@ +import cpp + +/** + * A macro. + */ +class Macro extends PreprocessorDirective, @ppd_define { + + /** + * Gets the head of this macro. For example, `MAX(x,y)` in + * `#define MAX(x,y) (((x)>(y))?(x):(y))`. + */ + override string getHead() { preproctext(this,result,_) } + + /** + * Gets the body of this macro. For example, `(((x)>(y))?(x):(y))` in + * `#define MAX(x,y) (((x)>(y))?(x):(y))`. + */ + string getBody() { preproctext(this,_,result) } + + /** Gets an invocation of this macro. */ + MacroInvocation getAnInvocation() { result.getMacro() = this } + + override string toString() { + if (this.getBody() = "") then ( + result = "#define " + this.getHead() + ) else ( + result = "#define " + this.getHead() + " " + this.getBody() + ) + } + + /** + * Holds if the body of the macro starts with an unmatched closing + * parenthesis. For example: + * + * #define RPAREN() ) + * + * DEPRECATED: This predicate has a misleading name. + */ + deprecated predicate isFunctionLike() { + this.getBody().regexpMatch("[^(]*\\).*") + } + + /** + * Gets the name of the macro. For example, `MAX` in + * `#define MAX(x,y) (((x)>(y))?(x):(y))`. + */ + string getName() { + result = getHead().splitAt("(", 0) + } + + /** Holds if the macro has name `name`. */ + predicate hasName(string name) { + getName() = name + } +} + +/** + * A macro access (macro expansion or other macro access). + */ +class MacroAccess extends Locatable, @macroinvocation { + /** Gets the macro being invoked. */ + Macro getMacro() { macroinvocations(this,result,_,_) } + + /** + * Gets the location of the outermost macro access that triggered this macro + * access. This is equivalent to calling + * `this.getOutermostMacroAccess().getActualLocation()`. For example, the + * location of the invocation of `C` in `P(C)` will be the whole source range + * starting with `P` and ending with `)`. + */ + override Location getLocation() { + result = this.getOutermostMacroAccess().getActualLocation() + } + + /** + * Gets the location of this macro invocation. For a nested invocation, where + * `exists(this.getParentInvocation())`, this yields a location either inside + * a `#define` directive or inside an argument to another macro. + */ + Location getActualLocation() { + macroinvocations(this,_,result,_) + } + + /** + * Gets the parent macro invocation, if any. For example: + * + * ``` + * 1: #define C 0 + * 2: #define P C + * 3: static int x = P; + * ``` + * + * The invocation of `P` on line 3 also invokes `C`. The invocation of + * `P` is the parent of the invocation of `C`. + * + * A macro invocation occurring in a macro argument often also establishes a + * parent relationship. This is due to the "call-by-name" evaluation order of + * C macros, where macro arguments are first substituted textually into the + * macro body before macro expansion is again performed on the body, invoking + * the macros present in the original macro argument. For example: + * + * ``` + * 1: #define C 0 + * 2: #define P(c) c + c + * 3: static int x = P(C); + * ``` + * + * In this case, `P(C)` first expands to `C + C`, which triggers an + * invocation of `C` whose parent is the invocation of `P`. Had `c` not + * occurred in the body of `P`, there would have been no invocation of `C`. + * There is only a single invocation even though `c` occurs twice; this is an + * optimization for efficiency. + */ + MacroInvocation getParentInvocation() { macroparent(this,result) } + + /** + * Gets the outermost `MacroAccess` along the chain of `getParentInvocation`. + * If `this` has no parent, the result will be `this` itself. + */ + MacroAccess getOutermostMacroAccess() { + if (not exists(this.getParentInvocation())) then + result = this + else + result = this.getParentInvocation().getOutermostMacroAccess() + } + + override string toString() { result = this.getMacro().getHead() } + + /** Gets the name of the invoked macro. */ + string getMacroName() { + result = getMacro().getName() + } +} + +/** + * A macro invocation (macro expansion). + */ +class MacroInvocation extends MacroAccess { + MacroInvocation() { + macroinvocations(this,_,_,1) + } + + /** + * Gets an element that occurs in this macro invocation or a nested macro + * invocation. + */ + Locatable getAnExpandedElement() { inmacroexpansion(result,this) } + + /** + * Gets an element that is (partially) affected by a macro + * invocation. This is a superset of the set of expanded elements and + * includes elements that are not completely enclosed by the expansion as + * well. + */ + Locatable getAnAffectedElement() { + inmacroexpansion(result,this) or macrolocationbind(this, result.getLocation()) + } + + /** + * Gets an element that is either completely in the macro expansion, or + * (if it is a statement) 'almost' in the macro expansion (for instance + * up to a trailing semicolon). Useful for common patterns in which + * macros are almost syntactically complete elements but not quite. + */ + Locatable getAGeneratedElement() { + result = this.getAnExpandedElement() or + result.(Stmt).getGeneratingMacro() = this + } + + Function getEnclosingFunction() { + result = this.getAnAffectedElement().(Expr).getEnclosingFunction() + } + + /** + * Gets the top-level expression associated with this macro invocation, + * if any. Note that this predicate will fail if the top-level expanded + * element is a statement rather than an expression. + */ + Expr getExpr() { + result = getAnExpandedElement() and + not (result.getParent() = getAnExpandedElement()) + } + + /** + * Gets the top-level statement associated with this macro invocation, if + * any. Note that this predicate will fail if the top-level expanded + * element is an expression rather than a statement. + */ + Stmt getStmt() { + result = getAnExpandedElement() and + not (result.getParent() = getAnExpandedElement()) + } + + /** + * Gets the `i`th _unexpanded_ argument of this macro invocation, where the + * first argument has `i = 0`. The result has been expanded for macro + * parameters but _not_ for macro invocations. This means that for macro + * invocations not inside a `#define`, which can have no macro parameters in + * their arguments, the result is equivalent to what is in the source text, + * modulo whitespace. + * + * In the following code example, the argument of the outermost invocation is + * `ID(1)` in unexpanded form and `1` in expanded form. + * + * ``` + * #define ID(x) x + * ID(ID(1)) + * ``` + * + * In the following example code, the last line contains an invocation of + * macro `A` and a child invocation of macro `ID`. The argument to `ID` is + * `1` in both unexpanded and expanded form because macro parameters (here, + * `x`) are expanded in both cases. + * + * ``` + * #define ID(x) x + * #define A(x) ID(x) + * A(1) + * ``` + * + * The `...` parameter in variadic macros counts as one parameter that always + * receives one argument, which may contain commas. + * + * Use `getExpandedArgument` to get the expanded form. + */ + string getUnexpandedArgument(int i) { + macro_argument_unexpanded(this, i, result) + } + + /** + * Gets the `i`th _expanded_ argument of this macro invocation, where the + * first argument has `i = 0`. The result has been expanded for macros _and_ + * macro parameters. If the macro definition does not use this argument, the + * extractor will avoid computing the expanded form for efficiency, and the + * result will be "". + * + * See the documentation of `getUnexpandedArgument` for examples of the + * differences between expanded and unexpanded arguments. + */ + string getExpandedArgument(int i) { + macro_argument_expanded(this, i, result) + } +} + +/** + * A top-level expression generated by a macro invocation. + */ +class MacroInvocationExpr extends Expr { + MacroInvocationExpr() { + exists(MacroInvocation i | this = i.getExpr()) + } + + /** + * Gets the macro invocation of which this is the top-level expression. + */ + MacroInvocation getInvocation() { + result.getExpr() = this + } + + /** Gets the name of the invoked macro. */ + string getMacroName() { + result = getInvocation().getMacroName() + } +} + +/** + * A top-level statement generated by a macro invocation. + */ +class MacroInvocationStmt extends Stmt { + MacroInvocationStmt() { + exists(MacroInvocation i | this = i.getStmt()) + } + + /** + * Gets the macro invocation of which this is the top-level statement. + */ + MacroInvocation getInvocation() { + result.getStmt() = this + } + + /** Gets the name of the invoked macro. */ + string getMacroName() { + result = getInvocation().getMacroName() + } +} + +/** Holds if `l` is the location of a macro. */ +predicate macroLocation(Location l) { + macrolocationbind(_, l) +} + +/** Holds if `element` is in the expansion of a macro. */ +predicate inMacroExpansion(Locatable element) { + inmacroexpansion(element,_) or + (macroLocation(element.getLocation()) and + not topLevelMacroAccess(element)) +} + +/** + * Holds if `ma` is a `MacroAccess` that is not nested inside another + * macro invocation. + */ +private predicate topLevelMacroAccess(MacroAccess ma) { + not exists(ma.getParentInvocation()) +} + +/** + * Holds if `element` is in the expansion of a macro from + * a system header. + */ +predicate inSystemMacroExpansion(Locatable element) { + exists (MacroInvocation m + | element = m.getAnExpandedElement() and + not exists (m.getMacro().getLocation().getFile().getRelativePath())) +} + +/** Holds if `element` is affected by a macro. */ +predicate affectedByMacro(Locatable element) { + inMacroExpansion(element) or + affectedbymacroexpansion(element,_) +} + +/** Holds if there is a macro invocation on line `line` of file `f`. */ +predicate macroLine(File f, int line) { + exists(MacroInvocation mi, Location l | + l = mi.getLocation() and + l.getFile() = f and + (l.getStartLine() = line or l.getEndLine() = line) + ) +} + +/** Holds if there might be a macro invocation at location `l`. */ +predicate possibleMacroLocation(Location l) { + macroLine(l.getFile(), l.getStartLine()) or + macroLine(l.getFile(), l.getEndLine()) +} diff --git a/cpp/ql/src/semmle/code/cpp/Member.qll b/cpp/ql/src/semmle/code/cpp/Member.qll new file mode 100644 index 000000000000..92769486ae98 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/Member.qll @@ -0,0 +1,2 @@ +import semmle.code.cpp.Element +import semmle.code.cpp.Type diff --git a/cpp/ql/src/semmle/code/cpp/NameQualifiers.qll b/cpp/ql/src/semmle/code/cpp/NameQualifiers.qll new file mode 100644 index 000000000000..f44e6e42d4c5 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/NameQualifiers.qll @@ -0,0 +1,126 @@ +import cpp + +/** + * A C++ name qualifier, for example `N::`. + */ +class NameQualifier extends NameQualifiableElement, @namequalifier { + /** + * Gets the expression ultimately qualified by the chain of name + * qualifiers. For example, `f()` in `N1::N2::f()`. + */ + Expr getExpr() { + result = getQualifiedElement+() + } + + /** Gets a location for this name qualifier. */ + override Location getLocation() { + namequalifiers(this,_,_,result) + } + + /** + * Gets the name qualifier that qualifies this name qualifier, if any. + * This is used for name qualifier chains, for example the name qualifier + * `N2::` has a name qualifier `N1::` in the chain `N1::N2::f()`. + */ + override NameQualifier getNameQualifier() { + namequalifiers(result,this,_,_) + } + + /** + * Gets the element qualified by this name qualifier. For example, `f()` + * in `N::f()`. + */ + NameQualifiableElement getQualifiedElement() { + namequalifiers(this,result,_,_) + } + + /** + * Gets the qualifying element of this name qualifier. For example, `N` + * in `N::f()`. + */ + NameQualifyingElement getQualifyingElement() { + exists (NameQualifyingElement nqe + | namequalifiers(this,_,nqe,_) and + if nqe instanceof SpecialNameQualifyingElement + then (exists (Access a + | a = getQualifiedElement() and + result = a.getTarget().getDeclaringType()) + or + exists (FunctionCall c + | c = getQualifiedElement() and + result = c.getTarget().getDeclaringType())) + else result = nqe) + } + + override string toString() { + exists (NameQualifyingElement nqe + | namequalifiers(this,_,nqe,_) + and result = nqe.getName() + "::") + } +} + +/** + * A C++ element that can be qualified with a name. This is in practice + * either an expression or a name qualifier. For instance, in + * `N1::N2::f()`, there are two name-qualifiable elements: the expression + * `f()` and the name qualifier `N2::`. The former is qualified by `N2` and + * the latter is qualified by `N1`. + */ +class NameQualifiableElement extends Element, @namequalifiableelement { + /** Gets the name qualifier associated with this element. */ + NameQualifier getNameQualifier() { + namequalifiers(result,this,_,_) + } + + /** + * Holds if this element has a globally qualified name. For example, + * `::x` is globally qualified. It is used to refer to `x` in the global + * namespace. + */ + predicate hasGlobalQualifiedName() { + getNameQualifier*().getQualifyingElement() instanceof GlobalNamespace + } + + /** + * Holds if this element has a `__super`-qualified name. For example: + * `__super::get()`. Note: `__super` is non-standard C++ extension, only + * supported by some C++ compilers. + */ + predicate hasSuperQualifiedName() { + exists(NameQualifier nq, SpecialNameQualifyingElement snqe | + nq = getNameQualifier*() + and namequalifiers(nq,_,snqe,_) + and snqe.getName() = "__super" + ) + } +} + +/** + * A C++ element that can qualify a name. For example, `N` in `N::f()`. A + * name-qualifying element is either a namespace or a user-defined type. + */ +class NameQualifyingElement extends Element, @namequalifyingelement { + /** + * Gets a name qualifier for which this is the qualifying namespace or + * user-defined type. For example: class `X` is the + * `NameQualifyingElement` and `X::` is the `NameQualifier`. + */ + NameQualifier getANameQualifier() { + namequalifiers(result,_,this,_) + } + + /** Gets the name of this namespace or user-defined type. */ + string getName() { + none() + } +} + +/** + * A special name-qualifying element. For example: `__super`. + */ +library class SpecialNameQualifyingElement extends NameQualifyingElement, @specialnamequalifyingelement { + /** Gets the name of this special qualifying element. */ + override string getName() { + specialnamequalifyingelements(this,result) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/Namespace.qll b/cpp/ql/src/semmle/code/cpp/Namespace.qll new file mode 100644 index 000000000000..0b84ba288297 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/Namespace.qll @@ -0,0 +1,237 @@ +import semmle.code.cpp.Element +import semmle.code.cpp.Type +import semmle.code.cpp.metrics.MetricNamespace + +/** + * A C++ namespace. + * + * Note that namespaces are somewhat nebulous entities, as they do not in + * general have a single well-defined location in the source code. The + * related notion of a `NamespaceDeclarationEntry` is rather more concrete, + * and should be used when a location is required. For example, the `std::` + * namespace is particularly nebulous, as parts of it are defined across a + * wide range of headers. As a more extreme example, the global namespace + * is never explicitly declared, but might correspond to a large proportion + * of the source code. + */ +class Namespace extends NameQualifyingElement, @namespace { + + /** + * Gets the location of the namespace. Most namespaces do not have a + * single well-defined source location, so a dummy location is returned, + * unless the namespace has exactly one declaration entry. + */ + override Location getLocation() { + if strictcount(getADeclarationEntry()) = 1 then + result = getADeclarationEntry().getLocation() + else + ( + result instanceof UnknownDefaultLocation + ) + } + + /** Gets the simple name of this namespace. */ + override string getName() { namespaces(this,result) } + + /** Holds if this element is named `name`. */ + predicate hasName(string name) { name = this.getName() } + + /** Holds if the namespace is anonymous. */ + predicate isAnonymous() { + hasName("(unnamed namespace)") + } + + /** Gets the name of the parent namespace, if it exists. */ + private string getParentName() { + result = this.getParentNamespace().getName() and + result != "" + } + + /** Gets the qualified name of this namespace. For example: `a::b`. */ + string getQualifiedName() { + if exists (getParentName()) + then result = getParentNamespace().getQualifiedName() + "::" + getName() + else result = getName() + } + + /** Gets the parent namespace, if any. */ + Namespace getParentNamespace() { + namespacembrs(result,this) or + (not namespacembrs(_, this) and result instanceof GlobalNamespace) + } + + /** Gets a child declaration of this namespace. */ + Declaration getADeclaration() { namespacembrs(this,result) } + + /** Gets a child namespace of this namespace. */ + Namespace getAChildNamespace() { namespacembrs(this,result) } + + /** Holds if this namespace may be from source. */ + override predicate fromSource() { this.getADeclaration().fromSource() } + + /** + * Holds if this namespace is in a library. + * + * DEPRECATED: never holds. + */ + deprecated override + predicate fromLibrary() { not this.fromSource() } + + /** Gets the metric namespace. */ + MetricNamespace getMetrics() { result = this } + + override string toString() { result = this.getQualifiedName() } + + /** Gets a declaration of (part of) this namespace. */ + NamespaceDeclarationEntry getADeclarationEntry() { + result.getNamespace() = this + } + + /** Gets a file which declares (part of) this namespace. */ + File getAFile() { + result = this.getADeclarationEntry().getLocation().getFile() + } + +} + +/** + * A declaration of (part of) a C++ namespace. + * + * This corresponds to a single `namespace N { ... }` occurrence in the + * source code. + */ +class NamespaceDeclarationEntry extends Locatable, @namespace_decl { + /** + * Get the namespace that this declaration entry corresponds to. There + * is a one-to-many relationship between `Namespace` and + * `NamespaceDeclarationEntry`. + */ + Namespace getNamespace() { namespace_decls(this,result,_,_) } + + override string toString() { result = this.getNamespace().toString() } + + /** + * Gets the location of the token preceding the namespace declaration + * entry's body. + * + * For named declarations, such as "namespace MyStuff { ... }", this will + * give the "MyStuff" token. + * + * For anonymous declarations, such as "namespace { ... }", this will + * give the "namespace" token. + */ + override Location getLocation() { namespace_decls(this,_,result,_) } + + /** + * Gets the location of the namespace declaration entry's body. For + * example: the "{ ... }" in "namespace N { ... }". + */ + Location getBodyLocation() { namespace_decls(this,_,_,result) } +} + +/** + * A C++ `using` directive or `using` declaration. + */ +abstract class UsingEntry extends Locatable, @using { + override Location getLocation() { usings(this,_,result) } +} + +/** + * A C++ `using` declaration. For example: + * + * `using std::string;` + */ +class UsingDeclarationEntry extends UsingEntry { + UsingDeclarationEntry() { not exists(Namespace n | usings(this,n,_)) } + + /** + * Gets the declaration that is referenced by this using declaration. For + * example, `std::string` in `using std::string`. + */ + Declaration getDeclaration() { usings(this,result,_) } + + override string toString() { + result = "using " + this.getDeclaration().toString() + } +} + +/** + * A C++ `using` directive. For example: + * + * `using namespace std;` + */ +class UsingDirectiveEntry extends UsingEntry { + UsingDirectiveEntry() { exists(Namespace n | usings(this,n,_)) } + + /** + * Gets the namespace that is referenced by this using directive. For + * example, `std` in `using namespace std`. + */ + Namespace getNamespace() { usings(this,result,_) } + + override string toString() { + result = "using namespace " + this.getNamespace().toString() + } +} + +/** + * Holds if `g` is an instance of `GlobalNamespace`. This predicate + * is used suppress a warning in `GlobalNamespace.getADeclaration()` + * by providing a fake use of `this`. + */ +private predicate suppressWarningForUnused(GlobalNamespace g) { any() } + +/** + * The C/C++ global namespace. + */ +class GlobalNamespace extends Namespace { + + GlobalNamespace() { this.hasName("") } + + override Declaration getADeclaration() { + suppressWarningForUnused(this) and + not exists(DeclStmt d | + d.getADeclaration() = result and + not result instanceof Function + ) and + not exists(ConditionDeclExpr cde | cde.getVariable() = result) and + not exists(Enum e | e.getAnEnumConstant() = result) and + not result instanceof Parameter and + not result instanceof ProxyClass and + not result instanceof TemplateParameter and + not result instanceof LocalVariable and + not namespacembrs(_, result) and + not result.isMember() + } + + /** Gets a child namespace of the global namespace. */ + override Namespace getAChildNamespace() { + suppressWarningForUnused(this) and + not (namespacembrs(result, _)) + } + + override Namespace getParentNamespace() { + none() + } + + /** + * DEPRECATED: use `getName()`. + */ + deprecated string getFullName() { + result = this.getName() + } + + override string toString() { + result = "(global namespace)" + } + +} + +/** + * The C++ `std::` namespace. + */ +class StdNamespace extends Namespace { + StdNamespace() { + this.hasName("std") and this.getParentNamespace() instanceof GlobalNamespace + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ObjectiveC.qll b/cpp/ql/src/semmle/code/cpp/ObjectiveC.qll new file mode 100644 index 000000000000..f168e551faa2 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ObjectiveC.qll @@ -0,0 +1,192 @@ +import semmle.code.cpp.Class +private import semmle.code.cpp.internal.Type + +/** + * DEPRECATED: Objective-C is no longer supported. + * An Objective C class. + */ +deprecated class ObjectiveClass extends Class { + ObjectiveClass() { none() } +} + +/** + * DEPRECATED: Objective-C is no longer supported. + * An Objective C protocol. + */ +deprecated class Protocol extends Class { + Protocol() { none() } + + /** + * Holds if the type implements the protocol, either because the type + * itself does, or because it is a type conforming to the protocol. + */ + predicate isImplementedBy(Type t) { none() } +} + +/** + * DEPRECATED: Objective-C is no longer supported. + * A type which conforms to a protocol. Use `getAProtocol` to get a + * protocol that this type conforms to. + */ +deprecated class TypeConformingToProtocol extends DerivedType { + TypeConformingToProtocol() { none() } + + /** Gets a protocol that this type conforms to. */ + Protocol getAProtocol() { none() } + + /** Gets the size of this type. */ + override int getSize() { none() } + + override int getAlignment() { none() } +} + +/** + * DEPRECATED: Objective-C is no longer supported. + * An Objective C `@autoreleasepool` statement, for example + * `@autoreleasepool { int x; int y; }`. + */ +deprecated class AutoReleasePoolStmt extends Stmt { + AutoReleasePoolStmt() { none() } + + override string toString() { none() } + + /** Gets the body statement of this `@autoreleasepool` statement. */ + Stmt getStmt() { none() } + + override predicate mayBeImpure() { none() } + + override predicate mayBeGloballyImpure() { none() } +} + +/** + * DEPRECATED: Objective-C is no longer supported. + * An Objective C `@synchronized statement`, for example + * `@synchronized (x) { [x complicationOperation]; }`. + */ +deprecated class SynchronizedStmt extends Stmt { + SynchronizedStmt() { none() } + + override string toString() { none() } + + /** Gets the expression which gives the object to be locked. */ + Expr getLockedObject() { none() } + + /** Gets the body statement of this `@synchronized` statement. */ + Stmt getStmt() { none() } +} + +/** + * DEPRECATED: Objective-C is no longer supported. + * An Objective C for-in statement. + */ +deprecated class ForInStmt extends Loop { + ForInStmt() { none() } + + /** + * Gets the condition expression of the `while` statement that the + * `for...in` statement desugars into. + */ + override Expr getCondition() { none() } + + override Expr getControllingExpr() { none() } + + /** Gets the collection that the loop iterates over. */ + Expr getCollection() { none() } + + /** Gets the body of the loop. */ + override Stmt getStmt() { none() } + + override string toString() { none() } +} + +/** + * DEPRECATED: Objective-C is no longer supported. + * An Objective C category or class extension. + */ +deprecated class Category extends Class { + Category() { none() } +} + +/** + * DEPRECATED: Objective-C is no longer supported. + * An Objective C class extension. + */ +deprecated class ClassExtension extends Category { + ClassExtension() { none() } +} + +/** + * DEPRECATED: Objective-C is no longer supported. + * An Objective C try statement. + */ +deprecated class ObjcTryStmt extends TryStmt { + ObjcTryStmt() { none() } + + override string toString() { none() } + + /** Gets the finally clause of this try statement, if any. */ + FinallyBlock getFinallyClause() { none() } +} + +/** + * DEPRECATED: Objective-C is no longer supported. + * An Objective C `@finally` block. + */ +deprecated class FinallyBlock extends Block { + FinallyBlock() { none() } + + /** Gets the try statement corresponding to this finally block. */ + ObjcTryStmt getTryStmt() { + none() + } +} + +/** + * DEPRECATED: Objective-C is no longer supported. + * An Objective C `@property`. + */ +deprecated class Property extends Declaration { + Property() { none() } + + /** Gets the name of this property. */ + override string getName() { none() } + + /** + * Gets nothing (provided for compatibility with Declaration). + * + * For the attribute list following the `@property` keyword, use + * `getAnAttribute()`. + */ + override Specifier getASpecifier() { none() } + + /** + * Gets an attribute of this property (such as `readonly`, `nonatomic`, + * or `getter=isEnabled`). + */ + Attribute getAnAttribute() { none() } + + override Location getADeclarationLocation() { result = getLocation() } + override Location getDefinitionLocation() { result = getLocation() } + override Location getLocation() { none() } + + /** Gets the type of this property. */ + Type getType() { none() } + + /** + * Gets the instance method which is called to get the value of this + * property. + */ + MemberFunction getGetter() { none() } + + /** + * Gets the instance method which is called to set the value of this + * property (if it is a writable property). + */ + MemberFunction getSetter() { none() } + + /** + * Gets the instance variable which stores the property value (if this + * property was explicitly or automatically `@synthesize`d). + */ + MemberVariable getInstanceVariable() { none() } +} diff --git a/cpp/ql/src/semmle/code/cpp/PODType03.qll b/cpp/ql/src/semmle/code/cpp/PODType03.qll new file mode 100644 index 000000000000..5b47f80af979 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/PODType03.qll @@ -0,0 +1,112 @@ +/** + * Provides predicates to determine whether a type is an aggregate or POD + * (Plain Old Data), as defined by C++03. + */ + +import cpp + +/** + * Holds if `t` is a scalar type, according to the rules specified in + * C++03 3.9(10): + * + * Arithmetic types (3.9.1), enumeration types, pointer types, and + * pointer to member types (3.9.2), and cv-qualified versions of these + * types (3.9.3) are collectively called scalar types. + */ +predicate isScalarType03(Type t) { + exists (Type ut + | ut = t.getUnderlyingType() + | ut instanceof ArithmeticType or + ut instanceof Enum or + ut instanceof FunctionPointerType or + ut instanceof PointerToMemberType or + ut instanceof PointerType or + isScalarType03(ut.(SpecifiedType).getUnspecifiedType())) +} + +/** + * Holds if `c` is an aggregate class, according to the rules specified in + * C++03 8.5.1(1): + * + * An aggregate [class] is ... a class (clause 9) with no user-declared + * constructors (12.1), no private or protected non-static data members + * (clause 11), no base classes (clause 10), and no virtual functions + * (10.3). + */ +predicate isAggregateClass03(Class c) { + not (c instanceof TemplateClass) and + not exists (Constructor cons + | cons.getDeclaringType() = c and + not cons.isCompilerGenerated()) and + not exists (Variable v + | v.getDeclaringType() = c and + not v.isStatic() + | v.hasSpecifier("private") or + v.hasSpecifier("protected")) and + not exists (c.getABaseClass()) and + not exists (VirtualFunction f + | f.getDeclaringType() = c) +} + +/** + * Holds if `t` is an aggregate type, according to the rules specified in + * C++03 8.5.1(1): + * + * An aggregate is an array or a class (clause 9) with no user-declared + * constructors (12.1), no private or protected non-static data members + * (clause 11), no base classes (clause 10), and no virtual functions + * (10.3). + */ +predicate isAggregateType03(Type t) { + exists (Type ut + | ut = t.getUnderlyingType() + | ut instanceof ArrayType or + isAggregateClass03(ut)) +} + +/** + * Holds if `c` is a POD class, according to the rules specified in + * C++03 9(4): + * + * A POD-struct is an aggregate class that has no non-static data members + * of type non-POD-struct, non-POD-union (or array of such types) or + * reference, and has no user-defined copy assignment operator and no + * user-defined destructor. Similarly, a POD-union is an aggregate union + * that has no non-static data members of type non-POD-struct, + * non-POD-union (or array of such types) or reference, and has no + * user-defined copy assignment operator and no user-defined destructor. + * A POD class is a class that is either a POD-struct or a POD-union. + */ +predicate isPODClass03(Class c) { + isAggregateClass03(c) and + not exists (Variable v + | v.getDeclaringType() = c and + not v.isStatic() + | not isPODType03(v.getType()) or + exists (ArrayType at + | at = v.getType() and + not isPODType03(at.getBaseType())) or + v.getType() instanceof ReferenceType) and + not exists (CopyAssignmentOperator o | o.getDeclaringType() = c and + not o.isCompilerGenerated()) and + not exists (Destructor dest | dest.getDeclaringType() = c and + not dest.isCompilerGenerated()) +} + +/** + * Holds if `t` is a POD type, according to the rules specified in + * C++03 3.9(10): + * + * Scalar types, POD-struct types, POD-union types (clause 9), arrays of + * such types and cv-qualified versions of these types (3.9.3) are + * collectively called POD types. + */ +predicate isPODType03(Type t) +{ + exists (Type ut + | ut = t.getUnderlyingType() + | isScalarType03(ut) or + isPODClass03(ut) or + exists (ArrayType at | at = ut and isPODType03(at.getBaseType())) or + isPODType03(ut.(SpecifiedType).getUnspecifiedType())) +} diff --git a/cpp/ql/src/semmle/code/cpp/Parameter.qll b/cpp/ql/src/semmle/code/cpp/Parameter.qll new file mode 100644 index 000000000000..7653a60344b9 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/Parameter.qll @@ -0,0 +1,143 @@ +import semmle.code.cpp.Location +import semmle.code.cpp.Declaration +private import semmle.code.cpp.internal.Type + +/** + * A C/C++ function parameter or catch block parameter. + * + * For catch block parameters, there is a one-to-one correspondence between + * the `Parameter` and its `ParameterDeclarationEntry`. + * + * For function parameters, there is a one-to-many relationship between + * `Parameter` and `ParameterDeclarationEntry`, because one function can + * have multiple declarations. + */ +class Parameter extends LocalScopeVariable, @parameter { + + /** + * Gets the canonical name, or names, of this parameter. + * + * The canonical names are the first non-empty category from the + * following list: + * 1. The name given to the parameter at the function's definition or + * (for catch block parameters) at the catch block. + * 2. A name given to the parameter at a function declaration. + * 3. The name "p#i" where i is the index of the parameter. + */ + override string getName() { + exists (VariableDeclarationEntry vde + | vde = getANamedDeclarationEntry() and result = vde.getName() + | vde.isDefinition() or not getANamedDeclarationEntry().isDefinition()) + or + (not exists(getANamedDeclarationEntry()) and + result = "p#" + this.getIndex().toString()) + } + + /** + * Gets the name of this parameter, including it's type. + * + * For example: `int p`. + */ + string getTypedName() { + exists (string typeString, string nameString + | (if exists(getType().getName()) + then typeString = getType().getName() + else typeString = "") + and + (if exists(getName()) + then nameString = getName() + else nameString = "") + and + (if typeString != "" and nameString != "" + then result = typeString + " " + nameString + else result = typeString + nameString)) + } + + private VariableDeclarationEntry getANamedDeclarationEntry() { + result = getAnEffectiveDeclarationEntry() and result.getName() != "" + } + + /** + * Gets a declaration entry corresponding to this declaration. + * + * This predicate is the same as getADeclarationEntry(), except that for + * parameters of instantiated function templates, gives the declaration + * entry of the prototype instantiation of the parameter (as + * non-prototype instantiations don't have declaration entries of their + * own). + */ + private VariableDeclarationEntry getAnEffectiveDeclarationEntry() { + if getFunction().isConstructedFrom(_) + then exists (Parameter prototype + | prototype = result.getVariable() and + prototype.getIndex() = getIndex() and + getFunction().isConstructedFrom(prototype.getFunction())) + else result = getADeclarationEntry() + } + + /** + * Gets the name of this parameter in the given block (which should be + * the body of a function with which the parameter is associated). + * + * DEPRECATED: this method was used in a previous implementation of + * getName, but is no longer in use. + */ + deprecated string getNameInBlock(Block b) { + exists (ParameterDeclarationEntry pde + | pde.getFunctionDeclarationEntry().getBlock() = b and + this.getFunction().getBlock() = b and + pde.getVariable() = this and + result = pde.getName()) + } + + /** + * Holds if this parameter has a name. + * + * In other words, this predicate holds precisely when the result of + * `getName()` is not "p#i" (where `i` is the index of the parameter). + */ + predicate isNamed() { exists(getANamedDeclarationEntry()) } + + /** + * Gets the function to which this parameter belongs, if it is a function + * parameter. + */ + override Function getFunction() { params(this,result,_,_) } + + /** + * Gets the catch block to which this parameter belongs, if it is a catch + * block parameter. + */ + Block getCatchBlock() { params(this,result,_,_) } + + /** + * Gets the zero-based index of this parameter. + * + * For catch block parameters, this is always zero. + */ + int getIndex() { params(this,_,result,_) } + + /** + * Gets the type of this parameter. + * + * Function parameters of array type are a special case in C/C++, + * as they are syntactic sugar for parameters of pointer type. The + * result is an array type for such parameters. + */ + override Type getType() { params(this,_,_,unresolve(result)) } + + /** + * Gets the canonical location, or locations, of this parameter. + * + * 1. For catch block parameters, gets the obvious location. + * 2. For parameters of functions which have a definition, gets the + * location within the function definition. + * 3. For parameters of functions which don't have a definition, gets all + * of the declaration locations. + */ + override Location getLocation() { + exists(VariableDeclarationEntry vde | vde = getAnEffectiveDeclarationEntry() and result = vde.getLocation() | + vde.isDefinition() or not getAnEffectiveDeclarationEntry().isDefinition() + ) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/Preprocessor.qll b/cpp/ql/src/semmle/code/cpp/Preprocessor.qll new file mode 100644 index 000000000000..751a37baa916 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/Preprocessor.qll @@ -0,0 +1,223 @@ +import semmle.code.cpp.Location +import semmle.code.cpp.Element + +/** + * A C/C++ preprocessor directive. + * + * For example: `#ifdef`, `#line`, or `#pragma`. + */ +class PreprocessorDirective extends Locatable, @preprocdirect { + override string toString() { result = "Preprocessor directive" } + override Location getLocation() { preprocdirects(this,_,result) } + string getHead() { preproctext(this,result,_) } + + /** + * Gets a preprocessor branching directive whose condition affects + * whether this directive is performed. + * + * From a lexical point of view, this returns all `#if`, `#ifdef`, + * `#ifndef`, or `#elif` directives which occur before this directive and + * have a matching `#endif` which occurs after this directive. + */ + PreprocessorBranch getAGuard() { + exists(PreprocessorEndif e, int line | + result.getEndIf() = e and + e.getFile() = getFile() and + result.getFile() = getFile() and + line = this.getLocation().getStartLine() and + result.getLocation().getStartLine() < line and + line < e.getLocation().getEndLine() + ) + } +} + +/** + * A C/C++ preprocessor branch related directive: `#if`, `#ifdef`, + * `#ifndef`, `#elif`, `#else` or `#endif`. + */ +abstract class PreprocessorBranchDirective extends PreprocessorDirective { + /** + * Gets the `#if`, `#ifdef` or `#ifndef` directive which matches this + * branching directive. + * + * If this branch directive was unbalanced, then there will be no + * result. Conversely, if the branch matches different `#if` directives + * in different translation units, then there can be more than one + * result. + */ + PreprocessorBranch getIf() { + result = (PreprocessorIf)this or + result = (PreprocessorIfdef)this or + result = (PreprocessorIfndef)this or + preprocpair(result, this) + } + + /** + * Gets the `#endif` directive which matches this branching directive. + * + * If this branch directive was unbalanced, then there will be no + * result. Conversely, if the branch matched different `#endif` + * directives in different translation units, then there can be more than + * one result. + */ + PreprocessorEndif getEndIf() { + preprocpair(getIf(), result) + } + + /** + * Gets the next `#elif`, `#else` or `#endif` matching this branching + * directive. + * + * For example `somePreprocessorBranchDirective.getIf().getNext()` gets + * the second directive in the same construct as + * `somePreprocessorBranchDirective`. + */ + PreprocessorBranchDirective getNext() { + getIf() = result.getIf() and + getLocation().getStartLine() < result.getLocation().getStartLine() and + not exists(PreprocessorBranchDirective other | + getIf() = other.getIf() and + getLocation().getStartLine() < other.getLocation().getStartLine() and + other.getLocation().getStartLine() < result.getLocation().getStartLine() + ) + } +} + +/** + * A C/C++ preprocessor branching directive: `#if`, `#ifdef`, `#ifndef`, or + * `#elif`. + * + * A branching directive can have its condition evaluated at compile-time, + * and as a result, the preprocessor will either take the branch, or not + * take the branch. + * + * However, there are also situations in which a branch's condition isn't + * evaluated. The obvious case of this is when the directive is contained + * within a branch which wasn't taken. There is also a much more subtle + * case involving header guard branches: suitably clever compilers can + * notice that a branch is a header guard, and can then subsequently ignore + * a `#include` for the file being guarded. It is for this reason that + * `wasTaken()` always holds on header guard branches, but `wasNotToken()` + * rarely holds on header guard branches. + */ +class PreprocessorBranch extends PreprocessorBranchDirective, @ppd_branch { + /** + * Holds if at least one translation unit evaluated this directive's + * condition and subsequently took the branch. + */ + predicate wasTaken() { + preproctrue(this) + } + + /** + * Holds if at least one translation unit evaluated this directive's + * condition but then didn't take the branch. + * + * If `#else` is the next matching directive, then this means that the + * `#else` was taken instead. + */ + predicate wasNotTaken() { + preprocfalse(this) + } + + /** + * Holds if this directive was either taken by all translation units + * which evaluated it, or was not taken by any translation unit which + * evaluated it. + */ + predicate wasPredictable() { + not ( wasTaken() and wasNotTaken() ) + } +} + +/** + * A C/C++ preprocessor `#if` directive. + * + * For the related notion of a directive which causes branching (which + * includes `#if`, plus also `#ifdef`, `#ifndef`, and `#elif`), see + * `PreprocessorBranch`. + */ +class PreprocessorIf extends PreprocessorBranch, @ppd_if { + override string toString() { result = "#if " + this.getHead() } +} + +/** + * A C/C++ preprocessor `#ifdef` directive. + * + * The syntax `#ifdef X` is shorthand for `#if defined(X)`. + */ +class PreprocessorIfdef extends PreprocessorBranch, @ppd_ifdef { + override string toString() { result = "#ifdef " + this.getHead() } +} + +/** + * A C/C++ preprocessor `#ifndef` directive. + * + * The syntax `#ifndef X` is shorthand for `#if !defined(X)`. + */ +class PreprocessorIfndef extends PreprocessorBranch, @ppd_ifndef { + override string toString() { result = "#ifndef " + this.getHead() } +} + +/** + * A C/C++ preprocessor `#else` directive. + */ +class PreprocessorElse extends PreprocessorBranchDirective, @ppd_else { + override string toString() { result = "#else" } +} + +/** + * A C/C++ preprocessor `#elif` directive. + */ +class PreprocessorElif extends PreprocessorBranch, @ppd_elif { + override string toString() { result = "#elif " + this.getHead() } +} + +/** + * A C/C++ preprocessor `#endif` directive. + */ +class PreprocessorEndif extends PreprocessorBranchDirective, @ppd_endif { + override string toString() { result = "#endif" } +} + +/** + * A C/C++ preprocessor `#warning` directive. + */ +class PreprocessorWarning extends PreprocessorDirective, @ppd_warning { + override string toString() { result = "#warning " + this.getHead() } +} + +/** + * A C/C++ preprocessor `#error` directive. + */ +class PreprocessorError extends PreprocessorDirective, @ppd_error { + override string toString() { result = "#error " + this.getHead() } +} + +/** + * A C/C++ preprocessor `#undef` directive. + */ +class PreprocessorUndef extends PreprocessorDirective, @ppd_undef { + override string toString() { result = "#undef " + this.getHead() } + + /** + * Gets the name of the macro that is undefined. + */ + string getName() { + result = getHead() + } +} + +/** + * A C/C++ preprocessor `#pragma` directive. + */ +class PreprocessorPragma extends PreprocessorDirective, @ppd_pragma { + override string toString() { result = "#pragma " + this.getHead() } +} + +/** + * A C/C++ preprocessor `#line` directive. + */ +class PreprocessorLine extends PreprocessorDirective, @ppd_line { + override string toString() { result = "#line " + this.getHead() } +} diff --git a/cpp/ql/src/semmle/code/cpp/PrintAST.ql b/cpp/ql/src/semmle/code/cpp/PrintAST.ql new file mode 100644 index 000000000000..78ee141cce90 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/PrintAST.ql @@ -0,0 +1,8 @@ +/** + * @name Print AST + * @description Outputs a representation of the Abstract Syntax Tree. + * @id cpp/print-ast + */ + +import cpp +import PrintAST diff --git a/cpp/ql/src/semmle/code/cpp/PrintAST.qll b/cpp/ql/src/semmle/code/cpp/PrintAST.qll new file mode 100644 index 000000000000..dccda86c8b0f --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/PrintAST.qll @@ -0,0 +1,182 @@ +import cpp + +private newtype TPrintASTConfiguration = MkPrintASTConfiguration() + +class PrintASTConfiguration extends TPrintASTConfiguration { + string toString() { + result = "PrintASTConfiguration" + } + + predicate shouldPrintFunction(Function func) { + any() + } +} + +private predicate shouldPrintFunction(Function func) { + exists(PrintASTConfiguration config | + config.shouldPrintFunction(func) + ) +} + +private Locatable getAChild(Locatable parent) { + result = getChild(parent, _) +} + +private Function getEnclosingFunction(Locatable ast) { + getAChild*(result) = ast +} + +private int getEntryPointIndex(Function func) { + if func instanceof Constructor then + result = count(func.(Constructor).getAnInitializer()) + else + result = 0 +} + +private Locatable getChild(Locatable parent, int childIndex) { + exists(Function func, Stmt entryPoint | + parent = func and + result = entryPoint and + entryPoint = func.getEntryPoint() and + childIndex = getEntryPointIndex(func) + ) or + exists(Function func, Expr childExpr | + parent = func and + result = childExpr and + ( + childExpr = func.(Constructor).getInitializer(childIndex) or + childExpr = func.(Destructor).getDestruction(childIndex - 1) + ) + ) or + exists(Stmt parentStmt | + parentStmt = parent and + ( + parentStmt.getChild(childIndex).(Expr).getFullyConverted() = result or + parentStmt.getChild(childIndex).(Stmt) = result + ) + ) or + exists(Expr parentExpr, Expr childExpr | + parent = parentExpr and + result = childExpr and + childExpr = parentExpr.getChild(childIndex).getFullyConverted() + ) or + exists(Conversion parentConv, Expr childExpr | + parent = parentConv and + result = childExpr and + childExpr = parentConv.getExpr() and + childIndex = 0 + ) or + exists(DeclStmt declStmt, DeclarationEntry childEntry | + parent = declStmt and + result = childEntry and + childEntry = declStmt.getDeclarationEntry(childIndex) + ) or + exists(VariableDeclarationEntry declEntry, Initializer init | + parent = declEntry and + result = init and + init = declEntry.getVariable().getInitializer() and + childIndex = 0 + ) or + exists(Initializer init, Expr expr | + parent = init and + result = expr and + expr = init.getExpr().getFullyConverted() and + childIndex = 0 + ) +} + +private string getTypeString(Locatable ast) { + if ast instanceof Expr then ( + exists(Expr expr | + expr = ast and + result = expr.getValueCategoryString() + ": " + expr.getType().toString() + ) + ) + else if ast instanceof DeclarationEntry then ( + result = ast.(DeclarationEntry).getType().toString() + ) + else ( + result = "" + ) +} + +private string getExtraInfoString(Locatable ast) { + if ast instanceof Cast then ( + result = ast.(Cast).getSemanticConversionString() + ) + else ( + result = "" + ) +} + +private string getValueString(Locatable ast) { + if exists(ast.(Expr).getValue()) then + result = "=" + ast.(Expr).getValue() + else + result = "" +} + +private Locatable getChildByRank(Locatable parent, int rankIndex) { + result = rank[rankIndex + 1](Locatable child, int id | + child = getChild(parent, id) | + child order by id + ) +} + +language[monotonicAggregates] +private int getDescendantCount(Locatable ast) { + result = 1 + sum(Locatable child | + child = getChildByRank(ast, _) | + getDescendantCount(child) + ) +} + +private Locatable getParent(Locatable ast) { + ast = getAChild(result) +} + +private int getUniqueId(Locatable ast) { + shouldPrintFunction(getEnclosingFunction(ast)) and + if not exists(getParent(ast)) then + result = 0 + else + exists(Locatable parent | + parent = getParent(ast) and + if ast = getChildByRank(parent, 0) then + result = 1 + getUniqueId(parent) + else + exists(int childIndex, Locatable previousChild | + ast = getChildByRank(parent, childIndex) and + previousChild = getChildByRank(parent, childIndex - 1) and + result = getUniqueId(previousChild) + + getDescendantCount(previousChild) + ) + ) +} + +query predicate printAST(string functionLocation, + string function, int nodeId, int parentId, int childIndex, string ast, + string extra, string value, string type, string astLocation) { + exists(Function func, Locatable astNode | + shouldPrintFunction(func) and + func = getEnclosingFunction(astNode) and + nodeId = getUniqueId(astNode) and + if nodeId = 0 then ( + parentId = -1 and + childIndex = 0 + ) + else ( + exists(Locatable parent | + astNode = getChild(parent, childIndex) and + parentId = getUniqueId(parent) + ) + ) and + functionLocation = func.getLocation().toString() and + function = func.getFullSignature() and + ast = astNode.toString() and + extra = getExtraInfoString(astNode) and + value = getValueString(astNode) and + type = getTypeString(astNode) and + astLocation = astNode.getLocation().toString() + ) +} diff --git a/cpp/ql/src/semmle/code/cpp/Specifier.qll b/cpp/ql/src/semmle/code/cpp/Specifier.qll new file mode 100644 index 000000000000..188dfa418544 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/Specifier.qll @@ -0,0 +1,310 @@ +import semmle.code.cpp.Element +private import semmle.code.cpp.internal.Type + +/** + * A C/C++ specifier: `friend`, `auto`, `register`, `static`, `extern`, + * `mutable`, `inline`, `virtual`, or `explicit`. + */ +class Specifier extends Element, @specifier { + + /** Gets a dummy location for the specifier. */ + override Location getLocation() { + suppressUnusedThis(this) and + result instanceof UnknownDefaultLocation + } + + /** Gets the name of this specifier. */ + string getName() { specifiers(this,result) } + + /** Holds if the name of this specifier is `name`. */ + predicate hasName(string name) { name = this.getName() } + + override string toString() { result = this.getName() } +} + +/** + * A C/C++ function specifier: `inline`, `virtual`, or `explicit`. + */ +class FunctionSpecifier extends Specifier { + FunctionSpecifier() { + this.hasName("inline") or + this.hasName("virtual") or + this.hasName("explicit") + } +} + +/** + * A C/C++ storage class specifier: `auto`, `register`, `static`, `extern`, + * or `mutable". + */ +class StorageClassSpecifier extends Specifier { + StorageClassSpecifier() { + this.hasName("auto") or + this.hasName("register") or + this.hasName("static") or + this.hasName("extern") or + this.hasName("mutable") + } +} + +/** + * A C++ access specifier: `public`, `protected`, or `private`. + */ +class AccessSpecifier extends Specifier { + AccessSpecifier() { + this.hasName("public") or + this.hasName("protected") or + this.hasName("private") + } + + /** + * Gets the visibility of a field with access specifier `this` if it is + * directly inherited with access specifier `baseAccess`. For example: + * + * ``` + * class A { protected int f; }; + * class B : private A {}; + * ``` + * + * In this example, `this` is `protected`, `baseAccess` is `private`, and + * `result` is `private` because the visibility of field `f` in class `B` + * is `private`. + * + * This method encodes the rules of N4140 11.2/1, tabulated here: + * + * ``` + * `this` | `baseAccess` | `result` + * (access in base) | (base class specifier) | (access in derived) + * ---------------------------------------------------------- + * private | private | N/A + * private | protected | N/A + * private | public | N/A + * protected | private | private + * protected | protected | protected + * protected | public | protected + * public | private | private + * public | protected | protected + * public | public | public + * ``` + */ + AccessSpecifier accessInDirectDerived(AccessSpecifier baseAccess) { + this.getName() != "private" and ( + // Alphabetically, "private" < "protected" < "public". This disjunction + // encodes that `result` is the minimum access of `this` and + // `baseAccess`. + baseAccess.getName() < this.getName() and result = baseAccess or + baseAccess.getName() >= this.getName() and result = this + ) + } +} + +/** + * An attribute introduced by GNU's `__attribute__((name))` syntax, + * Microsoft's `__declspec(name)` syntax, Microsoft's `[name]` syntax, the + * C++11 standard `[[name]]` syntax, or the C++11 `alignas` syntax. + */ +class Attribute extends Element, @attribute { + /** + * Gets the name of this attribute. + * + * As examples, this is "noreturn" for `__attribute__((__noreturn__))`, + * "fallthrough" for `[[clang::fallthrough]]`, and "dllimport" for + * `__declspec(dllimport)`. + * + * Note that the name does not include the namespace. For example, the + * name of `[[clang::fallthrough]]` is "fallthrough". + */ + string getName() { attributes(this, _, result, _, _) } + + override Location getLocation() { attributes(this, _, _, _, result) } + + /** Holds if the name of this attribute is `name`. */ + predicate hasName(string name) { name = this.getName() } + + override string toString() { result = this.getName() } + + /** Gets the `i`th argument of the attribute. */ + AttributeArgument getArgument(int i) { + result.getAttribute() = this and result.getIndex() = i + } + + /** Gets an argument of the attribute. */ + AttributeArgument getAnArgument() { + result = getArgument(_) + } +} + +/** + * An attribute introduced by GNU's `__attribute__((name))` syntax, for + * example: `__attribute__((__noreturn__))`. + */ +class GnuAttribute extends Attribute, @gnuattribute { +} + +/** + * An attribute introduced by the C++11 standard `[[name]]` syntax, for + * example: `[[clang::fallthrough]]`. + */ +class StdAttribute extends Attribute, @stdattribute { + /** + * Gets the namespace of this attribute. + * + * As examples, this is "" for `[[carries_dependency]]`, and "clang" for + * `[[clang::fallthrough]]`. + */ + string getNamespace() { attributes(this, _, _, result, _) } + + /** + * Holds if this attribute has the given namespace and name. + */ + predicate hasQualifiedName(string namespace, string name) { + namespace = getNamespace() and hasName(name) + } +} + +/** + * An attribute introduced by Microsoft's `__declspec(name)` syntax, for + * example: `__declspec(dllimport)`. + */ +class Declspec extends Attribute, @declspec { +} + +/** + * An attribute introduced by Microsoft's "[name]" syntax, for example "[SA_Pre(Deref=1,Access=SA_Read)]". + */ +class MicrosoftAttribute extends Attribute, @msattribute { + AttributeArgument getNamedArgument(string name) { + result = getAnArgument() and result.getName() = name + } +} + +/** + * A C++11 `alignas` construct. + * + * Though it doesn't use the attribute syntax, `alignas(...)` is presented + * as an `Attribute` for consistency with the `[[align(...)]]` attribute. + */ +class AlignAs extends Attribute, @alignas { + override string toString() { result = "alignas(...)" } +} + +/** + * A GNU `format` attribute of the form `__attribute__((format(archetype, format-index, first-arg)))` + * that declares a function to accept a `printf` style format string. + */ +class FormatAttribute extends GnuAttribute { + FormatAttribute() { + getName() = "format" + } + + /** + * Gets the archetype of this format attribute, for example + * `"printf"`. + */ + string getArchetype() { + result = getArgument(0).getValueText() + } + + /** + * Gets the index in (1-based) format attribute notation associated + * with the first argument of the function. + */ + private int firstArgumentNumber() { + if exists(MemberFunction f | f.getAnAttribute() = this and not f.isStatic()) then ( + // 1 is `this`, so the first parameter is 2 + result = 2 + ) else ( + result = 1 + ) + } + + /** + * Gets the (0-based) index of the format string, + * according to this attribute. + */ + int getFormatIndex() { + result = getArgument(1).getValueInt() - firstArgumentNumber() + } + + /** + * Gets the (0-based) index of the first format argument (if any), + * according to this attribute. + */ + int getFirstFormatArgIndex() { + exists(int val | + val = getArgument(2).getValueInt() and + result = val - firstArgumentNumber() and + not val = 0 // indicates a `vprintf` style format function with arguments not directly available. + ) + } +} + +/** + * An argument to an `Attribute`. + */ +class AttributeArgument extends Element, @attribute_arg { + /** + * Gets the name of this argument, if it is a named argument. Named + * arguments are a Microsoft feature, so only a `MicrosoftAttribute` can + * have a named argument. + */ + string getName() { + attribute_arg_name(this, result) + } + + /** + * Gets the text for the value of this argument, if its value is + * a string or a number. + */ + string getValueText() { + attribute_arg_value(this, result) + } + + /** + * Gets the value of this argument, if its value is integral. + */ + int getValueInt() { + result = getValueText().toInt() + } + + /** + * Gets the value of this argument, if its value is a type. + */ + Type getValueType() { + attribute_arg_type(this, unresolve(result)) + } + + /** + * Gets the attribute to which this is an argument. + */ + Attribute getAttribute() { + attribute_args(this, _, result, _, _) + } + + /** + * Gets the zero-based index of this argument in the containing + * attribute's argument list. + */ + int getIndex() { + attribute_args(this, _, _, result, _) + } + + override Location getLocation() { + attribute_args(this, _, _, _, result) + } + + override string toString() { + if exists (@attribute_arg_empty self | self = this) + then result = "empty argument" + else exists (string prefix, string tail + | (if exists(getName()) + then prefix = getName() + "=" + else prefix = "") and + (if exists (@attribute_arg_type self | self = this) + then tail = getValueType().getName() + else tail = getValueText()) and + result = prefix + tail) + } +} + +private predicate suppressUnusedThis(Specifier s) { any() } diff --git a/cpp/ql/src/semmle/code/cpp/Struct.qll b/cpp/ql/src/semmle/code/cpp/Struct.qll new file mode 100644 index 000000000000..ff34b35ea498 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/Struct.qll @@ -0,0 +1,42 @@ +import semmle.code.cpp.Type +import semmle.code.cpp.Class + +/** + * A C/C++ structure or union. + */ +class Struct extends Class { + + Struct() { usertypes(this,_,1) or usertypes(this,_,3) } + + override string explain() { result = "struct " + this.getName() } + + override predicate isDeeplyConstBelow() { any() } // No subparts +} + +/** + * A C++ struct that is directly enclosed by a function. + */ +class LocalStruct extends Struct { + LocalStruct() { + isLocal() + } +} + +/** + * A C++ nested struct. See 11.12. + */ +class NestedStruct extends Struct { + NestedStruct() { + this.isMember() + } + + /** Whether this member is private. */ + predicate isPrivate() { this.hasSpecifier("private") } + + /** Whether this member is protected. */ + predicate isProtected() { this.hasSpecifier("protected") } + + /** Whether this member is public. */ + predicate isPublic() { this.hasSpecifier("public") } + +} diff --git a/cpp/ql/src/semmle/code/cpp/TestFile.qll b/cpp/ql/src/semmle/code/cpp/TestFile.qll new file mode 100644 index 000000000000..c546ae04adb8 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/TestFile.qll @@ -0,0 +1,90 @@ +import semmle.code.cpp.File + +/** + * The `gtest/gtest.h` file. + */ +private class GoogleTestHeader extends File { + GoogleTestHeader() { + getBaseName() = "gtest.h" and + getParentContainer().getBaseName() = "gtest" + } +} + +/** + * A test using the Google Test library. + */ +private class GoogleTest extends MacroInvocation { + GoogleTest() { + // invocation of a macro from Google Test. + this.getMacro().getFile() instanceof GoogleTestHeader + } +} + +/** + * The `boost/test` directory. + */ +private class BoostTestFolder extends Folder { + BoostTestFolder() { + getBaseName() = "test" and + getParentContainer().getBaseName() = "boost" + } +} + +/** + * A test using the Boost Test library. + */ +private class BoostTest extends MacroInvocation { + BoostTest() { + // invocation of a macro from Boost Test. + this.getMacro().getFile().getParentContainer+() + instanceof BoostTestFolder + } +} + +/** + * The `cppunit` directory. + */ +private class CppUnitFolder extends Folder { + CppUnitFolder() { + getBaseName() = "cppunit" + } +} + +/** + * A class from the `cppunit` directory. + */ +private class CppUnitClass extends Class { + CppUnitClass() { + getFile().getParentContainer+() instanceof CppUnitFolder and + getNamespace().getParentNamespace*().getName() = "CppUnit" + } +} + +/** + * A test using the CppUnit library. + */ +private class CppUnitTest extends Element { + CppUnitTest() { + ( + // class with a base class from cppunit. + this.(Class).getABaseClass*() instanceof CppUnitClass and + + // class itself is not a part of cppunit. + not this instanceof CppUnitClass + ) or ( + // any member function of a test is also test code + this.(Function).getDeclaringType() instanceof CppUnitTest + ) + } +} + +/** + * A file that contains one or more test cases. + */ +class TestFile extends File { + TestFile() { + exists(GoogleTest test | test.getFile() = this) or + exists(BoostTest test | test.getFile() = this) or + exists(CppUnitTest test | test.getFile() = this) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/Type.qll b/cpp/ql/src/semmle/code/cpp/Type.qll new file mode 100644 index 000000000000..68c7fbfd674e --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/Type.qll @@ -0,0 +1,1216 @@ +import semmle.code.cpp.Element +import semmle.code.cpp.Member +import semmle.code.cpp.Function +private import semmle.code.cpp.internal.Type + +/** + * A C/C++ type. + */ +class Type extends Locatable, @type { + /** + * Gets the name of this type. + */ + string getName() { + none() + } + + /** + * Holds if this type is called `name`. + */ + predicate hasName(string name) { name = this.getName() } + + /** + * Holds if this declaration has a specifier called `name`, recursively looking + * through `typedef` and `decltype`. For example, in the context of + * `typedef const int *restrict t`, the type `volatile t` has specifiers + * `volatile` and `restrict` but not `const` since the `const` is attached to + * the type being pointed to rather than the pointer itself. + */ + // This predicate should not be overridden, but it cannot be declared `final` + // because there is a similarly-named predicate in Declaration, and UserType + // inherits from both Type and Declaration and must override it to resolve + // the ambiguity. + predicate hasSpecifier(string name) { + this.getASpecifier().hasName(name) + } + + /** + * Gets a specifier of this type, recursively looking through `typedef` and + * `decltype`. For example, in the context of `typedef const int *restrict + * t`, the type `volatile t` has specifiers `volatile` and `restrict` but not + * `const` since the `const` is attached to the type being pointed to rather + * than the pointer itself. + */ + // This predicate should not be overridden, but it cannot be declared `final` + // because there is a similarly-named predicate in Declaration, and UserType + // inherits from both Type and Declaration and must override it to resolve + // the ambiguity. + Specifier getASpecifier() { + typespecifiers(this,result) + or + result = this.internal_getAnAdditionalSpecifier() + } + + /** + * Gets an attribute of this type. + */ + Attribute getAnAttribute() { typeattributes(this,result) } + + /** + * Internal -- should be `protected` when QL supports such a flag. Subtypes + * override this to recursively get specifiers that are not attached directly + * to this `@type` in the database but arise through type aliases such as + * `typedef` and `decltype`. + */ + Specifier internal_getAnAdditionalSpecifier() { none() } + + /** + * Holds if this type is const. + */ + predicate isConst() { this.hasSpecifier("const") } + + /** + * Holds if this type is volatile. + */ + predicate isVolatile() { this.hasSpecifier("volatile") } + + /** + * Holds if this type refers to type `t` (by default, + * a type always refers to itself). + */ + predicate refersTo(Type t) { refersToDirectly*(t) } + + /** + * Holds if this type refers to type `t` directly. + */ + predicate refersToDirectly(Type t) { none() } + + /** + * Gets this type after typedefs have been resolved. + * + * The result of this predicate will be the type itself, except in the case of a TypedefType or a Decltype, + * in which case the result will be type which results from (possibly recursively) resolving typedefs. + */ + Type getUnderlyingType() { result = this } + + /** + * Gets this type after specifiers have been deeply stripped and typedefs have been resolved. + * + * For example, starting with `const i64* const` in the context of `typedef long long i64;`, this predicate will return `long long*`. + */ + Type getUnspecifiedType() { unspecifiedtype(this, unresolve(result)) } + + /** + * Gets this type after any top-level specifiers and typedefs have been stripped. + * + * For example, starting with `const i64* const`, this predicate will return `const i64*`. + */ + Type stripTopLevelSpecifiers() { result = this } + + /** + * Gets the size of this type in bytes. + */ + int getSize() { + builtintypes(this, _, _, result, _, _) + or pointerishsize(this, result, _) + or usertypesize(this, result, _) + } + + /** + * Gets the alignment of this type in bytes. + */ + int getAlignment() { + builtintypes(this, _, _, _, _, result) + or pointerishsize(this, _, result) + or usertypesize(this, _, result) + } + + /** + * Gets the pointer indirection level of this type. + */ + int getPointerIndirectionLevel() { + result = 0 + } + + /** + * Gets a detailed string representation explaining the AST of this type + * (with all specifiers and nested constructs such as pointers). This is + * intended to help debug queries and is a very expensive operation; not + * to be used in production queries. + * + * An example output is "const {pointer to {const {char}}}". + */ + string explain() { result = "type" } // Concrete base impl to allow filters on Type + + /** + * Holds if this type is constant and only contains constant types. + * For instance, a `char *const` is a constant type, but not deeply constant, + * because while the pointer can't be modified the character can. The type + * `const char *const*` is a deeply constant type though - both the pointer + * and what it points to are immutable. + */ + predicate isDeeplyConst() { this.isConst() and this.isDeeplyConstBelow() } + + /** + * Holds if this type is constant and only contains constant types, excluding + * the type itself. It is implied by Type.isDeeplyConst() and is just used to + * implement that predicate. + * For example, `const char *const` is deeply constant and deeply constant below, + * but `const char *` is only deeply constant below (the pointer can be changed, + * but not the underlying char). `char *const` is neither (it is just `const`). + */ + predicate isDeeplyConstBelow() { none() } // Concrete base impl to allow filters on Type + + /** + * Gets as many places as possible where this type is used by name in the source after macros have been replaced + * (in particular, therefore, this will find type name uses caused by macros). Note that all type name uses within + * instantiations are currently excluded - this is too draconian in the absence of indexing prototype instantiations + * of functions, and is likely to improve in the future. At present, the method takes the conservative approach of + * giving valid type name uses, but not necessarily *all* type name uses. + */ + Element getATypeNameUse() { + // An explicit cast to a type referring to T uses T. We exclude casts within instantiations, + // since they do not appear directly in the source. + exists(Cast c | + not(c.isImplicit()) + and c.getType().refersTo(this) + and result = c + and not c.getEnclosingFunction().isConstructedFrom(_) + ) + + // A class derivation from a type referring to T uses T. We exclude class derivations within + // instantiations, since they do not appear directly in the source. + or exists(ClassDerivation cd | + cd.getBaseType().refersTo(this) + and result = cd + and not cd.getDerivedClass() instanceof ClassTemplateInstantiation + ) + + // A new, new array, or placement new expression with a type that refers to T uses T. + // We exclude news within instantiations, since they do not appear directly in the source. + or exists(Expr e | + ( + e instanceof NewArrayExpr + or e instanceof NewExpr + ) + and e.getType().refersTo(this) + and result = e + and not e.getEnclosingFunction().isConstructedFrom(_) + ) + + // The declaration of a function that returns a type referring to T uses T. We exclude + // declarations of function template instantiations, since their return types do not + // appear directly in the source. We also exclude constructors and destructors, since + // they are indexed with a dummy return type of void that does not appear in the source. + or exists(FunctionDeclarationEntry fde, Type t | + ( + if exists(fde.getTypedefType()) then + t = fde.getTypedefType() + else + t = fde.getType() + ) + and t.refersTo(this) + and result = fde + and not fde.getDeclaration().isConstructedFrom(_) + and not(fde.getDeclaration() instanceof Constructor) + and not(fde.getDeclaration() instanceof Destructor) + ) + + // A function call that provides an explicit template argument that refers to T uses T. + // We exclude calls within instantiations, since they do not appear directly in the source. + or exists(FunctionCall c | + c.getAnExplicitTemplateArgument().refersTo(this) + and result = c + and not c.getEnclosingFunction().isConstructedFrom(_) + ) + + // Qualifying an expression with a type that refers to T uses T. We exclude qualifiers + // within instantiations, since they do not appear directly in the source. + or exists(NameQualifier nq | + nq.getQualifyingElement().(Type).refersTo(this) + and result = nq + and not nq.getExpr().getEnclosingFunction().isConstructedFrom(_) + ) + + // Calculating the size of a type that refers to T uses T. We exclude sizeofs within + // instantiations, since they do not appear directly in the source. + or exists(SizeofTypeOperator soto | + soto.getTypeOperand().refersTo(this) + and result = soto + and not soto.getEnclosingFunction().isConstructedFrom(_) + ) + + // A typedef of a type that refers to T uses T. + or exists(TypeDeclarationEntry tde | + tde.getDeclaration().(TypedefType).getBaseType().refersTo(this) + and result = tde + ) + + // Using something declared within a type that refers to T uses T. + or exists(UsingDeclarationEntry ude | + ude.getDeclaration().getDeclaringType().refersTo(this) + and result = ude + ) + + // The declaration of a variable with a type that refers to T uses T. We exclude declarations within + // instantiations, since those do not appear directly in the source. + or exists(VariableDeclarationEntry vde | + vde.getType().refersTo(this) + and result = vde + and not exists(LocalScopeVariable sv | sv = vde.getDeclaration() and sv.getFunction().isConstructedFrom(_)) + and not exists(MemberVariable mv | mv = vde.getDeclaration() and mv.getDeclaringType() instanceof ClassTemplateInstantiation) + ) + } + + /** + * Holds if this type involves a reference. + */ + predicate involvesReference() { + none() + } + + /** + * Holds if this type involves a template parameter. + */ + predicate involvesTemplateParameter() { + none() + } + + /** + * Gets this type with any typedefs resolved. For example, given + * `typedef C T`, this would resolve `const T&` to `const C&`. + * Note that this will only work if the resolved type actually appears + * on its own elsewhere in the program. + */ + Type resolveTypedefs() { + result = this + } + + /** + * Gets the type stripped of pointers, references and cv-qualifiers, and resolving typedefs. + * For example, given `typedef const C& T`, `stripType` returns `C`. + */ + Type stripType() { + result = this + } + + override Location getLocation() { + suppressUnusedThis(this) and + result instanceof UnknownDefaultLocation + } +} + +/** + * A C/C++ built-in primitive type (int, float, void, and so on). See 4.1.1. + */ +class BuiltInType extends Type, @builtintype { + override string toString() { result = this.getName() } + + override string getName() { builtintypes(this,result,_,_,_,_) } + + override string explain() { result = this.getName() } + + override predicate isDeeplyConstBelow() { any() } // No subparts +} + +/** + * An erroneous type. + */ +class ErroneousType extends BuiltInType { + + ErroneousType() { builtintypes(this,_,1,_,_,_) } + +} + +/** + * The unknown type. + */ +class UnknownType extends BuiltInType { + + UnknownType() { builtintypes(this,_,2,_,_,_) } + +} + +private predicate isArithmeticType(@builtintype type, int kind) { + builtintypes(type, _, kind, _, _, _) and + (kind >= 4) and + (kind != 34) // Exclude decltype(nullptr) +} + +/** + * The C/C++ arithmetic types. See 4.1.1. + */ +class ArithmeticType extends BuiltInType { + ArithmeticType() { + isArithmeticType(this, _) + } +} + +/** + * A C/C++ integral or enum type. + * The definition of "integral type" in the C++ Standard excludes enum types, + * but because an enum type holds a value of its underlying integral type, + * it is often useful to have a common category that includes both integral + * and enum types. + */ +class IntegralOrEnumType extends Type { + IntegralOrEnumType() { + // Integral type + exists(int kind | + isArithmeticType(this, kind) and + (kind < 24 or kind = 33 or (35 <= kind and kind <= 37) or + kind = 43 or kind = 44) + ) or + // Enum type + ( + usertypes(this, _, 4) or + usertypes(this, _, 13) + ) + } +} + +/** + * The C/C++ integral types. See 4.1.1. + */ +class IntegralType extends ArithmeticType, IntegralOrEnumType { + /** Holds if this integral type is signed. */ + predicate isSigned() { + builtintypes(this,_,_,_,-1,_) + } + + /** Holds if this integral type is unsigned. */ + predicate isUnsigned() { + builtintypes(this,_,_,_,1,_) + } + + /** Holds if this integral type is explicitly signed. */ + predicate isExplicitlySigned() { + builtintypes(this,_,7,_,_,_) or builtintypes(this,_,10,_,_,_) or builtintypes(this,_,13,_,_,_) or + builtintypes(this,_,16,_,_,_) or builtintypes(this,_,19,_,_,_) or + builtintypes(this,_,37,_,_,_) + } + + /** Holds if this integral type is explicitly unsigned. */ + predicate isExplicitlyUnsigned() { + builtintypes(this,_,6,_,_,_) or builtintypes(this,_,9,_,_,_) or builtintypes(this,_,12,_,_,_) or + builtintypes(this,_,15,_,_,_) or builtintypes(this,_,18,_,_,_) or + builtintypes(this,_,36,_,_,_) + } + + /** Holds if this integral type is implicitly signed. */ + predicate isImplicitlySigned() { + builtintypes(this,_,5,_,-1,_) or builtintypes(this,_,8,_,-1,_) or builtintypes(this,_,11,_,-1,_) or + builtintypes(this,_,14,_,-1,_) or builtintypes(this,_,17,_,-1,_) or + builtintypes(this,_,35,_,-1,_) + } + + /** + * Gets the unsigned type corresponding to this integral type. For + * example on a `short`, this would give the type `unsigned short`. + */ + IntegralType getUnsigned() { + (builtintypes(this,_, 5,_,_,_) or builtintypes(this,_, 6,_,_,_) or builtintypes(this,_, 7,_,_,_)) and builtintypes(result,_, 6,_,_,_) + or (builtintypes(this,_, 8,_,_,_) or builtintypes(this,_, 9,_,_,_) or builtintypes(this,_,10,_,_,_)) and builtintypes(result,_, 9,_,_,_) + or (builtintypes(this,_,11,_,_,_) or builtintypes(this,_,12,_,_,_) or builtintypes(this,_,13,_,_,_)) and builtintypes(result,_,12,_,_,_) + or (builtintypes(this,_,14,_,_,_) or builtintypes(this,_,15,_,_,_) or builtintypes(this,_,16,_,_,_)) and builtintypes(result,_,15,_,_,_) + or (builtintypes(this,_,17,_,_,_) or builtintypes(this,_,18,_,_,_) or builtintypes(this,_,19,_,_,_)) and builtintypes(result,_,18,_,_,_) + or (builtintypes(this,_,35,_,_,_) or builtintypes(this,_,36,_,_,_) or builtintypes(this,_,37,_,_,_)) and builtintypes(result,_,36,_,_,_) + } +} + +/** + * The C/C++ boolean type. See 4.2. + */ +class BoolType extends IntegralType { + + BoolType() { builtintypes(this,_,4,_,_,_) } + +} + +/** + * The C/C++ character types. See 4.3. + */ +abstract class CharType extends IntegralType { } + +/** + * The C/C++ char type (which is different to signed char and unsigned char). + */ +class PlainCharType extends CharType { + PlainCharType() { + builtintypes(this,_,5,_,_,_) + } +} + +/** + * The C/C++ unsigned char type (which is different to plain char, even when chars are unsigned by default). + */ +class UnsignedCharType extends CharType { + UnsignedCharType() { + builtintypes(this,_,6,_,_,_) + } +} + +/** + * The C/C++ signed char type (which is different to plain char, even when chars are signed by default). + */ +class SignedCharType extends CharType { + SignedCharType() { + builtintypes(this,_,7,_,_,_) + } +} + +/** + * The C/C++ short types. See 4.3. + */ +class ShortType extends IntegralType { + + ShortType() { + builtintypes(this,_,8,_,_,_) or builtintypes(this,_,9,_,_,_) or builtintypes(this,_,10,_,_,_) + } + +} + +/** + * The C/C++ integer types. See 4.4. + */ +class IntType extends IntegralType { + + IntType() { + builtintypes(this,_,11,_,_,_) or builtintypes(this,_,12,_,_,_) or builtintypes(this,_,13,_,_,_) + } + +} + +/** + * The C/C++ long types. See 4.4. + */ +class LongType extends IntegralType { + + LongType() { + builtintypes(this,_,14,_,_,_) or builtintypes(this,_,15,_,_,_) or builtintypes(this,_,16,_,_,_) + } + +} + +/** + * The C/C++ long long types. See 4.4. + */ +class LongLongType extends IntegralType { + + LongLongType() { + builtintypes(this,_,17,_,_,_) or builtintypes(this,_,18,_,_,_) or builtintypes(this,_,19,_,_,_) + } + +} + +/** + * The GNU C __int128 types. + */ +class Int128Type extends IntegralType { + + Int128Type() { + builtintypes(this,_,35,_,_,_) or builtintypes(this,_,36,_,_,_) or builtintypes(this,_,37,_,_,_) + } + +} + +/** + * The C/C++ floating point types. See 4.5. + */ +class FloatingPointType extends ArithmeticType { + + FloatingPointType() { + exists(int kind | builtintypes(this,_,kind,_,_,_) and ((kind >= 24 and kind <= 32) or (kind = 38))) + } + +} + +/** + * The C/C++ float type. + */ +class FloatType extends FloatingPointType { + + FloatType() { builtintypes(this,_,24,_,_,_) } + +} + +/** + * The C/C++ double type. + */ +class DoubleType extends FloatingPointType { + + DoubleType() { builtintypes(this,_,25,_,_,_) } + +} + +/** + * The C/C++ long double type. + */ +class LongDoubleType extends FloatingPointType { + + LongDoubleType() { builtintypes(this,_,26,_,_,_) } + +} + +/** + * The GNU C __float128 type. + */ +class Float128Type extends FloatingPointType { + + Float128Type() { builtintypes(this,_,38,_,_,_) } + +} + +/** + * The GNU C _Decimal32 type. + */ +class Decimal32Type extends FloatingPointType { + + Decimal32Type() { builtintypes(this,_,40,_,_,_) } + +} + +/** + * The GNU C _Decimal64 type. + */ +class Decimal64Type extends FloatingPointType { + + Decimal64Type() { builtintypes(this,_,41,_,_,_) } + +} + +/** + * The GNU C _Decimal128 type. + */ +class Decimal128Type extends FloatingPointType { + + Decimal128Type() { builtintypes(this,_,42,_,_,_) } + +} + +/** + * The C/C++ void type. See 4.7. + */ +class VoidType extends BuiltInType { + + VoidType() { builtintypes(this,_,3,_,_,_) } + +} + +/** + * The C/C++ wide character type. + */ +class WideCharType extends IntegralType { + + WideCharType() { builtintypes(this,_,33,_,_,_) } + +} + +/** + * The C/C++ `char16_t` type. + */ +class Char16Type extends IntegralType { + + Char16Type() { builtintypes(this,_,43,_,_,_) } + +} + +/** + * The C/C++ `char32_t` type. + */ +class Char32Type extends IntegralType { + + Char32Type() { builtintypes(this,_,44,_,_,_) } + +} + +/** + * The type of the C++11 nullptr constant. + * + * Note that this is not `nullptr_t`, as `nullptr_t` is defined as: + * ``` + * typedef decltype(nullptr) nullptr_t; + * ``` + * Instead, this is the unspeakable type given by `decltype(nullptr)`. + */ +class NullPointerType extends BuiltInType { + NullPointerType() { builtintypes(this,_,34,_,_,_) } +} + +/** + * A C/C++ derived type. + * + * These are pointer and reference types, array and vector types, and const and volatile types. + * In all cases, the type is formed from a single base type. + */ +class DerivedType extends Type, @derivedtype { + override string toString() { result = this.getName() } + + override string getName() { derivedtypes(this,result,_,_) } + + /** + * Gets the base type of this derived type. + * + * This predicate strips off one level of decoration from a type. For example, it returns `char*` for the PointerType `char**`, + * `const int` for the ReferenceType `const int&`, and `long` for the SpecifiedType `volatile long`. + */ + Type getBaseType() { derivedtypes(this,_,_,unresolve(result)) } + + override predicate refersToDirectly(Type t) { t = this.getBaseType() } + + override predicate involvesReference() { + getBaseType().involvesReference() + } + + override predicate involvesTemplateParameter() { + getBaseType().involvesTemplateParameter() + } + + override Type stripType() { + result = getBaseType().stripType() + } + + predicate isAutoReleasing() { + this.hasSpecifier("__autoreleasing") or + this.(PointerType).getBaseType().hasSpecifier("__autoreleasing") + } + + predicate isStrong() { + this.hasSpecifier("__strong") or + this.(PointerType).getBaseType().hasSpecifier("__strong") + } + + predicate isUnsafeRetained() { + this.hasSpecifier("__unsafe_unretained") or + this.(PointerType).getBaseType().hasSpecifier("__unsafe_unretained") + } + + predicate isWeak() { + this.hasSpecifier("__weak") or + this.(PointerType).getBaseType().hasSpecifier("__weak") + } +} + +/** + * An instance of the C++11 decltype operator. + */ +class Decltype extends Type, @decltype { + + /** + * The expression whose type is being obtained by this decltype. + */ + Expr getExpr() { + decltypes(this, result, _, _) + } + + /** + * The type immediately yielded by this decltype. + */ + Type getBaseType() { + decltypes(this, _, unresolve(result), _) + } + + /** + * Whether an extra pair of parentheses around the expression would change the semantics of this decltype. + * + * The following example shows the effect of an extra pair of parentheses: + * struct A { double x; }; + * const A* a = new A(); + * decltype( a->x ); // type is double + * decltype((a->x)); // type is const double& + * Consult the C++11 standard for more details. + */ + predicate parenthesesWouldChangeMeaning() { + decltypes(this, _, _, true) + } + + override Type getUnderlyingType() { + result = getBaseType().getUnderlyingType() + } + + override Type stripTopLevelSpecifiers() { + result = getBaseType().stripTopLevelSpecifiers() + } + + override Type stripType() { + result = getBaseType().stripType() + } + + override Type resolveTypedefs() { + result = getBaseType().resolveTypedefs() + } + + override Location getLocation() { + result = getExpr().getLocation() + } + + override string toString() { + result = "decltype(...)" + } + + override string getName() { + none() + } + + override int getSize() { + result = getBaseType().getSize() + } + + override int getAlignment() { + result = getBaseType().getAlignment() + } + + override int getPointerIndirectionLevel() { + result = getBaseType().getPointerIndirectionLevel() + } + + override string explain() { + result = "decltype resulting in {" + this.getBaseType().explain() + "}" + } + + override predicate involvesReference() { + getBaseType().involvesReference() + } + + override predicate involvesTemplateParameter() { + getBaseType().involvesTemplateParameter() + } + + override predicate isDeeplyConst() { + this.getBaseType().isDeeplyConst() + } + + override predicate isDeeplyConstBelow() { + this.getBaseType().isDeeplyConstBelow() + } + + override Specifier internal_getAnAdditionalSpecifier() { + result = this.getBaseType().getASpecifier() + } +} + +/** + * A C/C++ pointer type. See 4.9.1. + */ +class PointerType extends DerivedType { + + PointerType() { derivedtypes(this,_,1,_) } + + override int getPointerIndirectionLevel() { + result = 1 + this.getBaseType().getPointerIndirectionLevel() + } + + override string explain() { result = "pointer to {" + this.getBaseType().explain() + "}" } + + override predicate isDeeplyConstBelow() { this.getBaseType().isDeeplyConst() } + + override Type resolveTypedefs() { + result.(PointerType).getBaseType() = getBaseType().resolveTypedefs() + } +} + +/** + * A C++ reference type. See 4.9.1. + * + * For C++11 code bases, this includes both lvalue references (&) and rvalue references (&&). + * To distinguish between them, use the LValueReferenceType and RValueReferenceType classes. + */ +class ReferenceType extends DerivedType { + + ReferenceType() { derivedtypes(this,_,2,_) or derivedtypes(this,_,8,_) } + + override int getPointerIndirectionLevel() { + result = getBaseType().getPointerIndirectionLevel() + } + + override string explain() { result = "reference to {" + this.getBaseType().explain() + "}" } + + override predicate isDeeplyConst() { this.getBaseType().isDeeplyConst() } // No such thing as a const reference type + + override predicate isDeeplyConstBelow() { this.getBaseType().isDeeplyConst() } + + override predicate involvesReference() { + any() + } + + override Type resolveTypedefs() { + result.(ReferenceType).getBaseType() = getBaseType().resolveTypedefs() + } +} + +/** + * A C++11 lvalue reference type (e.g. int&). + */ +class LValueReferenceType extends ReferenceType { + LValueReferenceType() { derivedtypes(this,_,2,_) } +} + +/** + * A C++11 rvalue reference type (e.g. int&&). + */ +class RValueReferenceType extends ReferenceType { + RValueReferenceType() { derivedtypes(this,_,8,_) } + + override string explain() { result = "rvalue " + super.explain() } +} + +/** + * A type with specifiers. + */ +class SpecifiedType extends DerivedType { + + SpecifiedType() { derivedtypes(this,_,3,_) } + + override int getSize() { result = this.getBaseType().getSize() } + + override int getAlignment() { result = this.getBaseType().getAlignment() } + + override int getPointerIndirectionLevel() { + result = this.getBaseType().getPointerIndirectionLevel() + } + + /** all the specifiers of this type as a string in a fixed order (the order + only depends on the specifiers, not on the source program). This is intended + for debugging queries only and is an expensive operation. */ + string getSpecifierString() { + internalSpecString(this, result, 1) + } + + override string explain() { result = this.getSpecifierString() + "{" + this.getBaseType().explain() + "}" } + + override predicate isDeeplyConst() { this.getASpecifier().getName() = "const" and this.getBaseType().isDeeplyConstBelow() } + + override predicate isDeeplyConstBelow() { this.getBaseType().isDeeplyConstBelow() } + + override Specifier internal_getAnAdditionalSpecifier() { + result = this.getBaseType().getASpecifier() + } + + override Type resolveTypedefs() { + result.(SpecifiedType).getBaseType() = getBaseType().resolveTypedefs() + and result.getASpecifier() = getASpecifier() + } + + override Type stripTopLevelSpecifiers() { + result = getBaseType().stripTopLevelSpecifiers() + } +} + +/** + * A C/C++ array type. See 4.9.1. + */ +class ArrayType extends DerivedType { + + ArrayType() { derivedtypes(this,_,4,_) } + + predicate hasArraySize() { arraysizes(this,_,_,_) } + int getArraySize() { arraysizes(this,result,_,_) } + + int getByteSize() { arraysizes(this,_,result,_) } + + override int getAlignment() { arraysizes(this, _, _, result) } + + /** + * Gets the size of this array (only valid for arrays declared to be of a constant + * size, will fail for all others). + */ + override int getSize() { + result = this.getByteSize() + } + + override string explain() { + if exists(this.getArraySize()) then + result = "array of " + this.getArraySize().toString() + " {" + this.getBaseType().explain() + "}" + else + result = "array of {" + this.getBaseType().explain() + "}" + } + + override predicate isDeeplyConst() { this.getBaseType().isDeeplyConst() } // No such thing as a const array type + + override predicate isDeeplyConstBelow() { this.getBaseType().isDeeplyConst() } +} + +/** + * A GNU/Clang vector type. + * + * In both Clang and GNU compilers, vector types can be introduced using the + * __attribute__((vector_size(byte_size))) syntax. The Clang compiler also + * allows vector types to be introduced using the ext_vector_type, + * neon_vector_type, and neon_polyvector_type attributes (all of which take + * an element type rather than a byte size). + */ +class GNUVectorType extends DerivedType { + + GNUVectorType() { derivedtypes(this,_,5,_) } + + /** + * Get the number of elements in this vector type. + * + * For vector types declared using Clang's ext_vector_type, neon_vector_type, + * or neon_polyvector_type attribute, this is the value which appears within + * the attribute. For vector types declared using the vector_size attribute, + * the number of elements is the value in the attribute divided by the size + * of a single element. + */ + int getNumElements() { arraysizes(this,result,_,_) } + + /** + * Gets the size, in bytes, of this vector type. + * + * For vector types declared using the vector_size attribute, this is the + * value which appears within the attribute. For vector types declared using + * Clang's ext_vector_type, neon_vector_type, or neon_polyvector_type + * attribute, the byte size is the value in the attribute multiplied by the + * byte size of a single element. + */ + override int getSize() { arraysizes(this,_,result,_) } + + override int getAlignment() { arraysizes(this, _, _, result) } + + override string explain() { result = "GNU " + getNumElements() + " element vector of {" + this.getBaseType().explain() + "}" } + + override predicate isDeeplyConstBelow() { this.getBaseType().isDeeplyConst() } + +} + +/** + * A C/C++ pointer to function. See 7.7. + */ +class FunctionPointerType extends FunctionPointerIshType { + FunctionPointerType() { + derivedtypes(this,_,6,_) + } + + override int getPointerIndirectionLevel() { + result = 1 + } + + override string explain() { result = "pointer to {" + this.getBaseType().(RoutineType).explain() + "}" } +} + +/** + * A C/C++ reference to function. + */ +class FunctionReferenceType extends FunctionPointerIshType { + FunctionReferenceType() { + derivedtypes(this,_,7,_) + } + + override int getPointerIndirectionLevel() { + result = getBaseType().getPointerIndirectionLevel() + } + + override string explain() { result = "reference to {" + this.getBaseType().(RoutineType).explain() + "}" } +} + +/** + * A block type, for example int(^)(char, float). + * + * Block types (along with blocks themselves) are a language extension + * supported by Clang, and by Apple's branch of GCC. + */ +class BlockType extends FunctionPointerIshType { + BlockType() { + derivedtypes(this,_,10,_) + } + + override int getPointerIndirectionLevel() { + result = 0 + } + + override string explain() { result = "block of {" + this.getBaseType().(RoutineType).explain() + "}" } +} + +/** + * A C/C++ pointer to function, or a block. + */ +class FunctionPointerIshType extends DerivedType { + FunctionPointerIshType() { + derivedtypes(this,_,6, _) or + derivedtypes(this,_,7, _) or + derivedtypes(this,_,10,_) + } + + /** the return type of this function pointer type */ + Type getReturnType() { + exists(RoutineType t | derivedtypes(this,_,_,t) and result = t.getReturnType()) + } + + /** the type of the ith argument of this function pointer type */ + Type getParameterType(int i) { + exists(RoutineType t | derivedtypes(this,_,_,t) and result = t.getParameterType(i)) + } + + /** the type of an argument of this function pointer type */ + Type getAParameterType() { + exists(RoutineType t | derivedtypes(this,_,_,t) and result = t.getAParameterType()) + } + + /** the number of arguments of this function pointer type */ + int getNumberOfParameters() { + result = count(int i | exists(this.getParameterType(i))) + } + + override predicate involvesTemplateParameter() { + getReturnType().involvesTemplateParameter() + or getAParameterType().involvesTemplateParameter() + } + + override predicate isDeeplyConstBelow() { this.getBaseType().isDeeplyConst() } +} + +/** + * A C++ pointer to member. See 15.5. + */ +class PointerToMemberType extends Type, @ptrtomember { + /** a printable representation of this named element */ + override string toString() { result = this.getName() } + + /** the name of this type */ + override string getName() { result = "..:: *" } + + /** the base type of this pointer to member type */ + Type getBaseType() { ptrtomembers(this,unresolve(result),_) } + + /** the class referred by this pointer to member type */ + Type getClass() { ptrtomembers(this,_,unresolve(result)) } + + override predicate refersToDirectly(Type t) { + t = this.getBaseType() or + t = this.getClass() + } + + override int getPointerIndirectionLevel() { + result = 1 + this.getBaseType().getPointerIndirectionLevel() + } + + override string explain() { result = "pointer to member of " + this.getClass().toString() + " with type {" + this.getBaseType().explain() + "}" } + + override predicate involvesTemplateParameter() { + getBaseType().involvesTemplateParameter() + } + + override predicate isDeeplyConstBelow() { this.getBaseType().isDeeplyConst() } +} + +/** + * A C/C++ routine type. This is what results from stripping away the pointer from a function pointer type. + */ +class RoutineType extends Type, @routinetype { + /** a printable representation of this named element */ + override string toString() { result = this.getName() } + + override string getName() { result = "..()(..)" } + + Type getParameterType(int n) { routinetypeargs(this,n,unresolve(result)) } + + Type getAParameterType() { routinetypeargs(this,_,unresolve(result)) } + + Type getReturnType() { routinetypes(this, unresolve(result)) } + + override string explain() { + result = "function returning {" + this.getReturnType().explain() + + "} with arguments (" + this.explainParameters(0) + ")" + } + + /** + * Gets a string with the `explain()` values for the parameters of + * this function, for instance "int,int". + * + * The integer argument is the index of the first parameter to explain. + */ + private string explainParameters(int i) { + (i = 0 and result = "" and not exists(this.getAParameterType())) + or + ( + exists(this.getParameterType(i)) and + if i < max(int j | exists(this.getParameterType(j))) then + // Not the last one + result = this.getParameterType(i).explain() + "," + this.explainParameters(i+1) + else + // Last parameter + result = this.getParameterType(i).explain() + ) + } + + override predicate refersToDirectly(Type t) { + t = this.getReturnType() or + t = this.getAParameterType() + } + + override predicate isDeeplyConst() { none() } // Current limitation: no such thing as a const routine type + + override predicate isDeeplyConstBelow() { none() } // Current limitation: no such thing as a const routine type + + override predicate involvesTemplateParameter() { + getReturnType().involvesTemplateParameter() + or getAParameterType().involvesTemplateParameter() + } +} + +/** + * A C++ typename template parameter. + */ +class TemplateParameter extends UserType +{ + TemplateParameter() { usertypes(this, _, 7) or usertypes(this, _, 8) } + + override string getName() { usertypes(this, result, _) } + + override predicate involvesTemplateParameter() { + any() + } +} + +/** A C++ template template parameter, e.g. template <template <typename,typename> class T>. */ +class TemplateTemplateParameter extends TemplateParameter +{ + TemplateTemplateParameter() { + usertypes(this, _, 8) + } +} + +/** + * A type representing the use of the C++11 auto keyword. + */ +class AutoType extends TemplateParameter +{ + AutoType() { usertypes(this, "auto", 7) } + + override Location getLocation() { + suppressUnusedThis(this) and + result instanceof UnknownDefaultLocation + } +} + +// +// Internal implementation predicates +// + +predicate allSpecifiers(int i, string s) { + s = rank[i](string t | specifiers(_, t) | t) +} + +predicate internalSpecString(Type t, string res, int i) { + (if allSpecifiers(i, t.getASpecifier().getName()) + then exists(string spec, string rest + | allSpecifiers(i, spec) and res = spec + " " + rest + and internalSpecString(t, rest, i+1)) + else (allSpecifiers(i, _) and internalSpecString(t, res, i+1))) + or (i = count(Specifier s) + 1 and res = "") +} + +private predicate suppressUnusedThis(Type t) { any() } + +/** A source code location referring to a type */ +class TypeMention extends Locatable, @type_mention { + override string toString() {result = "mention of " + getMentionedType()} + + /** + * Gets the type being referenced by this type mention. + */ + Type getMentionedType() { type_mentions(this, result, _, _) } + + override Location getLocation() { type_mentions(this, _, result, _)} +} + diff --git a/cpp/ql/src/semmle/code/cpp/TypedefType.qll b/cpp/ql/src/semmle/code/cpp/TypedefType.qll new file mode 100644 index 000000000000..08bf9ea22611 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/TypedefType.qll @@ -0,0 +1,87 @@ +import semmle.code.cpp.Type +private import semmle.code.cpp.internal.Type + +/** + * A C/C++ typedef type. See 4.9.1. + */ +class TypedefType extends UserType { + + TypedefType() { usertypes(this,_,5) } + + /** + * Gets the base type of this typedef type. + */ + Type getBaseType() { typedefbase(this,unresolve(result)) } + + override Type getUnderlyingType() { result = this.getBaseType().getUnderlyingType() } + + override Type stripTopLevelSpecifiers() { + result = getBaseType().stripTopLevelSpecifiers() + } + + override int getSize() { result = this.getBaseType().getSize() } + + override int getAlignment() { result = this.getBaseType().getAlignment() } + + override int getPointerIndirectionLevel() { + result = this.getBaseType().getPointerIndirectionLevel() + } + + override string explain() { result = "typedef {" + this.getBaseType().explain() + "} as \"" + this.getName() + "\"" } + + override predicate isDeeplyConst() { this.getBaseType().isDeeplyConst() } // Just an alias + + override predicate isDeeplyConstBelow() { this.getBaseType().isDeeplyConstBelow() } // Just an alias + + override Specifier internal_getAnAdditionalSpecifier() { + result = this.getBaseType().getASpecifier() + } + + override predicate involvesReference() { + getBaseType().involvesReference() + } + + override Type resolveTypedefs() { + result = getBaseType().resolveTypedefs() + } + + override Type stripType() { + result = getBaseType().stripType() + } +} + +/** + * A C++ typedef type that is directly enclosed by a function. + */ +class LocalTypedefType extends TypedefType { + LocalTypedefType() { + isLocal() + } +} + +/** + * A C++ typedef type that is directly enclosed by a class, struct or union. + */ +class NestedTypedefType extends TypedefType { + NestedTypedefType() { + this.isMember() + } + + /** + * DEPRECATED + * Holds if this member is private. + */ + deprecated predicate isPrivate() { this.hasSpecifier("private") } + + /** + * DEPRECATED + * Holds if this member is protected. + */ + deprecated predicate isProtected() { this.hasSpecifier("protected") } + + /** + * DEPRECATED + * Holds if this member is public. + */ + deprecated predicate isPublic() { this.hasSpecifier("public") } +} diff --git a/cpp/ql/src/semmle/code/cpp/Union.qll b/cpp/ql/src/semmle/code/cpp/Union.qll new file mode 100644 index 000000000000..b0e609b4cc68 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/Union.qll @@ -0,0 +1,43 @@ +import semmle.code.cpp.Type +import semmle.code.cpp.Struct + +/** + * A C/C++ union. See C.8.2. + */ +class Union extends Struct { + + Union() { usertypes(this,_,3) } + + override string explain() { result = "union " + this.getName() } + + override predicate isDeeplyConstBelow() { any() } // No subparts + +} + +/** + * A C++ union that is directly enclosed by a function. + */ +class LocalUnion extends Union { + LocalUnion() { + isLocal() + } +} + +/** + * A C++ nested union. + */ +class NestedUnion extends Union { + NestedUnion() { + this.isMember() + } + + /** Whether this member is private. */ + predicate isPrivate() { this.hasSpecifier("private") } + + /** Whether this member is protected. */ + predicate isProtected() { this.hasSpecifier("protected") } + + /** Whether this member is public. */ + predicate isPublic() { this.hasSpecifier("public") } + +} diff --git a/cpp/ql/src/semmle/code/cpp/UserType.qll b/cpp/ql/src/semmle/code/cpp/UserType.qll new file mode 100644 index 000000000000..91e76b14d7cc --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/UserType.qll @@ -0,0 +1,114 @@ +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.Type + +/** + * A C/C++ user-defined type. Examples include `Class`, `Struct`, `Union`, + * `Enum`, and `TypedefType`. + */ +class UserType extends Type, Declaration, NameQualifyingElement, AccessHolder, @usertype { + UserType() { + isClass(this) implies this = resolve(_) + } + + /** the name of this type */ + override string getName() { usertypes(this,result,_) } + + /** the simple name of this type, without any template parameters */ + string getSimpleName() { + result = getName().regexpReplaceAll("<.*", "") + } + + override predicate hasName(string name) { + usertypes(this,name,_) + } + predicate isAnonymous() { + getName().matches("(unnamed%") + } + + override predicate hasSpecifier(string s) { + Type.super.hasSpecifier(s) + } + override Specifier getASpecifier() { + result = Type.super.getASpecifier() + } + + override Location getLocation() { + if isDefined() then + result = this.getDefinitionLocation() + else + result = this.getADeclarationLocation() + } + + override TypeDeclarationEntry getADeclarationEntry() { + if type_decls(_, unresolve(this), _) then + type_decls(result, unresolve(this), _) + else + exists(Class t | this.(Class).isConstructedFrom(t) and result = t.getADeclarationEntry()) + } + + override Location getADeclarationLocation() { + result = getADeclarationEntry().getLocation() + } + + override TypeDeclarationEntry getDefinition() { + result = getADeclarationEntry() and + result.isDefinition() + } + + /** the location of the definition */ + override Location getDefinitionLocation() { + if exists(getDefinition()) then + result = getDefinition().getLocation() + else + exists(Class t | this.(Class).isConstructedFrom(t) and result = t.getDefinition().getLocation()) + } + + /** Gets the function that directly encloses this type (if any). */ + Function getEnclosingFunction() { + enclosingfunction(this,result) + } + + /** Whether this is a local type (i.e. a type that has a directly-enclosing function). */ + predicate isLocal() { + exists(getEnclosingFunction()) + } + + // Dummy implementations of inherited methods. This class must not be + // made abstract, because it is important that it captures the @usertype + // type exactly - but this is not apparent from its subclasses + + Declaration getADeclaration() { none() } + + override string explain() { result = this.getName() } + + // further overridden in LocalClass + override AccessHolder getEnclosingAccessHolder() { + result = this.getDeclaringType() + } +} + +/** + * A particular definition or forward declaration of a C/C++ user-defined type. + */ +class TypeDeclarationEntry extends DeclarationEntry, @type_decl { + override UserType getDeclaration() { result = getType() } + override string getName() { result = getType().getName() } + + /** + * The type which is being declared or defined. + */ + override Type getType() { type_decls(this,unresolve(result),_) } + + override Location getLocation() { type_decls(this,_,result) } + override predicate isDefinition() { type_def(this) } + override string getASpecifier() { none() } + + /** + * A top level type declaration entry is not declared within a function, function declaration, + * class or typedef. + */ + predicate isTopLevel() { type_decl_top(this) } +} diff --git a/cpp/ql/src/semmle/code/cpp/Variable.qll b/cpp/ql/src/semmle/code/cpp/Variable.qll new file mode 100644 index 000000000000..b1417dbe8ad9 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/Variable.qll @@ -0,0 +1,409 @@ +import semmle.code.cpp.Element +import semmle.code.cpp.exprs.Access +import semmle.code.cpp.Initializer +private import semmle.code.cpp.internal.Type + +/** + * A C/C++ variable. + * + * For local variables, there is a one-to-one correspondence between + * `Variable` and `VariableDeclarationEntry`. + * + * For other types of variable, there is a one-to-many relationship between + * `Variable` and `VariableDeclarationEntry`. For example, a `Parameter` + * can have multiple declarations. + */ +class Variable extends Declaration, @variable { + + /** Gets the initializer of this variable, if any. */ + Initializer getInitializer() { result.getDeclaration() = this } + + /** Holds if this variable has an initializer. */ + predicate hasInitializer() { exists(this.getInitializer()) } + + /** Gets an access to this variable. */ + VariableAccess getAnAccess() { result.getTarget() = this } + + /** + * Gets a specifier of this variable. This includes `extern`, `static`, + * `auto`, `private`, `protected`, `public`. Specifiers of the *type* of + * this variable, such as `const` and `volatile`, are instead accessed + * through `this.getType().getASpecifier()`. + */ + override Specifier getASpecifier() { varspecifiers(this,result) } + + /** Gets an attribute of this variable. */ + Attribute getAnAttribute() { varattributes(this,result) } + + /** Holds if this variable is `const`. */ + predicate isConst() { this.getType().isConst() } + + /** Holds if this variable is `volatile`. */ + predicate isVolatile() { this.getType().isVolatile() } + + /** Gets the name of this variable. */ + override string getName() { none() } + + /** Gets the type of this variable. */ + Type getType() { none() } + + /** Gets the type of this variable, after typedefs have been resolved. */ + Type getUnderlyingType() { result = this.getType().getUnderlyingType() } + + /** + * Gets the type of this variable prior to deduction caused by the C++11 + * `auto` keyword. + * + * If the type of this variable was not declared with the C++11 `auto` + * keyword, then this predicate does not hold. + * + * If the type of this variable is completely `auto`, then `result` is an + * instance of `AutoType`. For example: + * + * `auto four = 4;` + * + * If the type of this variable is partially `auto`, then a descendant of + * `result` is an instance of `AutoType`. For example: + * + * `const auto& c = container;` + */ + Type getTypeWithAuto() { autoderivation(this, unresolve(result)) } + + /** + * Holds if the type of this variable is declared using the C++ `auto` + * keyword. + */ + predicate declaredUsingAutoType() { autoderivation(this, _) } + + override VariableDeclarationEntry getADeclarationEntry() { + result.getDeclaration() = this + } + + override Location getADeclarationLocation() { + result = getADeclarationEntry().getLocation() + } + + override VariableDeclarationEntry getDefinition() { + result = getADeclarationEntry() and + result.isDefinition() + } + + override Location getDefinitionLocation() { + result = getDefinition().getLocation() + } + + override Location getLocation() { + if exists(getDefinition()) then + result = this.getDefinitionLocation() + else + result = this.getADeclarationLocation() + } + + /** + * Gets an expression that is assigned to this variable somewhere in the + * program. + */ + Expr getAnAssignedValue() { + result = this.getInitializer().getExpr() + or + exists (ConstructorFieldInit cfi + | cfi.getTarget() = this and result = cfi.getExpr()) + or + exists (AssignExpr ae + | ae.getLValue().(Access).getTarget() = this and result = ae.getRValue()) + } + + /** + * Gets an assignment expression that assigns to this variable. + * For example: `x=...` or `x+=...`. + */ + Assignment getAnAssignment() { + result.getLValue() = this.getAnAccess() + } + + /** + * Holds if this variable is constructed from `v` as a result + * of template instantiation. If so, it originates either from a template + * variable or from a variable nested in a template class. + */ + predicate isConstructedFrom(Variable v) { + variable_instantiation(this, v) + } + + /** + * Gets an argument used to instantiate this variable from a template + * variable. + */ + Type getATemplateArgument() { + exists(int i | this.getTemplateArgument(i) = result) + } + + /** + * Gets a particular argument used to instantiate this variable from a + * template variable. + */ + Type getTemplateArgument(int index) { + variable_template_argument(this, index, unresolve(result)) + } + + /** + * Holds if this is a compiler-generated variable. For example, a + * [range-based for loop](http://en.cppreference.com/w/cpp/language/range-for) + * typically has three compiler-generated variables, named `__range`, + * `__begin`, and `__end`: + * + * `for (char c : str) { ... }` + */ + predicate isCompilerGenerated() { compgenerated(this) } +} + +/** + * A particular declaration or definition of a C/C++ variable. + */ +class VariableDeclarationEntry extends DeclarationEntry, @var_decl { + override Variable getDeclaration() { result = getVariable() } + + /** + * Gets the variable which is being declared or defined. + */ + Variable getVariable() { var_decls(this,result,_,_,_) } + + /** + * Gets the name, if any, used for the variable at this declaration or + * definition. + * + * In most cases, this will be the name of the variable itself. The only + * case in which it can differ is in a parameter declaration entry, + * because the parameter may have a different name in the declaration + * than in the definition. For example: + * + * ``` + * // Declaration. Parameter is named "x". + * int f(int x); + * + * // Definition. Parameter is named "y". + * int f(int y) { return y; } + * ``` + */ + override string getName() { var_decls(this,_,_,result,_) and result != "" } + + /** + * Gets the type of the variable which is being declared or defined. + */ + override Type getType() { var_decls(this,_,unresolve(result),_,_) } + + override Location getLocation() { var_decls(this,_,_,_,result) } + + /** + * Holds if this is a definition of a variable. + * + * This always holds for local variables and member variables, but need + * not hold for global variables. In the case of function parameters, + * this holds precisely when the enclosing `FunctionDeclarationEntry` is + * a definition. + */ + override predicate isDefinition() { var_def(this) } + + override string getASpecifier() { var_decl_specifiers(this,result) } +} + +/** + * A parameter as described within a particular declaration or definition + * of a C/C++ function. + */ +class ParameterDeclarationEntry extends VariableDeclarationEntry { + ParameterDeclarationEntry() { param_decl_bind(this,_,_) } + + /** + * Gets the function declaration or definition which this parameter + * description is part of. + */ + FunctionDeclarationEntry getFunctionDeclarationEntry() { + param_decl_bind(this,_,result) + } + + /** + * Gets the zero-based index of this parameter. + */ + int getIndex() { param_decl_bind(this,result,_) } + + override string toString() { + if exists(getName()) + then result = super.toString() + else exists (string idx + | idx = ((getIndex() + 1).toString() + "th") + .replaceAll("1th","1st") + .replaceAll("2th","2nd") + .replaceAll("3th","3rd") + .replaceAll("11st","11th") + .replaceAll("12nd","12th") + .replaceAll("13rd","13th") + | if exists(getCanonicalName()) + then result = "declaration of " + getCanonicalName() + + " as anonymous " + idx + " parameter" + else result = "declaration of " + idx + " parameter") + } + + /** + * Gets the name of this `ParameterDeclarationEntry` including it's type. + * + * For example: "int p". + */ + string getTypedName() { + exists(string typeString, string nameString | + if exists(getType().getName()) then typeString = getType().getName() else typeString = "" and + if exists(getName()) then nameString = getName() else nameString = "" and + if (typeString != "" and nameString != "") then ( + result = typeString + " " + nameString + ) else ( + result = typeString + nameString + ) + ) + } +} + +/** + * A C/C++ variable with block scope [N4140 3.3.3]. In other words, a local + * variable or a function parameter. + */ +class LocalScopeVariable extends Variable, @localscopevariable { + /** Gets the function to which this variable belongs. */ + /*abstract*/ Function getFunction() { none() } +} + +/** + * DEPRECATED: use `LocalScopeVariable` instead. + */ +deprecated class StackVariable extends Variable { + StackVariable() { this instanceof LocalScopeVariable } + Function getFunction() { + result = this.(LocalScopeVariable).getFunction() + } +} + +/** + * A C/C++ local variable. In other words, any variable that has block + * scope [N4140 3.3.3], but is not a function parameter. + */ +class LocalVariable extends LocalScopeVariable, @localvariable { + override string getName() { localvariables(this,_,result) } + + override Type getType() { localvariables(this,unresolve(result),_) } + + override Function getFunction() { + exists(DeclStmt s | s.getADeclaration() = this and s.getEnclosingFunction() = result) + } +} + +/** + * A C/C++ variable which has global scope or namespace scope. + */ +class GlobalOrNamespaceVariable extends Variable, @globalvariable { + override string getName() { globalvariables(this,_,result) } + + override Type getType() { globalvariables(this,unresolve(result),_) } + + override Element getEnclosingElement() { none() } +} + +/** + * A C/C++ variable which has namespace scope. + */ +class NamespaceVariable extends GlobalOrNamespaceVariable { + NamespaceVariable() { + exists(Namespace n | namespacembrs(n, this)) + } +} + +/** + * A C/C++ variable which has global scope. + * + * Note that variables declared in anonymous namespaces have namespace scope, + * even though they are accessed in the same manner as variables declared in + * the enclosing scope of said namespace (which may be the global scope). + */ +class GlobalVariable extends GlobalOrNamespaceVariable { + GlobalVariable() { + not this instanceof NamespaceVariable + } +} + +/** + * A C structure member or C++ member variable. + * + * This includes static member variables in C++. To exclude static member + * variables, use `Field` instead of `MemberVariable`. + */ +class MemberVariable extends Variable, @membervariable { + MemberVariable() { + this.isMember() + } + + /** Holds if this member is private. */ + predicate isPrivate() { this.hasSpecifier("private") } + + /** Holds if this member is protected. */ + predicate isProtected() { this.hasSpecifier("protected") } + + /** Holds if this member is public. */ + predicate isPublic() { this.hasSpecifier("public") } + + override string getName() { membervariables(this,_,result) } + + override Type getType() { + if (strictcount(this.getAType()) = 1) then ( + result = this.getAType() + ) else ( + // In rare situations a member variable may have multiple types in + // different translation units. In that case, we return the unspecified + // type. + result = this.getAType().getUnspecifiedType() + ) + } + + /** Holds if this member is mutable. */ + predicate isMutable() { + getADeclarationEntry().hasSpecifier("mutable") + } + + private Type getAType() { membervariables(this,unresolve(result),_) } +} + +/** + * A C/C++ function pointer variable. + */ +class FunctionPointerVariable extends Variable { + FunctionPointerVariable() { + this.getType() instanceof FunctionPointerType + } +} + +/** + * A C/C++ function pointer member variable. + */ +class FunctionPointerMemberVariable extends MemberVariable { + FunctionPointerMemberVariable() { + this instanceof FunctionPointerVariable + } +} + +/** + * A C++14 variable template. + */ +class TemplateVariable extends Variable { + TemplateVariable() { is_variable_template(this) } + Variable getAnInstantiation() { result.isConstructedFrom(this) } +} + +/** + * A non-static local variable or parameter that is not part of an + * uninstantiated template. Uninstantiated templates are purely syntax, and + * only on instantiation will they be complete with information about types, + * conversions, call targets, etc. + */ +class SemanticStackVariable extends LocalScopeVariable { + SemanticStackVariable() { + not this.isStatic() and + not this.isFromUninstantiatedTemplate(_) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/XML.qll b/cpp/ql/src/semmle/code/cpp/XML.qll new file mode 100644 index 000000000000..eaa2eefb8f9f --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/XML.qll @@ -0,0 +1,282 @@ +/** + * A library for working with XML files and their content. + */ + +import semmle.code.cpp.Location + +/** An XML element that has a location. */ +abstract class XMLLocatable extends @xmllocatable { + /** The source location for this element. */ + Location getLocation() { xmllocations(this,result) } + + /** + * Whether this element has the specified location information, + * including file path, start line, start column, end line and end column. + */ + predicate hasLocationInfo(string filepath, int startline, int startcolumn, int endline, int endcolumn) { + exists(File f, Location l | l = this.getLocation() | + locations_default(l,f,startline,startcolumn,endline,endcolumn) and + filepath = f.getAbsolutePath() + ) + } + + /** A printable representation of this element. */ + abstract string toString(); +} + +/** + * An `XMLParent` is either an `XMLElement` or an `XMLFile`, + * both of which can contain other elements. + */ +class XMLParent extends @xmlparent { + /** + * A printable representation of this XML parent. + * (Intended to be overridden in subclasses.) + */ + abstract string getName(); + + /** The file to which this XML parent belongs. */ + XMLFile getFile() { result = this or xmlElements(this,_,_,_,result) } + + /** The child element at a specified index of this XML parent. */ + XMLElement getChild(int index) { xmlElements(result, _, this, index, _) } + + /** A child element of this XML parent. */ + XMLElement getAChild() { xmlElements(result,_,this,_,_) } + + /** A child element of this XML parent with the given `name`. */ + XMLElement getAChild(string name) { xmlElements(result,_,this,_,_) and result.hasName(name) } + + /** A comment that is a child of this XML parent. */ + XMLComment getAComment() { xmlComments(result,_,this,_) } + + /** A character sequence that is a child of this XML parent. */ + XMLCharacters getACharactersSet() { xmlChars(result,_,this,_,_,_) } + + /** The depth in the tree. (Overridden in XMLElement.) */ + int getDepth() { result = 0 } + + /** The number of child XML elements of this XML parent. */ + int getNumberOfChildren() { + result = count(XMLElement e | xmlElements(e,_,this,_,_)) + } + + /** The number of places in the body of this XML parent where text occurs. */ + int getNumberOfCharacterSets() { + result = count(int pos | xmlChars(_,_,this,pos,_,_)) + } + + /** + * Append the character sequences of this XML parent from left to right, separated by a space, + * up to a specified (zero-based) index. + */ + string charsSetUpTo(int n) { + (n = 0 and xmlChars(_,result,this,0,_,_)) or + (n > 0 and exists(string chars | xmlChars(_,chars,this,n,_,_) | + result = this.charsSetUpTo(n-1) + " " + chars)) + } + + /** Append all the character sequences of this XML parent from left to right, separated by a space. */ + string allCharactersString() { + exists(int n | n = this.getNumberOfCharacterSets() | + (n = 0 and result = "") or + (n > 0 and result = this.charsSetUpTo(n-1)) + ) + } + + /** The text value contained in this XML parent. */ + string getTextValue() { + result = allCharactersString() + } + + /** A printable representation of this XML parent. */ + string toString() { result = this.getName() } +} + +/** An XML file. */ +class XMLFile extends XMLParent, File { + XMLFile() { + xmlEncoding(this,_) + } + + /** A printable representation of this XML file. */ + override + string toString() { result = XMLParent.super.toString() } + + /** The name of this XML file. */ + override + string getName() { files(this,result,_,_,_) } + + /** The path of this XML file. */ + string getPath() { files(this,_,result,_,_) } + + /** The path of the folder that contains this XML file. */ + string getFolder() { + result = this.getPath().substring(0, this.getPath().length()-this.getName().length()) + } + + /** The encoding of this XML file. */ + string getEncoding() { xmlEncoding(this,result) } + + /** The XML file itself. */ + override + XMLFile getFile() { result = this } + + /** A top-most element in an XML file. */ + XMLElement getARootElement() { result = this.getAChild() } + + /** A DTD associated with this XML file. */ + XMLDTD getADTD() { xmlDTDs(result,_,_,_,this) } +} + +/** A "Document Type Definition" of an XML file. */ +class XMLDTD extends @xmldtd { + /** The name of the root element of this DTD. */ + string getRoot() { xmlDTDs(this,result,_,_,_) } + + /** The public ID of this DTD. */ + string getPublicId() { xmlDTDs(this,_,result,_,_) } + + /** The system ID of this DTD. */ + string getSystemId() { xmlDTDs(this,_,_,result,_) } + + /** Whether this DTD is public. */ + predicate isPublic() { not xmlDTDs(this,_,"",_,_) } + + /** The parent of this DTD. */ + XMLParent getParent() { xmlDTDs(this,_,_,_,result) } + + /** A printable representation of this DTD. */ + string toString() { + (this.isPublic() and result = this.getRoot() + " PUBLIC '" + + this.getPublicId() + "' '" + + this.getSystemId() + "'") or + (not this.isPublic() and result = this.getRoot() + + " SYSTEM '" + + this.getSystemId() + "'") + } +} + +/** An XML tag in an XML file. */ +class XMLElement extends @xmlelement, XMLParent, XMLLocatable { + /** Whether this XML element has the given `name`. */ + predicate hasName(string name) { name = getName() } + + /** The name of this XML element. */ + override + string getName() { xmlElements(this,result,_,_,_) } + + /** The XML file in which this XML element occurs. */ + override + XMLFile getFile() { xmlElements(this,_,_,_,result) } + + /** The parent of this XML element. */ + XMLParent getParent() { xmlElements(this,_,result,_,_) } + + /** The index of this XML element among its parent's children. */ + int getIndex() { xmlElements(this, _, _, result, _) } + + /** Whether this XML element has a namespace. */ + predicate hasNamespace() { xmlHasNs(this,_,_) } + + /** The namespace of this XML element, if any. */ + XMLNamespace getNamespace() { xmlHasNs(this,result,_) } + + /** The index of this XML element among its parent's children. */ + int getElementPositionIndex() { xmlElements(this,_,_,result,_) } + + /** The depth of this element within the XML file tree structure. */ + override + int getDepth() { result = this.getParent().getDepth() + 1 } + + /** An XML attribute of this XML element. */ + XMLAttribute getAnAttribute() { result.getElement() = this } + + /** The attribute with the specified `name`, if any. */ + XMLAttribute getAttribute(string name) { + result.getElement() = this and result.getName() = name + } + + /** Whether this XML element has an attribute with the specified `name`. */ + predicate hasAttribute(string name) { + exists(XMLAttribute a| a = this.getAttribute(name)) + } + + /** The value of the attribute with the specified `name`, if any. */ + string getAttributeValue(string name) { + result = this.getAttribute(name).getValue() + } + + /** A printable representation of this XML element. */ + override + string toString() { result = XMLParent.super.toString() } +} + +/** An attribute that occurs inside an XML element. */ +class XMLAttribute extends @xmlattribute, XMLLocatable { + /** The name of this attribute. */ + string getName() { xmlAttrs(this,_,result,_,_,_) } + + /** The XML element to which this attribute belongs. */ + XMLElement getElement() { xmlAttrs(this,result,_,_,_,_) } + + /** Whether this attribute has a namespace. */ + predicate hasNamespace() { xmlHasNs(this,_,_) } + + /** The namespace of this attribute, if any. */ + XMLNamespace getNamespace() { xmlHasNs(this,result,_) } + + /** The value of this attribute. */ + string getValue() { xmlAttrs(this,_,_,result,_,_) } + + /** A printable representation of this XML attribute. */ + override string toString() { result = this.getName() + "=" + this.getValue() } +} + +/** A namespace used in an XML file */ +class XMLNamespace extends @xmlnamespace { + /** The prefix of this namespace. */ + string getPrefix() { xmlNs(this,result,_,_) } + + /** The URI of this namespace. */ + string getURI() { xmlNs(this,_,result,_) } + + /** Whether this namespace has no prefix. */ + predicate isDefault() { this.getPrefix() = "" } + + /** A printable representation of this XML namespace. */ + string toString() { + (this.isDefault() and result = this.getURI()) or + (not this.isDefault() and result = this.getPrefix() + ":" + this.getURI()) + } +} + +/** A comment of the form `` is an XML comment. */ +class XMLComment extends @xmlcomment, XMLLocatable { + /** The text content of this XML comment. */ + string getText() { xmlComments(this,result,_,_) } + + /** The parent of this XML comment. */ + XMLParent getParent() { xmlComments(this,_,result,_) } + + /** A printable representation of this XML comment. */ + override string toString() { result = this.getText() } +} + +/** + * A sequence of characters that occurs between opening and + * closing tags of an XML element, excluding other elements. + */ +class XMLCharacters extends @xmlcharacters, XMLLocatable { + /** The content of this character sequence. */ + string getCharacters() { xmlChars(this,result,_,_,_,_) } + + /** The parent of this character sequence. */ + XMLParent getParent() { xmlChars(this,_,result,_,_,_) } + + /** Whether this character sequence is CDATA. */ + predicate isCDATA() { xmlChars(this,_,_,_,1,_) } + + /** A printable representation of this XML character sequence. */ + override string toString() { result = this.getCharacters() } +} diff --git a/cpp/ql/src/semmle/code/cpp/commons/Alloc.qll b/cpp/ql/src/semmle/code/cpp/commons/Alloc.qll new file mode 100644 index 000000000000..e0a3f5427313 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/commons/Alloc.qll @@ -0,0 +1,118 @@ +import cpp + +/** + * A library routine that allocates memory. + */ +predicate allocationFunction(Function f) +{ + exists(string name | + f.hasQualifiedName(name) and + ( + name = "malloc" or + name = "calloc" or + name = "realloc" or + name = "strdup" or + name = "wcsdup" or + name = "_strdup" or + name = "_wcsdup" or + name = "_mbsdup" + ) + ) +} + +/** + * A call to a library routine that allocates memory. + */ +predicate allocationCall(FunctionCall fc) +{ + allocationFunction(fc.getTarget()) and + ( + // realloc(ptr, 0) only frees the pointer + fc.getTarget().hasQualifiedName("realloc") implies + not fc.getArgument(1).getValue() = "0" + ) +} + +/** + * A library routine that frees memory. + */ +predicate freeFunction(Function f, int argNum) +{ + exists(string name | + f.hasQualifiedName(name) and + ( + (name = "free" and argNum = 0) or + (name = "realloc" and argNum = 0) + ) + ) +} + +/** + * A call to a library routine that frees memory. + */ +predicate freeCall(FunctionCall fc, Expr arg) +{ + exists(int argNum | + freeFunction(fc.getTarget(), argNum) and + arg = fc.getArgument(argNum) + ) +} + +/** + * Is e some kind of allocation or deallocation (`new`, `alloc`, `realloc`, `delete`, `free` etc)? + */ +predicate isMemoryManagementExpr(Expr e) { + isAllocationExpr(e) or isDeallocationExpr(e) +} + +/** + * Is e an allocation from stdlib.h (`malloc`, `realloc` etc)? + */ +predicate isStdLibAllocationExpr(Expr e) +{ + allocationCall(e) +} + +/** + * Is e some kind of allocation (`new`, `alloc`, `realloc` etc)? + */ +predicate isAllocationExpr(Expr e) { + allocationCall(e) + or e instanceof NewExpr + or e instanceof NewArrayExpr +} + +/** + * Is e some kind of allocation (`new`, `alloc`, `realloc` etc) with a fixed size? + */ +predicate isFixedSizeAllocationExpr(Expr allocExpr, int size) { +exists (FunctionCall fc, string name | fc = allocExpr and name = fc.getTarget().getName() | + ( + name = "malloc" and + size = fc.getArgument(0).getValue().toInt() + ) or ( + name = "alloca" and + size = fc.getArgument(0).getValue().toInt() + ) or ( + name = "calloc" and + size = fc.getArgument(0).getValue().toInt() * fc.getArgument(1).getValue().toInt() + ) or ( + name = "realloc" and + size = fc.getArgument(1).getValue().toInt() and + size > 0 // realloc(ptr, 0) only frees the pointer + ) + ) or ( + size = allocExpr.(NewExpr).getAllocatedType().getSize() + ) or ( + size = allocExpr.(NewArrayExpr).getAllocatedType().getSize() + ) +} + +/** + * Is e some kind of deallocation (`delete`, `free`, `realloc` etc)? + */ +predicate isDeallocationExpr(Expr e) { + freeCall(e, _) + or e instanceof DeleteExpr + or e instanceof DeleteArrayExpr +} diff --git a/cpp/ql/src/semmle/code/cpp/commons/Assertions.qll b/cpp/ql/src/semmle/code/cpp/commons/Assertions.qll new file mode 100644 index 000000000000..3c5cb96962b0 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/commons/Assertions.qll @@ -0,0 +1,73 @@ +import cpp + +/** + * An assertion, that is, a condition that is only false if there is a bug in + * the program. To add support for more, define additional subclasses. A + * typical subclass will extend extend either `MacroInvocation` (for assertion + * macros) or `FunctionCall` (for assertion functions). + */ +abstract class Assertion extends Locatable { + /** Gets the expression whose truth is being asserted. */ + abstract Expr getAsserted(); +} + +/** + * A libc assert, as defined in assert.h. A macro with the head + * "assert(expr)" that expands to a conditional expression which + * may terminate the program. + */ +class LibcAssert extends MacroInvocation, Assertion { + + LibcAssert() { + this.getMacro().getHead() = "assert(expr)" + } + + override Expr getAsserted() { + exists(ConditionalExpr ce | this.getAGeneratedElement() = ce | + result = ce.getCondition()) + } +} + +/** + * A macro assert that expands to an if statement; the head is taken + * to be "Assert(x, y)", but further alternatives could be added. + */ +class MacroAssert extends MacroInvocation, Assertion { + + MacroAssert() { + this.getMacro().getHead() = "Assert(x, y)" + } + + override Expr getAsserted() { + exists(IfStmt i | this.getAGeneratedElement() = i | + result = i.getCondition()) + } +} + +/** + * An assertion that is not completely disabled in release builds but returns a + * Boolean value to enable recovery from unexpected anomalous behavior. This + * style of assert is advocated by the _Power of 10_ rules and the _NASA JPL + * Coding Standard_. + */ +class RecoverableAssert extends MacroInvocation, Assertion { + RecoverableAssert() { + this.getMacro().getHead().matches("c\\_assert(%") + } + + private + Expr getAnAssertedExpr() { + result = this.getAGeneratedElement() and + not result.getLocation().getStartColumn() = this.getLocation().getStartColumn() + } + + override Expr getAsserted() { + result = this.getAnAssertedExpr() and + not result.getParent() = this.getAnAssertedExpr() and + // Remove spurious "string literals" that arise when the macro + // uses #stringification + not result.(Literal).getType().getUnspecifiedType().(ArrayType).getBaseType() instanceof CharType + } +} + +/* More assertion definitions go here. */ diff --git a/cpp/ql/src/semmle/code/cpp/commons/Buffer.qll b/cpp/ql/src/semmle/code/cpp/commons/Buffer.qll new file mode 100644 index 000000000000..b184832e1661 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/commons/Buffer.qll @@ -0,0 +1,103 @@ +import cpp + +/** + * Holds if `sizeof(s)` occurs as part of the parameter of a dynamic + * memory allocation (`malloc`, `realloc`, etc.), except if `sizeof(s)` + * only ever occurs as the immediate parameter to allocations. + * + * For example, holds for `s` if it occurs as + * ``` + * malloc(sizeof(s) + 100 * sizeof(char)) + * ``` + * but not if it only ever occurs as + * ``` + * malloc(sizeof(s)) + * ``` +*/ +private predicate isDynamicallyAllocatedWithDifferentSize(Class s) { + exists(SizeofTypeOperator sof | + sof.getTypeOperand().getUnspecifiedType() = s | + // Check all ancestor nodes except the immediate parent for + // allocations. + isStdLibAllocationExpr(sof.getParent().(Expr).getParent+()) + ) +} + +/** + * Holds if `v` is a member variable of `c` that looks like it might be variable sized in practice. For + * example: + * ``` + * struct myStruct { // c + * int amount; + * char data[1]; // v + * }; + * ``` + * This requires that `v` is an array of size 0 or 1, and `v` is the last member of `c`. In addition, + * there must be at least one instance where a `c` pointer is allocated with additional space. + */ +predicate memberMayBeVarSize(Class c, MemberVariable v) { + exists(int i | + i = max(int j | c.getCanonicalMember(j) instanceof Field | j) and + v = c.getCanonicalMember(i) and + v.getType().getUnspecifiedType().(ArrayType).getSize() <= 1 + ) and + isDynamicallyAllocatedWithDifferentSize(c) +} + +/** + * Get the size in bytes of the buffer pointed to by an expression (if this can be determined). + */ +int getBufferSize(Expr bufferExpr, Element why) { + exists(Variable bufferVar | bufferVar = bufferExpr.(VariableAccess).getTarget() | + ( + // buffer is a fixed size array + result = bufferVar.getType().getUnspecifiedType().(ArrayType).getSize() and + why = bufferVar and + not memberMayBeVarSize(_, bufferVar) + ) or ( + // buffer is an initialized array + // e.g. int buffer[] = {1, 2, 3}; + why = bufferVar.getInitializer().getExpr() and + result = why.(Expr).getType().(ArrayType).getSize() and + not exists(bufferVar.getType().getUnspecifiedType().(ArrayType).getSize()) + ) or exists(Class parentClass, VariableAccess parentPtr | + // buffer is the parentPtr->bufferVar of a 'variable size struct' + memberMayBeVarSize(parentClass, bufferVar) and + why = bufferVar and + parentPtr = bufferExpr.(VariableAccess).getQualifier() and + parentPtr.getTarget().getType().getUnspecifiedType().(PointerType).getBaseType() = parentClass and + result = + getBufferSize(parentPtr, _) + + bufferVar.getType().getSize() - + parentClass.getSize() + ) + ) or exists(Expr def | + // buffer is assigned with an allocation + definitionUsePair(_, def, bufferExpr) and + exprDefinition(_, def, why) and + isFixedSizeAllocationExpr(why, result) + ) or exists(Expr def, Expr e, Element why2 | + // buffer is assigned with another buffer + definitionUsePair(_, def, bufferExpr) and + exprDefinition(_, def, e) and + result = getBufferSize(e, why2) and + ( + why = def or + why = why2 + ) + ) or exists(Type bufferType | + // buffer is the address of a variable + why = bufferExpr.(AddressOfExpr).getAddressable() and + bufferType = why.(Variable).getType() and + result = bufferType.getSize() and + not bufferType instanceof ReferenceType and + not any(Union u).getAMemberVariable() = why + ) or exists(Union bufferType | + // buffer is the address of a union member; in this case, we + // take the size of the union itself rather the union member, since + // it's usually OK to access that amount (e.g. clearing with memset). + why = bufferExpr.(AddressOfExpr).getAddressable() and + bufferType.getAMemberVariable() = why and + result = bufferType.getSize() + ) +} diff --git a/cpp/ql/src/semmle/code/cpp/commons/CommonType.qll b/cpp/ql/src/semmle/code/cpp/commons/CommonType.qll new file mode 100644 index 000000000000..99496da0cf70 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/commons/CommonType.qll @@ -0,0 +1,153 @@ +import semmle.code.cpp.Type + +/** + * The C/C++ char* type. + */ +class CharPointerType extends PointerType { + + CharPointerType() { this.getBaseType() instanceof CharType } + +} + +/** + * The C/C++ int* type. + */ +class IntPointerType extends PointerType { + + IntPointerType() { this.getBaseType() instanceof IntType } + +} + + +/** + * The C/C++ void* type. + */ +class VoidPointerType extends PointerType { + + VoidPointerType() { this.getBaseType() instanceof VoidType } + +} + +/** + * The C/C++ size_t type. + */ +class Size_t extends Type { + Size_t() { + this.getUnderlyingType() instanceof IntegralType and + this.hasName("size_t") + } +} + +/** + * The C/C++ ssize_t type. + */ +class Ssize_t extends Type { + Ssize_t() { + this.getUnderlyingType() instanceof IntegralType and + this.hasName("ssize_t") + } +} + +/** + * The C/C++ ptrdiff_t type. + */ +class Ptrdiff_t extends Type { + Ptrdiff_t() { + this.getUnderlyingType() instanceof IntegralType and + this.hasName("ptrdiff_t") + } +} + +/** + * The C/C++ intmax_t type. + */ +class Intmax_t extends Type { + Intmax_t() { + this.getUnderlyingType() instanceof IntegralType and + this.hasName("intmax_t") + } +} + +/** + * The C/C++ uintmax_t type. + */ +class Uintmax_t extends Type { + Uintmax_t() { + this.getUnderlyingType() instanceof IntegralType and + this.hasName("uintmax_t") + } +} + +/** + * The C/C++ wchar_t type. + */ +class Wchar_t extends Type { + Wchar_t() { + this.getUnderlyingType() instanceof IntegralType and + this.hasName("wchar_t") + } +} + +/** + * The type that the Microsoft C/C++ `__int8` type specifier is a + * synonym for. Note that since `__int8` is not a distinct type, + * `MicrosoftInt8Type` corresponds to an existing `IntegralType` as + * well. + * + * This class is meaningless if a Microsoft compiler was not used. + */ +class MicrosoftInt8Type extends IntegralType { + MicrosoftInt8Type() { + this instanceof CharType and + not isExplicitlyUnsigned() and + not isExplicitlySigned() + } +} + +/** + * The type that the Microsoft C/C++ `__int16` type specifier is a + * synonym for. Note that since `__int16` is not a distinct type, + * `MicrosoftInt16Type` corresponds to an existing `IntegralType` as + * well. + * + * This class is meaningless if a Microsoft compiler was not used. + */ +class MicrosoftInt16Type extends IntegralType { + MicrosoftInt16Type() { + this instanceof ShortType and + not isExplicitlyUnsigned() and + not isExplicitlySigned() + } +} + +/** + * The type that the Microsoft C/C++ `__int32` type specifier is a + * synonym for. Note that since `__int32` is not a distinct type, + * `MicrosoftInt32Type` corresponds to an existing `IntegralType` as + * well. + * + * This class is meaningless if a Microsoft compiler was not used. + */ +class MicrosoftInt32Type extends IntegralType { + MicrosoftInt32Type() { + this instanceof IntType and + not isExplicitlyUnsigned() and + not isExplicitlySigned() + } +} + +/** + * The type that the Microsoft C/C++ `__int64` type specifier is a + * synonym for. Note that since `__int64` is not a distinct type, + * `MicrosoftInt64Type` corresponds to an existing `IntegralType` as + * well. + * + * This class is meaningless if a Microsoft compiler was not used. + */ +class MicrosoftInt64Type extends IntegralType { + MicrosoftInt64Type() { + this instanceof LongLongType and + not isExplicitlyUnsigned() and + not isExplicitlySigned() + } +} diff --git a/cpp/ql/src/semmle/code/cpp/commons/Dependency.qll b/cpp/ql/src/semmle/code/cpp/commons/Dependency.qll new file mode 100644 index 000000000000..3c12621ef7c1 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/commons/Dependency.qll @@ -0,0 +1,523 @@ +import semmle.code.cpp.Element +import semmle.code.cpp.Declaration +import semmle.code.cpp.Function +import semmle.code.cpp.Variable + +/** + * Options that control the dependencies generated by + * this library. + */ +class DependencyOptions extends string +{ + DependencyOptions() { + this = "DependencyOptions" + } + + /** + * Holds if dependencies should only be generated in templates rather than + * in both templates and instantiations, where possible. This is expensive + * to compute, but tends to produce dependencies that are easier to read. + */ + cached predicate preferTemplateDeps() { + any() + } +} + +/** + * Gets the `DependencyOptions`. + */ +DependencyOptions getDependencyOptions() +{ + any() +} + +/** + * An Element that can be the source of a transitive dependency. This is any + * Element that is not in a template instantiation, plus declarations of template + * specializations (even though they are technically in an instantiation) because + * we need to generate (at least) a dependency from them to the general declaration. + */ +class DependsSource extends Element { + DependsSource() { + // not inside a template instantiation + not exists(Element other | isFromTemplateInstantiation(other)) or + + // allow DeclarationEntrys of template specializations + this.(DeclarationEntry).getDeclaration().(Function).isConstructedFrom(_) or + this.(DeclarationEntry).getDeclaration().(Class).isConstructedFrom(_) + } +} + +/** + * A program element which can be the target of inter-function or inter-file dependencies. + * + * This is the union of Declaration, DeclarationEntry and Macro, minus various kinds of declaration: + * * FriendDecl is not included, as a friend declaration cannot be the target of a dependency (nor, as it happens, can they be a source). + * * TemplateParameter and related UserTypes are not included, as they are intrinsic sub-components of their associated template. + * * Template instantiations are excluded, as the template itself is more useful as a dependency target. + * * Stack variables and local types are excluded, as they are lexically tied to their enclosing function, and intra-function dependencies + * can only be inter-file dependencies in pathological cases. + * * Builtin functions and macros are excluded, as dependencies on them do not translate to inter-file dependencies (note that static functions + * and declarations within anonymous namespaces cannot be excluded for this reason, as the declaration can be in a header). + * * DeclarationEntrys are only needed if they're not definitions, for the definition to declaration dependency. + */ +class Symbol extends DependsSource { + Symbol() { + ( + exists(EnumConstant ec | this = ec and not ec.getDeclaringEnum() instanceof LocalEnum) or + (this instanceof Macro and this.getFile().getAbsolutePath() != "") or + (this instanceof DeclarationEntry and + not this.(VariableDeclarationEntry).getVariable() instanceof LocalScopeVariable and + not this.(FunctionDeclarationEntry).getFunction() instanceof BuiltInFunction and + not this.(TypeDeclarationEntry).getType() instanceof LocalEnum and + not this.(TypeDeclarationEntry).getType() instanceof LocalClass and + not this.(TypeDeclarationEntry).getType() instanceof LocalTypedefType and + not this.(TypeDeclarationEntry).getType() instanceof TemplateParameter + ) or + (this instanceof NamespaceDeclarationEntry) + ) + } + + /** + * Gets an element which depends upon this symbol. + * + * To a first approximation, dependent elements can be thought of as occurrences of the symbol's name: instances of `VariableAccess` + * for `Variable` symbols, instances of `MacroInvocation` for `Macro` symbols, and so on. + * + * category: + * 1 - C/C++ compile-time dependency + * 2 - C/C++ link-time dependency (or transitive dependency with a link-time component) + */ + cached + Element getADependentElement(int category) { + dependsOnFull(result, this, category) + } +} + +/** + * Associates a Declaration with it's DeclarationEntries, or (for a template + * instantiation) with the DeclarationEntries of its template. + */ +cached predicate getDeclarationEntries(Declaration decl, DeclarationEntry de) +{ + ( + decl = de.getDeclaration() or + decl.(Function).isConstructedFrom(de.getDeclaration()) or + decl.(Class).isConstructedFrom(de.getDeclaration()) + ) and + /* + * ParameterDeclarationEntries are special, as (a) they can only be accessed + * from within the definition, and (b) non-definition PDEs may be commonly + * included. Thus, for PDEs, we point only to the definition. + */ + (de instanceof ParameterDeclarationEntry implies de.isDefinition()) +} + +/** + * A 'simple' dependency from src to dest. This type of dependency + * does not make any special account of templates. + * + * Consider using Symbol.getADependentElement() rather than directly + * accessing this predicate. + */ +predicate dependsOnSimple(Element src, Element dest) { + dependsOnSimpleInline(src, dest) or + dependency_macroUse(src, dest) +} + +/** + * A 'simple' dependency that might be inlined. + */ +private +predicate dependsOnSimpleInline(Element src, Element dest) { + dependency_functionUse(src, dest) or + dependency_typeUse(src, dest) or + dependency_variableUse(src, dest) or + dependency_usingDeclaration(src, dest) or + dependency_usingNamespace(src, dest) or + dependency_enumConstantUse(src, dest) or + dependency_outOfLineDeclaration(src, dest) or + dependency_outOfLineInitializer(src, dest) or + dependency_functionSpecialization(src, dest) or + dependency_classSpecialization(src, dest) +} + +/** + * Holds if a simple, non-template dependency exists between two Locations + * specified by the parameters. + */ +private predicate dependsLocation(File f1, int sl1, int sc1, int el1, int ec1, File f2, int sl2, int sc2, int el2, int ec2) { + exists(Element src, Element dest, Location loc1, Location loc2 | + dependsOnSimpleInline(src, dest) and + src instanceof DependsSource and + loc1 = src.getLocation() and + f1 = loc1.getFile() and + sl1 = loc1.getStartLine() and + sc1 = loc1.getStartColumn() and + el1 = loc1.getEndLine() and + ec1 = loc1.getEndColumn() and + loc2 = dest.getLocation() and + f2 = loc2.getFile() and + sl2 = loc2.getStartLine() and + sc2 = loc2.getStartColumn() and + el2 = loc2.getEndLine() and + ec2 = loc2.getEndColumn() + ) +} + +/** + * Holds if a simple dependency from `loc` to `loc2` in a template has a + * non-template alternative? + * (if `DependencyOptions.preferTemplateDeps()` is enabled) + */ +private predicate dependsNonTemplateAlternative(Location loc1, Location loc2) { + getDependencyOptions().preferTemplateDeps() and + exists(Element src, Element dest | + dependsOnSimpleInline(src, dest) and + src.isFromTemplateInstantiation(_) and + src.getLocation() = loc1 and + dest.getLocation() = loc2 + ) and + dependsLocation(loc1.getFile(), loc1.getStartLine(), loc1.getStartColumn(), loc1.getEndLine(), loc1.getEndColumn(), loc2.getFile(), loc2.getStartLine(), loc2.getStartColumn(), loc2.getEndLine(), loc2.getEndColumn()) +} + +/** + * A simple dependency from src to a declaration dest, where the definition is not + * needed at compile time. + */ +predicate dependsOnDeclOnly(Element src, Element dest) { + dependency_functionUse(src, dest) or + dependency_variableUse(src, dest) or + dependency_pointerTypeUse(src, dest) +} + +/** + * A dependency from src to dest. This predicate inlines + * template dependencies. + */ +private predicate dependsOnViaTemplate(Declaration src, Element dest) { + // A template instantiation depends on everything that anything + // inside it depends upon. This effectively inlines the things + // inside at the point where the template is called or + // referenced. + exists(Element internal, Location internalLocation, Location destLocation | + // internal is an element in the template {function or class} instantiation that cannot + // itself be a transitive dependency source + internal.isFromTemplateInstantiation(src) and + + // don't generate template dependencies through a member function of a template class; + // these dependencies are also generated through the class, which has to be referenced + // somewhere anyway. + not exists(Class c | + internal.isFromTemplateInstantiation(c) and + src.getDeclaringType() = c + ) and + + // dest is anything that the internal element depends upon + dependsOnSimpleInline(internal, dest) and + + // is there something in the template (not the instantiation) that's generating + // (better) dependencies from internal anyway? + internalLocation = internal.getLocation() and + destLocation = dest.getLocation() and + not dependsNonTemplateAlternative(internalLocation, destLocation) + ) +} + +/** + * Holds if `src` is related to `dest` by one `dependsOnSimple` and any + * number of `dependsOnViaTemplate` steps. + * + * Consider using `Symbol.getADependentElement()` rather than directly + * accessing this predicate. + */ +predicate dependsOnTransitive(DependsSource src, Element dest) { + exists(Element mid1 | + // begin with a simple step + dependsOnSimpleInline(src, mid1) and + + // any number of recursive steps + ( + mid1 = dest or // mid1 is not necessarily a Declaration + dependsOnViaTemplate+(mid1, dest) + ) + ) or dependency_macroUse(src, dest) +} + +/** + * A dependency that targets a TypeDeclarationEntry. + */ +private predicate dependsOnTDE(Element src, Type t, TypeDeclarationEntry dest) { + dependsOnTransitive(src, t) and + getDeclarationEntries(t, dest) +} + +/** + * A dependency that targets a visible TypeDeclarationEntry. + */ +private pragma[noopt] predicate dependsOnVisibleTDE(Element src, Type t, TypeDeclarationEntry dest) { + dependsOnTDE(src, t, dest) and + exists(File g | g = dest.getFile() | + exists(File f | f = src.getFile() | + f.getAnIncludedFile*() = g + ) + ) +} + +/** + * A dependency that targets a DeclarationEntry + */ +private predicate dependsOnDeclarationEntry(Element src, DeclarationEntry dest) { + exists(Type t | + // dependency from a Type use -> unique visible TDE + dependsOnVisibleTDE(src, t, dest) and + strictcount(TypeDeclarationEntry alt | + dependsOnVisibleTDE(src, t, alt) + ) = 1 + ) or exists(TypedefType mid | + // dependency from a TypedefType use -> any (visible) TDE + dependsOnTransitive(src, mid) and + getDeclarationEntries(mid, (TypeDeclarationEntry)dest) + ) or exists(Declaration mid | + // dependency from a Variable / Function use -> any (visible) declaration entry + dependsOnTransitive(src, mid) and + not mid instanceof Type and + not mid instanceof EnumConstant and + + getDeclarationEntries(mid, (DeclarationEntry)dest) and + not dest instanceof TypeDeclarationEntry + ) or exists(Declaration mid | + // dependency from a Type / Variable / Function use -> any (visible) definition + dependsOnTransitive(src, mid) and + not mid instanceof EnumConstant and + + getDeclarationEntries(mid, (DeclarationEntry)dest) and + + // must be definition + dest.(DeclarationEntry).isDefinition() + ) +} + +/** + * The full dependsOn relation, made up of dependsOnTransitive plus some logic + * to fix up the results for Declarations to most reasonable DeclarationEntrys. + */ +private predicate dependsOnFull(DependsSource src, Symbol dest, int category) { + ( + // direct result + dependsOnTransitive(src, dest) and + category = 1 + ) or ( + // result to a visible DeclarationEntry + dependsOnDeclarationEntry(src, dest) and + src.getFile().getAnIncludedFile*() = dest.getFile() and + category = 1 + ) or exists(Declaration mid | + // dependency from a Variable / Function use -> non-visible definition (link time) + dependsOnTransitive(src, mid) and + not mid instanceof EnumConstant and + + getDeclarationEntries(mid, (DeclarationEntry)dest) and + not dest instanceof TypeDeclarationEntry and + + // must be definition + dest.(DeclarationEntry).isDefinition() and + + // must not be visible (else covered above) + not src.getFile().getAnIncludedFile*() = dest.getFile() and + + // filter out FDEs that are only defined in the dummy link target + ( + ( + dest instanceof FunctionDeclarationEntry and + isLinkerAwareExtracted() + ) implies exists(LinkTarget lt | not lt.isDummy() | + lt.getAFunction() = dest.(FunctionDeclarationEntry).getFunction() + ) + ) and + + category = 2 + ) +} + +/** + * A dependency caused by a function call / use. + */ +private +predicate dependency_functionUse(Element src, Function dest) { + funbind(src, dest) +} + +/** + * A Type which refers to a UserType. + */ +private cached predicate refersToUserType(Type a, UserType b) { + a.refersTo(b) +} + +/** + * A Type which refers to a type directly, without using a pointer or reference. + */ +private predicate refersToDirectlyNonPointer(Type a, Type b) { + a.refersToDirectly(b) and + not a instanceof PointerType and + not a instanceof ReferenceType +} + +/** + * A Type which refers to a UserType, but only through a pointer or reference. + */ +private cached predicate refersToUserTypePointer(Type a, UserType b) { + refersToUserType(a, b) and + not refersToDirectlyNonPointer*(a, b) +} + +/** + * A dependency caused by a type use. + */ +private +predicate dependency_typeUse(Element src, UserType dest) { + refersToUserType(typeUsedBy(src), dest) +} + +/** + * A dependency caused by a pointer/reference type use only. + */ +predicate dependency_pointerTypeUse(Element src, UserType dest) { + refersToUserTypePointer(typeUsedBy(src), dest) +} + +/** + * The Types that must be defined for a particular Element. + */ +private +Type typeUsedBy(Element src) { + ( + result = src.(VariableDeclarationEntry).getType() and not src.(VariableDeclarationEntry).getVariable().declaredUsingAutoType() + ) or ( + result = src.(FunctionDeclarationEntry).getType() + ) or ( + result = src.(Cast).getType() and not src.(Cast).isImplicit() + ) or ( + result = src.(ClassDerivation).getBaseClass() + ) or ( + result = src.(TypeDeclarationEntry).getType().(TypedefType).getBaseType() + ) or ( + result = src.(TypeDeclarationEntry).getDeclaration().(Enum).getExplicitUnderlyingType() + ) or ( + result = src.(SizeofTypeOperator).getTypeOperand() + ) or exists(Function f | + funbind(src, f) and result = f.getATemplateArgument() + ) or ( + result = src.(NewExpr).getType() and not result.(Class).hasConstructor() + ) or ( + result = src.(NewArrayExpr).getType() and not result.(ArrayType).getBaseType().(Class).hasConstructor() + ) or ( + result = src.(DeleteExpr).getExpr().getType() and not result.(PointerType).getBaseType().(Class).hasDestructor() + ) or ( + result = src.(DeleteArrayExpr).getExpr().getType() and not result.(PointerType).getBaseType().(Class).hasDestructor() + ) +} + +/** + * A dependency caused by a variable use. + */ +private +predicate dependency_variableUse(VariableAccess src, Variable dest) { + src.getTarget() = dest and + not dest instanceof LocalScopeVariable +} + +/** + * A dependency caused by an enum constant use. + */ +private +predicate dependency_enumConstantUse(EnumConstantAccess src, EnumConstant dest) { + src.getTarget() = dest +} + +/** + * A dependency caused by a macro access. + */ +private +predicate dependency_macroUse(MacroAccess src, Macro dest) { + src.getMacro() = dest +} + +/** + * A dependency caused by a 'using' declaration 'using X::Y'. + */ +private +predicate dependency_usingDeclaration(UsingDeclarationEntry src, Declaration dest) { + src.getDeclaration() = dest +} + +/** + * A dependency caused by a 'using' directive 'using namespace X'. + */ +private +predicate dependency_usingNamespace(UsingDirectiveEntry src, NamespaceDeclarationEntry dest) { + exists(Namespace nsdecl | + nsdecl = src.getNamespace() and + dest.getNamespace() = nsdecl and + dest.getFile().getAnIncludedFile*() = src.getFile() and + ( + dest.getFile() = src.getFile() implies + dest.getLocation().getStartLine() < src.getLocation().getStartLine() + ) and + none() // temporarily disabled until we have suitable UI in Architect + ) +} + +/** + * A dependency from the definition of a class member to a corresponding declaration. This + * ensures that an externally defined class member has a dependency on (something in) the + * class definition. + */ +private +predicate dependency_outOfLineDeclaration(DeclarationEntry src, DeclarationEntry dest) { + src.getDeclaration().hasDeclaringType() and + src.isDefinition() and + ( + dest.getDeclaration() = src.getDeclaration() or + + // also permit out of line declarations to jump from the declaration of a specialized + // function to it's definition in the primary template. Note that the specialization + // in this case may be on a template class parameter. + src.getDeclaration().(Function).isConstructedFrom(dest.getDeclaration()) + ) and + not dest.isDefinition() +} + +/** + * A dependency from an initialization of a (static) class member to a corresponding + * declaration. + */ +private +predicate dependency_outOfLineInitializer(Initializer src, DeclarationEntry dest) { + src.getDeclaration().hasDeclaringType() and + dest.getDeclaration() = src.getDeclaration() and + not dest.isDefinition() +} + +/** + * A dependency from a template function specialization to the general one. + */ +private +predicate dependency_functionSpecialization(DeclarationEntry src, DeclarationEntry dest) { + exists(FunctionTemplateSpecialization fts | + src.getDeclaration() = fts and + dest.getDeclaration() = fts.getPrimaryTemplate() + ) +} + +/** + * A dependency from a template class specialization to the most general one. + */ +private +predicate dependency_classSpecialization(DeclarationEntry src, DeclarationEntry dest) { + exists(ClassTemplateSpecialization cts | + src.getDeclaration() = cts and + dest.getDeclaration() = cts.getPrimaryTemplate() + ) +} diff --git a/cpp/ql/src/semmle/code/cpp/commons/Environment.qll b/cpp/ql/src/semmle/code/cpp/commons/Environment.qll new file mode 100644 index 000000000000..d882f6f6e18e --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/commons/Environment.qll @@ -0,0 +1,44 @@ +/** + * Reading from the environment, for example with 'getenv'. + */ + +import cpp + +/** + * An expression that reads from an environment variable. + */ +class EnvironmentRead extends Expr { + EnvironmentRead() { + readsEnvironment(this, _) + } + + /** + * The name of the environment variable. + */ + string getEnvironmentVariable() { + // Conveniently, it's always the first argument to the call + this.(Call).getArgument(0).(TextLiteral).getValue() = result + } + + /** + * A very short description of the source, suitable for use in + * an error message. + */ + string getSourceDescription() { + readsEnvironment(this, result) + } +} + +private predicate readsEnvironment(Expr read, string sourceDescription) { + exists(FunctionCall call, string name | + read = call and + call.getTarget().hasGlobalName(name) and + (name = "getenv" or name = "secure_getenv" or name = "_wgetenv") and + sourceDescription = name) or + exists(MessageExpr getObjectKey, MessageExpr getEnviron | + read = getObjectKey and + getObjectKey.getTarget().getQualifiedName().matches("NSDictionary%::-objectForKey:") and + getObjectKey.getQualifier() = getEnviron and + getEnviron.getTarget().getQualifiedName().matches("NSProcessInfo%:-environment") and + sourceDescription = "NSProcessInfo") +} diff --git a/cpp/ql/src/semmle/code/cpp/commons/File.qll b/cpp/ql/src/semmle/code/cpp/commons/File.qll new file mode 100644 index 000000000000..602eccb33b05 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/commons/File.qll @@ -0,0 +1,42 @@ +import cpp + +/** + * A call to a library function that opens a file. + */ +predicate fopenCall(FunctionCall fc) +{ + exists(Function f | f = fc.getTarget() | + f.hasQualifiedName("fopen") or + f.hasQualifiedName("open") or + f.hasQualifiedName("_open") or + f.hasQualifiedName("_wopen") or + f.hasQualifiedName("CreateFile") or + f.hasQualifiedName("CreateFileA") or + f.hasQualifiedName("CreateFileW") or + f.hasQualifiedName("CreateFileTransacted") or + f.hasQualifiedName("CreateFileTransactedA") or + f.hasQualifiedName("CreateFileTransactedW") + ) +} + +/** + * A call to a library function that closes a file. + */ +predicate fcloseCall(FunctionCall fc, Expr closed) +{ + exists(Function f | f = fc.getTarget() | + ( + f.hasQualifiedName("fclose") and + closed = fc.getArgument(0) + ) or ( + f.hasQualifiedName("close") and + closed = fc.getArgument(0) + ) or ( + f.hasQualifiedName("_close") and + closed = fc.getArgument(0) + ) or ( + f.hasQualifiedName("CloseHandle") and + closed = fc.getArgument(0) + ) + ) +} \ No newline at end of file diff --git a/cpp/ql/src/semmle/code/cpp/commons/NULL.qll b/cpp/ql/src/semmle/code/cpp/commons/NULL.qll new file mode 100644 index 000000000000..0204d752ecc7 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/commons/NULL.qll @@ -0,0 +1,15 @@ +import semmle.code.cpp.Macro + +/** A macro defining NULL. */ +class NULLMacro extends Macro { + NULLMacro() { + this.getHead() = "NULL" + } +} + +/** A use of the NULL macro. */ +class NULL extends Literal { + NULL() { + exists(NULLMacro nm | this = nm.getAnInvocation().getAnExpandedElement()) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/commons/NullTermination.qll b/cpp/ql/src/semmle/code/cpp/commons/NullTermination.qll new file mode 100644 index 000000000000..7b27ea913a53 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/commons/NullTermination.qll @@ -0,0 +1,124 @@ +import cpp +private import semmle.code.cpp.models.interfaces.ArrayFunction +private import semmle.code.cpp.models.implementations.Strcat + +private predicate mayAddNullTerminatorHelper(Expr e, VariableAccess va, Expr e0) { + exists(LocalScopeVariable v0, Expr val | + exprDefinition(v0, e, val) and + val.getAChild*() = va and + mayAddNullTerminator(e0, v0.getAnAccess()) + ) +} + +/** + * Holds if the expression `e` may add a null terminator to the string in + * variable `v`. + */ +predicate mayAddNullTerminator(Expr e, VariableAccess va) { + // Assignment: dereferencing or array access + exists(AssignExpr ae | + e = ae | + ( + // *v = x, *v++ = x, etc. + ae.getLValue().(PointerDereferenceExpr).getOperand().getAChild*() = va + or + // v[x] = y + ae.getLValue().(ArrayExpr).getArrayBase() = va + ) + and + // Rule out assignments where the assigned value is a non-zero constant + not ae.getRValue().getFullyConverted().getValue().toInt() != 0 + ) + or + // Assignment to another stack variable + exists(Expr e0, BasicBlock bb, int pos, BasicBlock bb0, int pos0 | + mayAddNullTerminatorHelper(e, va, e0) and + bb.getNode(pos) = e and + bb0.getNode(pos0) = e0 | + bb = bb0 and pos < pos0 + or + bb.getASuccessor+() = bb0 + ) + // Assignment to non-stack variable + or + exists(AssignExpr ae | + e = ae | + not ae.getLValue().(VariableAccess).getTarget() instanceof LocalScopeVariable and + ae.getRValue().getAChild*() = va + ) + or + // Function call: library function, varargs function, function + // containing assembler code, or function where the relevant + // parameter is potentially added a null terminator. + exists(Call c, Function f, int i | + e = c and + f = c.getTarget() and + not functionArgumentMustBeNullTerminated(f, i) and + c.getAnArgumentSubExpr(i) = va | + not f.hasEntryPoint() and not functionArgumentMustBeNullTerminated(f, i) or + mayAddNullTerminator(_, f.getParameter(i).getAnAccess()) or + f.isVarargs() and i >= f.getNumberOfParameters() or + exists(AsmStmt s | s.getEnclosingFunction() = f) + ) + or + // Call without target (e.g., function pointer call) + exists(Call c | + e = c and + not exists(c.getTarget()) and + c.getAnArgumentSubExpr(_) = va + ) +} + +/** + * Holds if `f` is a (library) function whose `i`th argument must be null + * terminated. + */ +predicate functionArgumentMustBeNullTerminated(Function f, int i) { + ( + f.(ArrayFunction).hasArrayWithNullTerminator(i) and + f.(ArrayFunction).hasArrayInput(i) + ) + or + f instanceof StrcatFunction and i = 0 + or + f.hasName("strlen") and i = 0 + or + f.hasName("strcmp") and i in [0 .. 1] + or + f.hasName("strchr") and i = 0 + or + f.hasName("strstr") and i in [0 .. 1] +} + +/** + * Holds if `va` is a variable access where the contents must be null terminated. + */ +predicate variableMustBeNullTerminated(VariableAccess va) { + exists(FunctionCall fc | + // Call to a function that requires null termination + exists(int i | + functionArgumentMustBeNullTerminated(fc.getTarget(), i) and + fc.getArgument(i) = va + ) + or + // Call to a wrapper function that requires null termination + // (not itself adding a null terminator) + exists(Function wrapper, int i, Parameter p, VariableAccess use | + fc.getTarget() = wrapper and + fc.getArgument(i) = va and + p = wrapper.getParameter(i) and + parameterUsePair(p, use) and + variableMustBeNullTerminated(use) and + // Simplified: check that `p` may not be null terminated on *any* + // path to `use` (including the one found via `parameterUsePair`) + not exists(Expr e, BasicBlock bb1, int pos1, BasicBlock bb2, int pos2 | + mayAddNullTerminator(e, p.getAnAccess()) and + bb1.getNode(pos1) = e and + bb2.getNode(pos2) = use | + bb1 = bb2 and pos1 < pos2 + or + bb1.getASuccessor+() = bb2 + ) + ) + ) +} diff --git a/cpp/ql/src/semmle/code/cpp/commons/PolymorphicClass.qll b/cpp/ql/src/semmle/code/cpp/commons/PolymorphicClass.qll new file mode 100644 index 000000000000..46157ed54b97 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/commons/PolymorphicClass.qll @@ -0,0 +1,10 @@ +import cpp + +/** + * A C++ class or structure which (possibly by inheritance) has at least one virtual method. + */ +class PolymorphicClass extends Class { + PolymorphicClass() { + exists(MemberFunction f | this.getABaseClass*() = f.getDeclaringType() and f.isVirtual()) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/commons/Printf.qll b/cpp/ql/src/semmle/code/cpp/commons/Printf.qll new file mode 100644 index 000000000000..5125c972f4bb --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/commons/Printf.qll @@ -0,0 +1,1007 @@ +/** + * A library for dealing with `printf`-like formatting strings. + */ +import semmle.code.cpp.Type +import semmle.code.cpp.commons.CommonType +import semmle.code.cpp.commons.StringAnalysis +import semmle.code.cpp.models.interfaces.FormattingFunction +import semmle.code.cpp.models.implementations.Printf + +/** + * A function that can be identified as a `printf` style formatting + * function by it's use of the GNU `format` attribute. + */ +class AttributeFormattingFunction extends FormattingFunction { + FormatAttribute printf_attrib; + + AttributeFormattingFunction() { + printf_attrib = getAnAttribute() and + ( + printf_attrib.getArchetype() = "printf" or + printf_attrib.getArchetype() = "__printf__" + ) and + exists(printf_attrib.getFirstFormatArgIndex()) // exclude `vprintf` style format functions + } + + override int getFormatParameterIndex() { + result = printf_attrib.getFormatIndex() + } +} + +/** + * A standard function such as `vprintf` that has a format parameter + * and a variable argument list of type `va_arg`. + */ +predicate primitiveVariadicFormatter(TopLevelFunction f, int formatParamIndex, boolean wide) { + f.getName().regexpMatch("_?_?va?[fs]?n?w?printf(_s)?(_p)?(_l)?") + and ( + if f.getName().matches("%\\_l") + then formatParamIndex = f.getNumberOfParameters() - 3 + else formatParamIndex = f.getNumberOfParameters() - 2 + ) and if f.getName().matches("%w%") then wide = true else wide = false +} + +private +predicate callsVariadicFormatter(Function f, int formatParamIndex, boolean wide) { + exists(FunctionCall fc, int i | + variadicFormatter(fc.getTarget(), i, wide) + and fc.getEnclosingFunction() = f + and fc.getArgument(i) = f.getParameter(formatParamIndex).getAnAccess() + ) +} + +/** + * 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`. + */ +predicate variadicFormatter(Function f, int formatParamIndex, boolean wide) { + primitiveVariadicFormatter(f, formatParamIndex, wide) + or ( + not f.isVarargs() + and callsVariadicFormatter(f, formatParamIndex, wide) + ) +} + +/** + * A function not in the standard library which takes a `printf`-like formatting + * string and a variable number of arguments. + */ +class UserDefinedFormattingFunction extends FormattingFunction { + UserDefinedFormattingFunction() { + isVarargs() and callsVariadicFormatter(this, _, _) + } + + override int getFormatParameterIndex() { callsVariadicFormatter(this, result, _) } + + override predicate isWideCharDefault() { callsVariadicFormatter(this, _, true) } +} + +/** + * The Objective C method `stringWithFormat:`. + */ +class NsstringStringWithFormat extends FormattingFunction { + NsstringStringWithFormat() { + getQualifiedName().matches("NSString%::+stringWithFormat:") or + getQualifiedName().matches("NSString%::+localizedStringWithFormat:") + } + + override int getFormatParameterIndex() { + result = 0 + } +} + +/** + * A call to one of the formatting functions. + */ +class FormattingFunctionCall extends Expr { + FormattingFunctionCall() { + this.(Call).getTarget() instanceof FormattingFunction + } + + /** + * Gets the formatting function being called. + */ + FormattingFunction getTarget() { + result = this.(Call).getTarget() + } + + /** + * Gets the `i`th argument for this call. + * + * The range of `i` is from `0` to `getNumberOfArguments() - 1`. + */ + Expr getArgument(int i) { + result = this.(Call).getArgument(i) + } + + /** + * Gets the number of actual parameters in this call; use + * `getArgument(i)` with `i` between `0` and `result - 1` to + * retrieve actuals. + */ + int getNumberOfArguments() { + result = this.(Call).getNumberOfArguments() + } + + /** + * Gets the index at which the format string occurs in the argument list. + */ + int getFormatParameterIndex() { + result = this.getTarget().(FormattingFunction).getFormatParameterIndex() + } + + /** + * Gets the format expression used in this call. + */ + Expr getFormat() { + result = this.getArgument(this.getFormatParameterIndex()) + } + + /** + * Gets the nth argument to the format (including width and precision arguments). + */ + Expr getFormatArgument(int n) { + exists(int i | + result = this.getArgument(i) + and n >= 0 + and n = i - getTarget().(FormattingFunction).getFirstFormatArgumentIndex() + ) + } + + /** + * Gets the argument corresponding to the nth conversion specifier + */ + Expr getConversionArgument(int n) { + exists(FormatLiteral fl, int b, int o | + fl = this.getFormat() and + b = sum(int i, int toSum | (i < n) and (toSum = fl.getNumArgNeeded(i)) | toSum) and + o = fl.getNumArgNeeded(n) and + o > 0 and + result = this.getFormatArgument(b+o-1)) + } + + /** + * Gets the argument corresponding to the nth conversion specifier's + * minimum field width (has no result if that conversion specifier has + * an explicit minimum field width). + */ + Expr getMinFieldWidthArgument(int n) { + exists(FormatLiteral fl, int b | + fl = this.getFormat() and + b = sum(int i, int toSum | (i < n) and (toSum = fl.getNumArgNeeded(i)) | toSum) and + fl.hasImplicitMinFieldWidth(n) and + result = this.getFormatArgument(b)) + } + + /** + * Gets the argument corresponding to the nth conversion specifier's + * precision (has no result if that conversion specifier has an explicit + * precision). + */ + Expr getPrecisionArgument(int n) { + exists(FormatLiteral fl, int b, int o | + fl = this.getFormat() and + b = sum(int i, int toSum | (i < n) and (toSum = fl.getNumArgNeeded(i)) | toSum) and + (if fl.hasImplicitMinFieldWidth(n) then o=1 else o=0) and + fl.hasImplicitPrecision(n) and + result = this.getFormatArgument(b+o)) + } + + /** + * Gets the number of arguments to this call that are parameters to the + * format string. + */ + int getNumFormatArgument() { + result = count(this.getFormatArgument(_)) + } +} + +/** + * A class to represent format strings that occur as arguments to invocations of formatting functions. + */ +class FormatLiteral extends Literal { + FormatLiteral() { + exists(FormattingFunctionCall ffc | ffc.getFormat() = this) and + this instanceof StringLiteral + } + + /** + * Gets the function call where this format string is used. + */ + FormattingFunctionCall getUse() { + result.getFormat() = this + } + + /** + * Holds if the default meaning of `%s` is a `wchar_t *`, rather than + * a `char *` (either way, `%S` will have the opposite meaning). + */ + predicate isWideCharDefault() { + getUse().getTarget().(FormattingFunction).isWideCharDefault() + } + + /** + * Holds if this `FormatLiteral` is in a context that supports + * Microsoft rules and extensions. + */ + predicate isMicrosoft() { + getFile().compiledAsMicrosoft() + } + + /** + * Gets the format string, with '%%' replaced by '_' (to avoid processing + * '%%' as a format specifier). + */ + string getFormat() { + result = this.getValue().replaceAll("%%", "_") + } + + /** + * Gets the number of conversion specifiers (not counting `%%`) + */ + int getNumConvSpec() { + result = count(this.getFormat().indexOf("%")) + } + + /** + * Gets the position in the string at which the nth conversion specifier + * starts. + */ + int getConvSpecOffset(int n) { + n = 0 and result = this.getFormat().indexOf("%", 0, 0) + or n > 0 and exists(int p | n = p + 1 and result = this.getFormat().indexOf("%", 0, this.getConvSpecOffset(p)+2)) + } + + // these predicates gets a regular expressions to match each individual + // parts of a conversion specifier. + private string getParameterFieldRegexp() { + // the parameter field is a posix extension, for example `%5$i` uses the fifth + // parameter as an integer, regardless of the position of this substring in the + // format string. + result = "(?:[1-9][0-9]*\\$)?" + } + private string getFlagRegexp() { + if isMicrosoft() then ( + result = "[-+ #0']*" + ) else ( + result = "[-+ #0'I]*" + ) + } + private string getFieldWidthRegexp() { + result = "(?:[1-9][0-9]*|\\*|\\*[0-9]+\\$)?" + } + private string getPrecRegexp() { + result = "(?:\\.(?:[0-9]*|\\*|\\*[0-9]+\\$))?" + } + private string getLengthRegexp() { + if isMicrosoft() then ( + result = "(?:hh?|ll?|L|q|j|z|t|w|I32|I64|I)?" + ) else ( + result = "(?:hh?|ll?|L|q|j|z|Z|t)?" + ) + } + private string getConvCharRegexp() { + if isMicrosoft() then ( + result = "[aAcCdeEfFgGimnopsSuxXZ@]" + ) else ( + result = "[aAcCdeEfFgGimnopsSuxX@]" + ) + } + + /** + * Gets a regular expression used for matching a whole conversion specifier. + */ + string getConvSpecRegexp() { + // capture groups: 1 - entire conversion spec, including "%" + // 2 - parameters + // 3 - flags + // 4 - minimum field width + // 5 - precision + // 6 - length + // 7 - conversion character + // NB: this matches "%%" with conversion character "%" + result = + "(?s)(\\%("+ + this.getParameterFieldRegexp()+")(" + + this.getFlagRegexp()+")("+ + this.getFieldWidthRegexp()+")("+ + this.getPrecRegexp()+")("+ + this.getLengthRegexp()+")("+ + this.getConvCharRegexp()+")"+ + "|\\%\\%).*" + } + + /** + * Holds if the arguments are a parsing of a conversion specifier to this format string. + * @param n which conversion specifier to parse + * @param spec the whole conversion specifier + * @param params the parameter field of the conversion specifier (empty string if none is given) + * @param flags the flags of the conversion specifier (empty string if none are given) + * @param width the minimum field width of the conversion specifier (empty string if none is given) + * @param prec the precision of the conversion specifier (empty string if none is given) + * @param len the length flag of the conversion specifier (empty string if none is given) + * @param conv the conversion character of the conversion specifier ("%" for specifier "%%") + */ + predicate parseConvSpec(int n, string spec, string params, string flags, string width, string prec, string len, string conv) { + exists(int offset, string fmt, string rst, string regexp | + offset = this.getConvSpecOffset(n) and + fmt = this.getFormat() and + rst = fmt.substring(offset, fmt.length()) and + regexp = this.getConvSpecRegexp() and + ((spec = rst.regexpCapture(regexp, 1) and + params = rst.regexpCapture(regexp, 2) and + flags = rst.regexpCapture(regexp, 3) and + width = rst.regexpCapture(regexp, 4) and + prec = rst.regexpCapture(regexp, 5) and + len = rst.regexpCapture(regexp, 6) and + conv = rst.regexpCapture(regexp, 7)) + or + (spec = rst.regexpCapture(regexp, 1) and + not exists(rst.regexpCapture(regexp, 2)) and + params = "" and + flags = "" and + width = "" and + prec = "" and + len = "" and + conv = "%"))) + } + + /** + * Gets the nth conversion specifier (including the initial `%`). + */ + string getConvSpec(int n) { + exists(int offset, string fmt, string rst, string regexp | + offset = this.getConvSpecOffset(n) and + fmt = this.getFormat() and + rst = fmt.substring(offset, fmt.length()) and + regexp = this.getConvSpecRegexp() and + result = rst.regexpCapture(regexp, 1)) + } + + /** + * Gets the parameter field of the nth conversion specifier (for example, `1$`). + */ + string getParameterField(int n) { + this.parseConvSpec(n, _, result, _, _, _, _, _) + } + + /** + * Gets the flags of the nth conversion specifier. + */ + string getFlags(int n) { + this.parseConvSpec(n, _, _, result, _, _, _, _) + } + + /** + * Holds if the nth conversion specifier has alternate flag ("#"). + */ + predicate hasAlternateFlag(int n) { this.getFlags(n).matches("%#%") } + + /** + * Holds if the nth conversion specifier has zero padding flag ("0"). + */ + predicate isZeroPadded(int n) { this.getFlags(n).matches("%0%") and not this.isLeftAdjusted(n) } + + /** + * Holds if the nth conversion specifier has flag left adjustment flag + * ("-"). Note that this overrides the zero padding flag. + */ + predicate isLeftAdjusted(int n) { this.getFlags(n).matches("%-%") } + + /** + * Holds if the nth conversion specifier has the blank flag (" "). + */ + predicate hasBlank(int n) { this.getFlags(n).matches("% %") } + + /** + * Holds if the nth conversion specifier has the explicit sign flag ("+"). + */ + predicate hasSign(int n) { this.getFlags(n).matches("%+%") } + + /** + * Holds if the nth conversion specifier has the thousands grouping flag ("'"). + */ + predicate hasThousandsGrouping(int n) { this.getFlags(n).matches("%'%") } + + /** + * Holds if the nth conversion specifier has the alternative digits flag ("I"). + */ + predicate hasAlternativeDigits(int n) { this.getFlags(n).matches("%I%") } + + /** + * Gets the minimum field width of the nth conversion specifier + * (empty string if none is given). + */ + string getMinFieldWidthOpt(int n) { this.parseConvSpec(n, _, _, _, result, _, _, _) } + + /** + * Holds if the nth conversion specifier has a minimum field width. + */ + predicate hasMinFieldWidth(int n) { this.getMinFieldWidthOpt(n) != "" } + + /** + * Holds if the nth conversion specifier has an explicitly given minimum + * field width. + */ + predicate hasExplicitMinFieldWidth(int n) { this.getMinFieldWidthOpt(n).regexpMatch("[0-9]+") } + + /** + * Holds if the nth conversion specifier has an implicitly given minimum + * field width (either "*" or "*i$" for some number i). + */ + predicate hasImplicitMinFieldWidth(int n) { this.getMinFieldWidthOpt(n).regexpMatch("\\*.*") } + + /** + * Gets the minimum field width of the nth conversion specifier. + */ + int getMinFieldWidth(int n) { result = this.getMinFieldWidthOpt(n).toInt() } + + /** + * Gets the precision of the nth conversion specifier (empty string if none is given). + */ + string getPrecisionOpt(int n) { this.parseConvSpec(n, _, _, _, _, result, _, _) } + + /** + * Holds if the nth conversion specifier has a precision. + */ + predicate hasPrecision(int n) { this.getPrecisionOpt(n) != "" } + + /** + * Holds if the nth conversion specifier has an explicitly given precision. + */ + predicate hasExplicitPrecision(int n) { this.getPrecisionOpt(n).regexpMatch("\\.[0-9]*") } + + /** + * Holds if the nth conversion specifier has an implicitly given precision + * (either "*" or "*i$" for some number i). + */ + predicate hasImplicitPrecision(int n) { this.getPrecisionOpt(n).regexpMatch("\\.\\*.*") } + + /** + * Gets the precision of the nth conversion specifier. + */ + int getPrecision(int n) { + if this.getPrecisionOpt(n) = "." then + result = 0 + else + result = this.getPrecisionOpt(n).regexpCapture("\\.([0-9]*)", 1).toInt() + } + + /** + * Gets the length flag of the nth conversion specifier. + */ + string getLength(int n) { this.parseConvSpec(n, _, _, _, _, _, result, _) } + + /** + * Gets the conversion character of the nth conversion specifier. + */ + string getConversionChar(int n) { this.parseConvSpec(n, _, _, _, _, _, _, result) } + + /** + * Gets the size of pointers in the target this formatting function is + * compiled for. + */ + private int targetBitSize() { result = this.getFullyConverted().getType().getSize() } + + private LongType getLongType() { + ( + this.targetBitSize() = 4 and result.getSize() = min(LongType l | | l.getSize()) + ) or ( + this.targetBitSize() = 8 and result.getSize() = max(LongType l | | l.getSize()) + ) or ( + this.targetBitSize() != 4 and + this.targetBitSize() != 8 + ) + } + + private Intmax_t getIntmax_t() { + ( + this.targetBitSize() = 4 and result.getSize() = min(Intmax_t l | | l.getSize()) + ) or ( + this.targetBitSize() = 8 and result.getSize() = max(Intmax_t l | | l.getSize()) + ) or ( + this.targetBitSize() != 4 and + this.targetBitSize() != 8 + ) + } + + private Size_t getSize_t() { + ( + this.targetBitSize() = 4 and result.getSize() = min(Size_t l | | l.getSize()) + ) or ( + this.targetBitSize() = 8 and result.getSize() = max(Size_t l | | l.getSize()) + ) or ( + this.targetBitSize() != 4 and + this.targetBitSize() != 8 + ) + } + + private Ssize_t getSsize_t() { + ( + this.targetBitSize() = 4 and result.getSize() = min(Ssize_t l | | l.getSize()) + ) or ( + this.targetBitSize() = 8 and result.getSize() = max(Ssize_t l | | l.getSize()) + ) or ( + this.targetBitSize() != 4 and + this.targetBitSize() != 8 + ) + } + + private Ptrdiff_t getPtrdiff_t() { + ( + this.targetBitSize() = 4 and result.getSize() = min(Ptrdiff_t l | | l.getSize()) + ) or ( + this.targetBitSize() = 8 and result.getSize() = max(Ptrdiff_t l | | l.getSize()) + ) or ( + this.targetBitSize() != 4 and + this.targetBitSize() != 8 + ) + } + + /** + * Gets the family of integral types required by the nth conversion + * specifier's length flag. + */ + Type getIntegralConversion(int n) { + exists(string len | len = this.getLength(n) and + ((len="hh" and result instanceof IntType) + or (len="h" and result instanceof IntType) + or (len="l" and result = this.getLongType()) + or ((len="ll" or len="q") + and result instanceof LongLongType) + or (len="L" and result instanceof IntType) // doesn't affect integral conversion + or (len="j" and result = this.getIntmax_t()) + or ((len="z" or len="Z") + and (result = this.getSize_t() or result = this.getSsize_t())) + or (len="t" and result = this.getPtrdiff_t()) + or (len="I" and result instanceof IntType) + or (len="I32" and exists(MicrosoftInt32Type t | + t.getUnsigned() = result.(IntegralType).getUnsigned() + )) + or (len="I64" and exists(MicrosoftInt64Type t | + t.getUnsigned() = result.(IntegralType).getUnsigned() + )) + or (len="" and result instanceof IntType) + )) + } + + /** + * Gets the family of integral types output / displayed by the nth + * conversion specifier's length flag. + */ + Type getIntegralDisplayType(int n) { + exists(string len | len = this.getLength(n) and + ((len="hh" and result instanceof CharType) + or (len="h" and result instanceof ShortType) + or (len="l" and result = this.getLongType()) + or ((len="ll" or len="q") + and result instanceof LongLongType) + or (len="L" and result instanceof IntType) // doesn't affect integral conversion + or (len="j" and result = this.getIntmax_t()) + or ((len="z" or len="Z") + and (result = this.getSize_t() or result = this.getSsize_t())) + or (len="t" and result = this.getPtrdiff_t()) + or (len="I" and result instanceof IntType) + or (len="I32" and exists(MicrosoftInt32Type t | + t.getUnsigned() = result.(IntegralType).getUnsigned() + )) + or (len="I64" and exists(MicrosoftInt64Type t | + t.getUnsigned() = result.(IntegralType).getUnsigned() + )) + or (len="" and result instanceof IntType))) + } + + /** + * Gets the family of floating point types required by the nth conversion + * specifier's length flag. + */ + FloatingPointType getFloatingPointConversion(int n) { + exists(string len | len = this.getLength(n) and + if len="L" then + result instanceof LongDoubleType + else + result instanceof DoubleType) + } + + /** + * Gets the family of pointer types required by the nth conversion + * specifier's length flag. + */ + PointerType getStorePointerConversion(int n) { + exists(IntegralType base | + exists(string len | len = this.getLength(n) | + (len="hh" and base instanceof CharType) + or (len="h" and base instanceof ShortType) + or (len="l" and base = this.getLongType()) + or (len="ll" and base instanceof LongLongType) + or (len="q" and base instanceof LongLongType) + ) + and base.isSigned() and base = result.getBaseType() + ) + } + + /** + * Gets the argument type required by the nth conversion specifier. + */ + Type getConversionType(int n) { + result = getConversionType1(n) or + result = getConversionType1b(n) or + result = getConversionType2(n) or + result = getConversionType3(n) or + result = getConversionType4(n) or + result = getConversionType5(n) or + result = getConversionType6(n) or + result = getConversionType7(n) or + result = getConversionType8(n) or + result = getConversionType9(n) or + result = getConversionType10(n) + } + + private Type getConversionType1(int n) { + exists(string cnv | cnv = this.getConversionChar(n) | + cnv.regexpMatch("d|i") and result = this.getIntegralConversion(n) + and not result.getUnderlyingType().(IntegralType).isExplicitlySigned() + and not result.getUnderlyingType().(IntegralType).isExplicitlyUnsigned() + ) + } + + /** + * Gets the 'effective' char type character, that is, 'c' (meaning a `char`) or + * 'C' (meaning a `wchar_t`). + * - in the base case this is the same as the format type character. + * - for a `wprintf` or similar function call, the meanings are reversed. + * - the size prefixes 'l'/'w' (long) and 'h' (short) override the + * type character to effectively 'C' or 'c' respectively. + */ + private string getEffectiveCharConversionChar(int n) { + exists(string len, string conv | this.parseConvSpec(n, _, _, _, _, _, len, conv) and (conv = "c" or conv = "C") | + (len = "l" and result = "C") or + (len = "w" and result = "C") or + (len = "h" and result = "c") or + (len != "l" and len != "w" and len != "h" and (result = "c" or result = "C") and (if isWideCharDefault() then result != conv else result = conv)) + ) + } + + private Type getConversionType1b(int n) { + exists(string cnv | cnv = this.getEffectiveCharConversionChar(n) | + ( + cnv = "c" and + result instanceof CharType and + not result.(CharType).isExplicitlySigned() and + not result.(CharType).isExplicitlyUnsigned() + ) or ( + cnv = "C" and + isMicrosoft() and + result instanceof WideCharType + ) or ( + cnv = "C" and + not isMicrosoft() and + result.hasName("wint_t") + ) + ) + } + + private Type getConversionType2(int n) { + exists(string cnv | cnv = this.getConversionChar(n) | + cnv.regexpMatch("o|u|x|X") and + result = this.getIntegralConversion(n) and + result.getUnderlyingType().(IntegralType).isUnsigned() + ) + } + + private Type getConversionType3(int n) { + exists(string cnv | cnv = this.getConversionChar(n) | + cnv.regexpMatch("a|A|e|E|f|F|g|G") and result = this.getFloatingPointConversion(n) + ) + } + + /** + * Gets the 'effective' string type character, that is, 's' (meaning a char string) or + * 'S' (meaning a wide string). + * - in the base case this is the same as the format type character. + * - for a `wprintf` or similar function call, the meanings are reversed. + * - the size prefixes 'l'/'w' (long) and 'h' (short) override the + * type character to effectively 'S' or 's' respectively. + */ + private string getEffectiveStringConversionChar(int n) { + exists(string len, string conv | this.parseConvSpec(n, _, _, _, _, _, len, conv) and (conv = "s" or conv = "S") | + (len = "l" and result = "S") or + (len = "w" and result = "S") or + (len = "h" and result = "s") or + (len != "l" and len != "w" and len != "h" and (result = "s" or result = "S") and (if isWideCharDefault() then result != conv else result = conv)) + ) + } + + private Type getConversionType4(int n) { + exists(string cnv, CharType t | cnv = this.getEffectiveStringConversionChar(n) | + cnv="s" and t = result.(PointerType).getBaseType() + and not t.isExplicitlySigned() + and not t.isExplicitlyUnsigned() + ) + } + + private Type getConversionType5(int n) { + exists(string cnv | cnv = this.getEffectiveStringConversionChar(n) | + cnv="S" and result.(PointerType).getBaseType().hasName("wchar_t") + ) + } + + private Type getConversionType6(int n) { + exists(string cnv | cnv = this.getConversionChar(n) | + cnv="p" and result instanceof VoidPointerType + ) + } + + private Type getConversionType7(int n) { + exists(string cnv | cnv = this.getConversionChar(n) | + cnv="n" and result = this.getStorePointerConversion(n) + ) + } + + private IntPointerType getConversionType8(int n) { + exists(string cnv | cnv = this.getConversionChar(n) | + cnv="n" and not exists(this.getStorePointerConversion(n)) and + result.getBaseType().(IntType).isSigned() and + not result.getBaseType().(IntType).isExplicitlySigned() + ) + } + + private Type getConversionType9(int n) { + this.getConversionChar(n) = "Z" and + ( + this.getLength(n) = "l" or + this.getLength(n) = "w" + ) and exists(Type t | + t.getName() = "UNICODE_STRING" and + result.(PointerType).getBaseType() = t + ) + } + + private Type getConversionType10(int n) { + this.getConversionChar(n) = "Z" and + not this.getLength(n) = "l" and + not this.getLength(n) = "w" and + exists(Type t | + t.getName() = "ANSI_STRING" and + result.(PointerType).getBaseType() = t + ) + } + + /** + * Gets the number of arguments required by the nth conversion specifier + * of this format string. + */ + int getNumArgNeeded(int n) { + exists(this.getConvSpecOffset(n)) and + not this.getConversionChar(n) = "%" and + exists(int n1, int n2, int n3 | + (if this.hasImplicitMinFieldWidth(n) then n1=1 else n1=0) and + (if this.hasImplicitPrecision(n) then n2=1 else n2=0) and + (if this.getConversionChar(n)="m" then n3=0 else n3=1) and + result = n1 + n2 + n3) + } + + /** + * Gets the number of arguments needed by this format string. + */ + int getNumArgNeeded() { + if this.getParameterField(_) != "" then ( + // At least one conversion specifier has a parameter field, in which case, + // they all should have. + result = max(string s | this.getParameterField(_) = s + "$" | s.toInt()) + ) else ( + result = sum(int n, int toSum | (toSum = this.getNumArgNeeded(n)) | toSum) + ) + } + + /** + * Holds if all conversion specifiers of this format string have been + * parsed by the library (this does not hold if one or more unrecognized + * format specifiers are present in the format string). + */ + predicate specsAreKnown() { + this.getNumConvSpec() = count(int n | exists(this.getNumArgNeeded(n))) + } + + /** + * Gets the maximum length of the string that can be produced by the nth + * conversion specifier of this format string; has no result if this cannot + * be determined. + */ + int getMaxConvertedLength(int n) { + exists(int len | + (if this.hasExplicitMinFieldWidth(n) then + result = len.maximum(this.getMinFieldWidth(n)) + else + not this.hasImplicitMinFieldWidth(n) and result = len) + and + (this.getConversionChar(n)="%" and + len = 1 + or ( + this.getConversionChar(n).toLowerCase()="c" and + if (this.getEffectiveCharConversionChar(n)="C" and + not isMicrosoft() and + not isWideCharDefault()) then ( + len = 6 // MB_LEN_MAX + // the wint_t (wide character) argument is converted + // to a multibyte sequence by a call to the wcrtomb(3) + ) else ( + len = 1 // e.g. 'a' + ) + ) or this.getConversionChar(n).toLowerCase()="f" and + exists(int dot, int afterdot | + (if this.getPrecision(n) = 0 then dot = 0 else dot = 1) + and + (if this.hasExplicitPrecision(n) then + afterdot = this.getPrecision(n) + else + not this.hasImplicitPrecision(n) and afterdot = 6) + and + len = 1 + 309 + dot + afterdot) // e.g. -1e308="-100000"... + or this.getConversionChar(n).toLowerCase()="e" and + exists(int dot, int afterdot | + (if this.getPrecision(n) = 0 then dot = 0 else dot = 1) + and + (if this.hasExplicitPrecision(n) then + afterdot = this.getPrecision(n) + else + not this.hasImplicitPrecision(n) and afterdot = 6) + and + len = 1 + 1 + dot + afterdot + 1 + 1 + 3) // -1e308="-1.000000e+308" + or this.getConversionChar(n).toLowerCase()="g" and + exists(int dot, int afterdot | + (if this.getPrecision(n) = 0 then dot = 0 else dot = 1) + and + (if this.hasExplicitPrecision(n) then + afterdot = this.getPrecision(n) + else + not this.hasImplicitPrecision(n) and afterdot = 6) + and + // note: this could be displayed in the style %e or %f; + // however %f is only used when 'P > X >= -4' + // where P is the precision (default 6, minimum 1) + // and X is the exponent + // using a precision of P - 1 - X (i.e. to P significant figures) + // in other words the number is displayed to P significant figures if that is enough to express + // it with no trailing zeroes and at most four leading zeroes beyond the significant figures + // (e.g. 123456, 0.000123456 are just OK) + // so case %f can be at most P characters + 4 zeroes, sign, dot = P + 6 + 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 + // e.g. -2^31 = "-2147483648" + exists(int sizeBits | + sizeBits = min(int bits | + bits = getIntegralDisplayType(n).getSize() * 8 or + exists(IntegralType t | t = getUse().getConversionArgument(n).getType().getUnderlyingType() | t.isSigned() and bits = t.getSize() * 8) + ) and + len = 1 + ((sizeBits - 1) / 10.0.log2()).ceil() + // this calculation is as %u (below) only we take out the sign bit (- 1) and allow a whole + // character for it to be expressed as '-'. + ) + or this.getConversionChar(n).toLowerCase()="u" and + // e.g. 2^32 - 1 = "4294967295" + exists(int sizeBits | + sizeBits = min(int bits | + bits = getIntegralDisplayType(n).getSize() * 8 or + exists(IntegralType t | t = getUse().getConversionArgument(n).getType().getUnderlyingType() | t.isUnsigned() and bits = t.getSize() * 8) + ) and + len = (sizeBits / 10.0.log2()).ceil() + // convert the size from bits to decimal characters, and round up as you can't have + // fractional characters (10.0.log2() is the number of bits expressed per decimal character) + ) + or this.getConversionChar(n).toLowerCase()="x" and + // e.g. "12345678" + exists(int sizeBytes, int baseLen | + sizeBytes = min(int bytes | + bytes = getIntegralDisplayType(n).getSize() or + exists(IntegralType t | t = getUse().getConversionArgument(n).getType().getUnderlyingType() | t.isUnsigned() and bytes = t.getSize()) + ) and ( + baseLen = sizeBytes * 2 + ) and ( + if hasAlternateFlag(n) then len = 2 + baseLen else len = baseLen // "0x" + ) + ) + or this.getConversionChar(n).toLowerCase()="p" and + exists(PointerType ptrType, int baseLen | ( + ptrType = getFullyConverted().getType() + ) and ( + baseLen = max(ptrType.getSize() * 2) // e.g. "0x1234567812345678"; exact format is platform dependent + ) and ( + if hasAlternateFlag(n) then len = 2 + baseLen else len = baseLen // "0x" + ) + ) + or this.getConversionChar(n).toLowerCase()="o" and + // e.g. 2^32 - 1 = "37777777777" + exists(int sizeBits, int baseLen | + sizeBits = min(int bits | + bits = getIntegralDisplayType(n).getSize() * 8 or + exists(IntegralType t | t = getUse().getConversionArgument(n).getType().getUnderlyingType() | t.isUnsigned() and bits = t.getSize() * 8) + ) and ( + baseLen = (sizeBits / 3.0).ceil() + ) and ( + if hasAlternateFlag(n) then len = 1 + baseLen else len = baseLen // "0" + ) + ) + or this.getConversionChar(n).toLowerCase()="s" and + len = min(int v | + (v = this.getPrecision(n)) or + (v = this.getUse().getFormatArgument(n).(AnalysedString).getMaxLength() - 1) // (don't count null terminator) + ) + ) + ) + } + + /** + * Gets the maximum length of the string that can be produced by the nth + * conversion specifier of this format string, except that float to string + * conversions are assumed to be 8 characters. This is helpful for + * determining whether a buffer overflow is caused by long float to string + * conversions. + */ + int getMaxConvertedLengthLimited(int n) { + if this.getConversionChar(n).toLowerCase() = "f" then ( + result = getMaxConvertedLength(n).minimum(8) + ) else ( + result = getMaxConvertedLength(n) + ) + } + + /** + * Gets the constant part of the format string just before the nth + * conversion specifier. If `n` is zero, this will be the constant prefix + * before the 0th specifier, otherwise it is the string between the end + * of the n-1th specifier and the nth specifier. Has no result if `n` + * is negative or not strictly less than the number of conversion + * specifiers. + */ + string getConstantPart(int n) { + if n=0 then + result = this.getFormat().substring(0, this.getConvSpecOffset(0)) + else + result = this.getFormat().substring(this.getConvSpecOffset(n-1)+this.getConvSpec(n-1).length(), this.getConvSpecOffset(n)) + } + + /** + * Gets the constant part of the format string after the last conversion + * specifier. + */ + string getConstantSuffix() { + exists(int n | n = this.getNumConvSpec() and + (if n > 0 then + result = this.getFormat().substring(this.getConvSpecOffset(n-1)+this.getConvSpec(n-1).length(), this.getFormat().length()) + else + result = this.getFormat())) + } + + private int getMaxConvertedLengthAfter(int n) { + if n=this.getNumConvSpec() then + result = this.getConstantSuffix().length() + 1 + else + result = this.getConstantPart(n).length()+this.getMaxConvertedLength(n)+this.getMaxConvertedLengthAfter(n+1) + } + + private int getMaxConvertedLengthAfterLimited(int n) { + if n=this.getNumConvSpec() then + result = this.getConstantSuffix().length() + 1 + else + result = this.getConstantPart(n).length()+this.getMaxConvertedLengthLimited(n)+this.getMaxConvertedLengthAfterLimited(n+1) + } + + /** + * Gets the maximum length of the string that can be produced by this format + * string. Has no result if this cannot be determined. + */ + int getMaxConvertedLength() { + result = this.getMaxConvertedLengthAfter(0) + } + + /** + * Gets the maximum length of the string that can be produced by this format + * string, except that float to string conversions are assumed to be 8 + * characters. This is helpful for determining whether a buffer overflow + * is caused by long float to string conversions. + */ + int getMaxConvertedLengthLimited() { + result = this.getMaxConvertedLengthAfterLimited(0) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/commons/Scanf.qll b/cpp/ql/src/semmle/code/cpp/commons/Scanf.qll new file mode 100644 index 000000000000..6c1c4ae6fd10 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/commons/Scanf.qll @@ -0,0 +1,280 @@ +/** + * A library for dealing with scanf-like formatting strings. This is similar to + * printf.qll but the format specification for scanf is quite different. + */ +import semmle.code.cpp.Type + +/** + * A `scanf`-like standard library function. + */ +abstract class ScanfFunction extends Function +{ + /** + * Gets the position at which the input string or stream parameter occurs, + * if this function does not read from standard input. + */ + abstract int getInputParameterIndex(); + + /** + * Gets the position at which the format parameter occurs. + */ + abstract int getFormatParameterIndex(); + + /** + * Holds if the default meaning of `%s` is a `wchar_t*` string + * (rather than a `char*`). + */ + predicate isWideCharDefault() + { + exists(getName().indexOf("wscanf")) + } +} + +/** + * The standard function `scanf` (and variations). + */ +class Scanf extends ScanfFunction +{ + Scanf() + { + this instanceof TopLevelFunction and + ( + hasName("scanf") or // scanf(format, args...) + hasName("wscanf") or // wscanf(format, args...) + hasName("_scanf_l") or // _scanf_l(format, locale, args...) + hasName("_wscanf_l") // _wscanf_l(format, locale, args...) + ) + } + + override int getInputParameterIndex() { none() } + override int getFormatParameterIndex() { result = 0 } +} + +/** + * The standard function `fscanf` (and variations). + */ +class Fscanf extends ScanfFunction +{ + Fscanf() + { + this instanceof TopLevelFunction and + ( + hasName("fscanf") or // fscanf(src_stream, format, args...) + hasName("fwscanf") or // fwscanf(src_stream, format, args...) + hasName("_fscanf_l") or // _fscanf_l(src_stream, format, locale, args...) + hasName("_fwscanf_l") // _fwscanf_l(src_stream, format, locale, args...) + ) + } + + override int getInputParameterIndex() { result = 0 } + override int getFormatParameterIndex() { result = 1 } +} + +/** + * The standard function `sscanf` (and variations). + */ +class Sscanf extends ScanfFunction +{ + Sscanf() + { + this instanceof TopLevelFunction and + ( + hasName("sscanf") or // sscanf(src_stream, format, args...) + hasName("swscanf") or // swscanf(src, format, args...) + hasName("_sscanf_l") or // _sscanf_l(src, format, locale, args...) + hasName("_swscanf_l") // _swscanf_l(src, format, locale, args...) + ) + } + + override int getInputParameterIndex() { result = 0 } + override int getFormatParameterIndex() { result = 1 } +} + +/** + * The standard(ish) function `snscanf` (and variations). + */ +class Snscanf extends ScanfFunction +{ + Snscanf() + { + this instanceof TopLevelFunction and + ( + hasName("_snscanf") or // _snscanf(src, max_amount, format, args...) + hasName("_snwscanf") // _snwscanf(src, max_amount, format, args...) + // note that the max_amount is not a limit on the output length, it's an input length + // limit used with non null-terminated strings. + ) + } + + override int getInputParameterIndex() { result = 0 } + override int getFormatParameterIndex() { result = 2 } +} + +/** + * A call to one of the `scanf` functions. + */ +class ScanfFunctionCall extends FunctionCall +{ + ScanfFunctionCall() + { + this.getTarget() instanceof ScanfFunction + } + + /** + * Gets the `scanf`-like function that is called. + */ + ScanfFunction getScanfFunction() + { + result = getTarget() + } + + /** + * Gets the position at which the input string or stream parameter occurs, + * if this function call does not read from standard input. + */ + int getInputParameterIndex() + { + result = getScanfFunction().getInputParameterIndex() + } + + /** + * Gets the position at which the format parameter occurs. + */ + int getFormatParameterIndex() + { + result = getScanfFunction().getFormatParameterIndex() + } + + /** + * Gets the format expression used in this call. + */ + Expr getFormat() + { + result = this.getArgument(this.getFormatParameterIndex()) + } + + /** + * Holds if the default meaning of `%s` is a `wchar_t*` string + * (rather than a `char*`). + */ + predicate isWideCharDefault() + { + getScanfFunction().isWideCharDefault() + } +} + +/** + * A class to represent format strings that occur as arguments to invocations of `scanf` functions. + */ +class ScanfFormatLiteral extends Expr { + ScanfFormatLiteral() { + exists(ScanfFunctionCall sfc | sfc.getFormat() = this) and + this.isConstant() + } + + /** the function call where this format string is used */ + ScanfFunctionCall getUse() { + result.getFormat() = this + } + + /** Holds if the default meaning of `%s` is a `wchar_t*` (rather than a `char*`). */ + predicate isWideCharDefault() { + getUse().getTarget().(ScanfFunction).isWideCharDefault() + } + + /** + * Gets the format string itself, transformed as follows: + * - '%%' is replaced with '_' + * (this avoids accidentally processing them as format specifiers) + * - '%*' is replaced with '_' + * (%*any is matched but not assigned to an argument) + */ + string getFormat() { + result = this.getValue().replaceAll("%%", "_").replaceAll("%*", "_") + } + + /** + * Gets the number of conversion specifiers (not counting `%%` and `%*`...). + */ + int getNumConvSpec() { + result = count(this.getFormat().indexOf("%")) + } + + /** + * Gets the position in the string at which the nth conversion specifier starts. + */ + int getConvSpecOffset(int n) { + n = 0 and result = this.getFormat().indexOf("%", 0, 0) + or n > 0 and exists(int p | n = p + 1 and result = this.getFormat().indexOf("%", 0, this.getConvSpecOffset(p) + 2)) + } + + /** + * Gets the regular expression to match each individual part of a conversion specifier. + */ + private string getMaxWidthRegexp() { result = "(?:[1-9][0-9]*)?" } + private string getLengthRegexp() { result = "(?:hh?|ll?|L|q|j|z|t)?" } + private string getConvCharRegexp() { result = "[aAcCdeEfFgGimnopsSuxX]" } + + /** + * Gets the regular expression used for matching a whole conversion specifier. + */ + string getConvSpecRegexp() { + // capture groups: 1 - entire conversion spec, including "%" + // 2 - maximum width + // 3 - length modifier + // 4 - conversion character + result = "(\\%("+this.getMaxWidthRegexp()+")("+this.getLengthRegexp()+")("+this.getConvCharRegexp()+")).*" + } + + /** + * Holds if the arguments are a parsing of a conversion specifier to this format string. + * @param n which conversion specifier to parse + * @param spec the whole conversion specifier + * @param width the maximum width option of this input (empty string if none is given) + * @param len the length flag of this input (empty string if none is given) + * @param conv the conversion character of this input + */ + predicate parseConvSpec(int n, string spec, string width, string len, string conv) { + exists(int offset, string fmt, string rst, string regexp | + offset = this.getConvSpecOffset(n) and + fmt = this.getFormat() and + rst = fmt.substring(offset, fmt.length()) and + regexp = this.getConvSpecRegexp() and ( + spec = rst.regexpCapture(regexp, 1) and + width = rst.regexpCapture(regexp, 2) and + len = rst.regexpCapture(regexp, 3) and + conv = rst.regexpCapture(regexp, 4) + ) + ) + } + + /** + * Gets the maximum width option of the nth input (empty string if none is given). + */ + string getMaxWidthOpt(int n) { exists(string spec, string len, string conv | this.parseConvSpec(n, spec, result, len, conv)) } + + /** + * Gets the maximum width of the nth input. + */ + int getMaxWidth(int n) { result = this.getMaxWidthOpt(n).toInt() } + + /** + * Gets the length flag of the nth conversion specifier. + */ + string getLength(int n) { exists(string spec, string width, string conv | this.parseConvSpec(n, spec, width, result, conv)) } + + /** + * Gets the conversion character of the nth conversion specifier. + */ + string getConversionChar(int n) { exists(string spec, string width, string len | this.parseConvSpec(n, spec, width, len, result)) } + + /** + * Gets the maximum length of the string that can be produced by the nth + * conversion specifier of this format string; fails if no estimate is + * possible (or implemented). + */ + int getMaxConvertedLength(int n) { + (this.getConversionChar(n).toLowerCase() = "s") and + (result = this.getMaxWidth(n)) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/commons/Strcat.qll b/cpp/ql/src/semmle/code/cpp/commons/Strcat.qll new file mode 100644 index 000000000000..5c5ba9c1e4d0 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/commons/Strcat.qll @@ -0,0 +1,21 @@ +import cpp + +/** + * DEPRECATED: use `semmle.code.cpp.models.implementations.Strcat.qll` instead. + * + * A function that concatenates the string from its second argument + * to the string from its first argument, for example `strcat`. + */ +class StrcatFunction extends Function { + StrcatFunction() { + exists(string name | name = getName()| + name = "strcat" // strcat(dst, src) + or name = "strncat" // strncat(dst, src, max_amount) + or name = "wcscat" // wcscat(dst, src) + or name = "_mbscat" // _mbscat(dst, src) + or name = "wcsncat" // wcsncat(dst, src, max_amount) + or name = "_mbsncat" // _mbsncat(dst, src, max_amount) + or name = "_mbsncat_l" // _mbsncat_l(dst, src, max_amount, locale) + ) + } +} \ No newline at end of file diff --git a/cpp/ql/src/semmle/code/cpp/commons/StringAnalysis.qll b/cpp/ql/src/semmle/code/cpp/commons/StringAnalysis.qll new file mode 100644 index 000000000000..f61bac3963f6 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/commons/StringAnalysis.qll @@ -0,0 +1,73 @@ +import semmle.code.cpp.exprs.Expr +import semmle.code.cpp.controlflow.SSA + +/** + * Holds if a value can flow directly from one expr to another. + */ +predicate canValueFlow(Expr fromExpr, Expr toExpr) +{ + ( + // value propagated via a definition use pair + exists(Variable v, SsaDefinition def | fromExpr = def.getAnUltimateDefiningValue(v) | + toExpr = def.getAUse(v) + ) + ) or ( + // expr -> containing parenthesized expression + fromExpr = toExpr.(ParenthesisExpr).getExpr() + ) or ( + // R value -> containing assignment expression ('=' assignment) + fromExpr = toExpr.(AssignExpr).getRValue() + ) or ( + // then -> containing ternary (? :) operator + fromExpr = toExpr.(ConditionalExpr).getThen() + ) or ( + // else -> containing ternary (? :) operator + fromExpr = toExpr.(ConditionalExpr).getElse() + ) +} + +/** + * An analysed null terminated string. + */ +class AnalysedString extends Expr +{ + AnalysedString() + { + this.getType().getUnspecifiedType() instanceof ArrayType or + this.getType().getUnspecifiedType() instanceof PointerType + } + + /** + * Gets the maximum length (including null) this string can be, when this + * can be calculated. + */ + int getMaxLength() + { + // take the longest AnalysedString it's value could 'flow' from; however if even one doesn't + // return a value (this essentially means 'infinity') we can't return a value either. + result = max(AnalysedString expr, int toMax | (canValueFlow*(expr, this)) and (toMax = expr.(StringLiteral).getOriginalLength()) | toMax) // maximum length + and + forall(AnalysedString expr | canValueFlow(expr, this) | exists(expr.getMaxLength())) // all sources return a value (recursive) + } +} + +/** + * A call to a strlen like function. + */ +class StrlenCall extends FunctionCall { + StrlenCall() { + this.getTarget().hasQualifiedName("strlen") or + this.getTarget().hasQualifiedName("wcslen") or + this.getTarget().hasQualifiedName("_mbslen") or + this.getTarget().hasQualifiedName("_mbslen_l") or + this.getTarget().hasQualifiedName("_mbstrlen") or + this.getTarget().hasQualifiedName("_mbstrlen_l") + } + + /** + * The string argument passed into this strlen-like call. + */ + Expr getStringExpr() { + result = this.getArgument(0) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/commons/StructLikeClass.qll b/cpp/ql/src/semmle/code/cpp/commons/StructLikeClass.qll new file mode 100644 index 000000000000..ee5ac634b13d --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/commons/StructLikeClass.qll @@ -0,0 +1,101 @@ +import semmle.code.cpp.Class + +/** + * A class that is either a `struct` or just has getters and setters + * for its members. In either case it just stores data and has no + * real encapsulation. + */ +class StructLikeClass extends Class { + + StructLikeClass() { + this instanceof Struct + or + ( + forall(MemberFunction f | f = this.getAMemberFunction() | + exists(MemberVariable v | setter(v, f, this) or getter(v, f, this)) or + f instanceof Constructor or + f instanceof Destructor or + // Allow the copy and move assignment ops - still struct-like + f instanceof CopyAssignmentOperator or + f instanceof MoveAssignmentOperator + ) + ) + } + + /** + * Gets a setter function in this class, setting the given variable. + * This is a function whose name begins "set"... that assigns to this variable and no other + * member variable of the class. In addition, it takes a single parameter of + * type the type of the corresponding member variable. + */ + MemberFunction getASetter(MemberVariable v) { + setter(v, result, this) + } + + /** + * Gets a getter function in this class, getting the given variable. + * This is a function whose name begins "get"... that reads this variable and no other + * member variable of the class. In addition, its return type is the type + * of the corresponding member variable. + */ + MemberFunction getAGetter(MemberVariable v) { + getter(v, result, this) + } + +} + +/** + * Holds if `f` is a setter member function for `v`, in class `c`. + * See `StructLikeClass.getASetter`. + */ +predicate setter(MemberVariable v, MemberFunction f, Class c) { + f.getDeclaringType() = c and + v.getDeclaringType() = c and + f.getName().matches("set%") and + v.getAnAssignedValue().getEnclosingFunction() = f and + not exists(MemberVariable v2 | + v2.getDeclaringType() = c and + v2.getAnAssignedValue().getEnclosingFunction() = f and + v2 != v + ) and + f.getNumberOfParameters() = 1 and + sameBaseType(f.getParameter(0).getType(), v.getType()) +} + +/** + * Holds if `f` is a getter member function for `v`, in class `c`. + * See `StructLikeClass.getAGetter`. + */ +predicate getter(MemberVariable v, MemberFunction f, Class c) { + f.getDeclaringType() = c and + v.getDeclaringType() = c and + f.getName().matches("get%") and + v.getAnAccess().getEnclosingFunction() = f and + not exists(MemberVariable v2 | + v2.getDeclaringType() = c and + v2.getAnAccess().getEnclosingFunction() = f and + v2 != v + ) and + f.getNumberOfParameters() = 0 and + sameBaseType(f.getType(), v.getType()) +} + +/** + * Holds if `t1` and `t2` are the same type up to typedefs, specifiers, + * and removing a single layer of pointers or references (but not arrays). + * Equates, for example, `const int*` with `int`, but not `int**` with `int` + * or `int[]` with `int`. + */ +predicate sameBaseType(Type t1, Type t2) { + exists (Type base1, Type base2 | + base1 = t1.getUnderlyingType().getUnspecifiedType() and + base2 = t2.getUnspecifiedType().getUnspecifiedType() and + ( + base1 = base2 or + base1.(PointerType).getBaseType() = base2 or + base2.(PointerType).getBaseType() = base1 or + base1.(ReferenceType).getBaseType() = base2 or + base2.(ReferenceType).getBaseType() = base1 + ) + ) +} diff --git a/cpp/ql/src/semmle/code/cpp/commons/Synchronization.qll b/cpp/ql/src/semmle/code/cpp/commons/Synchronization.qll new file mode 100644 index 000000000000..0fe8077a94e7 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/commons/Synchronization.qll @@ -0,0 +1,215 @@ +/** + * Utilities for analyzing synchronization primitives, such + * as mutexes and semaphores. + */ +import cpp + +/** + * A type that acts as a mutex. This class is extended below and and may + * be extended in `Options.qll`. + */ +abstract class MutexType extends Type { + /** + * Holds if `fc` is a call that always locks mutex `arg` + * of this type. + */ + abstract predicate mustlockAccess(FunctionCall fc, Expr arg); + + /** + * Holds if `fc` is a call that tries to lock mutex `arg` + * of this type, but may return without success. + */ + abstract predicate trylockAccess(FunctionCall fc, Expr arg); + + /** + * Holds if `fc` is a call that unlocks mutex `arg` + * of this type. + */ + abstract predicate unlockAccess(FunctionCall fc, Expr arg); + + /** + * Holds if `fc` is n call that locks or tries to lock mutex + * `arg` of this type. + */ + predicate lockAccess(FunctionCall fc, Expr arg) { + this.mustlockAccess(fc, arg) or + this.trylockAccess(fc, arg) + } + + /** + * Holds if `fc` is a call that locks or tries to lock any + * mutex of this type. + */ + FunctionCall getLockAccess() { + result = getMustlockAccess() or + result = getTrylockAccess() + } + + /** + * Holds if `fc` is 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, + * by may return without success. + */ + FunctionCall getTrylockAccess() { + this.trylockAccess(result, _) + } + + /** + * Holds if `fc` is a call that unlocks any mutex of this type. + */ + FunctionCall getUnlockAccess() { + this.unlockAccess(result, _) + } + + /** + * DEPRECATED: use mustlockAccess(fc, arg) instead + */ + deprecated Function getMustlockFunction() { + result = getMustlockAccess().getTarget() + } + + /** + * DEPRECATED: use trylockAccess(fc, arg) instead + */ + deprecated Function getTrylockFunction() { + result = getTrylockAccess().getTarget() + } + + /** + * DEPRECATED: use lockAccess(fc, arg) instead + */ + deprecated Function getLockFunction() { + result = getLockAccess().getTarget() + } + + /** + * DEPRECATED: use unlockAccess(fc, arg) instead + */ + deprecated Function getUnlockFunction() { + result = getUnlockAccess().getTarget() + } +} + +/** + * A function that looks like a lock function. + */ +private Function mustlockCandidate() { + exists (string name + | name = result.getName() + | name = "lock" or + name.suffix(name.length() - 10) = "mutex_lock") +} + +/** + * A function that looks like a try-lock function. + */ +private Function trylockCandidate() { + exists (string name + | name = result.getName() + | name = "try_lock" or + name.suffix(name.length() - 13) = "mutex_trylock") +} + +/** + * A function that looks like an unlock function. + */ +private Function unlockCandidate() { + exists (string name + | name = result.getName() + | name = "unlock" or + name.suffix(name.length() - 12) = "mutex_unlock") +} + +/** + * Gets a type that is a parameter to a function, or it's declaring type + * (i.e. it's `this`). If the function is a locking related function, + * these can be thought of as candidates for the mutex is it locking or + * unlocking. It is narrowed down in `DefaultMutexType` by requiring that + * it must be a class type with both a lock and an unlock function. + */ +private Class lockArgTypeCandidate(Function fcn) { + result = fcn.getDeclaringType() or + result = fcn.getAParameter().getType().stripType() +} + +/** + * A class or struct type that has both a lock and an unlock function + * candidate, and is therefore a mutex. + * + * This excludes types like `std::weak_ptr` which has a lock + * method, but not an unlock method, and is not a mutex.) + */ +class DefaultMutexType extends MutexType { + DefaultMutexType() { + this = lockArgTypeCandidate(mustlockCandidate()) + and + this = lockArgTypeCandidate(unlockCandidate()) + } + + private predicate lockArgType(FunctionCall fc, Expr arg) { + exists(int n | + arg = fc.getArgument(n) and + fc.getTarget().getParameter(n).getType().stripType() = this + ) or ( + fc.getTarget().getDeclaringType() = this and + arg = fc.getQualifier() + ) or ( + // if we're calling our own method with an implicit `this`, + // let `arg` be the function call, since we don't really have + // anything else to use. + fc.getTarget().getDeclaringType() = this and + not exists(fc.getQualifier()) and + arg = fc + ) + } + + override predicate mustlockAccess(FunctionCall fc, Expr arg) { + fc.getTarget() = mustlockCandidate() and + lockArgType(fc, arg) + } + + override predicate trylockAccess(FunctionCall fc, Expr arg) { + fc.getTarget() = trylockCandidate() and + lockArgType(fc, arg) + } + + override predicate unlockAccess(FunctionCall fc, Expr arg) { + fc.getTarget() = unlockCandidate() and + lockArgType(fc, arg) + } +} + +/** Get the mutex argument of a call to lock or unlock. */ +private predicate lockArg(Expr arg, MutexType argType, FunctionCall call) { + argType = arg.getUnderlyingType().stripType() and + ( + arg = call.getQualifier() or + arg = call.getAnArgument() + ) + // note: this seems to arbitrarily care about argument types, rather + // than parameter types as elsewhere. As a result `mustlockCall`, + // for example, has slightly different results from + // `MutexType.mustlockAccess`. +} + +predicate lockCall(Expr arg, FunctionCall call) { + exists(MutexType t | lockArg(arg, t, call) and call = t.getLockAccess()) +} + +predicate mustlockCall(Expr arg, FunctionCall call) { + exists(MutexType t | lockArg(arg, t, call) and call = t.getMustlockAccess()) +} + +predicate trylockCall(Expr arg, FunctionCall call) { + exists(MutexType t | lockArg(arg, t, call) and call = t.getTrylockAccess()) +} + +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 new file mode 100644 index 000000000000..652fecb419e4 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/commons/VoidContext.qll @@ -0,0 +1,38 @@ +import semmle.code.cpp.exprs.Expr + +/** + * An expression that is used to qualify some other expression. + */ +class Qualifier extends Expr { + Qualifier() { + exists(VariableAccess a | a.getQualifier() = this) + or exists(Call c | c.getQualifier() = this) + or exists(VacuousDestructorCall v | v.getQualifier() = this) + } +} + +/** + * An expression that occurs in a void context, i.e. either as the toplevel expression of + * an expression statement or on the left hand side of the comma operator. + * + * Expressions that are explicitly cast to void are not considered to be in void context. + */ +class ExprInVoidContext extends Expr { + ExprInVoidContext() { + exprInVoidContext(this) + } +} + +private predicate exprInVoidContext(Expr e) { + ( exists(ExprStmt s | + s = e.getParent() + and not exists(StmtExpr se | + s = se.getStmt().(Block).getLastStmt())) + or exists(ConditionalExpr c | c.getThen() = e and c instanceof ExprInVoidContext) + or exists(ConditionalExpr c | c.getElse() = e and c instanceof ExprInVoidContext) + or exists(CommaExpr c | c.getLeftOperand() = e) + or exists(CommaExpr c | c.getRightOperand() = e and c instanceof ExprInVoidContext)) and + not e instanceof Qualifier and + not e.getActualType() instanceof VoidType +} + diff --git a/cpp/ql/src/semmle/code/cpp/commons/unix/Constants.qll b/cpp/ql/src/semmle/code/cpp/commons/unix/Constants.qll new file mode 100644 index 000000000000..c3174c87b879 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/commons/unix/Constants.qll @@ -0,0 +1,97 @@ +/** + * Standard Unix constants. + */ +import cpp + +bindingset[input] +int parseOctal(string input) { + input.charAt(0) = "0" + and + result = strictsum(int ix + | ix in [0 .. input.length()] + | 8.pow(input.length() - (ix+1))*input.charAt(ix).toInt()) +} + +int s_isuid() { + result = parseOctal("04000") +} + +int s_isgid() { + result = parseOctal("02000") +} + +int s_isvtx() { + result = parseOctal("01000") +} + +int s_irusr() { + result = parseOctal("0400") +} + +int s_iwusr() { + result = parseOctal("0200") +} + +int s_ixusr() { + result = parseOctal("0100") +} + +int s_irwxu() { + result = s_irusr().bitOr(s_iwusr()).bitOr(s_ixusr()) +} + +int s_irgrp() { + result = s_irusr().bitShiftRight(3) +} + +int s_iwgrp() { + result = s_iwusr().bitShiftRight(3) +} + +int s_ixgrp() { + result = s_ixusr().bitShiftRight(3) +} + +int s_irwxg() { + result = s_irwxu().bitShiftRight(3) +} + +int s_iroth() { + result = s_irgrp().bitShiftRight(3) +} + +int s_iwoth() { + result = s_iwgrp().bitShiftRight(3) +} + +int s_ixoth() { + result = s_ixgrp().bitShiftRight(3) +} + +int s_irwxo() { + result = s_irwxg().bitShiftRight(3) +} + +int o_accmode() { + result = parseOctal("0003") +} + +int o_rdonly() { + result = parseOctal("00") +} + +int o_wronly() { + result = parseOctal("01") +} + +int o_rdwr() { + result = parseOctal("02") +} + +int o_creat() { + result = parseOctal("0100") +} + +int o_excl() { + result = parseOctal("0200") +} \ No newline at end of file diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/BasicBlocks.qll b/cpp/ql/src/semmle/code/cpp/controlflow/BasicBlocks.qll new file mode 100644 index 000000000000..2b38a46ac990 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/controlflow/BasicBlocks.qll @@ -0,0 +1,287 @@ +import cpp +private import internal.PrimitiveBasicBlocks +private import internal.ConstantExprs +/* + * `BasicBlock`s are refinements of `PrimitiveBasicBlock`s, taking + * impossible CFG edges into account (using the `successors_adapted` + * relation). The refinement manifests itself in two changes: + * + * - The successor relation on `BasicBlock`s uses `successors_adapted` + * (instead of `successors_extended` used by `PrimtiveBasicBlock`s). Consequently, + * some edges between `BasicBlock`s may be removed. Example: + * ``` + * x = 1; // s1 + * if (true) { // s2 + * x = 2; // s3 + * } else { + * x = 3; // s4 + * } + * ``` + * The `BasicBlock` successor edge from the basic block containing `s1` + * and `s2` to the basic block containing `s4` is removed. + * + * - `PrimitiveBasicBlock`s may be split up into two or more + * `BasicBlock`s: Internal nodes of `PrimitiveBasicBlock`s whose + * predecessor edges have been removed (unreachable code) will be entry + * points of new `BasicBlock`s. Consequently, each entry point of a + * `PrimitiveBasicBlock` will also be an entry point of a `BasicBlock`, + * but the converse does not necessarily hold. Example: + * ``` + * x = 1; // s5 + * abort(); // s6 + * x = 2; // s7 + * ``` + * `s5`-`s7` belong to the same `PrimitiveBasicBlock`, but the CFG edge + * from `s6` to `s7` is impossible, so `s7` will be the entry point of + * its own (unreachable) `BasicBlock`. + * + * Note that, although possible, two or more `PrimitiveBasicBlock`s are + * never merged to one `BasicBlock`: Consider the first example above; + * it would be possible to define a single `BasicBlock` consisting of + * `s1`-`s3`, however, the result would be counter-intuitive. + */ + +private import Cached +private cached module Cached { + /** + * Any node that is the entry point of a primitive basic block is + * also the entry point of a basic block. In addition, all nodes + * with a primitive successor, where the predecessor has been pruned + * (that is, `getAPredecessor()` does not exist while a predecessor + * using the primitive `successors_extended` relation does exist), is also + * considered a basic block entry node. + */ + cached + predicate basic_block_entry_node(ControlFlowNode node) { + primitive_basic_block_entry_node(node) or + non_primitive_basic_block_entry_node(node) + } + + private predicate non_primitive_basic_block_entry_node(ControlFlowNode node) { + not primitive_basic_block_entry_node(node) and + not exists(node.getAPredecessor()) and + successors_extended(node, _) + } + + /** + * Holds if basic block `bb` equals a primitive basic block. + * + * There are two situations in which this is *not* the case: + * + * - Either the entry node of `bb` does not correspond to an + * entry node of a primitive basic block, or + * - The primitive basic block with the same entry node contains + * a (non-entry) node which is the entry node of a non-primitive + * basic block (that is, the primitive basic block has been split + * up). + * + * This predicate is used for performance optimization only: + * Whenever a `BasicBlock` equals a `PrimitiveBasicBlock`, we can + * reuse predicates already computed for `PrimitiveBasicBlocks`. + */ + private predicate equalsPrimitiveBasicBlock(BasicBlock bb) { + primitive_basic_block_entry_node(bb) + and + not exists(int i | + i > 0 and + non_primitive_basic_block_entry_node(bb.(PrimitiveBasicBlock).getNode(i)) + ) + } + + /** Holds if `node` is the `pos`th control-flow node in basic block `bb`. */ + cached predicate basic_block_member(ControlFlowNode node, BasicBlock bb, int pos) { + equalsPrimitiveBasicBlock(bb) and primitive_basic_block_member(node, bb, pos) // reuse already computed relation + or + non_primitive_basic_block_member(node, bb, pos) + } + + private predicate non_primitive_basic_block_member(ControlFlowNode node, BasicBlock bb, int pos) { + (not equalsPrimitiveBasicBlock(bb) and node = bb and pos = 0) + or + (not (node instanceof BasicBlock) and + exists (ControlFlowNode pred + | successors_extended(pred,node) + | non_primitive_basic_block_member(pred, bb, pos - 1))) + } + + /** Gets the number of control-flow nodes in the basic block `bb`. */ + cached int bb_length(BasicBlock bb) { + if equalsPrimitiveBasicBlock(bb) then + result = bb.(PrimitiveBasicBlock).length() // reuse already computed relation + else + result = strictcount(ControlFlowNode node | basic_block_member(node, bb, _)) + } + + /** Successor relation for basic blocks. */ + cached + predicate bb_successor_cached(BasicBlock pred, BasicBlock succ) { + exists(ControlFlowNode last | + basic_block_member(last, pred, bb_length(pred)-1) and + last.getASuccessor() = succ + ) + } +} + +predicate bb_successor = bb_successor_cached/2; + +/** + * A basic block in the C/C++ control-flow graph. + * + * A basic block is a simple sequence of control-flow nodes, + * connected to each other and nothing else: + * + * ``` + * A - B - C - D ABCD is a basic block + * ``` + * + * Any incoming or outgoing edges break the block into two: + * + * ``` + * A - B > C - D AB is a basic block and CD is a basic block (C has two incoming edges) + * + * + * A - B < C - D AB is a basic block and CD is a basic block (B has two outgoing edges) + * ``` + */ +class BasicBlock extends @cfgnode { + + BasicBlock() { + basic_block_entry_node(this) + } + + /** Gets a textual representation of this element. */ + string toString() { + result = "BasicBlock" + } + + predicate contains(ControlFlowNode node) { + basic_block_member(node, this, _) + } + + ControlFlowNode getNode(int pos) { + basic_block_member(result, this, pos) + } + + ControlFlowNode getANode() { + basic_block_member(result, this, _) + } + + BasicBlock getASuccessor() { + bb_successor(this, result) + } + + BasicBlock getAPredecessor() { + bb_successor(result, this) + } + + BasicBlock getATrueSuccessor() { + result.getStart() = this.getEnd().getATrueSuccessor() + } + + BasicBlock getAFalseSuccessor() { + result.getStart() = this.getEnd().getAFalseSuccessor() + } + + ControlFlowNode getEnd() { + basic_block_member(result, this, bb_length(this)-1) + } + + ControlFlowNode getStart() { + result = this + } + + /** Gets the number of `ControlFlowNode`s in this basic block. */ + int length() { + result = bb_length(this) + } + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [LGTM locations](https://lgtm.com/help/ql/locations). + * + * Yields no result if this basic block spans multiple source files. + */ + predicate hasLocationInfo(string filepath, int startline, int startcolumn, int endline, int endcolumn) { + hasLocationInfoInternal(filepath, startline, startcolumn, filepath, endline, endcolumn) + } + + pragma[noinline] private + predicate hasLocationInfoInternal(string file, int line, int col, string endf, int endl, int endc) { + this.getStart().getLocation().hasLocationInfo(file, line, col, _, _) + and + this.getEnd().getLocation().hasLocationInfo(endf, _, _, endl, endc) + } + + Function getEnclosingFunction() { + result = this.getStart().getControlFlowScope() + } + + /** + * Holds if this basic block is in a loop of the control-flow graph. This + * includes loops created by `goto` statements. This predicate may not hold + * even if this basic block is syntactically inside a `while` loop if the + * necessary back edges are unreachable. + */ + predicate inLoop() { + this.getASuccessor+() = this + } + + /** + * DEPRECATED since version 1.11: this predicate does not match the standard + * definition of _loop header_. + * + * Holds if this basic block is in a loop of the control-flow graph and + * additionally has an incoming edge that is not part of any loop containing + * this basic block. A typical example would be the basic block that computes + * `x > 0` in an outermost loop `while (x > 0) { ... }`. + */ + deprecated + predicate isLoopHeader() { + this.inLoop() and exists(BasicBlock pred | pred = this.getAPredecessor() and not pred = this.getASuccessor+()) + } + + /** + * Holds if control flow may reach this basic block from a function entry + * point or a `catch` clause of a reachable `try` statement. + */ + predicate isReachable() { + exists(Function f | f.getBlock() = this) + or + exists(TryStmt t, BasicBlock tryblock | this = t.getACatchClause() and tryblock.isReachable() and tryblock.contains(t)) + or + exists(BasicBlock pred | pred.getASuccessor() = this and pred.isReachable()) + } + + /** Means `not isReachable()`. */ + predicate isUnreachable() { + not this.isReachable() + } + +} + +/** Correct relation for reachability of ControlFlowNodes. */ +predicate unreachable(ControlFlowNode n) { + exists(BasicBlock bb | bb.contains(n) and bb.isUnreachable()) +} + +/** + * An entry point of a function. + */ +class EntryBasicBlock extends BasicBlock { + EntryBasicBlock() { + exists (Function f | this = f.getEntryPoint()) + } +} + +/** + * A basic block whose last node is the exit point of a function. + */ +class ExitBasicBlock extends BasicBlock { + ExitBasicBlock() { + getEnd() instanceof Function or + aborting(getEnd()) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/ControlFlowGraph.qll b/cpp/ql/src/semmle/code/cpp/controlflow/ControlFlowGraph.qll new file mode 100644 index 000000000000..b255a2153caf --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/controlflow/ControlFlowGraph.qll @@ -0,0 +1,173 @@ +import cpp +import BasicBlocks +private import semmle.code.cpp.controlflow.internal.ConstantExprs + +/** + * A control-flow node is either a statement or an expression; in addition, + * functions are control-flow nodes representing the exit point of the + * function. The graph represents one possible evaluation order out of all the + * ones the compiler might have picked. + * + * Control-flow nodes have successors and predecessors at the expression level, + * so control flow is accurately represented in expressions as well as between + * statements. Statements and initializers precede their contained expressions, + * and expressions deeper in the tree precede those higher up; for example, the + * statement `x = y + 1` gets a control-flow graph that looks like + * + * ``` + * ExprStmt -> y -> 1 -> (+) -> x -> (=) + * ``` + * + * The first control-flow node in a function is the body of the function (a + * block), and the last is the function itself, which is used to represent the + * exit point. + * + * Each `throw` expression or `Handler` has a path (along any necessary + * destructor calls) to its nearest enclosing `Handler` within the same + * function, or to the exit point of the function if there is no such + * `Handler`. There are no edges from function calls to `Handler`s. + */ +class ControlFlowNode extends Locatable, @cfgnode { + + ControlFlowNode getASuccessor() { successors_adapted(this,result) } + + ControlFlowNode getAPredecessor() { this = result.getASuccessor() } + + /** Gets the function containing this control-flow node. */ + abstract Function getControlFlowScope(); + + /** Gets the smallest statement containing this control-flow node. */ + abstract Stmt getEnclosingStmt(); + + /** + * Holds if this node is the top-level expression of a conditional statement, + * meaning that `this.getATrueSuccessor()` or `this.getAFalseSuccessor()` + * will have a result. + */ + predicate isCondition() { + exists(this.getATrueSuccessor()) or + exists(this.getAFalseSuccessor()) + } + + /** + * Gets a node such that the control-flow edge `(this, result)` may be + * taken when this expression is true. + */ + ControlFlowNode getATrueSuccessor() { + truecond(this,result) and result = getASuccessor() + } + + /** + * Gets a node such that the control-flow edge `(this, result)` may be + * taken when this expression is false. + */ + ControlFlowNode getAFalseSuccessor() { + falsecond(this,result) and result = getASuccessor() + } + + BasicBlock getBasicBlock() { + result.getANode() = this + } +} + +import Cached +private cached module Cached { + /** + * Holds if the control-flow node `n` is reachable, meaning that either + * it is an entry point, or there exists a path in the control-flow + * graph of its function that connects an entry point to it. + * Compile-time constant conditions are taken into account, so that + * the call to `f` is not reachable in `if (0) f();` even if the + * `if` statement as a whole is reachable. + */ + cached + predicate reachable(ControlFlowNode n) + { + exists(Function f | f.getEntryPoint() = n) + or + // Okay to use successors_extended directly here + (not successors_extended(_,n) and not successors_extended(n,_)) + or + reachable(n.getAPredecessor()) + or + n instanceof CatchBlock + } + + /** Holds if `condition` always evaluates to a nonzero value. */ + cached + predicate conditionAlwaysTrue(Expr condition) { + conditionAlways(condition, true) + } + + /** Holds if `condition` always evaluates to zero. */ + cached + predicate conditionAlwaysFalse(Expr condition) { + conditionAlways(condition, false) + or + // If a loop condition evaluates to false upon entry, it will always + // be false + loopConditionAlwaysUponEntry(_, condition, false) + } + + /** + * The condition `condition` for the loop `loop` is provably `true` upon entry. + * That is, at least one iteration of the loop is guaranteed. + */ + cached + predicate loopConditionAlwaysTrueUponEntry(ControlFlowNode loop, Expr condition) { + loopConditionAlwaysUponEntry(loop, condition, true) + } +} + +private predicate conditionAlways(Expr condition, boolean b) { + exists(ConditionEvaluator x, int val | + val = x.getValue(condition) | + val != 0 and b = true + or + val = 0 and b = false + ) +} + +private predicate loopConditionAlwaysUponEntry(ControlFlowNode loop, Expr condition, boolean b) { + exists(LoopEntryConditionEvaluator x, int val | + x.isLoopEntry(condition, loop) and + val = x.getValue(condition) | + val != 0 and b = true + or + val = 0 and b = false + ) +} + +/** + * An abstract class that can be extended to add additional edges to the + * control-flow graph. Instances of this class correspond to the source nodes + * of such edges, and the predicate `getAnEdgeTarget` should be overridden to + * produce the target nodes of each source. + * + * Changing the control-flow graph in some queries and not others can be + * expensive in execution time and disk space. Most cached predicates in the + * library depend on the control-flow graph, so these predicates will be + * computed and cached for each variation of the control-flow graph + * that is used. + * + * Edges added by this class will still be removed by the library if they + * appear to be unreachable. See the documentation on `ControlFlowNode` for + * more information about the control-flow graph. + */ +abstract class AdditionalControlFlowEdge extends @cfgnode { + /** Gets a target node of this edge, where the source node is `this`. */ + abstract ControlFlowNode getAnEdgeTarget(); + + string toString() { result = this.(ControlFlowNode).toString() } +} + +/** + * 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`. + */ +predicate successors_extended(@cfgnode source, @cfgnode target) { + successors(source, target) + or + source.(AdditionalControlFlowEdge).getAnEdgeTarget() = target +} diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/Dataflow.qll b/cpp/ql/src/semmle/code/cpp/controlflow/Dataflow.qll new file mode 100644 index 000000000000..0f5126666cf2 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/controlflow/Dataflow.qll @@ -0,0 +1,150 @@ +/** + * Provides a simple data flow analysis to find expressions that are definitely + * null or that may be null. + */ +import cpp +import Nullness +import Dereferenced + +/** + * INTERNAL: Do not use. + * A string that identifies a data flow analysis along with a set of member + * predicates that implement this analysis. + */ +abstract class DataflowAnnotation extends string { + DataflowAnnotation() { this = "pointer-null" or this = "pointer-valid" } + + abstract predicate isDefault(); + abstract predicate generatedOn(Expr e); + abstract predicate generatedBy(LocalScopeVariable v, ControlFlowNode src, ControlFlowNode dest); + abstract predicate killedBy(LocalScopeVariable v, ControlFlowNode src, ControlFlowNode dest); + + predicate marks(Expr e) { + (this.generatedOn(e) and reachable(e)) + or + this.marks(e.(AssignExpr).getRValue()) + or + exists(LocalScopeVariable v | this.marks(v, e) and e = v.getAnAccess()) + } + + predicate marks(LocalScopeVariable v, ControlFlowNode n) { + ( + v.getAnAccess().getEnclosingFunction().getBlock() = n and + this.isDefault() + ) + or + ( + this.marks(n.(Initializer).getExpr()) and + v.getInitializer() = n + ) + or + exists(ControlFlowNode pred | + this.generatedBy(v, pred, n) + and not this.killedBy(v, pred, n) + and reachable(pred) + ) + or + exists(ControlFlowNode pred | + this.assignedBy(v, pred, n) + and not this.killedBy(v, pred, n) + and reachable(pred) + ) + or + exists(ControlFlowNode pred | + this.preservedBy(v, pred, n) + and not this.killedBy(v, pred, n) + and reachable(pred) + ) + } + + predicate preservedBy(LocalScopeVariable v, ControlFlowNode src, ControlFlowNode dest) { + this.marks(v, src) and + src.getASuccessor() = dest and + not v.getInitializer() = dest and + not v.getAnAssignment() = src + } + + predicate assignedBy(LocalScopeVariable v, ControlFlowNode src, ControlFlowNode dest) { + this.marks(src.(AssignExpr).getRValue()) and + src = v.getAnAssignment() and + src.getASuccessor() = dest + } +} + +/** + * INTERNAL: Do not use. + * Two analyses relating to nullness: `"pointer-null"` and `"pointer-valid"`. + * These analyses mark expressions that are possibly null or possibly non-null, + * respectively. + */ +class NullnessAnnotation extends DataflowAnnotation { + NullnessAnnotation() { this = "pointer-null" or this = "pointer-valid" } + + override predicate isDefault() { this = "pointer-valid" } + + override predicate generatedOn(Expr e) { + exists(Variable v | + v.getAnAccess() = e and + (v instanceof GlobalVariable or v instanceof Field) and + this.isDefault()) + or + (e instanceof Call and this = "pointer-valid") + or + (nullValue(e) and this = "pointer-null") + } + + override predicate killedBy(LocalScopeVariable v, ControlFlowNode src, ControlFlowNode dest) { + src.(AnalysedExpr).getNullSuccessor(v) = dest and this = "pointer-valid" + or + src.(AnalysedExpr).getNonNullSuccessor(v) = dest and this = "pointer-null" + or + dest = src.getASuccessor() and callByReference(src, v) and not this.isDefault() + or + dest = src.getASuccessor() and deref(v, src) and this = "pointer-null" + } + + override predicate generatedBy(LocalScopeVariable v, ControlFlowNode src, ControlFlowNode dest) { + dest = src.getASuccessor() and + callByReference(src, v) and + this.isDefault() + } +} + +/** + * Holds if evaluation of `op` dereferences `v`. + */ +predicate deref(Variable v, Expr op) { + dereferencedByOperation(op, v.getAnAccess()) +} + +/** + * Holds if `call` passes `v` by reference, either with an explicit address-of + * operator or implicitly as a C++ reference. Both const and non-const + * references are included. + */ +predicate callByReference(Call call, Variable v) { + exists(Expr arg | + call.getAnArgument() = arg and + ( + arg.(AddressOfExpr).getAChild() = v.getAnAccess() + or + (v.getAnAccess() = arg and arg.getConversion*() instanceof ReferenceToExpr) + ) + ) +} + +/** + * Holds if a simple data-flow analysis determines that `e` is definitely null. + */ +predicate definitelyNull(Expr e) { + "pointer-null".(NullnessAnnotation).marks(e) + and + not "pointer-valid".(NullnessAnnotation).marks(e) +} + +/** + * Holds if a simple data-flow analysis determines that `e` may be null. + */ +predicate maybeNull(Expr e) { + "pointer-null".(NullnessAnnotation).marks(e) +} diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/DefinitionsAndUses.qll b/cpp/ql/src/semmle/code/cpp/controlflow/DefinitionsAndUses.qll new file mode 100644 index 000000000000..ce650df961f1 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/controlflow/DefinitionsAndUses.qll @@ -0,0 +1,410 @@ +import cpp +private import semmle.code.cpp.controlflow.LocalScopeVariableReachability +private import semmle.code.cpp.dataflow.EscapesTree + +/** + * Computed relation: A "definition-use-pair" for a particular variable. + * Intuitively, this means that `def` is an assignment to `var`, and + * `use` is a read of `var` at which the value assigned by `def` may + * be read. (There can be more than one definition reaching a single + * use, and a single definition can reach many uses.) + */ +predicate definitionUsePair(SemanticStackVariable var, Expr def, Expr use) { + exists(Use u | + u = use + and + def.(Def).reaches(true, var, u) + and + u.getVariable(false) = var + ) +} + +/** + * Holds if the definition `def` of some stack variable can reach `node`, which + * is a definition or use, without crossing definitions of the same variable. + */ +predicate definitionReaches(Expr def, Expr node) { + def.(Def).reaches(true, _, (DefOrUse)node) +} + +private predicate hasAddressOfAccess(SemanticStackVariable var) { + var.getAnAccess().isAddressOfAccessNonConst() +} + +/** + * A use/use pair is a pair of uses of a particular variable `var` + * where the same value might be read (meaning that there is a + * control-flow path from `first` to `second` without crossing + * a definition of `var`). + */ +predicate useUsePair(SemanticStackVariable var, Expr first, Expr second) { + ( + /* If the address of `var` is used anywhere, we require that + * a definition of `var` can reach the first use. This is to + * rule out examples such as this: + * ``` + * int x = 0; + * int& y = x; + * use(x); + * y = 1; + * use(x); // not a use-use pair with the use above + * ``` + */ + hasAddressOfAccess(var) + implies + definitionUsePair(var, _, first) + ) + and + // If `first` is both a def and a use, like `x` in `f(x)` when `f` takes a + // reference parameter, it'll play the role of a use first and a def second. + // We are not interested in uses that follow its role as a def. + not definition(var, first) + and + exists(Use u | + u = second + and + first.(Use).reaches(false, var, u) + and + u.getVariable(false) = var + ) +} + +/** + * Holds if `va` is a use of the parameter `p` that could + * observe the passed-in value. + */ +predicate parameterUsePair(Parameter p, VariableAccess va) { + not parameterIsOverwritten(_, p) and va.getTarget() = p + or + exists(ParameterDef pd | pd.reaches(true, p, (Use)va)) +} + +/** + * Utility class: A definition or use of a stack variable. + */ +library +class DefOrUse extends @cfgnode { + DefOrUse() { + // Uninstantiated templates are purely syntax, and only on instantiation + // will they be complete with information about types, conversions, call + // targets, etc. + not this.(Element).isFromUninstantiatedTemplate(_) + } + + /** + * Gets the target variable of this definition or use. + * + * The `isDef` parameter is needed in order to ensure disjointness + * of definitions and uses; in a variable initialization such as + * `int x = y`, `y` is both a definition of `x`, as well as a use of + * `y`, and `isDef` is used to distinguish the two situations. + */ + abstract SemanticStackVariable getVariable(boolean isDef); + + pragma[noinline] + private predicate reaches_helper(boolean isDef, SemanticStackVariable v, BasicBlock bb, int i) { + getVariable(isDef) = v and + bb.getNode(i) = this + } + + /** + * Holds if the value of `v` in this control-flow node reaches + * `defOrUse` along some control-flow path without crossing a + * definition of `v`. + */ + cached + predicate reaches(boolean isDef, SemanticStackVariable v, DefOrUse defOrUse) { + /* Implementation detail: this predicate and its private auxiliary + * predicates are instances of the more general predicates in + * LocalScopeVariableReachability.qll, and should be kept in sync. + * + * Unfortunately, caching of abstract predicates does not work well, so the + * predicates are duplicated for now. + */ + exists(BasicBlock bb, int i | + reaches_helper(isDef, v, bb, i) | + exists(int j | + j > i + and + (bbDefAt(bb, j, v, defOrUse) or bbUseAt(bb, j, v, defOrUse)) + and + not exists(int k | firstBarrierAfterThis(isDef, k, v) and k < j) + ) + or + not firstBarrierAfterThis(isDef, _, v) and + bbSuccessorEntryReachesDefOrUse(bb, v, defOrUse, _) + ) + } + + private predicate firstBarrierAfterThis(boolean isDef, int j, SemanticStackVariable v) { + exists(BasicBlock bb, int i | + getVariable(isDef) = v + and + bb.getNode(i) = this + and + j = min(int k | bbBarrierAt(bb, k, v, _) and k > i) + ) + } + + /** Gets a textual representation of this element. */ + string toString() { result = "DefOrUse" } +} + +library +class Def extends DefOrUse { + Def() { + definition(_, this) + } + + override SemanticStackVariable getVariable(boolean isDef) { + definition(result, this) and isDef = true + } +} + +/** Holds if parameter `p` is potentially overwritten in the body of `f`. */ +private predicate parameterIsOverwritten(Function f, Parameter p) { + f.getAParameter() = p and + definitionBarrier(p, _) +} + +library +class ParameterDef extends DefOrUse { + ParameterDef() { + // Optimization: parameters that are not overwritten do not require + // reachability analysis + exists(Function f | parameterIsOverwritten(f, _) | this = f.getEntryPoint()) + } + + override SemanticStackVariable getVariable(boolean isDef) { + exists(Function f | parameterIsOverwritten(f, result) | this = f.getEntryPoint()) + and + isDef = true + } +} + +library +class Use extends DefOrUse { + Use() { + useOfVar(_, this) + } + + override SemanticStackVariable getVariable(boolean isDef) { + useOfVar(result, this) and isDef = false + } +} + +private predicate bbUseAt(BasicBlock bb, int i, SemanticStackVariable v, Use use) { + bb.getNode(i) = use + and + use.getVariable(false) = v +} + +private predicate bbDefAt(BasicBlock bb, int i, SemanticStackVariable v, Def def) { + bb.getNode(i) = def + and + def.getVariable(true) = v +} + +private predicate bbBarrierAt(BasicBlock bb, int i, SemanticStackVariable v, ControlFlowNode node) { + bb.getNode(i) = node + and + definitionBarrier(v, node) +} + +/** + * Holds if the entry node of a _successor_ of basic block `bb` can + * reach `defOrUse` without going through a barrier for `v`. + * `defOrUse` may either be in the successor block, or in another + * basic block reachable from the successor. + * + * `skipsFirstLoopAlwaysTrueUponEntry` indicates whether the first loop + * on the path to `defOrUse` (if any), where the condition is provably + * true upon entry, is skipped (including the step from `bb` to the + * successor). + */ +private predicate bbSuccessorEntryReachesDefOrUse(BasicBlock bb, SemanticStackVariable v, DefOrUse defOrUse, boolean skipsFirstLoopAlwaysTrueUponEntry) { + exists(BasicBlock succ, boolean succSkipsFirstLoopAlwaysTrueUponEntry | + bbSuccessorEntryReachesLoopInvariant(bb, succ, skipsFirstLoopAlwaysTrueUponEntry, succSkipsFirstLoopAlwaysTrueUponEntry) | + bbEntryReachesDefOrUseLocally(succ, v, defOrUse) and + succSkipsFirstLoopAlwaysTrueUponEntry = false and + not excludeReachesFunction(bb.getEnclosingFunction()) + or + not bbBarrierAt(succ, _, v, _) and + bbSuccessorEntryReachesDefOrUse(succ, v, defOrUse, succSkipsFirstLoopAlwaysTrueUponEntry) + ) +} + +private predicate bbEntryReachesDefOrUseLocally(BasicBlock bb, SemanticStackVariable v, DefOrUse defOrUse) { + exists(int n | + bbDefAt(bb, n, v, defOrUse) or bbUseAt(bb, n, v, defOrUse) | + not exists(int m | m < n | bbBarrierAt(bb, m, v, _)) + ) +} + +/** + * Holds if `barrier` is either a (potential) definition of `v` or follows an + * access that gets the address of `v`. In both cases, the value of + * `v` after `barrier` cannot be assumed to be the same as before. + */ +predicate definitionBarrier(SemanticStackVariable v, ControlFlowNode barrier) { + definition(v, barrier) + or + exists(VariableAccess va | + /* `va = barrier` does not work, as that could generate an + * incorrect use-use pair (a barrier must exist _between_ + * uses): + * ``` + * x = 0; + * int& y = x; // use1 + * y = 1; + * use(x); // use2 + * ``` + */ + va.getASuccessor() = barrier and + va.getTarget() = v and + va.isAddressOfAccessNonConst() and + not exists(Call c | c.passesByReferenceNonConst(_, va)) // handled in definitionByReference + ) +} + +/** + * Holds if `def` is a (potential) assignment to stack variable `v`. That is, + * the variable may hold another value in the control-flow node(s) + * following `def` than before. + */ +predicate definition(SemanticStackVariable v, Expr def) { + def = v.getInitializer().getExpr() + or + variableAccessedAsValue( + v.getAnAccess(), def.(Assignment).getLValue().getFullyConverted()) + or + variableAccessedAsValue( + v.getAnAccess(), def.(CrementOperation).getOperand().getFullyConverted()) + or + exists(AsmStmt asmStmt | + def = asmStmt.getAChild() and + def = v.getAnAccess().getParent*()) + or + definitionByReference(v.getAnAccess(), def) +} + +/** + * Holds if `def` is a (definite) assignment to the stack variable `v`. `e` is + * the assigned expression. + */ +predicate exprDefinition(SemanticStackVariable v, ControlFlowNode def, Expr e) { + ( + def = v.getInitializer().getExpr() and + def = e and + not v.getType() instanceof ReferenceType + ) + or + exists(AssignExpr assign | + def = assign and + assign.getLValue() = v.getAnAccess() and + e = assign.getRValue() + ) +} + +pragma[noinline] +private predicate containsAssembly(Function f) { + f = any(AsmStmt s).getEnclosingFunction() +} + +/** + * Holds if `va` is a variable passed by reference as argument `def`, where the + * callee potentially assigns the corresponding parameter. The + * definitions-and-uses library models assignment by reference as if it happens + * on evaluation of the argument, `def`. + * + * All library functions except `std::move` are assumed to assign + * call-by-reference parameters, and source code functions are assumed to + * assign call-by-reference parameters that are accessed somewhere within the + * function. The latter is an over-approximation, but avoids having to take + * aliasing of the parameter into account. + */ +predicate definitionByReference(VariableAccess va, Expr def) { + exists(Call c, int i | + c.passesByReferenceNonConst(i, va) + and + def = c.getArgument(i) + and + forall(Function f | + f = c.getTarget() and f.hasEntryPoint() | + exists(f.getParameter(i).getAnAccess()) + or + f.isVarargs() and i >= f.getNumberOfParameters() + or + // If the callee contains an AsmStmt, then it is better to + // be conservative and assume that the parameter can be + // modified. + containsAssembly(f) + ) + ) + or + // Extractor artifact when using templates; an expression call where the + // target expression is an unknown literal. Since we cannot know what + // these calls represent, assume they assign all their arguments + exists(ExprCall c, Literal l | + l = c.getExpr() and + c.getAnArgument() = va and + not exists(l.getValue()) and + def = va + ) +} + +private predicate accessInSizeof(VariableAccess use) { + use.getParent+() instanceof SizeofOperator +} + +/** + * Holds if `use` is a non-definition use of stack variable `v`. This will not + * include accesses on the LHS of an assignment (which don't retrieve the + * variable value), but _will_ include accesses in increment/decrement + * operations. + */ +predicate useOfVar(SemanticStackVariable v, VariableAccess use) { + use = v.getAnAccess() and + not exists(AssignExpr e | e.getLValue() = use) and + // sizeof accesses are resolved at compile-time + not accessInSizeof(use) +} + +/** + * Same as `useOfVar(v, use)`, but with the extra condition that the + * access `use` actually reads the value of the stack variable `v` at + * run-time. (Non-examples include `&x` and function calls where the + * callee does not use the relevant parameter.) + */ +predicate useOfVarActual(SemanticStackVariable v, VariableAccess use) { + useOfVar(v, use) and + exists(Expr e | + variableAccessedAsValue(use, e) and + not exists(AssignExpr assign | + e = assign.getLValue().getFullyConverted() + ) + ) and + // A call to a function that does not use the relevant parameter + not exists(Call c, int i | + c.getArgument(i) = use and + c.getTarget().hasEntryPoint() and + not exists(c.getTarget().getParameter(i).getAnAccess()) + ) +} + +/** + * A function that should be excluded from 'reaches' analysis. + * + * The current implementation performs badly in some cases where a + * function has both a huge number of def/uses and a huge number of + * basic blocks, typically in generated code. We exclude these + * functions based on the former because it is cheaper to calculate. + */ +private predicate excludeReachesFunction(Function f) { + exists(int defOrUses | + defOrUses = + count(Def def | def.(ControlFlowNode).getControlFlowScope() = f) + + count(Use use | use.(ControlFlowNode).getControlFlowScope() = f) and + defOrUses >= 13000 + ) +} diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/Dereferenced.qll b/cpp/ql/src/semmle/code/cpp/controlflow/Dereferenced.qll new file mode 100644 index 000000000000..69ffc314d374 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/controlflow/Dereferenced.qll @@ -0,0 +1,107 @@ +import cpp +import Nullness + +/** + * Holds if the call `fc` will dereference argument `i`. + */ +predicate callDereferences(FunctionCall fc, int i) +{ + exists(string name | + fc.getTarget().hasQualifiedName(name) and + ( + (name = "bcopy" and i in [0..1]) or + (name = "memcpy" and i in [0..1]) or + (name = "memmove" and i in [0..1]) or + (name = "strcpy" and i in [0..1]) or + (name = "strncpy" and i in [0..1]) or + (name = "strdup" and i = 0) or + (name = "strndup" and i = 0) or + (name = "strlen" and i = 0) or + (name = "printf" and fc.getArgument(i).getType() instanceof PointerType) or + (name = "fprintf" and fc.getArgument(i).getType() instanceof PointerType) or + (name = "sprintf" and fc.getArgument(i).getType() instanceof PointerType) or + (name = "snprintf" and fc.getArgument(i).getType() instanceof PointerType) or + (name = "vprintf" and fc.getArgument(i).getType() instanceof PointerType) or + (name = "vfprintf" and fc.getArgument(i).getType() instanceof PointerType) or + (name = "vsprintf" and fc.getArgument(i).getType() instanceof PointerType) or + (name = "vsnprintf" and fc.getArgument(i).getType() instanceof PointerType) + ) + ) +} + +/** + * Holds if evaluation of `op` dereferences `e`. + */ +predicate dereferencedByOperation(Expr op, Expr e) +{ + exists(PointerDereferenceExpr deref | + deref.getAChild() = e and deref = op and + not deref.getParent*() instanceof SizeofOperator) + or + exists(CrementOperation crement | + dereferencedByOperation(e, op) and crement.getOperand() = e) + or + exists(ArrayExpr ae | + (not ae.getParent() instanceof AddressOfExpr and + not ae.getParent*() instanceof SizeofOperator) and + ae = op and + ( + (e = ae.getArrayBase() and e.getType() instanceof PointerType) + or + (e = ae.getArrayOffset() and e.getType() instanceof PointerType) + ) + ) + or + exists(AddressOfExpr addof, ArrayExpr ae | + dereferencedByOperation(addof, op) and addof.getOperand() = ae and + (e = ae.getArrayBase() or e = ae.getArrayOffset()) and + e.getType() instanceof PointerType) + or + exists(UnaryArithmeticOperation arithop | + dereferencedByOperation(arithop, op) and e = arithop.getAnOperand() and e.getType() instanceof PointerType) + or + exists(BinaryArithmeticOperation arithop | + dereferencedByOperation(arithop, op) and e = arithop.getAnOperand() and e.getType() instanceof PointerType) + or + + exists(FunctionCall fc, int i | + (callDereferences(fc, i) or functionCallDereferences(fc, i)) + and e = fc.getArgument(i) and op = fc) + or + // ptr->Field + e = op.(FieldAccess).getQualifier() and isClassPointerType(e.getType()) + or + // ptr->method() + e = op.(Call).getQualifier() and isClassPointerType(e.getType()) +} + +private predicate isClassPointerType(Type t) { + t.getUnderlyingType().(PointerType).getBaseType().getUnderlyingType() instanceof Class +} + +/** + * Holds if `e` will be dereferenced after being evaluated. + */ +predicate dereferenced(Expr e) +{ + dereferencedByOperation(_, e) +} + +pragma[noinline] +private predicate functionCallDereferences(FunctionCall fc, int i) +{ + functionDereferences(fc.getTarget(), i) +} + +/** + * Holds if the body of a function `f` is likely to dereference its `i`th + * parameter unconditionally. This analysis does not account for reassignment. + */ +predicate functionDereferences(Function f, int i) +{ + exists(VariableAccess access, Parameter p | + p = f.getParameter(i) and + dereferenced(access) and + access = p.getAnAccess() and + not checkedValid(p, access)) +} diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/Dominance.qll b/cpp/ql/src/semmle/code/cpp/controlflow/Dominance.qll new file mode 100644 index 000000000000..6bb4838e2be1 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/controlflow/Dominance.qll @@ -0,0 +1,85 @@ +/** + * Provides dominance predicates for control-flow nodes. + * + * These variations of the _dominance relation_ are used for computing SSA + * form. Formally, a node `d` _dominates_ a node `n` if all paths from the + * function entry point to `n` go through `d`; this applies within a function + * and only for nodes reachable from the entry point. Unreachable nodes are not + * part the dominance relation. + */ +import cpp + + +/* + * In rare cases, the same node is used in multiple control-flow scopes. This + * confuses the dominance analysis, so this predicate is used to exclude them. + */ +private predicate +hasMultiScopeNode(Function f) { + exists (ControlFlowNode node | + node.getControlFlowScope() = f + and node.getControlFlowScope() != f) +} + +/** Holds if `entry` is the entry point of a function. */ +predicate functionEntry(ControlFlowNode entry) { + exists (Function function | + function.getEntryPoint() = entry + and not hasMultiScopeNode(function)) +} + +/** + * Holds if `dest` is an immediate successor of `src` in the control-flow graph. + */ +private predicate nodeSucc(ControlFlowNode src, ControlFlowNode dest) { + src.getASuccessor() = dest +} + +/** + * Holds if `dominator` is an immediate dominator of `node` in the control-flow + * graph. + */ +predicate iDominates(ControlFlowNode dominator, ControlFlowNode node) = idominance(functionEntry/1,nodeSucc/2)(_, dominator, node) + +/** + * Holds if `dominator` is a strict dominator of `node` in the control-flow + * graph. Being strict means that `dominator != node`. + */ +predicate strictlyDominates(ControlFlowNode dominator, ControlFlowNode node) { + iDominates+(dominator, node) +} + +/** + * Holds if `dominator` is a dominator of `node` in the control-flow graph. This + * is reflexive. + */ +predicate dominates(ControlFlowNode dominator, ControlFlowNode node) { + strictlyDominates(dominator, node) or dominator = node +} + +/* + * Dominance predicates for basic blocks. + */ + +/** + * Holds if `dominator` is an immediate dominator of `node` in the control-flow + * graph of basic blocks. + */ +predicate bbIDominates(BasicBlock dom, BasicBlock node) = idominance(functionEntry/1, bb_successor/2)(_, dom, node) + +/** + * Holds if `dominator` is a strict dominator of `node` in the control-flow + * graph of basic blocks. Being strict means that `dominator != node`. + */ +predicate bbStrictlyDominates(BasicBlock dominator, BasicBlock node) { + bbIDominates+(dominator, node) +} + + +/** + * Holds if `dominator` is a dominator of `node` in the control-flow graph of + * basic blocks. This is reflexive. + */ +predicate bbDominates(BasicBlock dominator, BasicBlock node) { + bbStrictlyDominates(dominator, node) or dominator = node +} diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/Guards.qll b/cpp/ql/src/semmle/code/cpp/controlflow/Guards.qll new file mode 100644 index 000000000000..a0042c16ae46 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/controlflow/Guards.qll @@ -0,0 +1,310 @@ +import cpp +import semmle.code.cpp.controlflow.BasicBlocks +import semmle.code.cpp.controlflow.SSA +import semmle.code.cpp.controlflow.Dominance + +/** + * A Boolean condition that guards one or more basic blocks. This includes + * operands of logical operators but not switch statements. + */ +class GuardCondition extends Expr { + + GuardCondition() { + is_condition(this) + } + + /** + * Holds if this condition controls `block`, meaning that `block` is only + * entered if the value of this condition is `testIsTrue`. + * + * Illustration: + * + * ``` + * [ (testIsTrue) ] + * [ this ----------------succ ---- controlled ] + * [ | | ] + * [ (testIsFalse) | ------ ... ] + * [ other ] + * ``` + * + * The predicate holds if all paths to `controlled` go via the `testIsTrue` + * edge of the control-flow graph. In other words, the `testIsTrue` edge + * must dominate `controlled`. This means that `controlled` must be + * dominated by both `this` and `succ` (the target of the `testIsTrue` + * edge). It also means that any other edge into `succ` must be a back-edge + * from a node which is dominated by `succ`. + * + * The short-circuit boolean operations have slightly surprising behavior + * here: because the operation itself only dominates one branch (due to + * being short-circuited) then it will only control blocks dominated by the + * true (for `&&`) or false (for `||`) branch. + */ + cached predicate controls(BasicBlock controlled, boolean testIsTrue) { + /* This condition must determine the flow of control; that is, this + * node must be a top-level condition. */ + this.controlsBlock(controlled, testIsTrue) + or + exists (BinaryLogicalOperation binop, GuardCondition lhs, GuardCondition rhs + | this = binop and + lhs = binop.getLeftOperand() and + rhs = binop.getRightOperand() and + lhs.controls(controlled, testIsTrue) and + rhs.controls(controlled, testIsTrue)) + or + exists (GuardCondition ne, GuardCondition operand + | this = operand and + operand = ne.(NotExpr).getOperand() and + ne.controls(controlled, testIsTrue.booleanNot())) + } + + /** Holds if (determined by this guard) `left < right + k` evaluates to `isLessThan` if this expression evaluates to `testIsTrue`. */ + cached predicate comparesLt(Expr left, Expr right, int k, boolean isLessThan, boolean testIsTrue) { + compares_lt(this, left, right, k, isLessThan, testIsTrue) + } + + /** Holds if (determined by this guard) `left < right + k` must be `isLessThan` in `block`. + If `isLessThan = false` then this implies `left >= right + k`. */ + cached predicate ensuresLt(Expr left, Expr right, int k, BasicBlock block, boolean isLessThan) { + exists(boolean testIsTrue | + compares_lt(this, left, right, k, isLessThan, testIsTrue) and this.controls(block, testIsTrue) + ) + } + + /** Holds if (determined by this guard) `left == right + k` evaluates to `areEqual` if this expression evaluates to `testIsTrue`. */ + cached predicate comparesEq(Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) { + compares_eq(this, left, right, k, areEqual, testIsTrue) + } + + /** Holds if (determined by this guard) `left == right + k` must be `areEqual` in `block`. + If `areEqual = false` then this implies `left != right + k`. */ + cached predicate ensuresEq(Expr left, Expr right, int k, BasicBlock block, boolean areEqual) { + exists(boolean testIsTrue | + compares_eq(this, left, right, k, areEqual, testIsTrue) and this.controls(block, testIsTrue) + ) + } + + /** + * Holds if this condition controls `block`, meaning that `block` is only + * entered if the value of this condition is `testIsTrue`. This helper + * predicate does not necessarily hold for binary logical operations like + * `&&` and `||`. See the detailed explanation on predicate `controls`. + */ + private predicate controlsBlock(BasicBlock controlled, boolean testIsTrue) { + exists(BasicBlock thisblock + | thisblock.contains(this) + | exists(BasicBlock succ + | testIsTrue = true and succ = this.getATrueSuccessor() or + testIsTrue = false and succ = this.getAFalseSuccessor() + | bbDominates(succ, controlled) and + forall(BasicBlock pred + | pred.getASuccessor() = succ + | pred = thisblock or bbDominates(succ, pred) or not reachable(pred)))) + } +} + +private predicate is_condition(Expr guard) { + guard.isCondition() or + is_condition(guard.(BinaryLogicalOperation).getAnOperand()) or + exists(NotExpr cond | is_condition(cond) and cond.getOperand() = guard) +} + + + /* Simplify conditions in the source to the canonical form l op r + k. + */ + +/* Simplification of equality expressions */ + +/** + * Holds if `left == right + k` is `areEqual` given that test is `testIsTrue`. + * + * Beware making mistaken logical implications here relating `areEqual` and `testIsTrue`. + */ +private predicate compares_eq(Expr test, Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) { + /* The simple case where the test *is* the comparison so areEqual = testIsTrue xor eq. */ + exists(boolean eq | simple_comparison_eq(test, left, right, k, eq) | + areEqual = true and testIsTrue = eq or areEqual = false and testIsTrue = eq.booleanNot() + ) + or + logical_comparison_eq(test, left, right, k, areEqual, testIsTrue) + or + /* a == b + k => b == a - k */ + exists(int mk | k = -mk | compares_eq(test, right, left, mk, areEqual, testIsTrue)) + or + complex_eq(test, left, right, k, areEqual, testIsTrue) + or + /* (x is true => (left == right + k)) => (!x is false => (left == right + k)) */ + exists(boolean isFalse | testIsTrue = isFalse.booleanNot() | + compares_eq(test.(NotExpr).getOperand(), left, right, k, areEqual, isFalse) + ) +} + +/** If `test => part` and `part => left == right + k` then `test => left == right + k`. + Similarly for the case where `test` is false. */ +private +predicate logical_comparison_eq(BinaryLogicalOperation test, Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) { + exists(boolean partIsTrue, Expr part | + test.impliesValue(part, partIsTrue, testIsTrue) | + compares_eq(part, left, right, k, areEqual, partIsTrue) + ) +} + +/** Rearrange various simple comparisons into `left == right + k` form. */ +private predicate simple_comparison_eq(ComparisonOperation cmp, Expr left, Expr right, int k, boolean areEqual) { + left = cmp.getLeftOperand() and cmp.getOperator() = "==" and right = cmp.getRightOperand() and k = 0 and areEqual = true + or + left = cmp.getLeftOperand() and cmp.getOperator() = "!=" and right = cmp.getRightOperand() and k = 0 and areEqual = false +} + +private predicate complex_eq(ComparisonOperation cmp, Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) { + sub_eq(cmp, left, right, k, areEqual, testIsTrue) + or + add_eq(cmp, left, right, k, areEqual, testIsTrue) +} + + +/* left - x == right + c => left == right + (c+x) + left == (right - x) + c => left == right + (c-x) */ +private predicate sub_eq(ComparisonOperation cmp, Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) { + exists(SubExpr lhs, int c, int x | compares_eq(cmp, lhs, right, c, areEqual, testIsTrue) and + left = lhs.getLeftOperand() and x = int_value(lhs.getRightOperand()) + and k = c + x + ) + or + exists(SubExpr rhs, int c, int x | compares_eq(cmp, left, rhs, c, areEqual, testIsTrue) and + right = rhs.getLeftOperand() and x = int_value(rhs.getRightOperand()) + and k = c - x + ) +} + +/* left + x == right + c => left == right + (c-x) + left == (right + x) + c => left == right + (c+x) */ +private predicate add_eq(ComparisonOperation cmp, Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) { + exists(AddExpr lhs, int c, int x | compares_eq(cmp, lhs, right, c, areEqual, testIsTrue) and + (left = lhs.getLeftOperand() and x = int_value(lhs.getRightOperand()) + or + left = lhs.getRightOperand() and x = int_value(lhs.getLeftOperand()) + ) + and k = c - x + ) + or + exists(AddExpr rhs, int c, int x | compares_eq(cmp, left, rhs, c, areEqual, testIsTrue) and + (right = rhs.getLeftOperand() and x = int_value(rhs.getRightOperand()) + or + right = rhs.getRightOperand() and x = int_value(rhs.getLeftOperand()) + ) + and k = c + x + ) +} + +/* Simplification of inequality expressions + * Simplify conditions in the source to the canonical form l < r + k. + */ + +/** Holds if `left < right + k` evaluates to `isLt` given that test is `testIsTrue`. */ +private predicate compares_lt(Expr test, Expr left, Expr right, int k, boolean isLt, boolean testIsTrue) { + /* In the simple case, the test is the comparison, so isLt = testIsTrue */ + simple_comparison_lt(test, left, right, k) and isLt = true and testIsTrue = true + or + simple_comparison_lt(test, left, right, k) and isLt = false and testIsTrue = false + or + logical_comparison_lt(test, left, right, k, isLt, testIsTrue) + or + complex_lt(test, left, right, k, isLt, testIsTrue) + or + /* (not (left < right + k)) => (left >= right + k) */ + exists(boolean isGe | isLt = isGe.booleanNot() | + compares_ge(test, left, right, k, isGe, testIsTrue) + ) + or + /* (x is true => (left < right + k)) => (!x is false => (left < right + k)) */ + exists(boolean isFalse | testIsTrue = isFalse.booleanNot() | + compares_lt(test.(NotExpr).getOperand(), left, right, k, isLt, isFalse) + ) +} + +/** `(a < b + k) => (b > a - k) => (b >= a + (1-k))` */ +private predicate compares_ge(Expr test, Expr left, Expr right, int k, boolean isGe, boolean testIsTrue) { + exists(int onemk | k = 1 - onemk | compares_lt(test, right, left, onemk, isGe, testIsTrue)) +} + +/** If `test => part` and `part => left < right + k` then `test => left < right + k`. + Similarly for the case where `test` evaluates false. */ +private +predicate logical_comparison_lt(BinaryLogicalOperation test, Expr left, Expr right, int k, boolean isLt, boolean testIsTrue) { + exists(boolean partIsTrue, Expr part | + test.impliesValue(part, partIsTrue, testIsTrue) | + compares_lt(part, left, right, k, isLt, partIsTrue) + ) +} + +/** Rearrange various simple comparisons into `left < right + k` form. */ +private predicate simple_comparison_lt(ComparisonOperation cmp, Expr left, Expr right, int k) { + left = cmp.getLeftOperand() and cmp.getOperator() = "<" and right = cmp.getRightOperand() and k = 0 + or + left = cmp.getLeftOperand() and cmp.getOperator() = "<=" and right = cmp.getRightOperand() and k = 1 + or + right = cmp.getLeftOperand() and cmp.getOperator() = ">" and left = cmp.getRightOperand() and k = 0 + or + right = cmp.getLeftOperand() and cmp.getOperator() = ">=" and left = cmp.getRightOperand() and k = 1 +} + +private predicate complex_lt(ComparisonOperation cmp, Expr left, Expr right, int k, boolean isLt, boolean testIsTrue) { + sub_lt(cmp, left, right, k, isLt, testIsTrue) + or + add_lt(cmp, left, right, k, isLt, testIsTrue) +} + + +/* left - x < right + c => left < right + (c+x) + left < (right - x) + c => left < right + (c-x) */ +private predicate sub_lt(ComparisonOperation cmp, Expr left, Expr right, int k, boolean isLt, boolean testIsTrue) { + exists(SubExpr lhs, int c, int x | compares_lt(cmp, lhs, right, c, isLt, testIsTrue) and + left = lhs.getLeftOperand() and x = int_value(lhs.getRightOperand()) + and k = c + x + ) + or + exists(SubExpr rhs, int c, int x | compares_lt(cmp, left, rhs, c, isLt, testIsTrue) and + right = rhs.getLeftOperand() and x = int_value(rhs.getRightOperand()) + and k = c - x + ) +} + +/* left + x < right + c => left < right + (c-x) + left < (right + x) + c => left < right + (c+x) */ +private predicate add_lt(ComparisonOperation cmp, Expr left, Expr right, int k, boolean isLt, boolean testIsTrue) { + exists(AddExpr lhs, int c, int x | compares_lt(cmp, lhs, right, c, isLt, testIsTrue) and + (left = lhs.getLeftOperand() and x = int_value(lhs.getRightOperand()) + or + left = lhs.getRightOperand() and x = int_value(lhs.getLeftOperand()) + ) + and k = c - x + ) + or + exists(AddExpr rhs, int c, int x | compares_lt(cmp, left, rhs, c, isLt, testIsTrue) and + (right = rhs.getLeftOperand() and x = int_value(rhs.getRightOperand()) + or + right = rhs.getRightOperand() and x = int_value(rhs.getLeftOperand()) + ) + and k = c + x + ) +} + +/** The int value of integer constant expression. */ +private int int_value(Expr e) { + e.getUnderlyingType() instanceof IntegralType + and + result = e.getValue().toInt() +} + +/** An `SsaDefinition` with an additional predicate `isLt`. */ +class GuardedSsa extends SsaDefinition { + + /** Holds if this `SsaDefinition` is guarded such that `this(var) < gt + k` is `testIsTrue` in `block`. */ + predicate isLt(LocalScopeVariable var, Expr gt, int k, BasicBlock block, boolean testIsTrue) { + exists(Expr luse, GuardCondition test | this.getAUse(var) = luse | + test.ensuresLt(luse, gt, k, block, testIsTrue) + ) + } + +} + diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/LocalScopeVariableReachability.qll b/cpp/ql/src/semmle/code/cpp/controlflow/LocalScopeVariableReachability.qll new file mode 100644 index 000000000000..f89171263959 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/controlflow/LocalScopeVariableReachability.qll @@ -0,0 +1,340 @@ +import cpp + +/** + * A reachability analysis for control-flow nodes involving stack variables. + * This defines sources, sinks, and any other configurable aspect of the + * analysis. Multiple analyses can coexist. To create an analysis, extend this + * class with a subclass whose characteristic predicate is a unique singleton + * string. For example, write + * + * ``` + * class MyAnalysisConfiguration extends LocalScopeVariableReachability { + * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } + * // Override `isSource` and `isSink`. + * // Override `isBarrier`. + * } + * ``` + * + * Then, to query whether there is flow between some source and sink, call the + * `reaches` predicate on an instance of `MyAnalysisConfiguration`. + */ +abstract class LocalScopeVariableReachability extends string { + bindingset[this] + LocalScopeVariableReachability() { length() >= 0 } + + /** Holds if `node` is a source for the reachability analysis using variable `v`. */ + abstract predicate isSource(ControlFlowNode node, LocalScopeVariable v); + + /** Holds if `sink` is a (potential) sink for the reachability analysis using variable `v`. */ + abstract predicate isSink(ControlFlowNode node, LocalScopeVariable v); + + /** Holds if `node` is a barrier for the reachability analysis using variable `v`. */ + abstract predicate isBarrier(ControlFlowNode node, LocalScopeVariable v); + + /** + * Holds if the source node `source` can reach the sink `sink` without crossing + * a barrier. This is (almost) equivalent to the following QL predicate but + * uses basic blocks internally for better performance: + * + * ``` + * predicate reaches(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) { + * reachesImpl(source, v, sink) + * and + * isSink(sink, v) + * } + * + * predicate reachesImpl(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) { + * sink = source.getASuccessor() and isSource(source, v) + * or + * exists(ControlFlowNode mid | reachesImpl(source, v, mid) | + * not isBarrier(mid, v) + * and + * sink = mid.getASuccessor() + * ) + * } + * ``` + * + * In addition to using a better performing implementation, this analysis + * accounts for loops where the condition is provably true upon entry. + */ + predicate reaches(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) { + /* Implementation detail: the predicates in this class are a generalization of + * those in DefinitionsAndUses.qll, and should be kept in sync. + * + * Unfortunately, caching of abstract predicates does not work well, so the + * predicates in DefinitionsAndUses.qll cannot use this library. + */ + exists(BasicBlock bb, int i | + isSource(source, v) and + bb.getNode(i) = source and + not bb.isUnreachable() | + exists(int j | + j > i + and + sink = bb.getNode(j) + and + isSink(sink, v) + and + not exists(int k | isBarrier(bb.getNode(k), v) | k in [i + 1 .. j - 1]) + ) + or + not exists(int k | isBarrier(bb.getNode(k), v) | k > i) and + bbSuccessorEntryReaches(bb, v, sink, _) + ) + } + + private predicate bbSuccessorEntryReaches(BasicBlock bb, SemanticStackVariable v, ControlFlowNode node, boolean skipsFirstLoopAlwaysTrueUponEntry) { + exists(BasicBlock succ, boolean succSkipsFirstLoopAlwaysTrueUponEntry | + bbSuccessorEntryReachesLoopInvariant(bb, succ, skipsFirstLoopAlwaysTrueUponEntry, succSkipsFirstLoopAlwaysTrueUponEntry) | + bbEntryReachesLocally(succ, v, node) and + succSkipsFirstLoopAlwaysTrueUponEntry = false + or + not isBarrier(succ.getNode(_), v) and + bbSuccessorEntryReaches(succ, v, node, succSkipsFirstLoopAlwaysTrueUponEntry) + ) + } + + private predicate bbEntryReachesLocally(BasicBlock bb, SemanticStackVariable v, ControlFlowNode node) { + exists(int n | + node = bb.getNode(n) and isSink(node, v) | + not exists(int m | m < n | isBarrier(bb.getNode(m), v)) + ) + } +} + +/** + * Holds if `bb` contains the entry point `loop` for a loop at position `i`. + * The condition of that loop is provably true upon entry but not provably + * true in general (if it were, the false-successor had already been removed + * from the CFG). + * + * Examples: + * ``` + * for (int i = 0; i < 2; i++) { } // always true upon entry + * for (int i = 0; true; i++) { } // always true + * ``` + */ +private predicate bbLoopEntryConditionAlwaysTrueAt(BasicBlock bb, int i, ControlFlowNode loop) { + exists(Expr condition | + loopConditionAlwaysTrueUponEntry(loop, condition) and + not conditionAlwaysTrue(condition) and + bb.getNode(i) = loop + ) +} + +/** + * Basic block `pred` ends with a condition belonging to a loop, and that + * condition is provably true upon entry. Basic block `succ` is a successor + * of `pred`, and `skipsLoop` indicates whether `succ` is the false-successor + * of `pred`. + */ +private predicate bbLoopConditionAlwaysTrueUponEntrySuccessor(BasicBlock pred, BasicBlock succ, boolean skipsLoop) { + succ = pred.getASuccessor() and + exists(ControlFlowNode last | + last = pred.getEnd() and + loopConditionAlwaysTrueUponEntry(_, last) and + if succ = pred.getAFalseSuccessor() then + skipsLoop = true + else + skipsLoop = false + ) +} + +/** + * Loop invariant for `bbSuccessorEntryReaches`: + * + * - `succ` is a successor of `pred`. + * - `predSkipsFirstLoopAlwaysTrueUponEntry`: whether the path from + * `pred` (via `succ`) skips the first loop where the condition is + * provably true upon entry. + * - `succSkipsFirstLoopAlwaysTrueUponEntry`: whether the path from + * `succ` skips the first loop where the condition is provably true + * upon entry. + * - If `pred` contains the entry point of a loop where the condition + * is provably true upon entry, then `succ` is not allowed to skip + * that loop (`succSkipsFirstLoopAlwaysTrueUponEntry = false`). + */ +predicate bbSuccessorEntryReachesLoopInvariant(BasicBlock pred, BasicBlock succ, boolean predSkipsFirstLoopAlwaysTrueUponEntry, boolean succSkipsFirstLoopAlwaysTrueUponEntry) { + succ = pred.getASuccessor() and + (succSkipsFirstLoopAlwaysTrueUponEntry = true or succSkipsFirstLoopAlwaysTrueUponEntry = false) and + ( + // The edge from `pred` to `succ` is from a loop condition provably + // true upon entry, so the value of `predSkipsFirstLoopAlwaysTrueUponEntry` + // is determined by whether the true edge or the false edge is chosen, + // regardless of the value of `succSkipsFirstLoopAlwaysTrueUponEntry`. + bbLoopConditionAlwaysTrueUponEntrySuccessor(pred, succ, predSkipsFirstLoopAlwaysTrueUponEntry) + or + ( + // The edge from `pred` to `succ` is _not_ from a loop condition provably + // true upon entry, so the values of `predSkipsFirstLoopAlwaysTrueUponEntry` + // and `succSkipsFirstLoopAlwaysTrueUponEntry` must be the same. + not bbLoopConditionAlwaysTrueUponEntrySuccessor(pred, _, _) and + succSkipsFirstLoopAlwaysTrueUponEntry = predSkipsFirstLoopAlwaysTrueUponEntry and + // Moreover, if `pred` contains the entry point of a loop where the + // condition is provably true upon entry, then `succ` is not allowed + // to skip that loop, and hence `succSkipsFirstLoopAlwaysTrueUponEntry = false`. + (bbLoopEntryConditionAlwaysTrueAt(pred, _, _) implies succSkipsFirstLoopAlwaysTrueUponEntry = false) + ) + ) +} + +/** + * Reachability analysis for control-flow nodes involving stack variables. + * Unlike `LocalScopeVariableReachability`, this analysis takes variable + * reassignments into account. + * + * This class is used like `LocalScopeVariableReachability`, except that + * subclasses should override `isSourceActual` and `isSinkActual` instead of + * `isSource` and `isSink`, and that there is a `reachesTo` predicate in + * addition to `reaches`. + */ +abstract class LocalScopeVariableReachabilityWithReassignment extends LocalScopeVariableReachability { + bindingset[this] + LocalScopeVariableReachabilityWithReassignment() { length() >= 0 } + + /** Override this predicate rather than `isSource` (`isSource` is used internally). */ + abstract predicate isSourceActual(ControlFlowNode node, LocalScopeVariable v); + + /** Override this predicate rather than `isSink` (`isSink` is used internally). */ + abstract predicate isSinkActual(ControlFlowNode node, LocalScopeVariable v); + + /** + * Holds if the source node `source` can reach the sink `sink` without crossing + * a barrier, taking reassignments into account. This is (almost) equivalent + * to the following QL predicate, but uses basic blocks internally for better + * performance: + * + * ``` + * predicate reaches(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) { + * reachesImpl(source, v, sink) + * and + * isSinkActual(sink, v) + * } + * + * predicate reachesImpl(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) { + * isSourceActual(source, v) + * and + * ( + * sink = source.getASuccessor() + * or + * exists(ControlFlowNode mid, SemanticStackVariable v0 | reachesImpl(source, v0, mid) | + * // ordinary successor + * not isBarrier(mid, v) and + * sink = mid.getASuccessor() and + * v = v0 + * or + * // reassigned from v0 to v + * exprDefinition(v, mid, v0.getAnAccess()) and + * sink = mid.getASuccessor() + * ) + * ) + * } + * ``` + * + * In addition to using a better performing implementation, this analysis + * accounts for loops where the condition is provably true upon entry. + */ + override predicate reaches(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) { + reachesTo(source, v, sink, _) + } + + /** + * As `reaches`, but also specifies the last variable it was reassigned to (`v0`). + */ + predicate reachesTo(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink, SemanticStackVariable v0) { + exists(ControlFlowNode def | + actualSourceReaches(source, v, def, v0) and + LocalScopeVariableReachability.super.reaches(def, v0, sink) and + isSinkActual(sink, v0) + ) + } + + private predicate actualSourceReaches(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode def, SemanticStackVariable v0) { + isSourceActual(source, v) and def = source and v0 = v + or + exists(ControlFlowNode source1, SemanticStackVariable v1 | + actualSourceReaches(source, v, source1, v1) | + reassignment(source1, v1, def, v0) + ) + } + + private predicate reassignment(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode def, SemanticStackVariable v0) { + LocalScopeVariableReachability.super.reaches(source, v, def) and + exprDefinition(v0, def, v.getAnAccess()) + } + + final override predicate isSource(ControlFlowNode node, LocalScopeVariable v) { + isSourceActual(node, v) + or + // Reassignment generates a new (non-actual) source + reassignment(_, _, node, v) + } + + final override predicate isSink(ControlFlowNode node, LocalScopeVariable v) { + isSinkActual(node, v) + or + // Reassignment generates a new (non-actual) sink + exprDefinition(_, node, v.getAnAccess()) + } +} + +/** + * Same as `LocalScopeVariableReachability`, but `isBarrier` works on control-flow + * edges rather than nodes and is therefore parameterized by the original + * source node as well. Otherwise, this class is used like + * `LocalScopeVariableReachability`. + */ +abstract class LocalScopeVariableReachabilityExt extends string { + bindingset[this] + LocalScopeVariableReachabilityExt() { length() >= 0 } + + /** `node` is a source for the reachability analysis using variable `v`. */ + abstract predicate isSource(ControlFlowNode node, LocalScopeVariable v); + + /** `sink` is a (potential) sink for the reachability analysis using variable `v`. */ + abstract predicate isSink(ControlFlowNode node, LocalScopeVariable v); + + /** `node` is a barrier for the reachability analysis using variable `v` and starting from `source`. */ + abstract predicate isBarrier(ControlFlowNode source, ControlFlowNode node, ControlFlowNode next, LocalScopeVariable v); + + /** See `LocalScopeVariableReachability.reaches`. */ + predicate reaches(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) { + exists(BasicBlock bb, int i | + isSource(source, v) and + bb.getNode(i) = source and + not bb.isUnreachable() | + exists(int j | + j > i + and + sink = bb.getNode(j) + and + isSink(sink, v) + and + not exists(int k | isBarrier(source, bb.getNode(k), bb.getNode(k + 1), v) | k in [i .. j - 1]) + ) + or + not exists(int k | isBarrier(source, bb.getNode(k), bb.getNode(k + 1), v) | k >= i) and + bbSuccessorEntryReaches(source, bb, v, sink, _) + ) + } + + private predicate bbSuccessorEntryReaches(ControlFlowNode source, BasicBlock bb, SemanticStackVariable v, ControlFlowNode node, boolean skipsFirstLoopAlwaysTrueUponEntry) { + exists(BasicBlock succ, boolean succSkipsFirstLoopAlwaysTrueUponEntry | + bbSuccessorEntryReachesLoopInvariant(bb, succ, skipsFirstLoopAlwaysTrueUponEntry, succSkipsFirstLoopAlwaysTrueUponEntry) and + not isBarrier(source, bb.getEnd(), succ.getStart(), v) | + bbEntryReachesLocally(source, succ, v, node) and + succSkipsFirstLoopAlwaysTrueUponEntry = false + or + not exists(int k | isBarrier(source, succ.getNode(k), succ.getNode(k + 1), v)) and + bbSuccessorEntryReaches(source, succ, v, node, succSkipsFirstLoopAlwaysTrueUponEntry) + ) + } + + private predicate bbEntryReachesLocally(ControlFlowNode source, BasicBlock bb, SemanticStackVariable v, ControlFlowNode node) { + isSource(source, v) and + exists(int n | + node = bb.getNode(n) and isSink(node, v) | + not exists(int m | m < n | isBarrier(source, bb.getNode(m), bb.getNode(m + 1), v)) + ) + } +} \ No newline at end of file diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/Nullness.qll b/cpp/ql/src/semmle/code/cpp/controlflow/Nullness.qll new file mode 100644 index 000000000000..af2429fecb32 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/controlflow/Nullness.qll @@ -0,0 +1,281 @@ +import cpp +import DefinitionsAndUses + +/** + * A C/C++ literal whose value is considered null. + */ +abstract class NullValue extends Expr +{ +} + +/** + * A C/C++ literal whose value is zero. + */ +class Zero extends NullValue +{ + Zero() { this.(Literal).getValue() = "0" } +} + +/** + * Holds if `var` is null when `checkExpr` evaluates to a true value. + */ +cached +predicate nullCheckExpr(Expr checkExpr, Variable var) +{ + exists(LocalScopeVariable v, AnalysedExpr expr | + var = v and + checkExpr = expr + | + exists(NotExpr notexpr, AnalysedExpr child | + expr = notexpr and notexpr.getOperand() = child and validCheckExpr(child, v)) + or + exists(EQExpr eq, AnalysedExpr child, NullValue zero, int i | + expr = eq and eq.getChild(i) = child and + validCheckExpr(child, v) and eq.getChild(1-i) = zero) + or + exists(NEExpr neq, AnalysedExpr child, NullValue zero, int i | + expr = neq and neq.getChild(i) = child and + nullCheckExpr(child, v) and neq.getChild(1-i) = zero) + or + exists(LogicalAndExpr op, AnalysedExpr child | + expr = op and op.getRightOperand() = child and + nullCheckExpr(child, v)) + or + exists(AssignExpr assign, AnalysedExpr child | + expr = assign and assign.getRValue() = child and + nullCheckExpr(child, v) and not expr.isDef(v)) + or + exists(FunctionCall fc, AnalysedExpr child | + expr = fc and + fc.getTarget().hasQualifiedName("__builtin_expect") and + fc.getArgument(0) = child and nullCheckExpr(child, v)) + ) +} + +/** + * Holds if `var` is non-null when `checkExpr` evaluates to a true value. + */ +cached +predicate validCheckExpr(Expr checkExpr, Variable var) +{ + exists(AnalysedExpr expr, LocalScopeVariable v | + v = var and + expr = checkExpr + | + expr.isUse(v) + or + expr.isDef(v) + or + exists(NotExpr notexpr, AnalysedExpr child | + expr = notexpr and notexpr.getOperand() = child and nullCheckExpr(child, v)) + or + exists(EQExpr eq, AnalysedExpr child, NullValue zero, int i | + expr = eq and eq.getChild(i) = child and + nullCheckExpr(child, v) and eq.getChild(1-i) = zero) + or + exists(NEExpr neq, AnalysedExpr child, NullValue zero, int i | + expr = neq and neq.getChild(i) = child and + validCheckExpr(child, v) and neq.getChild(1-i) = zero) + or + exists(LogicalAndExpr op, AnalysedExpr child | + expr = op and op.getRightOperand() = child and + validCheckExpr(child, v)) + or + exists(AssignExpr assign, AnalysedExpr child | + expr = assign and assign.getRValue() = child and + validCheckExpr(child, v) and not expr.isDef(v)) + or + exists(FunctionCall fc, AnalysedExpr child | + expr = fc and + fc.getTarget().hasQualifiedName("__builtin_expect") and + fc.getArgument(0) = child and validCheckExpr(child, v)) + ) +} + +/** + * An expression that has been extended with member predicates that provide + * information about the role of this expression in nullness checks. + */ +class AnalysedExpr extends Expr { + + /** + * Holds if `v` is null when this expression evaluates to a true value. + */ + predicate isNullCheck(LocalScopeVariable v) { + nullCheckExpr(this, v) + } + + /** + * Holds if `v` is non-null when this expression evaluates to a true value. + */ + predicate isValidCheck(LocalScopeVariable v) { + validCheckExpr(this, v) + } + + /** + * Gets a successor of `this` in the control flow graph, where that successor + * is among the nodes to which control may flow when `this` tests `v` to be + * _null_. + */ + ControlFlowNode getNullSuccessor(LocalScopeVariable v) + { + (this.isNullCheck(v) and result = this.getATrueSuccessor()) + or + (this.isValidCheck(v) and result = this.getAFalseSuccessor()) + } + + /** + * Gets a successor of `this` in the control flow graph, where that successor + * is among the nodes to which control may flow when `this` tests `v` to be + * _not null_. + */ + ControlFlowNode getNonNullSuccessor(LocalScopeVariable v) + { + (this.isNullCheck(v) and result = this.getAFalseSuccessor()) + or + (this.isValidCheck(v) and result = this.getATrueSuccessor()) + } + + /** + * DEPRECATED: Use `getNonNullSuccessor` instead, which does the same. + */ + deprecated + ControlFlowNode getValidSuccessor(LocalScopeVariable v) + { + (this.isValidCheck(v) and result = this.getATrueSuccessor()) + or + (this.isNullCheck(v) and result = this.getAFalseSuccessor()) + } + + /** + * Holds if this is a `VariableAccess` of `v` nested inside a condition. + */ + predicate isUse(LocalScopeVariable v) + { + this.inCondition() and + this = v.getAnAccess() + } + + /** + * Holds if this is an `Assignment` to `v` nested inside a condition. + */ + predicate isDef(LocalScopeVariable v) + { + this.inCondition() and + this.(Assignment).getLValue() = v.getAnAccess() + } + + /** + * Holds if `this` occurs at or below the controlling expression of an `if`, + * `while`, `?:`, or similar. + */ + predicate inCondition() + { + this.isCondition() or + this.getParent().(AnalysedExpr).inCondition() + } +} + +/** + * Holds if `var` is likely to be null at `node`. + */ +cached +predicate checkedNull(Variable var, ControlFlowNode node) +{ + exists(LocalScopeVariable v | v = var | + exists(AnalysedExpr e | + e.getNullSuccessor(v) = node + ) + or + exists(ControlFlowNode pred | + pred = node.getAPredecessor() and + not pred.(AnalysedExpr).isDef(v) and + checkedNull(v, pred) + ) + ) +} + +/** + * Holds if `var` is likely to be non-null at `node`. + */ +cached +predicate checkedValid(Variable var, ControlFlowNode node) +{ + exists(LocalScopeVariable v | v = var | + exists(AnalysedExpr e | + e.getNonNullSuccessor(v) = node + ) + or + exists(ControlFlowNode pred | + pred = node.getAPredecessor() and + not pred.(AnalysedExpr).isDef(v) and + checkedValid(v, pred) + ) + ) +} + +/** + * Holds if `val` is a null literal or a call to a function that may return a + * null literal. + */ +predicate nullValue(Expr val) +{ + val instanceof NullValue + or + callMayReturnNull(val) + or + nullValue(val.(Assignment).getRValue()) +} + +/** + * Holds if the evaluation of `n` may have the effect of, directly or + * indirectly, assigning a null literal to `var`. + */ +predicate nullInit(Variable v, ControlFlowNode n) +{ + exists(Initializer init | + init = n and nullValue(init.getExpr()) and + v.getInitializer() = init) + or + exists(AssignExpr assign | + assign = n and nullValue(assign.getRValue()) and + assign.getLValue() = v.getAnAccess()) +} + +/** + * Holds if `call` may, directly or indirectly, evaluate to a null literal. + */ +predicate callMayReturnNull(Call call) +{ + exists(Options opts | + if opts.overrideReturnsNull(call) then + opts.returnsNull(call) + else + mayReturnNull(call.(FunctionCall).getTarget()) + ) +} + +/** + * Holds if `f` may, directly or indirectly, return a null literal. + */ +predicate mayReturnNull(Function f) +{ + f.getQualifiedName() = "malloc" + or + f.getQualifiedName() = "calloc" + or +// f.getQualifiedName() = "strchr" +// or +// f.getQualifiedName() = "strstr" +// or + exists(ReturnStmt ret | + nullValue(ret.getExpr()) and + ret.getEnclosingFunction() = f) + or + exists(ReturnStmt ret, Expr returned, ControlFlowNode init, LocalScopeVariable v | + ret.getExpr() = returned and + nullInit(v, init) and + definitionUsePair(v, init, returned) and + not(checkedValid(v, returned)) and + ret.getEnclosingFunction() = f) +} diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/SSA.qll b/cpp/ql/src/semmle/code/cpp/controlflow/SSA.qll new file mode 100644 index 000000000000..725f2b057e25 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/controlflow/SSA.qll @@ -0,0 +1,170 @@ +import cpp +import semmle.code.cpp.controlflow.Dominance +import SSAUtils + +library class StandardSSA extends SSAHelper { + StandardSSA() { this = 0 } +} + +/** + * A definition of one or more SSA variables, including phi node definitions. + * An _SSA variable_, as defined in the literature, is effectively the pair of + * an `SsaDefinition d` and a `LocalScopeVariable v`, written `(d, v)` in this + * documentation. Note that definitions and uses can be coincident due to the + * presence of parameter definitions and phi nodes. + * + * Not all `LocalScopeVariable`s of a function have SSA definitions. If the variable + * has its address taken, either explicitly or implicitly, then it is excluded + * from analysis. `SsaDefinition`s are not generated in locations that are + * statically seen to be unreachable. + */ +class SsaDefinition extends @cfgnode { + + SsaDefinition() { + exists(StandardSSA x | x.ssa_defn(_, (ControlFlowNode)this, _, _)) + } + + /** + * Gets a variable corresponding to an SSA LocalScopeVariable defined by + * this definition. + */ + LocalScopeVariable getAVariable() { + exists(StandardSSA x | x.ssa_defn(result, (ControlFlowNode)this, _, _)) + } + + /** Gets a textual representation of this element. */ + string toString() { + result = "SSA definition" + } + + /** + * Gets a string representation of the SSA variable represented by the pair + * (this, v). + */ + string toString(LocalScopeVariable v) { + exists(StandardSSA x | result = x.toString((ControlFlowNode)this, v)) + } + + /** Gets a use of the SSA variable represented by the pair (this, v). */ + VariableAccess getAUse(LocalScopeVariable v) { + exists(StandardSSA x | result = x.getAUse((ControlFlowNode)this, v)) + } + + /** + * Gets the control-flow node for this definition. This will usually be the + * control-flow node that assigns to this variable as a side effect, but + * there are some exceptions. If `this` is defined by initialization, the + * result is the value of `Initializer.getExpr()` for that initialization. + * If `this` is a function parameter (see `definedByParameter`), the result + * will be the function entry point. If `this` variable is defined by being + * passed as a reference in a function call, including overloaded + * operators, the result will be the `VariableAccess` expression for this + * parameter. If `this` is a phi node (see `isPhiNode`), the result will be + * the node where control flow is joined from multiple paths. + */ + ControlFlowNode getDefinition() { + result = this + } + + BasicBlock getBasicBlock() { + result.contains(getDefinition()) + } + + /** Holds if this definition is a phi node for variable `v`. */ + predicate isPhiNode(LocalScopeVariable v) { + exists(StandardSSA x | x.phi_node(v, (BasicBlock)this)) + } + + Location getLocation() { + result = this.(ControlFlowNode).getLocation() + } + + /** Holds if the SSA variable `(this, p)` is defined by parameter `p`. */ + predicate definedByParameter(Parameter p) { + this = p.getFunction().getEntryPoint() + } + + /** + * Holds if the SSA variable `(result, v)` is an input to the phi definition + * `(this, v)`. + */ + SsaDefinition getAPhiInput(LocalScopeVariable v) { + this.isPhiNode(v) + and + result.reachesEndOfBB(v, this.(BasicBlock).getAPredecessor()) + } + + /** + * Gets the expression assigned to the SSA variable `(this, v)`, if any, + * when it is not a phi definition. The following is an exhaustive list of + * expressions that may be the result of this predicate. + * + * - The contained expression of an `Initializer`. + * - The right-hand side of an `AssignExpr`. + * - An `AssignOperation`. + * - A `CrementOperation`. + * + * In all cases except `PostfixCrementOperation`, the variable `v` will be + * equal to the result of this predicate after evaluation of + * `this.getDefinition()`. + * + * If the SSA variable is defined in other ways than those four (such as + * function parameters or `f(&x)`) there is no result. These cases are + * instead covered via `definedByParameter` and `getDefinition`, + * respectively. + */ + Expr getDefiningValue(LocalScopeVariable v) { + exists(ControlFlowNode def | def = this.getDefinition() | + def = v.getInitializer().getExpr() and def = result + or + exists(AssignExpr assign | def = assign and + assign.getLValue() = v.getAnAccess() and result = assign.getRValue() + ) + or + exists(AssignOperation assign | def = assign and + assign.getLValue() = v.getAnAccess() and result = assign + ) + or + exists(CrementOperation crement | def = crement and + crement.getOperand() = v.getAnAccess() and result = crement + ) + ) + } + + /** Holds if `(this, v)` reaches the end of basic block `b`. */ + predicate reachesEndOfBB(LocalScopeVariable v, BasicBlock b) { + exists(StandardSSA x | x.ssaDefinitionReachesEndOfBB(v, (ControlFlowNode)this, b)) + } + + /** + * Gets a definition that ultimately defines this variable and is not + * itself a phi node. + */ + SsaDefinition getAnUltimateSsaDefinition(LocalScopeVariable v) { + result = this.getAPhiInput(v).getAnUltimateSsaDefinition(v) + or + v = this.getAVariable() and + not this.isPhiNode(v) and + result = this + } + + /** + * Gets a possible defining expression for `v` at this SSA definition, + * recursing backwards through phi definitions. Not all definitions have a + * defining expression---see the documentation for `getDefiningValue`. + */ + Expr getAnUltimateDefiningValue(LocalScopeVariable v) { + result = this.getAnUltimateSsaDefinition(v).getDefiningValue(v) + } + + /** + * DEPRECATED: this is the old name for `getAnUltimateDefiningValue`. The + * name was confusing as it seemed analogous to `getDefinition` rather than + * `getDefiningValue`. The SSA libraries for other languages use the name + * `getAnUltimateSsaDefinition` to refer to a predicate named + * `getAnUltimateSsaDefinition` in this class. + */ + deprecated Expr getAnUltimateDefinition(LocalScopeVariable v) { + result = this.getAnUltimateDefiningValue(v) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/SSAUtils.qll b/cpp/ql/src/semmle/code/cpp/controlflow/SSAUtils.qll new file mode 100644 index 000000000000..62ec85562476 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/controlflow/SSAUtils.qll @@ -0,0 +1,269 @@ +import cpp +import semmle.code.cpp.controlflow.Dominance +import semmle.code.cpp.controlflow.SSA // must be imported for proper caching of SSAHelper +import semmle.code.cpp.rangeanalysis.RangeSSA // must be imported for proper caching of SSAHelper + +/* The dominance frontier of a block `x` is the set of all blocks `w` such that + * `x` dominates a predecessor of `w` but does not strictly dominate `w`. */ +pragma[noinline] +private predicate dominanceFrontier(BasicBlock x, BasicBlock w) { + bbDominates(x, w.getAPredecessor()) and not bbStrictlyDominates(x, w) +} + +/** + * Extended version of `definition` that also includes parameters but excludes + * static variables. + */ +predicate var_definition(LocalScopeVariable v, ControlFlowNode node) { + not v.isStatic() + and + not addressTakenVariable(v) + and + not unreachable(node) + and + (if isReferenceVar(v) + then // Assignments to reference variables modify the referenced + // value, not the reference itself. So reference variables only + // have two kinds of definition: initializers and parameters. + node = v.getInitializer().getExpr() + else definition(v, node)) + or + v instanceof Parameter and exists(BasicBlock b | b.getStart() = node and not exists(b.getAPredecessor()) and + b = v.(Parameter).getFunction().getEntryPoint()) +} + +/** + * Stack variables that have their address taken are excluded from the + * analysis because the pointer could be used to change the value at + * any moment. + */ +private predicate addressTakenVariable(LocalScopeVariable var) { + // If the type of the variable is a reference type, then it is safe (as + // far as SSA is concerned) to take its address, because this does not + // enable the variable to be modified indirectly. Obviously the + // referenced value can change, but that is not the same thing as + // changing which value the reference points to. SSA tracks the latter, + // but the target of a reference is immutable so reference variables + // always have exactly one definition. + not isReferenceVar(var) + and + + // Find a VariableAccess that takes the address of `var`. + exists (VariableAccess va + | va = var.getAnAccess() and + va.isAddressOfAccessNonConst() and + // If the address is passed to a function then we will trust that it + // is only used to modify the variable for the duration of the + // function call. + not exists(Call call | call.passesByReferenceNonConst(_, va)) + ) +} + +private predicate isReferenceVar(LocalScopeVariable v) { + v.getType().getUnspecifiedType() instanceof ReferenceType +} + +/** + * This predicate is the same as `var_definition`, but annotated with + * the basic block and index of the control flow node. + */ +private predicate variableUpdate(LocalScopeVariable v, ControlFlowNode n, BasicBlock b, int i) { + var_definition(v, n) and n = b.getNode(i) +} + +private predicate ssa_use(LocalScopeVariable v, VariableAccess node, BasicBlock b, int index) { + useOfVar(v, node) and b.getNode(index) = node +} + +private predicate live_at_start_of_bb(LocalScopeVariable v, BasicBlock b) { + exists (int i | ssa_use(v, _, b, i) | + not exists (int j | variableUpdate(v, _, b, j) | j < i)) + or + (live_at_exit_of_bb(v, b) and not variableUpdate(v, _, b, _)) +} + +pragma[noinline] +private predicate live_at_exit_of_bb(LocalScopeVariable v, BasicBlock b) { + live_at_start_of_bb(v, b.getASuccessor()) +} + +/** Common SSA logic for standard SSA and range-analysis SSA. */ +cached library class SSAHelper extends int { + /* 0 = StandardSSA, 1 = RangeSSA */ + cached SSAHelper() { this in [0 .. 1] } + + /** + * Override to insert a custom phi node for variable `v` at the start of + * basic block `b`. + */ + cached predicate custom_phi_node(LocalScopeVariable v, BasicBlock b) { none() } + + /** + * Remove any custom phi nodes that are invalid. + */ + private predicate sanitized_custom_phi_node(LocalScopeVariable v, BasicBlock b) { + custom_phi_node(v,b) and + not addressTakenVariable(v) and + not isReferenceVar(v) and + b.isReachable() + } + + /** + * Holds if there is a phi node for variable `v` at the start of basic block + * `b`. + */ + cached predicate phi_node(LocalScopeVariable v, BasicBlock b) { + frontier_phi_node(v,b) or sanitized_custom_phi_node(v,b) + } + + /** + * A phi node is required for variable `v` at the start of basic block `b` + * if there exists a basic block `x` such that `b` is in the dominance + * frontier of `x` and `v` is defined in `x` (including phi-nodes as + * definitions). This is known as the iterated dominance frontier. See + * Modern Compiler Implementation by Andrew Appel. + */ + private predicate frontier_phi_node(LocalScopeVariable v, BasicBlock b) { + exists(BasicBlock x | dominanceFrontier(x, b) and ssa_defn(v, _, x, _)) + /* We can also eliminate those nodes where the variable is not live on any incoming edge */ + and live_at_start_of_bb(v, b) + } + + /** + * Holds if `v` is defined, for the purpose of SSA, at `node`, which is at + * position `index` in block `b`. This includes definitions from phi nodes. + */ + cached predicate ssa_defn(LocalScopeVariable v, ControlFlowNode node, BasicBlock b, int index) { + phi_node(v, b) and b.getStart() = node and index = -1 + or + variableUpdate(v, node, b, index) + } + + /* + * The construction of SSA form ensures that each use of a variable is + * dominated by its definition. A definition of an SSA variable therefore + * reaches a `ControlFlowNode` if it is the _closest_ SSA variable definition + * that dominates the node. If two definitions dominate a node then one must + * dominate the other, so therefore the definition of _closest_ is given by the + * dominator tree. Thus, reaching definitions can be calculated in terms of + * dominance. + */ + + /** + * A ranking of the indices `i` at which there is an SSA definition or use of + * `v` in the basic block `b`. + * + * Basic block indices are translated to rank indices in order to skip + * irrelevant indices at which there is no definition or use when traversing + * basic blocks. + */ + private predicate defUseRank(LocalScopeVariable v, BasicBlock b, int rankix, int i) { + i = rank[rankix](int j | ssa_defn(v, _, b, j) or ssa_use(v, _, b, j)) + } + + /** + * Gets the maximum rank index for the given variable `v` and basic block + * `b`. This will be the number of defs/uses of `v` in `b` plus one, where + * the extra rank at the end represents a position past the last node in + * the block. + */ + private int lastRank(LocalScopeVariable v, BasicBlock b) { + result = max(int rankix | defUseRank(v, b, rankix, _)) + 1 + } + + /** + * Holds if SSA variable `(v, def)` is defined at rank index `rankix` in + * basic block `b`. + */ + private predicate ssaDefRank(LocalScopeVariable v, ControlFlowNode def, BasicBlock b, int rankix) { + exists(int i + | ssa_defn(v, def, b, i) and + defUseRank(v, b, rankix, i)) + } + + /** + * Holds if SSA variable `(v, def)` reaches the rank index `rankix` in its + * own basic block `b` before being overwritten by another definition of + * `v` that comes _at or after_ the reached node. Reaching a node means + * that the definition is visible to any _use_ at that node. + */ + private predicate ssaDefReachesRank(LocalScopeVariable v, ControlFlowNode def, BasicBlock b, int rankix) { + // A definition should not reach its own node unless a loop allows it. + // When nodes are both definitions and uses for the same variable, the + // use is understood to happen _before_ the definition. Phi nodes are + // at rankidx -1 and will therefore always reach the first node in the + // basic block. + ssaDefRank(v, def, b, rankix-1) + or + (ssaDefReachesRank(v, def, b, rankix-1) and + rankix <= lastRank(v, b) and // Without this, the predicate would be infinite. + not ssaDefRank(v, _, b, rankix-1)) // Range is inclusive of but not past next def. + } + + /** Holds if SSA variable `(v, def)` reaches the end of block `b`. */ + cached predicate ssaDefinitionReachesEndOfBB(LocalScopeVariable v, ControlFlowNode def, BasicBlock b) { + (live_at_exit_of_bb(v, b) and ssaDefReachesRank(v, def, b, lastRank(v, b))) + or + exists (BasicBlock idom | + ssaDefinitionReachesEndOfBB(v, def, idom) and + noDefinitionsSinceIDominator(v, idom, b) + ) + } + + /** + * Helper predicate for ssaDefinitionReachesEndOfBB. If there is no + * definition of `v` in basic block `b`, then any definition of `v` + * that reaches the end of `idom` (the immediate dominator of `b`) also + * reaches the end of `b`. + */ + pragma[noinline] + private predicate noDefinitionsSinceIDominator(LocalScopeVariable v, BasicBlock idom, BasicBlock b) { + bbIDominates(idom, b) and // It is sufficient to traverse the dominator graph, cf. discussion above. + live_at_exit_of_bb(v, b) and + not ssa_defn(v, _, b, _) + } + + /** + * Holds if SSA variable `(v, def)` reaches `use` within the same basic + * block, where `use` is a `VariableAccess` of `v`. + */ + private predicate ssaDefinitionReachesUseWithinBB(LocalScopeVariable v, ControlFlowNode def, Expr use) { + exists (BasicBlock b, int rankix, int i + | ssaDefReachesRank(v, def, b, rankix) and + defUseRank(v, b, rankix, i) and + ssa_use(v, use, b, i)) + } + + /** + * Holds if SSA variable `(v, def)` reaches the control-flow node `use`. + */ + private + predicate ssaDefinitionReaches(LocalScopeVariable v, ControlFlowNode def, Expr use) { + ssaDefinitionReachesUseWithinBB(v, def, use) or + exists (BasicBlock b + | ssa_use(v, use, b, _) and + ssaDefinitionReachesEndOfBB(v, def, b.getAPredecessor()) and + not ssaDefinitionReachesUseWithinBB(v, _, use)) + } + + /** + * Gets a string representation of the SSA variable represented by the pair + * `(node, v)`. + */ + cached string toString(ControlFlowNode node, LocalScopeVariable v) { + if phi_node(v, (BasicBlock)node) then + result = "SSA phi(" + v.getName() + ")" + else + (ssa_defn(v, node, _, _) and result = "SSA def(" + v.getName() + ")") + } + + /** + * Holds if SSA variable `(v, def)` reaches `result`, where `result` is an + * access of `v`. + */ + cached VariableAccess getAUse(ControlFlowNode def, LocalScopeVariable v) { + ssaDefinitionReaches(v, def, result) + and + ssa_use(v, result, _, _) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/StackVariableReachability.qll b/cpp/ql/src/semmle/code/cpp/controlflow/StackVariableReachability.qll new file mode 100644 index 000000000000..8ebf850cd5bc --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/controlflow/StackVariableReachability.qll @@ -0,0 +1,329 @@ +import cpp + +/** + * DEPRECATED: use `LocalScopeVariableReachability` instead. + * + * A reachability analysis for control-flow nodes involving stack variables. + */ +deprecated abstract class StackVariableReachability extends string { + bindingset[this] + StackVariableReachability() { length() >= 0 } + + /** Holds if `node` is a source for the reachability analysis using variable `v`. */ + abstract predicate isSource(ControlFlowNode node, StackVariable v); + + /** Holds if `sink` is a (potential) sink for the reachability analysis using variable `v`. */ + abstract predicate isSink(ControlFlowNode node, StackVariable v); + + /** Holds if `node` is a barrier for the reachability analysis using variable `v`. */ + abstract predicate isBarrier(ControlFlowNode node, StackVariable v); + + /** + * Holds if the source node `source` can reach the sink `sink` without crossing + * a barrier. This is (almost) equivalent to the following QL predicate but + * uses basic blocks internally for better performance: + * + * ``` + * predicate reaches(ControlFlowNode source, StackVariable v, ControlFlowNode sink) { + * reachesImpl(source, v, sink) + * and + * isSink(sink, v) + * } + * + * predicate reachesImpl(ControlFlowNode source, StackVariable v, ControlFlowNode sink) { + * sink = source.getASuccessor() and isSource(source, v) + * or + * exists(ControlFlowNode mid | reachesImpl(source, v, mid) | + * not isBarrier(mid, v) + * and + * sink = mid.getASuccessor() + * ) + * } + * ``` + * + * In addition to using a better performing implementation, this analysis + * accounts for loops where the condition is provably true upon entry. + */ + predicate reaches(ControlFlowNode source, StackVariable v, ControlFlowNode sink) { + /* Implementation detail: the predicates in this class are a generalization of + * those in DefinitionsAndUses.qll, and should be kept in sync. + * + * Unfortunately, caching of abstract predicates does not work well, so the + * predicates in DefinitionsAndUses.qll cannot use this library. + */ + exists(BasicBlock bb, int i | + isSource(source, v) and + bb.getNode(i) = source and + not bb.isUnreachable() | + exists(int j | + j > i + and + sink = bb.getNode(j) + and + isSink(sink, v) + and + not exists(int k | isBarrier(bb.getNode(k), v) | k in [i + 1 .. j - 1]) + ) + or + not exists(int k | isBarrier(bb.getNode(k), v) | k > i) and + bbSuccessorEntryReaches(bb, v, sink, _) + ) + } + + private predicate bbSuccessorEntryReaches(BasicBlock bb, StackVariable v, ControlFlowNode node, boolean skipsFirstLoopAlwaysTrueUponEntry) { + exists(BasicBlock succ, boolean succSkipsFirstLoopAlwaysTrueUponEntry | + bbSuccessorEntryReachesLoopInvariant(bb, succ, skipsFirstLoopAlwaysTrueUponEntry, succSkipsFirstLoopAlwaysTrueUponEntry) | + bbEntryReachesLocally(succ, v, node) and + succSkipsFirstLoopAlwaysTrueUponEntry = false + or + not isBarrier(succ.getNode(_), v) and + bbSuccessorEntryReaches(succ, v, node, succSkipsFirstLoopAlwaysTrueUponEntry) + ) + } + + private predicate bbEntryReachesLocally(BasicBlock bb, StackVariable v, ControlFlowNode node) { + exists(int n | + node = bb.getNode(n) and isSink(node, v) | + not exists(int m | m < n | isBarrier(bb.getNode(m), v)) + ) + } +} + +/** + * Holds if `bb` contains the entry point `loop` for a loop at position `i`. + * The condition of that loop is provably true upon entry but not provably + * true in general (if it were, the false-successor had already been removed + * from the CFG). + * + * Examples: + * ``` + * for (int i = 0; i < 2; i++) { } // always true upon entry + * for (int i = 0; true; i++) { } // always true + * ``` + */ +private predicate bbLoopEntryConditionAlwaysTrueAt(BasicBlock bb, int i, ControlFlowNode loop) { + exists(Expr condition | + loopConditionAlwaysTrueUponEntry(loop, condition) and + not conditionAlwaysTrue(condition) and + bb.getNode(i) = loop + ) +} + +/** + * Basic block `pred` ends with a condition belonging to a loop, and that + * condition is provably true upon entry. Basic block `succ` is a successor + * of `pred`, and `skipsLoop` indicates whether `succ` is the false-successor + * of `pred`. + */ +private predicate bbLoopConditionAlwaysTrueUponEntrySuccessor(BasicBlock pred, BasicBlock succ, boolean skipsLoop) { + succ = pred.getASuccessor() and + exists(ControlFlowNode last | + last = pred.getEnd() and + loopConditionAlwaysTrueUponEntry(_, last) and + if succ = pred.getAFalseSuccessor() then + skipsLoop = true + else + skipsLoop = false + ) +} + +/** + * DEPRECATED: use the corresponding predicate in + * `LocalScopeVariableReachability` instead. + * + * Loop invariant for `bbSuccessorEntryReaches`: + * + * - `succ` is a successor of `pred`. + * - `predSkipsFirstLoopAlwaysTrueUponEntry`: whether the path from + * `pred` (via `succ`) skips the first loop where the condition is + * provably true upon entry. + * - `succSkipsFirstLoopAlwaysTrueUponEntry`: whether the path from + * `succ` skips the first loop where the condition is provably true + * upon entry. + * - If `pred` contains the entry point of a loop where the condition + * is provably true upon entry, then `succ` is not allowed to skip + * that loop (`succSkipsFirstLoopAlwaysTrueUponEntry = false`). + */ +deprecated +predicate bbSuccessorEntryReachesLoopInvariant(BasicBlock pred, BasicBlock succ, boolean predSkipsFirstLoopAlwaysTrueUponEntry, boolean succSkipsFirstLoopAlwaysTrueUponEntry) { + succ = pred.getASuccessor() and + (succSkipsFirstLoopAlwaysTrueUponEntry = true or succSkipsFirstLoopAlwaysTrueUponEntry = false) and + ( + // The edge from `pred` to `succ` is from a loop condition provably + // true upon entry, so the value of `predSkipsFirstLoopAlwaysTrueUponEntry` + // is determined by whether the true edge or the false edge is chosen, + // regardless of the value of `succSkipsFirstLoopAlwaysTrueUponEntry`. + bbLoopConditionAlwaysTrueUponEntrySuccessor(pred, succ, predSkipsFirstLoopAlwaysTrueUponEntry) + or + ( + // The edge from `pred` to `succ` is _not_ from a loop condition provably + // true upon entry, so the values of `predSkipsFirstLoopAlwaysTrueUponEntry` + // and `succSkipsFirstLoopAlwaysTrueUponEntry` must be the same. + not bbLoopConditionAlwaysTrueUponEntrySuccessor(pred, _, _) and + succSkipsFirstLoopAlwaysTrueUponEntry = predSkipsFirstLoopAlwaysTrueUponEntry and + // Moreover, if `pred` contains the entry point of a loop where the + // condition is provably true upon entry, then `succ` is not allowed + // to skip that loop, and hence `succSkipsFirstLoopAlwaysTrueUponEntry = false`. + (bbLoopEntryConditionAlwaysTrueAt(pred, _, _) implies succSkipsFirstLoopAlwaysTrueUponEntry = false) + ) + ) +} + +/** + * DEPRECATED: use `LocalScopeVariableReachabilityWithReassignment` instead. + * + * Reachability analysis for control-flow nodes involving stack variables. + * Unlike `StackVariableReachability`, this analysis takes variable + * reassignments into account. + */ +deprecated abstract class StackVariableReachabilityWithReassignment extends StackVariableReachability { + bindingset[this] + StackVariableReachabilityWithReassignment() { length() >= 0 } + + /** Override this predicate rather than `isSource` (`isSource` is used internally). */ + abstract predicate isSourceActual(ControlFlowNode node, StackVariable v); + + /** Override this predicate rather than `isSink` (`isSink` is used internally). */ + abstract predicate isSinkActual(ControlFlowNode node, StackVariable v); + + /** + * Holds if the source node `source` can reach the sink `sink` without crossing + * a barrier, taking reassignments into account. This is (almost) equivalent + * to the following QL predicate, but uses basic blocks internally for better + * performance: + * + * ``` + * predicate reaches(ControlFlowNode source, StackVariable v, ControlFlowNode sink) { + * reachesImpl(source, v, sink) + * and + * isSinkActual(sink, v) + * } + * + * predicate reachesImpl(ControlFlowNode source, StackVariable v, ControlFlowNode sink) { + * isSourceActual(source, v) + * and + * ( + * sink = source.getASuccessor() + * or + * exists(ControlFlowNode mid, StackVariable v0 | reachesImpl(source, v0, mid) | + * // ordinary successor + * not isBarrier(mid, v) and + * sink = mid.getASuccessor() and + * v = v0 + * or + * // reassigned from v0 to v + * exprDefinition(v, mid, v0.getAnAccess()) and + * sink = mid.getASuccessor() + * ) + * ) + * } + * ``` + * + * In addition to using a better performing implementation, this analysis + * accounts for loops where the condition is provably true upon entry. + */ + override predicate reaches(ControlFlowNode source, StackVariable v, ControlFlowNode sink) { + reachesTo(source, v, sink, _) + } + + /** + * As `reaches`, but also specifies the last variable it was reassigned to (`v0`). + */ + predicate reachesTo(ControlFlowNode source, StackVariable v, ControlFlowNode sink, StackVariable v0) { + exists(ControlFlowNode def | + actualSourceReaches(source, v, def, v0) and + StackVariableReachability.super.reaches(def, v0, sink) and + isSinkActual(sink, v0) + ) + } + + private predicate actualSourceReaches(ControlFlowNode source, StackVariable v, ControlFlowNode def, StackVariable v0) { + isSourceActual(source, v) and def = source and v0 = v + or + exists(ControlFlowNode source1, StackVariable v1 | + actualSourceReaches(source, v, source1, v1) | + reassignment(source1, v1, def, v0) + ) + } + + private predicate reassignment(ControlFlowNode source, StackVariable v, ControlFlowNode def, StackVariable v0) { + StackVariableReachability.super.reaches(source, v, def) and + exprDefinition(v0, def, v.getAnAccess()) + } + + final override predicate isSource(ControlFlowNode node, StackVariable v) { + isSourceActual(node, v) + or + // Reassignment generates a new (non-actual) source + reassignment(_, _, node, v) + } + + final override predicate isSink(ControlFlowNode node, StackVariable v) { + isSinkActual(node, v) + or + // Reassignment generates a new (non-actual) sink + exprDefinition(_, node, v.getAnAccess()) + } +} + +/** + * DEPRECATED: use `LocalScopeVariableReachabilityExt` instead. + * + * Same as `StackVariableReachability`, but `isBarrier` works on control-flow + * edges rather than nodes and is therefore parameterized by the original + * source node as well. + */ +deprecated abstract class StackVariableReachabilityExt extends string { + bindingset[this] + StackVariableReachabilityExt() { length() >= 0 } + + /** `node` is a source for the reachability analysis using variable `v`. */ + abstract predicate isSource(ControlFlowNode node, StackVariable v); + + /** `sink` is a (potential) sink for the reachability analysis using variable `v`. */ + abstract predicate isSink(ControlFlowNode node, StackVariable v); + + /** `node` is a barrier for the reachability analysis using variable `v` and starting from `source`. */ + abstract predicate isBarrier(ControlFlowNode source, ControlFlowNode node, ControlFlowNode next, StackVariable v); + + /** See `StackVariableReachability.reaches`. */ + predicate reaches(ControlFlowNode source, StackVariable v, ControlFlowNode sink) { + exists(BasicBlock bb, int i | + isSource(source, v) and + bb.getNode(i) = source and + not bb.isUnreachable() | + exists(int j | + j > i + and + sink = bb.getNode(j) + and + isSink(sink, v) + and + not exists(int k | isBarrier(source, bb.getNode(k), bb.getNode(k + 1), v) | k in [i .. j - 1]) + ) + or + not exists(int k | isBarrier(source, bb.getNode(k), bb.getNode(k + 1), v) | k >= i) and + bbSuccessorEntryReaches(source, bb, v, sink, _) + ) + } + + private predicate bbSuccessorEntryReaches(ControlFlowNode source, BasicBlock bb, StackVariable v, ControlFlowNode node, boolean skipsFirstLoopAlwaysTrueUponEntry) { + exists(BasicBlock succ, boolean succSkipsFirstLoopAlwaysTrueUponEntry | + bbSuccessorEntryReachesLoopInvariant(bb, succ, skipsFirstLoopAlwaysTrueUponEntry, succSkipsFirstLoopAlwaysTrueUponEntry) and + not isBarrier(source, bb.getEnd(), succ.getStart(), v) | + bbEntryReachesLocally(source, succ, v, node) and + succSkipsFirstLoopAlwaysTrueUponEntry = false + or + not exists(int k | isBarrier(source, succ.getNode(k), succ.getNode(k + 1), v)) and + bbSuccessorEntryReaches(source, succ, v, node, succSkipsFirstLoopAlwaysTrueUponEntry) + ) + } + + private predicate bbEntryReachesLocally(ControlFlowNode source, BasicBlock bb, StackVariable v, ControlFlowNode node) { + isSource(source, v) and + exists(int n | + node = bb.getNode(n) and isSink(node, v) | + not exists(int m | m < n | isBarrier(source, bb.getNode(m), bb.getNode(m + 1), v)) + ) + } +} \ No newline at end of file diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/SubBasicBlocks.qll b/cpp/ql/src/semmle/code/cpp/controlflow/SubBasicBlocks.qll new file mode 100644 index 000000000000..63eca58688cf --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/controlflow/SubBasicBlocks.qll @@ -0,0 +1,183 @@ +// +// NOTE: Maintain this file in synchrony with +// semmlecode-cpp-queries/semmle/code/cpp/dataflow/internal/SubBasicBlocks.qll +// + +/** + * Provides the `SubBasicBlock` class, used for partitioning basic blocks in + * smaller pieces. + */ +import cpp + +/** + * An abstract class that directs where in the control-flow graph a new + * `SubBasicBlock` must start. If a `ControlFlowNode` is an instance of this + * class, that node is guaranteed to be the first node in a `SubBasicBlock`. + * If multiple libraries use the `SubBasicBlock` library, basic blocks may be + * split in more places than either library expects, but nothing should break + * as a direct result of that. + */ +abstract class SubBasicBlockCutNode extends @cfgnode { + SubBasicBlockCutNode() { + // Some control-flow nodes are not in any basic block. This includes + // `Conversion`s, expressions that are evaluated at compile time, default + // arguments, and `Function`s without implementation. + exists(this.(ControlFlowNode).getBasicBlock()) + } + string toString() { result = "SubBasicBlockCutNode" } +} + +/** + * A block that can be smaller than or equal to a `BasicBlock`. Use this class + * when `ControlFlowNode` is too fine-grained and `BasicBlock` too + * coarse-grained. Their successor graph is like that of basic blocks, except + * that the blocks are split up with an extra edge right before any instance of + * the abstract class `SubBasicBlockCutNode`. Users of this library must + * therefore extend `SubBasicBlockCutNode` to direct where basic blocks will be + * split up. + */ +class SubBasicBlock extends @cfgnode { + SubBasicBlock() { + this instanceof BasicBlock + or + this instanceof SubBasicBlockCutNode + } + + /** Gets the basic block in which this `SubBasicBlock` is contained. */ + BasicBlock getBasicBlock() { + result = this.(ControlFlowNode).getBasicBlock() + } + + /** + * Holds if this `SubBasicBlock` comes first in its basic block. This is the + * only condition under which a `SubBasicBlock` may have multiple + * predecessors. + */ + predicate firstInBB() { + exists(BasicBlock bb | this.getPosInBasicBlock(bb) = 0) + } + + /** + * Holds if this `SubBasicBlock` comes last in its basic block. This is the + * only condition under which a `SubBasicBlock` may have multiple successors. + */ + predicate lastInBB() { + exists(BasicBlock bb | + this.getPosInBasicBlock(bb) = countSubBasicBlocksInBasicBlock(bb) - 1 + ) + } + + /** + * Gets the position of this `SubBasicBlock` in its containing basic block + * `bb`, where `bb` is equal to `getBasicBlock()`. + */ + int getPosInBasicBlock(BasicBlock bb) { + exists(int nodePos, int rnk | + bb = this.(ControlFlowNode).getBasicBlock() and + this = bb.getNode(nodePos) and + nodePos = rank[rnk](int i | exists(SubBasicBlock n | n = bb.getNode(i))) and + result = rnk - 1 + ) + } + + /** Gets a successor in the control-flow graph of `SubBasicBlock`s. */ + SubBasicBlock getASuccessor() { + this.lastInBB() and + result = this.getBasicBlock().getASuccessor() + or + exists(BasicBlock bb | + result.getPosInBasicBlock(bb) = this.getPosInBasicBlock(bb) + 1 + ) + } + + /** + * Gets the `pos`th control-flow node in this `SubBasicBlock`. Positions + * start from 0, and the node at position 0 always exists and compares equal + * to `this`. + */ + ControlFlowNode getNode(int pos) { + exists(BasicBlock bb | bb = this.getBasicBlock() | + exists(int thisPos | this = bb.getNode(thisPos) | + result = bb.getNode(thisPos + pos) and + pos >= 0 and + pos < this.getNumberOfNodes() + ) + ) + } + + /** Gets a control-flow node in this `SubBasicBlock`. */ + ControlFlowNode getANode() { + result = this.getNode(_) + } + + /** Holds if `this` contains `node`. */ + predicate contains(ControlFlowNode node) { + node = this.getANode() + } + + /** Gets a predecessor in the control-flow graph of `SubBasicBlock`s. */ + SubBasicBlock getAPredecessor() { + result.getASuccessor() = this + } + + string toString() { result = "SubBasicBlock" } + + /** + * Gets a node such that the control-flow edge `(this, result)` may be taken + * when the final node of this `SubBasicBlock` is a conditional expression + * and evaluates to true. + */ + SubBasicBlock getATrueSuccessor() { + this.lastInBB() and + result = this.getBasicBlock().getATrueSuccessor() + } + + /** + * Gets a node such that the control-flow edge `(this, result)` may be taken + * when the final node of this `SubBasicBlock` is a conditional expression + * and evaluates to false. + */ + SubBasicBlock getAFalseSuccessor() { + this.lastInBB() and + result = this.getBasicBlock().getAFalseSuccessor() + } + + /** + * Gets the number of control-flow nodes in this `SubBasicBlock`. There is + * always at least one. + */ + int getNumberOfNodes() { + exists(BasicBlock bb | bb = this.getBasicBlock() | + exists(int thisPos | this = bb.getNode(thisPos) | + this.lastInBB() and + result = bb.length() - thisPos + or + exists(SubBasicBlock succ, int succPos | + succ.getPosInBasicBlock(bb) = this.getPosInBasicBlock(bb) + 1 and + bb.getNode(succPos) = succ and + result = succPos - thisPos + ) + ) + ) + } + + /** Gets the last control-flow node in this `SubBasicBlock`. */ + ControlFlowNode getEnd() { + result = this.getNode(this.getNumberOfNodes() - 1) + } + + /** Gets the first control-flow node in this `SubBasicBlock`. */ + ControlFlowNode getStart() { + result = this + } + + pragma[noinline] + Function getEnclosingFunction() { + result = this.getStart().getControlFlowScope() + } +} + +/** Gets the number of `SubBasicBlock`s in the given basic block. */ +private int countSubBasicBlocksInBasicBlock(BasicBlock bb) { + result = strictcount(SubBasicBlock sbb | sbb.getBasicBlock() = bb) +} diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/internal/ConstantExprs.qll b/cpp/ql/src/semmle/code/cpp/controlflow/internal/ConstantExprs.qll new file mode 100644 index 000000000000..1aab24acfa35 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/controlflow/internal/ConstantExprs.qll @@ -0,0 +1,1010 @@ +import cpp +private import PrimitiveBasicBlocks + +/** A call to a function known not to return. */ +predicate aborting(FunctionCall c) { + not potentiallyReturningFunctionCall(c) +} + +/** + * Functions that are known not to return. This is normally because the function + * exits the program or longjmps to another location. + */ +predicate abortingFunction(Function f) { + not potentiallyReturningFunction(f) +} + +/** A function call that *may* return; if in doubt, we assume it may. */ +private predicate potentiallyReturningFunctionCall(FunctionCall fc) { + potentiallyReturningFunction(fc.getTarget()) or fc.isVirtual() +} + +/** A function that *may* return; if in doubt, we assume it may. */ +private predicate potentiallyReturningFunction(Function f) { + not getOptions().exits(f) + and + ( + nonAnalyzableFunction(f) + or + // otherwise the exit-point of `f` must be reachable + reachable(f) + ) +} + +/** + * A non-analyzable function is one for which there is no + * path from the entry point to the exit point using the bare + * `successors_extended` relation. This can happen if either the + * function has no entry point (an external/library function), + * or if the CFG for that function is incomplete. + */ +private predicate nonAnalyzableFunction(Function f) { + /* + * Identical to + * ``` + * not successors_extended*(f.getEntryPoint(), f) + * ``` + * but uses primitive basic blocks instead for better performance + * (transitive closure of `PrimitiveBasicBlock.getASuccessor()` is + * faster than transitive closure of the much larger `successors_extended` + * relation). + */ + not exists(PrimitiveBasicBlock bb1, int pos1, PrimitiveBasicBlock bb2, int pos2 | + f.getEntryPoint() = bb1.getNode(pos1) and + f = bb2.getNode(pos2) | + (bb1 = bb2 and pos2 > pos1) + or + bb1.getASuccessor+() = bb2 + ) +} + +/** + * If a condition is provably true, then control-flow edges to its false successors are impossible. + */ +private predicate impossibleFalseEdge(Expr condition, @cfgnode succ) { + conditionAlwaysTrue(condition) + and falsecond(condition,succ) + and not truecond(condition,succ) +} + +/** + * If a condition is provably false, then control-flow edges to its true successors are impossible. + */ +private predicate impossibleTrueEdge(Expr condition, @cfgnode succ) { + conditionAlwaysFalse(condition) + and truecond(condition,succ) + and not falsecond(condition,succ) +} + +/** + * Normally a switch case is for a single value, but it might cover a range + * of values. Either way, this tells us the upper bound. + */ +private int switchCaseRangeEnd(SwitchCase sc) { + if exists(sc.getEndExpr()) + then result = sc.getEndExpr().(CompileTimeConstantInt).getIntValue() + else result = sc.getExpr().(CompileTimeConstantInt).getIntValue() +} + +/** + * Gets a switch expression for switch statement `switch` with + * body `switchBlock`. There may be several such expressions: for example, if + * the condition is `(x ? y : z)` then the result is {`y`, `z`}. + */ +private @cfgnode getASwitchExpr(SwitchStmt switch, Block switchBlock) { + switch.getStmt() = switchBlock and + successors_extended(result, switchBlock) +} + +/** + * If a switch provably never chooses `sc`, then the control-flow edge + * from `switchBlock` to `sc` is impossible. This considers only non-`default` + * switch cases. + */ +private predicate impossibleSwitchEdge(Block switchBlock, SwitchCase sc) { + not sc instanceof DefaultCase and + exists(SwitchStmt switch | + switch = sc.getSwitchStmt() + and switch.getStmt() = switchBlock + // If all of the successors have known values, and none of those + // values are in our range, then this edge is impossible. + and forall(@cfgnode n | + n = getASwitchExpr(switch, switchBlock) | + exists(int switchValue | + switchValue = getSwitchValue(n) + and (switchValue < sc.getExpr().(CompileTimeConstantInt).getIntValue() or + switchValue > switchCaseRangeEnd(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) { + exists(SwitchStmt switch | + switch = dc.getSwitchStmt() + and switch.getStmt() = switchBlock + // If all of the successors lead to other switch cases + // then this edge is impossible. + and forall(@cfgnode n | + n = getASwitchExpr(switch, switchBlock) | + exists(SwitchCase sc, int val | + sc.getSwitchStmt() = switch and + val = getSwitchValue(n) and + val >= sc.getExpr().(CompileTimeConstantInt).getIntValue() and + val <= switchCaseRangeEnd(sc)))) +} + +/** + * If `pred` is a function call with (at least) one function target, + * (at least) one such target must be potentially returning. + */ +private predicate possiblePredecessor(@cfgnode pred) { + not exists(pred.(FunctionCall).getTarget()) + or + potentiallyReturningFunctionCall(pred) +} + +/** + * An adapted version of the `successors_extended` relation that excludes + * impossible control-flow edges - flow will never occur along these + * edges, so it is safe (and indeed sensible) to remove them. + */ +cached predicate successors_adapted(@cfgnode pred, @cfgnode succ) { + successors_extended(pred, succ) + and possiblePredecessor(pred) + and not impossibleFalseEdge(pred, succ) + and not impossibleTrueEdge(pred, succ) + and not impossibleSwitchEdge(pred, succ) + and not impossibleDefaultSwitchEdge(pred, succ) + and not getOptions().exprExits(pred) +} + +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 +} + +library class CompileTimeConstantInt extends Expr { + CompileTimeConstantInt() { + compileTimeConstantInt(this, _) + } + + int getIntValue() { + compileTimeConstantInt(this, result) + } +} + +library class CompileTimeVariableExpr extends Expr { + CompileTimeVariableExpr() { + not compileTimeConstantInt(this, _) + } +} + +/** A helper class for evaluation of expressions. */ +library class ExprEvaluator extends int { + /* + * 0 = ConditionEvaluator, + * 1 = SwitchEvaluator, + * 2 = WhileLoopEntryConditionEvaluator, + * 3 = ForLoopEntryConditionEvaluator + */ + ExprEvaluator() { this in [0 .. 3] } + + /** `e` is an expression for which we want to calculate a value. */ + abstract predicate interesting(Expr e); + + /** Gets the value of (interesting) expression `e`, if any. */ + int getValue(Expr e) { result = getValueInternal(e, e) } + + /** + * When evaluating a syntactic subexpression of `e`, we may + * ignore the non-analyzable variable definition `def` for + * variable `v`. + * + * Subclasses may implement this predicate when needed. + */ + predicate ignoreNonAnalyzableVariableDefinition(Expr e, Variable v, StmtParent def) { none() } + + /** + * When evaluating a syntactic subexpression of `e`, we may + * ignore the expression `value` assigned to variable `v`. + * + * Subclasses may implement this predicate when needed. + */ + predicate ignoreVariableAssignment(Expr e, Variable v, Expr value) { none() } + + /** + * When evaluating a syntactic subexpression of `e`, we may + * consider variable `v` even though it lacks an initializer. + * Normally, this is not sound, as uninitialized variables + * can contain arbitrary data (in fact, a query + * UninitialisedLocal.ql exists for finding that). + * + * Subclasses may implement this predicate when needed. + */ + predicate allowVariableWithoutInitializer(Expr e, Variable v) { none() } + + /* Internal implementation predicates below */ + + /** + * `req` is an expression for which a value is required to be evaluated in + * order to calculate a value for interesting expression `e`. `sub` + * indicates whether `req` is a syntactic subexpression of `e`. + * + * This predicate enumerates the nodes top-down; `getValueInternal()` + * calculates the values bottom-up. + */ + predicate interestingInternal(Expr e, Expr req, boolean sub) { + interesting(e) and req = e and sub = true + or + exists(Expr mid | + interestingInternal(e, mid, sub) | + req = mid.(NotExpr).getOperand() or + req = mid.(BinaryLogicalOperation).getAnOperand() or + req = mid.(RelationalOperation).getAnOperand() or + req = mid.(EQExpr).getAnOperand() or + req = mid.(NEExpr).getAnOperand() or + req = mid.(AddExpr).getAnOperand() or + req = mid.(SubExpr).getAnOperand() or + req = mid.(MulExpr).getAnOperand() or + req = mid.(RemExpr).getAnOperand() or + req = mid.(DivExpr).getAnOperand() or + req = mid.(AssignExpr).getRValue() + ) + or + exists(VariableAccess va, Variable v, boolean sub1 | + interestingVariableAccess(e, va, v, sub1) and + req = v.getAnAssignedValue() and + (sub1 = true implies not ignoreVariableAssignment(e, v, req)) and + sub = false + ) + or + exists(Function f | + interestingFunction(e, f) and + returnStmt(f, req) and + sub = false + ) + } + + private predicate interestingVariableAccess(Expr e, VariableAccess va, Variable v, boolean sub) { + interestingInternal(e, va, sub) and + v = getVariableTarget(va) and + (v.hasInitializer() or sub = true and allowVariableWithoutInitializer(e, v)) and + tractableVariable(v) and + forall(StmtParent def | + nonAnalyzableVariableDefinition(v, def) | + sub = true and + ignoreNonAnalyzableVariableDefinition(e, v, def) + ) + } + + private predicate interestingFunction(Expr e, Function f) { + exists(FunctionCall fc | + interestingInternal(e, fc, _) | + f = fc.getTarget() + and not obviouslyNonConstant(f) + and not f.getType().getUnspecifiedType() instanceof VoidType + ) + } + + /** Gets the value of subexpressions `req` for expression `e`, if any. */ + private int getValueInternal(Expr e, Expr req) { + ( + interestingInternal(e, req, true) + and + ( + result = req.(CompileTimeConstantInt).getIntValue() or + result = getCompoundValue(e, (CompileTimeVariableExpr)req) + ) + and + ( + req.getUnderlyingType().(IntegralType).isSigned() or + result >= 0 + ) + ) + } + + /** Gets the value of compound subexpressions `val` for expression `e`, if any. */ + private int getCompoundValue(Expr e, CompileTimeVariableExpr val) { + interestingInternal(e, val, true) + and + ( + exists(NotExpr req | + req = val | + result = 1 and getValueInternal(e, req.getOperand()) = 0 or + result = 0 and getValueInternal(e, req.getOperand()) != 0 + ) + or + exists(LogicalAndExpr req | + req = val | + result = 1 and getValueInternal(e, req.getLeftOperand()) != 0 and getValueInternal(e, req.getRightOperand()) != 0 or + result = 0 and getValueInternal(e, req.getAnOperand()) = 0 + ) + or + exists(LogicalOrExpr req | + req = val | + result = 1 and getValueInternal(e, req.getAnOperand()) != 0 or + result = 0 and getValueInternal(e, req.getLeftOperand()) = 0 and getValueInternal(e, req.getRightOperand()) = 0 + ) + or + exists(LTExpr req | + req = val | + result = 1 and getValueInternal(e, req.getLeftOperand()) < getValueInternal(e, req.getRightOperand()) or + result = 0 and getValueInternal(e, req.getLeftOperand()) >= getValueInternal(e, req.getRightOperand()) + ) + or + exists(GTExpr req | + req = val | + result = 1 and getValueInternal(e, req.getLeftOperand()) > getValueInternal(e, req.getRightOperand()) or + result = 0 and getValueInternal(e, req.getLeftOperand()) <= getValueInternal(e, req.getRightOperand()) + ) + or + exists(LEExpr req | + req = val | + result = 1 and getValueInternal(e, req.getLeftOperand()) <= getValueInternal(e, req.getRightOperand()) or + result = 0 and getValueInternal(e, req.getLeftOperand()) > getValueInternal(e, req.getRightOperand()) + ) + or + exists(GEExpr req | + req = val | + result = 1 and getValueInternal(e, req.getLeftOperand()) >= getValueInternal(e, req.getRightOperand()) or + result = 0 and getValueInternal(e, req.getLeftOperand()) < getValueInternal(e, req.getRightOperand()) + ) + or + exists(EQExpr req | + req = val | + result = 1 and getValueInternal(e, req.getLeftOperand()) = getValueInternal(e, req.getRightOperand()) or + result = 0 and getValueInternal(e, req.getLeftOperand()) != getValueInternal(e, req.getRightOperand()) + ) + or + exists(NEExpr req | + req = val | + result = 0 and getValueInternal(e, req.getLeftOperand()) = getValueInternal(e, req.getRightOperand()) or + result = 1 and getValueInternal(e, req.getLeftOperand()) != getValueInternal(e, req.getRightOperand()) + ) + or + exists(AddExpr req | + req = val | + result = getValueInternal(e, req.getLeftOperand()) + getValueInternal(e, req.getRightOperand()) + ) + or + exists(SubExpr req | + req = val | + result = getValueInternal(e, req.getLeftOperand()) - getValueInternal(e, req.getRightOperand()) + ) + or + exists(MulExpr req | + req = val | + result = getValueInternal(e, req.getLeftOperand()) * getValueInternal(e, req.getRightOperand()) + ) + or + exists(RemExpr req | + req = val | + result = getValueInternal(e, req.getLeftOperand()) % getValueInternal(e, req.getRightOperand()) + ) + or + exists(DivExpr req | + req = val | + result = getValueInternal(e, req.getLeftOperand()) / getValueInternal(e, req.getRightOperand()) + ) + or + exists(AssignExpr req | + req = val | + result = getValueInternal(e, req.getRValue()) + ) + or + result = getVariableValue(e, (VariableAccess)val) + or + exists(FunctionCall call | + call = val and not callWithMultipleTargets(call) | + result = getFunctionValue(call.getTarget())) + ) + } + + language[monotonicAggregates] + private int getVariableValue(Expr e, VariableAccess va) { + exists(Variable v | + interestingVariableAccess(e, va, v, true) + and + // All assignments must have the same int value + result = min(Expr value | value = v.getAnAssignedValue() and not ignoreVariableAssignment(e, v, value) | + getValueInternalNonSubExpr(value) + ) and + result = max(Expr value | value = v.getAnAssignedValue() and not ignoreVariableAssignment(e, v, value) | + getValueInternalNonSubExpr(value) + ) + ) + } + + private int getFunctionValue(Function f) { + interestingFunction(_, f) + and + // All returns must have the same int value + // And it must have at least one return + forex(Expr ret | returnStmt(f, ret) | result = getValueInternalNonSubExpr(ret)) + } + + /** + * Same as `getValueInternal` but where `req` is not a syntactic subexpression + * of an interesting expression, and hence `nonAnalyzableVariableDefinition` and + * `ignoreNonAnalyzableVariableDefinition` are not relevant. That is, `req` is + * either a value assigned to an interesting variable or the return expression of + * an interesting function. + * + * This predicate, `getCompoundValueNonSubExpr`, and `getVariableValueNonSubExpr` + * need to be duplicated for performance reasons (the extra argument `e` can be + * omitted). + */ + private int getValueInternalNonSubExpr(Expr req) { + interestingInternal(_, req, false) + and + ( + result = req.(CompileTimeConstantInt).getIntValue() or + result = getCompoundValueNonSubExpr((CompileTimeVariableExpr)req) + ) + and + ( + req.getUnderlyingType().(IntegralType).isSigned() or + result >= 0 + ) + } + + private int getCompoundValueNonSubExpr(CompileTimeVariableExpr val) { + ( + exists(NotExpr req | + req = val | + result = 1 and getValueInternalNonSubExpr(req.getOperand()) = 0 or + result = 0 and getValueInternalNonSubExpr(req.getOperand()) != 0 + ) + or + exists(LogicalAndExpr req | + req = val | + result = 1 and getValueInternalNonSubExpr(req.getLeftOperand()) != 0 and getValueInternalNonSubExpr(req.getRightOperand()) != 0 or + result = 0 and getValueInternalNonSubExpr(req.getAnOperand()) = 0 + ) + or + exists(LogicalOrExpr req | + req = val | + result = 1 and getValueInternalNonSubExpr(req.getAnOperand()) != 0 or + result = 0 and getValueInternalNonSubExpr(req.getLeftOperand()) = 0 and getValueInternalNonSubExpr(req.getRightOperand()) = 0 + ) + or + exists(LTExpr req | + req = val | + result = 1 and getValueInternalNonSubExpr(req.getLeftOperand()) < getValueInternalNonSubExpr(req.getRightOperand()) or + result = 0 and getValueInternalNonSubExpr(req.getLeftOperand()) >= getValueInternalNonSubExpr(req.getRightOperand()) + ) + or + exists(GTExpr req | + req = val | + result = 1 and getValueInternalNonSubExpr(req.getLeftOperand()) > getValueInternalNonSubExpr(req.getRightOperand()) or + result = 0 and getValueInternalNonSubExpr(req.getLeftOperand()) <= getValueInternalNonSubExpr(req.getRightOperand()) + ) + or + exists(LEExpr req | + req = val | + result = 1 and getValueInternalNonSubExpr(req.getLeftOperand()) <= getValueInternalNonSubExpr(req.getRightOperand()) or + result = 0 and getValueInternalNonSubExpr(req.getLeftOperand()) > getValueInternalNonSubExpr(req.getRightOperand()) + ) + or + exists(GEExpr req | + req = val | + result = 1 and getValueInternalNonSubExpr(req.getLeftOperand()) >= getValueInternalNonSubExpr(req.getRightOperand()) or + result = 0 and getValueInternalNonSubExpr(req.getLeftOperand()) < getValueInternalNonSubExpr(req.getRightOperand()) + ) + or + exists(EQExpr req | + req = val | + result = 1 and getValueInternalNonSubExpr(req.getLeftOperand()) = getValueInternalNonSubExpr(req.getRightOperand()) or + result = 0 and getValueInternalNonSubExpr(req.getLeftOperand()) != getValueInternalNonSubExpr(req.getRightOperand()) + ) + or + exists(NEExpr req | + req = val | + result = 0 and getValueInternalNonSubExpr(req.getLeftOperand()) = getValueInternalNonSubExpr(req.getRightOperand()) or + result = 1 and getValueInternalNonSubExpr(req.getLeftOperand()) != getValueInternalNonSubExpr(req.getRightOperand()) + ) + or + exists(AddExpr req | + req = val | + result = getValueInternalNonSubExpr(req.getLeftOperand()) + getValueInternalNonSubExpr(req.getRightOperand()) + ) + or + exists(SubExpr req | + req = val | + result = getValueInternalNonSubExpr(req.getLeftOperand()) - getValueInternalNonSubExpr(req.getRightOperand()) + ) + or + exists(MulExpr req | + req = val | + result = getValueInternalNonSubExpr(req.getLeftOperand()) * getValueInternalNonSubExpr(req.getRightOperand()) + ) + or + exists(RemExpr req | + req = val | + result = getValueInternalNonSubExpr(req.getLeftOperand()) % getValueInternalNonSubExpr(req.getRightOperand()) + ) + or + exists(DivExpr req | + req = val | + result = getValueInternalNonSubExpr(req.getLeftOperand()) / getValueInternalNonSubExpr(req.getRightOperand()) + ) + or + exists(AssignExpr req | + req = val | + result = getValueInternalNonSubExpr(req.getRValue()) + ) + or + result = getVariableValueNonSubExpr(val.(VariableAccess)) + or + exists(FunctionCall call | + call = val and not callWithMultipleTargets(call) | + result = getFunctionValue(call.getTarget())) + ) + } + + private int getVariableValueNonSubExpr(VariableAccess va) { + // All assignments must have the same int value + result = getMinVariableValueNonSubExpr(va) and + result = getMaxVariableValueNonSubExpr(va) + } + + /** + * Helper predicate for `getVariableValueNonSubExpr`: computes the minimum value considered by this library + * that is assigned to the variable accessed by `va`. + */ + language[monotonicAggregates] + pragma[noopt] + private int getMinVariableValueNonSubExpr(VariableAccess va) { + exists(Variable v | + interestingVariableAccess(_, va, v, false) + and + result = min(Expr value | value = v.getAnAssignedValue() | getValueInternalNonSubExpr(value)) + ) + } + + /** + * Helper predicate for `getVariableValueNonSubExpr`: computes the maximum value considered by this library + * that is assigned to the variable accessed by `va`. + */ + language[monotonicAggregates] + pragma[noopt] + private int getMaxVariableValueNonSubExpr(VariableAccess va) { + exists(Variable v | + interestingVariableAccess(_, va, v, false) and + result = max(Expr value | value = v.getAnAssignedValue() | getValueInternalNonSubExpr(value)) + ) + } +} + +/** + * Holds if the dynamic target of `call` cannot be determined statically even + * though there is a `call.getTarget()`. This can happen due to virtual + * dispatch or due to insufficient information about which object files should + * be linked together. + */ +private predicate callWithMultipleTargets(FunctionCall call) { + // Some function calls have more than one possible target. Such call need to + // be excluded from the analysis, because they could lead to ambiguous + // results. + call.getTarget() != call.getTarget() + or + call.isVirtual() +} + +// Folded predicate for proper join-order +private Variable getVariableTarget(VariableAccess va) { + result = va.getTarget() + and + (result instanceof LocalVariable or result instanceof GlobalOrNamespaceVariable) +} + +/** + * Holds if `def` is a (potential) definition of variable `v` that makes it + * impossible to analyze the value of `v` by only looking at direct assignments + * to `v`. + */ +private predicate nonAnalyzableVariableDefinition(Variable v, StmtParent def) { + def.(AddressOfExpr).getAddressable() = v + or + exists(VariableAccess va | + va.getTarget() = v | + definitionByReference(va, def) + or + def.(CrementOperation).getAnOperand() = va + or + def.(Assignment).getLValue() = va and not def instanceof AssignExpr + ) + or + asmStmtMayDefineVariable(def, v) +} + +/** + * For performance reasons, we want to restrict some of the computation to only + * variables that are tractable. The threshold chosen here has been tested + * empirically to have effect only on a few rare and pathological examples. + */ +private predicate tractableVariable(Variable v) { + not exists(StmtParent def | nonAnalyzableVariableDefinition(v, def)) or + strictcount(StmtParent def | nonAnalyzableVariableDefinition(v, def)) < 1000 +} + +/** + * Helper predicate: Gets an expression which is obviously just an access to + * the parameter `p`, including ternary expressions. + */ +private Expr parameterAccess(Parameter p) { + result = p.getAnAccess() or + result.(ConditionalExpr).getThen() = parameterAccess(p) or + result.(ConditionalExpr).getElse() = parameterAccess(p) +} + +/** + * Holds if the function `f` is obviously not tractable for this library's + * analysis techniques: + * - it may obviously return multiple distinct constant values, + * - it may return one of its parameters without reassigning it, or + * - it may return an expression for which this analysis cannot infer a constant value. + * + * For example, `int f(int x) { if (x) return 0; else return 1; }` may + * obviously return two constant values, and so will never be considered to + * have a compile-time constant value by this library, and + * `int min(int x, int y) { return x < y ? x : y; }` returns a parameter and + * so won't have a locally constant value. + */ +private predicate obviouslyNonConstant(Function f) { + // May return multiple distinct constant values + 1 < strictcount(Expr e, string value | returnStmt(f, e) and value = e.getValue()) or + // May return a parameter without reassignment + exists(Parameter p, Expr ret | + returnStmt(f, ret) and + p = f.getAParameter() and + not exists(p.getAnAssignedValue()) | + ret = parameterAccess(p) + ) or + // May return a value for which this analysis cannot infer a constant value + exists(Expr ret | returnStmt(f, ret) | nonComputableConstant(ret)) + or + // Returns many non-constant values + strictcount(Expr e | returnStmt(f, e) and not exists(e.getValue())) > 100 +} + +/** + * Helper predicate: holds if the analysis cannot compute a + * constant value for the expression `e`. + */ +private predicate nonComputableConstant(Expr e) { + // Variable targets that are ignored + e instanceof VariableAccess and not exists(getVariableTarget(e)) + or + // Recursive cases + nonComputableConstant(e.(BinaryArithmeticOperation).getAnOperand()) + or + nonComputableConstant(e.(ComparisonOperation).getAnOperand()) +} + +/** Holds if assembler statement `asm` may define variable `v`. */ +private predicate asmStmtMayDefineVariable(AsmStmt asm, Variable v) { + // We don't actually have access to the assembler code, so assume + // that any variable declared in the same function may be defined + exists(DeclStmt decl, Function f | + decl.getADeclaration() = v and + f = decl.getEnclosingFunction() and + f = asm.getEnclosingFunction() + ) +} + +private predicate returnStmt(Function f, Expr value) { + exists(ReturnStmt ret | + ret.getEnclosingFunction() = f + and value = ret.getExpr() + ) +} + +/** A helper class for evaluation of conditions. */ +library class ConditionEvaluator extends ExprEvaluator { + ConditionEvaluator() { this = 0 } + + override predicate interesting(Expr e) { + falsecond(e, _) + or + truecond(e, _) + } +} + +/** A helper class for evaluation of switch expressions. */ +library class SwitchEvaluator extends ExprEvaluator { + SwitchEvaluator() { this = 1 } + + override predicate interesting(Expr e) { + e = getASwitchExpr(_, _) + } +} + +private int getSwitchValue(Expr e) { + exists(SwitchEvaluator x | result = x.getValue(e)) +} + +/** A helper class for evaluation of loop entry conditions. */ +library class LoopEntryConditionEvaluator extends ExprEvaluator { + LoopEntryConditionEvaluator() { this in [2 .. 3] } + + abstract override predicate interesting(Expr e); + + /** Holds if `cfn` is the entry point of the loop for which `e` is the condition. */ + abstract predicate isLoopEntry(Expr e, ControlFlowNode cfn); + + /** Holds if `s` is the loop body guarded by the condition `e`. */ + abstract predicate isLoopBody(Expr e, StmtParent s); + + private predicate isLoopBodyDescendant(Expr e, StmtParent s) { + isLoopBody(e, s) + or + exists(StmtParent mid | + isLoopBodyDescendant(e, mid) | + s = mid.(Stmt).getAChild() or + s = mid.(Expr).getAChild() + ) + } + + // Same as `interestingInternal(e, sub, true)` but avoids negative recursion + private predicate interestingSubExpr(Expr e, Expr sub) { + interesting(e) and e = sub + or + exists(Expr mid | interestingSubExpr(e, mid) and sub = mid.getAChild()) + } + + private predicate maybeInterestingVariable(Expr e, Variable v) { + exists(VariableAccess va | + interestingSubExpr(e, va) | + va.getTarget() = v + ) + } + + /** + * Holds if the expression `valueOrDef` in the body of the loop belonging to + * `e` can reach the entry point of the loop without crossing an assignment + * to `v`. + * + * `valueOrDef` is either a value assigned to variable `v` or a non-analyzable + * definition of `v`. + */ + private predicate reachesLoopEntryFromLoopBody(Expr e, Variable v, StmtParent valueOrDef) { + maybeInterestingVariable(e, v) and + (valueOrDef = v.getAnAssignedValue() or nonAnalyzableVariableDefinition(v, valueOrDef)) and + isLoopBodyDescendant(e, valueOrDef) and + /* Use primitive basic blocks in reachability analysis for better performance. + * This is similar to the pattern used in e.g. `DefinitionsAndUses` and + * `LocalScopeVariableReachability`. + */ + exists(PrimitiveBasicBlock bb1, int pos1 | + bb1.getNode(pos1) = valueOrDef | + // Reaches in same basic block + exists(int pos2 | + loopEntryAt(bb1, pos2, e) and + pos2 > pos1 and + not exists(int k | assignmentAt(bb1, k, v) | k in [pos1 + 1 .. pos2 - 1]) + ) + or + // Reaches in a successor block + exists(PrimitiveBasicBlock bb2 | + bb2 = bb1.getASuccessor() and + not exists(int pos3 | assignmentAt(bb1, pos3, v) and pos3 > pos1) and + bbReachesLoopEntry(bb2, e, v) + ) + ) + } + + private predicate loopEntryAt(PrimitiveBasicBlock bb, int pos, Expr e) { + exists(ControlFlowNode cfn | + bb.getNode(pos) = cfn and + isLoopEntry(e, cfn) + ) + } + + private predicate assignmentAt(PrimitiveBasicBlock bb, int pos, Variable v) { + maybeInterestingVariable(_, v) and + bb.getNode(pos) = v.getAnAssignedValue() + } + + /** + * Holds if the entry point of basic block `bb` can reach the entry point of + * the loop belonging to `e` without crossing an assignment to `v`. + */ + private predicate bbReachesLoopEntry(PrimitiveBasicBlock bb, Expr e, Variable v) { + bbReachesLoopEntryLocally(bb, e, v) + or + exists(PrimitiveBasicBlock succ | + succ = bb.getASuccessor() | + bbReachesLoopEntry(succ, e, v) and + not assignmentAt(bb, _, v) + ) + } + + private predicate bbReachesLoopEntryLocally(PrimitiveBasicBlock bb, Expr e, Variable v) { + exists(int pos | + loopEntryAt(bb, pos, e) and + maybeInterestingVariable(e, v) and + not exists(int pos1 | assignmentAt(bb, pos1, v) | pos1 < pos) + ) + } + + /** + * When evaluating a syntactic subexpression of loop condition `e`, + * we may ignore non-analyzable variable definition `def` for + * variable `v`, provided that `def` is in the body of the loop and + * cannot reach the loop entry point. + * + * Example: + * + * ``` + * done = false; + * while (!done) { + * f(&done); // can be ignored + * } + * + * while (...) { + * done = false; + * while (!done) { + * f(&done); // can be ignored + * } + * } + * + * done = false; + * while (...) { + * while (!done) { + * f(&done); // cannot be ignored + * } + * } + * ``` + */ + override predicate ignoreNonAnalyzableVariableDefinition(Expr e, Variable v, StmtParent def) { + maybeInterestingVariable(e, v) and + nonAnalyzableVariableDefinition(v, def) and + isLoopBodyDescendant(e, def) and + not reachesLoopEntryFromLoopBody(e, v, def) + } + + /** + * When evaluating a syntactic subexpression of loop condition `e`, + * we may ignore the expression `value` assigned to variable `v`, + * provided that `value` is in the body of the loop and cannot reach + * the loop entry point. + * + * Example: + * + * ``` + * done = false; + * while (!done) { + * done = true; // can be ignored + * } + * + * while (...) { + * done = false; + * while (!done) { + * done = true; // can be ignored + * } + * } + * + * done = false; + * while (...) { + * while (!done) { + * done = true; // cannot be ignored + * } + * } + * ``` + */ + override predicate ignoreVariableAssignment(Expr e, Variable v, Expr value) { + maybeInterestingVariable(e, v) and + value = v.getAnAssignedValue() and + isLoopBodyDescendant(e, value) and + not reachesLoopEntryFromLoopBody(e, v, value) + } +} + +/** A helper class for evaluation of while-loop entry conditions. */ +library class WhileLoopEntryConditionEvaluator extends LoopEntryConditionEvaluator { + WhileLoopEntryConditionEvaluator() { this = 2 } + + override predicate interesting(Expr e) { + exists(WhileStmt while | e = while.getCondition()) + } + + override predicate isLoopEntry(Expr e, ControlFlowNode cfn) { + cfn.(WhileStmt).getCondition() = e + } + + override predicate isLoopBody(Expr e, StmtParent s) { + exists(WhileStmt while | + e = while.getCondition() | + s = while.getStmt() + ) + } +} + +/** A helper class for evaluation of for-loop entry conditions. */ +library class ForLoopEntryConditionEvaluator extends LoopEntryConditionEvaluator { + ForLoopEntryConditionEvaluator() { this = 3 } + + override predicate interesting(Expr e) { + exists(ForStmt for | e = for.getCondition()) + } + + override predicate isLoopEntry(Expr e, ControlFlowNode cfn) { + cfn.(ForStmt).getCondition() = e + } + + override predicate isLoopBody(Expr e, StmtParent s) { + exists(ForStmt for | + e = for.getCondition() | + s = for.getUpdate() or + s = for.getStmt() + ) + } + + override predicate ignoreNonAnalyzableVariableDefinition(Expr e, Variable v, StmtParent def) { + LoopEntryConditionEvaluator.super.ignoreNonAnalyzableVariableDefinition(e, v, def) + or + // If the for loop initializes variable `v` we know its exact value so all + // non-analyzable definitions can be ignored + exists(ForStmt for | + forLoopInitializesVariable(for, v, _) and + e = for.getCondition() and + nonAnalyzableVariableDefinition(v, def) + ) + } + + override predicate ignoreVariableAssignment(Expr e, Variable v, Expr value) { + LoopEntryConditionEvaluator.super.ignoreVariableAssignment(e, v, value) + or + // If the for loop initializes variable `v` we know its exact value so all + // other assignments can be ignored + exists(ForStmt for | + forLoopInitializesVariable(for, v, _) and + e = for.getCondition() and + value = v.getAnAssignedValue() and + not forLoopInitializesVariable(for, v, value) + ) + } + + override predicate allowVariableWithoutInitializer(Expr e, Variable v) { + // If the for loop initializes variable `v` we know its exact value + // regardless of a lacking initializer + exists(ForStmt for | + forLoopInitializesVariable(for, v, _) and + e = for.getCondition() and + not v.hasInitializer() + ) + } +} + +/** Holds if for-loop `for` initializes variable `v` to expression `e`. */ +private predicate forLoopInitializesVariable(ForStmt for, Variable v, Expr e) { + exists(AssignExpr ae | + ae = for.getInitialization().(ExprStmt).getExpr() and + assignsValue(ae, v, e) + ) + or + exists(DeclStmt decl, Expr init, Variable v1 | + decl = for.getInitialization() and + v1 = decl.getADeclaration() and + init = v1.getInitializer().getExpr() | + e = init and v = v1 + or + assignsValue(init, v, e) + ) +} + +/** + * Holds if assignment `ae` assigns expression `e` to variable `v`. + * + * Nested assignments are included, e.g. `i = j = 0;` assigns the + * expression `j = 0` to `i` and the expression `0` to `j`. + */ +private predicate assignsValue(AssignExpr ae, Variable v, Expr e) { + v.getAnAccess() = ae.getLValue() and e = ae.getRValue() + or + assignsValue(ae.getRValue(), v, e) +} diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/internal/PrimitiveBasicBlocks.qll b/cpp/ql/src/semmle/code/cpp/controlflow/internal/PrimitiveBasicBlocks.qll new file mode 100644 index 000000000000..bb3cf562c6e2 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/controlflow/internal/PrimitiveBasicBlocks.qll @@ -0,0 +1,110 @@ +/** + * INTERNAL: use the `BasicBlocks` library instead. + * This library defines `PrimitiveBasicBlock`s, an intermediate stage in the + * computation of `BasicBlock`s. + */ +/* + * Unlike `BasicBlock`s, `PrimitiveBasicBlock`s are constructed using + * the primitive `successors_extended` relation only. That is, impossible + * edges removed in `successors_adapted` are still taken into account. + * + * `PrimitiveBasicBlock`s are used as helper entities for actually + * constructing `successors_adapted`, hence the need for two "layers" + * of basic blocks. In addition to being used to construct + * `successors_adapted`, the relations for `BasicBlocks` (e.g., + * `basic_block_entry_node`) can reuse the relations computed here + * (e.g, `primitive_basic_block_entry_node`), as most `BasicBlock`s + * will coincide with `PrimitiveBasicBlock`s. + */ +import cpp + +import Cached +private cached module Cached { + /** Holds if `node` is the entry node of a primitive basic block. */ + cached + predicate primitive_basic_block_entry_node(ControlFlowNode node) { + // The entry point of the CFG is the start of a BB. + exists (Function f | f.getEntryPoint() = node) + + // If the node has more than one predecessor, + // or the node's predecessor has more than one successor, + // then the node is the start of a new primitive basic block. + or + strictcount (ControlFlowNode pred, ControlFlowNode other + | successors_extended(pred,node) and successors_extended(pred,other)) > 1 + + // If the node has zero predecessors then it is the start of + // a BB. However, the C++ AST contains many nodes with zero + // predecessors and zero successors, which are not members of + // the CFG. So we exclude all of those trivial BBs by requiring + // that the node have at least one successor. + or + (not successors_extended(_, node) and successors_extended(node, _)) + } + + /** Holds if `node` is the `pos`th control-flow node in primitive basic block `bb`. */ + cached + predicate primitive_basic_block_member(ControlFlowNode node, PrimitiveBasicBlock bb, int pos) { + (node = bb and pos = 0) + or + (not (node instanceof PrimitiveBasicBlock) and + exists (ControlFlowNode pred + | successors_extended(pred,node) + | primitive_basic_block_member(pred, bb, pos - 1))) + } + + /** Gets the number of control-flow nodes in the primitive basic block `bb`. */ + cached + int primitive_bb_length(PrimitiveBasicBlock bb) { + result = strictcount(ControlFlowNode node | primitive_basic_block_member(node, bb, _)) + } + + /** Successor relation for primitive basic blocks. */ + cached + predicate primitive_bb_successor(PrimitiveBasicBlock pred, PrimitiveBasicBlock succ) { + exists(ControlFlowNode last | + primitive_basic_block_member(last, pred, primitive_bb_length(pred)-1) and + successors_extended(last, succ) + ) + } +} + +/** + * A primitive basic block in the C/C++ control-flow graph constructed using + * the primitive `successors_extended` relation only. + */ +class PrimitiveBasicBlock extends @cfgnode { + + PrimitiveBasicBlock() { + primitive_basic_block_entry_node(this) + } + + /** Gets a textual representation of this element. */ + string toString() { + result = "PrimitiveBasicBlock" + } + + predicate contains(ControlFlowNode node) { + primitive_basic_block_member(node, this, _) + } + + ControlFlowNode getNode(int pos) { + primitive_basic_block_member(result, this, pos) + } + + ControlFlowNode getANode() { + primitive_basic_block_member(result, this, _) + } + + PrimitiveBasicBlock getASuccessor() { + primitive_bb_successor(this, result) + } + + PrimitiveBasicBlock getAPredecessor() { + primitive_bb_successor(result, this) + } + + int length() { + result = primitive_bb_length(this) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/DataFlow.qll b/cpp/ql/src/semmle/code/cpp/dataflow/DataFlow.qll new file mode 100644 index 000000000000..bfb93f77bfa7 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/dataflow/DataFlow.qll @@ -0,0 +1,21 @@ +/** + * Provides a library for local (intra-procedural) and global (inter-procedural) + * data flow analysis: deciding whether data can flow from a _source_ to a + * _sink_. + * + * Unless configured otherwise, _flow_ means that the exact value of + * the source may reach the sink. We do not track flow across pointer + * dereferences or array indexing. To track these types of flow, where the + * exact value may not be preserved, import + * `semmle.code.cpp.dataflow.TaintTracking`. + * + * To use global (interprocedural) data flow, extend the class + * `DataFlow::Configuration` as documented on that class. To use local + * (intraprocedural) data flow, invoke `DataFlow::localFlow` or + * `DataFlow::LocalFlowStep` with arguments of type `DataFlow::Node`. + */ +import cpp + +module DataFlow { + import semmle.code.cpp.dataflow.internal.DataFlowImpl +} diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/DataFlow2.qll b/cpp/ql/src/semmle/code/cpp/dataflow/DataFlow2.qll new file mode 100644 index 000000000000..6f6c41ee16c0 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/dataflow/DataFlow2.qll @@ -0,0 +1,38 @@ +/** + * Provides a `DataFlow2` module, which is a copy of the `DataFlow` module. Use + * this class when data-flow configurations must depend on each other. Two + * classes extending `DataFlow::Configuration` should never depend on each + * other, but one of them should instead depend on a + * `DataFlow2::Configuration`, a `DataFlow3::Configuration`, or a + * `DataFlow4::Configuration`. + * + * See `semmle.code.cpp.dataflow.DataFlow` for the full documentation. + */ +import cpp + +module DataFlow2 { + import semmle.code.cpp.dataflow.internal.DataFlowImpl2 + + /** + * This class exists to prevent mutual recursion between the user-overridden + * member predicates of `Configuration` and the rest of the data-flow library. + * Good performance cannot be guaranteed in the presence of such recursion, so + * it should be replaced by using more than one copy of the data flow library. + * Four copies are available: `DataFlow` through `DataFlow4`. + */ + private abstract + class ConfigurationRecursionPrevention extends Configuration { + bindingset[this] + ConfigurationRecursionPrevention() { any() } + + override predicate hasFlow(Node source, Node sink) { + strictcount(Node n | this.isSource(n)) < 0 + or + strictcount(Node n | this.isSink(n)) < 0 + or + strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0 + or + super.hasFlow(source, sink) + } + } +} diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/DataFlow3.qll b/cpp/ql/src/semmle/code/cpp/dataflow/DataFlow3.qll new file mode 100644 index 000000000000..77385b2dbe5c --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/dataflow/DataFlow3.qll @@ -0,0 +1,38 @@ +/** + * Provides a `DataFlow3` module, which is a copy of the `DataFlow` module. Use + * this class when data-flow configurations must depend on each other. Two + * classes extending `DataFlow::Configuration` should never depend on each + * other, but one of them should instead depend on a + * `DataFlow2::Configuration`, a `DataFlow3::Configuration`, or a + * `DataFlow4::Configuration`. + * + * See `semmle.code.cpp.dataflow.DataFlow` for the full documentation. + */ +import cpp + +module DataFlow3 { + import semmle.code.cpp.dataflow.internal.DataFlowImpl3 + + /** + * This class exists to prevent mutual recursion between the user-overridden + * member predicates of `Configuration` and the rest of the data-flow library. + * Good performance cannot be guaranteed in the presence of such recursion, so + * it should be replaced by using more than one copy of the data flow library. + * Four copies are available: `DataFlow` through `DataFlow4`. + */ + private abstract + class ConfigurationRecursionPrevention extends Configuration { + bindingset[this] + ConfigurationRecursionPrevention() { any() } + + override predicate hasFlow(Node source, Node sink) { + strictcount(Node n | this.isSource(n)) < 0 + or + strictcount(Node n | this.isSink(n)) < 0 + or + strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0 + or + super.hasFlow(source, sink) + } + } +} diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/DataFlow4.qll b/cpp/ql/src/semmle/code/cpp/dataflow/DataFlow4.qll new file mode 100644 index 000000000000..30b7273f4513 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/dataflow/DataFlow4.qll @@ -0,0 +1,38 @@ +/** + * Provides a `DataFlow4` module, which is a copy of the `DataFlow` module. Use + * this class when data-flow configurations must depend on each other. Two + * classes extending `DataFlow::Configuration` should never depend on each + * other, but one of them should instead depend on a + * `DataFlow2::Configuration`, a `DataFlow3::Configuration`, or a + * `DataFlow4::Configuration`. + * + * See `semmle.code.cpp.dataflow.DataFlow` for the full documentation. + */ +import cpp + +module DataFlow4 { + import semmle.code.cpp.dataflow.internal.DataFlowImpl4 + + /** + * This class exists to prevent mutual recursion between the user-overridden + * member predicates of `Configuration` and the rest of the data-flow library. + * Good performance cannot be guaranteed in the presence of such recursion, so + * it should be replaced by using more than one copy of the data flow library. + * Four copies are available: `DataFlow` through `DataFlow4`. + */ + private abstract + class ConfigurationRecursionPrevention extends Configuration { + bindingset[this] + ConfigurationRecursionPrevention() { any() } + + override predicate hasFlow(Node source, Node sink) { + strictcount(Node n | this.isSource(n)) < 0 + or + strictcount(Node n | this.isSink(n)) < 0 + or + strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0 + or + super.hasFlow(source, sink) + } + } +} diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/EscapesTree.qll b/cpp/ql/src/semmle/code/cpp/dataflow/EscapesTree.qll new file mode 100644 index 000000000000..4f9474e2efc6 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/dataflow/EscapesTree.qll @@ -0,0 +1,302 @@ +/** + * Provides a local analysis for identifying where a variable address or value + * may escape an _expression tree_, meaning that it is assigned to a variable, + * passed to a function, or similar. + */ +private import cpp + +/** + * Holds if `f` is an instantiation of the `std::move` or `std::forward` + * template functions, these functions are essentially casts, so we treat them + * as such. + */ +private predicate stdIdentityFunction(Function f) { + f.getNamespace().getParentNamespace() instanceof GlobalNamespace and + f.getNamespace().getName() = "std" and + ( f.getName() = "move" + or + f.getName() = "forward" ) +} + +private predicate lvalueToLvalueStepPure(Expr lvalueIn, Expr lvalueOut) { + lvalueIn = lvalueOut.(DotFieldAccess).getQualifier().getFullyConverted() + or + lvalueIn.getConversion() = lvalueOut.(ParenthesisExpr) + or + // When an object is implicitly converted to a reference to one of its base + // classes, it gets two `Conversion`s: there is first an implicit + // `CStyleCast` to its base class followed by a `ReferenceToExpr` to a + // reference to its base class. Whereas an explicit cast to the base class + // would produce an rvalue, which would not be convertible to an lvalue + // reference, this implicit cast instead produces an lvalue. The following + // case ensures that we propagate the property of being an lvalue through + // 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() + or + // C++ only + lvalueIn = lvalueOut.(Assignment).getLValue().getFullyConverted() +} + +private predicate pointerToLvalueStep(Expr pointerIn, Expr lvalueOut) { + pointerIn = lvalueOut.(ArrayExpr).getArrayBase().getFullyConverted() + or + pointerIn = lvalueOut.(PointerDereferenceExpr) + .getOperand().getFullyConverted() + or + pointerIn = lvalueOut.(PointerFieldAccess).getQualifier().getFullyConverted() +} + +private predicate lvalueToPointerStep(Expr lvalueIn, Expr pointerOut) { + lvalueIn.getConversion() = pointerOut.(ArrayToPointerConversion) + or + lvalueIn = pointerOut.(AddressOfExpr).getOperand().getFullyConverted() +} + +private predicate pointerToPointerStep(Expr pointerIn, Expr pointerOut) { + ( + pointerOut instanceof PointerAddExpr + or + pointerOut instanceof PointerSubExpr + ) and + pointerIn = pointerOut.getAChild().getFullyConverted() and + pointerIn.getType().getUnspecifiedType() instanceof PointerType + or + pointerIn = pointerOut.(UnaryPlusExpr).getOperand().getFullyConverted() + or + pointerIn.getConversion() = pointerOut.(Cast) + or + pointerIn.getConversion() = pointerOut.(ParenthesisExpr) + or + pointerIn = pointerOut.(ConditionalExpr).getThen().getFullyConverted() + or + pointerIn = pointerOut.(ConditionalExpr).getElse().getFullyConverted() + or + pointerIn = pointerOut.(CommaExpr).getRightOperand().getFullyConverted() + or + pointerIn = pointerOut.(StmtExpr).getResultExpr().getFullyConverted() +} + +private predicate lvalueToReferenceStep(Expr lvalueIn, Expr referenceOut) { + lvalueIn.getConversion() = referenceOut.(ReferenceToExpr) +} + +private predicate referenceToLvalueStep(Expr referenceIn, Expr lvalueOut) { + // This probably cannot happen. It would require an expression to be + // converted to a reference and back again without an intermediate variable + // assignment. + referenceIn.getConversion() = lvalueOut.(ReferenceDereferenceExpr) +} + +private predicate referenceToReferenceStep(Expr referenceIn, Expr referenceOut) { + referenceOut = any(FunctionCall call | + stdIdentityFunction(call.getTarget()) and + referenceIn = call.getArgument(0).getFullyConverted() + ) + or + referenceIn.getConversion() = referenceOut.(Cast) + or + referenceIn.getConversion() = referenceOut.(ParenthesisExpr) +} + +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 + // 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) + ) +} + +private predicate valueMayEscapeAt(Expr e) { + exists(Call call | + e = call.getAnArgument().getFullyConverted() and + not stdIdentityFunction(call.getTarget()) + ) + or + exists(AssignExpr assign | e = assign.getRValue().getFullyConverted()) + or + exists(Initializer init | e = init.getExpr().getFullyConverted()) + or + exists(ConstructorFieldInit init | e = init.getExpr().getFullyConverted()) + or + exists(ReturnStmt ret | e = ret.getExpr().getFullyConverted()) + or + exists(ThrowExpr throw | e = throw.getExpr().getFullyConverted()) + or + exists(AggregateLiteral agg | e = agg.getAChild().getFullyConverted()) + or + exists(AsmStmt asm | e = asm.getAChild().(Expr).getFullyConverted()) +} + +private predicate valueMayEscapeMutablyAt(Expr e) { + valueMayEscapeAt(e) and + exists(Type t | t = e.getType().getUnderlyingType() | + exists(PointerType pt | + pt = t + or + pt = t.(SpecifiedType).getBaseType() + | + not pt.getBaseType().isConst() + ) + or + t instanceof ReferenceType and + not t.(ReferenceType).getBaseType().isConst() + ) +} + +private +predicate addressFromVariableAccess(VariableAccess va, Expr e) { + pointerFromVariableAccess(va, e) + or + referenceFromVariableAccess(va, e) + or + // `e` could be a pointer that is converted to a reference as the final step, + // meaning that we pass a value that is two dereferences away from referring + // to `va`. This happens, for example, with `void std::vector::push_back(T&& + // value);` when called as `v.push_back(&x)`, for a static variable `x`. It + // can also happen when taking a reference to a const pointer to a + // (potentially non-const) value. + exists(Expr pointerValue | + pointerFromVariableAccess(va, pointerValue) and + e = pointerValue.getConversion().(ReferenceToExpr) + ) +} + +import EscapesTree_Cached +private cached module EscapesTree_Cached { + /** + * Holds if `e` is a fully-converted expression that evaluates to an address + * derived from the address of `va` and is stored in a variable or passed + * across functions. This means `e` is the `Expr.getFullyConverted`-form of: + * + * - The right-hand side of an assignment or initialization; + * - A function argument or return value; + * - The argument to `throw`. + * - An entry in an `AggregateLiteral`, including the compiler-generated + * `ClassAggregateLiteral` that initializes a `LambdaExpression`; or + * - An expression in an inline assembly statement. + * + * This predicate includes pointers or reference to `const` types. See + * `variableAddressEscapesTreeNonConst` for a version of this predicate that + * does not. + * + * If `va` has reference type, the escape analysis concerns the value pointed + * to by the reference rather than the reference itself. The C++ language does + * not allow taking the address of a reference in any way, so this predicate + * would never produce any results for the reference itself. Callers that are + * not interested in the value referred to by references should exclude + * variable accesses to reference-typed values. + */ + cached predicate variableAddressEscapesTree(VariableAccess va, Expr e) { + valueMayEscapeAt(e) and + addressFromVariableAccess(va, e) + } + + /** + * Holds if `e` is a fully-converted expression that evaluates to a non-const + * address derived from the address of `va` and is stored in a variable or + * passed across functions. This means `e` is the `Expr.getFullyConverted`-form + * of: + * + * - The right-hand side of an assignment or initialization; + * - A function argument or return value; + * - The argument to `throw`. + * - An entry in an `AggregateLiteral`, including the compiler-generated + * `ClassAggregateLiteral` that initializes a `LambdaExpression`; or + * - An expression in an inline assembly statement. + * + * This predicate omits pointers or reference to `const` types. See + * `variableAddressEscapesTree` for a version of this predicate that includes + * those. + * + * If `va` has reference type, the escape analysis concerns the value pointed + * to by the reference rather than the reference itself. The C++ language + * offers no way to take the address of a reference, so this predicate will + * never produce any results for the reference itself. Callers that are not + * interested in the value referred to by references should exclude variable + * accesses to reference-typed values. + */ + cached predicate variableAddressEscapesTreeNonConst(VariableAccess va, Expr e) { + valueMayEscapeMutablyAt(e) and + addressFromVariableAccess(va, e) + } + + /** + * 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. 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`. + */ + cached predicate variableAccessedAsValue(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/RecursionPrevention.qll b/cpp/ql/src/semmle/code/cpp/dataflow/RecursionPrevention.qll new file mode 100644 index 000000000000..75f22a077fd5 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/dataflow/RecursionPrevention.qll @@ -0,0 +1,34 @@ +/** + * When this module is imported, recursive use of `DataFlow::Configuration` is + * disallowed. Importing this module will guarantee the absence of such + * recursion, which is unsupported and will be unconditionally disallowed in a + * future release. + * + * Recursive use of `DataFlow{2..4}::Configuration` is always disallowed, so no + * import is needed for those. + */ +import cpp +private import semmle.code.cpp.dataflow.DataFlow + +/** + * This class exists to prevent mutual recursion between the user-overridden + * member predicates of `Configuration` and the rest of the data-flow library. + * Good performance cannot be guaranteed in the presence of such recursion, so + * it should be replaced by using more than one copy of the data flow library. + * Four copies are available: `DataFlow` through `DataFlow4`. + */ +private abstract +class ConfigurationRecursionPrevention extends DataFlow::Configuration { + bindingset[this] + ConfigurationRecursionPrevention() { any() } + + override predicate hasFlow(DataFlow::Node source, DataFlow::Node sink) { + strictcount(DataFlow::Node n | this.isSource(n)) < 0 + or + strictcount(DataFlow::Node n | this.isSink(n)) < 0 + or + strictcount(DataFlow::Node n1, DataFlow::Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0 + or + super.hasFlow(source, sink) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/StackAddress.qll b/cpp/ql/src/semmle/code/cpp/dataflow/StackAddress.qll new file mode 100644 index 000000000000..d696df934770 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/dataflow/StackAddress.qll @@ -0,0 +1,304 @@ +/** + * Provides utilities for determining which expressions contain + * stack addresses. + */ +import cpp +import semmle.code.cpp.controlflow.SSA + +/** + * A stack address flows to `use`. + * The simplest case is when `use` is the expression `&var`, but + * assignments are also handled. For example: + * + * x = &var; + * y = x; + * ...y... // use of &var + * + * `useType` is the type of data which we believe was allocated on the + * stack. It is particularly important when dealing with pointers. Consider + * this example: + * + * int x[10]; + * int *y = new int[10]; + * ... = &x[1]; + * ... = &y[1]; + * + * In this example, x and y are both stack variables. But &x[1] is a + * pointer to the stack and &y[1] is a pointer to the heap. The difference + * is that the type of x is int[10], but the type of y is int*. This + * information is stored in `useType`. + * + * `source` is the origin of the stack address. It is only used to improve + * the quality of the error messages. + * + * `isLocal` is true if the stack address came from the current + * function. It is false if the stack address arrived via a function + * parameter. This information is only used to improve the quality of the + * error messages. + */ +predicate stackPointerFlowsToUse( + Expr use, Type useType, Expr source, boolean isLocal) { + // Arrays in C are convertible to pointers. For example, if the type of x + // is int[10] then it is convertible to int*. The same rule applies to + // multidimensional arrays: if the type of y is int[2][3][4], then it is + // convertible to int(*)[3][4]. This conversion is what happens under the + // hood when we index an array. For example, x[5] is equivalent to + // *(x+5), so x is first converted to a pointer and then the pointer + // arithmetic is applied to the pointer. + exists (ArrayType arrayType + | stackReferenceFlowsToUse(use, arrayType, source, isLocal) and + useType = getExprPtrType(use.getConversion()).getBaseType()) + or + // Address of: &x + stackReferenceFlowsToUse( + use.(AddressOfExpr).getOperand(), useType, source, isLocal) + or + // Pointer subtract: if p is a pointer to a stack address, then p-1 is + // too. + stackPointerFlowsToUse( + use.(PointerSubExpr).getLeftOperand(), useType, source, isLocal) + or + // Pointer add: if p is a pointer to a stack address, then p+1 is too. + stackPointerFlowsToUse( + use.(PointerAddExpr).getAnOperand(), useType, source, isLocal) + or + // Indirect use of a stack address. + exists (SsaDefinition def, LocalScopeVariable var + | stackPointerFlowsToDef(def, var, useType, source, isLocal) and + use = def.getAUse(var)) + or + // Use of a stack address which arrived via one of the function's + // parameters. We do not use the original pointer source here because it + // could lead to an extremely large number of results if the function has + // many callers. Instead we set `source` to the use of the parameter. + exists (SsaDefinition def, Parameter param + | pointerParamFlowsToDef(def, param, useType) and + use = def.getAUse(param) and + source = use and + isLocal = false) + or + // Similar to the parameter case above. If a member function is called + // on an object which is allocated on the stack, then the "this" pointer + // contains a stack address. + exists (ThisExpr thisExpr + | use = thisExpr and + memberFcnMightRunOnStack(thisExpr.getEnclosingFunction(), useType) and + source = use and + isLocal = false) +} + +/** + * Helper function for stackPointerFlowsToUse. Gets the type of a pointer + * expression. + */ +cached private PointerType getExprPtrType(Expr use) { + result = use.getType().getUnspecifiedType() +} + +predicate stackReferenceFlowsToUse( + Expr use, Type useType, Expr source, boolean isLocal) { + // Stack variables + exists (LocalScopeVariable var + | not var.isStatic() and + use = source and + source = var.getAnAccess() and + isLocal = true and + + // If the type of the variable is a reference type, such as int&, then + // we need to look at its definition to determine whether it contains a + // stack reference. This is handled by stackReferenceFlowsToDef, below. + not isReferenceVariable(var) and + + // There is a subtlety relating to ArrayType which is hidden by the + // simplicity of the line below. Consider this example: + // + // int arraytest(int p[3][4][5]) { + // int x[3][4][5] = { ... }; + // return x[1][2][3] + p[1][2][3]; + // } + // + // The types of `x` and `p` are the same, but `x` is stack allocated + // and `p` is not. So we want `useType` to be an ArrayType for `x` and + // a PointerType for `p`. Luckily, this conversion happens + // automatically when the variable is used. So we get the correct type + // provided that we get it from `use` rather than from `var`. + useType = use.getType().getUnspecifiedType()) + or + // Accessing the field of a class, struct, or union. + exists (FieldAccess access, Class classType + | use = access and useType = access.getType().getUnspecifiedType() + | // Handle both x.f and x->f: + stackReferenceFlowsToUse(access.getQualifier(), classType, source, isLocal) or + stackPointerFlowsToUse(access.getQualifier(), classType, source, isLocal)) + or + // Array indexing: x[i]. + // Note that array types are converted to pointers in + // stackPointerFlowsToUse. + stackPointerFlowsToUse( + use.(ArrayExpr).getArrayBase(), useType, source, isLocal) + or + // Pointer dereference: *x + stackPointerFlowsToUse( + use.(PointerDereferenceExpr).getOperand(), useType, source, isLocal) + or + // Indirect use of a stack reference, via a reference variable. + exists (SsaDefinition def, LocalScopeVariable var + | stackReferenceFlowsToDef(def, var, useType, source, isLocal) and + use = def.getAUse(var)) + or + // Use of a stack reference which arrived via one of the function's + // parameters. We do not use the original reference source here because + // it could lead to an extremely large number of results if the function + // has many callers. Instead we set `source` to the use of the parameter. + exists (SsaDefinition def, Parameter param + | referenceParamFlowsToDef(def, param, useType) and + use = def.getAUse(param) and + source = use and + isLocal = false) +} + +/** + * Helper predicate for stackPointerFlowsToUse. Tracks the flow of stack + * addresses through SSA definitions. + */ +predicate stackPointerFlowsToDef( + SsaDefinition def, LocalScopeVariable var, + Type useType, Expr source, boolean isLocal) { + stackPointerFlowsToUse( + def.getDefiningValue(var), useType, source, isLocal) + or + // Increment/decrement operators. + exists (VariableAccess access + | access = def.(CrementOperation).getOperand() and + var = access.getTarget() and + stackPointerFlowsToUse(access, useType, source, isLocal)) + or + // Transitive closure over phi definitions. + stackPointerFlowsToDef( + def.getAPhiInput(var), var, useType, source, isLocal) +} + +/** + * Helper predicate for stackPointerFlowsToUse. Tracks the flow of stack + * references through SSA definitions. This predicate is almost identical + * to stackPointerFlowsToDef, except it handles references types, such as + * int&, rather than pointers. + */ +predicate stackReferenceFlowsToDef( + SsaDefinition def, LocalScopeVariable var, + Type useType, Expr source, boolean isLocal) { + // Check that the type of the variable is a reference type and delegate + // the rest of the work to stackReferenceFlowsToDef_Impl. + isReferenceVariable(var) and + stackReferenceFlowsToDef_Impl(def, var, useType, source, isLocal) +} + +/** + * stackReferenceFlowsToDef delegates most of the work to this + * predicate. + */ +predicate stackReferenceFlowsToDef_Impl( + SsaDefinition def, LocalScopeVariable var, + Type useType, Expr source, boolean isLocal) { + stackReferenceFlowsToUse( + def.getDefiningValue(var), useType, source, isLocal) + or + // Increment/decrement operators. + exists (VariableAccess access + | access = def.(CrementOperation).getOperand() and + var = access.getTarget() and + stackReferenceFlowsToUse(access, useType, source, isLocal)) + or + // Transitive closure over phi definitions. + stackReferenceFlowsToDef( + def.getAPhiInput(var), var, useType, source, isLocal) +} + +/** The type of the variable is a reference type, such as int&. */ +predicate isReferenceVariable(LocalScopeVariable var) { + var.getType().getUnspecifiedType() instanceof ReferenceType +} + +/** + * Helper predicate for stackPointerFlowsToUse. Tracks the flow of stack + * addresses which arrived through one of the function's parameters. This + * predicate is very similar to stackPointerFlowsToDef but they cannot be + * merged, because we cannot identify a sensible source expression here. + */ +predicate pointerParamFlowsToDef( + SsaDefinition def, Parameter param, Type useType) { + // Only include a parameter definition if we can find a call site which + // might pass it a stack address. + exists (FunctionCall call + | call.getTarget() = param.getFunction() and + stackPointerFlowsToUse( + call.getArgument(param.getIndex()), useType, _, _) and + def.definedByParameter(param)) + or + // Transitive closure over phi definitions. + pointerParamFlowsToDef(def.getAPhiInput(param), param, useType) +} + +/** + * Helper predicate for stackPointerFlowsToUse. Tracks the flow of stack + * addresses which arrived through one of the function's parameters. This + * predicate is very similar to stackPointerFlowsToDef but they cannot be + * merged, because we cannot identify a sensible source expression here. + */ +predicate referenceParamFlowsToDef( + SsaDefinition def, Parameter param, Type useType) { + // Only include a parameter definition if we can find a call site which + // might pass it a stack reference. + exists (FunctionCall call + | call.getTarget() = param.getFunction() and + stackReferenceFlowsToUse( + call.getArgument(param.getIndex()), useType, _, _) and + def.definedByParameter(param)) + or + // Transitive closure over phi definitions. + referenceParamFlowsToDef(def.getAPhiInput(param), param, useType) +} + +/** + * Holds if this member function might be called on an object which + * is allocated on the stack. + */ +predicate memberFcnMightRunOnStack(MemberFunction fcn, Type useType) { + exists (FunctionCall call + | call.getTarget() = fcn + | // Call of the form `x.f()` where `x` is allocated on the stack. + stackReferenceFlowsToUse(call.getQualifier(), useType, _, _) + + or + // Call of the form `x->f()` where `x` is allocated on the stack. + stackPointerFlowsToUse(call.getQualifier(), useType, _, _)) + + or + // Constructor calls need to be treated as a special case, because + // `call.getQualifier()` is empty. + (constructorMightRunOnStack(fcn) and + useType = fcn.getDeclaringType().getUnspecifiedType()) +} + +/** + * Helper predicate for memberFcnMightRunOnStack. Function calls + * to constructors need to be treated as a special case, because + * `call.getQualifier()` is empty. Instead, we need to check whether + * the constructor is called from an initializer. There are several + * kinds of initializers to consider. + */ +predicate constructorMightRunOnStack(Constructor constructor) { + exists (ConstructorCall call + | call.getTarget() = constructor + | // Call to a constructor from a stack variable's initializer. + exists (LocalScopeVariable var | var.getInitializer().getExpr() = call) + + or + // Call to a constructor from another constructor which might + // also run on the stack. + (constructorMightRunOnStack(call.getEnclosingFunction()) and + (call instanceof ConstructorDirectInit or + call instanceof ConstructorVirtualInit or + call instanceof ConstructorDelegationInit or + exists (ConstructorFieldInit init | init.getExpr() = call)))) +} diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/TaintTracking.qll b/cpp/ql/src/semmle/code/cpp/dataflow/TaintTracking.qll new file mode 100644 index 000000000000..2d8181be9454 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/dataflow/TaintTracking.qll @@ -0,0 +1,229 @@ +/** + * Provides classes for performing local (intra-procedural) and + * global (inter-procedural) taint-tracking analyses. + * + * We define _taint propagation_ informally to mean that a substantial part of + * the information from the source is preserved at the sink. For example, taint + * propagates from `x` to `x + 100`, but it does not propagate from `x` to `x > + * 100` since we consider a single bit of information to be too little. + */ +import semmle.code.cpp.dataflow.DataFlow +import semmle.code.cpp.dataflow.DataFlow2 + +module TaintTracking { + + /** + * A configuration of interprocedural taint tracking analysis. This defines + * sources, sinks, and any other configurable aspect of the analysis. Each + * use of the taint tracking library must define its own unique extension of + * this abstract class. + * + * A taint-tracking configuration is a special data flow configuration + * (`DataFlow::Configuration`) that allows for flow through nodes that do not + * necessarily preserve values but are still relevant from a taint-tracking + * perspective. (For example, string concatenation, where one of the operands + * is tainted.) + * + * To create a configuration, extend this class with a subclass whose + * characteristic predicate is a unique singleton string. For example, write + * + * ``` + * class MyAnalysisConfiguration extends TaintTracking::Configuration { + * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } + * // Override `isSource` and `isSink`. + * // Optionally override `isSanitizer`. + * // Optionally override `isSanitizerEdge`. + * // Optionally override `isAdditionalTaintStep`. + * } + * ``` + * + * Then, to query whether there is flow between some `source` and `sink`, + * write + * + * ``` + * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) + * ``` + * + * Multiple configurations can coexist, but it is unsupported to depend on a + * `TaintTracking::Configuration` or a `DataFlow::Configuration` in the + * overridden predicates that define sources, sinks, or additional steps. + * Instead, the dependency should go to a `TaintTracking::Configuration2` or + * a `DataFlow{2,3,4}::Configuration`. + */ + abstract class Configuration extends DataFlow::Configuration { + bindingset[this] + Configuration() { any() } + + /** Holds if `source` is a taint source. */ + // overridden to provide taint-tracking specific qldoc + abstract override predicate isSource(DataFlow::Node source); + + /** Holds if `sink` is a taint sink. */ + // overridden to provide taint-tracking specific qldoc + abstract override predicate isSink(DataFlow::Node sink); + + /** + * Holds if taint should not flow into `node`. + */ + predicate isSanitizer(DataFlow::Node node) { none() } + + /** Holds if data flow from `node1` to `node2` is prohibited. */ + predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) { + none() + } + + /** + * Holds if the additional taint propagation step + * from `source` to `target` must be taken into account in the analysis. + * This step will only be followed if `target` is not in the `isSanitizer` + * predicate. + */ + predicate isAdditionalTaintStep(DataFlow::Node source, + DataFlow::Node target) + { none() } + + final override + predicate isBarrier(DataFlow::Node node) { isSanitizer(node) } + + /** DEPRECATED: use `isSanitizerEdge` instead. */ + override deprecated + predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) { + this.isSanitizerEdge(node1, node2) + } + + final override + predicate isAdditionalFlowStep(DataFlow::Node source, DataFlow::Node target) { + this.isAdditionalTaintStep(source, target) + or + localTaintStep(source, target) + } + } + + /** + * A taint-tracking configuration that is backed by the `DataFlow2` library + * instead of `DataFlow`. Use this class when taint-tracking configurations + * or data-flow configurations must depend on each other. + * + * See `TaintTracking::Configuration` for the full documentation. + */ + abstract class Configuration2 extends DataFlow2::Configuration { + bindingset[this] + Configuration2() { any() } + + /** Holds if `source` is a taint source. */ + // overridden to provide taint-tracking specific qldoc + abstract override predicate isSource(DataFlow::Node source); + + /** Holds if `sink` is a taint sink. */ + // overridden to provide taint-tracking specific qldoc + abstract override predicate isSink(DataFlow::Node sink); + + /** + * Holds if taint should not flow into `node`. + */ + predicate isSanitizer(DataFlow::Node node) { none() } + + /** Holds if data flow from `node1` to `node2` is prohibited. */ + predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) { + none() + } + + /** + * Holds if the additional taint propagation step + * from `source` to `target` must be taken into account in the analysis. + * This step will only be followed if `target` is not in the `isSanitizer` + * predicate. + */ + predicate isAdditionalTaintStep(DataFlow::Node source, + DataFlow::Node target) + { none() } + + final override + predicate isBarrier(DataFlow::Node node) { isSanitizer(node) } + + /** DEPRECATED: use `isSanitizerEdge` instead. */ + override deprecated + predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) { + this.isSanitizerEdge(node1, node2) + } + + final override + predicate isAdditionalFlowStep(DataFlow::Node source, DataFlow::Node target) { + this.isAdditionalTaintStep(source, target) + or + localTaintStep(source, target) + } + } + + /** + * Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local + * (intra-procedural) step. + */ + predicate localTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + // Taint can flow into using ordinary data flow. + DataFlow::localFlowStep(nodeFrom, nodeTo) + or + // Taint can flow through expressions that alter the value but preserve + // more than one bit of it _or_ expressions that follow data through + // pointer indirections. + exists(Expr exprFrom, Expr exprTo | + exprFrom = nodeFrom.asExpr() and + exprTo = nodeTo.asExpr() + | + exprFrom = exprTo.getAChild() and + not noParentExprFlow(exprFrom, exprTo) and + not noFlowFromChildExpr(exprTo) + or + // Taint can flow from the `x` variable in `x++` to all subsequent + // accesses to the unmodified `x` variable. + // + // `DataFlow` without taint specifies flow from `++x` and `x += 1` into the + // variable `x` and thus into subsequent accesses because those expressions + // compute the same value as `x`. This is not the case for `x++`, which + // computes a different value, so we have to add that ourselves for taint + // tracking. The flow from expression `x` into `x++` etc. is handled in the + // case above. + exprTo = DataFlow::getAnAccessToAssignedVariable( + exprFrom.(PostfixCrementOperation) + ) + ) + } + + /** + * Holds if taint may propagate from `source` to `sink` in zero or more local + * (intra-procedural) steps. + */ + predicate localTaint(DataFlow::Node source, DataFlow::Node sink) { + localTaintStep*(source, sink) + } + + /** + * Holds if we do not propagate taint from `fromExpr` to `toExpr` + * even though `toExpr` is the AST parent of `fromExpr`. + */ + private predicate noParentExprFlow(Expr fromExpr, Expr toExpr) { + fromExpr = toExpr.(ConditionalExpr).getCondition() + or + fromExpr = toExpr.(CommaExpr).getLeftOperand() + or + fromExpr = toExpr.(AssignExpr).getLValue() // LHS of `=` + } + + /** + * Holds if we do not propagate taint from a child of `e` to `e` itself. + */ + private predicate noFlowFromChildExpr(Expr e) { + e instanceof ComparisonOperation + or + e instanceof LogicalAndExpr + or + e instanceof LogicalOrExpr + or + e instanceof Call + or + e instanceof SizeofOperator + or + e instanceof AlignofOperator + } + +} \ No newline at end of file diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowDispatch.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowDispatch.qll new file mode 100644 index 000000000000..8beb2c5d319a --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowDispatch.qll @@ -0,0 +1,60 @@ +private import cpp +private import DataFlowPrivate + +Function viableImpl(MethodAccess ma) { + result = ma.getTarget() +} + +Function viableCallable(Call call) { + result = call.getTarget() +} + +/** + * Holds if the call context `ctx` reduces the set of viable dispatch + * targets of `ma` in `c`. + */ +predicate reducedViableImplInCallContext(MethodAccess ma, Callable c, Call ctx) { + none() +} + +/** + * Gets a viable dispatch target of `ma` in the context `ctx`. This is + * restricted to those `ma`s for which a context might make a difference. + */ +private Method viableImplInCallContext(MethodAccess ma, Call ctx) { + // stub implementation + result = viableImpl(ma) and + viableCallable(ctx) = ma.getEnclosingFunction() +} + +/** + * 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. + */ +Method prunedViableImplInCallContext(MethodAccess ma, Call ctx) { + result = viableImplInCallContext(ma, ctx) and + reducedViableImplInCallContext(ma, _, ctx) +} + +/** + * 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(Method m, MethodAccess ma) { + exists(int tgts, int ctxtgts | + m = viableImpl(ma) and + ctxtgts = count(Call ctx | m = viableImplInCallContext(ma, ctx)) and + tgts = strictcount(Call ctx | viableCallable(ctx) = ma.getEnclosingFunction()) and + ctxtgts < tgts + ) +} + +/** + * 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`. + */ +Method prunedViableImplInCallContextReverse(MethodAccess ma, Call ctx) { + result = viableImplInCallContext(ma, ctx) and + reducedViableImplInReturn(result, ma) +} diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll new file mode 100644 index 000000000000..142ea7f8a7e9 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll @@ -0,0 +1,1536 @@ +/** + * Provides an implementation of global (interprocedural) data flow. This file + * re-exports the local (intraprocedural) data flow analysis from `DataFlowUtil` + * and adds a global analysis, mainly exposed through the `Configuration` class. + * This file exists in several identical copies, allowing queries to use + * multiple `Configuration` classes that depend on each other without + * introducing mutual recursion among those configurations. + */ + + import DataFlowUtil + private import DataFlowPrivate + private import DataFlowDispatch + private import DataFlowImplCommon + + + /** + * A configuration of interprocedural data flow analysis. This defines + * sources, sinks, and any other configurable aspect of the analysis. Each + * use of the global data flow library must define its own unique extension + * of this abstract class. To create a configuration, extend this class with + * a subclass whose characteristic predicate is a unique singleton string. + * For example, write + * + * ``` + * class MyAnalysisConfiguration extends DataFlow::Configuration { + * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } + * // Override `isSource` and `isSink`. + * // Optionally override `isBarrier`. + * // Optionally override `isAdditionalFlowStep`. + * } + * ``` + * + * Then, to query whether there is flow between some `source` and `sink`, + * write + * + * ``` + * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) + * ``` + * + * Multiple configurations can coexist, but two classes extending + * `DataFlow::Configuration` should never depend on each other. One of them + * should instead depend on a `DataFlow2::Configuration`, a + * `DataFlow3::Configuration`, or a `DataFlow4::Configuration`. + */ + abstract class Configuration extends string { + bindingset[this] + Configuration() { any() } + + /** + * Holds if `source` is a relevant data flow source. + */ + abstract predicate isSource(Node source); + + /** + * Holds if `sink` is a relevant data flow sink. + */ + abstract predicate isSink(Node sink); + + /** Holds if data flow through `node` is prohibited. */ + predicate isBarrier(Node node) { none() } + + /** Holds if data flow from `node1` to `node2` is prohibited. */ + predicate isBarrierEdge(Node node1, Node node2) { none() } + + /** + * Holds if the additional flow step from `node1` to `node2` must be taken + * into account in the analysis. + */ + predicate isAdditionalFlowStep(Node node1, Node node2) { none() } + + /** + * Gets the virtual dispatch branching limit when calculating field flow. + * This can be overridden to a smaller value to improve performance (a + * value of 0 disables field flow), or a larger value to get more results. + */ + int fieldFlowBranchLimit() { result = 2 } + + /** + * Holds if data may flow from `source` to `sink` for this configuration. + */ + predicate hasFlow(Node source, Node sink) { + flowsTo(source, sink, this) + } + + /** + * Holds if data may flow from `source` to `sink` for this configuration. + * + * The corresponding paths are generated from the end-points and the graph + * included in the module `PathGraph`. + */ + predicate hasFlowPath(PathNode source, PathNode sink) { + flowsTo(source, sink, _, _, this) + } + + /** + * Holds if data may flow from some source to `sink` for this configuration. + */ + predicate hasFlowTo(Node sink) { + hasFlow(_, sink) + } + + /** + * Holds if data may flow from some source to `sink` for this configuration. + */ + predicate hasFlowToExpr(Expr sink) { + hasFlowTo(exprNode(sink)) + } + + /** DEPRECATED: use `hasFlow` instead. */ + deprecated predicate hasFlowForward(Node source, Node sink) { + hasFlow(source, sink) + } + + /** DEPRECATED: use `hasFlow` instead. */ + deprecated predicate hasFlowBackward(Node source, Node sink) { + hasFlow(source, sink) + } + } + + /** + * Holds if the additional step from `node1` to `node2` jumps between callables. + */ + private predicate additionalJumpStep(Node node1, Node node2, Configuration config) { + config.isAdditionalFlowStep(node1, node2) and + node1.getEnclosingCallable() != node2.getEnclosingCallable() + } + + pragma[noinline] + private predicate isAdditionalFlowStep(Node node1, Node node2, Callable callable1, Callable callable2, Configuration config) { + config.isAdditionalFlowStep(node1, node2) and + callable1 = node1.getEnclosingCallable() and + callable2 = node2.getEnclosingCallable() + } + + /** + * Holds if the additional step from `node1` to `node2` does not jump between callables. + */ + private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) { + exists(Callable callable | + isAdditionalFlowStep(node1, node2, callable, callable, config) + ) + } + + /** + * Holds if data can flow from `node1` to `node2` through a static field or + * variable capture. + */ + private predicate jumpStep(Node node1, Node node2, boolean preservesValue, Configuration config) { + jumpStep(node1, node2) and preservesValue = true or + additionalJumpStep(node1, node2, config) and preservesValue = false + } + + /** + * Holds if data can flow in one local step from `node1` to `node2` taking + * additional steps from the configuration into account. + */ + private predicate localFlowStep(Node node1, Node node2, boolean preservesValue, Configuration config) { + localFlowStep(node1, node2) and not config.isBarrierEdge(node1, node2) and preservesValue = true or + additionalLocalFlowStep(node1, node2, config) and preservesValue = false + } + + pragma[noinline] + private Method returnNodeGetEnclosingCallable(ReturnNode ret) { + result = ret.getEnclosingCallable() + } + + /** + * Holds if field flow should be used for the given configuration. + */ + private predicate useFieldFlow(Configuration config) { + config.fieldFlowBranchLimit() >= 1 + } + + /** + * Holds if `node` is reachable from a source in the given configuration + * ignoring call contexts. + */ + private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) { + not config.isBarrier(node) and + ( + config.isSource(node) and stored = false + or + exists(Node mid, boolean preservesValue | + nodeCandFwd1(mid, stored, config) and + localFlowStep(mid, node, preservesValue, config) and + (stored = false or preservesValue = true) + ) + or + exists(Node mid, boolean preservesValue | + nodeCandFwd1(mid, stored, config) and + jumpStep(mid, node, preservesValue, config) and + (stored = false or preservesValue = true) + ) + or + // store + exists(Node mid | + useFieldFlow(config) and + nodeCandFwd1(mid, _, config) and + store(mid, _, node) and + stored = true + ) + or + // read + exists(Node mid, Content f | + nodeCandFwd1(mid, true, config) and + read(mid, f, node) and + storeCandFwd1(f, unbind(config)) and + (stored = false or stored = true) + ) + or + // flow into a callable + exists(Node arg | + nodeCandFwd1(arg, stored, config) and + viableParamArg(node, arg) + ) + or + // flow out of an argument + exists(PostUpdateNode mid, ParameterNode p | + nodeCandFwd1(mid, stored, config) and + parameterValueFlowsToUpdate(p, mid) and + viableParamArg(p, node.(PostUpdateNode).getPreUpdateNode()) + ) + or + // flow out of a callable + exists(Method m, MethodAccess ma, ReturnNode ret | + nodeCandFwd1(ret, stored, config) and + m = returnNodeGetEnclosingCallable(ret) and + m = viableImpl(ma) and + node.asExpr() = ma + ) + ) + } + + /** + * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`. + */ + private predicate storeCandFwd1(Content f, Configuration config) { + exists(Node mid, Node node | + not config.isBarrier(node) and + useFieldFlow(unbind(config)) and + nodeCandFwd1(mid, _, config) and + store(mid, f, node) + ) + } + + bindingset[result, b] + private boolean unbindBool(boolean b) { result != b.booleanNot() } + + /** + * Holds if `node` is part of a path from a source to a sink in the given + * configuration ignoring call contexts. + */ + pragma[nomagic] + private predicate nodeCand1(Node node, boolean stored, Configuration config) { + nodeCandFwd1(node, false, config) and + config.isSink(node) and stored = false + or + nodeCandFwd1(node, unbindBool(stored), unbind(config)) and + ( + exists(Node mid, boolean preservesValue | + localFlowStep(node, mid, preservesValue, config) and + nodeCand1(mid, stored, config) and + (stored = false or preservesValue = true) + ) + or + exists(Node mid, boolean preservesValue | + jumpStep(node, mid, preservesValue, config) and + nodeCand1(mid, stored, config) and + (stored = false or preservesValue = true) + ) + or + // store + exists(Node mid, Content f | + store(node, f, mid) and + readCand1(f, unbind(config)) and + nodeCand1(mid, true, config) and + (stored = false or stored = true) + ) + or + // read + exists(Node mid, Content f | + read(node, f, mid) and + storeCandFwd1(f, unbind(config)) and + nodeCand1(mid, _, config) and + stored = true + ) + or + // flow into a callable + exists(Node param | + viableParamArg(param, node) and + nodeCand1(param, stored, config) + ) + or + // flow out of an argument + exists(PostUpdateNode mid, ParameterNode p | + parameterValueFlowsToUpdate(p, node) and + viableParamArg(p, mid.getPreUpdateNode()) and + nodeCand1(mid, stored, config) + ) + or + // flow out of a callable + exists(Method m, ExprNode ma | + nodeCand1(ma, stored, config) and + m = returnNodeGetEnclosingCallable(node) and + m = viableImpl(ma.getExpr()) + ) + ) + } + + /** + * Holds if `f` is the target of a read in the flow covered by `nodeCand1`. + */ + private predicate readCand1(Content f, Configuration config) { + exists(Node mid, Node node | + useFieldFlow(unbind(config)) and + nodeCandFwd1(node, true, unbind(config)) and + read(node, f, mid) and + storeCandFwd1(f, unbind(config)) and + nodeCand1(mid, _, config) + ) + } + + /** + * Holds if there is a path from `p` to `node` in the same callable that is + * part of a path from a source to a sink taking simple call contexts into + * consideration. This is restricted to paths that does not necessarily + * preserve the value of `p` by making use of at least one additional step + * from the configuration. + */ + pragma[nomagic] + private predicate simpleParameterFlow(ParameterNode p, Node node, RefType t, Configuration config) { + nodeCand1(node, false, config) and + p = node and + t = getErasedRepr(node.getType()) and + not parameterValueFlowsThrough(p) + or + nodeCand1(node, false, unbind(config)) and + exists(Node mid | + simpleParameterFlow(p, mid, t, config) and + localFlowStep(mid, node, true, config) and + compatibleTypes(t, node.getType()) + ) + or + nodeCand1(node, false, unbind(config)) and + exists(Node mid | + simpleParameterFlow(p, mid, _, config) and + localFlowStep(mid, node, false, config) and + t = getErasedRepr(node.getType()) + ) + or + nodeCand1(node, false, unbind(config)) and + exists(Node mid | + simpleParameterFlow(p, mid, t, config) and + localStoreReadStep(mid, node) and + compatibleTypes(t, node.getType()) + ) + or + // value flow through a callable + nodeCand1(node, false, config) and + exists(Node arg | + simpleParameterFlow(p, arg, t, config) and + argumentValueFlowsThrough(arg, node) and + compatibleTypes(t, node.getType()) + ) + or + // flow through a callable + nodeCand1(node, false, config) and + exists(Node arg | + simpleParameterFlow(p, arg, _, config) and + simpleArgumentFlowsThrough(arg, node, t, config) + ) + } + + /** + * Holds if data can flow from `arg` through the `call` taking simple call + * contexts into consideration and that this is part of a path from a source + * to a sink. This is restricted to paths through the `call` that does not + * necessarily preserve the value of `arg` by making use of at least one + * additional step from the configuration. + */ + private predicate simpleArgumentFlowsThrough(ArgumentNode arg, ExprNode call, RefType t, Configuration config) { + exists(ParameterNode param, ReturnNode ret | + nodeCand1(arg, false, unbind(config)) and + nodeCand1(call, false, unbind(config)) and + viableParamArg(param, arg) and + simpleParameterFlow(param, ret, t, config) and + arg.argumentOf(call.getExpr(), _) + ) + } + + /** + * Holds if data can flow from `node1` to `node2` by a step through a method. + */ + private predicate flowThroughMethod(Node node1, Node node2, boolean preservesValue, Configuration config) { + simpleArgumentFlowsThrough(node1, node2, _, config) and preservesValue = false or + argumentValueFlowsThrough(node1, node2) and preservesValue = true + } + + /** + * Holds if data can flow from `node1` to `node2` in one local step or a step + * through a method. + */ + private predicate localFlowStepOrFlowThroughMethod(Node node1, Node node2, boolean preservesValue, Configuration config) { + localFlowStep(node1, node2, preservesValue, config) or + flowThroughMethod(node1, node2, preservesValue, config) + } + + /** + * Holds if data can flow out of a callable from `node1` to `node2`, either + * through a `ReturnNode` or through an argument that has been mutated, and + * that this step is part of a path from a source to a sink. + */ + private predicate flowOutOfCallable(Node node1, Node node2, Configuration config) { + nodeCand1(node1, _, unbind(config)) and + nodeCand1(node2, _, config) and + ( + // flow out of an argument + exists(ParameterNode p | + parameterValueFlowsToUpdate(p, node1) and + viableParamArg(p, node2.(PostUpdateNode).getPreUpdateNode()) + ) + or + // flow out of a method + exists(Method m, MethodAccess ma, ReturnNode ret | + ret = node1 and + m = returnNodeGetEnclosingCallable(ret) and + m = viableImpl(ma) and + node2.asExpr() = ma + ) + ) + } + + /** + * Holds if data can flow into a callable and that this step is part of a + * path from a source to a sink. + */ + private predicate flowIntoCallable(Node node1, Node node2, Configuration config) { + viableParamArg(node2, node1) and + nodeCand1(node1, _, unbind(config)) and + nodeCand1(node2, _, config) + } + + /** + * Gets the amount of forward branching on the origin of a cross-call path + * edge in the graph of paths between sources and sinks that ignores call + * contexts. + */ + private int branch(Node n1, Configuration conf) { + result = strictcount(Node n | flowOutOfCallable(n1, n, conf) or flowIntoCallable(n1, n, conf)) + } + + /** + * Gets the amount of backward branching on the target of a cross-call path + * edge in the graph of paths between sources and sinks that ignores call + * contexts. + */ + private int join(Node n2, Configuration conf) { + result = strictcount(Node n | flowOutOfCallable(n, n2, conf) or flowIntoCallable(n, n2, conf)) + } + + /** + * Holds if data can flow out of a callable from `node1` to `node2`, either + * through a `ReturnNode` or through an argument that has been mutated, and + * that this step is part of a path from a source to a sink. The + * `allowsFieldFlow` flag indicates whether the branching is within the limit + * specified by the configuration. + */ + private predicate flowOutOfCallable(Node node1, Node node2, boolean allowsFieldFlow, Configuration config) { + flowOutOfCallable(node1, node2, config) and + exists(int b, int j | + b = branch(node1, config) and + j = join(node2, config) and + if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false + ) + } + + /** + * Holds if data can flow into a callable and that this step is part of a + * path from a source to a sink. The `allowsFieldFlow` flag indicates whether + * the branching is within the limit specified by the configuration. + */ + private predicate flowIntoCallable(Node node1, Node node2, boolean allowsFieldFlow, Configuration config) { + flowIntoCallable(node1, node2, config) and + exists(int b, int j | + b = branch(node1, config) and + j = join(node2, config) and + if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false + ) + } + + /** + * Holds if `node` is part of a path from a source to a sink in the given + * configuration taking simple call contexts into consideration. + */ + private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Configuration config) { + nodeCand1(node, false, config) and + config.isSource(node) and fromArg = false and stored = false + or + nodeCand1(node, unbindBool(stored), unbind(config)) and + ( + exists(Node mid, boolean preservesValue | + nodeCandFwd2(mid, fromArg, stored, config) and + localFlowStepOrFlowThroughMethod(mid, node, preservesValue, config) and + (stored = false or preservesValue = true) + ) + or + exists(Node mid, boolean preservesValue | + nodeCandFwd2(mid, _, stored, config) and + jumpStep(mid, node, preservesValue, config) and + fromArg = false and + (stored = false or preservesValue = true) + ) + or + // store + exists(Node mid, Content f | + nodeCandFwd2(mid, fromArg, _, config) and + store(mid, f, node) and + readCand1(f, unbind(config)) and + stored = true + ) + or + // read + exists(Node mid, Content f | + nodeCandFwd2(mid, fromArg, true, config) and + read(mid, f, node) and + storeCandFwd2(f, unbind(config)) and + (stored = false or stored = true) + ) + or + exists(Node mid, boolean allowsFieldFlow | + nodeCandFwd2(mid, _, stored, config) and + flowIntoCallable(mid, node, allowsFieldFlow, config) and + fromArg = true and + (stored = false or allowsFieldFlow = true) + ) + or + exists(Node mid, boolean allowsFieldFlow | + nodeCandFwd2(mid, false, stored, config) and + flowOutOfCallable(mid, node, allowsFieldFlow, config) and + fromArg = false and + (stored = false or allowsFieldFlow = true) + ) + ) + } + + /** + * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`. + */ + private predicate storeCandFwd2(Content f, Configuration config) { + exists(Node mid, Node node | + useFieldFlow(unbind(config)) and + nodeCand1(node, true, unbind(config)) and + nodeCandFwd2(mid, _, _, config) and + store(mid, f, node) and + readCand1(f, unbind(config)) + ) + } + + /** + * Holds if `node` is part of a path from a source to a sink in the given + * configuration taking simple call contexts into consideration. + */ + private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configuration config) { + nodeCandFwd2(node, _, false, config) and + config.isSink(node) and toReturn = false and stored = false + or + nodeCandFwd2(node, _, unbindBool(stored), unbind(config)) and + ( + exists(Node mid, boolean preservesValue | + localFlowStepOrFlowThroughMethod(node, mid, preservesValue, config) and + nodeCand2(mid, toReturn, stored, config) and + (stored = false or preservesValue = true) + ) + or + exists(Node mid, boolean preservesValue | + jumpStep(node, mid, preservesValue, config) and + nodeCand2(mid, _, stored, config) and + toReturn = false and + (stored = false or preservesValue = true) + ) + or + // store + exists(Node mid, Content f | + store(node, f, mid) and + readCand2(f, unbind(config)) and + nodeCand2(mid, toReturn, true, config) and + (stored = false or stored = true) + ) + or + // read + exists(Node mid, Content f | + read(node, f, mid) and + storeCandFwd2(f, unbind(config)) and + nodeCand2(mid, toReturn, _, config) and + stored = true + ) + or + exists(Node mid, boolean allowsFieldFlow | + flowIntoCallable(node, mid, allowsFieldFlow, config) and + nodeCand2(mid, false, stored, config) and + toReturn = false and + (stored = false or allowsFieldFlow = true) + ) + or + exists(Node mid, boolean allowsFieldFlow | + flowOutOfCallable(node, mid, allowsFieldFlow, config) and + nodeCand2(mid, _, stored, config) and + toReturn = true and + (stored = false or allowsFieldFlow = true) + ) + ) + } + + /** + * Holds if `f` is the target of a read in the flow covered by `nodeCand2`. + */ + private predicate readCand2(Content f, Configuration config) { + exists(Node mid, Node node | + useFieldFlow(unbind(config)) and + nodeCandFwd2(node, _, true, unbind(config)) and + read(node, f, mid) and + storeCandFwd2(f, unbind(config)) and + nodeCand2(mid, _, _, config) + ) + } + + private predicate storeCand(Content f, Configuration conf) { + exists(Node n1, Node n2 | + store(n1, f, n2) and + nodeCand2(n1, _, _, conf) and + nodeCand2(n2, _, _, unbind(conf)) + ) + } + + private predicate readCand(Content f, Configuration conf) { + readCand2(f, conf) + } + + /** + * Holds if `f` is the target of both a store and a read in the path graph + * covered by `nodeCand2`. + */ + pragma[noinline] + private predicate readStoreCand(Content f, Configuration conf) { + storeCand(f, conf) and + readCand(f, conf) + } + + private predicate nodeCand(Node node, Configuration config) { + nodeCand2(node, _, _, config) + } + + /** + * 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) { + nodeCand(node, config) and + ( + config.isSource(node) or + jumpStep(_, node, _, config) or + node instanceof ParameterNode or + node.asExpr() instanceof MethodAccess or + node instanceof PostUpdateNode or + read(_, _, node) or + node.asExpr() instanceof CastExpr + ) + } + + /** + * Holds if `node` can be the last node in a maximal subsequence of local + * flow steps in a dataflow path. + */ + private predicate localFlowExit(Node node, Configuration config) { + exists(Node next | + nodeCand(next, config) + | + jumpStep(node, next, _, config) or + flowIntoCallable(node, next, config) or + flowOutOfCallable(node, next, config) or + flowThroughMethod(node, next, _, config) or + store(node, _, next) or + read(node, _, next) + ) or + node.asExpr() instanceof CastExpr or + config.isSink(node) + } + + /** + * Holds if the local path from `node1` to `node2` is a prefix of a maximal + * subsequence of local flow steps in a dataflow path. + * + * This is the transitive closure of `localFlowStep` beginning at `localFlowEntry`. + */ + private predicate localFlowStepPlus(Node node1, Node node2, boolean preservesValue, Configuration config) { + localFlowEntry(node1, config) and + localFlowStep(node1, node2, preservesValue, config) and + node1 != node2 and + nodeCand(node2, unbind(config)) + or + exists(Node mid, boolean pv1, boolean pv2 | + localFlowStepPlus(node1, mid, pv1, config) and + localFlowStep(mid, node2, pv2, config) and + not mid.asExpr() instanceof CastExpr and + preservesValue = pv1.booleanAnd(pv2) and + nodeCand(node2, unbind(config)) + ) + } + + /** + * Holds if `node1` can step to `node2` in one or more local steps and this + * path can occur as a maximal subsequence of local steps in a dataflow path. + */ + pragma[noinline] + private predicate localFlowBigStep(Node node1, Node node2, boolean preservesValue, Configuration config) { + localFlowStepPlus(node1, node2, preservesValue, config) and + localFlowExit(node2, config) + } + + /** + * Holds if `f` may contain an object of the same type, `t`, as the one + * that contains `f`, and if this fact should be used to compress + * access paths. + * + * Examples include the tail pointer in a linked list or the left and right + * pointers in a binary tree. + */ + private predicate selfRef(Content f, RefType t) { + t = f.getDeclaringType() and + f.isSelfRef() + } + + private newtype TFlowContainer = + TRegularContent(Content f) { not selfRef(f, _) } or + TSelfRefContent(RefType t) { selfRef(_, t) } + + /** + * A `Content` or a `Content` abstracted as its declaring type. + * + * Sequences of one or more `Content`s in the same declaring type for which + * `isSelfRef()` holds are represented as a single `FlowContainer` in an + * `AccessPath`. + */ + private class FlowContainer extends TFlowContainer { + string toString() { + exists(Content f | this = TRegularContent(f) and result = f.toString()) or + exists(RefType t | this = TSelfRefContent(t) and result = t.toString()) + } + predicate usesContent(Content f) { + this = TRegularContent(f) or + exists(RefType t | this = TSelfRefContent(t) and selfRef(f, t)) + } + RefType getContainerType() { + exists(Content f | this = TRegularContent(f) and result = f.getDeclaringType()) or + this = TSelfRefContent(result) + } + } + + private newtype TAccessPathFront = TFrontNil(Type t) or TFrontHead(FlowContainer f) + + /** + * The front of an `AccessPath`. This is either a head or a nil. + */ + private class AccessPathFront extends TAccessPathFront { + string toString() { + exists(Type t | this = TFrontNil(t) | result = t.toString()) or + exists(FlowContainer f | this = TFrontHead(f) | result = f.toString()) + } + Type getType() { + this = TFrontNil(result) or + exists(FlowContainer head | this = TFrontHead(head) | result = head.getContainerType()) + } + predicate headUsesContent(Content f) { + exists(FlowContainer fc | + fc.usesContent(f) and + this = TFrontHead(fc) + ) + } + } + + private class AccessPathFrontNil extends AccessPathFront, TFrontNil { + } + + /** + * A `Node` at which a cast can occur such that the type should be checked. + */ + private class CastingNode extends Node { + CastingNode() { + this instanceof ParameterNode or + this.asExpr() instanceof CastExpr or + this.asExpr() instanceof MethodAccess or + this.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNode + } + } + + /** + * Holds if data can flow from a source to `node` with the given `apf`. + */ + private predicate flowCandFwd(Node node, boolean fromArg, AccessPathFront apf, Configuration config) { + flowCandFwd0(node, fromArg, apf, config) and + if node instanceof CastingNode then + compatibleTypes(node.getType(), apf.getType()) + else + any() + } + private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf, Configuration config) { + nodeCand2(node, _, false, config) and + config.isSource(node) and fromArg = false and apf = TFrontNil(getErasedRepr(node.getType())) + or + nodeCand(node, unbind(config)) and + ( + exists(Node mid | + flowCandFwd(mid, fromArg, apf, config) and + localFlowBigStep(mid, node, true, config) + ) + or + exists(Node mid, AccessPathFront apf0 | + flowCandFwd(mid, fromArg, apf0, config) and + localFlowBigStep(mid, node, false, config) and + apf0 instanceof AccessPathFrontNil and + apf = TFrontNil(getErasedRepr(node.getType())) + ) + or + exists(Node mid | + flowCandFwd(mid, _, apf, config) and + jumpStep(mid, node, true, config) and + fromArg = false + ) + or + exists(Node mid, AccessPathFront apf0 | + flowCandFwd(mid, _, apf0, config) and + jumpStep(mid, node, false, config) and + fromArg = false and + apf0 instanceof AccessPathFrontNil and + apf = TFrontNil(getErasedRepr(node.getType())) + ) + or + exists(Node mid, boolean allowsFieldFlow | + flowCandFwd(mid, _, apf, config) and + flowIntoCallable(mid, node, allowsFieldFlow, config) and + fromArg = true and + (apf instanceof AccessPathFrontNil or allowsFieldFlow = true) + ) + or + exists(Node mid, boolean allowsFieldFlow | + flowCandFwd(mid, false, apf, config) and + flowOutOfCallable(mid, node, allowsFieldFlow, config) and + fromArg = false and + (apf instanceof AccessPathFrontNil or allowsFieldFlow = true) + ) + or + exists(Node mid, boolean preservesValue, AccessPathFront apf0 | + flowCandFwd(mid, fromArg, apf0, config) and + flowThroughMethod(mid, node, preservesValue, config) and + (preservesValue = true and apf = apf0 or preservesValue = false and apf0 instanceof AccessPathFrontNil and apf = TFrontNil(getErasedRepr(node.getType()))) + ) + ) + or + exists(Node mid, Content f | + flowCandFwd(mid, fromArg, _, config) and + store(mid, f, node) and + nodeCand(node, unbind(config)) and + apf.headUsesContent(f) + ) + or + exists(Node mid, Content f, AccessPathFront apf0 | + flowCandFwd(mid, fromArg, apf0, config) and + read(mid, f, node) and + nodeCand(node, config) and + apf0.headUsesContent(f) and + consCandFwd(f, apf, unbind(config)) + ) + } + private predicate consCandFwd(Content f, AccessPathFront apf, Configuration config) { + exists(Node mid, Node n | + flowCandFwd(mid, _, apf, config) and + store(mid, f, n) and + nodeCand(n, unbind(config)) and + readStoreCand(f, unbind(config)) and + compatibleTypes(apf.getType(), f.getType()) + ) + } + + /** + * Holds if data can flow from a source to `node` with the given `apf` and + * from there flow to a sink. + */ + private predicate flowCand(Node node, boolean toReturn, AccessPathFront apf, Configuration config) { + flowCand0(node, toReturn, apf, config) and + flowCandFwd(node, _, apf, config) + } + private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Configuration config) { + flowCandFwd(node, _, apf, config) and + config.isSink(node) and toReturn = false and apf instanceof AccessPathFrontNil + or + ( + exists(Node mid | + localFlowBigStep(node, mid, true, config) and + flowCand(mid, toReturn, apf, config) + ) + or + exists(Node mid, AccessPathFront apf0 | + flowCandFwd(node, _, apf, config) and + localFlowBigStep(node, mid, false, config) and + flowCand(mid, toReturn, apf0, config) and + apf0 instanceof AccessPathFrontNil and + apf instanceof AccessPathFrontNil + ) + or + exists(Node mid | + jumpStep(node, mid, true, config) and + flowCand(mid, _, apf, config) and + toReturn = false + ) + or + exists(Node mid, AccessPathFront apf0 | + flowCandFwd(node, _, apf, config) and + jumpStep(node, mid, false, config) and + flowCand(mid, _, apf0, config) and + toReturn = false and + apf0 instanceof AccessPathFrontNil and + apf instanceof AccessPathFrontNil + ) + or + exists(Node mid, boolean allowsFieldFlow | + flowIntoCallable(node, mid, allowsFieldFlow, config) and + flowCand(mid, false, apf, config) and + toReturn = false and + (apf instanceof AccessPathFrontNil or allowsFieldFlow = true) + ) + or + exists(Node mid, boolean allowsFieldFlow | + flowOutOfCallable(node, mid, allowsFieldFlow, config) and + flowCand(mid, _, apf, config) and + toReturn = true and + (apf instanceof AccessPathFrontNil or allowsFieldFlow = true) + ) + or + exists(Node mid, boolean preservesValue, AccessPathFront apf0 | + flowThroughMethod(node, mid, preservesValue, config) and + flowCand(mid, toReturn, apf0, config) and + (preservesValue = true and apf = apf0 or preservesValue = false and apf0 instanceof AccessPathFrontNil and apf instanceof AccessPathFrontNil and flowCandFwd(node, _, apf, config)) + ) + or + exists(Node mid, Content f, AccessPathFront apf0 | + store(node, f, mid) and + flowCand(mid, toReturn, apf0, config) and + apf0.headUsesContent(f) and + consCand(f, apf, unbind(config)) + ) + or + exists(Node mid, Content f, AccessPathFront apf0 | + read(node, f, mid) and + flowCand(mid, toReturn, apf0, config) and + consCandFwd(f, apf0, unbind(config)) and + apf.headUsesContent(f) + ) + ) + } + private predicate consCand(Content f, AccessPathFront apf, Configuration config) { + consCandFwd(f, apf, config) and + exists(Node mid, Node n, AccessPathFront apf0 | + flowCandFwd(n, _, apf0, config) and + apf0.headUsesContent(f) and + read(n, f, mid) and + flowCand(mid, _, apf, config) + ) + } + + private newtype TAccessPath = TNil(Type t) or TCons(FlowContainer f, int len) { len in [1..5] } + + /** + * Conceptually a list of `Content`s followed by a `Type`, but only the first + * element of the list and its length are tracked. If data flows from a source to + * a given node with a given `AccessPath`, this indicates the sequence of + * dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ + private class AccessPath extends TAccessPath { + abstract string toString(); + FlowContainer getHead() { this = TCons(result, _) } + int len() { + this = TNil(_) and result = 0 + or + this = TCons(_, result) + } + Type getType() { + this = TNil(result) or + exists(FlowContainer head | this = TCons(head, _) | result = head.getContainerType()) + } + abstract AccessPathFront getFront(); + } + + private class AccessPathNil extends AccessPath, TNil { + override string toString() { exists(Type t | this = TNil(t) | result = t.toString()) } + override AccessPathFront getFront() { exists(Type t | this = TNil(t) | result = TFrontNil(t)) } + } + + private class AccessPathCons extends AccessPath, TCons { + override string toString() { + exists(FlowContainer f, int len | this = TCons(f, len) | result = f.toString() + ", ... (" + len.toString() + ")") + } + override AccessPathFront getFront() { exists(FlowContainer f | this = TCons(f, _) | result = TFrontHead(f)) } + } + + /** Holds if `ap0` corresponds to the cons of `f` and `ap`. */ + private predicate pop(AccessPath ap0, Content f, AccessPath ap) { + ap0.getFront().headUsesContent(f) and + consCand(f, ap.getFront(), _) and + ap0.len() = 1 + ap.len() + } + + /** Holds if `ap0` corresponds to the cons of `f` and `ap` and `apf` is the front of `ap`. */ + pragma[noinline] + private predicate popWithFront(AccessPath ap0, Content f, AccessPathFront apf, AccessPath ap) { + pop(ap0, f, ap) and apf = ap.getFront() + } + + /** Holds if `ap` corresponds to the cons of `f` and `ap0`. */ + private predicate push(AccessPath ap0, Content f, AccessPath ap) { pop(ap, f, ap0) } + + /** + * Holds if data can flow from a source to `node` with the given `ap`. + */ + private predicate flowFwd(Node node, boolean fromArg, AccessPathFront apf, AccessPath ap, Configuration config) { + flowFwd0(node, fromArg, apf, ap, config) and + flowCand(node, _, apf, config) + } + private predicate flowFwd0(Node node, boolean fromArg, AccessPathFront apf, AccessPath ap, Configuration config) { + flowCand(node, _, _, config) and + config.isSource(node) and fromArg = false and ap = TNil(getErasedRepr(node.getType())) and apf = ap.(AccessPathNil).getFront() + or + flowCand(node, _, _, unbind(config)) and + ( + exists(Node mid | + flowFwd(mid, fromArg, apf, ap, config) and + localFlowBigStep(mid, node, true, config) + ) + or + exists(Node mid, AccessPath ap0 | + flowFwd(mid, fromArg, _, ap0, config) and + localFlowBigStep(mid, node, false, config) and + ap0 instanceof AccessPathNil and + ap = TNil(getErasedRepr(node.getType())) and + apf = ap.(AccessPathNil).getFront() + ) + or + exists(Node mid | + flowFwd(mid, _, apf, ap, config) and + jumpStep(mid, node, true, config) and + fromArg = false + ) + or + exists(Node mid, AccessPath ap0 | + flowFwd(mid, _, _, ap0, config) and + jumpStep(mid, node, false, config) and + fromArg = false and + ap0 instanceof AccessPathNil and + ap = TNil(getErasedRepr(node.getType())) and + apf = ap.(AccessPathNil).getFront() + ) + or + exists(Node mid, boolean allowsFieldFlow | + flowFwd(mid, _, apf, ap, config) and + flowIntoCallable(mid, node, allowsFieldFlow, config) and + fromArg = true and + (ap instanceof AccessPathNil or allowsFieldFlow = true) + ) + or + exists(Node mid, boolean allowsFieldFlow | + flowFwd(mid, false, apf, ap, config) and + flowOutOfCallable(mid, node, allowsFieldFlow, config) and + fromArg = false and + (ap instanceof AccessPathNil or allowsFieldFlow = true) + ) + or + exists(Node mid, boolean preservesValue, AccessPathFront apf0, AccessPath ap0 | + flowFwd(mid, fromArg, apf0, ap0, config) and + flowThroughMethod(mid, node, preservesValue, config) and + (preservesValue = true and ap = ap0 and apf = apf0 or preservesValue = false and ap0 instanceof AccessPathNil and ap = TNil(getErasedRepr(node.getType())) and apf = ap.(AccessPathNil).getFront()) + ) + ) + or + exists(Content f, AccessPath ap0 | + flowFwdStore(node, f, ap0, apf, fromArg, config) and + push(ap0, f, ap) + ) + or + exists(Content f, AccessPath ap0 | + flowFwdRead(node, f, ap0, fromArg, config) and + popWithFront(ap0, f, apf, ap) + ) + } + pragma[nomagic] + private predicate flowFwdStore(Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg, Configuration config) { + exists(Node mid, AccessPathFront apf0 | + flowFwd(mid, fromArg, apf0, ap0, config) and + flowFwdStoreAux(mid, f, node, apf0, apf, config) + ) + } + private predicate flowFwdStoreAux(Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFront apf, Configuration config) { + store(mid, f, node) and + consCand(f, apf0, config) and + apf.headUsesContent(f) and + flowCand(node, _, apf, unbind(config)) + } + pragma[nomagic] + private predicate flowFwdRead(Node node, Content f, AccessPath ap0, boolean fromArg, Configuration config) { + exists(Node mid, AccessPathFront apf0 | + flowFwd(mid, fromArg, apf0, ap0, config) and + read(mid, f, node) and + apf0.headUsesContent(f) and + flowCand(node, _, _, unbind(config)) + ) + } + + /** + * Holds if data can flow from a source to `node` with the given `ap` and + * from there flow to a sink. + */ + private predicate flow(Node node, boolean toReturn, AccessPath ap, Configuration config) { + flow0(node, toReturn, ap, config) and + flowFwd(node, _, _, ap, config) + } + private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuration config) { + flowFwd(node, _, _, ap, config) and + config.isSink(node) and toReturn = false and ap instanceof AccessPathNil + or + ( + exists(Node mid | + localFlowBigStep(node, mid, true, config) and + flow(mid, toReturn, ap, config) + ) + or + exists(Node mid, AccessPath ap0 | + flowFwd(node, _, _, ap, config) and + localFlowBigStep(node, mid, false, config) and + flow(mid, toReturn, ap0, config) and + ap0 instanceof AccessPathNil and + ap instanceof AccessPathNil + ) + or + exists(Node mid | + jumpStep(node, mid, true, config) and + flow(mid, _, ap, config) and + toReturn = false + ) + or + exists(Node mid, AccessPath ap0 | + flowFwd(node, _, _, ap, config) and + jumpStep(node, mid, false, config) and + flow(mid, _, ap0, config) and + toReturn = false and + ap0 instanceof AccessPathNil and + ap instanceof AccessPathNil + ) + or + exists(Node mid, boolean allowsFieldFlow | + flowIntoCallable(node, mid, allowsFieldFlow, config) and + flow(mid, false, ap, config) and + toReturn = false and + (ap instanceof AccessPathNil or allowsFieldFlow = true) + ) + or + exists(Node mid, boolean allowsFieldFlow | + flowOutOfCallable(node, mid, allowsFieldFlow, config) and + flow(mid, _, ap, config) and + toReturn = true and + (ap instanceof AccessPathNil or allowsFieldFlow = true) + ) + or + exists(Node mid, boolean preservesValue, AccessPath ap0 | + flowThroughMethod(node, mid, preservesValue, config) and + flow(mid, toReturn, ap0, config) and + (preservesValue = true and ap = ap0 or preservesValue = false and ap0 instanceof AccessPathNil and ap instanceof AccessPathNil and flowFwd(node, _, _, ap, config)) + ) + or + exists(Content f, AccessPath ap0 | + flowStore(node, f, toReturn, ap0, config) and + pop(ap0, f, ap) + ) + or + exists(Content f, AccessPath ap0 | + flowRead(node, f, toReturn, ap0, config) and + push(ap0, f, ap) + ) + ) + } + pragma[nomagic] + private predicate flowStore(Node node, Content f, boolean toReturn, AccessPath ap0, Configuration config) { + exists(Node mid | + store(node, f, mid) and + flow(mid, toReturn, ap0, config) + ) + } + pragma[nomagic] + private predicate flowRead(Node node, Content f, boolean toReturn, AccessPath ap0, Configuration config) { + exists(Node mid | + read(node, f, mid) and + flow(mid, toReturn, ap0, config) + ) + } + + private bindingset[conf, result] Configuration unbind(Configuration conf) { result >= conf and result <= conf } + + private predicate flow(Node n, Configuration config) { + flow(n, _, _, config) + } + + private newtype TPathNode = + TPathNodeMid(Node node, CallContext cc, AccessPath ap, Configuration config) { + // A PathNode is introduced by a source ... + flow(node, config) and + config.isSource(node) and + cc instanceof CallContextAny and + ap = TNil(getErasedRepr(node.getType())) + or + // ... or a step from an existing PathNode to another node. + exists(PathNodeMid mid | + flowStep(mid, node, cc, ap) and + config = mid.getConfiguration() and + flow(node, _, ap, unbind(config)) + ) + } or + TPathNodeSink(Node node, Configuration config) { // The AccessPath on a sink is empty. + config.isSink(node) and + flow(node, config) + } + + /** + * 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. + */ + abstract class PathNode extends TPathNode { + /** Gets a textual representation of this element. */ + string toString() { result = getNode().toString() + ppAp() } + /** Gets the source location for this element. */ + Location getLocation() { result = getNode().getLocation() } + /** Gets the underlying `Node`. */ + abstract Node getNode(); + /** Gets the associated configuration. */ + abstract Configuration getConfiguration(); + /** Gets a successor. */ + abstract PathNode getSucc(); + private string ppAp() { + this instanceof PathNodeSink and result = "" or + result = " [" + this.(PathNodeMid).getAp().toString() + "]" + } + } + + /** Holds if `n` can reach a sink. */ + private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getSucc()) } + + /** Holds if `n1.getSucc() = n2` and `n2` can reach a sink. */ + private predicate pathSucc(PathNode n1, PathNode n2) { n1.getSucc() = n2 and reach(n2) } + + private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2) + + /** + * Provides the query predicates needed to include a graph in a path-problem query. + */ + module PathGraph { + /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ + query predicate edges(PathNode a, PathNode b) { pathSucc(a, b) } + } + + /** + * An intermediate flow graph node. This is a triple consisting of a `Node`, + * a `CallContext`, and a `Configuration`. + */ + private class PathNodeMid extends PathNode, TPathNodeMid { + Node node; + CallContext cc; + AccessPath ap; + Configuration config; + PathNodeMid() { this = TPathNodeMid(node, cc, ap, config) } + override Node getNode() { result = node } + CallContext getCallContext() { result = cc } + AccessPath getAp() { result = ap } + override Configuration getConfiguration() { result = config } + private PathNodeMid getSuccMid() { + flowStep(this, result.getNode(), result.getCallContext(), result.getAp()) and + result.getConfiguration() = unbind(this.getConfiguration()) + } + override PathNode getSucc() { + // an intermediate step to another intermediate node + result = getSuccMid() + or + // a final step to a sink via one or more local steps + localFlowStepPlus(node, result.getNode(), _, config) and + ap instanceof AccessPathNil and + result instanceof PathNodeSink and + result.getConfiguration() = unbind(this.getConfiguration()) + or + // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges + exists(PathNodeMid mid | + mid = getSuccMid() and + mid.getNode() = result.getNode() and + mid.getAp() instanceof AccessPathNil and + result instanceof PathNodeSink and + result.getConfiguration() = unbind(mid.getConfiguration()) + ) + or + // a direct step from a source to a sink if a node is both + this instanceof PathNodeSource and + result instanceof PathNodeSink and + this.getNode() = result.getNode() and + result.getConfiguration() = unbind(this.getConfiguration()) + } + } + + /** + * A flow graph node corresponding to a source. + */ + private class PathNodeSource extends PathNodeMid { + PathNodeSource() { + getConfiguration().isSource(getNode()) and + getCallContext() instanceof CallContextAny and + getAp() instanceof AccessPathNil + } + } + + /** + * A flow graph node corresponding to a sink. This is disjoint from the + * intermediate nodes in order to uniquely correspond to a given sink by + * excluding the `CallContext`. + */ + private class PathNodeSink extends PathNode, TPathNodeSink { + Node node; + Configuration config; + PathNodeSink() { this = TPathNodeSink(node, config) } + override Node getNode() { result = node } + override Configuration getConfiguration() { result = config } + override PathNode getSucc() { none() } + } + + /** + * Holds if data may flow from `mid` to `node`. The last step in or out of + * a callable is recorded by `cc`. + */ + private predicate flowStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) { + localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and + cc = mid.getCallContext() and + ap = mid.getAp() + or + localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration()) and + cc = mid.getCallContext() and + mid.getAp() instanceof AccessPathNil and + ap = TNil(getErasedRepr(node.getType())) + or + jumpStep(mid.getNode(), node, true, mid.getConfiguration()) and + cc instanceof CallContextAny and + ap = mid.getAp() + or + jumpStep(mid.getNode(), node, false, mid.getConfiguration()) and + cc instanceof CallContextAny and + mid.getAp() instanceof AccessPathNil and + ap = TNil(getErasedRepr(node.getType())) + or + contentReadStep(mid, node, ap) and cc = mid.getCallContext() + or + exists(Content f, AccessPath ap0 | contentStoreStep(mid, node, ap0, f, cc) and push(ap0, f, ap)) + or + flowOutOfArgument(mid, node, cc) and ap = mid.getAp() + or + flowIntoCallable(mid, node, _, cc, _) and ap = mid.getAp() + or + flowOutOfMethod(mid, node.asExpr(), cc) and ap = mid.getAp() + or + flowThroughMethod(mid, node.asExpr(), cc) and ap = TNil(getErasedRepr(node.getType())) + or + valueFlowThroughMethod(mid, node.asExpr(), cc) and ap = mid.getAp() + } + + private predicate contentReadStep(PathNodeMid mid, Node node, AccessPath ap) { + exists(Content f, AccessPath ap0 | + ap0 = mid.getAp() and + read(mid.getNode(), f, node) and + pop(ap0, f, ap) + ) + } + + pragma[noinline] + private predicate contentStoreStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) { + ap0 = mid.getAp() and + store(mid.getNode(), f, node) and + cc = mid.getCallContext() + } + + /** + * Holds if data may flow from `mid` to an exit of `m` in the context + * `innercc`, and the path did not flow through a parameter of `m`. + */ + private predicate flowOutOfMethod0(PathNodeMid mid, Method m, CallContext innercc) { + exists(ReturnNode ret | + ret = mid.getNode() and + innercc = mid.getCallContext() and + m = returnNodeGetEnclosingCallable(ret) and + not innercc instanceof CallContextCall + ) + } + + /** + * Holds if data may flow from `mid` to `ma`. The last step of this path + * is a return from a method and is recorded by `cc`, if needed. + */ + pragma[noinline] + private predicate flowOutOfMethod(PathNodeMid mid, MethodAccess ma, CallContext cc) { + exists(Method m, CallContext innercc | + flowOutOfMethod0(mid, m, innercc) and + resolveReturn(innercc, m, ma) + | + if reducedViableImplInReturn(m, ma) then cc = TReturn(m, ma) else cc = TAnyCallContext() + ) + } + + private predicate flowOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallContext cc) { + exists(PostUpdateNode n, ParameterNode p, Callable callable, CallContext innercc, int i, Call call, ArgumentNode arg | + mid.getNode() = n and + parameterValueFlowsToUpdate(p, n) and + innercc = mid.getCallContext() and + p.isParameterOf(callable, i) and + resolveReturn(innercc, callable, call) and + node.getPreUpdateNode() = arg and + arg.argumentOf(call, i) and + flow(node, unbind(mid.getConfiguration())) + | + if reducedViableImplInReturn(callable, call) then cc = TReturn(callable, call) else cc = TAnyCallContext() + ) + } + + /** + * Holds if data may flow from `mid` to the `i`th argument of `call` in `cc`. + */ + pragma[noinline] + private predicate flowIntoArg(PathNodeMid mid, int i, CallContext cc, Call call, boolean emptyAp) { + exists(ArgumentNode arg, AccessPath ap | + arg = mid.getNode() and + cc = mid.getCallContext() and + arg.argumentOf(call, i) and + ap = mid.getAp() + | + ap instanceof AccessPathNil and emptyAp = true or + ap instanceof AccessPathCons and emptyAp = false + ) + } + + pragma[noinline] + private predicate parameterCand(Callable callable, int i, Configuration config) { + exists(ParameterNode p | + flow(p, config) and + p.isParameterOf(callable, i) + ) + } + + pragma[nomagic] + private predicate flowIntoCallable0(PathNodeMid mid, Callable callable, int i, CallContext outercc, Call call, boolean emptyAp) { + flowIntoArg(mid, i, outercc, call, emptyAp) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), mid.getConfiguration()) + } + + /** + * Holds if data may flow from `mid` to `p` through `call`. The contexts + * before and after entering the callable are `outercc` and `innercc`, + * respectively. + */ + private predicate flowIntoCallable(PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, Call call) { + exists(int i, Callable callable, boolean emptyAp | + flowIntoCallable0(mid, callable, i, outercc, call, emptyAp) and + p.isParameterOf(callable, i) + | + if reducedViableImplInCallContext(_, callable, call) then innercc = TSpecificCall(call, i, emptyAp) else innercc = TSomeCall(p, emptyAp) + ) + } + + /** Holds if data may flow from `p` to a return statement in the callable. */ + pragma[nomagic] + private predicate paramFlowsThrough(ParameterNode p, CallContextCall cc, Configuration config) { + exists(PathNodeMid mid, ReturnNode ret | + mid.getNode() = ret and + cc = mid.getCallContext() and + config = mid.getConfiguration() and + mid.getAp() instanceof AccessPathNil + | + cc = TSomeCall(p, true) or + exists(int i | cc = TSpecificCall(_, i, true) | + p.isParameterOf(returnNodeGetEnclosingCallable(ret), i) + ) + ) + } + + /** + * Holds if data may flow from `mid` to an argument of `methodcall`, + * through a called method `m`, and back out through a return statement in + * `m`. The context `cc` is restored to its value prior to entering `m`. + */ + pragma[noinline] + private predicate flowThroughMethod(PathNodeMid mid, Call methodcall, CallContext cc) { + exists(ParameterNode p, CallContext innercc | + flowIntoCallable(mid, p, cc, innercc, methodcall) and + paramFlowsThrough(p, innercc, unbind(mid.getConfiguration())) and + not parameterValueFlowsThrough(p) and + mid.getAp() instanceof AccessPathNil + ) + } + + private predicate valueFlowThroughMethod(PathNodeMid mid, Call methodcall, CallContext cc) { + exists(ParameterNode p | + flowIntoCallable(mid, p, cc, _, methodcall) and + parameterValueFlowsThrough(p) + ) + } + + /** + * Holds if data can flow (inter-procedurally) from `source` to `sink`. + * + * Will only have results if `configuration` has non-empty sources and + * sinks. + */ + private predicate flowsTo(PathNodeSource flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration) { + flowsource.getConfiguration() = configuration and + flowsource.getNode() = source and + pathSuccPlus(flowsource, flowsink) and + flowsink.getNode() = sink + } + + /** + * Holds if data can flow (inter-procedurally) from `source` to `sink`. + * + * Will only have results if `configuration` has non-empty sources and + * sinks. + */ + predicate flowsTo(Node source, Node sink, Configuration configuration) { + flowsTo(_, _, source, sink, configuration) + } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll new file mode 100644 index 000000000000..142ea7f8a7e9 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll @@ -0,0 +1,1536 @@ +/** + * Provides an implementation of global (interprocedural) data flow. This file + * re-exports the local (intraprocedural) data flow analysis from `DataFlowUtil` + * and adds a global analysis, mainly exposed through the `Configuration` class. + * This file exists in several identical copies, allowing queries to use + * multiple `Configuration` classes that depend on each other without + * introducing mutual recursion among those configurations. + */ + + import DataFlowUtil + private import DataFlowPrivate + private import DataFlowDispatch + private import DataFlowImplCommon + + + /** + * A configuration of interprocedural data flow analysis. This defines + * sources, sinks, and any other configurable aspect of the analysis. Each + * use of the global data flow library must define its own unique extension + * of this abstract class. To create a configuration, extend this class with + * a subclass whose characteristic predicate is a unique singleton string. + * For example, write + * + * ``` + * class MyAnalysisConfiguration extends DataFlow::Configuration { + * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } + * // Override `isSource` and `isSink`. + * // Optionally override `isBarrier`. + * // Optionally override `isAdditionalFlowStep`. + * } + * ``` + * + * Then, to query whether there is flow between some `source` and `sink`, + * write + * + * ``` + * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) + * ``` + * + * Multiple configurations can coexist, but two classes extending + * `DataFlow::Configuration` should never depend on each other. One of them + * should instead depend on a `DataFlow2::Configuration`, a + * `DataFlow3::Configuration`, or a `DataFlow4::Configuration`. + */ + abstract class Configuration extends string { + bindingset[this] + Configuration() { any() } + + /** + * Holds if `source` is a relevant data flow source. + */ + abstract predicate isSource(Node source); + + /** + * Holds if `sink` is a relevant data flow sink. + */ + abstract predicate isSink(Node sink); + + /** Holds if data flow through `node` is prohibited. */ + predicate isBarrier(Node node) { none() } + + /** Holds if data flow from `node1` to `node2` is prohibited. */ + predicate isBarrierEdge(Node node1, Node node2) { none() } + + /** + * Holds if the additional flow step from `node1` to `node2` must be taken + * into account in the analysis. + */ + predicate isAdditionalFlowStep(Node node1, Node node2) { none() } + + /** + * Gets the virtual dispatch branching limit when calculating field flow. + * This can be overridden to a smaller value to improve performance (a + * value of 0 disables field flow), or a larger value to get more results. + */ + int fieldFlowBranchLimit() { result = 2 } + + /** + * Holds if data may flow from `source` to `sink` for this configuration. + */ + predicate hasFlow(Node source, Node sink) { + flowsTo(source, sink, this) + } + + /** + * Holds if data may flow from `source` to `sink` for this configuration. + * + * The corresponding paths are generated from the end-points and the graph + * included in the module `PathGraph`. + */ + predicate hasFlowPath(PathNode source, PathNode sink) { + flowsTo(source, sink, _, _, this) + } + + /** + * Holds if data may flow from some source to `sink` for this configuration. + */ + predicate hasFlowTo(Node sink) { + hasFlow(_, sink) + } + + /** + * Holds if data may flow from some source to `sink` for this configuration. + */ + predicate hasFlowToExpr(Expr sink) { + hasFlowTo(exprNode(sink)) + } + + /** DEPRECATED: use `hasFlow` instead. */ + deprecated predicate hasFlowForward(Node source, Node sink) { + hasFlow(source, sink) + } + + /** DEPRECATED: use `hasFlow` instead. */ + deprecated predicate hasFlowBackward(Node source, Node sink) { + hasFlow(source, sink) + } + } + + /** + * Holds if the additional step from `node1` to `node2` jumps between callables. + */ + private predicate additionalJumpStep(Node node1, Node node2, Configuration config) { + config.isAdditionalFlowStep(node1, node2) and + node1.getEnclosingCallable() != node2.getEnclosingCallable() + } + + pragma[noinline] + private predicate isAdditionalFlowStep(Node node1, Node node2, Callable callable1, Callable callable2, Configuration config) { + config.isAdditionalFlowStep(node1, node2) and + callable1 = node1.getEnclosingCallable() and + callable2 = node2.getEnclosingCallable() + } + + /** + * Holds if the additional step from `node1` to `node2` does not jump between callables. + */ + private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) { + exists(Callable callable | + isAdditionalFlowStep(node1, node2, callable, callable, config) + ) + } + + /** + * Holds if data can flow from `node1` to `node2` through a static field or + * variable capture. + */ + private predicate jumpStep(Node node1, Node node2, boolean preservesValue, Configuration config) { + jumpStep(node1, node2) and preservesValue = true or + additionalJumpStep(node1, node2, config) and preservesValue = false + } + + /** + * Holds if data can flow in one local step from `node1` to `node2` taking + * additional steps from the configuration into account. + */ + private predicate localFlowStep(Node node1, Node node2, boolean preservesValue, Configuration config) { + localFlowStep(node1, node2) and not config.isBarrierEdge(node1, node2) and preservesValue = true or + additionalLocalFlowStep(node1, node2, config) and preservesValue = false + } + + pragma[noinline] + private Method returnNodeGetEnclosingCallable(ReturnNode ret) { + result = ret.getEnclosingCallable() + } + + /** + * Holds if field flow should be used for the given configuration. + */ + private predicate useFieldFlow(Configuration config) { + config.fieldFlowBranchLimit() >= 1 + } + + /** + * Holds if `node` is reachable from a source in the given configuration + * ignoring call contexts. + */ + private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) { + not config.isBarrier(node) and + ( + config.isSource(node) and stored = false + or + exists(Node mid, boolean preservesValue | + nodeCandFwd1(mid, stored, config) and + localFlowStep(mid, node, preservesValue, config) and + (stored = false or preservesValue = true) + ) + or + exists(Node mid, boolean preservesValue | + nodeCandFwd1(mid, stored, config) and + jumpStep(mid, node, preservesValue, config) and + (stored = false or preservesValue = true) + ) + or + // store + exists(Node mid | + useFieldFlow(config) and + nodeCandFwd1(mid, _, config) and + store(mid, _, node) and + stored = true + ) + or + // read + exists(Node mid, Content f | + nodeCandFwd1(mid, true, config) and + read(mid, f, node) and + storeCandFwd1(f, unbind(config)) and + (stored = false or stored = true) + ) + or + // flow into a callable + exists(Node arg | + nodeCandFwd1(arg, stored, config) and + viableParamArg(node, arg) + ) + or + // flow out of an argument + exists(PostUpdateNode mid, ParameterNode p | + nodeCandFwd1(mid, stored, config) and + parameterValueFlowsToUpdate(p, mid) and + viableParamArg(p, node.(PostUpdateNode).getPreUpdateNode()) + ) + or + // flow out of a callable + exists(Method m, MethodAccess ma, ReturnNode ret | + nodeCandFwd1(ret, stored, config) and + m = returnNodeGetEnclosingCallable(ret) and + m = viableImpl(ma) and + node.asExpr() = ma + ) + ) + } + + /** + * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`. + */ + private predicate storeCandFwd1(Content f, Configuration config) { + exists(Node mid, Node node | + not config.isBarrier(node) and + useFieldFlow(unbind(config)) and + nodeCandFwd1(mid, _, config) and + store(mid, f, node) + ) + } + + bindingset[result, b] + private boolean unbindBool(boolean b) { result != b.booleanNot() } + + /** + * Holds if `node` is part of a path from a source to a sink in the given + * configuration ignoring call contexts. + */ + pragma[nomagic] + private predicate nodeCand1(Node node, boolean stored, Configuration config) { + nodeCandFwd1(node, false, config) and + config.isSink(node) and stored = false + or + nodeCandFwd1(node, unbindBool(stored), unbind(config)) and + ( + exists(Node mid, boolean preservesValue | + localFlowStep(node, mid, preservesValue, config) and + nodeCand1(mid, stored, config) and + (stored = false or preservesValue = true) + ) + or + exists(Node mid, boolean preservesValue | + jumpStep(node, mid, preservesValue, config) and + nodeCand1(mid, stored, config) and + (stored = false or preservesValue = true) + ) + or + // store + exists(Node mid, Content f | + store(node, f, mid) and + readCand1(f, unbind(config)) and + nodeCand1(mid, true, config) and + (stored = false or stored = true) + ) + or + // read + exists(Node mid, Content f | + read(node, f, mid) and + storeCandFwd1(f, unbind(config)) and + nodeCand1(mid, _, config) and + stored = true + ) + or + // flow into a callable + exists(Node param | + viableParamArg(param, node) and + nodeCand1(param, stored, config) + ) + or + // flow out of an argument + exists(PostUpdateNode mid, ParameterNode p | + parameterValueFlowsToUpdate(p, node) and + viableParamArg(p, mid.getPreUpdateNode()) and + nodeCand1(mid, stored, config) + ) + or + // flow out of a callable + exists(Method m, ExprNode ma | + nodeCand1(ma, stored, config) and + m = returnNodeGetEnclosingCallable(node) and + m = viableImpl(ma.getExpr()) + ) + ) + } + + /** + * Holds if `f` is the target of a read in the flow covered by `nodeCand1`. + */ + private predicate readCand1(Content f, Configuration config) { + exists(Node mid, Node node | + useFieldFlow(unbind(config)) and + nodeCandFwd1(node, true, unbind(config)) and + read(node, f, mid) and + storeCandFwd1(f, unbind(config)) and + nodeCand1(mid, _, config) + ) + } + + /** + * Holds if there is a path from `p` to `node` in the same callable that is + * part of a path from a source to a sink taking simple call contexts into + * consideration. This is restricted to paths that does not necessarily + * preserve the value of `p` by making use of at least one additional step + * from the configuration. + */ + pragma[nomagic] + private predicate simpleParameterFlow(ParameterNode p, Node node, RefType t, Configuration config) { + nodeCand1(node, false, config) and + p = node and + t = getErasedRepr(node.getType()) and + not parameterValueFlowsThrough(p) + or + nodeCand1(node, false, unbind(config)) and + exists(Node mid | + simpleParameterFlow(p, mid, t, config) and + localFlowStep(mid, node, true, config) and + compatibleTypes(t, node.getType()) + ) + or + nodeCand1(node, false, unbind(config)) and + exists(Node mid | + simpleParameterFlow(p, mid, _, config) and + localFlowStep(mid, node, false, config) and + t = getErasedRepr(node.getType()) + ) + or + nodeCand1(node, false, unbind(config)) and + exists(Node mid | + simpleParameterFlow(p, mid, t, config) and + localStoreReadStep(mid, node) and + compatibleTypes(t, node.getType()) + ) + or + // value flow through a callable + nodeCand1(node, false, config) and + exists(Node arg | + simpleParameterFlow(p, arg, t, config) and + argumentValueFlowsThrough(arg, node) and + compatibleTypes(t, node.getType()) + ) + or + // flow through a callable + nodeCand1(node, false, config) and + exists(Node arg | + simpleParameterFlow(p, arg, _, config) and + simpleArgumentFlowsThrough(arg, node, t, config) + ) + } + + /** + * Holds if data can flow from `arg` through the `call` taking simple call + * contexts into consideration and that this is part of a path from a source + * to a sink. This is restricted to paths through the `call` that does not + * necessarily preserve the value of `arg` by making use of at least one + * additional step from the configuration. + */ + private predicate simpleArgumentFlowsThrough(ArgumentNode arg, ExprNode call, RefType t, Configuration config) { + exists(ParameterNode param, ReturnNode ret | + nodeCand1(arg, false, unbind(config)) and + nodeCand1(call, false, unbind(config)) and + viableParamArg(param, arg) and + simpleParameterFlow(param, ret, t, config) and + arg.argumentOf(call.getExpr(), _) + ) + } + + /** + * Holds if data can flow from `node1` to `node2` by a step through a method. + */ + private predicate flowThroughMethod(Node node1, Node node2, boolean preservesValue, Configuration config) { + simpleArgumentFlowsThrough(node1, node2, _, config) and preservesValue = false or + argumentValueFlowsThrough(node1, node2) and preservesValue = true + } + + /** + * Holds if data can flow from `node1` to `node2` in one local step or a step + * through a method. + */ + private predicate localFlowStepOrFlowThroughMethod(Node node1, Node node2, boolean preservesValue, Configuration config) { + localFlowStep(node1, node2, preservesValue, config) or + flowThroughMethod(node1, node2, preservesValue, config) + } + + /** + * Holds if data can flow out of a callable from `node1` to `node2`, either + * through a `ReturnNode` or through an argument that has been mutated, and + * that this step is part of a path from a source to a sink. + */ + private predicate flowOutOfCallable(Node node1, Node node2, Configuration config) { + nodeCand1(node1, _, unbind(config)) and + nodeCand1(node2, _, config) and + ( + // flow out of an argument + exists(ParameterNode p | + parameterValueFlowsToUpdate(p, node1) and + viableParamArg(p, node2.(PostUpdateNode).getPreUpdateNode()) + ) + or + // flow out of a method + exists(Method m, MethodAccess ma, ReturnNode ret | + ret = node1 and + m = returnNodeGetEnclosingCallable(ret) and + m = viableImpl(ma) and + node2.asExpr() = ma + ) + ) + } + + /** + * Holds if data can flow into a callable and that this step is part of a + * path from a source to a sink. + */ + private predicate flowIntoCallable(Node node1, Node node2, Configuration config) { + viableParamArg(node2, node1) and + nodeCand1(node1, _, unbind(config)) and + nodeCand1(node2, _, config) + } + + /** + * Gets the amount of forward branching on the origin of a cross-call path + * edge in the graph of paths between sources and sinks that ignores call + * contexts. + */ + private int branch(Node n1, Configuration conf) { + result = strictcount(Node n | flowOutOfCallable(n1, n, conf) or flowIntoCallable(n1, n, conf)) + } + + /** + * Gets the amount of backward branching on the target of a cross-call path + * edge in the graph of paths between sources and sinks that ignores call + * contexts. + */ + private int join(Node n2, Configuration conf) { + result = strictcount(Node n | flowOutOfCallable(n, n2, conf) or flowIntoCallable(n, n2, conf)) + } + + /** + * Holds if data can flow out of a callable from `node1` to `node2`, either + * through a `ReturnNode` or through an argument that has been mutated, and + * that this step is part of a path from a source to a sink. The + * `allowsFieldFlow` flag indicates whether the branching is within the limit + * specified by the configuration. + */ + private predicate flowOutOfCallable(Node node1, Node node2, boolean allowsFieldFlow, Configuration config) { + flowOutOfCallable(node1, node2, config) and + exists(int b, int j | + b = branch(node1, config) and + j = join(node2, config) and + if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false + ) + } + + /** + * Holds if data can flow into a callable and that this step is part of a + * path from a source to a sink. The `allowsFieldFlow` flag indicates whether + * the branching is within the limit specified by the configuration. + */ + private predicate flowIntoCallable(Node node1, Node node2, boolean allowsFieldFlow, Configuration config) { + flowIntoCallable(node1, node2, config) and + exists(int b, int j | + b = branch(node1, config) and + j = join(node2, config) and + if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false + ) + } + + /** + * Holds if `node` is part of a path from a source to a sink in the given + * configuration taking simple call contexts into consideration. + */ + private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Configuration config) { + nodeCand1(node, false, config) and + config.isSource(node) and fromArg = false and stored = false + or + nodeCand1(node, unbindBool(stored), unbind(config)) and + ( + exists(Node mid, boolean preservesValue | + nodeCandFwd2(mid, fromArg, stored, config) and + localFlowStepOrFlowThroughMethod(mid, node, preservesValue, config) and + (stored = false or preservesValue = true) + ) + or + exists(Node mid, boolean preservesValue | + nodeCandFwd2(mid, _, stored, config) and + jumpStep(mid, node, preservesValue, config) and + fromArg = false and + (stored = false or preservesValue = true) + ) + or + // store + exists(Node mid, Content f | + nodeCandFwd2(mid, fromArg, _, config) and + store(mid, f, node) and + readCand1(f, unbind(config)) and + stored = true + ) + or + // read + exists(Node mid, Content f | + nodeCandFwd2(mid, fromArg, true, config) and + read(mid, f, node) and + storeCandFwd2(f, unbind(config)) and + (stored = false or stored = true) + ) + or + exists(Node mid, boolean allowsFieldFlow | + nodeCandFwd2(mid, _, stored, config) and + flowIntoCallable(mid, node, allowsFieldFlow, config) and + fromArg = true and + (stored = false or allowsFieldFlow = true) + ) + or + exists(Node mid, boolean allowsFieldFlow | + nodeCandFwd2(mid, false, stored, config) and + flowOutOfCallable(mid, node, allowsFieldFlow, config) and + fromArg = false and + (stored = false or allowsFieldFlow = true) + ) + ) + } + + /** + * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`. + */ + private predicate storeCandFwd2(Content f, Configuration config) { + exists(Node mid, Node node | + useFieldFlow(unbind(config)) and + nodeCand1(node, true, unbind(config)) and + nodeCandFwd2(mid, _, _, config) and + store(mid, f, node) and + readCand1(f, unbind(config)) + ) + } + + /** + * Holds if `node` is part of a path from a source to a sink in the given + * configuration taking simple call contexts into consideration. + */ + private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configuration config) { + nodeCandFwd2(node, _, false, config) and + config.isSink(node) and toReturn = false and stored = false + or + nodeCandFwd2(node, _, unbindBool(stored), unbind(config)) and + ( + exists(Node mid, boolean preservesValue | + localFlowStepOrFlowThroughMethod(node, mid, preservesValue, config) and + nodeCand2(mid, toReturn, stored, config) and + (stored = false or preservesValue = true) + ) + or + exists(Node mid, boolean preservesValue | + jumpStep(node, mid, preservesValue, config) and + nodeCand2(mid, _, stored, config) and + toReturn = false and + (stored = false or preservesValue = true) + ) + or + // store + exists(Node mid, Content f | + store(node, f, mid) and + readCand2(f, unbind(config)) and + nodeCand2(mid, toReturn, true, config) and + (stored = false or stored = true) + ) + or + // read + exists(Node mid, Content f | + read(node, f, mid) and + storeCandFwd2(f, unbind(config)) and + nodeCand2(mid, toReturn, _, config) and + stored = true + ) + or + exists(Node mid, boolean allowsFieldFlow | + flowIntoCallable(node, mid, allowsFieldFlow, config) and + nodeCand2(mid, false, stored, config) and + toReturn = false and + (stored = false or allowsFieldFlow = true) + ) + or + exists(Node mid, boolean allowsFieldFlow | + flowOutOfCallable(node, mid, allowsFieldFlow, config) and + nodeCand2(mid, _, stored, config) and + toReturn = true and + (stored = false or allowsFieldFlow = true) + ) + ) + } + + /** + * Holds if `f` is the target of a read in the flow covered by `nodeCand2`. + */ + private predicate readCand2(Content f, Configuration config) { + exists(Node mid, Node node | + useFieldFlow(unbind(config)) and + nodeCandFwd2(node, _, true, unbind(config)) and + read(node, f, mid) and + storeCandFwd2(f, unbind(config)) and + nodeCand2(mid, _, _, config) + ) + } + + private predicate storeCand(Content f, Configuration conf) { + exists(Node n1, Node n2 | + store(n1, f, n2) and + nodeCand2(n1, _, _, conf) and + nodeCand2(n2, _, _, unbind(conf)) + ) + } + + private predicate readCand(Content f, Configuration conf) { + readCand2(f, conf) + } + + /** + * Holds if `f` is the target of both a store and a read in the path graph + * covered by `nodeCand2`. + */ + pragma[noinline] + private predicate readStoreCand(Content f, Configuration conf) { + storeCand(f, conf) and + readCand(f, conf) + } + + private predicate nodeCand(Node node, Configuration config) { + nodeCand2(node, _, _, config) + } + + /** + * 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) { + nodeCand(node, config) and + ( + config.isSource(node) or + jumpStep(_, node, _, config) or + node instanceof ParameterNode or + node.asExpr() instanceof MethodAccess or + node instanceof PostUpdateNode or + read(_, _, node) or + node.asExpr() instanceof CastExpr + ) + } + + /** + * Holds if `node` can be the last node in a maximal subsequence of local + * flow steps in a dataflow path. + */ + private predicate localFlowExit(Node node, Configuration config) { + exists(Node next | + nodeCand(next, config) + | + jumpStep(node, next, _, config) or + flowIntoCallable(node, next, config) or + flowOutOfCallable(node, next, config) or + flowThroughMethod(node, next, _, config) or + store(node, _, next) or + read(node, _, next) + ) or + node.asExpr() instanceof CastExpr or + config.isSink(node) + } + + /** + * Holds if the local path from `node1` to `node2` is a prefix of a maximal + * subsequence of local flow steps in a dataflow path. + * + * This is the transitive closure of `localFlowStep` beginning at `localFlowEntry`. + */ + private predicate localFlowStepPlus(Node node1, Node node2, boolean preservesValue, Configuration config) { + localFlowEntry(node1, config) and + localFlowStep(node1, node2, preservesValue, config) and + node1 != node2 and + nodeCand(node2, unbind(config)) + or + exists(Node mid, boolean pv1, boolean pv2 | + localFlowStepPlus(node1, mid, pv1, config) and + localFlowStep(mid, node2, pv2, config) and + not mid.asExpr() instanceof CastExpr and + preservesValue = pv1.booleanAnd(pv2) and + nodeCand(node2, unbind(config)) + ) + } + + /** + * Holds if `node1` can step to `node2` in one or more local steps and this + * path can occur as a maximal subsequence of local steps in a dataflow path. + */ + pragma[noinline] + private predicate localFlowBigStep(Node node1, Node node2, boolean preservesValue, Configuration config) { + localFlowStepPlus(node1, node2, preservesValue, config) and + localFlowExit(node2, config) + } + + /** + * Holds if `f` may contain an object of the same type, `t`, as the one + * that contains `f`, and if this fact should be used to compress + * access paths. + * + * Examples include the tail pointer in a linked list or the left and right + * pointers in a binary tree. + */ + private predicate selfRef(Content f, RefType t) { + t = f.getDeclaringType() and + f.isSelfRef() + } + + private newtype TFlowContainer = + TRegularContent(Content f) { not selfRef(f, _) } or + TSelfRefContent(RefType t) { selfRef(_, t) } + + /** + * A `Content` or a `Content` abstracted as its declaring type. + * + * Sequences of one or more `Content`s in the same declaring type for which + * `isSelfRef()` holds are represented as a single `FlowContainer` in an + * `AccessPath`. + */ + private class FlowContainer extends TFlowContainer { + string toString() { + exists(Content f | this = TRegularContent(f) and result = f.toString()) or + exists(RefType t | this = TSelfRefContent(t) and result = t.toString()) + } + predicate usesContent(Content f) { + this = TRegularContent(f) or + exists(RefType t | this = TSelfRefContent(t) and selfRef(f, t)) + } + RefType getContainerType() { + exists(Content f | this = TRegularContent(f) and result = f.getDeclaringType()) or + this = TSelfRefContent(result) + } + } + + private newtype TAccessPathFront = TFrontNil(Type t) or TFrontHead(FlowContainer f) + + /** + * The front of an `AccessPath`. This is either a head or a nil. + */ + private class AccessPathFront extends TAccessPathFront { + string toString() { + exists(Type t | this = TFrontNil(t) | result = t.toString()) or + exists(FlowContainer f | this = TFrontHead(f) | result = f.toString()) + } + Type getType() { + this = TFrontNil(result) or + exists(FlowContainer head | this = TFrontHead(head) | result = head.getContainerType()) + } + predicate headUsesContent(Content f) { + exists(FlowContainer fc | + fc.usesContent(f) and + this = TFrontHead(fc) + ) + } + } + + private class AccessPathFrontNil extends AccessPathFront, TFrontNil { + } + + /** + * A `Node` at which a cast can occur such that the type should be checked. + */ + private class CastingNode extends Node { + CastingNode() { + this instanceof ParameterNode or + this.asExpr() instanceof CastExpr or + this.asExpr() instanceof MethodAccess or + this.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNode + } + } + + /** + * Holds if data can flow from a source to `node` with the given `apf`. + */ + private predicate flowCandFwd(Node node, boolean fromArg, AccessPathFront apf, Configuration config) { + flowCandFwd0(node, fromArg, apf, config) and + if node instanceof CastingNode then + compatibleTypes(node.getType(), apf.getType()) + else + any() + } + private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf, Configuration config) { + nodeCand2(node, _, false, config) and + config.isSource(node) and fromArg = false and apf = TFrontNil(getErasedRepr(node.getType())) + or + nodeCand(node, unbind(config)) and + ( + exists(Node mid | + flowCandFwd(mid, fromArg, apf, config) and + localFlowBigStep(mid, node, true, config) + ) + or + exists(Node mid, AccessPathFront apf0 | + flowCandFwd(mid, fromArg, apf0, config) and + localFlowBigStep(mid, node, false, config) and + apf0 instanceof AccessPathFrontNil and + apf = TFrontNil(getErasedRepr(node.getType())) + ) + or + exists(Node mid | + flowCandFwd(mid, _, apf, config) and + jumpStep(mid, node, true, config) and + fromArg = false + ) + or + exists(Node mid, AccessPathFront apf0 | + flowCandFwd(mid, _, apf0, config) and + jumpStep(mid, node, false, config) and + fromArg = false and + apf0 instanceof AccessPathFrontNil and + apf = TFrontNil(getErasedRepr(node.getType())) + ) + or + exists(Node mid, boolean allowsFieldFlow | + flowCandFwd(mid, _, apf, config) and + flowIntoCallable(mid, node, allowsFieldFlow, config) and + fromArg = true and + (apf instanceof AccessPathFrontNil or allowsFieldFlow = true) + ) + or + exists(Node mid, boolean allowsFieldFlow | + flowCandFwd(mid, false, apf, config) and + flowOutOfCallable(mid, node, allowsFieldFlow, config) and + fromArg = false and + (apf instanceof AccessPathFrontNil or allowsFieldFlow = true) + ) + or + exists(Node mid, boolean preservesValue, AccessPathFront apf0 | + flowCandFwd(mid, fromArg, apf0, config) and + flowThroughMethod(mid, node, preservesValue, config) and + (preservesValue = true and apf = apf0 or preservesValue = false and apf0 instanceof AccessPathFrontNil and apf = TFrontNil(getErasedRepr(node.getType()))) + ) + ) + or + exists(Node mid, Content f | + flowCandFwd(mid, fromArg, _, config) and + store(mid, f, node) and + nodeCand(node, unbind(config)) and + apf.headUsesContent(f) + ) + or + exists(Node mid, Content f, AccessPathFront apf0 | + flowCandFwd(mid, fromArg, apf0, config) and + read(mid, f, node) and + nodeCand(node, config) and + apf0.headUsesContent(f) and + consCandFwd(f, apf, unbind(config)) + ) + } + private predicate consCandFwd(Content f, AccessPathFront apf, Configuration config) { + exists(Node mid, Node n | + flowCandFwd(mid, _, apf, config) and + store(mid, f, n) and + nodeCand(n, unbind(config)) and + readStoreCand(f, unbind(config)) and + compatibleTypes(apf.getType(), f.getType()) + ) + } + + /** + * Holds if data can flow from a source to `node` with the given `apf` and + * from there flow to a sink. + */ + private predicate flowCand(Node node, boolean toReturn, AccessPathFront apf, Configuration config) { + flowCand0(node, toReturn, apf, config) and + flowCandFwd(node, _, apf, config) + } + private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Configuration config) { + flowCandFwd(node, _, apf, config) and + config.isSink(node) and toReturn = false and apf instanceof AccessPathFrontNil + or + ( + exists(Node mid | + localFlowBigStep(node, mid, true, config) and + flowCand(mid, toReturn, apf, config) + ) + or + exists(Node mid, AccessPathFront apf0 | + flowCandFwd(node, _, apf, config) and + localFlowBigStep(node, mid, false, config) and + flowCand(mid, toReturn, apf0, config) and + apf0 instanceof AccessPathFrontNil and + apf instanceof AccessPathFrontNil + ) + or + exists(Node mid | + jumpStep(node, mid, true, config) and + flowCand(mid, _, apf, config) and + toReturn = false + ) + or + exists(Node mid, AccessPathFront apf0 | + flowCandFwd(node, _, apf, config) and + jumpStep(node, mid, false, config) and + flowCand(mid, _, apf0, config) and + toReturn = false and + apf0 instanceof AccessPathFrontNil and + apf instanceof AccessPathFrontNil + ) + or + exists(Node mid, boolean allowsFieldFlow | + flowIntoCallable(node, mid, allowsFieldFlow, config) and + flowCand(mid, false, apf, config) and + toReturn = false and + (apf instanceof AccessPathFrontNil or allowsFieldFlow = true) + ) + or + exists(Node mid, boolean allowsFieldFlow | + flowOutOfCallable(node, mid, allowsFieldFlow, config) and + flowCand(mid, _, apf, config) and + toReturn = true and + (apf instanceof AccessPathFrontNil or allowsFieldFlow = true) + ) + or + exists(Node mid, boolean preservesValue, AccessPathFront apf0 | + flowThroughMethod(node, mid, preservesValue, config) and + flowCand(mid, toReturn, apf0, config) and + (preservesValue = true and apf = apf0 or preservesValue = false and apf0 instanceof AccessPathFrontNil and apf instanceof AccessPathFrontNil and flowCandFwd(node, _, apf, config)) + ) + or + exists(Node mid, Content f, AccessPathFront apf0 | + store(node, f, mid) and + flowCand(mid, toReturn, apf0, config) and + apf0.headUsesContent(f) and + consCand(f, apf, unbind(config)) + ) + or + exists(Node mid, Content f, AccessPathFront apf0 | + read(node, f, mid) and + flowCand(mid, toReturn, apf0, config) and + consCandFwd(f, apf0, unbind(config)) and + apf.headUsesContent(f) + ) + ) + } + private predicate consCand(Content f, AccessPathFront apf, Configuration config) { + consCandFwd(f, apf, config) and + exists(Node mid, Node n, AccessPathFront apf0 | + flowCandFwd(n, _, apf0, config) and + apf0.headUsesContent(f) and + read(n, f, mid) and + flowCand(mid, _, apf, config) + ) + } + + private newtype TAccessPath = TNil(Type t) or TCons(FlowContainer f, int len) { len in [1..5] } + + /** + * Conceptually a list of `Content`s followed by a `Type`, but only the first + * element of the list and its length are tracked. If data flows from a source to + * a given node with a given `AccessPath`, this indicates the sequence of + * dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ + private class AccessPath extends TAccessPath { + abstract string toString(); + FlowContainer getHead() { this = TCons(result, _) } + int len() { + this = TNil(_) and result = 0 + or + this = TCons(_, result) + } + Type getType() { + this = TNil(result) or + exists(FlowContainer head | this = TCons(head, _) | result = head.getContainerType()) + } + abstract AccessPathFront getFront(); + } + + private class AccessPathNil extends AccessPath, TNil { + override string toString() { exists(Type t | this = TNil(t) | result = t.toString()) } + override AccessPathFront getFront() { exists(Type t | this = TNil(t) | result = TFrontNil(t)) } + } + + private class AccessPathCons extends AccessPath, TCons { + override string toString() { + exists(FlowContainer f, int len | this = TCons(f, len) | result = f.toString() + ", ... (" + len.toString() + ")") + } + override AccessPathFront getFront() { exists(FlowContainer f | this = TCons(f, _) | result = TFrontHead(f)) } + } + + /** Holds if `ap0` corresponds to the cons of `f` and `ap`. */ + private predicate pop(AccessPath ap0, Content f, AccessPath ap) { + ap0.getFront().headUsesContent(f) and + consCand(f, ap.getFront(), _) and + ap0.len() = 1 + ap.len() + } + + /** Holds if `ap0` corresponds to the cons of `f` and `ap` and `apf` is the front of `ap`. */ + pragma[noinline] + private predicate popWithFront(AccessPath ap0, Content f, AccessPathFront apf, AccessPath ap) { + pop(ap0, f, ap) and apf = ap.getFront() + } + + /** Holds if `ap` corresponds to the cons of `f` and `ap0`. */ + private predicate push(AccessPath ap0, Content f, AccessPath ap) { pop(ap, f, ap0) } + + /** + * Holds if data can flow from a source to `node` with the given `ap`. + */ + private predicate flowFwd(Node node, boolean fromArg, AccessPathFront apf, AccessPath ap, Configuration config) { + flowFwd0(node, fromArg, apf, ap, config) and + flowCand(node, _, apf, config) + } + private predicate flowFwd0(Node node, boolean fromArg, AccessPathFront apf, AccessPath ap, Configuration config) { + flowCand(node, _, _, config) and + config.isSource(node) and fromArg = false and ap = TNil(getErasedRepr(node.getType())) and apf = ap.(AccessPathNil).getFront() + or + flowCand(node, _, _, unbind(config)) and + ( + exists(Node mid | + flowFwd(mid, fromArg, apf, ap, config) and + localFlowBigStep(mid, node, true, config) + ) + or + exists(Node mid, AccessPath ap0 | + flowFwd(mid, fromArg, _, ap0, config) and + localFlowBigStep(mid, node, false, config) and + ap0 instanceof AccessPathNil and + ap = TNil(getErasedRepr(node.getType())) and + apf = ap.(AccessPathNil).getFront() + ) + or + exists(Node mid | + flowFwd(mid, _, apf, ap, config) and + jumpStep(mid, node, true, config) and + fromArg = false + ) + or + exists(Node mid, AccessPath ap0 | + flowFwd(mid, _, _, ap0, config) and + jumpStep(mid, node, false, config) and + fromArg = false and + ap0 instanceof AccessPathNil and + ap = TNil(getErasedRepr(node.getType())) and + apf = ap.(AccessPathNil).getFront() + ) + or + exists(Node mid, boolean allowsFieldFlow | + flowFwd(mid, _, apf, ap, config) and + flowIntoCallable(mid, node, allowsFieldFlow, config) and + fromArg = true and + (ap instanceof AccessPathNil or allowsFieldFlow = true) + ) + or + exists(Node mid, boolean allowsFieldFlow | + flowFwd(mid, false, apf, ap, config) and + flowOutOfCallable(mid, node, allowsFieldFlow, config) and + fromArg = false and + (ap instanceof AccessPathNil or allowsFieldFlow = true) + ) + or + exists(Node mid, boolean preservesValue, AccessPathFront apf0, AccessPath ap0 | + flowFwd(mid, fromArg, apf0, ap0, config) and + flowThroughMethod(mid, node, preservesValue, config) and + (preservesValue = true and ap = ap0 and apf = apf0 or preservesValue = false and ap0 instanceof AccessPathNil and ap = TNil(getErasedRepr(node.getType())) and apf = ap.(AccessPathNil).getFront()) + ) + ) + or + exists(Content f, AccessPath ap0 | + flowFwdStore(node, f, ap0, apf, fromArg, config) and + push(ap0, f, ap) + ) + or + exists(Content f, AccessPath ap0 | + flowFwdRead(node, f, ap0, fromArg, config) and + popWithFront(ap0, f, apf, ap) + ) + } + pragma[nomagic] + private predicate flowFwdStore(Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg, Configuration config) { + exists(Node mid, AccessPathFront apf0 | + flowFwd(mid, fromArg, apf0, ap0, config) and + flowFwdStoreAux(mid, f, node, apf0, apf, config) + ) + } + private predicate flowFwdStoreAux(Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFront apf, Configuration config) { + store(mid, f, node) and + consCand(f, apf0, config) and + apf.headUsesContent(f) and + flowCand(node, _, apf, unbind(config)) + } + pragma[nomagic] + private predicate flowFwdRead(Node node, Content f, AccessPath ap0, boolean fromArg, Configuration config) { + exists(Node mid, AccessPathFront apf0 | + flowFwd(mid, fromArg, apf0, ap0, config) and + read(mid, f, node) and + apf0.headUsesContent(f) and + flowCand(node, _, _, unbind(config)) + ) + } + + /** + * Holds if data can flow from a source to `node` with the given `ap` and + * from there flow to a sink. + */ + private predicate flow(Node node, boolean toReturn, AccessPath ap, Configuration config) { + flow0(node, toReturn, ap, config) and + flowFwd(node, _, _, ap, config) + } + private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuration config) { + flowFwd(node, _, _, ap, config) and + config.isSink(node) and toReturn = false and ap instanceof AccessPathNil + or + ( + exists(Node mid | + localFlowBigStep(node, mid, true, config) and + flow(mid, toReturn, ap, config) + ) + or + exists(Node mid, AccessPath ap0 | + flowFwd(node, _, _, ap, config) and + localFlowBigStep(node, mid, false, config) and + flow(mid, toReturn, ap0, config) and + ap0 instanceof AccessPathNil and + ap instanceof AccessPathNil + ) + or + exists(Node mid | + jumpStep(node, mid, true, config) and + flow(mid, _, ap, config) and + toReturn = false + ) + or + exists(Node mid, AccessPath ap0 | + flowFwd(node, _, _, ap, config) and + jumpStep(node, mid, false, config) and + flow(mid, _, ap0, config) and + toReturn = false and + ap0 instanceof AccessPathNil and + ap instanceof AccessPathNil + ) + or + exists(Node mid, boolean allowsFieldFlow | + flowIntoCallable(node, mid, allowsFieldFlow, config) and + flow(mid, false, ap, config) and + toReturn = false and + (ap instanceof AccessPathNil or allowsFieldFlow = true) + ) + or + exists(Node mid, boolean allowsFieldFlow | + flowOutOfCallable(node, mid, allowsFieldFlow, config) and + flow(mid, _, ap, config) and + toReturn = true and + (ap instanceof AccessPathNil or allowsFieldFlow = true) + ) + or + exists(Node mid, boolean preservesValue, AccessPath ap0 | + flowThroughMethod(node, mid, preservesValue, config) and + flow(mid, toReturn, ap0, config) and + (preservesValue = true and ap = ap0 or preservesValue = false and ap0 instanceof AccessPathNil and ap instanceof AccessPathNil and flowFwd(node, _, _, ap, config)) + ) + or + exists(Content f, AccessPath ap0 | + flowStore(node, f, toReturn, ap0, config) and + pop(ap0, f, ap) + ) + or + exists(Content f, AccessPath ap0 | + flowRead(node, f, toReturn, ap0, config) and + push(ap0, f, ap) + ) + ) + } + pragma[nomagic] + private predicate flowStore(Node node, Content f, boolean toReturn, AccessPath ap0, Configuration config) { + exists(Node mid | + store(node, f, mid) and + flow(mid, toReturn, ap0, config) + ) + } + pragma[nomagic] + private predicate flowRead(Node node, Content f, boolean toReturn, AccessPath ap0, Configuration config) { + exists(Node mid | + read(node, f, mid) and + flow(mid, toReturn, ap0, config) + ) + } + + private bindingset[conf, result] Configuration unbind(Configuration conf) { result >= conf and result <= conf } + + private predicate flow(Node n, Configuration config) { + flow(n, _, _, config) + } + + private newtype TPathNode = + TPathNodeMid(Node node, CallContext cc, AccessPath ap, Configuration config) { + // A PathNode is introduced by a source ... + flow(node, config) and + config.isSource(node) and + cc instanceof CallContextAny and + ap = TNil(getErasedRepr(node.getType())) + or + // ... or a step from an existing PathNode to another node. + exists(PathNodeMid mid | + flowStep(mid, node, cc, ap) and + config = mid.getConfiguration() and + flow(node, _, ap, unbind(config)) + ) + } or + TPathNodeSink(Node node, Configuration config) { // The AccessPath on a sink is empty. + config.isSink(node) and + flow(node, config) + } + + /** + * 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. + */ + abstract class PathNode extends TPathNode { + /** Gets a textual representation of this element. */ + string toString() { result = getNode().toString() + ppAp() } + /** Gets the source location for this element. */ + Location getLocation() { result = getNode().getLocation() } + /** Gets the underlying `Node`. */ + abstract Node getNode(); + /** Gets the associated configuration. */ + abstract Configuration getConfiguration(); + /** Gets a successor. */ + abstract PathNode getSucc(); + private string ppAp() { + this instanceof PathNodeSink and result = "" or + result = " [" + this.(PathNodeMid).getAp().toString() + "]" + } + } + + /** Holds if `n` can reach a sink. */ + private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getSucc()) } + + /** Holds if `n1.getSucc() = n2` and `n2` can reach a sink. */ + private predicate pathSucc(PathNode n1, PathNode n2) { n1.getSucc() = n2 and reach(n2) } + + private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2) + + /** + * Provides the query predicates needed to include a graph in a path-problem query. + */ + module PathGraph { + /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ + query predicate edges(PathNode a, PathNode b) { pathSucc(a, b) } + } + + /** + * An intermediate flow graph node. This is a triple consisting of a `Node`, + * a `CallContext`, and a `Configuration`. + */ + private class PathNodeMid extends PathNode, TPathNodeMid { + Node node; + CallContext cc; + AccessPath ap; + Configuration config; + PathNodeMid() { this = TPathNodeMid(node, cc, ap, config) } + override Node getNode() { result = node } + CallContext getCallContext() { result = cc } + AccessPath getAp() { result = ap } + override Configuration getConfiguration() { result = config } + private PathNodeMid getSuccMid() { + flowStep(this, result.getNode(), result.getCallContext(), result.getAp()) and + result.getConfiguration() = unbind(this.getConfiguration()) + } + override PathNode getSucc() { + // an intermediate step to another intermediate node + result = getSuccMid() + or + // a final step to a sink via one or more local steps + localFlowStepPlus(node, result.getNode(), _, config) and + ap instanceof AccessPathNil and + result instanceof PathNodeSink and + result.getConfiguration() = unbind(this.getConfiguration()) + or + // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges + exists(PathNodeMid mid | + mid = getSuccMid() and + mid.getNode() = result.getNode() and + mid.getAp() instanceof AccessPathNil and + result instanceof PathNodeSink and + result.getConfiguration() = unbind(mid.getConfiguration()) + ) + or + // a direct step from a source to a sink if a node is both + this instanceof PathNodeSource and + result instanceof PathNodeSink and + this.getNode() = result.getNode() and + result.getConfiguration() = unbind(this.getConfiguration()) + } + } + + /** + * A flow graph node corresponding to a source. + */ + private class PathNodeSource extends PathNodeMid { + PathNodeSource() { + getConfiguration().isSource(getNode()) and + getCallContext() instanceof CallContextAny and + getAp() instanceof AccessPathNil + } + } + + /** + * A flow graph node corresponding to a sink. This is disjoint from the + * intermediate nodes in order to uniquely correspond to a given sink by + * excluding the `CallContext`. + */ + private class PathNodeSink extends PathNode, TPathNodeSink { + Node node; + Configuration config; + PathNodeSink() { this = TPathNodeSink(node, config) } + override Node getNode() { result = node } + override Configuration getConfiguration() { result = config } + override PathNode getSucc() { none() } + } + + /** + * Holds if data may flow from `mid` to `node`. The last step in or out of + * a callable is recorded by `cc`. + */ + private predicate flowStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) { + localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and + cc = mid.getCallContext() and + ap = mid.getAp() + or + localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration()) and + cc = mid.getCallContext() and + mid.getAp() instanceof AccessPathNil and + ap = TNil(getErasedRepr(node.getType())) + or + jumpStep(mid.getNode(), node, true, mid.getConfiguration()) and + cc instanceof CallContextAny and + ap = mid.getAp() + or + jumpStep(mid.getNode(), node, false, mid.getConfiguration()) and + cc instanceof CallContextAny and + mid.getAp() instanceof AccessPathNil and + ap = TNil(getErasedRepr(node.getType())) + or + contentReadStep(mid, node, ap) and cc = mid.getCallContext() + or + exists(Content f, AccessPath ap0 | contentStoreStep(mid, node, ap0, f, cc) and push(ap0, f, ap)) + or + flowOutOfArgument(mid, node, cc) and ap = mid.getAp() + or + flowIntoCallable(mid, node, _, cc, _) and ap = mid.getAp() + or + flowOutOfMethod(mid, node.asExpr(), cc) and ap = mid.getAp() + or + flowThroughMethod(mid, node.asExpr(), cc) and ap = TNil(getErasedRepr(node.getType())) + or + valueFlowThroughMethod(mid, node.asExpr(), cc) and ap = mid.getAp() + } + + private predicate contentReadStep(PathNodeMid mid, Node node, AccessPath ap) { + exists(Content f, AccessPath ap0 | + ap0 = mid.getAp() and + read(mid.getNode(), f, node) and + pop(ap0, f, ap) + ) + } + + pragma[noinline] + private predicate contentStoreStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) { + ap0 = mid.getAp() and + store(mid.getNode(), f, node) and + cc = mid.getCallContext() + } + + /** + * Holds if data may flow from `mid` to an exit of `m` in the context + * `innercc`, and the path did not flow through a parameter of `m`. + */ + private predicate flowOutOfMethod0(PathNodeMid mid, Method m, CallContext innercc) { + exists(ReturnNode ret | + ret = mid.getNode() and + innercc = mid.getCallContext() and + m = returnNodeGetEnclosingCallable(ret) and + not innercc instanceof CallContextCall + ) + } + + /** + * Holds if data may flow from `mid` to `ma`. The last step of this path + * is a return from a method and is recorded by `cc`, if needed. + */ + pragma[noinline] + private predicate flowOutOfMethod(PathNodeMid mid, MethodAccess ma, CallContext cc) { + exists(Method m, CallContext innercc | + flowOutOfMethod0(mid, m, innercc) and + resolveReturn(innercc, m, ma) + | + if reducedViableImplInReturn(m, ma) then cc = TReturn(m, ma) else cc = TAnyCallContext() + ) + } + + private predicate flowOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallContext cc) { + exists(PostUpdateNode n, ParameterNode p, Callable callable, CallContext innercc, int i, Call call, ArgumentNode arg | + mid.getNode() = n and + parameterValueFlowsToUpdate(p, n) and + innercc = mid.getCallContext() and + p.isParameterOf(callable, i) and + resolveReturn(innercc, callable, call) and + node.getPreUpdateNode() = arg and + arg.argumentOf(call, i) and + flow(node, unbind(mid.getConfiguration())) + | + if reducedViableImplInReturn(callable, call) then cc = TReturn(callable, call) else cc = TAnyCallContext() + ) + } + + /** + * Holds if data may flow from `mid` to the `i`th argument of `call` in `cc`. + */ + pragma[noinline] + private predicate flowIntoArg(PathNodeMid mid, int i, CallContext cc, Call call, boolean emptyAp) { + exists(ArgumentNode arg, AccessPath ap | + arg = mid.getNode() and + cc = mid.getCallContext() and + arg.argumentOf(call, i) and + ap = mid.getAp() + | + ap instanceof AccessPathNil and emptyAp = true or + ap instanceof AccessPathCons and emptyAp = false + ) + } + + pragma[noinline] + private predicate parameterCand(Callable callable, int i, Configuration config) { + exists(ParameterNode p | + flow(p, config) and + p.isParameterOf(callable, i) + ) + } + + pragma[nomagic] + private predicate flowIntoCallable0(PathNodeMid mid, Callable callable, int i, CallContext outercc, Call call, boolean emptyAp) { + flowIntoArg(mid, i, outercc, call, emptyAp) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), mid.getConfiguration()) + } + + /** + * Holds if data may flow from `mid` to `p` through `call`. The contexts + * before and after entering the callable are `outercc` and `innercc`, + * respectively. + */ + private predicate flowIntoCallable(PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, Call call) { + exists(int i, Callable callable, boolean emptyAp | + flowIntoCallable0(mid, callable, i, outercc, call, emptyAp) and + p.isParameterOf(callable, i) + | + if reducedViableImplInCallContext(_, callable, call) then innercc = TSpecificCall(call, i, emptyAp) else innercc = TSomeCall(p, emptyAp) + ) + } + + /** Holds if data may flow from `p` to a return statement in the callable. */ + pragma[nomagic] + private predicate paramFlowsThrough(ParameterNode p, CallContextCall cc, Configuration config) { + exists(PathNodeMid mid, ReturnNode ret | + mid.getNode() = ret and + cc = mid.getCallContext() and + config = mid.getConfiguration() and + mid.getAp() instanceof AccessPathNil + | + cc = TSomeCall(p, true) or + exists(int i | cc = TSpecificCall(_, i, true) | + p.isParameterOf(returnNodeGetEnclosingCallable(ret), i) + ) + ) + } + + /** + * Holds if data may flow from `mid` to an argument of `methodcall`, + * through a called method `m`, and back out through a return statement in + * `m`. The context `cc` is restored to its value prior to entering `m`. + */ + pragma[noinline] + private predicate flowThroughMethod(PathNodeMid mid, Call methodcall, CallContext cc) { + exists(ParameterNode p, CallContext innercc | + flowIntoCallable(mid, p, cc, innercc, methodcall) and + paramFlowsThrough(p, innercc, unbind(mid.getConfiguration())) and + not parameterValueFlowsThrough(p) and + mid.getAp() instanceof AccessPathNil + ) + } + + private predicate valueFlowThroughMethod(PathNodeMid mid, Call methodcall, CallContext cc) { + exists(ParameterNode p | + flowIntoCallable(mid, p, cc, _, methodcall) and + parameterValueFlowsThrough(p) + ) + } + + /** + * Holds if data can flow (inter-procedurally) from `source` to `sink`. + * + * Will only have results if `configuration` has non-empty sources and + * sinks. + */ + private predicate flowsTo(PathNodeSource flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration) { + flowsource.getConfiguration() = configuration and + flowsource.getNode() = source and + pathSuccPlus(flowsource, flowsink) and + flowsink.getNode() = sink + } + + /** + * Holds if data can flow (inter-procedurally) from `source` to `sink`. + * + * Will only have results if `configuration` has non-empty sources and + * sinks. + */ + predicate flowsTo(Node source, Node sink, Configuration configuration) { + flowsTo(_, _, source, sink, configuration) + } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll new file mode 100644 index 000000000000..142ea7f8a7e9 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll @@ -0,0 +1,1536 @@ +/** + * Provides an implementation of global (interprocedural) data flow. This file + * re-exports the local (intraprocedural) data flow analysis from `DataFlowUtil` + * and adds a global analysis, mainly exposed through the `Configuration` class. + * This file exists in several identical copies, allowing queries to use + * multiple `Configuration` classes that depend on each other without + * introducing mutual recursion among those configurations. + */ + + import DataFlowUtil + private import DataFlowPrivate + private import DataFlowDispatch + private import DataFlowImplCommon + + + /** + * A configuration of interprocedural data flow analysis. This defines + * sources, sinks, and any other configurable aspect of the analysis. Each + * use of the global data flow library must define its own unique extension + * of this abstract class. To create a configuration, extend this class with + * a subclass whose characteristic predicate is a unique singleton string. + * For example, write + * + * ``` + * class MyAnalysisConfiguration extends DataFlow::Configuration { + * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } + * // Override `isSource` and `isSink`. + * // Optionally override `isBarrier`. + * // Optionally override `isAdditionalFlowStep`. + * } + * ``` + * + * Then, to query whether there is flow between some `source` and `sink`, + * write + * + * ``` + * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) + * ``` + * + * Multiple configurations can coexist, but two classes extending + * `DataFlow::Configuration` should never depend on each other. One of them + * should instead depend on a `DataFlow2::Configuration`, a + * `DataFlow3::Configuration`, or a `DataFlow4::Configuration`. + */ + abstract class Configuration extends string { + bindingset[this] + Configuration() { any() } + + /** + * Holds if `source` is a relevant data flow source. + */ + abstract predicate isSource(Node source); + + /** + * Holds if `sink` is a relevant data flow sink. + */ + abstract predicate isSink(Node sink); + + /** Holds if data flow through `node` is prohibited. */ + predicate isBarrier(Node node) { none() } + + /** Holds if data flow from `node1` to `node2` is prohibited. */ + predicate isBarrierEdge(Node node1, Node node2) { none() } + + /** + * Holds if the additional flow step from `node1` to `node2` must be taken + * into account in the analysis. + */ + predicate isAdditionalFlowStep(Node node1, Node node2) { none() } + + /** + * Gets the virtual dispatch branching limit when calculating field flow. + * This can be overridden to a smaller value to improve performance (a + * value of 0 disables field flow), or a larger value to get more results. + */ + int fieldFlowBranchLimit() { result = 2 } + + /** + * Holds if data may flow from `source` to `sink` for this configuration. + */ + predicate hasFlow(Node source, Node sink) { + flowsTo(source, sink, this) + } + + /** + * Holds if data may flow from `source` to `sink` for this configuration. + * + * The corresponding paths are generated from the end-points and the graph + * included in the module `PathGraph`. + */ + predicate hasFlowPath(PathNode source, PathNode sink) { + flowsTo(source, sink, _, _, this) + } + + /** + * Holds if data may flow from some source to `sink` for this configuration. + */ + predicate hasFlowTo(Node sink) { + hasFlow(_, sink) + } + + /** + * Holds if data may flow from some source to `sink` for this configuration. + */ + predicate hasFlowToExpr(Expr sink) { + hasFlowTo(exprNode(sink)) + } + + /** DEPRECATED: use `hasFlow` instead. */ + deprecated predicate hasFlowForward(Node source, Node sink) { + hasFlow(source, sink) + } + + /** DEPRECATED: use `hasFlow` instead. */ + deprecated predicate hasFlowBackward(Node source, Node sink) { + hasFlow(source, sink) + } + } + + /** + * Holds if the additional step from `node1` to `node2` jumps between callables. + */ + private predicate additionalJumpStep(Node node1, Node node2, Configuration config) { + config.isAdditionalFlowStep(node1, node2) and + node1.getEnclosingCallable() != node2.getEnclosingCallable() + } + + pragma[noinline] + private predicate isAdditionalFlowStep(Node node1, Node node2, Callable callable1, Callable callable2, Configuration config) { + config.isAdditionalFlowStep(node1, node2) and + callable1 = node1.getEnclosingCallable() and + callable2 = node2.getEnclosingCallable() + } + + /** + * Holds if the additional step from `node1` to `node2` does not jump between callables. + */ + private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) { + exists(Callable callable | + isAdditionalFlowStep(node1, node2, callable, callable, config) + ) + } + + /** + * Holds if data can flow from `node1` to `node2` through a static field or + * variable capture. + */ + private predicate jumpStep(Node node1, Node node2, boolean preservesValue, Configuration config) { + jumpStep(node1, node2) and preservesValue = true or + additionalJumpStep(node1, node2, config) and preservesValue = false + } + + /** + * Holds if data can flow in one local step from `node1` to `node2` taking + * additional steps from the configuration into account. + */ + private predicate localFlowStep(Node node1, Node node2, boolean preservesValue, Configuration config) { + localFlowStep(node1, node2) and not config.isBarrierEdge(node1, node2) and preservesValue = true or + additionalLocalFlowStep(node1, node2, config) and preservesValue = false + } + + pragma[noinline] + private Method returnNodeGetEnclosingCallable(ReturnNode ret) { + result = ret.getEnclosingCallable() + } + + /** + * Holds if field flow should be used for the given configuration. + */ + private predicate useFieldFlow(Configuration config) { + config.fieldFlowBranchLimit() >= 1 + } + + /** + * Holds if `node` is reachable from a source in the given configuration + * ignoring call contexts. + */ + private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) { + not config.isBarrier(node) and + ( + config.isSource(node) and stored = false + or + exists(Node mid, boolean preservesValue | + nodeCandFwd1(mid, stored, config) and + localFlowStep(mid, node, preservesValue, config) and + (stored = false or preservesValue = true) + ) + or + exists(Node mid, boolean preservesValue | + nodeCandFwd1(mid, stored, config) and + jumpStep(mid, node, preservesValue, config) and + (stored = false or preservesValue = true) + ) + or + // store + exists(Node mid | + useFieldFlow(config) and + nodeCandFwd1(mid, _, config) and + store(mid, _, node) and + stored = true + ) + or + // read + exists(Node mid, Content f | + nodeCandFwd1(mid, true, config) and + read(mid, f, node) and + storeCandFwd1(f, unbind(config)) and + (stored = false or stored = true) + ) + or + // flow into a callable + exists(Node arg | + nodeCandFwd1(arg, stored, config) and + viableParamArg(node, arg) + ) + or + // flow out of an argument + exists(PostUpdateNode mid, ParameterNode p | + nodeCandFwd1(mid, stored, config) and + parameterValueFlowsToUpdate(p, mid) and + viableParamArg(p, node.(PostUpdateNode).getPreUpdateNode()) + ) + or + // flow out of a callable + exists(Method m, MethodAccess ma, ReturnNode ret | + nodeCandFwd1(ret, stored, config) and + m = returnNodeGetEnclosingCallable(ret) and + m = viableImpl(ma) and + node.asExpr() = ma + ) + ) + } + + /** + * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`. + */ + private predicate storeCandFwd1(Content f, Configuration config) { + exists(Node mid, Node node | + not config.isBarrier(node) and + useFieldFlow(unbind(config)) and + nodeCandFwd1(mid, _, config) and + store(mid, f, node) + ) + } + + bindingset[result, b] + private boolean unbindBool(boolean b) { result != b.booleanNot() } + + /** + * Holds if `node` is part of a path from a source to a sink in the given + * configuration ignoring call contexts. + */ + pragma[nomagic] + private predicate nodeCand1(Node node, boolean stored, Configuration config) { + nodeCandFwd1(node, false, config) and + config.isSink(node) and stored = false + or + nodeCandFwd1(node, unbindBool(stored), unbind(config)) and + ( + exists(Node mid, boolean preservesValue | + localFlowStep(node, mid, preservesValue, config) and + nodeCand1(mid, stored, config) and + (stored = false or preservesValue = true) + ) + or + exists(Node mid, boolean preservesValue | + jumpStep(node, mid, preservesValue, config) and + nodeCand1(mid, stored, config) and + (stored = false or preservesValue = true) + ) + or + // store + exists(Node mid, Content f | + store(node, f, mid) and + readCand1(f, unbind(config)) and + nodeCand1(mid, true, config) and + (stored = false or stored = true) + ) + or + // read + exists(Node mid, Content f | + read(node, f, mid) and + storeCandFwd1(f, unbind(config)) and + nodeCand1(mid, _, config) and + stored = true + ) + or + // flow into a callable + exists(Node param | + viableParamArg(param, node) and + nodeCand1(param, stored, config) + ) + or + // flow out of an argument + exists(PostUpdateNode mid, ParameterNode p | + parameterValueFlowsToUpdate(p, node) and + viableParamArg(p, mid.getPreUpdateNode()) and + nodeCand1(mid, stored, config) + ) + or + // flow out of a callable + exists(Method m, ExprNode ma | + nodeCand1(ma, stored, config) and + m = returnNodeGetEnclosingCallable(node) and + m = viableImpl(ma.getExpr()) + ) + ) + } + + /** + * Holds if `f` is the target of a read in the flow covered by `nodeCand1`. + */ + private predicate readCand1(Content f, Configuration config) { + exists(Node mid, Node node | + useFieldFlow(unbind(config)) and + nodeCandFwd1(node, true, unbind(config)) and + read(node, f, mid) and + storeCandFwd1(f, unbind(config)) and + nodeCand1(mid, _, config) + ) + } + + /** + * Holds if there is a path from `p` to `node` in the same callable that is + * part of a path from a source to a sink taking simple call contexts into + * consideration. This is restricted to paths that does not necessarily + * preserve the value of `p` by making use of at least one additional step + * from the configuration. + */ + pragma[nomagic] + private predicate simpleParameterFlow(ParameterNode p, Node node, RefType t, Configuration config) { + nodeCand1(node, false, config) and + p = node and + t = getErasedRepr(node.getType()) and + not parameterValueFlowsThrough(p) + or + nodeCand1(node, false, unbind(config)) and + exists(Node mid | + simpleParameterFlow(p, mid, t, config) and + localFlowStep(mid, node, true, config) and + compatibleTypes(t, node.getType()) + ) + or + nodeCand1(node, false, unbind(config)) and + exists(Node mid | + simpleParameterFlow(p, mid, _, config) and + localFlowStep(mid, node, false, config) and + t = getErasedRepr(node.getType()) + ) + or + nodeCand1(node, false, unbind(config)) and + exists(Node mid | + simpleParameterFlow(p, mid, t, config) and + localStoreReadStep(mid, node) and + compatibleTypes(t, node.getType()) + ) + or + // value flow through a callable + nodeCand1(node, false, config) and + exists(Node arg | + simpleParameterFlow(p, arg, t, config) and + argumentValueFlowsThrough(arg, node) and + compatibleTypes(t, node.getType()) + ) + or + // flow through a callable + nodeCand1(node, false, config) and + exists(Node arg | + simpleParameterFlow(p, arg, _, config) and + simpleArgumentFlowsThrough(arg, node, t, config) + ) + } + + /** + * Holds if data can flow from `arg` through the `call` taking simple call + * contexts into consideration and that this is part of a path from a source + * to a sink. This is restricted to paths through the `call` that does not + * necessarily preserve the value of `arg` by making use of at least one + * additional step from the configuration. + */ + private predicate simpleArgumentFlowsThrough(ArgumentNode arg, ExprNode call, RefType t, Configuration config) { + exists(ParameterNode param, ReturnNode ret | + nodeCand1(arg, false, unbind(config)) and + nodeCand1(call, false, unbind(config)) and + viableParamArg(param, arg) and + simpleParameterFlow(param, ret, t, config) and + arg.argumentOf(call.getExpr(), _) + ) + } + + /** + * Holds if data can flow from `node1` to `node2` by a step through a method. + */ + private predicate flowThroughMethod(Node node1, Node node2, boolean preservesValue, Configuration config) { + simpleArgumentFlowsThrough(node1, node2, _, config) and preservesValue = false or + argumentValueFlowsThrough(node1, node2) and preservesValue = true + } + + /** + * Holds if data can flow from `node1` to `node2` in one local step or a step + * through a method. + */ + private predicate localFlowStepOrFlowThroughMethod(Node node1, Node node2, boolean preservesValue, Configuration config) { + localFlowStep(node1, node2, preservesValue, config) or + flowThroughMethod(node1, node2, preservesValue, config) + } + + /** + * Holds if data can flow out of a callable from `node1` to `node2`, either + * through a `ReturnNode` or through an argument that has been mutated, and + * that this step is part of a path from a source to a sink. + */ + private predicate flowOutOfCallable(Node node1, Node node2, Configuration config) { + nodeCand1(node1, _, unbind(config)) and + nodeCand1(node2, _, config) and + ( + // flow out of an argument + exists(ParameterNode p | + parameterValueFlowsToUpdate(p, node1) and + viableParamArg(p, node2.(PostUpdateNode).getPreUpdateNode()) + ) + or + // flow out of a method + exists(Method m, MethodAccess ma, ReturnNode ret | + ret = node1 and + m = returnNodeGetEnclosingCallable(ret) and + m = viableImpl(ma) and + node2.asExpr() = ma + ) + ) + } + + /** + * Holds if data can flow into a callable and that this step is part of a + * path from a source to a sink. + */ + private predicate flowIntoCallable(Node node1, Node node2, Configuration config) { + viableParamArg(node2, node1) and + nodeCand1(node1, _, unbind(config)) and + nodeCand1(node2, _, config) + } + + /** + * Gets the amount of forward branching on the origin of a cross-call path + * edge in the graph of paths between sources and sinks that ignores call + * contexts. + */ + private int branch(Node n1, Configuration conf) { + result = strictcount(Node n | flowOutOfCallable(n1, n, conf) or flowIntoCallable(n1, n, conf)) + } + + /** + * Gets the amount of backward branching on the target of a cross-call path + * edge in the graph of paths between sources and sinks that ignores call + * contexts. + */ + private int join(Node n2, Configuration conf) { + result = strictcount(Node n | flowOutOfCallable(n, n2, conf) or flowIntoCallable(n, n2, conf)) + } + + /** + * Holds if data can flow out of a callable from `node1` to `node2`, either + * through a `ReturnNode` or through an argument that has been mutated, and + * that this step is part of a path from a source to a sink. The + * `allowsFieldFlow` flag indicates whether the branching is within the limit + * specified by the configuration. + */ + private predicate flowOutOfCallable(Node node1, Node node2, boolean allowsFieldFlow, Configuration config) { + flowOutOfCallable(node1, node2, config) and + exists(int b, int j | + b = branch(node1, config) and + j = join(node2, config) and + if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false + ) + } + + /** + * Holds if data can flow into a callable and that this step is part of a + * path from a source to a sink. The `allowsFieldFlow` flag indicates whether + * the branching is within the limit specified by the configuration. + */ + private predicate flowIntoCallable(Node node1, Node node2, boolean allowsFieldFlow, Configuration config) { + flowIntoCallable(node1, node2, config) and + exists(int b, int j | + b = branch(node1, config) and + j = join(node2, config) and + if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false + ) + } + + /** + * Holds if `node` is part of a path from a source to a sink in the given + * configuration taking simple call contexts into consideration. + */ + private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Configuration config) { + nodeCand1(node, false, config) and + config.isSource(node) and fromArg = false and stored = false + or + nodeCand1(node, unbindBool(stored), unbind(config)) and + ( + exists(Node mid, boolean preservesValue | + nodeCandFwd2(mid, fromArg, stored, config) and + localFlowStepOrFlowThroughMethod(mid, node, preservesValue, config) and + (stored = false or preservesValue = true) + ) + or + exists(Node mid, boolean preservesValue | + nodeCandFwd2(mid, _, stored, config) and + jumpStep(mid, node, preservesValue, config) and + fromArg = false and + (stored = false or preservesValue = true) + ) + or + // store + exists(Node mid, Content f | + nodeCandFwd2(mid, fromArg, _, config) and + store(mid, f, node) and + readCand1(f, unbind(config)) and + stored = true + ) + or + // read + exists(Node mid, Content f | + nodeCandFwd2(mid, fromArg, true, config) and + read(mid, f, node) and + storeCandFwd2(f, unbind(config)) and + (stored = false or stored = true) + ) + or + exists(Node mid, boolean allowsFieldFlow | + nodeCandFwd2(mid, _, stored, config) and + flowIntoCallable(mid, node, allowsFieldFlow, config) and + fromArg = true and + (stored = false or allowsFieldFlow = true) + ) + or + exists(Node mid, boolean allowsFieldFlow | + nodeCandFwd2(mid, false, stored, config) and + flowOutOfCallable(mid, node, allowsFieldFlow, config) and + fromArg = false and + (stored = false or allowsFieldFlow = true) + ) + ) + } + + /** + * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`. + */ + private predicate storeCandFwd2(Content f, Configuration config) { + exists(Node mid, Node node | + useFieldFlow(unbind(config)) and + nodeCand1(node, true, unbind(config)) and + nodeCandFwd2(mid, _, _, config) and + store(mid, f, node) and + readCand1(f, unbind(config)) + ) + } + + /** + * Holds if `node` is part of a path from a source to a sink in the given + * configuration taking simple call contexts into consideration. + */ + private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configuration config) { + nodeCandFwd2(node, _, false, config) and + config.isSink(node) and toReturn = false and stored = false + or + nodeCandFwd2(node, _, unbindBool(stored), unbind(config)) and + ( + exists(Node mid, boolean preservesValue | + localFlowStepOrFlowThroughMethod(node, mid, preservesValue, config) and + nodeCand2(mid, toReturn, stored, config) and + (stored = false or preservesValue = true) + ) + or + exists(Node mid, boolean preservesValue | + jumpStep(node, mid, preservesValue, config) and + nodeCand2(mid, _, stored, config) and + toReturn = false and + (stored = false or preservesValue = true) + ) + or + // store + exists(Node mid, Content f | + store(node, f, mid) and + readCand2(f, unbind(config)) and + nodeCand2(mid, toReturn, true, config) and + (stored = false or stored = true) + ) + or + // read + exists(Node mid, Content f | + read(node, f, mid) and + storeCandFwd2(f, unbind(config)) and + nodeCand2(mid, toReturn, _, config) and + stored = true + ) + or + exists(Node mid, boolean allowsFieldFlow | + flowIntoCallable(node, mid, allowsFieldFlow, config) and + nodeCand2(mid, false, stored, config) and + toReturn = false and + (stored = false or allowsFieldFlow = true) + ) + or + exists(Node mid, boolean allowsFieldFlow | + flowOutOfCallable(node, mid, allowsFieldFlow, config) and + nodeCand2(mid, _, stored, config) and + toReturn = true and + (stored = false or allowsFieldFlow = true) + ) + ) + } + + /** + * Holds if `f` is the target of a read in the flow covered by `nodeCand2`. + */ + private predicate readCand2(Content f, Configuration config) { + exists(Node mid, Node node | + useFieldFlow(unbind(config)) and + nodeCandFwd2(node, _, true, unbind(config)) and + read(node, f, mid) and + storeCandFwd2(f, unbind(config)) and + nodeCand2(mid, _, _, config) + ) + } + + private predicate storeCand(Content f, Configuration conf) { + exists(Node n1, Node n2 | + store(n1, f, n2) and + nodeCand2(n1, _, _, conf) and + nodeCand2(n2, _, _, unbind(conf)) + ) + } + + private predicate readCand(Content f, Configuration conf) { + readCand2(f, conf) + } + + /** + * Holds if `f` is the target of both a store and a read in the path graph + * covered by `nodeCand2`. + */ + pragma[noinline] + private predicate readStoreCand(Content f, Configuration conf) { + storeCand(f, conf) and + readCand(f, conf) + } + + private predicate nodeCand(Node node, Configuration config) { + nodeCand2(node, _, _, config) + } + + /** + * 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) { + nodeCand(node, config) and + ( + config.isSource(node) or + jumpStep(_, node, _, config) or + node instanceof ParameterNode or + node.asExpr() instanceof MethodAccess or + node instanceof PostUpdateNode or + read(_, _, node) or + node.asExpr() instanceof CastExpr + ) + } + + /** + * Holds if `node` can be the last node in a maximal subsequence of local + * flow steps in a dataflow path. + */ + private predicate localFlowExit(Node node, Configuration config) { + exists(Node next | + nodeCand(next, config) + | + jumpStep(node, next, _, config) or + flowIntoCallable(node, next, config) or + flowOutOfCallable(node, next, config) or + flowThroughMethod(node, next, _, config) or + store(node, _, next) or + read(node, _, next) + ) or + node.asExpr() instanceof CastExpr or + config.isSink(node) + } + + /** + * Holds if the local path from `node1` to `node2` is a prefix of a maximal + * subsequence of local flow steps in a dataflow path. + * + * This is the transitive closure of `localFlowStep` beginning at `localFlowEntry`. + */ + private predicate localFlowStepPlus(Node node1, Node node2, boolean preservesValue, Configuration config) { + localFlowEntry(node1, config) and + localFlowStep(node1, node2, preservesValue, config) and + node1 != node2 and + nodeCand(node2, unbind(config)) + or + exists(Node mid, boolean pv1, boolean pv2 | + localFlowStepPlus(node1, mid, pv1, config) and + localFlowStep(mid, node2, pv2, config) and + not mid.asExpr() instanceof CastExpr and + preservesValue = pv1.booleanAnd(pv2) and + nodeCand(node2, unbind(config)) + ) + } + + /** + * Holds if `node1` can step to `node2` in one or more local steps and this + * path can occur as a maximal subsequence of local steps in a dataflow path. + */ + pragma[noinline] + private predicate localFlowBigStep(Node node1, Node node2, boolean preservesValue, Configuration config) { + localFlowStepPlus(node1, node2, preservesValue, config) and + localFlowExit(node2, config) + } + + /** + * Holds if `f` may contain an object of the same type, `t`, as the one + * that contains `f`, and if this fact should be used to compress + * access paths. + * + * Examples include the tail pointer in a linked list or the left and right + * pointers in a binary tree. + */ + private predicate selfRef(Content f, RefType t) { + t = f.getDeclaringType() and + f.isSelfRef() + } + + private newtype TFlowContainer = + TRegularContent(Content f) { not selfRef(f, _) } or + TSelfRefContent(RefType t) { selfRef(_, t) } + + /** + * A `Content` or a `Content` abstracted as its declaring type. + * + * Sequences of one or more `Content`s in the same declaring type for which + * `isSelfRef()` holds are represented as a single `FlowContainer` in an + * `AccessPath`. + */ + private class FlowContainer extends TFlowContainer { + string toString() { + exists(Content f | this = TRegularContent(f) and result = f.toString()) or + exists(RefType t | this = TSelfRefContent(t) and result = t.toString()) + } + predicate usesContent(Content f) { + this = TRegularContent(f) or + exists(RefType t | this = TSelfRefContent(t) and selfRef(f, t)) + } + RefType getContainerType() { + exists(Content f | this = TRegularContent(f) and result = f.getDeclaringType()) or + this = TSelfRefContent(result) + } + } + + private newtype TAccessPathFront = TFrontNil(Type t) or TFrontHead(FlowContainer f) + + /** + * The front of an `AccessPath`. This is either a head or a nil. + */ + private class AccessPathFront extends TAccessPathFront { + string toString() { + exists(Type t | this = TFrontNil(t) | result = t.toString()) or + exists(FlowContainer f | this = TFrontHead(f) | result = f.toString()) + } + Type getType() { + this = TFrontNil(result) or + exists(FlowContainer head | this = TFrontHead(head) | result = head.getContainerType()) + } + predicate headUsesContent(Content f) { + exists(FlowContainer fc | + fc.usesContent(f) and + this = TFrontHead(fc) + ) + } + } + + private class AccessPathFrontNil extends AccessPathFront, TFrontNil { + } + + /** + * A `Node` at which a cast can occur such that the type should be checked. + */ + private class CastingNode extends Node { + CastingNode() { + this instanceof ParameterNode or + this.asExpr() instanceof CastExpr or + this.asExpr() instanceof MethodAccess or + this.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNode + } + } + + /** + * Holds if data can flow from a source to `node` with the given `apf`. + */ + private predicate flowCandFwd(Node node, boolean fromArg, AccessPathFront apf, Configuration config) { + flowCandFwd0(node, fromArg, apf, config) and + if node instanceof CastingNode then + compatibleTypes(node.getType(), apf.getType()) + else + any() + } + private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf, Configuration config) { + nodeCand2(node, _, false, config) and + config.isSource(node) and fromArg = false and apf = TFrontNil(getErasedRepr(node.getType())) + or + nodeCand(node, unbind(config)) and + ( + exists(Node mid | + flowCandFwd(mid, fromArg, apf, config) and + localFlowBigStep(mid, node, true, config) + ) + or + exists(Node mid, AccessPathFront apf0 | + flowCandFwd(mid, fromArg, apf0, config) and + localFlowBigStep(mid, node, false, config) and + apf0 instanceof AccessPathFrontNil and + apf = TFrontNil(getErasedRepr(node.getType())) + ) + or + exists(Node mid | + flowCandFwd(mid, _, apf, config) and + jumpStep(mid, node, true, config) and + fromArg = false + ) + or + exists(Node mid, AccessPathFront apf0 | + flowCandFwd(mid, _, apf0, config) and + jumpStep(mid, node, false, config) and + fromArg = false and + apf0 instanceof AccessPathFrontNil and + apf = TFrontNil(getErasedRepr(node.getType())) + ) + or + exists(Node mid, boolean allowsFieldFlow | + flowCandFwd(mid, _, apf, config) and + flowIntoCallable(mid, node, allowsFieldFlow, config) and + fromArg = true and + (apf instanceof AccessPathFrontNil or allowsFieldFlow = true) + ) + or + exists(Node mid, boolean allowsFieldFlow | + flowCandFwd(mid, false, apf, config) and + flowOutOfCallable(mid, node, allowsFieldFlow, config) and + fromArg = false and + (apf instanceof AccessPathFrontNil or allowsFieldFlow = true) + ) + or + exists(Node mid, boolean preservesValue, AccessPathFront apf0 | + flowCandFwd(mid, fromArg, apf0, config) and + flowThroughMethod(mid, node, preservesValue, config) and + (preservesValue = true and apf = apf0 or preservesValue = false and apf0 instanceof AccessPathFrontNil and apf = TFrontNil(getErasedRepr(node.getType()))) + ) + ) + or + exists(Node mid, Content f | + flowCandFwd(mid, fromArg, _, config) and + store(mid, f, node) and + nodeCand(node, unbind(config)) and + apf.headUsesContent(f) + ) + or + exists(Node mid, Content f, AccessPathFront apf0 | + flowCandFwd(mid, fromArg, apf0, config) and + read(mid, f, node) and + nodeCand(node, config) and + apf0.headUsesContent(f) and + consCandFwd(f, apf, unbind(config)) + ) + } + private predicate consCandFwd(Content f, AccessPathFront apf, Configuration config) { + exists(Node mid, Node n | + flowCandFwd(mid, _, apf, config) and + store(mid, f, n) and + nodeCand(n, unbind(config)) and + readStoreCand(f, unbind(config)) and + compatibleTypes(apf.getType(), f.getType()) + ) + } + + /** + * Holds if data can flow from a source to `node` with the given `apf` and + * from there flow to a sink. + */ + private predicate flowCand(Node node, boolean toReturn, AccessPathFront apf, Configuration config) { + flowCand0(node, toReturn, apf, config) and + flowCandFwd(node, _, apf, config) + } + private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Configuration config) { + flowCandFwd(node, _, apf, config) and + config.isSink(node) and toReturn = false and apf instanceof AccessPathFrontNil + or + ( + exists(Node mid | + localFlowBigStep(node, mid, true, config) and + flowCand(mid, toReturn, apf, config) + ) + or + exists(Node mid, AccessPathFront apf0 | + flowCandFwd(node, _, apf, config) and + localFlowBigStep(node, mid, false, config) and + flowCand(mid, toReturn, apf0, config) and + apf0 instanceof AccessPathFrontNil and + apf instanceof AccessPathFrontNil + ) + or + exists(Node mid | + jumpStep(node, mid, true, config) and + flowCand(mid, _, apf, config) and + toReturn = false + ) + or + exists(Node mid, AccessPathFront apf0 | + flowCandFwd(node, _, apf, config) and + jumpStep(node, mid, false, config) and + flowCand(mid, _, apf0, config) and + toReturn = false and + apf0 instanceof AccessPathFrontNil and + apf instanceof AccessPathFrontNil + ) + or + exists(Node mid, boolean allowsFieldFlow | + flowIntoCallable(node, mid, allowsFieldFlow, config) and + flowCand(mid, false, apf, config) and + toReturn = false and + (apf instanceof AccessPathFrontNil or allowsFieldFlow = true) + ) + or + exists(Node mid, boolean allowsFieldFlow | + flowOutOfCallable(node, mid, allowsFieldFlow, config) and + flowCand(mid, _, apf, config) and + toReturn = true and + (apf instanceof AccessPathFrontNil or allowsFieldFlow = true) + ) + or + exists(Node mid, boolean preservesValue, AccessPathFront apf0 | + flowThroughMethod(node, mid, preservesValue, config) and + flowCand(mid, toReturn, apf0, config) and + (preservesValue = true and apf = apf0 or preservesValue = false and apf0 instanceof AccessPathFrontNil and apf instanceof AccessPathFrontNil and flowCandFwd(node, _, apf, config)) + ) + or + exists(Node mid, Content f, AccessPathFront apf0 | + store(node, f, mid) and + flowCand(mid, toReturn, apf0, config) and + apf0.headUsesContent(f) and + consCand(f, apf, unbind(config)) + ) + or + exists(Node mid, Content f, AccessPathFront apf0 | + read(node, f, mid) and + flowCand(mid, toReturn, apf0, config) and + consCandFwd(f, apf0, unbind(config)) and + apf.headUsesContent(f) + ) + ) + } + private predicate consCand(Content f, AccessPathFront apf, Configuration config) { + consCandFwd(f, apf, config) and + exists(Node mid, Node n, AccessPathFront apf0 | + flowCandFwd(n, _, apf0, config) and + apf0.headUsesContent(f) and + read(n, f, mid) and + flowCand(mid, _, apf, config) + ) + } + + private newtype TAccessPath = TNil(Type t) or TCons(FlowContainer f, int len) { len in [1..5] } + + /** + * Conceptually a list of `Content`s followed by a `Type`, but only the first + * element of the list and its length are tracked. If data flows from a source to + * a given node with a given `AccessPath`, this indicates the sequence of + * dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ + private class AccessPath extends TAccessPath { + abstract string toString(); + FlowContainer getHead() { this = TCons(result, _) } + int len() { + this = TNil(_) and result = 0 + or + this = TCons(_, result) + } + Type getType() { + this = TNil(result) or + exists(FlowContainer head | this = TCons(head, _) | result = head.getContainerType()) + } + abstract AccessPathFront getFront(); + } + + private class AccessPathNil extends AccessPath, TNil { + override string toString() { exists(Type t | this = TNil(t) | result = t.toString()) } + override AccessPathFront getFront() { exists(Type t | this = TNil(t) | result = TFrontNil(t)) } + } + + private class AccessPathCons extends AccessPath, TCons { + override string toString() { + exists(FlowContainer f, int len | this = TCons(f, len) | result = f.toString() + ", ... (" + len.toString() + ")") + } + override AccessPathFront getFront() { exists(FlowContainer f | this = TCons(f, _) | result = TFrontHead(f)) } + } + + /** Holds if `ap0` corresponds to the cons of `f` and `ap`. */ + private predicate pop(AccessPath ap0, Content f, AccessPath ap) { + ap0.getFront().headUsesContent(f) and + consCand(f, ap.getFront(), _) and + ap0.len() = 1 + ap.len() + } + + /** Holds if `ap0` corresponds to the cons of `f` and `ap` and `apf` is the front of `ap`. */ + pragma[noinline] + private predicate popWithFront(AccessPath ap0, Content f, AccessPathFront apf, AccessPath ap) { + pop(ap0, f, ap) and apf = ap.getFront() + } + + /** Holds if `ap` corresponds to the cons of `f` and `ap0`. */ + private predicate push(AccessPath ap0, Content f, AccessPath ap) { pop(ap, f, ap0) } + + /** + * Holds if data can flow from a source to `node` with the given `ap`. + */ + private predicate flowFwd(Node node, boolean fromArg, AccessPathFront apf, AccessPath ap, Configuration config) { + flowFwd0(node, fromArg, apf, ap, config) and + flowCand(node, _, apf, config) + } + private predicate flowFwd0(Node node, boolean fromArg, AccessPathFront apf, AccessPath ap, Configuration config) { + flowCand(node, _, _, config) and + config.isSource(node) and fromArg = false and ap = TNil(getErasedRepr(node.getType())) and apf = ap.(AccessPathNil).getFront() + or + flowCand(node, _, _, unbind(config)) and + ( + exists(Node mid | + flowFwd(mid, fromArg, apf, ap, config) and + localFlowBigStep(mid, node, true, config) + ) + or + exists(Node mid, AccessPath ap0 | + flowFwd(mid, fromArg, _, ap0, config) and + localFlowBigStep(mid, node, false, config) and + ap0 instanceof AccessPathNil and + ap = TNil(getErasedRepr(node.getType())) and + apf = ap.(AccessPathNil).getFront() + ) + or + exists(Node mid | + flowFwd(mid, _, apf, ap, config) and + jumpStep(mid, node, true, config) and + fromArg = false + ) + or + exists(Node mid, AccessPath ap0 | + flowFwd(mid, _, _, ap0, config) and + jumpStep(mid, node, false, config) and + fromArg = false and + ap0 instanceof AccessPathNil and + ap = TNil(getErasedRepr(node.getType())) and + apf = ap.(AccessPathNil).getFront() + ) + or + exists(Node mid, boolean allowsFieldFlow | + flowFwd(mid, _, apf, ap, config) and + flowIntoCallable(mid, node, allowsFieldFlow, config) and + fromArg = true and + (ap instanceof AccessPathNil or allowsFieldFlow = true) + ) + or + exists(Node mid, boolean allowsFieldFlow | + flowFwd(mid, false, apf, ap, config) and + flowOutOfCallable(mid, node, allowsFieldFlow, config) and + fromArg = false and + (ap instanceof AccessPathNil or allowsFieldFlow = true) + ) + or + exists(Node mid, boolean preservesValue, AccessPathFront apf0, AccessPath ap0 | + flowFwd(mid, fromArg, apf0, ap0, config) and + flowThroughMethod(mid, node, preservesValue, config) and + (preservesValue = true and ap = ap0 and apf = apf0 or preservesValue = false and ap0 instanceof AccessPathNil and ap = TNil(getErasedRepr(node.getType())) and apf = ap.(AccessPathNil).getFront()) + ) + ) + or + exists(Content f, AccessPath ap0 | + flowFwdStore(node, f, ap0, apf, fromArg, config) and + push(ap0, f, ap) + ) + or + exists(Content f, AccessPath ap0 | + flowFwdRead(node, f, ap0, fromArg, config) and + popWithFront(ap0, f, apf, ap) + ) + } + pragma[nomagic] + private predicate flowFwdStore(Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg, Configuration config) { + exists(Node mid, AccessPathFront apf0 | + flowFwd(mid, fromArg, apf0, ap0, config) and + flowFwdStoreAux(mid, f, node, apf0, apf, config) + ) + } + private predicate flowFwdStoreAux(Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFront apf, Configuration config) { + store(mid, f, node) and + consCand(f, apf0, config) and + apf.headUsesContent(f) and + flowCand(node, _, apf, unbind(config)) + } + pragma[nomagic] + private predicate flowFwdRead(Node node, Content f, AccessPath ap0, boolean fromArg, Configuration config) { + exists(Node mid, AccessPathFront apf0 | + flowFwd(mid, fromArg, apf0, ap0, config) and + read(mid, f, node) and + apf0.headUsesContent(f) and + flowCand(node, _, _, unbind(config)) + ) + } + + /** + * Holds if data can flow from a source to `node` with the given `ap` and + * from there flow to a sink. + */ + private predicate flow(Node node, boolean toReturn, AccessPath ap, Configuration config) { + flow0(node, toReturn, ap, config) and + flowFwd(node, _, _, ap, config) + } + private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuration config) { + flowFwd(node, _, _, ap, config) and + config.isSink(node) and toReturn = false and ap instanceof AccessPathNil + or + ( + exists(Node mid | + localFlowBigStep(node, mid, true, config) and + flow(mid, toReturn, ap, config) + ) + or + exists(Node mid, AccessPath ap0 | + flowFwd(node, _, _, ap, config) and + localFlowBigStep(node, mid, false, config) and + flow(mid, toReturn, ap0, config) and + ap0 instanceof AccessPathNil and + ap instanceof AccessPathNil + ) + or + exists(Node mid | + jumpStep(node, mid, true, config) and + flow(mid, _, ap, config) and + toReturn = false + ) + or + exists(Node mid, AccessPath ap0 | + flowFwd(node, _, _, ap, config) and + jumpStep(node, mid, false, config) and + flow(mid, _, ap0, config) and + toReturn = false and + ap0 instanceof AccessPathNil and + ap instanceof AccessPathNil + ) + or + exists(Node mid, boolean allowsFieldFlow | + flowIntoCallable(node, mid, allowsFieldFlow, config) and + flow(mid, false, ap, config) and + toReturn = false and + (ap instanceof AccessPathNil or allowsFieldFlow = true) + ) + or + exists(Node mid, boolean allowsFieldFlow | + flowOutOfCallable(node, mid, allowsFieldFlow, config) and + flow(mid, _, ap, config) and + toReturn = true and + (ap instanceof AccessPathNil or allowsFieldFlow = true) + ) + or + exists(Node mid, boolean preservesValue, AccessPath ap0 | + flowThroughMethod(node, mid, preservesValue, config) and + flow(mid, toReturn, ap0, config) and + (preservesValue = true and ap = ap0 or preservesValue = false and ap0 instanceof AccessPathNil and ap instanceof AccessPathNil and flowFwd(node, _, _, ap, config)) + ) + or + exists(Content f, AccessPath ap0 | + flowStore(node, f, toReturn, ap0, config) and + pop(ap0, f, ap) + ) + or + exists(Content f, AccessPath ap0 | + flowRead(node, f, toReturn, ap0, config) and + push(ap0, f, ap) + ) + ) + } + pragma[nomagic] + private predicate flowStore(Node node, Content f, boolean toReturn, AccessPath ap0, Configuration config) { + exists(Node mid | + store(node, f, mid) and + flow(mid, toReturn, ap0, config) + ) + } + pragma[nomagic] + private predicate flowRead(Node node, Content f, boolean toReturn, AccessPath ap0, Configuration config) { + exists(Node mid | + read(node, f, mid) and + flow(mid, toReturn, ap0, config) + ) + } + + private bindingset[conf, result] Configuration unbind(Configuration conf) { result >= conf and result <= conf } + + private predicate flow(Node n, Configuration config) { + flow(n, _, _, config) + } + + private newtype TPathNode = + TPathNodeMid(Node node, CallContext cc, AccessPath ap, Configuration config) { + // A PathNode is introduced by a source ... + flow(node, config) and + config.isSource(node) and + cc instanceof CallContextAny and + ap = TNil(getErasedRepr(node.getType())) + or + // ... or a step from an existing PathNode to another node. + exists(PathNodeMid mid | + flowStep(mid, node, cc, ap) and + config = mid.getConfiguration() and + flow(node, _, ap, unbind(config)) + ) + } or + TPathNodeSink(Node node, Configuration config) { // The AccessPath on a sink is empty. + config.isSink(node) and + flow(node, config) + } + + /** + * 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. + */ + abstract class PathNode extends TPathNode { + /** Gets a textual representation of this element. */ + string toString() { result = getNode().toString() + ppAp() } + /** Gets the source location for this element. */ + Location getLocation() { result = getNode().getLocation() } + /** Gets the underlying `Node`. */ + abstract Node getNode(); + /** Gets the associated configuration. */ + abstract Configuration getConfiguration(); + /** Gets a successor. */ + abstract PathNode getSucc(); + private string ppAp() { + this instanceof PathNodeSink and result = "" or + result = " [" + this.(PathNodeMid).getAp().toString() + "]" + } + } + + /** Holds if `n` can reach a sink. */ + private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getSucc()) } + + /** Holds if `n1.getSucc() = n2` and `n2` can reach a sink. */ + private predicate pathSucc(PathNode n1, PathNode n2) { n1.getSucc() = n2 and reach(n2) } + + private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2) + + /** + * Provides the query predicates needed to include a graph in a path-problem query. + */ + module PathGraph { + /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ + query predicate edges(PathNode a, PathNode b) { pathSucc(a, b) } + } + + /** + * An intermediate flow graph node. This is a triple consisting of a `Node`, + * a `CallContext`, and a `Configuration`. + */ + private class PathNodeMid extends PathNode, TPathNodeMid { + Node node; + CallContext cc; + AccessPath ap; + Configuration config; + PathNodeMid() { this = TPathNodeMid(node, cc, ap, config) } + override Node getNode() { result = node } + CallContext getCallContext() { result = cc } + AccessPath getAp() { result = ap } + override Configuration getConfiguration() { result = config } + private PathNodeMid getSuccMid() { + flowStep(this, result.getNode(), result.getCallContext(), result.getAp()) and + result.getConfiguration() = unbind(this.getConfiguration()) + } + override PathNode getSucc() { + // an intermediate step to another intermediate node + result = getSuccMid() + or + // a final step to a sink via one or more local steps + localFlowStepPlus(node, result.getNode(), _, config) and + ap instanceof AccessPathNil and + result instanceof PathNodeSink and + result.getConfiguration() = unbind(this.getConfiguration()) + or + // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges + exists(PathNodeMid mid | + mid = getSuccMid() and + mid.getNode() = result.getNode() and + mid.getAp() instanceof AccessPathNil and + result instanceof PathNodeSink and + result.getConfiguration() = unbind(mid.getConfiguration()) + ) + or + // a direct step from a source to a sink if a node is both + this instanceof PathNodeSource and + result instanceof PathNodeSink and + this.getNode() = result.getNode() and + result.getConfiguration() = unbind(this.getConfiguration()) + } + } + + /** + * A flow graph node corresponding to a source. + */ + private class PathNodeSource extends PathNodeMid { + PathNodeSource() { + getConfiguration().isSource(getNode()) and + getCallContext() instanceof CallContextAny and + getAp() instanceof AccessPathNil + } + } + + /** + * A flow graph node corresponding to a sink. This is disjoint from the + * intermediate nodes in order to uniquely correspond to a given sink by + * excluding the `CallContext`. + */ + private class PathNodeSink extends PathNode, TPathNodeSink { + Node node; + Configuration config; + PathNodeSink() { this = TPathNodeSink(node, config) } + override Node getNode() { result = node } + override Configuration getConfiguration() { result = config } + override PathNode getSucc() { none() } + } + + /** + * Holds if data may flow from `mid` to `node`. The last step in or out of + * a callable is recorded by `cc`. + */ + private predicate flowStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) { + localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and + cc = mid.getCallContext() and + ap = mid.getAp() + or + localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration()) and + cc = mid.getCallContext() and + mid.getAp() instanceof AccessPathNil and + ap = TNil(getErasedRepr(node.getType())) + or + jumpStep(mid.getNode(), node, true, mid.getConfiguration()) and + cc instanceof CallContextAny and + ap = mid.getAp() + or + jumpStep(mid.getNode(), node, false, mid.getConfiguration()) and + cc instanceof CallContextAny and + mid.getAp() instanceof AccessPathNil and + ap = TNil(getErasedRepr(node.getType())) + or + contentReadStep(mid, node, ap) and cc = mid.getCallContext() + or + exists(Content f, AccessPath ap0 | contentStoreStep(mid, node, ap0, f, cc) and push(ap0, f, ap)) + or + flowOutOfArgument(mid, node, cc) and ap = mid.getAp() + or + flowIntoCallable(mid, node, _, cc, _) and ap = mid.getAp() + or + flowOutOfMethod(mid, node.asExpr(), cc) and ap = mid.getAp() + or + flowThroughMethod(mid, node.asExpr(), cc) and ap = TNil(getErasedRepr(node.getType())) + or + valueFlowThroughMethod(mid, node.asExpr(), cc) and ap = mid.getAp() + } + + private predicate contentReadStep(PathNodeMid mid, Node node, AccessPath ap) { + exists(Content f, AccessPath ap0 | + ap0 = mid.getAp() and + read(mid.getNode(), f, node) and + pop(ap0, f, ap) + ) + } + + pragma[noinline] + private predicate contentStoreStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) { + ap0 = mid.getAp() and + store(mid.getNode(), f, node) and + cc = mid.getCallContext() + } + + /** + * Holds if data may flow from `mid` to an exit of `m` in the context + * `innercc`, and the path did not flow through a parameter of `m`. + */ + private predicate flowOutOfMethod0(PathNodeMid mid, Method m, CallContext innercc) { + exists(ReturnNode ret | + ret = mid.getNode() and + innercc = mid.getCallContext() and + m = returnNodeGetEnclosingCallable(ret) and + not innercc instanceof CallContextCall + ) + } + + /** + * Holds if data may flow from `mid` to `ma`. The last step of this path + * is a return from a method and is recorded by `cc`, if needed. + */ + pragma[noinline] + private predicate flowOutOfMethod(PathNodeMid mid, MethodAccess ma, CallContext cc) { + exists(Method m, CallContext innercc | + flowOutOfMethod0(mid, m, innercc) and + resolveReturn(innercc, m, ma) + | + if reducedViableImplInReturn(m, ma) then cc = TReturn(m, ma) else cc = TAnyCallContext() + ) + } + + private predicate flowOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallContext cc) { + exists(PostUpdateNode n, ParameterNode p, Callable callable, CallContext innercc, int i, Call call, ArgumentNode arg | + mid.getNode() = n and + parameterValueFlowsToUpdate(p, n) and + innercc = mid.getCallContext() and + p.isParameterOf(callable, i) and + resolveReturn(innercc, callable, call) and + node.getPreUpdateNode() = arg and + arg.argumentOf(call, i) and + flow(node, unbind(mid.getConfiguration())) + | + if reducedViableImplInReturn(callable, call) then cc = TReturn(callable, call) else cc = TAnyCallContext() + ) + } + + /** + * Holds if data may flow from `mid` to the `i`th argument of `call` in `cc`. + */ + pragma[noinline] + private predicate flowIntoArg(PathNodeMid mid, int i, CallContext cc, Call call, boolean emptyAp) { + exists(ArgumentNode arg, AccessPath ap | + arg = mid.getNode() and + cc = mid.getCallContext() and + arg.argumentOf(call, i) and + ap = mid.getAp() + | + ap instanceof AccessPathNil and emptyAp = true or + ap instanceof AccessPathCons and emptyAp = false + ) + } + + pragma[noinline] + private predicate parameterCand(Callable callable, int i, Configuration config) { + exists(ParameterNode p | + flow(p, config) and + p.isParameterOf(callable, i) + ) + } + + pragma[nomagic] + private predicate flowIntoCallable0(PathNodeMid mid, Callable callable, int i, CallContext outercc, Call call, boolean emptyAp) { + flowIntoArg(mid, i, outercc, call, emptyAp) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), mid.getConfiguration()) + } + + /** + * Holds if data may flow from `mid` to `p` through `call`. The contexts + * before and after entering the callable are `outercc` and `innercc`, + * respectively. + */ + private predicate flowIntoCallable(PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, Call call) { + exists(int i, Callable callable, boolean emptyAp | + flowIntoCallable0(mid, callable, i, outercc, call, emptyAp) and + p.isParameterOf(callable, i) + | + if reducedViableImplInCallContext(_, callable, call) then innercc = TSpecificCall(call, i, emptyAp) else innercc = TSomeCall(p, emptyAp) + ) + } + + /** Holds if data may flow from `p` to a return statement in the callable. */ + pragma[nomagic] + private predicate paramFlowsThrough(ParameterNode p, CallContextCall cc, Configuration config) { + exists(PathNodeMid mid, ReturnNode ret | + mid.getNode() = ret and + cc = mid.getCallContext() and + config = mid.getConfiguration() and + mid.getAp() instanceof AccessPathNil + | + cc = TSomeCall(p, true) or + exists(int i | cc = TSpecificCall(_, i, true) | + p.isParameterOf(returnNodeGetEnclosingCallable(ret), i) + ) + ) + } + + /** + * Holds if data may flow from `mid` to an argument of `methodcall`, + * through a called method `m`, and back out through a return statement in + * `m`. The context `cc` is restored to its value prior to entering `m`. + */ + pragma[noinline] + private predicate flowThroughMethod(PathNodeMid mid, Call methodcall, CallContext cc) { + exists(ParameterNode p, CallContext innercc | + flowIntoCallable(mid, p, cc, innercc, methodcall) and + paramFlowsThrough(p, innercc, unbind(mid.getConfiguration())) and + not parameterValueFlowsThrough(p) and + mid.getAp() instanceof AccessPathNil + ) + } + + private predicate valueFlowThroughMethod(PathNodeMid mid, Call methodcall, CallContext cc) { + exists(ParameterNode p | + flowIntoCallable(mid, p, cc, _, methodcall) and + parameterValueFlowsThrough(p) + ) + } + + /** + * Holds if data can flow (inter-procedurally) from `source` to `sink`. + * + * Will only have results if `configuration` has non-empty sources and + * sinks. + */ + private predicate flowsTo(PathNodeSource flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration) { + flowsource.getConfiguration() = configuration and + flowsource.getNode() = source and + pathSuccPlus(flowsource, flowsink) and + flowsink.getNode() = sink + } + + /** + * Holds if data can flow (inter-procedurally) from `source` to `sink`. + * + * Will only have results if `configuration` has non-empty sources and + * sinks. + */ + predicate flowsTo(Node source, Node sink, Configuration configuration) { + flowsTo(_, _, source, sink, configuration) + } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll new file mode 100644 index 000000000000..142ea7f8a7e9 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll @@ -0,0 +1,1536 @@ +/** + * Provides an implementation of global (interprocedural) data flow. This file + * re-exports the local (intraprocedural) data flow analysis from `DataFlowUtil` + * and adds a global analysis, mainly exposed through the `Configuration` class. + * This file exists in several identical copies, allowing queries to use + * multiple `Configuration` classes that depend on each other without + * introducing mutual recursion among those configurations. + */ + + import DataFlowUtil + private import DataFlowPrivate + private import DataFlowDispatch + private import DataFlowImplCommon + + + /** + * A configuration of interprocedural data flow analysis. This defines + * sources, sinks, and any other configurable aspect of the analysis. Each + * use of the global data flow library must define its own unique extension + * of this abstract class. To create a configuration, extend this class with + * a subclass whose characteristic predicate is a unique singleton string. + * For example, write + * + * ``` + * class MyAnalysisConfiguration extends DataFlow::Configuration { + * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } + * // Override `isSource` and `isSink`. + * // Optionally override `isBarrier`. + * // Optionally override `isAdditionalFlowStep`. + * } + * ``` + * + * Then, to query whether there is flow between some `source` and `sink`, + * write + * + * ``` + * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) + * ``` + * + * Multiple configurations can coexist, but two classes extending + * `DataFlow::Configuration` should never depend on each other. One of them + * should instead depend on a `DataFlow2::Configuration`, a + * `DataFlow3::Configuration`, or a `DataFlow4::Configuration`. + */ + abstract class Configuration extends string { + bindingset[this] + Configuration() { any() } + + /** + * Holds if `source` is a relevant data flow source. + */ + abstract predicate isSource(Node source); + + /** + * Holds if `sink` is a relevant data flow sink. + */ + abstract predicate isSink(Node sink); + + /** Holds if data flow through `node` is prohibited. */ + predicate isBarrier(Node node) { none() } + + /** Holds if data flow from `node1` to `node2` is prohibited. */ + predicate isBarrierEdge(Node node1, Node node2) { none() } + + /** + * Holds if the additional flow step from `node1` to `node2` must be taken + * into account in the analysis. + */ + predicate isAdditionalFlowStep(Node node1, Node node2) { none() } + + /** + * Gets the virtual dispatch branching limit when calculating field flow. + * This can be overridden to a smaller value to improve performance (a + * value of 0 disables field flow), or a larger value to get more results. + */ + int fieldFlowBranchLimit() { result = 2 } + + /** + * Holds if data may flow from `source` to `sink` for this configuration. + */ + predicate hasFlow(Node source, Node sink) { + flowsTo(source, sink, this) + } + + /** + * Holds if data may flow from `source` to `sink` for this configuration. + * + * The corresponding paths are generated from the end-points and the graph + * included in the module `PathGraph`. + */ + predicate hasFlowPath(PathNode source, PathNode sink) { + flowsTo(source, sink, _, _, this) + } + + /** + * Holds if data may flow from some source to `sink` for this configuration. + */ + predicate hasFlowTo(Node sink) { + hasFlow(_, sink) + } + + /** + * Holds if data may flow from some source to `sink` for this configuration. + */ + predicate hasFlowToExpr(Expr sink) { + hasFlowTo(exprNode(sink)) + } + + /** DEPRECATED: use `hasFlow` instead. */ + deprecated predicate hasFlowForward(Node source, Node sink) { + hasFlow(source, sink) + } + + /** DEPRECATED: use `hasFlow` instead. */ + deprecated predicate hasFlowBackward(Node source, Node sink) { + hasFlow(source, sink) + } + } + + /** + * Holds if the additional step from `node1` to `node2` jumps between callables. + */ + private predicate additionalJumpStep(Node node1, Node node2, Configuration config) { + config.isAdditionalFlowStep(node1, node2) and + node1.getEnclosingCallable() != node2.getEnclosingCallable() + } + + pragma[noinline] + private predicate isAdditionalFlowStep(Node node1, Node node2, Callable callable1, Callable callable2, Configuration config) { + config.isAdditionalFlowStep(node1, node2) and + callable1 = node1.getEnclosingCallable() and + callable2 = node2.getEnclosingCallable() + } + + /** + * Holds if the additional step from `node1` to `node2` does not jump between callables. + */ + private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) { + exists(Callable callable | + isAdditionalFlowStep(node1, node2, callable, callable, config) + ) + } + + /** + * Holds if data can flow from `node1` to `node2` through a static field or + * variable capture. + */ + private predicate jumpStep(Node node1, Node node2, boolean preservesValue, Configuration config) { + jumpStep(node1, node2) and preservesValue = true or + additionalJumpStep(node1, node2, config) and preservesValue = false + } + + /** + * Holds if data can flow in one local step from `node1` to `node2` taking + * additional steps from the configuration into account. + */ + private predicate localFlowStep(Node node1, Node node2, boolean preservesValue, Configuration config) { + localFlowStep(node1, node2) and not config.isBarrierEdge(node1, node2) and preservesValue = true or + additionalLocalFlowStep(node1, node2, config) and preservesValue = false + } + + pragma[noinline] + private Method returnNodeGetEnclosingCallable(ReturnNode ret) { + result = ret.getEnclosingCallable() + } + + /** + * Holds if field flow should be used for the given configuration. + */ + private predicate useFieldFlow(Configuration config) { + config.fieldFlowBranchLimit() >= 1 + } + + /** + * Holds if `node` is reachable from a source in the given configuration + * ignoring call contexts. + */ + private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) { + not config.isBarrier(node) and + ( + config.isSource(node) and stored = false + or + exists(Node mid, boolean preservesValue | + nodeCandFwd1(mid, stored, config) and + localFlowStep(mid, node, preservesValue, config) and + (stored = false or preservesValue = true) + ) + or + exists(Node mid, boolean preservesValue | + nodeCandFwd1(mid, stored, config) and + jumpStep(mid, node, preservesValue, config) and + (stored = false or preservesValue = true) + ) + or + // store + exists(Node mid | + useFieldFlow(config) and + nodeCandFwd1(mid, _, config) and + store(mid, _, node) and + stored = true + ) + or + // read + exists(Node mid, Content f | + nodeCandFwd1(mid, true, config) and + read(mid, f, node) and + storeCandFwd1(f, unbind(config)) and + (stored = false or stored = true) + ) + or + // flow into a callable + exists(Node arg | + nodeCandFwd1(arg, stored, config) and + viableParamArg(node, arg) + ) + or + // flow out of an argument + exists(PostUpdateNode mid, ParameterNode p | + nodeCandFwd1(mid, stored, config) and + parameterValueFlowsToUpdate(p, mid) and + viableParamArg(p, node.(PostUpdateNode).getPreUpdateNode()) + ) + or + // flow out of a callable + exists(Method m, MethodAccess ma, ReturnNode ret | + nodeCandFwd1(ret, stored, config) and + m = returnNodeGetEnclosingCallable(ret) and + m = viableImpl(ma) and + node.asExpr() = ma + ) + ) + } + + /** + * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`. + */ + private predicate storeCandFwd1(Content f, Configuration config) { + exists(Node mid, Node node | + not config.isBarrier(node) and + useFieldFlow(unbind(config)) and + nodeCandFwd1(mid, _, config) and + store(mid, f, node) + ) + } + + bindingset[result, b] + private boolean unbindBool(boolean b) { result != b.booleanNot() } + + /** + * Holds if `node` is part of a path from a source to a sink in the given + * configuration ignoring call contexts. + */ + pragma[nomagic] + private predicate nodeCand1(Node node, boolean stored, Configuration config) { + nodeCandFwd1(node, false, config) and + config.isSink(node) and stored = false + or + nodeCandFwd1(node, unbindBool(stored), unbind(config)) and + ( + exists(Node mid, boolean preservesValue | + localFlowStep(node, mid, preservesValue, config) and + nodeCand1(mid, stored, config) and + (stored = false or preservesValue = true) + ) + or + exists(Node mid, boolean preservesValue | + jumpStep(node, mid, preservesValue, config) and + nodeCand1(mid, stored, config) and + (stored = false or preservesValue = true) + ) + or + // store + exists(Node mid, Content f | + store(node, f, mid) and + readCand1(f, unbind(config)) and + nodeCand1(mid, true, config) and + (stored = false or stored = true) + ) + or + // read + exists(Node mid, Content f | + read(node, f, mid) and + storeCandFwd1(f, unbind(config)) and + nodeCand1(mid, _, config) and + stored = true + ) + or + // flow into a callable + exists(Node param | + viableParamArg(param, node) and + nodeCand1(param, stored, config) + ) + or + // flow out of an argument + exists(PostUpdateNode mid, ParameterNode p | + parameterValueFlowsToUpdate(p, node) and + viableParamArg(p, mid.getPreUpdateNode()) and + nodeCand1(mid, stored, config) + ) + or + // flow out of a callable + exists(Method m, ExprNode ma | + nodeCand1(ma, stored, config) and + m = returnNodeGetEnclosingCallable(node) and + m = viableImpl(ma.getExpr()) + ) + ) + } + + /** + * Holds if `f` is the target of a read in the flow covered by `nodeCand1`. + */ + private predicate readCand1(Content f, Configuration config) { + exists(Node mid, Node node | + useFieldFlow(unbind(config)) and + nodeCandFwd1(node, true, unbind(config)) and + read(node, f, mid) and + storeCandFwd1(f, unbind(config)) and + nodeCand1(mid, _, config) + ) + } + + /** + * Holds if there is a path from `p` to `node` in the same callable that is + * part of a path from a source to a sink taking simple call contexts into + * consideration. This is restricted to paths that does not necessarily + * preserve the value of `p` by making use of at least one additional step + * from the configuration. + */ + pragma[nomagic] + private predicate simpleParameterFlow(ParameterNode p, Node node, RefType t, Configuration config) { + nodeCand1(node, false, config) and + p = node and + t = getErasedRepr(node.getType()) and + not parameterValueFlowsThrough(p) + or + nodeCand1(node, false, unbind(config)) and + exists(Node mid | + simpleParameterFlow(p, mid, t, config) and + localFlowStep(mid, node, true, config) and + compatibleTypes(t, node.getType()) + ) + or + nodeCand1(node, false, unbind(config)) and + exists(Node mid | + simpleParameterFlow(p, mid, _, config) and + localFlowStep(mid, node, false, config) and + t = getErasedRepr(node.getType()) + ) + or + nodeCand1(node, false, unbind(config)) and + exists(Node mid | + simpleParameterFlow(p, mid, t, config) and + localStoreReadStep(mid, node) and + compatibleTypes(t, node.getType()) + ) + or + // value flow through a callable + nodeCand1(node, false, config) and + exists(Node arg | + simpleParameterFlow(p, arg, t, config) and + argumentValueFlowsThrough(arg, node) and + compatibleTypes(t, node.getType()) + ) + or + // flow through a callable + nodeCand1(node, false, config) and + exists(Node arg | + simpleParameterFlow(p, arg, _, config) and + simpleArgumentFlowsThrough(arg, node, t, config) + ) + } + + /** + * Holds if data can flow from `arg` through the `call` taking simple call + * contexts into consideration and that this is part of a path from a source + * to a sink. This is restricted to paths through the `call` that does not + * necessarily preserve the value of `arg` by making use of at least one + * additional step from the configuration. + */ + private predicate simpleArgumentFlowsThrough(ArgumentNode arg, ExprNode call, RefType t, Configuration config) { + exists(ParameterNode param, ReturnNode ret | + nodeCand1(arg, false, unbind(config)) and + nodeCand1(call, false, unbind(config)) and + viableParamArg(param, arg) and + simpleParameterFlow(param, ret, t, config) and + arg.argumentOf(call.getExpr(), _) + ) + } + + /** + * Holds if data can flow from `node1` to `node2` by a step through a method. + */ + private predicate flowThroughMethod(Node node1, Node node2, boolean preservesValue, Configuration config) { + simpleArgumentFlowsThrough(node1, node2, _, config) and preservesValue = false or + argumentValueFlowsThrough(node1, node2) and preservesValue = true + } + + /** + * Holds if data can flow from `node1` to `node2` in one local step or a step + * through a method. + */ + private predicate localFlowStepOrFlowThroughMethod(Node node1, Node node2, boolean preservesValue, Configuration config) { + localFlowStep(node1, node2, preservesValue, config) or + flowThroughMethod(node1, node2, preservesValue, config) + } + + /** + * Holds if data can flow out of a callable from `node1` to `node2`, either + * through a `ReturnNode` or through an argument that has been mutated, and + * that this step is part of a path from a source to a sink. + */ + private predicate flowOutOfCallable(Node node1, Node node2, Configuration config) { + nodeCand1(node1, _, unbind(config)) and + nodeCand1(node2, _, config) and + ( + // flow out of an argument + exists(ParameterNode p | + parameterValueFlowsToUpdate(p, node1) and + viableParamArg(p, node2.(PostUpdateNode).getPreUpdateNode()) + ) + or + // flow out of a method + exists(Method m, MethodAccess ma, ReturnNode ret | + ret = node1 and + m = returnNodeGetEnclosingCallable(ret) and + m = viableImpl(ma) and + node2.asExpr() = ma + ) + ) + } + + /** + * Holds if data can flow into a callable and that this step is part of a + * path from a source to a sink. + */ + private predicate flowIntoCallable(Node node1, Node node2, Configuration config) { + viableParamArg(node2, node1) and + nodeCand1(node1, _, unbind(config)) and + nodeCand1(node2, _, config) + } + + /** + * Gets the amount of forward branching on the origin of a cross-call path + * edge in the graph of paths between sources and sinks that ignores call + * contexts. + */ + private int branch(Node n1, Configuration conf) { + result = strictcount(Node n | flowOutOfCallable(n1, n, conf) or flowIntoCallable(n1, n, conf)) + } + + /** + * Gets the amount of backward branching on the target of a cross-call path + * edge in the graph of paths between sources and sinks that ignores call + * contexts. + */ + private int join(Node n2, Configuration conf) { + result = strictcount(Node n | flowOutOfCallable(n, n2, conf) or flowIntoCallable(n, n2, conf)) + } + + /** + * Holds if data can flow out of a callable from `node1` to `node2`, either + * through a `ReturnNode` or through an argument that has been mutated, and + * that this step is part of a path from a source to a sink. The + * `allowsFieldFlow` flag indicates whether the branching is within the limit + * specified by the configuration. + */ + private predicate flowOutOfCallable(Node node1, Node node2, boolean allowsFieldFlow, Configuration config) { + flowOutOfCallable(node1, node2, config) and + exists(int b, int j | + b = branch(node1, config) and + j = join(node2, config) and + if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false + ) + } + + /** + * Holds if data can flow into a callable and that this step is part of a + * path from a source to a sink. The `allowsFieldFlow` flag indicates whether + * the branching is within the limit specified by the configuration. + */ + private predicate flowIntoCallable(Node node1, Node node2, boolean allowsFieldFlow, Configuration config) { + flowIntoCallable(node1, node2, config) and + exists(int b, int j | + b = branch(node1, config) and + j = join(node2, config) and + if b.minimum(j) <= config.fieldFlowBranchLimit() then allowsFieldFlow = true else allowsFieldFlow = false + ) + } + + /** + * Holds if `node` is part of a path from a source to a sink in the given + * configuration taking simple call contexts into consideration. + */ + private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Configuration config) { + nodeCand1(node, false, config) and + config.isSource(node) and fromArg = false and stored = false + or + nodeCand1(node, unbindBool(stored), unbind(config)) and + ( + exists(Node mid, boolean preservesValue | + nodeCandFwd2(mid, fromArg, stored, config) and + localFlowStepOrFlowThroughMethod(mid, node, preservesValue, config) and + (stored = false or preservesValue = true) + ) + or + exists(Node mid, boolean preservesValue | + nodeCandFwd2(mid, _, stored, config) and + jumpStep(mid, node, preservesValue, config) and + fromArg = false and + (stored = false or preservesValue = true) + ) + or + // store + exists(Node mid, Content f | + nodeCandFwd2(mid, fromArg, _, config) and + store(mid, f, node) and + readCand1(f, unbind(config)) and + stored = true + ) + or + // read + exists(Node mid, Content f | + nodeCandFwd2(mid, fromArg, true, config) and + read(mid, f, node) and + storeCandFwd2(f, unbind(config)) and + (stored = false or stored = true) + ) + or + exists(Node mid, boolean allowsFieldFlow | + nodeCandFwd2(mid, _, stored, config) and + flowIntoCallable(mid, node, allowsFieldFlow, config) and + fromArg = true and + (stored = false or allowsFieldFlow = true) + ) + or + exists(Node mid, boolean allowsFieldFlow | + nodeCandFwd2(mid, false, stored, config) and + flowOutOfCallable(mid, node, allowsFieldFlow, config) and + fromArg = false and + (stored = false or allowsFieldFlow = true) + ) + ) + } + + /** + * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`. + */ + private predicate storeCandFwd2(Content f, Configuration config) { + exists(Node mid, Node node | + useFieldFlow(unbind(config)) and + nodeCand1(node, true, unbind(config)) and + nodeCandFwd2(mid, _, _, config) and + store(mid, f, node) and + readCand1(f, unbind(config)) + ) + } + + /** + * Holds if `node` is part of a path from a source to a sink in the given + * configuration taking simple call contexts into consideration. + */ + private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configuration config) { + nodeCandFwd2(node, _, false, config) and + config.isSink(node) and toReturn = false and stored = false + or + nodeCandFwd2(node, _, unbindBool(stored), unbind(config)) and + ( + exists(Node mid, boolean preservesValue | + localFlowStepOrFlowThroughMethod(node, mid, preservesValue, config) and + nodeCand2(mid, toReturn, stored, config) and + (stored = false or preservesValue = true) + ) + or + exists(Node mid, boolean preservesValue | + jumpStep(node, mid, preservesValue, config) and + nodeCand2(mid, _, stored, config) and + toReturn = false and + (stored = false or preservesValue = true) + ) + or + // store + exists(Node mid, Content f | + store(node, f, mid) and + readCand2(f, unbind(config)) and + nodeCand2(mid, toReturn, true, config) and + (stored = false or stored = true) + ) + or + // read + exists(Node mid, Content f | + read(node, f, mid) and + storeCandFwd2(f, unbind(config)) and + nodeCand2(mid, toReturn, _, config) and + stored = true + ) + or + exists(Node mid, boolean allowsFieldFlow | + flowIntoCallable(node, mid, allowsFieldFlow, config) and + nodeCand2(mid, false, stored, config) and + toReturn = false and + (stored = false or allowsFieldFlow = true) + ) + or + exists(Node mid, boolean allowsFieldFlow | + flowOutOfCallable(node, mid, allowsFieldFlow, config) and + nodeCand2(mid, _, stored, config) and + toReturn = true and + (stored = false or allowsFieldFlow = true) + ) + ) + } + + /** + * Holds if `f` is the target of a read in the flow covered by `nodeCand2`. + */ + private predicate readCand2(Content f, Configuration config) { + exists(Node mid, Node node | + useFieldFlow(unbind(config)) and + nodeCandFwd2(node, _, true, unbind(config)) and + read(node, f, mid) and + storeCandFwd2(f, unbind(config)) and + nodeCand2(mid, _, _, config) + ) + } + + private predicate storeCand(Content f, Configuration conf) { + exists(Node n1, Node n2 | + store(n1, f, n2) and + nodeCand2(n1, _, _, conf) and + nodeCand2(n2, _, _, unbind(conf)) + ) + } + + private predicate readCand(Content f, Configuration conf) { + readCand2(f, conf) + } + + /** + * Holds if `f` is the target of both a store and a read in the path graph + * covered by `nodeCand2`. + */ + pragma[noinline] + private predicate readStoreCand(Content f, Configuration conf) { + storeCand(f, conf) and + readCand(f, conf) + } + + private predicate nodeCand(Node node, Configuration config) { + nodeCand2(node, _, _, config) + } + + /** + * 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) { + nodeCand(node, config) and + ( + config.isSource(node) or + jumpStep(_, node, _, config) or + node instanceof ParameterNode or + node.asExpr() instanceof MethodAccess or + node instanceof PostUpdateNode or + read(_, _, node) or + node.asExpr() instanceof CastExpr + ) + } + + /** + * Holds if `node` can be the last node in a maximal subsequence of local + * flow steps in a dataflow path. + */ + private predicate localFlowExit(Node node, Configuration config) { + exists(Node next | + nodeCand(next, config) + | + jumpStep(node, next, _, config) or + flowIntoCallable(node, next, config) or + flowOutOfCallable(node, next, config) or + flowThroughMethod(node, next, _, config) or + store(node, _, next) or + read(node, _, next) + ) or + node.asExpr() instanceof CastExpr or + config.isSink(node) + } + + /** + * Holds if the local path from `node1` to `node2` is a prefix of a maximal + * subsequence of local flow steps in a dataflow path. + * + * This is the transitive closure of `localFlowStep` beginning at `localFlowEntry`. + */ + private predicate localFlowStepPlus(Node node1, Node node2, boolean preservesValue, Configuration config) { + localFlowEntry(node1, config) and + localFlowStep(node1, node2, preservesValue, config) and + node1 != node2 and + nodeCand(node2, unbind(config)) + or + exists(Node mid, boolean pv1, boolean pv2 | + localFlowStepPlus(node1, mid, pv1, config) and + localFlowStep(mid, node2, pv2, config) and + not mid.asExpr() instanceof CastExpr and + preservesValue = pv1.booleanAnd(pv2) and + nodeCand(node2, unbind(config)) + ) + } + + /** + * Holds if `node1` can step to `node2` in one or more local steps and this + * path can occur as a maximal subsequence of local steps in a dataflow path. + */ + pragma[noinline] + private predicate localFlowBigStep(Node node1, Node node2, boolean preservesValue, Configuration config) { + localFlowStepPlus(node1, node2, preservesValue, config) and + localFlowExit(node2, config) + } + + /** + * Holds if `f` may contain an object of the same type, `t`, as the one + * that contains `f`, and if this fact should be used to compress + * access paths. + * + * Examples include the tail pointer in a linked list or the left and right + * pointers in a binary tree. + */ + private predicate selfRef(Content f, RefType t) { + t = f.getDeclaringType() and + f.isSelfRef() + } + + private newtype TFlowContainer = + TRegularContent(Content f) { not selfRef(f, _) } or + TSelfRefContent(RefType t) { selfRef(_, t) } + + /** + * A `Content` or a `Content` abstracted as its declaring type. + * + * Sequences of one or more `Content`s in the same declaring type for which + * `isSelfRef()` holds are represented as a single `FlowContainer` in an + * `AccessPath`. + */ + private class FlowContainer extends TFlowContainer { + string toString() { + exists(Content f | this = TRegularContent(f) and result = f.toString()) or + exists(RefType t | this = TSelfRefContent(t) and result = t.toString()) + } + predicate usesContent(Content f) { + this = TRegularContent(f) or + exists(RefType t | this = TSelfRefContent(t) and selfRef(f, t)) + } + RefType getContainerType() { + exists(Content f | this = TRegularContent(f) and result = f.getDeclaringType()) or + this = TSelfRefContent(result) + } + } + + private newtype TAccessPathFront = TFrontNil(Type t) or TFrontHead(FlowContainer f) + + /** + * The front of an `AccessPath`. This is either a head or a nil. + */ + private class AccessPathFront extends TAccessPathFront { + string toString() { + exists(Type t | this = TFrontNil(t) | result = t.toString()) or + exists(FlowContainer f | this = TFrontHead(f) | result = f.toString()) + } + Type getType() { + this = TFrontNil(result) or + exists(FlowContainer head | this = TFrontHead(head) | result = head.getContainerType()) + } + predicate headUsesContent(Content f) { + exists(FlowContainer fc | + fc.usesContent(f) and + this = TFrontHead(fc) + ) + } + } + + private class AccessPathFrontNil extends AccessPathFront, TFrontNil { + } + + /** + * A `Node` at which a cast can occur such that the type should be checked. + */ + private class CastingNode extends Node { + CastingNode() { + this instanceof ParameterNode or + this.asExpr() instanceof CastExpr or + this.asExpr() instanceof MethodAccess or + this.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNode + } + } + + /** + * Holds if data can flow from a source to `node` with the given `apf`. + */ + private predicate flowCandFwd(Node node, boolean fromArg, AccessPathFront apf, Configuration config) { + flowCandFwd0(node, fromArg, apf, config) and + if node instanceof CastingNode then + compatibleTypes(node.getType(), apf.getType()) + else + any() + } + private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf, Configuration config) { + nodeCand2(node, _, false, config) and + config.isSource(node) and fromArg = false and apf = TFrontNil(getErasedRepr(node.getType())) + or + nodeCand(node, unbind(config)) and + ( + exists(Node mid | + flowCandFwd(mid, fromArg, apf, config) and + localFlowBigStep(mid, node, true, config) + ) + or + exists(Node mid, AccessPathFront apf0 | + flowCandFwd(mid, fromArg, apf0, config) and + localFlowBigStep(mid, node, false, config) and + apf0 instanceof AccessPathFrontNil and + apf = TFrontNil(getErasedRepr(node.getType())) + ) + or + exists(Node mid | + flowCandFwd(mid, _, apf, config) and + jumpStep(mid, node, true, config) and + fromArg = false + ) + or + exists(Node mid, AccessPathFront apf0 | + flowCandFwd(mid, _, apf0, config) and + jumpStep(mid, node, false, config) and + fromArg = false and + apf0 instanceof AccessPathFrontNil and + apf = TFrontNil(getErasedRepr(node.getType())) + ) + or + exists(Node mid, boolean allowsFieldFlow | + flowCandFwd(mid, _, apf, config) and + flowIntoCallable(mid, node, allowsFieldFlow, config) and + fromArg = true and + (apf instanceof AccessPathFrontNil or allowsFieldFlow = true) + ) + or + exists(Node mid, boolean allowsFieldFlow | + flowCandFwd(mid, false, apf, config) and + flowOutOfCallable(mid, node, allowsFieldFlow, config) and + fromArg = false and + (apf instanceof AccessPathFrontNil or allowsFieldFlow = true) + ) + or + exists(Node mid, boolean preservesValue, AccessPathFront apf0 | + flowCandFwd(mid, fromArg, apf0, config) and + flowThroughMethod(mid, node, preservesValue, config) and + (preservesValue = true and apf = apf0 or preservesValue = false and apf0 instanceof AccessPathFrontNil and apf = TFrontNil(getErasedRepr(node.getType()))) + ) + ) + or + exists(Node mid, Content f | + flowCandFwd(mid, fromArg, _, config) and + store(mid, f, node) and + nodeCand(node, unbind(config)) and + apf.headUsesContent(f) + ) + or + exists(Node mid, Content f, AccessPathFront apf0 | + flowCandFwd(mid, fromArg, apf0, config) and + read(mid, f, node) and + nodeCand(node, config) and + apf0.headUsesContent(f) and + consCandFwd(f, apf, unbind(config)) + ) + } + private predicate consCandFwd(Content f, AccessPathFront apf, Configuration config) { + exists(Node mid, Node n | + flowCandFwd(mid, _, apf, config) and + store(mid, f, n) and + nodeCand(n, unbind(config)) and + readStoreCand(f, unbind(config)) and + compatibleTypes(apf.getType(), f.getType()) + ) + } + + /** + * Holds if data can flow from a source to `node` with the given `apf` and + * from there flow to a sink. + */ + private predicate flowCand(Node node, boolean toReturn, AccessPathFront apf, Configuration config) { + flowCand0(node, toReturn, apf, config) and + flowCandFwd(node, _, apf, config) + } + private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Configuration config) { + flowCandFwd(node, _, apf, config) and + config.isSink(node) and toReturn = false and apf instanceof AccessPathFrontNil + or + ( + exists(Node mid | + localFlowBigStep(node, mid, true, config) and + flowCand(mid, toReturn, apf, config) + ) + or + exists(Node mid, AccessPathFront apf0 | + flowCandFwd(node, _, apf, config) and + localFlowBigStep(node, mid, false, config) and + flowCand(mid, toReturn, apf0, config) and + apf0 instanceof AccessPathFrontNil and + apf instanceof AccessPathFrontNil + ) + or + exists(Node mid | + jumpStep(node, mid, true, config) and + flowCand(mid, _, apf, config) and + toReturn = false + ) + or + exists(Node mid, AccessPathFront apf0 | + flowCandFwd(node, _, apf, config) and + jumpStep(node, mid, false, config) and + flowCand(mid, _, apf0, config) and + toReturn = false and + apf0 instanceof AccessPathFrontNil and + apf instanceof AccessPathFrontNil + ) + or + exists(Node mid, boolean allowsFieldFlow | + flowIntoCallable(node, mid, allowsFieldFlow, config) and + flowCand(mid, false, apf, config) and + toReturn = false and + (apf instanceof AccessPathFrontNil or allowsFieldFlow = true) + ) + or + exists(Node mid, boolean allowsFieldFlow | + flowOutOfCallable(node, mid, allowsFieldFlow, config) and + flowCand(mid, _, apf, config) and + toReturn = true and + (apf instanceof AccessPathFrontNil or allowsFieldFlow = true) + ) + or + exists(Node mid, boolean preservesValue, AccessPathFront apf0 | + flowThroughMethod(node, mid, preservesValue, config) and + flowCand(mid, toReturn, apf0, config) and + (preservesValue = true and apf = apf0 or preservesValue = false and apf0 instanceof AccessPathFrontNil and apf instanceof AccessPathFrontNil and flowCandFwd(node, _, apf, config)) + ) + or + exists(Node mid, Content f, AccessPathFront apf0 | + store(node, f, mid) and + flowCand(mid, toReturn, apf0, config) and + apf0.headUsesContent(f) and + consCand(f, apf, unbind(config)) + ) + or + exists(Node mid, Content f, AccessPathFront apf0 | + read(node, f, mid) and + flowCand(mid, toReturn, apf0, config) and + consCandFwd(f, apf0, unbind(config)) and + apf.headUsesContent(f) + ) + ) + } + private predicate consCand(Content f, AccessPathFront apf, Configuration config) { + consCandFwd(f, apf, config) and + exists(Node mid, Node n, AccessPathFront apf0 | + flowCandFwd(n, _, apf0, config) and + apf0.headUsesContent(f) and + read(n, f, mid) and + flowCand(mid, _, apf, config) + ) + } + + private newtype TAccessPath = TNil(Type t) or TCons(FlowContainer f, int len) { len in [1..5] } + + /** + * Conceptually a list of `Content`s followed by a `Type`, but only the first + * element of the list and its length are tracked. If data flows from a source to + * a given node with a given `AccessPath`, this indicates the sequence of + * dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ + private class AccessPath extends TAccessPath { + abstract string toString(); + FlowContainer getHead() { this = TCons(result, _) } + int len() { + this = TNil(_) and result = 0 + or + this = TCons(_, result) + } + Type getType() { + this = TNil(result) or + exists(FlowContainer head | this = TCons(head, _) | result = head.getContainerType()) + } + abstract AccessPathFront getFront(); + } + + private class AccessPathNil extends AccessPath, TNil { + override string toString() { exists(Type t | this = TNil(t) | result = t.toString()) } + override AccessPathFront getFront() { exists(Type t | this = TNil(t) | result = TFrontNil(t)) } + } + + private class AccessPathCons extends AccessPath, TCons { + override string toString() { + exists(FlowContainer f, int len | this = TCons(f, len) | result = f.toString() + ", ... (" + len.toString() + ")") + } + override AccessPathFront getFront() { exists(FlowContainer f | this = TCons(f, _) | result = TFrontHead(f)) } + } + + /** Holds if `ap0` corresponds to the cons of `f` and `ap`. */ + private predicate pop(AccessPath ap0, Content f, AccessPath ap) { + ap0.getFront().headUsesContent(f) and + consCand(f, ap.getFront(), _) and + ap0.len() = 1 + ap.len() + } + + /** Holds if `ap0` corresponds to the cons of `f` and `ap` and `apf` is the front of `ap`. */ + pragma[noinline] + private predicate popWithFront(AccessPath ap0, Content f, AccessPathFront apf, AccessPath ap) { + pop(ap0, f, ap) and apf = ap.getFront() + } + + /** Holds if `ap` corresponds to the cons of `f` and `ap0`. */ + private predicate push(AccessPath ap0, Content f, AccessPath ap) { pop(ap, f, ap0) } + + /** + * Holds if data can flow from a source to `node` with the given `ap`. + */ + private predicate flowFwd(Node node, boolean fromArg, AccessPathFront apf, AccessPath ap, Configuration config) { + flowFwd0(node, fromArg, apf, ap, config) and + flowCand(node, _, apf, config) + } + private predicate flowFwd0(Node node, boolean fromArg, AccessPathFront apf, AccessPath ap, Configuration config) { + flowCand(node, _, _, config) and + config.isSource(node) and fromArg = false and ap = TNil(getErasedRepr(node.getType())) and apf = ap.(AccessPathNil).getFront() + or + flowCand(node, _, _, unbind(config)) and + ( + exists(Node mid | + flowFwd(mid, fromArg, apf, ap, config) and + localFlowBigStep(mid, node, true, config) + ) + or + exists(Node mid, AccessPath ap0 | + flowFwd(mid, fromArg, _, ap0, config) and + localFlowBigStep(mid, node, false, config) and + ap0 instanceof AccessPathNil and + ap = TNil(getErasedRepr(node.getType())) and + apf = ap.(AccessPathNil).getFront() + ) + or + exists(Node mid | + flowFwd(mid, _, apf, ap, config) and + jumpStep(mid, node, true, config) and + fromArg = false + ) + or + exists(Node mid, AccessPath ap0 | + flowFwd(mid, _, _, ap0, config) and + jumpStep(mid, node, false, config) and + fromArg = false and + ap0 instanceof AccessPathNil and + ap = TNil(getErasedRepr(node.getType())) and + apf = ap.(AccessPathNil).getFront() + ) + or + exists(Node mid, boolean allowsFieldFlow | + flowFwd(mid, _, apf, ap, config) and + flowIntoCallable(mid, node, allowsFieldFlow, config) and + fromArg = true and + (ap instanceof AccessPathNil or allowsFieldFlow = true) + ) + or + exists(Node mid, boolean allowsFieldFlow | + flowFwd(mid, false, apf, ap, config) and + flowOutOfCallable(mid, node, allowsFieldFlow, config) and + fromArg = false and + (ap instanceof AccessPathNil or allowsFieldFlow = true) + ) + or + exists(Node mid, boolean preservesValue, AccessPathFront apf0, AccessPath ap0 | + flowFwd(mid, fromArg, apf0, ap0, config) and + flowThroughMethod(mid, node, preservesValue, config) and + (preservesValue = true and ap = ap0 and apf = apf0 or preservesValue = false and ap0 instanceof AccessPathNil and ap = TNil(getErasedRepr(node.getType())) and apf = ap.(AccessPathNil).getFront()) + ) + ) + or + exists(Content f, AccessPath ap0 | + flowFwdStore(node, f, ap0, apf, fromArg, config) and + push(ap0, f, ap) + ) + or + exists(Content f, AccessPath ap0 | + flowFwdRead(node, f, ap0, fromArg, config) and + popWithFront(ap0, f, apf, ap) + ) + } + pragma[nomagic] + private predicate flowFwdStore(Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg, Configuration config) { + exists(Node mid, AccessPathFront apf0 | + flowFwd(mid, fromArg, apf0, ap0, config) and + flowFwdStoreAux(mid, f, node, apf0, apf, config) + ) + } + private predicate flowFwdStoreAux(Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFront apf, Configuration config) { + store(mid, f, node) and + consCand(f, apf0, config) and + apf.headUsesContent(f) and + flowCand(node, _, apf, unbind(config)) + } + pragma[nomagic] + private predicate flowFwdRead(Node node, Content f, AccessPath ap0, boolean fromArg, Configuration config) { + exists(Node mid, AccessPathFront apf0 | + flowFwd(mid, fromArg, apf0, ap0, config) and + read(mid, f, node) and + apf0.headUsesContent(f) and + flowCand(node, _, _, unbind(config)) + ) + } + + /** + * Holds if data can flow from a source to `node` with the given `ap` and + * from there flow to a sink. + */ + private predicate flow(Node node, boolean toReturn, AccessPath ap, Configuration config) { + flow0(node, toReturn, ap, config) and + flowFwd(node, _, _, ap, config) + } + private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuration config) { + flowFwd(node, _, _, ap, config) and + config.isSink(node) and toReturn = false and ap instanceof AccessPathNil + or + ( + exists(Node mid | + localFlowBigStep(node, mid, true, config) and + flow(mid, toReturn, ap, config) + ) + or + exists(Node mid, AccessPath ap0 | + flowFwd(node, _, _, ap, config) and + localFlowBigStep(node, mid, false, config) and + flow(mid, toReturn, ap0, config) and + ap0 instanceof AccessPathNil and + ap instanceof AccessPathNil + ) + or + exists(Node mid | + jumpStep(node, mid, true, config) and + flow(mid, _, ap, config) and + toReturn = false + ) + or + exists(Node mid, AccessPath ap0 | + flowFwd(node, _, _, ap, config) and + jumpStep(node, mid, false, config) and + flow(mid, _, ap0, config) and + toReturn = false and + ap0 instanceof AccessPathNil and + ap instanceof AccessPathNil + ) + or + exists(Node mid, boolean allowsFieldFlow | + flowIntoCallable(node, mid, allowsFieldFlow, config) and + flow(mid, false, ap, config) and + toReturn = false and + (ap instanceof AccessPathNil or allowsFieldFlow = true) + ) + or + exists(Node mid, boolean allowsFieldFlow | + flowOutOfCallable(node, mid, allowsFieldFlow, config) and + flow(mid, _, ap, config) and + toReturn = true and + (ap instanceof AccessPathNil or allowsFieldFlow = true) + ) + or + exists(Node mid, boolean preservesValue, AccessPath ap0 | + flowThroughMethod(node, mid, preservesValue, config) and + flow(mid, toReturn, ap0, config) and + (preservesValue = true and ap = ap0 or preservesValue = false and ap0 instanceof AccessPathNil and ap instanceof AccessPathNil and flowFwd(node, _, _, ap, config)) + ) + or + exists(Content f, AccessPath ap0 | + flowStore(node, f, toReturn, ap0, config) and + pop(ap0, f, ap) + ) + or + exists(Content f, AccessPath ap0 | + flowRead(node, f, toReturn, ap0, config) and + push(ap0, f, ap) + ) + ) + } + pragma[nomagic] + private predicate flowStore(Node node, Content f, boolean toReturn, AccessPath ap0, Configuration config) { + exists(Node mid | + store(node, f, mid) and + flow(mid, toReturn, ap0, config) + ) + } + pragma[nomagic] + private predicate flowRead(Node node, Content f, boolean toReturn, AccessPath ap0, Configuration config) { + exists(Node mid | + read(node, f, mid) and + flow(mid, toReturn, ap0, config) + ) + } + + private bindingset[conf, result] Configuration unbind(Configuration conf) { result >= conf and result <= conf } + + private predicate flow(Node n, Configuration config) { + flow(n, _, _, config) + } + + private newtype TPathNode = + TPathNodeMid(Node node, CallContext cc, AccessPath ap, Configuration config) { + // A PathNode is introduced by a source ... + flow(node, config) and + config.isSource(node) and + cc instanceof CallContextAny and + ap = TNil(getErasedRepr(node.getType())) + or + // ... or a step from an existing PathNode to another node. + exists(PathNodeMid mid | + flowStep(mid, node, cc, ap) and + config = mid.getConfiguration() and + flow(node, _, ap, unbind(config)) + ) + } or + TPathNodeSink(Node node, Configuration config) { // The AccessPath on a sink is empty. + config.isSink(node) and + flow(node, config) + } + + /** + * 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. + */ + abstract class PathNode extends TPathNode { + /** Gets a textual representation of this element. */ + string toString() { result = getNode().toString() + ppAp() } + /** Gets the source location for this element. */ + Location getLocation() { result = getNode().getLocation() } + /** Gets the underlying `Node`. */ + abstract Node getNode(); + /** Gets the associated configuration. */ + abstract Configuration getConfiguration(); + /** Gets a successor. */ + abstract PathNode getSucc(); + private string ppAp() { + this instanceof PathNodeSink and result = "" or + result = " [" + this.(PathNodeMid).getAp().toString() + "]" + } + } + + /** Holds if `n` can reach a sink. */ + private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getSucc()) } + + /** Holds if `n1.getSucc() = n2` and `n2` can reach a sink. */ + private predicate pathSucc(PathNode n1, PathNode n2) { n1.getSucc() = n2 and reach(n2) } + + private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2) + + /** + * Provides the query predicates needed to include a graph in a path-problem query. + */ + module PathGraph { + /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ + query predicate edges(PathNode a, PathNode b) { pathSucc(a, b) } + } + + /** + * An intermediate flow graph node. This is a triple consisting of a `Node`, + * a `CallContext`, and a `Configuration`. + */ + private class PathNodeMid extends PathNode, TPathNodeMid { + Node node; + CallContext cc; + AccessPath ap; + Configuration config; + PathNodeMid() { this = TPathNodeMid(node, cc, ap, config) } + override Node getNode() { result = node } + CallContext getCallContext() { result = cc } + AccessPath getAp() { result = ap } + override Configuration getConfiguration() { result = config } + private PathNodeMid getSuccMid() { + flowStep(this, result.getNode(), result.getCallContext(), result.getAp()) and + result.getConfiguration() = unbind(this.getConfiguration()) + } + override PathNode getSucc() { + // an intermediate step to another intermediate node + result = getSuccMid() + or + // a final step to a sink via one or more local steps + localFlowStepPlus(node, result.getNode(), _, config) and + ap instanceof AccessPathNil and + result instanceof PathNodeSink and + result.getConfiguration() = unbind(this.getConfiguration()) + or + // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges + exists(PathNodeMid mid | + mid = getSuccMid() and + mid.getNode() = result.getNode() and + mid.getAp() instanceof AccessPathNil and + result instanceof PathNodeSink and + result.getConfiguration() = unbind(mid.getConfiguration()) + ) + or + // a direct step from a source to a sink if a node is both + this instanceof PathNodeSource and + result instanceof PathNodeSink and + this.getNode() = result.getNode() and + result.getConfiguration() = unbind(this.getConfiguration()) + } + } + + /** + * A flow graph node corresponding to a source. + */ + private class PathNodeSource extends PathNodeMid { + PathNodeSource() { + getConfiguration().isSource(getNode()) and + getCallContext() instanceof CallContextAny and + getAp() instanceof AccessPathNil + } + } + + /** + * A flow graph node corresponding to a sink. This is disjoint from the + * intermediate nodes in order to uniquely correspond to a given sink by + * excluding the `CallContext`. + */ + private class PathNodeSink extends PathNode, TPathNodeSink { + Node node; + Configuration config; + PathNodeSink() { this = TPathNodeSink(node, config) } + override Node getNode() { result = node } + override Configuration getConfiguration() { result = config } + override PathNode getSucc() { none() } + } + + /** + * Holds if data may flow from `mid` to `node`. The last step in or out of + * a callable is recorded by `cc`. + */ + private predicate flowStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) { + localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and + cc = mid.getCallContext() and + ap = mid.getAp() + or + localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration()) and + cc = mid.getCallContext() and + mid.getAp() instanceof AccessPathNil and + ap = TNil(getErasedRepr(node.getType())) + or + jumpStep(mid.getNode(), node, true, mid.getConfiguration()) and + cc instanceof CallContextAny and + ap = mid.getAp() + or + jumpStep(mid.getNode(), node, false, mid.getConfiguration()) and + cc instanceof CallContextAny and + mid.getAp() instanceof AccessPathNil and + ap = TNil(getErasedRepr(node.getType())) + or + contentReadStep(mid, node, ap) and cc = mid.getCallContext() + or + exists(Content f, AccessPath ap0 | contentStoreStep(mid, node, ap0, f, cc) and push(ap0, f, ap)) + or + flowOutOfArgument(mid, node, cc) and ap = mid.getAp() + or + flowIntoCallable(mid, node, _, cc, _) and ap = mid.getAp() + or + flowOutOfMethod(mid, node.asExpr(), cc) and ap = mid.getAp() + or + flowThroughMethod(mid, node.asExpr(), cc) and ap = TNil(getErasedRepr(node.getType())) + or + valueFlowThroughMethod(mid, node.asExpr(), cc) and ap = mid.getAp() + } + + private predicate contentReadStep(PathNodeMid mid, Node node, AccessPath ap) { + exists(Content f, AccessPath ap0 | + ap0 = mid.getAp() and + read(mid.getNode(), f, node) and + pop(ap0, f, ap) + ) + } + + pragma[noinline] + private predicate contentStoreStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) { + ap0 = mid.getAp() and + store(mid.getNode(), f, node) and + cc = mid.getCallContext() + } + + /** + * Holds if data may flow from `mid` to an exit of `m` in the context + * `innercc`, and the path did not flow through a parameter of `m`. + */ + private predicate flowOutOfMethod0(PathNodeMid mid, Method m, CallContext innercc) { + exists(ReturnNode ret | + ret = mid.getNode() and + innercc = mid.getCallContext() and + m = returnNodeGetEnclosingCallable(ret) and + not innercc instanceof CallContextCall + ) + } + + /** + * Holds if data may flow from `mid` to `ma`. The last step of this path + * is a return from a method and is recorded by `cc`, if needed. + */ + pragma[noinline] + private predicate flowOutOfMethod(PathNodeMid mid, MethodAccess ma, CallContext cc) { + exists(Method m, CallContext innercc | + flowOutOfMethod0(mid, m, innercc) and + resolveReturn(innercc, m, ma) + | + if reducedViableImplInReturn(m, ma) then cc = TReturn(m, ma) else cc = TAnyCallContext() + ) + } + + private predicate flowOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallContext cc) { + exists(PostUpdateNode n, ParameterNode p, Callable callable, CallContext innercc, int i, Call call, ArgumentNode arg | + mid.getNode() = n and + parameterValueFlowsToUpdate(p, n) and + innercc = mid.getCallContext() and + p.isParameterOf(callable, i) and + resolveReturn(innercc, callable, call) and + node.getPreUpdateNode() = arg and + arg.argumentOf(call, i) and + flow(node, unbind(mid.getConfiguration())) + | + if reducedViableImplInReturn(callable, call) then cc = TReturn(callable, call) else cc = TAnyCallContext() + ) + } + + /** + * Holds if data may flow from `mid` to the `i`th argument of `call` in `cc`. + */ + pragma[noinline] + private predicate flowIntoArg(PathNodeMid mid, int i, CallContext cc, Call call, boolean emptyAp) { + exists(ArgumentNode arg, AccessPath ap | + arg = mid.getNode() and + cc = mid.getCallContext() and + arg.argumentOf(call, i) and + ap = mid.getAp() + | + ap instanceof AccessPathNil and emptyAp = true or + ap instanceof AccessPathCons and emptyAp = false + ) + } + + pragma[noinline] + private predicate parameterCand(Callable callable, int i, Configuration config) { + exists(ParameterNode p | + flow(p, config) and + p.isParameterOf(callable, i) + ) + } + + pragma[nomagic] + private predicate flowIntoCallable0(PathNodeMid mid, Callable callable, int i, CallContext outercc, Call call, boolean emptyAp) { + flowIntoArg(mid, i, outercc, call, emptyAp) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), mid.getConfiguration()) + } + + /** + * Holds if data may flow from `mid` to `p` through `call`. The contexts + * before and after entering the callable are `outercc` and `innercc`, + * respectively. + */ + private predicate flowIntoCallable(PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, Call call) { + exists(int i, Callable callable, boolean emptyAp | + flowIntoCallable0(mid, callable, i, outercc, call, emptyAp) and + p.isParameterOf(callable, i) + | + if reducedViableImplInCallContext(_, callable, call) then innercc = TSpecificCall(call, i, emptyAp) else innercc = TSomeCall(p, emptyAp) + ) + } + + /** Holds if data may flow from `p` to a return statement in the callable. */ + pragma[nomagic] + private predicate paramFlowsThrough(ParameterNode p, CallContextCall cc, Configuration config) { + exists(PathNodeMid mid, ReturnNode ret | + mid.getNode() = ret and + cc = mid.getCallContext() and + config = mid.getConfiguration() and + mid.getAp() instanceof AccessPathNil + | + cc = TSomeCall(p, true) or + exists(int i | cc = TSpecificCall(_, i, true) | + p.isParameterOf(returnNodeGetEnclosingCallable(ret), i) + ) + ) + } + + /** + * Holds if data may flow from `mid` to an argument of `methodcall`, + * through a called method `m`, and back out through a return statement in + * `m`. The context `cc` is restored to its value prior to entering `m`. + */ + pragma[noinline] + private predicate flowThroughMethod(PathNodeMid mid, Call methodcall, CallContext cc) { + exists(ParameterNode p, CallContext innercc | + flowIntoCallable(mid, p, cc, innercc, methodcall) and + paramFlowsThrough(p, innercc, unbind(mid.getConfiguration())) and + not parameterValueFlowsThrough(p) and + mid.getAp() instanceof AccessPathNil + ) + } + + private predicate valueFlowThroughMethod(PathNodeMid mid, Call methodcall, CallContext cc) { + exists(ParameterNode p | + flowIntoCallable(mid, p, cc, _, methodcall) and + parameterValueFlowsThrough(p) + ) + } + + /** + * Holds if data can flow (inter-procedurally) from `source` to `sink`. + * + * Will only have results if `configuration` has non-empty sources and + * sinks. + */ + private predicate flowsTo(PathNodeSource flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration) { + flowsource.getConfiguration() = configuration and + flowsource.getNode() = source and + pathSuccPlus(flowsource, flowsink) and + flowsink.getNode() = sink + } + + /** + * Holds if data can flow (inter-procedurally) from `source` to `sink`. + * + * Will only have results if `configuration` has non-empty sources and + * sinks. + */ + predicate flowsTo(Node source, Node sink, Configuration configuration) { + flowsTo(_, _, source, sink, configuration) + } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll new file mode 100644 index 000000000000..94d81815dcb0 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll @@ -0,0 +1,274 @@ + +import DataFlowUtil +private import DataFlowPrivate +private import DataFlowDispatch + +cached +private module ImplCommon { + /** + * Holds if `p` is the `i`th parameter of a viable dispatch target of `call`. + * The instance parameter is considered to have index `-1`. + */ + pragma[nomagic] + private predicate viableParam(Call call, int i, ParameterNode p) { + exists(Callable callable | + callable = viableCallable(call) and + p.isParameterOf(callable, i) + ) + } + + /** + * Holds if `arg` is a possible argument to `p` taking virtual dispatch into account. + */ + cached + predicate viableParamArg(ParameterNode p, ArgumentNode arg) { + exists(int i, Call call | + viableParam(call, i, p) and + arg.argumentOf(call, i) + ) + } + + /** + * Holds if `p` can flow to `node` in the same callable using only + * value-preserving steps. + */ + private predicate parameterValueFlow(ParameterNode p, Node node) { + p = node + or + exists(Node mid | + parameterValueFlow(p, mid) and + localFlowStep(mid, node) and + compatibleTypes(p.getType(), node.getType()) + ) + or + // flow through a callable + exists(Node arg | + parameterValueFlow(p, arg) and + argumentValueFlowsThrough(arg, node) and + compatibleTypes(p.getType(), node.getType()) + ) + } + + /** + * Holds if `p` can flow to a `ReturnNode` in the same callable using only + * value-preserving steps. + */ + cached + predicate parameterValueFlowsThrough(ParameterNode p) { + exists(ReturnNode ret | + parameterValueFlow(p, ret) + ) + } + + /** + * Holds if `arg` flows through `call` using only value-preserving steps. + */ + cached + predicate argumentValueFlowsThrough(ArgumentNode arg, ExprNode call) { + exists(ParameterNode param | + viableParamArg(param, arg) and + parameterValueFlowsThrough(param) and + arg.argumentOf(call.getExpr(), _) and + compatibleTypes(arg.getType(), call.getType()) + ) + } + + /** + * Holds if `p` can flow to the pre-update node of `n` in the same callable + * using only value-preserving steps. + */ + cached + predicate parameterValueFlowsToUpdate(ParameterNode p, PostUpdateNode n) { + parameterValueFlow(p, n.getPreUpdateNode()) + } + + /** + * Holds if data can flow from `node1` to `node2` in one local step or a step + * through a value-preserving method. + */ + private predicate localValueStep(Node node1, Node node2) { + localFlowStep(node1, node2) or + argumentValueFlowsThrough(node1, node2) + } + + /* + * Calculation of `predicate store(Node node1, Content f, Node node2)`: + * There are three cases: + * - The base case: A direct local assignment given by `storeStep`. + * - A call to a method or constructor with two arguments, `arg1` and `arg2`, + * such the call has the side-effect `arg2.f = arg1`. + * - A call to a method that returns an object in which an argument has been + * stored. + * `storeViaSideEffect` covers the first two cases, and `storeReturn` covers + * the third case. + */ + + /** + * Holds if data can flow from `node1` to `node2` via a direct assignment to + * `f` or via a call that acts as a setter. + */ + cached + predicate store(Node node1, Content f, Node node2) { + storeViaSideEffect(node1, f, node2) or + storeReturn(node1, f, node2) + } + private predicate storeViaSideEffect(Node node1, Content f, PostUpdateNode node2) { + storeStep(node1, f, node2) and readStep(_, f, _) or + exists(Call call, int i1, int i2 | + setterCall(call, i1, i2, f) and + node1.(ArgumentNode).argumentOf(call, i1) and + node2.getPreUpdateNode().(ArgumentNode).argumentOf(call, i2) and + compatibleTypes(node1.getTypeBound(), f.getType()) and + compatibleTypes(node2.getTypeBound(), f.getDeclaringType()) + ) + } + pragma[nomagic] + private predicate setterInParam(ParameterNode p1, Content f, ParameterNode p2) { + exists(Node n1, PostUpdateNode n2 | + parameterValueFlow(p1, n1) and + storeViaSideEffect(n1, f, n2) and + parameterValueFlow(p2, n2.getPreUpdateNode()) and + p1 != p2 + ) + } + pragma[nomagic] + private predicate setterCall(Call call, int i1, int i2, Content f) { + exists(Callable callable, ParameterNode p1, ParameterNode p2 | + setterInParam(p1, f, p2) and + callable = viableCallable(call) and + p1.isParameterOf(callable, i1) and + p2.isParameterOf(callable, i2) + ) + } + private predicate storeReturn(Node node1, Content f, Node node2) { + exists(ParameterNode p, ArgumentNode arg | + arg = node1 and + viableParamArg(p, arg) and + setterReturn(p, f) and + arg.argumentOf(node2.asExpr(), _) and + compatibleTypes(node1.getTypeBound(), f.getType()) and + compatibleTypes(node2.getTypeBound(), f.getDeclaringType()) + ) + } + private predicate setterReturn(ParameterNode p, Content f) { + exists(Node n1, Node n2, ReturnNode ret | + parameterValueFlow(p, n1) and + store(n1, f, n2) and + localValueStep*(n2, ret) + ) + } + + /** + * Holds if data can flow from `node1` to `node2` via a direct read of `f` or + * via a getter. + */ + cached + predicate read(Node node1, Content f, Node node2) { + readStep(node1, f, node2) and storeStep(_, f, _) or + exists(ParameterNode p, ArgumentNode arg | + arg = node1 and + viableParamArg(p, arg) and + getter(p, f) and + arg.argumentOf(node2.asExpr(), _) and + compatibleTypes(node1.getTypeBound(), f.getDeclaringType()) and + compatibleTypes(node2.getTypeBound(), f.getType()) + ) + } + private predicate getter(ParameterNode p, Content f) { + exists(Node n1, Node n2, ReturnNode ret | + parameterValueFlow(p, n1) and + read(n1, f, n2) and + localValueStep*(n2, ret) + ) + } + + cached + predicate localStoreReadStep(Node node1, Node node2) { + exists(Node mid1, Node mid2, Content f | + store(node1, f, mid1) and + localValueStep*(mid1, mid2) and + read(mid2, f, node2) + ) + } + + /** + * Holds if `call` passes an implicit or explicit instance argument, i.e., an + * expression that reaches a `this` parameter. + */ + private predicate callHasInstanceArgument(Call call) { + exists(ArgumentNode arg | + arg.argumentOf(call, -1) + ) + } + + cached + newtype TCallContext = + TAnyCallContext() or + TSpecificCall(Call call, int i, boolean emptyAp) { + reducedViableImplInCallContext(_, _, call) and + (emptyAp = true or emptyAp = false) and + (exists(call.getArgument(i)) + or + i = -1 and callHasInstanceArgument(call)) + } or + TSomeCall(ParameterNode p, boolean emptyAp) { emptyAp = true or emptyAp = false } or + TReturn(Method m, MethodAccess ma) { reducedViableImplInReturn(m, ma) } +} +import ImplCommon + +/** + * A call context to restrict the targets of virtual dispatch and match the + * call sites of flow into a method with flow out of a method. + * + * There are four cases: + * - `TAnyCallContext()` : No restrictions on method flow. + * - `TSpecificCall(Call call, int i)` : Flow entered through the `i`th + * parameter at the given `call`. This call improves the set of viable + * dispatch targets for at least one method call in the current callable. + * - `TSomeCall(ParameterNode p)` : Flow entered through parameter `p`. The + * originating call does not improve the set of dispatch targets for any + * method call in the current callable and was therefore not recorded. + * - `TReturn(Method m, MethodAccess ma)` : Flow reached `ma` from `m` and + * this dispatch target of `ma` implies a reduced set of dispatch origins + * to which data may flow if it should reach a `return` statement. + */ +abstract class CallContext extends TCallContext { + abstract string toString(); +} +class CallContextAny extends CallContext, TAnyCallContext { + override string toString() { result = "CcAny" } +} +abstract class CallContextCall extends CallContext { } +class CallContextSpecificCall extends CallContextCall, TSpecificCall { + override string toString() { result = "CcCall" } +} +class CallContextSomeCall extends CallContextCall, TSomeCall { + override string toString() { result = "CcSomeCall" } +} +class CallContextReturn extends CallContext, TReturn { + override string toString() { result = "CcReturn" } +} + +bindingset[cc, callable] +predicate resolveReturn(CallContext cc, Callable callable, Call call) { + cc instanceof CallContextAny and callable = viableCallable(call) + or + exists(Method m0, MethodAccess ma0 | + ma0.getEnclosingCallable() = callable and + cc = TReturn(m0, ma0) and + m0 = prunedViableImplInCallContextReverse(ma0, call) + ) +} + +bindingset[call, cc] +Callable resolveCall(Call call, CallContext cc) { + exists(Call ctx | cc = TSpecificCall(ctx, _, _) | + if reducedViableImplInCallContext(call, _, ctx) then + result = prunedViableImplInCallContext(call, ctx) + else + result = viableCallable(call) + ) or + result = viableCallable(call) and cc instanceof CallContextSomeCall or + result = viableCallable(call) and cc instanceof CallContextAny or + result = viableCallable(call) and cc instanceof CallContextReturn +} diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll new file mode 100644 index 000000000000..8bc840da0c7f --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll @@ -0,0 +1,191 @@ +private import cpp +private import DataFlowUtil + +/** Gets the instance argument of a non-static call. */ +private Node getInstanceArgument(Call call) { + result.asExpr() = call.getQualifier() + // This does not include the implicit `this` argument on auto-generated + // base class destructor calls as those do not have an AST element. +} + +/** + * A data flow node that occurs as the argument of a call and is passed as-is + * to the callable. Arguments that are wrapped in an implicit varargs array + * creation are not included, but the implicitly created array is. + * Instance arguments are also included. + */ +class ArgumentNode extends Node { + ArgumentNode() { + exists(Argument arg | this.asExpr() = arg) or + this = getInstanceArgument(_) + } + + /** + * Holds if this argument occurs at the given position in the given call. + * The instance argument is considered to have index `-1`. + */ + predicate argumentOf(Call call, int pos) { + exists(Argument arg | this.asExpr() = arg | call = arg.getCall() and pos = arg.getPosition()) or + pos = -1 and this = getInstanceArgument(call) + } +} + +/** A data flow node that occurs as the result of a `ReturnStmt`. */ +class ReturnNode extends ExprNode { + ReturnNode() { + exists(ReturnStmt ret | this.getExpr() = ret.getExpr()) + } +} + +/** + * Holds if data can flow from `node1` to `node2` in a way that loses the + * calling context. For example, this would happen with flow through a + * global or static variable. + */ +predicate jumpStep(Node n1, Node n2) { + none() +} + +/** + * Holds if `call` does not pass an implicit or explicit qualifier, i.e., a + * `this` parameter. + */ +predicate callHasQualifier(Call call) { + call.hasQualifier() + or + call.getTarget() instanceof Destructor +} + +private newtype TContent = TFieldContent(Field f) or TCollectionContent() or TArrayContent() + +/** + * A reference contained in an object. Examples include instance fields, the + * contents of a collection object, or the contents of an array. + */ +class Content extends TContent { + /** Gets a textual representation of this element. */ + abstract string toString(); + 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 RefType getDeclaringType(); + /** Gets the type of this content. */ + abstract Type getType(); + /** + * Holds if this content may contain an object of the same type as the one + * that contains this content, and if this fact should be used to compress + * access paths. + * + * Examples include the tail pointer in a linked list or the left and right + * pointers in a binary tree. + */ + predicate isSelfRef() { none() } +} +private class FieldContent extends Content, TFieldContent { + Field f; + FieldContent() { this = TFieldContent(f) } + Field getField() { result = f } + override string toString() { result = f.toString() } + override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { + f.getLocation().hasLocationInfo(path, sl, sc, el, ec) + } + override RefType getDeclaringType() { result = f.getDeclaringType() } + override Type getType() { result = f.getType() } +} +private class CollectionContent extends Content, TCollectionContent { + override string toString() { result = "collection" } + override RefType getDeclaringType() { none() } + override Type getType() { none() } +} +private class ArrayContent extends Content, TArrayContent { + override string toString() { result = "array" } + override RefType getDeclaringType() { none() } + override Type getType() { none() } +} + +/** + * Holds if data can flow from `node1` to `node2` via an assignment to `f`. + * Thus, `node2` references an object with a field `f` that contains the + * value of `node1`. + */ +predicate storeStep(Node node1, Content f, PostUpdateNode node2) { + none() // stub implementation +} + +/** + * 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) { + none() // stub implementation +} + +/** + * 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. + */ +RefType getErasedRepr(Type t) { + result instanceof VoidType // 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) { + any() // stub implementation +} + +////////////////////////////////////////////////////////////////////////////// +// Java QL library compatibility wrappers +////////////////////////////////////////////////////////////////////////////// + +class RefType extends Type { +} + +class CastExpr extends Expr { + CastExpr() { none() } // stub implementation +} + +/** An argument to a call. */ +class Argument extends Expr { + Call call; + int pos; + + Argument() { + call.getArgument(pos) = this + } + + /** Gets the call that has this argument. */ + Call getCall() { result = call } + + /** Gets the position of this argument. */ + int getPosition() { + result = pos + } +} + +class Callable extends Function { } + +/** + * An alias for `Function` in the C++ library. In the Java library, a `Method` + * is any callable except a constructor. + */ +class Method extends Function { } + +/** + * An alias for `FunctionCall` in the C++ library. In the Java library, a + * `MethodAccess` is any `Call` that does not call a constructor. + */ +class MethodAccess extends FunctionCall { + /** + * INTERNAL: Do not use. Alternative name for `getEnclosingFunction`. + */ + Callable getEnclosingCallable() { + result = this.getEnclosingFunction() + } +} diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll new file mode 100644 index 000000000000..62dca33d323a --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll @@ -0,0 +1,250 @@ +/** + * Provides C++-specific definitions for use in the data flow library. + */ +import cpp +private import semmle.code.cpp.dataflow.internal.FlowVar + +private newtype TNode = + TExprNode(Expr e) or + TParameterNode(Parameter p) { exists(p.getFunction().getBlock()) } or + TUninitializedNode(LocalVariable v) { + not v.hasInitializer() + } + +/** + * A node in a data flow graph. + * + * A node can be either an expression, a parameter, or an uninitialized local + * variable. Such nodes are created with `DataFlow::exprNode`, + * `DataFlow::parameterNode`, and `DataFlow::uninitializedNode` respectively. +*/ +class Node extends TNode { + /** Gets the function to which this node belongs. */ + Function getFunction() { + result = this.asExpr().getEnclosingFunction() + or + result = this.asParameter().getFunction() + or + result = this.asUninitialized().getFunction() + } + + /** + * INTERNAL: Do not use. Alternative name for `getFunction`. + */ + Function getEnclosingCallable() { + result = this.getFunction() + } + + /** Gets the type of this node. */ + Type getType() { + result = this.asExpr().getType() + or + result = asVariable(this).getType() + } + + /** Gets the expression corresponding to this node, if any. */ + Expr asExpr() { result = this.(ExprNode).getExpr() } + + /** Gets the parameter corresponding to this node, if any. */ + Parameter asParameter() { result = this.(ParameterNode).getParameter() } + + /** + * Gets the uninitialized local variable corresponding to this node, if + * any. + */ + LocalVariable asUninitialized() { + result = this.(UninitializedNode).getLocalVariable() + } + + /** Gets a textual representation of this element. */ + string toString() { none() } // overridden by subclasses + + /** Gets the location of this element. */ + Location getLocation() { none() } // overridden by subclasses + + /** + * Gets an upper bound on the type of this node. + */ + Type getTypeBound() { result = getType() } +} + +/** + * An expression, viewed as a node in a data flow graph. + */ +class ExprNode extends Node, TExprNode { + Expr expr; + ExprNode() { this = TExprNode(expr) } + override string toString() { result = expr.toString() } + override Location getLocation() { result = expr.getLocation() } + /** Gets the expression corresponding to this node. */ + Expr getExpr() { result = expr } +} + +/** + * The value of a parameter at function entry, viewed as a node in a data + * flow graph. + */ +class ParameterNode extends Node, TParameterNode { + Parameter param; + ParameterNode() { this = TParameterNode(param) } + override string toString() { result = param.toString() } + override Location getLocation() { result = param.getLocation() } + /** Gets the parameter corresponding to this node. */ + Parameter getParameter() { result = param } + /** + * Holds if this node is the parameter of `c` at the specified (zero-based) + * position. The implicit `this` parameter is considered to have index `-1`. + */ + predicate isParameterOf(Function f, int i) { + f.getParameter(i) = param + } +} + +/** + * The value of an uninitialized local variable, viewed as a node in a data + * flow graph. + */ +class UninitializedNode extends Node, TUninitializedNode { + LocalVariable v; + UninitializedNode() { this = TUninitializedNode(v) } + override string toString() { result = v.toString() } + override Location getLocation() { result = v.getLocation() } + /** Gets the uninitialized local variable corresponding to this node. */ + LocalVariable getLocalVariable() { result = v } +} + +/** + * A node associated with an object after an operation that might have + * changed its state. + * + * This can be either the argument to a callable after the callable returns + * (which might have mutated the argument), or the qualifier of a field after + * an update to the field. + * + * Nodes corresponding to AST elements, for example `ExprNode`, usually refer + * to the value before the update with the exception of `ClassInstanceExpr`, + * which represents the value after the constructor has run. + */ +class PostUpdateNode extends Node { + PostUpdateNode() { none() } // stub implementation + /** + * Gets the node before the state update. + */ + Node getPreUpdateNode() { none() } // stub implementation +} + +/** + * Gets the `Node` corresponding to `e`. + */ +ExprNode exprNode(Expr e) { result.getExpr() = e } + +/** + * Gets the `Node` corresponding to the value of `p` at function entry. + */ +ParameterNode parameterNode(Parameter p) { result.getParameter() = p } + +/** + * Gets the `Node` corresponding to the value of an uninitialized local + * variable `v`. + */ +UninitializedNode uninitializedNode(LocalVariable v) { + result.getLocalVariable() = v +} + +private Variable asVariable(Node node) { + result = node.asParameter() + or + result = node.asUninitialized() +} + +/** + * Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local + * (intra-procedural) step. + */ +predicate localFlowStep(Node nodeFrom, Node nodeTo) { + // Expr -> Expr + exprToExprStep_nocfg(nodeFrom.asExpr(), nodeTo.asExpr()) + or + // Node -> FlowVar -> VariableAccess + exists(FlowVar var | + ( + exprToVarStep(nodeFrom.asExpr(), var) + or + varSourceBaseCase(var, asVariable(nodeFrom)) + ) and + varToExprStep(var, nodeTo.asExpr()) + ) +} + +/** + * Holds if data flows from `source` to `sink` in zero or more local + * (intra-procedural) steps. + */ +predicate localFlow(Node source, Node sink) { + localFlowStep*(source, sink) +} + +/** + * Holds if the initial value of `v`, if it is a source, flows to `var`. + */ +private predicate varSourceBaseCase(FlowVar var, Variable v) { + var.definedByInitialValue(v) +} + +/** + * Holds if `var` is defined by an assignment-like operation that causes flow + * directly from `assignedExpr` to `var`, _and_ `assignedExpr` evaluates to + * the same value as what is assigned to `var`. + */ +private predicate exprToVarStep(Expr assignedExpr, FlowVar var) { + exists(ControlFlowNode operation | + var.definedByExpr(assignedExpr, operation) and + not operation instanceof PostfixCrementOperation + ) +} + +/** + * Holds if the expression `e` is an access of the variable `var`. + */ +private predicate varToExprStep(FlowVar var, Expr e) { + e = var.getAnAccess() +} + +/** + * Holds if data flows from `fromExpr` to `toExpr` directly, in the case + * where `toExpr` is the immediate AST parent of `fromExpr`. For example, + * data flows from `x` and `y` to `b ? x : y`. + */ +private predicate exprToExprStep_nocfg(Expr fromExpr, Expr toExpr) { + toExpr = any(ConditionalExpr cond | + fromExpr = cond.getThen() or fromExpr = cond.getElse() + ) + or + toExpr = any(AssignExpr assign | + fromExpr = assign.getRValue() + ) + or + toExpr = any(CommaExpr comma | + fromExpr = comma.getRightOperand() + ) + or + toExpr = any(PostfixCrementOperation op | + fromExpr = op.getOperand() + ) + or + toExpr = any(FunctionCall moveCall | + moveCall.getTarget().getNamespace().getName() = "std" and + moveCall.getTarget().getName() = "move" and + fromExpr = moveCall.getArgument(0) + ) +} + +VariableAccess getAnAccessToAssignedVariable(Expr assign) { + (assign instanceof Assignment + or + assign instanceof CrementOperation) and + exists(FlowVar var | + var.definedByExpr(_, assign) and + result = var.getAnAccess() + ) +} diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll new file mode 100644 index 000000000000..963b648727fb --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll @@ -0,0 +1,534 @@ +/** + * Provides a class for handling variables in the data flow analysis. + */ +import cpp +private import semmle.code.cpp.controlflow.SSA +private import semmle.code.cpp.dataflow.internal.SubBasicBlocks + +/** + * A conceptual variable that is assigned only once, like an SSA variable. This + * class is used for tracking data flow through variables, where the desired + * semantics is sometimes different from what the SSA library provides. Unlike + * SSA, there are no _phi_ nodes; instead, each `VariableAccess` may be + * associated with more than one `FlowVar`. + * + * Each instance of this class corresponds to a modification or an initial + * value of a variable. A `FlowVar` has exactly one result for either + * `definedByExpr` or `definedByInitialValue`. The documentation on those two + * member predicates explains how a `FlowVar` relates to syntactic constructs of + * the language. + */ +cached class FlowVar extends TFlowVar { + /** + * Gets a `VariableAccess` that _may_ take its value from `this`. Consider + * the following snippet. + * + * ``` + * int x = 0; + * if (b) { f(x); x = 1; } + * g(x) + * ``` + * + * The access to `x` in `f(x)` may take its value from the `FlowVar` + * corresponding to `int x = 0`, while the access to `x` in `g(x)` may take + * its value from the `FlowVar` corresponding to `int x = 0` or the `FlowVar` + * corresponding to `x = 1`. The `x` in `x = 1` is not considered to be an + * access. + */ + cached abstract VariableAccess getAnAccess(); + + /** + * Holds if this `FlowVar` corresponds to a modification occurring when `node` is + * evaluated, receiving a value best described by `e`. The following is an + * exhaustive list of cases where this may happen. + * + * - `node` is an `Initializer` and `e` is its contained expression. + * - `node` is an `AssignExpr`, and `e` is its right-hand side. + * - `node` is an `AssignOperation`, and `e` is `node`. + * - `node` is a `CrementOperation`, and `e` is `node`. The case where + * `node instanceof PostCrementOperation` is an exception to the rule that + * `this` contains the value of `e` after the evaluation of `node`. + */ + cached abstract predicate definedByExpr(Expr e, ControlFlowNode node); + + /** + * Holds if this `FlowVar` corresponds to the initial value of `v`. The following + * is an exhaustive list of cases where this may happen. + * + * - `v` is a parameter, and `this` contains the value of the parameter at + * the entry point of its function body. + * - `v` is an uninitialized local variable, and `v` contains its (arbitrary) + * value before it is reassigned. If it can be statically determined that a + * local variable is always overwritten before it is used, there is no + * `FlowVar` instance for the uninitialized value of that variable. + */ + cached abstract predicate definedByInitialValue(LocalScopeVariable v); + + /** Gets a textual representation of this element. */ + cached abstract string toString(); + + /** Gets the location of this element. */ + cached abstract Location getLocation(); +} + +private import FlowVar_internal + +/** + * Provides classes and predicates that ought to be private but cannot use the + * `private` annotation because they may be referred to by unit tests. + */ +module FlowVar_internal { + /** + * For various reasons, not all variables handled perfectly by the SSA library. + * Ideally, this predicate should become larger as the SSA library improves. + * Before we can remove the `BlockVar` class completely, the SSA library needs + * the following improvements. + * - Considering uninitialized local variables to be definitions. + * - Supporting fields, globals and statics like the Java SSA library does. + * - Supporting all local variables, even if their address is taken by + * address-of, reference assignments, or lambdas. + */ + predicate fullySupportedSsaVariable(Variable v) { + v = any(SsaDefinition def).getAVariable() 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. + not mayBeUsedUninitialized(v, _) and + // If `v` may be a variable that is always overwritten in a loop that + // always executes at least once, we give it special treatment in + // `BlockVar`, somewhat analogous to unrolling the first iteration of the + // loop. + not exists(AlwaysTrueUponEntryLoop loop | + loop.alwaysAssignsBeforeLeavingCondition(_, _, v) + ) and + // 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.getType().getUnspecifiedType() instanceof ReferenceType + } + + /** + * Holds if `sbb` is the `SubBasicBlock` where `v` receives its initial value. + * See the documentation for `FlowVar.definedByInitialValue`. + */ + predicate blockVarDefinedByVariable( + SubBasicBlock sbb, LocalScopeVariable v) + { + sbb = v.(Parameter).getFunction().getEntryPoint() + or + exists(DeclStmt declStmt | + declStmt.getDeclaration(_) = v.(LocalVariable) and + sbb.contains(declStmt) and + mayBeUsedUninitialized(v, _) + ) + } + + newtype TFlowVar = + TSsaVar(SsaDefinition def, LocalScopeVariable v) { + fullySupportedSsaVariable(v) and + v = def.getAVariable() + } + or + TBlockVar(SubBasicBlock sbb, Variable v) { + not fullySupportedSsaVariable(v) and + reachable(sbb) and + ( + initializer(sbb.getANode(), v, _) + or + assignmentLikeOperation(sbb, v, _) + or + blockVarDefinedByVariable(sbb, v) + ) + } + + /** + * A variable whose analysis is backed by the SSA library. + */ + class SsaVar extends TSsaVar, FlowVar { + SsaDefinition def; + LocalScopeVariable v; + + SsaVar() { this = TSsaVar(def, v) } + + override VariableAccess getAnAccess() { + // There is no need to know about direct accesses to phi nodes because + // the data flow library will never see those, and the `FlowVar` library + // is only meant to be used by the data flow library. + this.isNonPhi() and + ( // This base case could be included in the transitive case by changing + // `+` to `*`, but that's slower because it goes through the `TSsaVar` + // indirection. + result = def.getAUse(v) + or + exists(SsaDefinition descendentDef | + getASuccessorSsaVar+() = TSsaVar(descendentDef, _) and + result = descendentDef.getAUse(v) + ) + ) + } + + override predicate definedByExpr(Expr e, ControlFlowNode node) { + e = def.getDefiningValue(v) and + (if def.getDefinition() = v.getInitializer().getExpr() + then node = v.getInitializer() + else node = def.getDefinition()) + } + + override predicate definedByInitialValue(LocalScopeVariable param) { + def.definedByParameter(param) and + param = v + } + + /** + * Holds if this `SsaVar` corresponds to a non-phi definition. Users of this + * library will never directly use an `SsaVar` that comes from a phi node, + * so such values are purely for implementation use. + */ + private predicate isNonPhi() { + // This implementation is positively phrased in terms of these two helper + // predicates because it performs better than phrasing it negatively in + // terms of `def.isPhiNode`. + this.definedByExpr(_, _) + or + this.definedByInitialValue(_) + } + + /** + * Holds if `result` might have the same value as `this` because `result` is + * a phi node with `this` as input. + */ + private SsaVar getASuccessorSsaVar() { + exists(SsaDefinition succDef | + result = TSsaVar(succDef, v) and + def = succDef.getAPhiInput(v) + ) + } + + override string toString() { result = def.toString(v) } + + override Location getLocation() { result = def.getLocation() } + } + + /** + * A variable whose analysis is backed by a simple control flow analysis that + * does not perform as well as the SSA library but gives better results for + * data flow purposes in some cases. + */ + class BlockVar extends TBlockVar, FlowVar { + SubBasicBlock sbb; + Variable v; + + BlockVar() { this = TBlockVar(sbb, v) } + + override VariableAccess getAnAccess() { + result.getTarget() = v and + result = getAReachedBlockVarSBB(this).getANode() and + not overwrite(result, _) + } + + override predicate definedByInitialValue(LocalScopeVariable lsv) { + blockVarDefinedByVariable(sbb, lsv) and + lsv = v + } + + override predicate definedByExpr(Expr e, ControlFlowNode node) { + assignmentLikeOperation(node, v, e) and + node = sbb + or + initializer(node, v, e) and + node = sbb.getANode() + } + + override string toString() { + exists(Expr e | + this.definedByExpr(e, _) and + result = v +" := "+ e + ) + or + this.definedByInitialValue(_) and + result = "initial value of "+ v + or + // impossible case + not this.definedByExpr(_, _) and + not this.definedByInitialValue(_) and + result = "undefined "+ v + } + + override Location getLocation() { result = sbb.getStart().getLocation() } + } + + /** Type-specialized version of `getEnclosingElement`. */ + private ControlFlowNode getCFNParent(ControlFlowNode node) { + result = node.getEnclosingElement() + } + + /** + * A for-loop or while-loop whose condition is always true upon entry but not + * always true after the first iteration. + */ + class AlwaysTrueUponEntryLoop extends Stmt { + AlwaysTrueUponEntryLoop() { + this.(WhileStmt).conditionAlwaysTrueUponEntry() and + not this.(WhileStmt).conditionAlwaysTrue() + or + this.(ForStmt).conditionAlwaysTrueUponEntry() and + not this.(ForStmt).conditionAlwaysTrue() + } + + /** + * Holds if this loop always assigns to `v` before leaving through an edge + * from `bbInside` in its condition to `bbOutside` outside the loop. Also, + * `v` must be used outside the loop. + */ + predicate alwaysAssignsBeforeLeavingCondition( + BasicBlock bbInside, BasicBlock bbOutside, Variable v + ) { + v = this.getARelevantVariable() and + this.bbInLoopCondition(bbInside) and + not this.bbInLoop(bbOutside) and + bbOutside = bbInside.getASuccessor() and + not reachesWithoutAssignment(bbInside, v) + } + + /** + * Gets a variable that is assigned in this loop and read outside the loop. + */ + private Variable getARelevantVariable() { + exists(BasicBlock bbAssign | + assignmentLikeOperation(bbAssign.getANode(), result, _) and + this.bbInLoop(bbAssign) + ) and + exists(VariableAccess va | + va.getTarget() = result and + readAccess(va) and + bbNotInLoop(va.getBasicBlock()) + ) + } + + private predicate bbInLoopCondition(BasicBlock bb) { + getCFNParent*(bb.getANode()) = this.(Loop).getCondition() + } + + private predicate bbInLoop(BasicBlock bb) { + bbDominates(this.(Loop).getStmt(), bb) + or + bbInLoopCondition(bb) + } + + predicate bbNotInLoop(BasicBlock bb) { + not this.bbInLoop(bb) and + bb.getEnclosingFunction() = this.getEnclosingFunction() + } + + /** + * Holds if `bb` is a basic block inside this loop where `v` has not been + * overwritten at the end of `bb`. + */ + private predicate reachesWithoutAssignment(BasicBlock bb, Variable v) { + ( + // For the type of loop we are interested in, the body is always a + // basic block. + bb = this.(Loop).getStmt() and + v = this.getARelevantVariable() + or + reachesWithoutAssignment(bb.getAPredecessor(), v) and + this.bbInLoop(bb) + ) and + not assignmentLikeOperation(bb.getANode(), v, _) + } + } + + /** + * Holds if some loop always assigns to `v` before leaving through an edge + * from `bbInside` in its condition to `bbOutside` outside the loop, where + * (`sbbDef`, `v`) is a `BlockVar` defined outside the loop. Also, `v` must + * be used outside the loop. + */ + predicate skipLoop( + SubBasicBlock sbbInside, SubBasicBlock sbbOutside, + SubBasicBlock sbbDef, Variable v + ) { + exists(AlwaysTrueUponEntryLoop loop, + BasicBlock bbInside, BasicBlock bbOutside | + loop.alwaysAssignsBeforeLeavingCondition(bbInside, bbOutside, v) and + bbInside = sbbInside.getBasicBlock() and + bbOutside = sbbOutside.getBasicBlock() and + sbbInside.lastInBB() and + sbbOutside.firstInBB() and + loop.bbNotInLoop(sbbDef.getBasicBlock()) and + exists(TBlockVar(sbbDef, v)) + ) + } + + pragma[nomagic] + private SubBasicBlock getAReachedBlockVarSBB(TBlockVar start) { + start = TBlockVar(result, _) + or + exists(SubBasicBlock mid, SubBasicBlock sbbDef, Variable v | + start = TBlockVar(sbbDef, v) and + mid = getAReachedBlockVarSBB(start) and + result = mid.getASuccessor() and + not skipLoop(mid, result, sbbDef, v) and + not assignmentLikeOperation(result, v, _) + ) + } + + /** + * A local variable that is uninitialized immediately after its declaration. + */ + class UninitializedLocalVariable extends LocalVariable { + UninitializedLocalVariable() { + not this.hasInitializer() and + not this.isStatic() + } + } + + /** Holds if `va` may be an uninitialized access to `v`. */ + predicate mayBeUsedUninitialized(UninitializedLocalVariable v, VariableAccess va) { + exists(BasicBlock bb, int vaIndex | + va.getTarget() = v and + readAccess(va) and + va = bb.getNode(vaIndex) and + notAccessedAtStartOfBB(v, bb) and + ( + vaIndex < indexOfFirstOverwriteInBB(v, bb) + or + not exists(indexOfFirstOverwriteInBB(v, bb)) + ) + ) + } + + /** + * Holds if `v` has not been accessed at the start of `bb`, for a variable + * `v` where `allReadsDominatedByOverwrite(v)` does not hold. + */ + predicate notAccessedAtStartOfBB(UninitializedLocalVariable v, BasicBlock bb) { + // Start from a BB containing an uninitialized variable + bb.getANode().(DeclStmt).getDeclaration(_) = v and + // Only consider variables for which initialization before reading cannot + // be proved by simpler means. This predicate is expensive in time and + // space, whereas `allReadsDominatedByOverwrite` is cheap. + not allReadsDominatedByOverwrite(v) + or + exists(BasicBlock pred | + pred = bb.getAPredecessor() and + notAccessedAtStartOfBB(v, pred) and + // Stop searching when `v` is accessed. + not pred.getANode() = v.getAnAccess() + ) + } + + /** + * Holds if all read accesses of `v` are dominated by an overwrite of `v`. + */ + predicate allReadsDominatedByOverwrite(UninitializedLocalVariable v) { + forall(VariableAccess va | + va.getTarget() = v and + readAccess(va) + | dominatedByOverwrite(v, va) + ) + } + + /** Holds if `va` accesses `v` and is dominated by an overwrite of `v`. */ + predicate dominatedByOverwrite(UninitializedLocalVariable v, + VariableAccess va) + { + exists(BasicBlock bb, int vaIndex | + va = bb.getNode(vaIndex) and + va.getTarget() = v + | vaIndex > indexOfFirstOverwriteInBB(v, bb) + or + bbStrictlyDominates(getAnOverwritingBB(v), bb) + ) + } + + /** Gets a basic block in which `v` is overwritten. */ + BasicBlock getAnOverwritingBB(UninitializedLocalVariable v) { + exists(indexOfFirstOverwriteInBB(v, result)) + } + + int indexOfFirstOverwriteInBB(LocalVariable v, BasicBlock bb) { + result = min(int i | overwrite(v.getAnAccess(), bb.getNode(i))) + } + + /** + * Holds if the value of the variable accessed at `va` may affect the execution + * of the program. + */ + predicate readAccess(VariableAccess va) { + reachable(va) and + not overwrite(va, _) and + not va = any(SizeofExprOperator so).getAChild+() and + not va = any(AlignofExprOperator ao).getAChild+() + } + + /** + * Holds if the value of the variable accessed at `va` is completely + * overwritten at `node`, where `va` is uniquely determined by `node`. + */ + predicate overwrite(VariableAccess va, ControlFlowNode node) { + va = node.(AssignExpr).getLValue() + } + + /** + * Holds if `v` is modified as a side effect of evaluating `node`, receiving a + * value best described by `e`. This corresponds to `FlowVar::definedByExpr`, + * except that the case where `node instanceof Initializer` is covered by + * `initializer` instead of this predicate. + */ + predicate assignmentLikeOperation( + ControlFlowNode node, Variable v, Expr assignedExpr) + { + // Together, the two following cases cover `Assignment` + node = any(AssignExpr ae | + v = ae.getLValue().(VariableAccess).getTarget() and + assignedExpr = ae.getRValue() + ) + or + node = any(AssignOperation ao | + v = ao.getLValue().(VariableAccess).getTarget() 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, + // `x` will contain the same value that `x += 1` evaluated to. + assignedExpr = ao + ) + or + // This case does not add further data flow paths, except if a + // `PrefixCrementOperation` is itself a source + node = any(CrementOperation op | + v = op.getOperand().(VariableAccess).getTarget() and + assignedExpr = op + ) + } + + /** + * Holds if `v` is initialized by `init` to have value `assignedExpr`. + */ + predicate initializer( + Initializer init, LocalVariable v, Expr assignedExpr) + { + v = init.getDeclaration() and + assignedExpr = init.getExpr() + } + + /** + * A point in a basic block where a non-SSA variable may have a different value + * than it did elsewhere in the same basic block. Extending this class + * configures the `SubBasicBlocks` library as needed for the implementation of + * this library. + */ + class DataFlowSubBasicBlockCutNode extends SubBasicBlockCutNode { + DataFlowSubBasicBlockCutNode() { + exists(Variable v | + not fullySupportedSsaVariable(v) and + assignmentLikeOperation(this, v, _) + // 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 + // entries for the sake of `Parameter`s since we are already guaranteed + // to have a `SubBasicBlock` starting at procedure entry. + ) + } + } +} /* module FlowVar_internal */ diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/SubBasicBlocks.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/SubBasicBlocks.qll new file mode 100644 index 000000000000..026e909cfaf3 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/SubBasicBlocks.qll @@ -0,0 +1,187 @@ +// +// NOTE: Maintain this file in synchrony with +// semmlecode-cpp-queries/semmle/code/cpp/controlflow/SubBasicBlocks.qll +// +// This is a private copy of the `SubBasicBlocks` library for internal use by +// the data flow library. Having an extra copy can prevent non-monotonic +// recursion errors in queries that use both the data flow library and the +// `SubBasicBlocks` library. + +/** + * Provides the `SubBasicBlock` class, used for partitioning basic blocks in + * smaller pieces. + */ +import cpp + +/** + * An abstract class that directs where in the control-flow graph a new + * `SubBasicBlock` must start. If a `ControlFlowNode` is an instance of this + * class, that node is guaranteed to be the first node in a `SubBasicBlock`. + * If multiple libraries use the `SubBasicBlock` library, basic blocks may be + * split in more places than either library expects, but nothing should break + * as a direct result of that. + */ +abstract class SubBasicBlockCutNode extends @cfgnode { + SubBasicBlockCutNode() { + // Some control-flow nodes are not in any basic block. This includes + // `Conversion`s, expressions that are evaluated at compile time, default + // arguments, and `Function`s without implementation. + exists(this.(ControlFlowNode).getBasicBlock()) + } + string toString() { result = "SubBasicBlockCutNode" } +} + +/** + * A block that can be smaller than or equal to a `BasicBlock`. Use this class + * when `ControlFlowNode` is too fine-grained and `BasicBlock` too + * coarse-grained. Their successor graph is like that of basic blocks, except + * that the blocks are split up with an extra edge right before any instance of + * the abstract class `SubBasicBlockCutNode`. Users of this library must + * therefore extend `SubBasicBlockCutNode` to direct where basic blocks will be + * split up. + */ +class SubBasicBlock extends @cfgnode { + SubBasicBlock() { + this instanceof BasicBlock + or + this instanceof SubBasicBlockCutNode + } + + /** Gets the basic block in which this `SubBasicBlock` is contained. */ + BasicBlock getBasicBlock() { + result = this.(ControlFlowNode).getBasicBlock() + } + + /** + * Holds if this `SubBasicBlock` comes first in its basic block. This is the + * only condition under which a `SubBasicBlock` may have multiple + * predecessors. + */ + predicate firstInBB() { + exists(BasicBlock bb | this.getPosInBasicBlock(bb) = 0) + } + + /** + * Holds if this `SubBasicBlock` comes last in its basic block. This is the + * only condition under which a `SubBasicBlock` may have multiple successors. + */ + predicate lastInBB() { + exists(BasicBlock bb | + this.getPosInBasicBlock(bb) = countSubBasicBlocksInBasicBlock(bb) - 1 + ) + } + + /** + * Gets the position of this `SubBasicBlock` in its containing basic block + * `bb`, where `bb` is equal to `getBasicBlock()`. + */ + int getPosInBasicBlock(BasicBlock bb) { + exists(int nodePos, int rnk | + bb = this.(ControlFlowNode).getBasicBlock() and + this = bb.getNode(nodePos) and + nodePos = rank[rnk](int i | exists(SubBasicBlock n | n = bb.getNode(i))) and + result = rnk - 1 + ) + } + + /** Gets a successor in the control-flow graph of `SubBasicBlock`s. */ + SubBasicBlock getASuccessor() { + this.lastInBB() and + result = this.getBasicBlock().getASuccessor() + or + exists(BasicBlock bb | + result.getPosInBasicBlock(bb) = this.getPosInBasicBlock(bb) + 1 + ) + } + + /** + * Gets the `pos`th control-flow node in this `SubBasicBlock`. Positions + * start from 0, and the node at position 0 always exists and compares equal + * to `this`. + */ + ControlFlowNode getNode(int pos) { + exists(BasicBlock bb | bb = this.getBasicBlock() | + exists(int thisPos | this = bb.getNode(thisPos) | + result = bb.getNode(thisPos + pos) and + pos >= 0 and + pos < this.getNumberOfNodes() + ) + ) + } + + /** Gets a control-flow node in this `SubBasicBlock`. */ + ControlFlowNode getANode() { + result = this.getNode(_) + } + + /** Holds if `this` contains `node`. */ + predicate contains(ControlFlowNode node) { + node = this.getANode() + } + + /** Gets a predecessor in the control-flow graph of `SubBasicBlock`s. */ + SubBasicBlock getAPredecessor() { + result.getASuccessor() = this + } + + string toString() { result = "SubBasicBlock" } + + /** + * Gets a node such that the control-flow edge `(this, result)` may be taken + * when the final node of this `SubBasicBlock` is a conditional expression + * and evaluates to true. + */ + SubBasicBlock getATrueSuccessor() { + this.lastInBB() and + result = this.getBasicBlock().getATrueSuccessor() + } + + /** + * Gets a node such that the control-flow edge `(this, result)` may be taken + * when the final node of this `SubBasicBlock` is a conditional expression + * and evaluates to false. + */ + SubBasicBlock getAFalseSuccessor() { + this.lastInBB() and + result = this.getBasicBlock().getAFalseSuccessor() + } + + /** + * Gets the number of control-flow nodes in this `SubBasicBlock`. There is + * always at least one. + */ + int getNumberOfNodes() { + exists(BasicBlock bb | bb = this.getBasicBlock() | + exists(int thisPos | this = bb.getNode(thisPos) | + this.lastInBB() and + result = bb.length() - thisPos + or + exists(SubBasicBlock succ, int succPos | + succ.getPosInBasicBlock(bb) = this.getPosInBasicBlock(bb) + 1 and + bb.getNode(succPos) = succ and + result = succPos - thisPos + ) + ) + ) + } + + /** Gets the last control-flow node in this `SubBasicBlock`. */ + ControlFlowNode getEnd() { + result = this.getNode(this.getNumberOfNodes() - 1) + } + + /** Gets the first control-flow node in this `SubBasicBlock`. */ + ControlFlowNode getStart() { + result = this + } + + pragma[noinline] + Function getEnclosingFunction() { + result = this.getStart().getControlFlowScope() + } +} + +/** Gets the number of `SubBasicBlock`s in the given basic block. */ +private int countSubBasicBlocksInBasicBlock(BasicBlock bb) { + result = strictcount(SubBasicBlock sbb | sbb.getBasicBlock() = bb) +} diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Access.qll b/cpp/ql/src/semmle/code/cpp/exprs/Access.qll new file mode 100644 index 000000000000..733054e7262c --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/exprs/Access.qll @@ -0,0 +1,319 @@ +import semmle.code.cpp.exprs.Expr +import semmle.code.cpp.Variable +import semmle.code.cpp.Enum +private import semmle.code.cpp.dataflow.EscapesTree + +/** + * A C/C++ access expression. This refers to a function, variable, or enum constant. + */ +abstract class Access extends Expr, NameQualifiableElement { + /** Gets the accessed function, variable, or enum constant. */ + abstract Declaration getTarget(); + + override predicate mayBeImpure() { + none() + } + override predicate mayBeGloballyImpure() { + none() + } + + override string toString() { none() } +} + +/** + * A C/C++ enum constant access expression. + */ +class EnumConstantAccess extends Access, @varaccess { + EnumConstantAccess() { + exists(EnumConstant c | varbind(this, c)) + } + + /** Gets the accessed enum constant. */ + override EnumConstant getTarget() { varbind(this, result) } + + /** Gets a textual representation of this enum constant access. */ + override string toString() { result = this.getTarget().getName() } +} + +/** + * A C/C++ variable access expression. + */ +class VariableAccess extends Access, @varaccess { + VariableAccess() { + not exists(EnumConstant c | varbind(this, c)) + } + + /** Gets the accessed variable. */ + override Variable getTarget() { varbind(this, result) } + + /** + * Holds if this variable access is providing an LValue in a meaningful way. + * For example, this includes accesses on the left-hand side of an assignment. + * It does not include accesses on the right-hand side of an assignment, even if they could appear on the left-hand side of some assignment. + */ + predicate isUsedAsLValue() { + exists(Assignment a | a.getLValue() = this) + or exists(CrementOperation c | c.getOperand() = this) + or exists(AddressOfExpr addof | addof.getOperand() = this) + or exists(ReferenceToExpr rte | this.getConversion() = rte) + or exists(ArrayToPointerConversion atpc | this.getConversion() = atpc) + } + + /** + * Holds if this variable access is in a position where it is (directly) modified, + * for instance by an assignment or increment/decrement operator. + */ + predicate isModified() { + exists(Assignment a | a.getLValue() = this) + or exists(CrementOperation c | c.getOperand() = this) + or exists(FunctionCall c | c.getQualifier() = this and c.getTarget().hasName("operator=")) + } + + /** Holds if this variable access is an rvalue. */ + predicate isRValue() { + not exists(AssignExpr ae | ae.getLValue() = this) and + not exists(AddressOfExpr addof | addof.getOperand() = this) and + not exists(ReferenceToExpr rte | this.getConversion() = rte) and + not exists(ArrayToPointerConversion atpc | this.getConversion() = atpc) + } + + /** + * Gets the expression generating the variable being accessed. + * + * As a few examples: + * For `ptr->x`, this gives `ptr`. + * For `(*ptr).x`, this gives `(*ptr)`. + * For `smart_ptr->x`, this gives the call to `operator->`. + * + * This applies mostly to FieldAccesses, but also to static member variables accessed + * "through" a pointer. Note that it does NOT apply to static member variables accessed + * through a type name, as in that case the type name is a qualifier on the variable + * rather than a qualifier on the access. + */ + Expr getQualifier() { this.getChild(-1) = result } + + /** Gets a textual representation of this variable access. */ + override string toString() { + if exists(this.getTarget()) then + result = this.getTarget().getName() + else + result = "variable access" + } + + override predicate mayBeImpure() { + this.getQualifier().mayBeImpure() or + this.getTarget().getType().isVolatile() + } + override predicate mayBeGloballyImpure() { + this.getQualifier().mayBeGloballyImpure() or + this.getTarget().getType().isVolatile() + } + + /** + * Holds if this access is used to get the address of the underlying variable + * in such a way that the address might escape. This can be either explicit, + * for example `&x`, or implicit, for example `T& y = x`. + */ + predicate isAddressOfAccess() { + variableAddressEscapesTree(this, _) + } + + /** + * Holds if this access is used to get the address of the underlying variable + * in such a way that the address might escape as a pointer or reference to + * non-const data. This can be either explicit, for example `&x`, or + * implicit, for example `T& y = x`. + */ + predicate isAddressOfAccessNonConst() { + variableAddressEscapesTreeNonConst(this, _) + } +} + +/** + * A C/C++ field access expression. + */ +class FieldAccess extends VariableAccess { + FieldAccess() { exists(Field f | varbind(this, f)) } + + /** Gets the accessed field. */ + override Field getTarget() { result = super.getTarget() } +} + +/** + * A field access of the form `obj->field`. The type of `obj` is a pointer, + * so this is equivalent to `(*obj).field`. + */ +class PointerFieldAccess extends FieldAccess { + PointerFieldAccess() { + exists (PointerType t + | t = getQualifier().getFullyConverted().getType().getUnspecifiedType() and + t.getBaseType() instanceof Class) + } +} + +/** + * A field access of the form `obj.field`. The type of `obj` is either a + * class/struct/union or a reference to one. `DotFieldAccess` has two + * sub-classes, `ValueFieldAccess` and `ReferenceFieldAccess`, to + * distinguish whether or not the type of `obj` is a reference type. + */ +class DotFieldAccess extends FieldAccess { + DotFieldAccess() { + exists (Class c + | c = getQualifier().getFullyConverted().getType().getUnspecifiedType()) + } +} + +/** + * A field access of the form `obj.field`, where the type of `obj` is a + * reference to a class/struct/union. + */ +class ReferenceFieldAccess extends DotFieldAccess { + ReferenceFieldAccess() { + exprHasReferenceConversion(this.getQualifier()) + } +} + +/** + * A field access of the form `obj.field`, where the type of `obj` is a + * class/struct/union (and not a reference). + */ +class ValueFieldAccess extends DotFieldAccess { + ValueFieldAccess() { + not exprHasReferenceConversion(this.getQualifier()) + } +} + +/** + * Holds if `c` is a conversion from type `T&` to `T` (or from `T&&` to + * `T`). + */ +private predicate referenceConversion(Conversion c) { + c.getType() = c.getExpr().getType().(ReferenceType).getBaseType() +} + +/** + * Holds if `e` is a reference expression (that is, it has a type of the + * form `T&`), which is converted to a value. For example: + * + * ``` + * int myfcn(MyStruct &x) { + * return x.field; + * } + * ``` + * + * In this example, the type of `x` is `MyStruct&`, but it gets implicitly + * converted to `MyStruct` in the expression `x.field`. + */ +private predicate exprHasReferenceConversion(Expr e) { + referenceConversion(e.getConversion+()) +} + +/** + * A field access of a field of `this`. The access has no qualifier because + * the use of `this` is implicit. For example, `field` is equivalent to + * `this->field` if `field` is a member of `this`. + * + * Note: the C++ front-end often automatically desugars `field` to + * `this->field`, so most implicit accesses of `this->field` are instances + * of `PointerFieldAccess` (with `ThisExpr` as the qualifier), not + * `ImplicitThisFieldAccess`. + */ +class ImplicitThisFieldAccess extends FieldAccess { + ImplicitThisFieldAccess() { + not exists (this.getQualifier()) + } +} + +/** + * A C/C++ function access expression. + */ +class FunctionAccess extends Access, @routineexpr { + FunctionAccess() { not iscall(this,_) } + + /** Gets the accessed function. */ + override Function getTarget() { funbind(this, result) } + + /** Gets a textual representation of this function access. */ + override string toString() { + if exists(this.getTarget()) then + result = this.getTarget().getName() + else + result = "function access" + } +} + +/** + * An access to a parameter of a function signature for the purposes of a decltype. + * + * For example, given the following code: + * ``` + * template + * auto add(L lhs, R rhs) -> decltype(lhs + rhs) { + * return lhs + rhs; + * } + * ``` + * The return type of the function is a decltype, the expression of which contains + * an add expression, which in turn has two ParamAccessForType children. + */ +class ParamAccessForType extends Expr, @param_ref { + override string toString() { + result = "param access" + } +} + +/** + * An access to a type. This occurs in certain contexts where a built-in + * works on types directly rather than variables, expressions etc. + */ +class TypeName extends Expr, @type_operand { + override string toString() { + result = this.getType().getName() + } +} + +/** + * A C/C++ array access expression. + * + * For calls to operator[], which look syntactically identical, see OverloadedArrayExpr. + */ +class ArrayExpr extends Expr, @subscriptexpr { + /** + * Gets the array or pointer expression being subscripted. + * + * This is arr in both arr[0] and 0[arr]. + */ + Expr getArrayBase() { result = this.getChild(0) } + + /** + * Gets the expression giving the index into the array. + * + * This is 0 in both arr[0] and 0[arr]. + */ + Expr getArrayOffset() { result = this.getChild(1) } + + /** + * Holds if this array access is in a position where it is (directly) modified, + * for instance by an assignment or an increment/decrement operation. + */ + predicate isModified() { + exists(Assignment a | a.getLValue() = this) + or exists(CrementOperation c | c.getOperand() = this) + or exists(FunctionCall c | c.getQualifier() = this and c.getTarget().hasName("operator=")) + } + + override string toString() { result = "access to array" } + + override predicate mayBeImpure() { + this.getArrayBase().mayBeImpure() or + this.getArrayOffset().mayBeImpure() or + this.getArrayBase().getFullyConverted().getType().(DerivedType).getBaseType().isVolatile() or + this.getArrayOffset().getFullyConverted().getType().(DerivedType).getBaseType().isVolatile() + } + override predicate mayBeGloballyImpure() { + this.getArrayBase().mayBeGloballyImpure() or + this.getArrayOffset().mayBeGloballyImpure() or + this.getArrayBase().getFullyConverted().getType().(DerivedType).getBaseType().isVolatile() or + this.getArrayOffset().getFullyConverted().getType().(DerivedType).getBaseType().isVolatile() + } +} diff --git a/cpp/ql/src/semmle/code/cpp/exprs/ArithmeticOperation.qll b/cpp/ql/src/semmle/code/cpp/exprs/ArithmeticOperation.qll new file mode 100644 index 000000000000..7662ec4a7757 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/exprs/ArithmeticOperation.qll @@ -0,0 +1,297 @@ +import semmle.code.cpp.exprs.Expr + +/** + * A C/C++ arithmetic operation. + */ +abstract class UnaryArithmeticOperation extends UnaryOperation { +} + +/** + * A C/C++ unary minus expression. + */ +class UnaryMinusExpr extends UnaryArithmeticOperation, @arithnegexpr { + override string getOperator() { result = "-" } + + override int getPrecedence() { result = 15 } +} + +/** + * A C/C++ unary plus expression. + */ +class UnaryPlusExpr extends UnaryArithmeticOperation, @unaryplusexpr { + override string getOperator() { result = "+" } + + override int getPrecedence() { result = 15 } +} + +/** + * A C/C++ GNU conjugation expression. + */ +class ConjugationExpr extends UnaryArithmeticOperation, @conjugation { + override string getOperator() { result = "~" } +} + +/** + * A C/C++ `++` or `--` expression (either prefix or postfix). + * + * Note that this doesn't include calls to user-defined `operator++` + * or `operator--`. + */ +abstract class CrementOperation extends UnaryArithmeticOperation { + override predicate mayBeImpure() { + any() + } + override predicate mayBeGloballyImpure() { + not exists(VariableAccess va, LocalScopeVariable v | + va = this.getOperand() + and v = va.getTarget() + and not va.getConversion+() instanceof ReferenceDereferenceExpr + and not v.isStatic()) + } +} + +/** + * A C/C++ `++` expression (either prefix or postfix). + * + * Note that this doesn't include calls to user-defined `operator++`. + */ +abstract class IncrementOperation extends CrementOperation { +} + +/** + * A C/C++ `--` expression (either prefix or postfix). + * + * Note that this doesn't include calls to user-defined `operator--`. + */ +abstract class DecrementOperation extends CrementOperation { +} + +/** + * A C/C++ `++` or `--` prefix expression. + * + * Note that this doesn't include calls to user-defined operators. + */ +abstract class PrefixCrementOperation extends CrementOperation { +} + +/** + * A C/C++ `++` or `--` postfix expression. + * + * Note that this doesn't include calls to user-defined operators. + */ +abstract class PostfixCrementOperation extends CrementOperation { +} + +/** + * A C/C++ prefix increment expression, as in `++x`. + * + * Note that this doesn't include calls to user-defined `operator++`. + */ +class PrefixIncrExpr extends IncrementOperation, PrefixCrementOperation, @preincrexpr { + override string getOperator() { result = "++" } + + override int getPrecedence() { result = 15 } +} + +/** + * A C/C++ prefix decrement expression, as in `--x`. + * + * Note that this doesn't include calls to user-defined `operator--`. + */ +class PrefixDecrExpr extends DecrementOperation, PrefixCrementOperation, @predecrexpr { + override string getOperator() { result = "--" } + + override int getPrecedence() { result = 15 } +} + +/** + * A C/C++ postfix increment expression, as in `x++`. + * + * Note that this doesn't include calls to user-defined `operator++`. + */ +class PostfixIncrExpr extends IncrementOperation, PostfixCrementOperation, @postincrexpr { + override string getOperator() { result = "++" } + + override int getPrecedence() { result = 16 } + + override string toString() { result = "... " + getOperator() } +} + +/** + * A C/C++ postfix decrement expression, as in `x--`. + * + * Note that this doesn't include calls to user-defined `operator--`. + */ +class PostfixDecrExpr extends DecrementOperation, PostfixCrementOperation, @postdecrexpr { + override string getOperator() { result = "--" } + + override int getPrecedence() { result = 16 } + + override string toString() { result = "... " + getOperator() } +} + +/** + * A C/C++ GNU real part expression. + */ +class RealPartExpr extends UnaryArithmeticOperation, @realpartexpr { + override string getOperator() { result = "__real" } +} + +/** + * A C/C++ GNU imaginary part expression. + */ +class ImaginaryPartExpr extends UnaryArithmeticOperation, @imagpartexpr { + override string getOperator() { result = "__imag" } +} + +/** + * A C/C++ binary arithmetic operation. + */ +abstract class BinaryArithmeticOperation extends BinaryOperation { +} + +/** + * A C/C++ add expression. + */ +class AddExpr extends BinaryArithmeticOperation, @addexpr { + override string getOperator() { result = "+" } + + override int getPrecedence() { result = 12 } +} + +/** + * A C/C++ subtract expression. + */ +class SubExpr extends BinaryArithmeticOperation, @subexpr { + override string getOperator() { result = "-" } + + override int getPrecedence() { result = 12 } +} + +/** + * A C/C++ multiply expression. + */ +class MulExpr extends BinaryArithmeticOperation, @mulexpr { + override string getOperator() { result = "*" } + + override int getPrecedence() { result = 13 } +} + +/** + * A C/C++ divide expression. + */ +class DivExpr extends BinaryArithmeticOperation, @divexpr { + override string getOperator() { result = "/" } + + override int getPrecedence() { result = 13 } +} + +/** + * A C/C++ remainder expression. + */ +class RemExpr extends BinaryArithmeticOperation, @remexpr { + override string getOperator() { result = "%" } + + override int getPrecedence() { result = 13 } +} + +/** + * A C/C++ multiply expression with an imaginary number. + */ +class ImaginaryMulExpr extends BinaryArithmeticOperation, @jmulexpr { + override string getOperator() { result = "*" } + + override int getPrecedence() { result = 13 } +} + +/** + * A C/C++ divide expression with an imaginary number. + */ +class ImaginaryDivExpr extends BinaryArithmeticOperation, @jdivexpr { + override string getOperator() { result = "/" } + + override int getPrecedence() { result = 13 } +} + +/** + * A C/C++ add expression with a real term and an imaginary term. + */ +class RealImaginaryAddExpr extends BinaryArithmeticOperation, @fjaddexpr { + override string getOperator() { result = "+" } + + override int getPrecedence() { result = 12 } +} + +/** + * A C/C++ add expression with an imaginary term and a real term. + */ +class ImaginaryRealAddExpr extends BinaryArithmeticOperation, @jfaddexpr { + override string getOperator() { result = "+" } + + override int getPrecedence() { result = 12 } +} + +/** + * A C/C++ subtract expression with a real term and an imaginary term. + */ +class RealImaginarySubExpr extends BinaryArithmeticOperation, @fjsubexpr { + override string getOperator() { result = "-" } + + override int getPrecedence() { result = 12 } +} + +/** + * A C/C++ subtract expression with an imaginary term and a real term. + */ +class ImaginaryRealSubExpr extends BinaryArithmeticOperation, @jfsubexpr { + override string getOperator() { result = "-" } + + override int getPrecedence() { result = 12 } +} + +/** + * A C/C++ GNU min expression. + */ +class MinExpr extends BinaryArithmeticOperation, @minexpr { + override string getOperator() { result = "?" } +} + +/** + * A C/C++ pointer arithmetic operation. + */ +abstract class PointerArithmeticOperation extends BinaryArithmeticOperation { +} + +/** + * A C/C++ pointer add expression. + */ +class PointerAddExpr extends PointerArithmeticOperation, @paddexpr { + override string getOperator() { result = "+" } + + override int getPrecedence() { result = 12 } +} + +/** + * A C/C++ pointer subtract expression. + */ +class PointerSubExpr extends PointerArithmeticOperation, @psubexpr { + override string getOperator() { result = "-" } + + override int getPrecedence() { result = 12 } +} + +/** + * A C/C++ pointer difference expression. + */ +class PointerDiffExpr extends PointerArithmeticOperation, @pdiffexpr { + override string getOperator() { result = "-" } + + override int getPrecedence() { result = 12 } +} diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Assignment.qll b/cpp/ql/src/semmle/code/cpp/exprs/Assignment.qll new file mode 100644 index 000000000000..7f509be1664d --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/exprs/Assignment.qll @@ -0,0 +1,158 @@ +import semmle.code.cpp.exprs.Expr +import semmle.code.cpp.exprs.ArithmeticOperation +import semmle.code.cpp.exprs.BitwiseOperation + +/** + * A non-overloaded binary assignment operation, including `=`, `+=`, `&=`, + * etc. A C++ overloaded operation looks syntactically identical but is instead + * a `FunctionCall`. + */ +abstract class Assignment extends Operation { + /** Gets the lvalue of this assignment. */ + Expr getLValue() { this.hasChild(result,0) } + + /** Gets the rvalue of this assignment. */ + Expr getRValue() { this.hasChild(result,1) } + + override int getPrecedence() { result = 2 } + + override predicate mayBeGloballyImpure() { + this.getRValue().mayBeGloballyImpure() or + not exists(VariableAccess va, LocalScopeVariable v | + va = this.getLValue() + and v = va.getTarget() + and not va.getConversion+() instanceof ReferenceDereferenceExpr + and not v.isStatic()) + } +} + +/** + * A non-overloaded assignment operation with the operator `=`. + */ +class AssignExpr extends Assignment, @assignexpr { + override string getOperator() { result = "=" } + + /** Gets a textual representation of this assignment. */ + override string toString() { result = "... = ..." } +} + +/** + * A non-overloaded binary assignment operation other than `=`. + */ +abstract class AssignOperation extends Assignment { + override string toString() { result = "... " + this.getOperator() + " ..." } +} + +/** + * A non-overloaded arithmetic assignment operation on a non-pointer lvalue: + * `+=`, `-=`, `*=`, `/=` and `%=`. + */ +abstract class AssignArithmeticOperation extends AssignOperation { +} + +/** + * A non-overloaded `+=` assignment expression on a non-pointer lvalue. + */ +class AssignAddExpr extends AssignArithmeticOperation, @assignaddexpr { + override string getOperator() { result = "+=" } +} + +/** + * A non-overloaded `-=` assignment expression on a non-pointer lvalue. + */ +class AssignSubExpr extends AssignArithmeticOperation, @assignsubexpr { + override string getOperator() { result = "-=" } +} + +/** + * A non-overloaded `*=` assignment expression. + */ +class AssignMulExpr extends AssignArithmeticOperation, @assignmulexpr { + override string getOperator() { result = "*=" } +} + +/** + * A non-overloaded `/=` assignment expression. + */ +class AssignDivExpr extends AssignArithmeticOperation, @assigndivexpr { + override string getOperator() { result = "/=" } +} + +/** + * A non-overloaded `%=` assignment expression. + */ +class AssignRemExpr extends AssignArithmeticOperation, @assignremexpr { + override string getOperator() { result = "%=" } +} + +/** + * A non-overloaded bitwise assignment operation. + `&=`, `|=`, `^=`, `<<=` and `>>=` + */ +abstract class AssignBitwiseOperation extends AssignOperation { +} + +/** + * A non-overloaded `&=` assignment expression. + */ +class AssignAndExpr extends AssignBitwiseOperation, @assignandexpr { + override string getOperator() { result = "&=" } +} + +/** + * A non-overloaded `|=` assignment expression. + */ +class AssignOrExpr extends AssignBitwiseOperation, @assignorexpr { + override string getOperator() { result = "|=" } +} + +/** + * A non-overloaded `^=` assignment expression. + */ +class AssignXorExpr extends AssignBitwiseOperation, @assignxorexpr { + override string getOperator() { result = "^=" } +} + +/** + * A non-overloaded `<<=` assignment expression. + */ +class AssignLShiftExpr extends AssignBitwiseOperation, @assignlshiftexpr { + override string getOperator() { result = "<<=" } +} + +/** + * A non-overloaded `>>=` assignment expression. + */ +class AssignRShiftExpr extends AssignBitwiseOperation, @assignrshiftexpr { + override string getOperator() { result = ">>=" } +} + +/** + * A non-overloaded `+=` pointer assignment expression. + */ +class AssignPointerAddExpr extends AssignOperation, @assignpaddexpr { + override string getOperator() { result = "+=" } +} + +/** + * A non-overloaded `-=` pointer assignment expression. + */ +class AssignPointerSubExpr extends AssignOperation, @assignpsubexpr { + override string getOperator() { result = "-=" } +} + +/** + * A C++ variable declaration in an expression where a condition is expected. + * For example, on the `ConditionDeclExpr` in `if (bool c = x < y)`, + * `getExpr()` is an access to `c` (with possible casts), and `getVariable` is + * the variable `c`, which has an initializer `x < y`. + */ +class ConditionDeclExpr extends Expr, @condition_decl { + /** Gets the access using the condition for this declaration. */ + Expr getExpr() { result = this.getChild(0) } + + /** Gets the variable that is declared. */ + Variable getVariable() { condition_decl_bind(this,result) } + + override string toString() { result = "(condition decl)" } +} diff --git a/cpp/ql/src/semmle/code/cpp/exprs/BitwiseOperation.qll b/cpp/ql/src/semmle/code/cpp/exprs/BitwiseOperation.qll new file mode 100644 index 000000000000..774e8099be1c --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/exprs/BitwiseOperation.qll @@ -0,0 +1,68 @@ +import semmle.code.cpp.exprs.Expr + +/** + * A C/C++ unary bitwise operation. + */ +abstract class UnaryBitwiseOperation extends UnaryOperation { +} + +/** + * A C/C++ complement expression. + */ +class ComplementExpr extends UnaryBitwiseOperation, @complementexpr { + override string getOperator() { result = "~" } + + override int getPrecedence() { result = 15 } +} + +/** + * A C/C++ binary bitwise operation. + */ +abstract class BinaryBitwiseOperation extends BinaryOperation { +} + + +/** + * A C/C++ left shift expression. + */ +class LShiftExpr extends BinaryBitwiseOperation, @lshiftexpr { + override string getOperator() { result = "<<" } + + override int getPrecedence() { result = 11 } +} + +/** + * A C/C++ right shift expression. + */ +class RShiftExpr extends BinaryBitwiseOperation, @rshiftexpr { + override string getOperator() { result = ">>" } + + override int getPrecedence() { result = 11 } +} + +/** + * A C/C++ bitwise and expression. + */ +class BitwiseAndExpr extends BinaryBitwiseOperation, @andexpr { + override string getOperator() { result = "&" } + + override int getPrecedence() { result = 8 } +} + +/** + * A C/C++ bitwise or expression. + */ +class BitwiseOrExpr extends BinaryBitwiseOperation, @orexpr { + override string getOperator() { result = "|" } + + override int getPrecedence() { result = 6 } +} + +/** + * A C/C++ bitwise or expression. + */ +class BitwiseXorExpr extends BinaryBitwiseOperation, @xorexpr { + override string getOperator() { result = "^" } + + override int getPrecedence() { result = 7 } +} diff --git a/cpp/ql/src/semmle/code/cpp/exprs/BuiltInOperations.qll b/cpp/ql/src/semmle/code/cpp/exprs/BuiltInOperations.qll new file mode 100644 index 000000000000..26c01adea8c6 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/exprs/BuiltInOperations.qll @@ -0,0 +1,371 @@ +import semmle.code.cpp.exprs.Expr + +/** + * A C/C++ builtin operation. + */ +abstract class BuiltInOperation extends Expr { +} + +/** + * A C/C++ `__builtin_va_start` expression (used by some implementations of `va_start`). + */ +class BuiltInVarArgsStart extends BuiltInOperation, @vastartexpr { + override string toString() { result = "__builtin_va_start" } +} + +/** + * A C/C++ `__builtin_va_end` expression (used by some implementations of `va_end`). + */ +class BuiltInVarArgsEnd extends BuiltInOperation, @vaendexpr { + override string toString() { result = "__builtin_va_end" } +} + +/** + * A C/C++ `__builtin_va_arg` expression (used by some implementations of `va_arg`). + */ +class BuiltInVarArg extends BuiltInOperation, @vaargexpr { + override string toString() { result = "__builtin_va_arg" } +} + +/** + * A C/C++ `__builtin_va_copy` expression (used by some implementations of `va_copy`). + */ +class BuiltInVarArgCopy extends BuiltInOperation, @vacopyexpr { + override string toString() { result = "__builtin_va_copy" } +} + +/** + * A Microsoft C/C++ `__noop` expression, which does nothing. + */ +class BuiltInNoOp extends BuiltInOperation, @noopexpr { + override string toString() { result = "__noop" } +} + +/** + * A C++ `__offsetof` expression (used by some implementations of offsetof in the presence of user-defined `operator&`). + */ +class BuiltInOperationOffsetOf extends BuiltInOperation, @offsetofexpr { + override string toString() { result = "__offsetof" } +} + +/** + * A C++ `__has_assign` expression (used by some implementations of the type_traits header). + */ +class BuiltInOperationHasAssign extends BuiltInOperation, @hasassignexpr { + override string toString() { result = "__has_assign" } +} + +/** + * A C++ `__has_copy` expression (used by some implementations of the type_traits header). + */ +class BuiltInOperationHasCopy extends BuiltInOperation, @hascopyexpr { + override string toString() { result = "__has_copy" } +} + +/** + * A C++ `__has_nothrow_assign` expression (used by some implementations of the type_traits header). + */ +class BuiltInOperationHasNoThrowAssign extends BuiltInOperation, @hasnothrowassign { + override string toString() { result = "__has_nothrow_assign" } +} + +/** + * A C++ `__has_nothrow_constructor` expression (used by some implementations of the type_traits header). + */ +class BuiltInOperationHasNoThrowConstructor extends BuiltInOperation, @hasnothrowconstr { + override string toString() { result = "__has_nothrow_constructor" } +} + +/** + * A C++ `__has_nothrow_copy` expression (used by some implementations of the type_traits header). + */ +class BuiltInOperationHasNoThrowCopy extends BuiltInOperation, @hasnothrowcopy { + override string toString() { result = "__has_nothrow_copy" } +} + +/** + * A C++ `__has_trivial_assign` expression (used by some implementations of the type_traits header). + */ +class BuiltInOperationHasTrivialAssign extends BuiltInOperation, @hastrivialassign { + override string toString() { result = "__has_trivial_assign" } +} + +/** + * A C++ `__has_trivial_constructor` expression (used by some implementations of the type_traits header). + */ +class BuiltInOperationHasTrivialConstructor extends BuiltInOperation, @hastrivialconstr { + override string toString() { result = "__has_trivial_constructor" } +} + +/** + * A C++ `__has_trivial_copy` expression (used by some implementations of the type_traits header). + */ +class BuiltInOperationHasTrivialCopy extends BuiltInOperation, @hastrivialcopy { + override string toString() { result = "__has_trivial_copy" } +} + +/** + * A C++ `__has_trivial_destructor` expression (used by some implementations of the type_traits header). + */ +class BuiltInOperationHasTrivialDestructor extends BuiltInOperation, @hastrivialdestructor { + override string toString() { result = "__has_trivial_destructor" } +} + +/** + * A C++ `__has_user_destructor` expression (used by some implementations of the type_traits header). + */ +class BuiltInOperationHasUserDestructor extends BuiltInOperation, @hasuserdestr { + override string toString() { result = "__has_user_destructor" } +} + +/** + * A C++ `__has_virtual_destructor` expression (used by some implementations of the type_traits header). + */ +class BuiltInOperationHasVirtualDestructor extends BuiltInOperation, @hasvirtualdestr { + override string toString() { result = "__has_virtual_destructor" } +} + +/** + * A C++ `__is_abstract` expression (used by some implementations of the type_traits header). + */ +class BuiltInOperationIsAbstract extends BuiltInOperation, @isabstractexpr { + override string toString() { result = "__is_abstract" } +} + +/** + * A C++ `__is_base_of` expression (used by some implementations of the type_traits header). + */ +class BuiltInOperationIsBaseOf extends BuiltInOperation, @isbaseofexpr { + override string toString() { result = "__is_base_of" } +} + +/** + * A C++ `__is_class` expression (used by some implementations of the type_traits header). + */ +class BuiltInOperationIsClass extends BuiltInOperation, @isclassexpr { + override string toString() { result = "__is_class" } +} + +/** + * A C++ `__is_convertible_to` expression (used by some implementations of the type_traits header). + */ +class BuiltInOperationIsConvertibleTo extends BuiltInOperation, @isconvtoexpr { + override string toString() { result = "__is_convertible_to" } +} + +/** + * A C++ `__is_empty` expression (used by some implementations of the type_traits header). + */ +class BuiltInOperationIsEmpty extends BuiltInOperation, @isemptyexpr { + override string toString() { result = "__is_empty" } +} + +/** + * A C++ `__is_enum` expression (used by some implementations of the type_traits header). + */ +class BuiltInOperationIsEnum extends BuiltInOperation, @isenumexpr { + override string toString() { result = "__is_enum" } +} + +/** + * A C++ `__is_pod` expression (used by some implementations of the type_traits header). + */ +class BuiltInOperationIsPod extends BuiltInOperation, @ispodexpr { + override string toString() { result = "__is_pod" } +} + +/** + * A C++ `__is_polymorphic` expression (used by some implementations of the type_traits header). + */ +class BuiltInOperationIsPolymorphic extends BuiltInOperation, @ispolyexpr { + override string toString() { result = "__is_polymorphic" } +} + +/** + * A C++ `__is_union` expression (used by some implementations of the type_traits header). + */ +class BuiltInOperationIsUnion extends BuiltInOperation, @isunionexpr { + override string toString() { result = "__is_union" } +} + +/** + * A C++ `__builtin_types` expression (used by some implementations of the type_traits header). + */ +class BuiltInOperationBuiltInTypes extends BuiltInOperation, @typescompexpr { + override string toString() { result = "__builtin_types" } +} + +/** + * A clang `__builtin_shufflevector` expression. + */ +class BuiltInOperationBuiltInShuffleVector extends BuiltInOperation, @builtinshufflevector { + override string toString() { result = "__builtin_shufflevector" } +} + +/** + * The `__is_trivially_constructible` type trait. + */ +class BuiltInOperationIsTriviallyConstructible extends BuiltInOperation, @istriviallyconstructibleexpr { + override string toString() { result = "__is_trivially_constructible" } +} + +/** + * The `__is_destructible` type trait. + */ +class BuiltInOperationIsDestructible extends BuiltInOperation, @isdestructibleexpr { + override string toString() { result = "__is_destructible" } +} + +/** + * The `__is_nothrow_destructible` type trait. + */ +class BuiltInOperationIsNothrowDestructible extends BuiltInOperation, @isnothrowdestructibleexpr { + override string toString() { result = "__is_nothrow_destructible" } +} + +/** + * The `__is_trivially_destructible` type trait. + */ +class BuiltInOperationIsTriviallyDestructible extends BuiltInOperation, @istriviallydestructibleexpr { + override string toString() { result = "__is_trivially_destructible" } +} + +/** + * The `__is_trivially_assignable` type trait. + */ +class BuiltInOperationIsTriviallyAssignable extends BuiltInOperation, @istriviallyassignableexpr { + override string toString() { result = "__is_trivially_assignable" } +} + +/** + * The `__is_nothrow_assignable` type trait. + */ +class BuiltInOperationIsNothrowAssignable extends BuiltInOperation, @isnothrowassignableexpr { + override string toString() { result = "__is_nothrow_assignable" } +} + +/** + * The `__is_standard_layout` type trait. + */ +class BuiltInOperationIsStandardLayout extends BuiltInOperation, @isstandardlayoutexpr { + override string toString() { result = "__is_standard_layout" } +} + +/** + * The `__is_trivially_copyable` type trait. + */ +class BuiltInOperationIsTriviallyCopyable extends BuiltInOperation, @istriviallycopyableexpr { + override string toString() { result = "__is_trivially_copyable" } +} + +/** + * The `__is_literal_type` type trait. + */ +class BuiltInOperationIsLiteralType extends BuiltInOperation, @isliteraltypeexpr { + override string toString() { result = "__is_literal_type" } +} + +/** + * The `__has_trivial_move_constructor` type trait. + */ +class BuiltInOperationHasTrivialMoveConstructor extends BuiltInOperation, @hastrivialmoveconstructorexpr { + override string toString() { result = "__has_trivial_move_constructor" } +} + +/** + * The `__has_trivial_move_assign` type trait. + */ +class BuiltInOperationHasTrivialMoveAssign extends BuiltInOperation, @hastrivialmoveassignexpr { + override string toString() { result = "__has_trivial_move_assign" } +} + +/** + * The `__has_nothrow_move_assign` type trait. + */ +class BuiltInOperationHasNothrowMoveAssign extends BuiltInOperation, @hasnothrowmoveassignexpr { + override string toString() { result = "__has_nothrow_move_assign" } +} + +/** + * The `__is_constructible` type trait. + */ +class BuiltInOperationIsConstructible extends BuiltInOperation, @isconstructibleexpr { + override string toString() { result = "__is_constructible" } +} + +/** + * The `__is_nothrow_constructible` type trait. + */ +class BuiltInOperationIsNothrowConstructible extends BuiltInOperation, @isnothrowconstructibleexpr { + override string toString() { result = "__is_nothrow_constructible" } +} + +/** + * The `__has_finalizer` type trait. + */ +class BuiltInOperationHasFinalizer extends BuiltInOperation, @hasfinalizerexpr { + override string toString() { result = "__has_finalizer" } +} + +/** + * The `__is_delegate` type trait. + */ +class BuiltInOperationIsDelegate extends BuiltInOperation, @isdelegateexpr { + override string toString() { result = "__is_delegate" } +} + +/** + * The `__is_interface_class` type trait. + */ +class BuiltInOperationIsInterfaceClass extends BuiltInOperation, @isinterfaceclassexpr { + override string toString() { result = "__is_interface_class" } +} + +/** + * The `__is_ref_array` type trait. + */ +class BuiltInOperationIsRefArray extends BuiltInOperation, @isrefarrayexpr { + override string toString() { result = "__is_ref_array" } +} + +/** + * The `__is_ref_class` type trait. + */ +class BuiltInOperationIsRefClass extends BuiltInOperation, @isrefclassexpr { + override string toString() { result = "__is_ref_class" } +} + +/** + * The `__is_sealed` type trait. + */ +class BuiltInOperationIsSealed extends BuiltInOperation, @issealedexpr { + override string toString() { result = "__is_sealed" } +} + +/** + * The `__is_simple_value_class` type trait. + */ +class BuiltInOperationIsSimpleValueClass extends BuiltInOperation, @issimplevalueclassexpr { + override string toString() { result = "__is_simple_value_class" } +} + +/** + * The `__is_value_class` type trait. + */ +class BuiltInOperationIsValueClass extends BuiltInOperation, @isvalueclassexpr { + override string toString() { result = "__is_value_class" } +} + +/** + * The `__is_final` type trait. + */ +class BuiltInOperationIsFinal extends BuiltInOperation, @isfinalexpr { + override string toString() { result = "__is_final" } +} + +/** + * The `__builtin_choose_expr` type trait. + */ +class BuiltInChooseExpr extends BuiltInOperation, @builtinchooseexpr { + override string toString() { result = "__builtin_choose_expr" } +} diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Call.qll b/cpp/ql/src/semmle/code/cpp/exprs/Call.qll new file mode 100644 index 000000000000..5af03dab4a2f --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/exprs/Call.qll @@ -0,0 +1,546 @@ +import semmle.code.cpp.exprs.Expr +import semmle.code.cpp.Function +private import semmle.code.cpp.dataflow.EscapesTree + +/** + * A C/C++ call. + */ +abstract class Call extends Expr, NameQualifiableElement { + /** + * Gets the number of actual parameters in this call; use + * `getArgument(i)` with `i` between `0` and `result - 1` to + * retrieve actuals. + */ + int getNumberOfArguments() { result = count(this.getAnArgument()) } + + /** + * Holds if this call has a qualifier. + * + * For example, `ptr->f()` has a qualifier, whereas plain `f()` does not. + */ + predicate hasQualifier() { + exists(Expr e | this.getChild(-1) = e) + } + + /** + * Gets the expression to the left of the function name or function pointer variable name. + * + * As a few examples: + * For the call to `f` in `ptr->f()`, this gives `ptr`. + * For the call to `f` in `(*ptr).f()`, this gives `(*ptr)`. + */ + Expr getQualifier() { + result = this.getChild(-1) + } + + /** + * Gets an argument for this call. + */ + Expr getAnArgument() { + exists(int i | result = this.getChild(i) and i >= 0) + } + + /** + * Gets the nth argument for this call. + * + * The range of `n` is from `0` to `getNumberOfArguments() - 1`. + */ + Expr getArgument(int n) { + result = this.getChild(n) and n >= 0 + } + + /** + * Gets a sub expression of the argument at position `index`. If the + * argument itself contains calls, such calls will be considered + * leafs in the expression tree. + * + * Example: the call `f(2, 3 + 4, g(4 + 5))` has sub expression(s) + * `2` at index 0; `3`, `4`, and `3 + 4` at index 1; and `g(4 + 5)` + * at index 2, respectively. + */ + Expr getAnArgumentSubExpr(int index) { + result = getArgument(index) + or + exists(Expr mid | + mid = getAnArgumentSubExpr(index) + and not mid instanceof Call + and not mid instanceof SizeofOperator + and result = mid.getAChild() + ) + } + + /** + * Gets the target of the call, as best as makes sense for this kind of call. + * The precise meaning depends on the kind of call it is: + * - For a call to a function, it's the function being called. + * - For a C++ method call, it's the statically resolved method. + * - For an Objective C message expression, it's the statically resolved + * method, and it might not exist. + * - For a variable call, it never exists. + */ + abstract Function getTarget(); + + override int getPrecedence() { result = 16 } + + override string toString() { none() } + + /** + * Holds if this call passes the variable accessed by `va` by + * reference as the `i`th argument. + * + * A variable is passed by reference if the `i`th parameter of the function + * receives an address that points within the object denoted by `va`. For a + * variable named `x`, passing by reference includes both explicit pointers + * (`&x`) and implicit conversion to a C++ reference (`x`), but it also + * includes deeper expressions such as `&x[0] + length` or `&*&*&x`. + * + * When `Field`s are involved, an argument `i` may pass more than one + * variable by reference simultaneously. For example, the call `f(&x.m1.m2)` + * counts as passing both `x`, `m1` and `m2` to argument 0 of `f`. + * + * This predicate holds for variables passed by reference even if they are + * passed as references to `const` and thus cannot be changed through that + * reference. See `passesByNonConstReference` for a predicate that only holds + * for variables passed by reference to non-const. + */ + predicate passesByReference(int i, VariableAccess va) { + variableAddressEscapesTree(va, this.getArgument(i).getFullyConverted()) + } + + /** + * Holds if this call passes the variable accessed by `va` by + * reference to non-const data as the `i`th argument. + * + * A variable is passed by reference if the `i`th parameter of the function + * receives an address that points within the object denoted by `va`. For a + * variable named `x`, passing by reference includes both explicit pointers + * (`&x`) and implicit conversion to a C++ reference (`x`), but it also + * includes deeper expressions such as `&x[0] + length` or `&*&*&x`. + * + * When `Field`s are involved, an argument `i` may pass more than one + * variable by reference simultaneously. For example, the call `f(&x.m1.m2)` + * counts as passing both `x`, `m1` and `m2` to argument 0 of `f`. + * + * This predicate only holds for variables passed by reference to non-const + * data and thus can be changed through that reference. See + * `passesByReference` for a predicate that also holds for variables passed + * by reference to const. + */ + predicate passesByReferenceNonConst(int i, VariableAccess va) { + variableAddressEscapesTreeNonConst( + va, + this.getArgument(i).getFullyConverted() + ) + } +} + +/** + * A C/C++ function call where the name of the target function is known at compile-time. + * + * This includes various kinds of call: + * 1. Calls such as `f(x)` where `f` is the name of a function. + * 2. Calls such as `ptr->f()` where `f` is the name of a (possibly virtual) member function. + * 3. Constructor calls for stack-allocated objects. + * 4. Implicit and explicit calls to user-defined operators. + * 5. Base class initializers in constructors. + */ +class FunctionCall extends Call, @funbindexpr { + FunctionCall() { iscall(this,_) } + + /** Gets an explicit template argument for this call. */ + Type getAnExplicitTemplateArgument() { + result = getExplicitTemplateArgument(_) + } + + /** Gets a template argument for this call. */ + Type getATemplateArgument() { + result = getTarget().getATemplateArgument() + } + + /** Gets the nth explicit template argument for this call. */ + Type getExplicitTemplateArgument(int n) { + n < getNumberOfExplicitTemplateArguments() + and result = getTemplateArgument(n) + } + + /** Gets the number of explicit template arguments for this call. */ + int getNumberOfExplicitTemplateArguments() { + if numtemplatearguments(this,_) then + numtemplatearguments(this,result) + else + result = 0 + } + + /** Gets the number of template arguments for this call. */ + int getNumberOfTemplateArguments() { + result = count(int i | exists(getTemplateArgument(i))) + } + + /** Gets the nth template argument for this call (indexed from 0). */ + Type getTemplateArgument(int n) { + result = getTarget().getTemplateArgument(n) + } + + /** Holds if any template arguments for this call are implicit / deduced. */ + predicate hasImplicitTemplateArguments() { + exists(int i | + exists(getTemplateArgument(i)) + and not exists(getExplicitTemplateArgument(i)) + ) + } + + /** Holds if a template argument list was provided for this call. */ + predicate hasTemplateArgumentList() { + numtemplatearguments(this,_) + } + + /** + * Gets the `RoutineType` of the call target as visible at the call site. For + * constructor calls, this predicate instead gets the `Class` of the constructor + * being called. + */ + private + Type getTargetType() { result = Call.super.getType().stripType() } + + /** + * Gets the expected return type of the function called by this call. + * + * In most cases, the expected return type will be the return type of the function being called. + * It is only different when the function being called is ambiguously declared, at which point + * the expected return type is the return type of the (unambiguous) function declaration that was + * visible at the call site. + */ + Type getExpectedReturnType() { + if getTargetType() instanceof RoutineType then + result = getTargetType().(RoutineType).getReturnType() + else + result = getTarget().getType() + } + + /** + * Gets the expected type of the nth parameter of the function called by this call. + * + * In most cases, the expected parameter types match the parameter types of the function being called. + * They are only different when the function being called is ambiguously declared, at which point + * the expected parameter types are the parameter types of the (unambiguous) function declaration that + * was visible at the call site. + */ + Type getExpectedParameterType(int n) { + if getTargetType() instanceof RoutineType then + result = getTargetType().(RoutineType).getParameterType(n) + else + result = getTarget().getParameter(n).getType() + } + + /** + * Gets the function called by this call. + * + * In the case of virtual function calls, the result is the most-specific function in the override tree (as + * determined by the compiler) such that the target at runtime will be one of result.getAnOverridingFunction*(). + */ + override Function getTarget() { funbind(this,result) } + + /** + * Gets the type of this expression, that is, the return type of the function being called. + */ + override Type getType() { result = getExpectedReturnType() } + + /** + * Holds if this is a call to a virtual function. + * + * Note that this holds even in cases where a sufficiently clever compiler could perform static dispatch. + */ + predicate isVirtual() { + iscall(this,1) + } + + /** + * Holds if the target of this function call was found by argument-dependent lookup and wouldn't have been + * found by any other means. + */ + predicate isOnlyFoundByADL() { + iscall(this,2) + } + + /** Gets a textual representation of this function call. */ + override string toString() { + if exists(getTarget()) then + result = "call to " + this.getTarget().getName() + else + result = "call to unknown function" + } + + override predicate mayBeImpure() { + this.getChild(_).mayBeImpure() or + this.getTarget().mayHaveSideEffects() or + isVirtual() + } + override predicate mayBeGloballyImpure() { + this.getChild(_).mayBeGloballyImpure() or + this.getTarget().mayHaveSideEffects() or + isVirtual() + } +} + +/** + * An instance of unary operator * applied to a user-defined type. + */ +class OverloadedPointerDereferenceExpr extends FunctionCall { + OverloadedPointerDereferenceExpr() { + getTarget().hasName("operator*") + and getTarget().getEffectiveNumberOfParameters() = 1 + } + + /** + * Gets the expression this operator * applies to. + */ + Expr getExpr() { + result = this.getChild(0) or + result = this.getQualifier() + } + + override predicate mayBeImpure() { + FunctionCall.super.mayBeImpure() and + (this.getExpr().mayBeImpure() or + not exists(Class declaring | + this.getTarget().getDeclaringType().isConstructedFrom*(declaring) | + declaring.getNamespace() instanceof StdNamespace)) + } + override predicate mayBeGloballyImpure() { + FunctionCall.super.mayBeGloballyImpure() and + (this.getExpr().mayBeGloballyImpure() or + not exists(Class declaring | + this.getTarget().getDeclaringType().isConstructedFrom*(declaring) | + declaring.getNamespace() instanceof StdNamespace)) + } +} + +/** + * An instance of operator [] applied to a user-defined type. + */ +class OverloadedArrayExpr extends FunctionCall { + OverloadedArrayExpr() { + getTarget().hasName("operator[]") + } + + /** + * Gets the expression being subscripted. + */ + Expr getArrayBase() { if exists(this.getQualifier()) then result = this.getQualifier() else result = this.getChild(0) } + + /** + * Gets the expression giving the index. + */ + Expr getArrayOffset() { if exists(this.getQualifier()) then result = this.getChild(0) else result = this.getChild(1) } +} + +/** + * A C/C++ call which is performed through a function pointer. + */ +class ExprCall extends Call, @callexpr { + /** + * Gets the expression which yields the function pointer to call. + */ + Expr getExpr() { result = this.getChild(0) } + + override Expr getAnArgument() { + exists(int i | result = this.getChild(i) and i >= 1) + } + override Expr getArgument(int index) { + result = this.getChild(index+1) and index in [0..this.getNumChild()-2] + } + + override string toString() { result = "call to expression" } + + override Function getTarget() { + none() + } +} + +/** + * A C/C++ call which is performed through a variable of function pointer type. + */ +class VariableCall extends ExprCall { + VariableCall() { this.getExpr() instanceof VariableAccess } + + /** + * Gets the variable which yields the function pointer to call. + */ + Variable getVariable() { + this.getExpr().(VariableAccess).getTarget() = result + } +} + +/** + * A call to a constructor. + */ +class ConstructorCall extends FunctionCall { + ConstructorCall() { super.getTarget() instanceof Constructor } + + /** Gets the constructor being called. */ + override Constructor getTarget() { result = super.getTarget() } +} + +/** + * A C++ `throw` expression. + */ +class ThrowExpr extends Expr, @throw_expr { + /** + * Gets the expression that will be thrown, if any. There is no result if + * `this` is a `ReThrowExpr`. + */ + Expr getExpr() { result = this.getChild(0) } + + override string toString() { result = "throw ..." } + + override int getPrecedence() { result = 1 } +} + +/** + * A C++ `throw` expression with no argument (which causes the current exception to be re-thrown). + */ +class ReThrowExpr extends ThrowExpr { + ReThrowExpr() { this.getType() instanceof VoidType } + + override string toString() { result = "re-throw exception " } +} + +/** + * A call to a destructor. + */ +class DestructorCall extends FunctionCall { + DestructorCall() { super.getTarget() instanceof Destructor } + + /** Gets the destructor being called. */ + override Destructor getTarget() { result = super.getTarget() } +} + +/** + * An expression that looks like a destructor call, but has no effect. + * + * For example, given a plain old data type `pod_t`, the syntax `ptr->~pod_t()` is + * a vacuous destructor call, as `~pod_t` isn't actually a function. This can also + * occur in instantiated templates, as `ptr->~T()` becomes vacuous when `T` is `int`. + */ +class VacuousDestructorCall extends Expr, @vacuous_destructor_call { + /** + * Gets the expression for the object whose destructor would be called. + */ + Expr getQualifier() { result = this.getChild(0) } + + override string toString() { result = "(vacuous destructor call)" } +} + +/** + * An initialization of a base class or member variable performed as part + * of a constructor's explicit initializer list or implicit actions. + */ +class ConstructorInit extends Expr, @ctorinit { +} + +/** + * A call to a constructor of a base class as part of a constructor's + * initializer list or compiler-generated actions. + */ +class ConstructorBaseInit extends ConstructorInit, ConstructorCall { +} + +/** + * A call to a constructor of a direct non-virtual base class as part of a + * constructor's initializer list or compiler-generated actions. + */ +class ConstructorDirectInit extends ConstructorBaseInit, @ctordirectinit { +} + +/** + * A call to a constructor of a virtual base class as part of a + * constructor's initializer list or compiler-generated actions. + * + * If the virtual base class has already been initialized, then this + * call won't be performed. + */ +class ConstructorVirtualInit extends ConstructorBaseInit, @ctorvirtualinit { +} + +/** + * A call to a constructor of the same class as part of a constructor's + * initializer list, which delegates object construction (C++11 only). + */ +class ConstructorDelegationInit extends ConstructorBaseInit, @ctordelegatinginit { +} + +/** + * An initialization of a member variable performed as part of a + * constructor's explicit initializer list or implicit actions. + */ +class ConstructorFieldInit extends ConstructorInit, @ctorfieldinit { + /** Gets the field being initialized. */ + Field getTarget() { varbind(this,result) } + + /** + * Gets the expression to which the field is initialized. + * + * This is typically either a Literal or a FunctionCall to a + * constructor, but more complex expressions can also occur. + */ + Expr getExpr() { result = this.getChild(0) } + + override string toString() { + result = "constructor init of field " + getTarget().getName() + } + + override predicate mayBeImpure() { + this.getExpr().mayBeImpure() + } + override predicate mayBeGloballyImpure() { + this.getExpr().mayBeGloballyImpure() + } +} + +/** + * A call to a destructor of a base class or field as part of a destructor's + * compiler-generated actions. + */ +class DestructorDestruction extends Expr, @dtordestruct { +} + +/** + * A call to a destructor of a base class as part of a destructor's + * compiler-generated actions. + */ +class DestructorBaseDestruction extends DestructorCall, DestructorDestruction { +} + +/** + * A call to a destructor of a direct non-virtual base class as part of a + * destructor's compiler-generated actions. + */ +class DestructorDirectDestruction extends DestructorBaseDestruction, @dtordirectdestruct { +} + +/** + * A call to a destructor of a direct virtual base class as part of a + * destructor's compiler-generated actions. + * + * If the virtual base class wasn't initialized by the ConstructorVirtualInit + * in the corresponding constructor, then this call won't be performed. + */ +class DestructorVirtualDestruction extends DestructorBaseDestruction, @dtorvirtualdestruct { +} + +/** + * A destruction of a member variable performed as part of a + * destructor's compiler-generated actions. + */ +class DestructorFieldDestruction extends DestructorDestruction, @dtorfielddestruct { + /** Gets the field being destructed. */ + Field getTarget() { varbind(this,result) } + + /** Gets the compiler-generated call to the variable's destructor. */ + DestructorCall getExpr() { result = this.getChild(0) } + + override string toString() { + result = "destructor field destruction of " + + this.getTarget().getName() + } +} diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll b/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll new file mode 100644 index 000000000000..985cca778142 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll @@ -0,0 +1,672 @@ +import semmle.code.cpp.exprs.Expr +private import semmle.code.cpp.internal.Type + +/** + * A C/C++ cast expression or similar unary expression that doesn't affect the logical value of its operand. + * + * Instances of this class are not present in the main AST which is navigated by parent/child links. Instead, + * instances of this class are attached to nodes in the main AST via special conversion links. + */ +abstract class Conversion extends Expr { + /** Gets the expression being converted. */ + Expr getExpr() { result.getConversion() = this } + + /** Holds if this conversion is an implicit conversion. */ + predicate isImplicit() { this.isCompilerGenerated() } + + override predicate mayBeImpure() { + this.getExpr().mayBeImpure() + } + override predicate mayBeGloballyImpure() { + this.getExpr().mayBeGloballyImpure() + } +} + +/** + * A C/C++ cast expression. + * + * To get the type which the expression is being cast to, use getType(). + * + * There are two groups of subtypes of `Cast`. The first group differentiates + * between the different cast syntax forms, e.g. `CStyleCast`, `StaticCast`, + * etc. The second group differentiates between the semantic operation being + * performed by the cast, e.g. `IntegralConversion`, `PointerBaseClassConversion`, + * etc. + * The two groups are largely orthogonal to one another. For example, a + * cast that is syntactically as `CStyleCast` may also be an `IntegralConversion`, + * a `PointerBaseClassConversion`, or some other semantic conversion. Similarly, + * a `PointerDerivedClassConversion` may also be a `CStyleCast` or a `StaticCast`. + */ +abstract class Cast extends Conversion, @cast { + /** + * Gets a string describing the semantic conversion operation being performed by + * this cast. + */ + string getSemanticConversionString() { + result = "unknown conversion" + } +} + +/** + * INTERNAL: Do not use. + * Query predicates used to check invariants that should hold for all `Cast` + * nodes. To run all sanity queries for the ASTs, including the ones below, + * run "semmle/code/cpp/ASTSanity.ql". + */ +module CastSanity { + query predicate multipleSemanticConversionStrings(Cast cast, Type fromType, string kind) { + // Every cast should have exactly one semantic conversion kind + count(cast.getSemanticConversionString()) > 1 and + kind = cast.getSemanticConversionString() and + fromType = cast.getExpr().getType().getUnspecifiedType() + } + + query predicate missingSemanticConversionString(Cast cast, Type fromType) { + // Every cast should have exactly one semantic conversion kind + not exists(cast.getSemanticConversionString()) and + fromType = cast.getExpr().getType().getUnspecifiedType() + } + + query predicate unknownSemanticConversionString(Cast cast, Type fromType) { + // Every cast should have a known semantic conversion kind + cast.getSemanticConversionString() = "unknown conversion" and + fromType = cast.getExpr().getType().getUnspecifiedType() + } +} + +/** + * A cast expression in C, or a C-style cast expression in C++. + */ +class CStyleCast extends Cast, @c_style_cast { + override string toString() { result = "(" + this.getType().getName() + ")..." } + + override int getPrecedence() { result = 15 } +} + +/** + * A C++ `static_cast` expression. + */ +class StaticCast extends Cast, @static_cast { + override string toString() { result = "static_cast<" + this.getType().getName() + ">..." } + + override int getPrecedence() { result = 16 } +} + +/** + * A C++ `const_cast` expression. + */ +class ConstCast extends Cast, @const_cast { + override string toString() { result = "const_cast<" + this.getType().getName() + ">..." } + + override int getPrecedence() { result = 16 } +} + +/** + * A C++ `reinterpret_cast` expression. + */ +class ReinterpretCast extends Cast, @reinterpret_cast { + override string toString() { result = "reinterpret_cast<" + this.getType().getName() + ">..." } + + override int getPrecedence() { result = 16 } +} + +private predicate isArithmeticOrEnum(Type type) { + type instanceof ArithmeticType or + type instanceof Enum +} + +private predicate isIntegralOrEnum(Type type) { + type instanceof IntegralType or + type instanceof Enum +} + +private predicate isPointerOrNullPointer(Type type) { + type instanceof PointerType or + type instanceof FunctionPointerType or + type instanceof NullPointerType +} + +private predicate isPointerToMemberOrNullPointer(Type type) { + type instanceof PointerToMemberType or + type instanceof NullPointerType +} + +/** + * A conversion from one arithmetic or enum type to another. + */ +class ArithmeticConversion extends Cast { + ArithmeticConversion() { + conversionkinds(this, 0) and + isArithmeticOrEnum(getType().getUnspecifiedType()) and + isArithmeticOrEnum(getExpr().getType().getUnspecifiedType()) + } + + override string getSemanticConversionString() { + result = "arithmetic conversion" + } +} + +/** + * A conversion from one integral or enum type to another. + */ +class IntegralConversion extends ArithmeticConversion { + IntegralConversion() { + isIntegralOrEnum(getType().getUnspecifiedType()) and + isIntegralOrEnum(getExpr().getType().getUnspecifiedType()) + } + + override string getSemanticConversionString() { + result = "integral conversion" + } +} + +/** + * A conversion from one floating point type to another. + */ +class FloatingPointConversion extends ArithmeticConversion { + FloatingPointConversion() { + getType().getUnspecifiedType() instanceof FloatingPointType and + getExpr().getType().getUnspecifiedType() instanceof FloatingPointType + } + + override string getSemanticConversionString() { + result = "floating point conversion" + } +} + +/** + * A conversion from a floating point type to an integral or enum type. + */ +class FloatingPointToIntegralConversion extends ArithmeticConversion { + FloatingPointToIntegralConversion() { + isIntegralOrEnum(getType().getUnspecifiedType()) and + getExpr().getType().getUnspecifiedType() instanceof FloatingPointType + } + + override string getSemanticConversionString() { + result = "floating point to integral conversion" + } +} + +/** + * A conversion from an integral or enum type to a floating point type. + */ +class IntegralToFloatingPointConversion extends ArithmeticConversion { + IntegralToFloatingPointConversion() { + getType().getUnspecifiedType() instanceof FloatingPointType and + isIntegralOrEnum(getExpr().getType().getUnspecifiedType()) + } + + override string getSemanticConversionString() { + result = "integral to floating point conversion" + } +} + +/** + * A conversion from one pointer type to another. The conversion does + * not modify the value of the pointer. For pointer conversions involving + * casts between base and derived classes, see `BaseClassConversion` and + * `DerivedClassConversion`. + */ +class PointerConversion extends Cast { + PointerConversion() { + conversionkinds(this, 0) and + isPointerOrNullPointer(getType().getUnspecifiedType()) and + isPointerOrNullPointer(getExpr().getType().getUnspecifiedType()) + } + + override string getSemanticConversionString() { + result = "pointer conversion" + } +} + +/** + * A conversion from one pointer-to-member type to another. The conversion + * does not modify the value of the pointer-to-member. For pointer-to-member + * conversions involving casts between base and derived classes, see + * `PointerToMemberBaseClassConversion` and `PointerToMemberDerivedClassConversion`. + */ +class PointerToMemberConversion extends Cast { + PointerToMemberConversion() { + conversionkinds(this, 0) and + exists(Type fromType, Type toType | + fromType = getExpr().getType().getUnspecifiedType() and + toType = getType().getUnspecifiedType() and + isPointerToMemberOrNullPointer(fromType) and + isPointerToMemberOrNullPointer(toType) and + // A conversion from nullptr to nullptr is a `PointerConversion`, not a + // `PointerToMemberConversion`. + not ( + fromType instanceof NullPointerType and + toType instanceof NullPointerType + ) + ) + } + + override string getSemanticConversionString() { + result = "pointer-to-member conversion" + } +} + +/** + * A conversion from a pointer type to an integral or enum type. + */ +class PointerToIntegralConversion extends Cast { + PointerToIntegralConversion() { + conversionkinds(this, 0) and + isIntegralOrEnum(getType().getUnspecifiedType()) and + isPointerOrNullPointer(getExpr().getType().getUnspecifiedType()) + } + + override string getSemanticConversionString() { + result = "pointer to integral conversion" + } +} + +/** + * A conversion from an integral or enum type to a pointer type. + */ +class IntegralToPointerConversion extends Cast { + IntegralToPointerConversion() { + conversionkinds(this, 0) and + isPointerOrNullPointer(getType().getUnspecifiedType()) and + isIntegralOrEnum(getExpr().getType().getUnspecifiedType()) + } + + override string getSemanticConversionString() { + result = "integral to pointer conversion" + } +} + +/** + * A conversion to `bool`. Returns `false` if the source value is zero, + * false, or nullptr. Returns `true` otherwise. + */ +class BoolConversion extends Cast { + BoolConversion() { + conversionkinds(this, 1) + } + + override string getSemanticConversionString() { + result = "conversion to bool" + } +} + +/** + * A conversion between two pointers or glvalues related by inheritance. The + * base class will always be either a direct base class of the derived class, + * or a virtual base class of the derived class. A conversion to an indirect + * non-virtual base class will be represented as a sequence of conversions to + * direct base classes. + */ +class InheritanceConversion extends Cast { + InheritanceConversion() { + conversionkinds(this, 2) or conversionkinds(this, 3) + } + + /** + * 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 ClassDerivation getDerivation() { + result.getBaseClass() = getBaseClass() and + result.getDerivedClass() = getDerivedClass() + } + + /** + * 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. + */ + Class getBaseClass() { + none() // Overridden by subclasses + } + + /** + * Gets the derived class of the conversion. + */ + Class getDerivedClass() { + none() // Overridden by subclasses + } +} + +/** + * Given the source operand or result of an `InheritanceConversion`, returns the + * class being converted from or to. If the type of the expression is a pointer, + * this returns the pointed-to class. Otherwise, the type of the expression must + * be a class, in which case the result is that class. + */ +private Class getConversionClass(Expr expr) { + exists(Type operandType | + operandType = expr.getType().getUnspecifiedType() and + ( + result = operandType or + result = operandType.(PointerType).getBaseType() + ) + ) +} + +/** + * A conversion from a pointer or glvalue of a derived class to a pointer or + * glvalue of a direct or virtual base class. + */ +class BaseClassConversion extends InheritanceConversion { + BaseClassConversion() { + conversionkinds(this, 2) + } + + override string getSemanticConversionString() { + result = "base class conversion" + } + + override Class getBaseClass() { + result = getConversionClass(this) + } + + override Class getDerivedClass() { + result = getConversionClass(getExpr()) + } + + /** + * Holds if this conversion is to a virtual base class. + */ + predicate isVirtual() { + getDerivation().isVirtual() or not exists(getDerivation()) + } +} + +/** + * A conversion from a pointer or glvalue to a base class to a pointer or glvalue + * to a direct derived class. + */ +class DerivedClassConversion extends InheritanceConversion { + DerivedClassConversion() { + conversionkinds(this, 3) + } + + override string getSemanticConversionString() { + result = "derived class conversion" + } + + override Class getBaseClass() { + result = getConversionClass(getExpr()) + } + + override Class getDerivedClass() { + result = getConversionClass(this) + } +} + +/** + * A conversion from a pointer-to-member of a derived class to a pointer-to-member + * of an immediate base class. + */ +class PointerToMemberBaseClassConversion extends Cast { + PointerToMemberBaseClassConversion() { + conversionkinds(this, 4) + } + + override string getSemanticConversionString() { + result = "pointer-to-member base class conversion" + } +} + +/** + * A conversion from a pointer-to-member of a base class to a pointer-to-member + * of an immediate derived class. + */ +class PointerToMemberDerivedClassConversion extends Cast { + PointerToMemberDerivedClassConversion() { + conversionkinds(this, 5) + } + + override string getSemanticConversionString() { + result = "pointer-to-member derived class conversion" + } +} + +/** + * A conversion of a glvalue from one type to another. The conversion does not + * modify the address of the glvalue. For glvalue conversions involving base and + * derived classes, see `BaseClassConversion` and `DerivedClassConversion`. + */ +class GlvalueConversion extends Cast { + GlvalueConversion() { + conversionkinds(this, 6) + } + + override string getSemanticConversionString() { + result = "glvalue conversion" + } +} + +/** + * The adjustment of the type of a class prvalue. Most commonly seen in code + * similar to: + * + * class String { ... }; + * String func(); + * void caller() { + * const String& r = func(); + * } + * + * In the above example, the result of the call to `func` is a prvalue of type + * `String`, which will be adjusted to type `const String` before being bound + * to the reference. + */ +class PrvalueAdjustmentConversion extends Cast { + PrvalueAdjustmentConversion() { + conversionkinds(this, 7) + } + + override string getSemanticConversionString() { + result = "prvalue adjustment conversion" + } +} + +/** + * A C++ `dynamic_cast` expression. + */ +class DynamicCast extends Cast, @dynamic_cast { + override string toString() { result = "dynamic_cast<" + this.getType().getName() + ">..." } + + override int getPrecedence() { result = 16 } + + override string getSemanticConversionString() { + result = "dynamic_cast" + } +} + +/** + * A Microsoft C/C++ `__uuidof` expression that returns the UUID of a type, as + * specified by the `__declspec(uuid)` attribute. + */ +class UuidofOperator extends Expr, @uuidof { + override string toString() { result = "__uuidof(" + getTypeOperand().getName() + ")" } + + override int getPrecedence() { result = 15 } + + /** Gets the contained type. */ + Type getTypeOperand() { + uuidof_bind(this, result) + } +} + +/** + * A C++ `typeid` expression which provides runtime type information + * about an expression or type. + */ +class TypeidOperator extends Expr, @type_id { + /** + * Gets the type that is returned by this typeid expression. + * + * For example in the following code the `typeid` returns the + * type `MyClass *`. + * + * ``` + * MyClass *ptr; + * + * printf("the type of ptr is: %s\n", typeid(ptr).name); + * ``` + */ + Type getResultType() { typeid_bind(this,unresolve(result)) } + + /** + * DEPRECATED: Use `getResultType()` instead. + * + * Gets the type that is returned by this typeid expression. + */ + deprecated Type getSpecifiedType() { result = this.getResultType() } + + /** + * Gets the contained expression, if any (if this typeid contains + * a type rather than an expression, there is no result). + */ + Expr getExpr() { result = this.getChild(0) } + + override string toString() { result = "typeid ..." } + + override int getPrecedence() { result = 16 } + + override predicate mayBeImpure() { + this.getExpr().mayBeImpure() + } + override predicate mayBeGloballyImpure() { + this.getExpr().mayBeGloballyImpure() + } +} + +/** + * A C++11 `sizeof...` expression which determines the size of a template parameter pack. + * + * This expression only appears in templates themselves - in any actual + * instantiations, "sizeof...(x)" will be replaced by its integer value. + */ +class SizeofPackOperator extends Expr, @sizeof_pack { + override string toString() { result = "sizeof...(...)" } + + override predicate mayBeImpure() { + none() + } + override predicate mayBeGloballyImpure() { + none() + } +} + +/** + * A C/C++ sizeof expression. + */ +abstract class SizeofOperator extends Expr, @runtime_sizeof { + override int getPrecedence() { result = 15 } +} + +/** + * A C/C++ sizeof expression whose operand is an expression. + */ +class SizeofExprOperator extends SizeofOperator { + SizeofExprOperator() { exists(Expr e | this.getChild(0) = e) } + + /** Gets the contained expression. */ + Expr getExprOperand() { result = this.getChild(0) } + + /** + * DEPRECATED: Use `getExprOperand()` instead + * + * Gets the contained expression. + * */ + deprecated Expr getExpr() { result = this.getExprOperand() } + + override string toString() { result = "sizeof()" } + + override predicate mayBeImpure() { + this.getExprOperand().mayBeImpure() + } + override predicate mayBeGloballyImpure() { + this.getExprOperand().mayBeGloballyImpure() + } +} + +/** + * A C/C++ sizeof expression whose operand is a type name. + */ +class SizeofTypeOperator extends SizeofOperator { + SizeofTypeOperator() { sizeof_bind(this,_) } + + /** Gets the contained type. */ + Type getTypeOperand() { sizeof_bind(this,unresolve(result)) } + + /** + * DEPRECATED: Use `getTypeOperand()` instead + * + * Gets the contained type. + */ + deprecated Type getSpecifiedType() { result = this.getTypeOperand() } + + override string toString() { result = "sizeof(" + this.getTypeOperand().getName() + ")" } + + override predicate mayBeImpure() { + none() + } + override predicate mayBeGloballyImpure() { + none() + } +} + +/** + * A C++11 `alignof` expression. + */ +abstract class AlignofOperator extends Expr, @runtime_alignof { + override int getPrecedence() { result = 15 } +} + +/** + * A C++11 `alignof` expression whose operand is an expression. + */ +class AlignofExprOperator extends AlignofOperator { + AlignofExprOperator() { exists(Expr e | this.getChild(0) = e) } + + /** + * Gets the contained expression. + */ + Expr getExprOperand() { result = this.getChild(0) } + + /** + * DEPRECATED: Use `getExprOperand()` instead. + */ + deprecated Expr getExpr() { result = this.getExprOperand() } + + override string toString() { result = "alignof()" } +} + +/** + * A C++11 `alignof` expression whose operand is a type name. + */ +class AlignofTypeOperator extends AlignofOperator { + AlignofTypeOperator() { sizeof_bind(this,_) } + + /** Gets the contained type. */ + Type getTypeOperand() { sizeof_bind(this,unresolve(result)) } + + /** + * DEPRECATED: Use `getTypeOperand()` instead. + */ + deprecated Type getSpecifiedType() { result = this.getTypeOperand() } + + override string toString() { result = "alignof(" + this.getTypeOperand().getName() + ")" } +} + +/** + * A C/C++ array to pointer conversion. + */ +class ArrayToPointerConversion extends Conversion, @array_to_pointer { + /** Gets a textual representation of this conversion. */ + override string toString() { result = "array to pointer conversion" } + + override predicate mayBeImpure() { + none() + } + override predicate mayBeGloballyImpure() { + none() + } +} diff --git a/cpp/ql/src/semmle/code/cpp/exprs/ComparisonOperation.qll b/cpp/ql/src/semmle/code/cpp/exprs/ComparisonOperation.qll new file mode 100644 index 000000000000..54fa38ae21b9 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/exprs/ComparisonOperation.qll @@ -0,0 +1,107 @@ +import semmle.code.cpp.exprs.Expr + +/** + * A C/C++ comparison operation, that is, either an equality operation or a relational operation. + */ +abstract class ComparisonOperation extends BinaryOperation { +} + +/** + * A C/C++ equality operation, that is, either "==" or "!=". + */ +abstract class EqualityOperation extends ComparisonOperation { + override int getPrecedence() { result = 9 } +} + +/** + * A C/C++ equal expression. + */ +class EQExpr extends EqualityOperation, @eqexpr { + override string getOperator() { result = "==" } +} + +/** + * A C/C++ not equal expression. + */ +class NEExpr extends EqualityOperation, @neexpr { + override string getOperator() { result = "!=" } +} + +/** + * A C/C++ relational operation, that is, one of `<=`, `<`, `>`, or `>=`. + */ +abstract class RelationalOperation extends ComparisonOperation { + override int getPrecedence() { result = 10 } + + /** + * DEPRECATED: Use `getGreaterOperand()` instead. + */ + deprecated + Expr getLarge() { + result = getGreaterOperand() + } + + /** + * DEPRECATED: Use `getLesserOperand()` instead. + */ + deprecated + Expr getSmall() { + result = getLesserOperand() + } + + /** + * 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`. + */ + abstract Expr getGreaterOperand(); + + /** + * 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`. + */ + abstract Expr getLesserOperand(); +} + +/** + * A C/C++ greater than expression. + */ +class GTExpr extends RelationalOperation, @gtexpr { + override string getOperator() { result = ">" } + + override Expr getGreaterOperand() { result = getLeftOperand() } + override Expr getLesserOperand() { result = getRightOperand() } +} + +/** + * A C/C++ lesser than expression. + */ +class LTExpr extends RelationalOperation, @ltexpr { + override string getOperator() { result = "<" } + + override Expr getGreaterOperand() { result = getRightOperand() } + override Expr getLesserOperand() { result = getLeftOperand() } +} + +/** + * A C/C++ greater than or equal expression. + */ +class GEExpr extends RelationalOperation, @geexpr { + override string getOperator() { result = ">=" } + + override Expr getGreaterOperand() { result = getLeftOperand() } + override Expr getLesserOperand() { result = getRightOperand() } +} + +/** + * A C/C++ lesser than or equal expression. + */ +class LEExpr extends RelationalOperation, @leexpr { + override string getOperator() { result = "<=" } + + override Expr getGreaterOperand() { result = getRightOperand() } + override Expr getLesserOperand() { result = getLeftOperand() } +} diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll b/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll new file mode 100644 index 000000000000..7e713c19f4bb --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll @@ -0,0 +1,986 @@ + +import semmle.code.cpp.Element +private import semmle.code.cpp.Enclosing +private import semmle.code.cpp.internal.Type + +/** + * A C/C++ expression. + */ +class Expr extends StmtParent, @expr { + /** Gets the nth child of this expression. */ + Expr getChild(int n) { exprparents(result,n,this) } + + /** Gets the number of direct children of this expression. */ + int getNumChild() { result = count(this.getAChild()) } + + /** Holds if e is the nth child of this expression. */ + predicate hasChild(Expr e, int n) { e = this.getChild(n) } + + /** Gets the enclosing function of this expression, if any. */ + 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() + } + + override Stmt getEnclosingStmt() { + result = this.getParent().(Expr).getEnclosingStmt() or + result = this.getParent().(Stmt) or + exists(Expr other | result = other.getEnclosingStmt() and other.getConversion() = this) or + exists(DeclStmt d, LocalVariable v | d.getADeclaration() = v and v.getInitializer().getExpr() = this and result = d) + } + + /** Gets the enclosing variable of this expression, if any. */ + Variable getEnclosingVariable() { result = exprEnclosingElement(this) } + + /** Gets a child of this expression. */ + Expr getAChild() { exists (int n | result = this.getChild(n)) } + + /** Gets the parent of this expression, if any. */ + Element getParent() { exprparents(this,_,result) } + + /** Gets the location of this expression. */ + override Location getLocation() { exprs(this,_,result) } + + /** Holds if this is an auxiliary expression generated by the compiler. */ + predicate isCompilerGenerated() { + compgenerated(this) or + this.getParent().(ConstructorFieldInit).isCompilerGenerated() + } + + /** + * Gets the type of this expression. + * + * As the type of an expression can sometimes be a TypedefType, calling getUnderlyingType() + * is often more useful than calling this predicate. + */ + pragma[nomagic] Type getType() { expr_types(this,unresolve(result),_) } + + /** + * Gets the type of this expression after typedefs have been resolved. + * + * In most cases, this predicate will be the same as getType(). It will + * only differ when the result of getType() is a TypedefType, in which + * case this predicate will (possibly recursively) resolve the typedef. + */ + Type getUnderlyingType() { result = this.getType().getUnderlyingType() } + + /** + * Gets an integer indicating the type of expression that this represents. + * + * Consider using subclasses of `Expr` rather than relying on this predicate. + */ + int getKind() { exprs(this,result,_) } + + /** Gets a textual representation of this expression. */ + override string toString() { none() } + + /** Gets the value of this expression, if it is a constant. */ + string getValue() { exists(@value v | values(v,result,_) and valuebind(v,this)) } + + /** Gets the source text for the value of this expression, if it is a constant. */ + string getValueText() { exists(@value v | values(v,_,result) and valuebind(v,this)) } + + /** Holds if this expression has a value that can be determined at compile time. */ + predicate isConstant() { valuebind(_,this) } + + /** + * Holds if this expression is side-effect free (conservative + * approximation). This predicate cannot be overridden; + * override mayBeImpure() instead. + * + * Note that this predicate does not strictly correspond with + * the usual definition of a 'pure' function because reading + * from global state is permitted, just not writing / output. + */ + final predicate isPure() { + not this.mayBeImpure() + } + + /** + * Holds if it is possible that the expression may be impure. If we are not + * sure, then it holds. + */ + predicate mayBeImpure() { + any() + } + + /** + * Holds if it is possible that the expression may be impure. If we are not + * sure, then it holds. Unlike `mayBeImpure()`, this predicate does not + * consider modifications to temporary local variables to be impure. If you + * call a function in which nothing may be globally impure then the function + * as a whole will have no side-effects, even if it mutates its own fresh + * stack variables. + */ + predicate mayBeGloballyImpure() { + any() + } + + /** + * Holds if this expression is an lvalue. An lvalue is an expression that + * represents a location, rather than a value. + * See [basic.lval] for more about lvalues. + */ + predicate isLValueCategory() { + expr_types(this, _, 3) + } + + /** + * Holds if this expression is an xvalue. An xvalue is a location whose + * lifetime is about to end (e.g. an rvalue reference returned from a function + * call). + * See [basic.lval] for more about xvalues. + */ + predicate isXValueCategory() { + expr_types(this, _, 2) + } + + /** + * Holds if this expression is a prvalue. A prvalue is an expression that + * represents a value, rather than a location. + * See [basic.lval] for more about prvalues. + */ + predicate isPRValueCategory() { + expr_types(this, _, 1) + } + + /** + * Holds if this expression is a glvalue. A glvalue is either an lvalue or an + * xvalue. + */ + predicate isGLValueCategory() { + isLValueCategory() or isXValueCategory() + } + + /** + * Holds if this expression is an rvalue. An rvalue is either a prvalue or an + * xvalue. + */ + predicate isRValueCategory() { + isPRValueCategory() or isXValueCategory() + } + + /** + * Gets a string representation of the value category of the expression. + * This is intended only for debugging. The possible values are: + * + * - "lvalue" + * - "xvalue" + * - "prvalue" + * - "prvalue(load)" + * + * The "prvalue(load)" string is used when the expression is a prvalue, but + * `hasLValueToRvalueConversion()` holds. + */ + string getValueCategoryString() { + ( + isLValueCategory() and + result = "lvalue" + ) or ( + isXValueCategory() and + result = "xvalue" + ) or ( + isPRValueCategory() and + if hasLValueToRValueConversion() then + result = "prvalue(load)" + else + result = "prvalue" + + ) + } + + /** + * Gets the parent of this expression, if any, in an alternative syntax tree + * that has `Conversion`s as part of the tree. + */ + Element getParentWithConversions() { convparents(this,_,result) } + + /** + * Holds if this expression will not be evaluated because of its context, + * such as an expression inside a sizeof. + */ + predicate isUnevaluated() { + exists (Element e | + e = getParentWithConversions+() | + e instanceof SizeofOperator or + exists ( Expr e2 | + e.(TypeidOperator).getExpr() = e2 and + ( + not e2.getActualType().getUnspecifiedType().(Class).isPolymorphic() or + not e2.isGLValueCategory() + ) + ) or + e instanceof NoExceptExpr or + e instanceof AlignofOperator + ) or exists (Decltype d | + d.getExpr() = getParentWithConversions*() + ) + } + + /** + * Holds if this expression has undergone an lvalue-to-rvalue conversion to + * extract its value. + * for example: + * ``` + * y = x; + * ``` + * The VariableAccess for `x` is a prvalue, and hasLValueToRValueConversion() + * holds because the value of `x` was loaded from the location of `x`. + * The VariableAccess for `y` is an lvalue, and hasLValueToRValueConversion() + * does not hold because the value of `y` was not extracted. + * + * See [conv.lval] for more about the lvalue-to-rvalue conversion + */ + predicate hasLValueToRValueConversion() { + expr_isload(this) + } + + /** + * Holds if this expression is an LValue, in the sense of having an address. + * + * Being an LValue is best approximated as having an address. + * This is a strict superset of modifiable LValues, which are best approximated by things which could be on the left-hand side of an assignment. + * This is also a strict superset of expressions which provide an LValue, which is best approximated by things whose address is important. + * + * See [basic.lval] in the C++ language specification. + * In C++03, every expression is either an LValue or an RValue. + * In C++11, every expression is exactly one of an LValue, an XValue, or a PRValue (with RValues being the union of XValues and PRValues). + * Using the C++11 terminology, this predicate selects expressions whose value category is lvalue. + */ + predicate isLValue() { + this instanceof StringLiteral /* C++ n3337 - 5.1.1 clause 1 */ + or this.(ParenthesisExpr).getExpr().isLValue() /* C++ n3337 - 5.1.1 clause 6 */ + or (this instanceof VariableAccess and not this instanceof FieldAccess) /* C++ n3337 - 5.1.1 clauses 8 and 9, variables and data members */ + or exists(FunctionAccess fa | fa = this | /* C++ n3337 - 5.1.1 clauses 8 and 9, functions */ + fa.getTarget().isStatic() + or not fa.getTarget().isMember() + ) + or this instanceof ArrayExpr /* C++ n3337 - 5.2.1 clause 1 */ + or this.getType() instanceof ReferenceType /* C++ n3337 - 5.2.2 clause 10 + 5.2.5 clause 4, no bullet point + 5.2.7 clauses 2 and 5 + 5.2.9 clause 1 + 5.2.10 clause 1 + 5.2.11 clause 1 + 5.4 clause 1 */ + or this.(FieldAccess).getQualifier().isLValue() /* C++ n3337 - 5.2.5 clause 4, 2nd bullet point */ + or this instanceof TypeidOperator /* C++ n3337 - 5.2.8 clause 1 */ + or this instanceof PointerDereferenceExpr /* C++ n3337 - 5.3.1 clause 1 */ + or this instanceof PrefixIncrExpr /* C++ n3337 - 5.3.2 clause 1 */ + or this instanceof PrefixDecrExpr /* C++ n3337 - 5.3.2 clause 2 */ + or exists(ConditionalExpr ce | ce = this | /* C++ n3337 - 5.16 clause 4 */ + ce.getThen().isLValue() and + ce.getElse().isLValue() and + ce.getThen().getType() = ce.getElse().getType() + ) + or this instanceof Assignment /* C++ n3337 - 5.17 clause 1 */ + or this.(CommaExpr).getRightOperand().isLValue() /* C++ n3337 - 5.18 clause 1 */ + } + + /** + * Gets the precedence of the main operator of this expression; + * higher precedence binds tighter. + */ + int getPrecedence() { + none() + } + + /** + * Holds if this expression has a conversion. + * + * Type casts and parameterized expressions are not part of the main + * expression tree structure but attached on the nodes they convert, + * for example: + * ``` + * 2 + (int)(bool)1 + * ``` + * has the main tree: + * ``` + * 2 + 1 + * ``` + * and 1 has a bool conversion, while the bool conversion itself has + * an int conversion. + */ + predicate hasConversion() { exists(Expr e | exprconv(this,e)) } + + /** + * Holds if this expression has an implicit conversion. + * + * For example in `char *str = 0`, the `0` has an implicit conversion to type `char *`. + */ + predicate hasImplicitConversion() { exists(Expr e | exprconv(this,e) and e.(Cast).isImplicit()) } + + /** + * Holds if this expression has an explicit conversion. + * + * For example in `(MyClass *)ptr`, the `ptr` has an explicit + * conversion to type `MyClass *`. + */ + predicate hasExplicitConversion() { exists(Expr e | exprconv(this,e) and not e.(Cast).isImplicit()) } + + /** + * Gets the conversion associated with this expression, if any. + */ + Expr getConversion() { exprconv(this,result) } + + /** + * Gets a string describing the conversion associated with this expression, + * or "" if there is none. + */ + string getConversionString() { (result = this.getConversion().toString() and this.hasConversion()) or (result = "" and not this.hasConversion()) } + + /** Gets the fully converted form of this expression, including all type casts and other conversions. */ + cached + Expr getFullyConverted() { + if this.hasConversion() then + result = this.getConversion().getFullyConverted() + else + result = this + } + + /** + * Gets this expression with all of its explicit casts, but none of its + * implicit casts. More precisely this takes conversions up to the last + * explicit cast (there may be implicit conversions along the way), but does + * not include conversions after the last explicit cast. + * + * C++ example: `C c = (B)d` might have three casts: (1) an implicit cast + * from A to some D, (2) an explicit cast from D to B, and (3) an implicit + * 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()) + } + + /** + * Gets this expression with all of its initial implicit casts, but none of + * its explicit casts. More precisely, this takes all implicit conversions + * up to (but not including) the first explicit cast (if any). + */ + Expr getImplicitlyConverted() { + if this.hasImplicitConversion() then + result = this.getConversion().getImplicitlyConverted() + else + result = this + } + + /** + * Gets the type of this expression, after any implicit conversions and explicit casts, and after resolving typedefs. + * + * As an example, consider the AST fragment `(i64)(void*)0` in the context of `typedef long long i64;`. The fragment + * contains three expressions: two CStyleCasts and one literal Zero. For all three expressions, the result of this + * predicate will be `long long`. + */ + Type getActualType() { + result = this.getFullyConverted().getType().getUnderlyingType() + } + + /** Holds if this expression is parenthesised. */ + predicate isParenthesised() { this.getConversion() instanceof ParenthesisExpr } + + /** Gets the function containing this control-flow node. */ + override Function getControlFlowScope() { + result = this.getEnclosingFunction() + } +} + +/** + * A C/C++ operation. + */ +abstract class Operation extends Expr { + /** Gets the operator of this operation. */ + abstract string getOperator(); + + /** Gets an operand of this operation. */ + Expr getAnOperand() { + result = this.getAChild() + } +} + +/** + * A C/C++ unary operation. + */ +abstract class UnaryOperation extends Operation { + /** Gets the operand of this unary operation. */ + Expr getOperand() { this.hasChild(result,0) } + + override string toString() { result = this.getOperator() + " ..." } + + override predicate mayBeImpure() { + this.getOperand().mayBeImpure() + } + override predicate mayBeGloballyImpure() { + this.getOperand().mayBeGloballyImpure() + } +} + +/** + * A C/C++ binary operation. + */ +abstract class BinaryOperation extends Operation { + /** Gets the left operand of this binary operation. */ + Expr getLeftOperand() { this.hasChild(result,0) } + + /** Gets the right operand of this binary operation. */ + Expr getRightOperand() { this.hasChild(result,1) } + + override string toString() { result = "... " + this.getOperator() + " ..." } + + override predicate mayBeImpure() { + this.getLeftOperand().mayBeImpure() or + this.getRightOperand().mayBeImpure() + } + override predicate mayBeGloballyImpure() { + this.getLeftOperand().mayBeGloballyImpure() or + this.getRightOperand().mayBeGloballyImpure() + } +} + +/** + * A C++11 parenthesized braced initializer list within a template. + * + * This is used to represent particular syntax within templates where the final + * form of the expression is not known. In actual instantiations, it will have + * been turned into a constructor call or aggregate initializer or similar. + */ +class ParenthesizedBracedInitializerList extends Expr, @braced_init_list { + override string toString() { result = "({...})" } +} + +/** + * A C/C++ parenthesis expression. +*/ +class ParenthesisExpr extends Conversion, @parexpr { + override string toString() { result = "(...)" } +} + +/** + * A C/C++ expression that has not been resolved. + */ +class ErrorExpr extends Expr, @errorexpr { + override string toString() { result = "" } +} + +/** + * A Microsoft C/C++ __assume expression. + */ +class AssumeExpr extends Expr, @assume { + override string toString() { result = "__assume(...)" } +} + +/** + * A C/C++ comma expression. + */ +class CommaExpr extends Expr, @commaexpr { + /** + * Gets the left operand, which is the one whose value is discarded. + */ + Expr getLeftOperand() { this.hasChild(result,0) } + + /** + * Gets the right operand, which is the one whose value is equal to the value + * of the comma expression itself. + */ + Expr getRightOperand() { this.hasChild(result,1) } + + override string toString() { result = "... , ..." } + + override int getPrecedence() { result = 0 } + + override predicate mayBeImpure() { + this.getLeftOperand().mayBeImpure() or + this.getRightOperand().mayBeImpure() + } + override predicate mayBeGloballyImpure() { + this.getLeftOperand().mayBeGloballyImpure() or + this.getRightOperand().mayBeGloballyImpure() + } +} + +/** + * A C/C++ address-of expression. + */ +class AddressOfExpr extends UnaryOperation, @address_of { + /** Gets the function or variable whose address is taken. */ + Declaration getAddressable() { + result = this.getOperand().(Access).getTarget() + // this handles the case where we are taking the address of a reference variable + or result = this.getOperand().(ReferenceDereferenceExpr).getChild(0).(Access).getTarget() + } + + override string getOperator() { result = "&" } + + override int getPrecedence() { result = 15 } + + override predicate mayBeImpure() { + this.getOperand().mayBeImpure() + } + override predicate mayBeGloballyImpure() { + this.getOperand().mayBeGloballyImpure() + } +} + +/** + * An implicit conversion from type T to type T&. + * + * This typically occurs when an expression of type T is used to initialize a variable or parameter of + * type T&, and is to reference types what AddressOfExpr is to pointer types - though this class is + * considered to be a conversion rather than an operation, and as such doesn't occur in the main AST. + */ +class ReferenceToExpr extends Conversion, @reference_to { + override string toString() { result = "(reference to)" } + + override int getPrecedence() { result = 15 } +} + +/** + * An instance of unary operator * applied to a built-in type. + * + * For user-defined types, see OverloadedPointerDereferenceExpr. + */ +class PointerDereferenceExpr extends UnaryOperation, @indirect { + /** + * DEPRECATED: Use getOperand() instead. + * + * Gets the expression that is being dereferenced. + */ + deprecated Expr getExpr() { + result = getOperand() + } + + override string getOperator() { result = "*" } + + override int getPrecedence() { result = 15 } + + override predicate mayBeImpure() { + this.getChild(0).mayBeImpure() or + this.getChild(0).getFullyConverted().getType().(DerivedType).getBaseType().isVolatile() + } + override predicate mayBeGloballyImpure() { + this.getChild(0).mayBeGloballyImpure() or + this.getChild(0).getFullyConverted().getType().(DerivedType).getBaseType().isVolatile() + } +} + +/** + * An implicit conversion from type T& to type T. + * + * This typically occurs when an variable of type T& is used in a context which expects type T, and + * is to reference types what PointerDereferenceExpr is to pointer types - though this class is + * considered to be a conversion rather than an operation, and as such doesn't occur in the main AST. + */ +class ReferenceDereferenceExpr extends Conversion, @ref_indirect { + override string toString() { result = "(reference dereference)" } +} + +/** + * A C++ `new` (non-array) expression. + */ +class NewExpr extends Expr, @new_expr { + override string toString() { result = "new" } + + override int getPrecedence() { result = 15 } + + /** + * Gets the type that is being allocated. + * + * For example, for `new int` the result is `int`. + */ + Type getAllocatedType() { + new_allocated_type(this, unresolve(result)) + } + + /** + * Gets the `operator new` that allocates storage. + */ + Function getAllocator() { + expr_allocator(this, result, _) + } + + /** + * Holds if the allocation function is the version that expects an alignment + * argument of type `std::align_val_t`. + */ + predicate hasAlignedAllocation() { + expr_allocator(this, _, 1) + } + + /** + * Gets the call to a non-default `operator new` that allocates storage, if any. + * + * As a rule of thumb, there will be an allocator call precisely when the type + * being allocated has a custom `operator new`, or when an argument list appears + * after the `new` keyword and before the name of the type being allocated. + * + * In particular note that uses of placement-new and nothrow-new will have an + * allocator call. + */ + FunctionCall getAllocatorCall() { result = this.getChild(0) } + + /** + * Gets the `operator delete` that deallocates storage if the initialization + * throws an exception, if any. + */ + Function getDeallocator() { + expr_deallocator(this, result, _) + } + + /** + * Holds if the deallocation function expects a size argument. + */ + predicate hasSizedDeallocation() { + exists(int form | + expr_deallocator(this, _, form) and + form.bitAnd(1) != 0 // Bit zero is the "size" bit + ) + } + + /** + * Holds if the deallocation function expects an alignment argument. + */ + predicate hasAlignedDeallocation() { + exists(int form | + expr_deallocator(this, _, form) and + form.bitAnd(2) != 0 // Bit one is the "alignment" bit + ) + } + + /** + * Gets the call or expression that initializes the allocated object, if any. + * + * As examples, for `new int(4)`, this will be `4`, and for `new std::vector(4)`, this will + * be a call to the constructor `std::vector::vector(size_t)` with `4` as an argument. + */ + Expr getInitializer() { result = this.getChild(1) } +} + +/** + * A C++ `new[]` (array) expression. + */ +class NewArrayExpr extends Expr, @new_array_expr { + override string toString() { result = "new[]" } + + override int getPrecedence() { result = 15 } + + /** + * Gets the type that is being allocated. + * + * For example, for `new int[5]` the result is `int[5]`. + */ + Type getAllocatedType() { + new_array_allocated_type(this, unresolve(result)) + } + + /** + * Gets the element type of the array being allocated. + */ + Type getAllocatedElementType() { + result = getType().getUnderlyingType().(PointerType).getBaseType() + } + + /** + * Gets the `operator new[]` that allocates storage. + */ + Function getAllocator() { + expr_allocator(this, result, _) + } + + /** + * Holds if the allocation function is the version that expects an alignment + * argument of type `std::align_val_t`. + */ + predicate hasAlignedAllocation() { + expr_allocator(this, _, 1) + } + + /** + * Gets the call to a non-default `operator new[]` that allocates storage for the array, if any. + * + * If the default `operator new[]` is used, then there will be no call. + */ + FunctionCall getAllocatorCall() { result = this.getChild(0) } + + /** + * Gets the `operator delete` that deallocates storage if the initialization + * throws an exception, if any. + */ + Function getDeallocator() { + expr_deallocator(this, result, _) + } + + /** + * Holds if the deallocation function expects a size argument. + */ + predicate hasSizedDeallocation() { + exists(int form | + expr_deallocator(this, _, form) and + form.bitAnd(1) != 0 // Bit zero is the "size" bit + ) + } + + /** + * Holds if the deallocation function expects an alignment argument. + */ + predicate hasAlignedDeallocation() { + exists(int form | + expr_deallocator(this, _, form) and + form.bitAnd(2) != 0 // Bit one is the "alignment" bit + ) + } + + /** + * Gets the call or expression that initializes the first element of the array, if any. + * + * This will either be a call to the default constructor for the array's element type (as + * in `new std::string[10]`), or a literal zero for arrays of scalars which are zero-initialized + * due to extra parentheses (as in `new int[10]()`). + * + * At runtime, the constructor will be called once for each element in the array, but the + * constructor call only exists once in the AST. + */ + Expr getInitializer() { result = this.getChild(1) } + + /** + * Gets the extent of the non-constant array dimension, if any. + * + * As examples, for `new char[n]` and `new char[n][10]`, this gives `n`, but for `new char[10]` this + * gives nothing, as the 10 is considered part of the type. + */ + Expr getExtent() { result = this.getChild(2) } +} + +/** + * A C++ `delete` (non-array) expression. + */ +class DeleteExpr extends Expr, @delete_expr { + override string toString() { result = "delete" } + + override int getPrecedence() { result = 15 } + + /** + * Gets the compile-time type of the object being deleted. + */ + Type getDeletedObjectType() { + result = getExpr().getFullyConverted().getType().stripTopLevelSpecifiers().(PointerType).getBaseType() + } + + /** + * Gets the call to a destructor that occurs prior to the object's memory being deallocated, if any. + */ + DestructorCall getDestructorCall() { result = this.getChild(1) } + + /** + * Gets the destructor to be called to destroy the object, if any. + */ + Destructor getDestructor() { result = getDestructorCall().getTarget() } + + /** + * Gets the `operator delete` that deallocates storage. Does not hold + * if the type being destroyed has a virtual destructor. In that case, the + * `operator delete` that will be called is determined at runtime based on the + * dynamic type of the object. + */ + Function getDeallocator() { + expr_deallocator(this, result, _) + } + + /** + * Holds if the deallocation function expects a size argument. + */ + predicate hasSizedDeallocation() { + exists(int form | + expr_deallocator(this, _, form) and + form.bitAnd(1) != 0 // Bit zero is the "size" bit + ) + } + + /** + * Holds if the deallocation function expects an alignment argument. + */ + predicate hasAlignedDeallocation() { + exists(int form | + expr_deallocator(this, _, form) and + form.bitAnd(2) != 0 // Bit one is the "alignment" bit + ) + } + +/** + * Gets the call to a non-default `operator delete` that deallocates storage, if any. + * + * This will only be present when the type being deleted has a custom `operator delete`. + */ + FunctionCall getAllocatorCall() { result = this.getChild(0) } + + /** + * Gets the object being deleted. + */ + Expr getExpr() { result = this.getChild(3) or result = this.getChild(1).getChild(-1) } +} + +/** + * A C++ `delete[]` (array) expression. + */ +class DeleteArrayExpr extends Expr, @delete_array_expr { + override string toString() { result = "delete[]" } + + override int getPrecedence() { result = 15 } + + /** + * Gets the element type of the array being deleted. + */ + Type getDeletedElementType() { + result = getExpr().getFullyConverted().getType().stripTopLevelSpecifiers().(PointerType).getBaseType() + } + + /** + * Gets the call to a destructor that occurs prior to the array's memory being deallocated, if any. + * + * At runtime, the destructor will be called once for each element in the array, but the + * destructor call only exists once in the AST. + */ + DestructorCall getDestructorCall() { result = this.getChild(1) } + + /** + * Gets the destructor to be called to destroy each element in the array, if any. + */ + Destructor getDestructor() { result = getDestructorCall().getTarget() } + + /** + * Gets the `operator delete[]` that deallocates storage. + */ + Function getDeallocator() { + expr_deallocator(this, result, _) + } + + /** + * Holds if the deallocation function expects a size argument. + */ + predicate hasSizedDeallocation() { + exists(int form | + expr_deallocator(this, _, form) and + form.bitAnd(1) != 0 // Bit zero is the "size" bit + ) + } + + /** + * Holds if the deallocation function expects an alignment argument. + */ + predicate hasAlignedDeallocation() { + exists(int form | + expr_deallocator(this, _, form) and + form.bitAnd(2) != 0 // Bit one is the "alignment" bit + ) + } + + /** + * Gets the call to a non-default `operator delete` that deallocates storage, if any. + * + * This will only be present when the type being deleted has a custom `operator delete`. + */ + FunctionCall getAllocatorCall() { result = this.getChild(0) } + + /** + * Gets the array being deleted. + */ + Expr getExpr() { result = this.getChild(3) or result = this.getChild(1).getChild(-1) } +} + +/** + * A compound statement enclosed in parentheses used as an expression (a GNU extension to C/C++). + */ +class StmtExpr extends Expr, @expr_stmt { + override string toString() { result = "(statement expression)" } + + /** + * Gets the statement enclosed by this `StmtExpr`. + */ + Stmt getStmt() { result.getParent() = this } + + /** + * Gets the result expression of the enclosed statement. For example, + * `a+b` is the result expression in this example: + * + * ``` + * x = ({ dosomething(); a+b; }); + * ``` + */ + Expr getResultExpr() { + result = getStmtResultExpr(getStmt()) + } +} + +/** 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()) +} + +/** + * A C/C++ this expression. + */ +class ThisExpr extends Expr, @thisaccess { + override string toString() { result = "this" } + + override predicate mayBeImpure() { + none() + } + override predicate mayBeGloballyImpure() { + none() + } +} + +/** + * A code block expression, for example `^ int (int x, int y) {return x + y;}`. + * + * Blocks are a language extension supported by Clang, and by Apple's + * branch of GCC. + */ +class BlockExpr extends Literal { + BlockExpr() { + code_block(this, _) + } + + override string toString() { result = "^ { ... }" } + + /** + * Gets the (anonymous) function associated with this code block expression. + */ + Function getFunction() { + code_block(this, result) + } +} + +/** + * A C++11 `noexcept` expression, for example `noexcept(1 + 2)`. + */ +class NoExceptExpr extends Expr, @noexceptexpr { + override string toString() { result = "noexcept(...)" } + + /** + * Gets the expression inside this noexcept expression. + */ + Expr getExpr() { + result = this.getChild(0) + } +} + +/** + * Holds if `child` is the `n`th child of `parent` in an alternative syntax + * tree that has `Conversion`s as part of the tree. + */ +private predicate convparents(Expr child, int idx, Element parent) { + child.getConversion() = parent and + idx = 0 + or + exists(Expr astChild | + exprparents(astChild, idx, parent) and + child = astChild.getFullyConverted() + ) +} diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Lambda.qll b/cpp/ql/src/semmle/code/cpp/exprs/Lambda.qll new file mode 100644 index 000000000000..e99e85edb028 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/exprs/Lambda.qll @@ -0,0 +1,148 @@ +import semmle.code.cpp.exprs.Expr +import semmle.code.cpp.Class + +/** + * A C++11 lambda expression, such as `[&, =y](int x) mutable -> double {return z = (y += x);}`. + * + * The type given by `getType()` will be an instance of `Closure`. + */ +class LambdaExpression extends Expr, @lambdaexpr { + override string toString() { + result = "[...](...){...}" + } + + /** + * Gets an implicitly or explicitly captured value of this lambda expression. + */ + LambdaCapture getACapture() { + result = getCapture(_) + } + + /** + * Gets the nth implicitly or explicitly captured value of this lambda expression. + */ + LambdaCapture getCapture(int index) { + lambda_capture(result, this, index, _, _, _) + } + + /** + * Gets the default variable capture mode for the lambda expression. + * + * Will be one of: + * - "" if no default was specified, meaning that all captures must be explicit. + * - "&" if capture-by-reference is the default for implicit captures. + * - "=" if capture-by-value is the default for implicit captures. + */ + string getDefaultCaptureMode() { + lambdas(this, result, _) + } + + /** + * Holds if the return type (of the call operator of the resulting object) was explicitly specified. + */ + predicate returnTypeIsExplicit() { + lambdas(this, _, true) + } + + /** + * Gets the function which will be invoked when the resulting object is called. + * + * Various components of the lambda expression can be obtained from components of this + * function, such as: + * - The number and type of parameters. + * - Whether the mutable keyword was used (iff this function is not const). + * - The return type. + * - The statements comprising the lambda body. + */ + Operator getLambdaFunction() { + result = getType().(Closure).getLambdaFunction() + } +} + +/** + * A class written by the compiler to be the type of a C++11 lambda expression. + */ +class Closure extends Class { + Closure() { + exists(LambdaExpression e | this = e.getType()) + } + + /** Gets the lambda expression of which this is the type. */ + LambdaExpression getLambdaExpression() { + result.getType() = this + } + + /** Gets the compiler-generated operator() of this closure type. */ + Operator getLambdaFunction() { + result = this.getAMember() + and result.getName() = "operator()" + } + + override string toString() { + result = "decltype([...](...){...})" + } +} + +/** + * Information about a value captured as part of a lambda expression. + */ +class LambdaCapture extends @lambdacapture { + string toString() { + result = getField().toString() + } + + /** + * Holds if this capture was made implicitly. + */ + predicate isImplicit() { + lambda_capture(this, _, _, _, true, _) + } + + /** + * Holds if the variable was captured by reference. + * + * An identifier is captured by reference if: + * - It is explicitly captured by reference. + * - It is implicitly captured, and the lambda's default capture mode is by-reference. + * - The identifier is "this". [Said behaviour is dictated by the C++11 standard, but it + * is actually "*this" being captured rather than "this".] + */ + predicate isCapturedByReference() { + lambda_capture(this, _, _, true, _, _) + } + + /** + * Gets the location of the declaration of this capture. + * + * For explicit captures, this is a location within the "[...]" part of the lambda expression. + * + * For implicit captures, this is the first location within the "{...}" part of the lambda + * expression which accesses the captured variable. + */ + Location getLocation() { + lambda_capture(this, _, _, _, _, result) + } + + /** + * Gets the field of the lambda expression's closure type which is used to store this capture. + */ + MemberVariable getField() { + exists(LambdaExpression lambda, int index | this = lambda.getCapture(index) | + result = lambda.getType().(Closure).getCanonicalMember(index) + ) + } + + /** + * Gets the expression which yields the final captured value. + * + * In many cases, this will be an instance of VariableAccess. + * If a this-pointer is being captured, this will be an instance of ThisExpr. + * For by-value captures of non-primitive types, this will be a call to a copy constructor. + */ + Expr getInitializer() { + exists(LambdaExpression lambda, int index | this = lambda.getCapture(index) | + result = lambda.getChild(0) // Call to the constructor of the closure type. + .getChild(index) // The appropriate argument to the constructor. + ) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Literal.qll b/cpp/ql/src/semmle/code/cpp/exprs/Literal.qll new file mode 100644 index 000000000000..262d99251713 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/exprs/Literal.qll @@ -0,0 +1,259 @@ +import semmle.code.cpp.exprs.Expr + +/** + * A C/C++ literal. + */ +class Literal extends Expr, @literal { + /** Gets a textual representation of this literal. */ + override string toString() { + result = this.getValue() or + ( + not exists(this.getValue()) and + result = "Unknown literal" + ) + } + + override predicate mayBeImpure() { + none() + } + override predicate mayBeGloballyImpure() { + none() + } +} + +/** + * A label literal, that is, a use of the '&&' operator to take the address of a + * label for use in a computed goto statement. This is a non-standard C/C++ extension. + * + * For example: + * ``` + * void *label_ptr = &&myLabel; // &&myLabel is a LabelLiteral + * + * goto *label_ptr; // this is a ComputedGotoStmt + * + * myLabel: // this is a LabelStmt + * ``` + */ +class LabelLiteral extends Literal { + LabelLiteral() { + jumpinfo(this,_,_) + } + + /** Gets the corresponding label statement. */ + LabelStmt getLabel() { + jumpinfo(this,_,result) + } +} + +/** A character literal or a string literal. */ +abstract class TextLiteral extends Literal { + /** Gets a hex escape sequence that appears in the character or string literal (see [lex.ccon] in the C++ Standard). */ + string getAHexEscapeSequence(int occurrence, int offset) { + result = getValueText().regexpFind("(?` for some protocol `P`, + * then there will be no result. If the receiving expression has type `C*` or type + * `C

    *` for some protocol `P`, then the result will be the type `C`. + */ + ObjectiveClass getReceiverClass() { + none() + } + + /** + * Gets the Objective C classes and/or protocols which are statically implemented + * by the receiving expression. + * + * If the receiving expression has type `id`, then there will be no result. + * If the receiving expression has type `id

    `, then `P` will be the sole result. + * If the receiving expression has type `C*`, then `C` will be the sole result. + * If the receiving expression has type `C

    *`, then `C` and `P` will both be results. + */ + Class getAReceiverClassOrProtocol() { + none() + } +} + +/** + * DEPRECATED: Objective-C is no longer supported. + * An access to an Objective C property using dot syntax. + * + * Such accesses are de-sugared into a message expression to the property's getter or setter. + */ +deprecated class PropertyAccess extends ExprMessageExpr { + PropertyAccess() { none() } + + /** + * Gets the property being accessed by this expression. + */ + Property getProperty() { + none() + } +} + +/** + * DEPRECATED: Objective-C is no longer supported. + * An Objective C `@selector` expression, for example `@selector(driveForDistance:)`. + */ +deprecated class AtSelectorExpr extends Expr { + AtSelectorExpr() { none() } + + override string toString() { + none() + } + + /** + * Gets the selector of this `@selector` expression, for example `driveForDistance:`. + */ + string getSelector() { none() } +} + +/** + * DEPRECATED: Objective-C is no longer supported. + * An Objective C `@protocol` expression, for example `@protocol(SomeProtocol)`. + */ +deprecated class AtProtocolExpr extends Expr { + AtProtocolExpr() { none() } + + override string toString() { + none() + } + + /** + * Gets the protocol of this `@protocol` expression, for example `SomeProtocol`. + */ + Protocol getProtocol() { none() } +} + +/** + * DEPRECATED: Objective-C is no longer supported. + * An Objective C `@encode` expression, for example `@encode(int *)`. + */ +deprecated class AtEncodeExpr extends Expr { + AtEncodeExpr() { none() } + + override string toString() { + none() + } + + /** + * Gets the type this `@encode` expression encodes, for example `int *`. + */ + Type getEncodedType() { none() } +} + +/** + * DEPRECATED: Objective-C is no longer supported. + * An Objective C throw expression. + */ +deprecated class ObjcThrowExpr extends ThrowExpr { + ObjcThrowExpr() { none() } + + override string toString() { none() } +} + +/** + * DEPRECATED: Objective-C is no longer supported. + * An Objective C throw expression with no argument (which causes the + * current exception to be re-thrown). + */ +deprecated class ObjcReThrowExpr extends ReThrowExpr, ObjcThrowExpr { + ObjcReThrowExpr() { none() } + + override string toString() { none() } +} + +/** + * DEPRECATED: Objective-C is no longer supported. + * An Objective C @ expression which boxes a single value, such as @(22). + */ +deprecated class AtExpr extends UnaryOperation { + AtExpr() { none() } + + override string toString() { none() } + + override string getOperator() { none() } +} + +/** + * DEPRECATED: Objective-C is no longer supported. + * An Objective C @[...] literal. + */ +deprecated class ArrayLiteral extends Expr { + ArrayLiteral() { none() } + + /** Gets a textual representation of this array literal. */ + override string toString() { none() } + + /** An element of the array */ + Expr getElement(int i) { none() } +} + +/** + * DEPRECATED: Objective-C is no longer supported. + * An Objective C @{...} literal. + */ +deprecated class DictionaryLiteral extends Expr { + DictionaryLiteral() { none() } + + /** Gets a textual representation of this dictionary literal. */ + override string toString() { none() } +} + +/** + * DEPRECATED: Objective-C is no longer supported. + * An Objective C @"..." string literal. + */ +deprecated class ObjCLiteralString extends TextLiteral { + ObjCLiteralString() { none() } +} + +/** + * DEPRECATED: Objective-C is no longer supported. + * An Objective C/C++ overloaded subscripting access expression. + * + * Either + * obj[idx] + * or + * obj[idx] = expr + */ +deprecated class SubscriptExpr extends Expr { + SubscriptExpr() { none() } + + /** + * Gets the object expression being subscripted. + */ + Expr getSubscriptBase() { none() } + + /** + * Gets the expression giving the index into the object. + */ + Expr getSubscriptIndex() { none() } + + /** + * Gets the expression being assigned (if this is an assignment). + */ + Expr getAssignedExpr() { none() } + + override string toString() { none() } +} + +/** + * DEPRECATED: Objective-C is no longer supported. + * An Objective C _cmd expression. + */ +deprecated class CmdExpr extends Expr { + CmdExpr() { none() } + + override string toString() { none() } + + override predicate mayBeImpure() { + none() + } + override predicate mayBeGloballyImpure() { + none() + } +} diff --git a/cpp/ql/src/semmle/code/cpp/headers/MultipleInclusion.qll b/cpp/ql/src/semmle/code/cpp/headers/MultipleInclusion.qll new file mode 100644 index 000000000000..d259d1ef8059 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/headers/MultipleInclusion.qll @@ -0,0 +1,176 @@ +/** + * Provides definitions related to _include guards_: techniques for preventing + * multiple inclusion of header files. + */ + +import cpp + +/** + * A header file with an include guard. + */ +abstract class IncludeGuardedHeader extends HeaderFile { + +} + +/** + * A header file that uses a non-portable mechanism to prevent multiple + * inclusion. + */ +abstract class BadIncludeGuard extends IncludeGuardedHeader { + + /** Gets the element to blame for this bad include guard pattern. */ + abstract Element blame(); + +} + +/** + * A header file with the correct include guard: `#ifndef` (or equivalent), + * `#define`, and `#endif`. + */ +class CorrectIncludeGuard extends IncludeGuardedHeader { + + CorrectIncludeGuard() { correctIncludeGuard(this,_,_,_,_) } + + /** Gets the name of the preprocessor define used to prevent multiple inclusion of this file. */ + string getIncludeGuardName() { correctIncludeGuard(this, _, _, _, result) } + + /** Gets the preprocessor macro used to prevent multiple inclusion of this file. */ + Macro getDefine() { correctIncludeGuard(this, _, result, _, _) } + + /** Gets the `#ifndef` or `#if` directive used to prevent multiple inclusion of this file. */ + PreprocessorDirective getIfndef() { correctIncludeGuard(this, result, _, _, _) } + + /** Gets the `#endif` directive closing this file. */ + PreprocessorEndif getEndif() { correctIncludeGuard(this, _, _, result, _) } + +} + +/** + * DEPRECATED: no longer useful. + */ +deprecated +class NotIncludedGuard extends IncludeGuardedHeader { + NotIncludedGuard() { + none() + } + + /** Gets the `#ifndef` directive used to prevent multiple inclusion of this file. */ + PreprocessorIfndef getIfndef() { result.getFile() = this } + + /** Gets the `#endif` directive closing this file. */ + PreprocessorEndif getEndif() { result.getFile() = this } +} + +/** + * A file with no code in it. + */ +class EmptyFile extends IncludeGuardedHeader { + EmptyFile() { + this.(MetricFile).getNumberOfLinesOfCode() = 0 + } +} + +private predicate hasMacro(HeaderFile hf, string name, Macro define) { + define.getFile() = hf and define.getName() = name +} + +/** + * Holds if `hf` begins with an `#ifndef` or `#if` directive `ifndef`, to test + * the macro named `includeGuard`, and ends with the matching `endif`. + */ +predicate hasIncludeGuard(HeaderFile hf, PreprocessorDirective ifndef, PreprocessorEndif endif, string includeGuard) { + startsWithIfndef(hf, ifndef, includeGuard) and + endsWithEndif(hf, endif) and + endif.getIf() = ifndef +} + +/** + * Holds if `hf` uses a valid include guard with the macro named `includeGuard` + * and the preprocessor directives `ifndef`, `define`, and `endif`. This + * analysis is also exposed in an object-oriented style through the class + * `CorrectIncludeGuard`. + */ +pragma[noopt] predicate correctIncludeGuard(HeaderFile hf, PreprocessorDirective ifndef, Macro define, PreprocessorEndif endif, string includeGuard) { + hasIncludeGuard(hf, ifndef, endif, includeGuard) and + hasMacro(hf, includeGuard, define) and + // we already know the ifndef is first and the endif last, so we just need + // to check there is nothing before the define that isn't the ifndef. + not exists(int relevant, + Location ifndefLocation, int ifndefLine, + Location defineLocation, int defineLine | + includeGuardRelevantLine(hf, relevant) and + ifndefLocation = ifndef.getLocation() and + ifndefLine = ifndefLocation.getStartLine() and + relevant != ifndefLine and + defineLocation = define.getLocation() and + defineLine = defineLocation.getStartLine() and + relevant < defineLine + ) +} + +/** + * Holds if `hf` begins with an `#ifndef` or `#if` directive `ifndef`, to test + * the macro named `macroName`. + */ +predicate startsWithIfndef(HeaderFile hf, PreprocessorDirective ifndef, string macroName) { + ifndefDirective(ifndef, macroName) and + ifndef.getFile() = hf and + ifndef.getLocation().getStartLine() = min(int l | includeGuardRelevantLine(hf, l)) +} + +private predicate endifLocation(PreprocessorEndif endif, File f, int line) { + endif.getFile() = f and + endif.getLocation().getStartLine() = line +} + +private predicate lastEndifLocation(PreprocessorEndif endif, File f, int line) { + endifLocation(endif, f, line) and + line = max(int line2 | endifLocation(_, f, line2)) +} + +/** + * Holds if `hf` ends with `endif`. + */ +predicate endsWithEndif(HeaderFile hf, PreprocessorEndif endif) { + exists(int line | lastEndifLocation(endif, hf, line) | + line = max(int l | includeGuardRelevantLine(hf, l) | l) + ) +} + +private predicate includeGuardRelevantLine(HeaderFile hf, int line) { + exists(Location l | l.getFile() = hf and line = l.getStartLine() | + // any declaration + exists(Declaration d | l = d.getADeclarationLocation()) or + + // most preprocessor directives + exists(PreprocessorDirective p | + l = p.getLocation() and + + // included files may be outside the include guards, as they + // should contain an include guarding mechanism of their own. + not p instanceof Include + ) + ) +} + +/** + * Holds if `ppd` is effectively an `#ifndef` directive that tests `macro`. + * This includes `#if !defined(macro)`. + */ +predicate ifndefDirective(PreprocessorDirective ppd, string macro) { + (ppd instanceof PreprocessorIfndef and macro = ppd.getHead()) + or + (ppd instanceof PreprocessorIf and + exists(string head | head = ppd.getHead() | + macro = head.replaceAll("(", " ").replaceAll(")", "").replaceAll("\t", " ").regexpCapture("[ ]*![ ]*defined[ ]+([^ ]*)[ ]*", 1).trim() + )) +} + +/** + * A header file with the `#pragma once` include guard. + */ +class PragmaOnceIncludeGuard extends BadIncludeGuard { + PragmaOnceIncludeGuard() { exists(PreprocessorPragma p | p.getFile() = this and p.getHead() = "once") } + + override Element blame() { exists(PreprocessorPragma p | p.getFile() = this and p = result and p.getHead() = "once") } +} diff --git a/cpp/ql/src/semmle/code/cpp/internal/Type.qll b/cpp/ql/src/semmle/code/cpp/internal/Type.qll new file mode 100644 index 000000000000..35d6cec58e51 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/internal/Type.qll @@ -0,0 +1,64 @@ +import semmle.code.cpp.Type + +/** Holds if `d` is a complete class named `name`. */ +pragma[noinline] +private predicate existsCompleteWithName(string name, @usertype d) { + isClass(d) and + is_complete(d) and + usertypes(d, name, _) +} + +/** Holds if `c` is an incomplete class named `name`. */ +pragma[noinline] +private predicate existsIncompleteWithName(string name, @usertype c) { + isClass(c) and + not is_complete(c) and + usertypes(c, name, _) +} + +/** + * Holds if `c` is an imcomplete class, and there exists a complete class `d` + * with the same name. + */ +private predicate hasCompleteTwin(@usertype c, @usertype d) { + exists(string name | + existsIncompleteWithName(name, c) and + existsCompleteWithName(name, d) + ) +} + +import Cached +cached private module Cached { + /** + * If `c` is incomplete, and there exists a complete class with the same name, + * then the result is that complete class. Otherwise, the result is `c`. If + * multiple complete classes have the same name, this predicate may have + * multiple results. + */ + cached @usertype resolve(@usertype c) { + hasCompleteTwin(c, result) + or + (not hasCompleteTwin(c, _) and result = c) + } + + /** + * Gets a type from the database for which `t` is a complete definition. + */ + cached @type unresolve(Type t) { + if isClass(t) + then resolve(result) = t + else result = t + } + + /** + * Holds if `t` is a struct, class, union, or template. + */ + cached predicate isClass(@usertype t) { + (usertypes(t,_,1) or usertypes(t,_,2) or usertypes(t,_,3) or usertypes(t,_,6) + or usertypes(t,_,10) or usertypes(t,_,11) or usertypes(t,_,12)) + } + + cached predicate isElement(@element e) { + isClass(e) implies e = resolve(_) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/EdgeKind.qll b/cpp/ql/src/semmle/code/cpp/ir/EdgeKind.qll new file mode 100644 index 000000000000..172a879c6642 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/EdgeKind.qll @@ -0,0 +1,141 @@ +import cpp + +private newtype TEdgeKind = + TGotoEdge() or // Single successor (including fall-through) + TTrueEdge() or // 'true' edge of conditional branch + TFalseEdge() or // 'false' edge of conditional branch + TExceptionEdge() or // Thrown exception + TDefaultEdge() or // 'default' label of switch + TCaseEdge(string minValue, string maxValue) { // Case label of switch + exists(SwitchCase switchCase | + hasCaseEdge(switchCase, minValue, maxValue) + ) + } + +/** + * Represents the kind of an edge in the IR control flow graph. Each + * `Instruction` or `IRBlock` has at most one successor of any single + * `EdgeKind`. + */ +abstract class EdgeKind extends TEdgeKind { + abstract string toString(); +} + +/** + * A "goto" edge, representing the unconditional successor of an `Instruction` + * or `IRBlock`. + */ +class GotoEdge extends EdgeKind, TGotoEdge { + override final string toString() { + result = "Goto" + } +} + +GotoEdge gotoEdge() { + result = TGotoEdge() +} + +/** + * A "true" edge, representing the successor of a conditional branch when the + * condition is non-zero. + */ +class TrueEdge extends EdgeKind, TTrueEdge { + override final string toString() { + result = "True" + } +} + +TrueEdge trueEdge() { + result = TTrueEdge() +} + +/** + * A "false" edge, representing the successor of a conditional branch when the + * condition is zero. + */ +class FalseEdge extends EdgeKind, TFalseEdge { + override final string toString() { + result = "False" + } +} + +FalseEdge falseEdge() { + result = TFalseEdge() +} + +/** + * An "exception" edge, representing the successor of an instruction when that + * instruction's evaluation throws an exception. + */ +class ExceptionEdge extends EdgeKind, TExceptionEdge { + override final string toString() { + result = "Exception" + } +} + +ExceptionEdge exceptionEdge() { + result = TExceptionEdge() +} + +/** + * A "default" edge, representing the successor of a `Switch` instruction when + * none of the case values matches the condition value. + */ +class DefaultEdge extends EdgeKind, TDefaultEdge { + override final string toString() { + result = "Default" + } +} + +DefaultEdge defaultEdge() { + result = TDefaultEdge() +} + +/** + * A "case" edge, representing the successor of a `Switch` instruction when the + * the condition value matches a correponding `case` label. + */ +class CaseEdge extends EdgeKind, TCaseEdge { + string minValue; + string maxValue; + + CaseEdge() { + this = TCaseEdge(minValue, maxValue) + } + + override final string toString() { + if minValue = maxValue then + result = "Case[" + minValue + "]" + else + result = "Case[" + minValue + ".." + maxValue + "]" + } + + string getMinValue() { + result = minValue + } + + string getMaxValue() { + result = maxValue + } +} + +CaseEdge caseEdge(string minValue, string maxValue) { + result = TCaseEdge(minValue, maxValue) +} + +private predicate hasCaseEdge(SwitchCase switchCase, string minValue, + string maxValue) { + minValue = switchCase.getExpr().getFullyConverted().getValue() and + if exists(switchCase.getEndExpr()) then + maxValue = switchCase.getEndExpr().getFullyConverted().getValue() + else + maxValue = minValue +} + +EdgeKind getCaseEdge(SwitchCase switchCase) { + exists(CaseEdge edge | + result = edge and + hasCaseEdge(switchCase, edge.getMinValue(), edge.getMaxValue()) + ) or + (switchCase instanceof DefaultCase and result instanceof DefaultEdge) +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/IR.qll b/cpp/ql/src/semmle/code/cpp/ir/IR.qll new file mode 100644 index 000000000000..07c19ae8fbad --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/IR.qll @@ -0,0 +1 @@ +import internal.IRImpl diff --git a/cpp/ql/src/semmle/code/cpp/ir/IRSanity.ql b/cpp/ql/src/semmle/code/cpp/ir/IRSanity.ql new file mode 100644 index 000000000000..151470849fdb --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/IRSanity.ql @@ -0,0 +1,8 @@ +/** + * @name IR Sanity Check + * @description Performs sanity checks on the Intermediate Representation. This query should have no results. + * @kind problem + * @id cpp/ir-sanity-check + */ + +import internal.IRSanityImpl diff --git a/cpp/ql/src/semmle/code/cpp/ir/MemoryAccessKind.qll b/cpp/ql/src/semmle/code/cpp/ir/MemoryAccessKind.qll new file mode 100644 index 000000000000..a71da214a7c7 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/MemoryAccessKind.qll @@ -0,0 +1,54 @@ +import cpp + +newtype TMemoryAccessKind = + TIndirectMemoryAccess() or + TEscapedMemoryAccess() or + TPhiMemoryAccess() or + TUnmodeledMemoryAccess() + +/** + * Describes the set of memory locations memory accessed by a memory operand or + * memory result. + */ +class MemoryAccessKind extends TMemoryAccessKind { + abstract string toString(); +} + +/** + * The operand or result accesses memory at the address specified by the + * `LoadStoreAddressOperand` on the same instruction. + */ +class IndirectMemoryAccess extends MemoryAccessKind, TIndirectMemoryAccess { + override string toString() { + result = "indirect" + } +} + +/** + * The operand or result accesses all memory whose address has escaped. + */ +class EscapedMemoryAccess extends MemoryAccessKind, TEscapedMemoryAccess { + override string toString() { + result = "escaped" + } +} + +/** + * The operand is a Phi operand, which accesses the same memory as its + * definition. + */ +class PhiMemoryAccess extends MemoryAccessKind, TPhiMemoryAccess { + override string toString() { + result = "phi" + } +} + +/** + * The operand accesses memory not modeled in SSA. Used only on the result of + * `UnmodeledDefinition` and on the operands of `UnmodeledUse`. + */ +class UnmodeledMemoryAccess extends MemoryAccessKind, TUnmodeledMemoryAccess { + override string toString() { + result = "unmodeled" + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/Opcode.qll b/cpp/ql/src/semmle/code/cpp/ir/Opcode.qll new file mode 100644 index 000000000000..4104c6a104ec --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/Opcode.qll @@ -0,0 +1 @@ +import internal.Opcode diff --git a/cpp/ql/src/semmle/code/cpp/ir/PrintIR.qll b/cpp/ql/src/semmle/code/cpp/ir/PrintIR.qll new file mode 100644 index 000000000000..2a8ffdf61693 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/PrintIR.qll @@ -0,0 +1 @@ +import internal.PrintIRImpl diff --git a/cpp/ql/src/semmle/code/cpp/ir/TempVariableTag.qll b/cpp/ql/src/semmle/code/cpp/ir/TempVariableTag.qll new file mode 100644 index 000000000000..b19d20503c85 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/TempVariableTag.qll @@ -0,0 +1,8 @@ +import cpp +private import internal.TempVariableTag + +class TempVariableTag extends TTempVariableTag { + string toString() { + result = "Tag" + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/ValueCategory.qll b/cpp/ql/src/semmle/code/cpp/ir/ValueCategory.qll new file mode 100644 index 000000000000..02f3883a8105 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/ValueCategory.qll @@ -0,0 +1,44 @@ +import cpp + +private newtype TValueCategory = + TLValue() or + TXValue() or + TPRValue() + +abstract class ValueCategory extends TValueCategory { + abstract string toString(); +} + +abstract class GLValue extends ValueCategory { +} + +abstract class RValue extends ValueCategory { +} + +class LValue extends GLValue, TLValue { + override string toString() { + result = "lvalue" + } +} + +class XValue extends GLValue, RValue, TXValue { + override string toString() { + result = "xvalue" + } +} + +class PRValue extends RValue, TPRValue { + override string toString() { + result = "prvalue" + } +} + +ValueCategory getExprValueCategory(Expr expr) { + ( + expr.isLValueCategory() and result instanceof LValue + ) or ( + expr.isXValueCategory() and result instanceof XValue + ) or ( + expr.isPRValueCategory() and result instanceof PRValue + ) +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/FunctionIR.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/FunctionIR.qll new file mode 100644 index 000000000000..ba8f524f202f --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/internal/FunctionIR.qll @@ -0,0 +1,91 @@ +private import IRInternal +import Instruction +import cpp + +private newtype TFunctionIR = + MkFunctionIR(Function func) { + Construction::functionHasIR(func) + } + +/** + * Represents the IR for a function. + */ +class FunctionIR extends TFunctionIR { + Function func; + + FunctionIR() { + this = MkFunctionIR(func) + } + + final string toString() { + result = "IR: " + func.toString() + } + + /** + * Gets the function whose IR is represented. + */ + final Function getFunction() { + result = func + } + + /** + * Gets the location of the function. + */ + final Location getLocation() { + result = func.getLocation() + } + + /** + * Gets the entry point for this function. + */ + final EnterFunctionInstruction getEnterFunctionInstruction() { + result.getFunctionIR() = this + } + + /** + * Gets the exit point for this function. + */ + final ExitFunctionInstruction getExitFunctionInstruction() { + result.getFunctionIR() = this + } + + final UnmodeledDefinitionInstruction getUnmodeledDefinitionInstruction() { + result.getFunctionIR() = this + } + + /** + * Gets the single return instruction for this function. + */ + final ReturnInstruction getReturnInstruction() { + result.getFunctionIR() = 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. + */ + final IRReturnVariable getReturnVariable() { + result.getFunctionIR() = this + } + + /** + * Gets the block containing the entry point of this function. + */ + final IRBlock getEntryBlock() { + result.getFirstInstruction() = getEnterFunctionInstruction() + } + + /** + * Gets all instructions in this function. + */ + final Instruction getAnInstruction() { + result.getFunctionIR() = this + } + + /** + * Gets all blocks in this function. + */ + final IRBlock getABlock() { + result.getFunctionIR() = this + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/IRBlock.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/IRBlock.qll new file mode 100644 index 000000000000..98ec650036db --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/internal/IRBlock.qll @@ -0,0 +1,131 @@ +private import IRInternal +import Instruction +import cpp +import semmle.code.cpp.ir.EdgeKind + +private predicate startsBasicBlock(Instruction instr) { + not instr instanceof PhiInstruction and + ( + count(Instruction predecessor | + instr = predecessor.getASuccessor() + ) != 1 or // Multiple predecessors or no predecessor + exists(Instruction predecessor | + instr = predecessor.getASuccessor() and + strictcount(Instruction other | + other = predecessor.getASuccessor() + ) > 1 + ) or // Predecessor has multiple successors + exists(Instruction predecessor, EdgeKind kind | + instr = predecessor.getSuccessor(kind) and + not kind instanceof GotoEdge + ) // Incoming edge is not a GotoEdge + ) +} + +private newtype TIRBlock = + MkIRBlock(Instruction firstInstr) { + startsBasicBlock(firstInstr) + } + +cached private predicate isEntryBlock(IRBlock block) { + block.getFirstInstruction() instanceof EnterFunctionInstruction +} + +cached private predicate blockSuccessor(IRBlock pred, IRBlock succ) { + succ = pred.getASuccessor() +} + +private predicate blockImmediatelyDominates(IRBlock dominator, IRBlock block) = + idominance(isEntryBlock/1, blockSuccessor/2)(_, dominator, block) + +class IRBlock extends TIRBlock { + Instruction firstInstr; + + IRBlock() { + this = MkIRBlock(firstInstr) + } + + final string toString() { + result = firstInstr.toString() + } + + final Location getLocation() { + result = getFirstInstruction().getLocation() + } + + final string getUniqueId() { + result = firstInstr.getUniqueId() + } + + final cached Instruction getInstruction(int index) { + index = 0 and result = firstInstr or + ( + index > 0 and + not startsBasicBlock(result) and + exists(Instruction predecessor, GotoEdge edge | + predecessor = getInstruction(index - 1) and + result = predecessor.getSuccessor(edge) + ) + ) + } + + final PhiInstruction getAPhiInstruction() { + Construction::getPhiInstructionBlockStart(result) = + getFirstInstruction() + } + + final Instruction getAnInstruction() { + result = getInstruction(_) or + result = getAPhiInstruction() + } + + final Instruction getFirstInstruction() { + result = firstInstr + } + + final Instruction getLastInstruction() { + result = getInstruction(getInstructionCount() - 1) + } + + final int getInstructionCount() { + result = strictcount(getInstruction(_)) + } + + final FunctionIR getFunctionIR() { + result = firstInstr.getFunctionIR() + } + + final Function getFunction() { + result = firstInstr.getFunction() + } + + final IRBlock getASuccessor() { + result.getFirstInstruction() = getLastInstruction().getASuccessor() + } + + final IRBlock getAPredecessor() { + firstInstr = result.getLastInstruction().getASuccessor() + } + + final IRBlock getSuccessor(EdgeKind kind) { + result.getFirstInstruction() = getLastInstruction().getSuccessor(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) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/IRConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/IRConstruction.qll new file mode 100644 index 000000000000..9f0b7e5b1015 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/internal/IRConstruction.qll @@ -0,0 +1,166 @@ +import cpp +import semmle.code.cpp.ir.IR +private import InstructionTag +private import Opcode +private import TempVariableTag +private import TranslatedElement +private import TranslatedFunction + +class InstructionTagType extends TInstructionTag { + final string toString() { + result = "Tag" + } +} + +private TranslatedElement getInstructionTranslatedElement( + Instruction instruction) { + result = getInstructionTranslatedElementAndTag(instruction, _) +} + +private TranslatedElement getInstructionTranslatedElementAndTag( + Instruction instruction, InstructionTag tag) { + result.getAST() = instruction.getAST() and + tag = instruction.getTag() and + result.hasInstruction(_, tag, _, _) +} + +private TranslatedElement getTempVariableTranslatedElement( + IRTempVariable var) { + result.getAST() = var.getAST() and + result.hasTempVariable(var.getTag(), _) +} + +import Cached +cached private module Cached { + cached predicate functionHasIR(Function func) { + exists(getTranslatedFunction(func)) + } + + cached int getMaxCallArgIndex() { + result = max(int argIndex | + exists(FunctionCall call | + exists(call.getArgument(argIndex)) + ) + ) + } + + cached newtype TInstruction = + MkInstruction(FunctionIR funcIR, Opcode opcode, Locatable ast, + InstructionTag tag, Type resultType, boolean isGLValue) { + hasInstruction(funcIR.getFunction(), opcode, ast, tag, + resultType, isGLValue) + } + + private predicate hasInstruction(Function func, Opcode opcode, Locatable ast, + InstructionTag tag, Type resultType, boolean isGLValue) { + exists(TranslatedElement element | + element.getAST() = ast and + func = element.getFunction() and + element.hasInstruction(opcode, tag, resultType, isGLValue) + ) + } + + cached predicate hasTempVariable(Function func, Locatable ast, TempVariableTag tag, + Type type) { + exists(TranslatedElement element | + element.getAST() = ast and + func = element.getFunction() and + element.hasTempVariable(tag, type) + ) + } + + cached predicate hasModeledMemoryResult(Instruction instruction) { + none() + } + + cached Instruction getInstructionOperand(Instruction instruction, OperandTag tag) { + result = getInstructionTranslatedElement(instruction).getInstructionOperand( + instruction.getTag(), tag) + } + + cached Instruction getPhiInstructionBlockStart(PhiInstruction instr) { + none() + } + + cached Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) { + result = getInstructionTranslatedElement(instruction).getInstructionSuccessor( + instruction.getTag(), kind) + } + + cached IRVariable getInstructionVariable(Instruction instruction) { + result = getInstructionTranslatedElement(instruction).getInstructionVariable( + instruction.getTag()) + } + + cached Field getInstructionField(Instruction instruction) { + exists(TranslatedElement element, InstructionTag tag | + instructionOrigin(instruction, element, tag) and + result = element.getInstructionField(tag) + ) + } + + cached Function getInstructionFunction(Instruction instruction) { + exists(InstructionTag tag | + result = getInstructionTranslatedElementAndTag(instruction, tag) + .getInstructionFunction(tag) + ) + } + + cached string getInstructionConstantValue(Instruction instruction) { + result = + getInstructionTranslatedElement(instruction).getInstructionConstantValue( + instruction.getTag()) + } + + cached StringLiteral getInstructionStringLiteral(Instruction instruction) { + result = + getInstructionTranslatedElement(instruction).getInstructionStringLiteral( + instruction.getTag()) + } + + cached Type getInstructionExceptionType(Instruction instruction) { + result = + getInstructionTranslatedElement(instruction).getInstructionExceptionType( + instruction.getTag()) + } + + cached predicate getInstructionInheritance(Instruction instruction, + Class baseClass, Class derivedClass) { + getInstructionTranslatedElement(instruction).getInstructionInheritance( + instruction.getTag(), baseClass, derivedClass) + } + + pragma[noinline] + private predicate instructionOrigin(Instruction instruction, + TranslatedElement element, InstructionTag tag) { + element = getInstructionTranslatedElement(instruction) and + tag = instruction.getTag() + } + + 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 | + instructionOrigin(instruction, element, tag) and + result = element.getInstructionResultSize(tag) + ) + } +} + +import CachedForDebugging +cached private module CachedForDebugging { + cached string getTempVariableUniqueId(IRTempVariable var) { + result = getTempVariableTranslatedElement(var).getId() + ":" + + getTempVariableTagId(var.getTag()) + } + + cached string getInstructionUniqueId(Instruction instruction) { + result = getInstructionTranslatedElement(instruction).getId() + ":" + + getInstructionTagId(instruction.getTag()) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/IRImpl.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/IRImpl.qll new file mode 100644 index 000000000000..97c027cc1280 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/internal/IRImpl.qll @@ -0,0 +1,7 @@ +import FunctionIR +import Instruction +import IRBlock +import IRVariable +import OperandTag +import semmle.code.cpp.ir.EdgeKind +import semmle.code.cpp.ir.MemoryAccessKind diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/IRInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/IRInternal.qll new file mode 100644 index 000000000000..79a47b0f8079 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/internal/IRInternal.qll @@ -0,0 +1 @@ +import IRConstruction as Construction diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/IRSanityImpl.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/IRSanityImpl.qll new file mode 100644 index 000000000000..744b28704604 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/internal/IRSanityImpl.qll @@ -0,0 +1,3 @@ +private import IRImpl +import InstructionSanity + diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/IRVariable.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/IRVariable.qll new file mode 100644 index 000000000000..a903e227b26c --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/internal/IRVariable.qll @@ -0,0 +1,202 @@ +private import IRInternal +import FunctionIR +import cpp +import semmle.code.cpp.ir.TempVariableTag +private import semmle.code.cpp.ir.internal.TempVariableTag + +private newtype TIRVariable = + TIRAutomaticUserVariable(LocalScopeVariable var, FunctionIR funcIR) { + exists(Function func | + func = funcIR.getFunction() and + ( + var.getFunction() = func or + var.(Parameter).getCatchBlock().getEnclosingFunction() = func + ) + ) + } or + TIRStaticUserVariable(Variable var, FunctionIR funcIR) { + ( + var instanceof GlobalOrNamespaceVariable or + var instanceof MemberVariable and not var instanceof Field + ) and + exists(VariableAccess access | + access.getTarget() = var and + access.getEnclosingFunction() = funcIR.getFunction() + ) + } or + TIRTempVariable(FunctionIR funcIR, Locatable ast, TempVariableTag tag, + Type type) { + Construction::hasTempVariable(funcIR.getFunction(), ast, tag, type) + } + +IRUserVariable getIRUserVariable(Function func, Variable var) { + result.getVariable() = var and + result.getFunction() = func +} + +/** + * Represents 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`). + */ +abstract class IRVariable extends TIRVariable { + FunctionIR funcIR; + + abstract string toString(); + + /** + * Gets the type of the variable. + */ + abstract Type getType(); + + /** + * Gets the AST node that declared this variable, or that introduced this + * variable as part of the AST-to-IR translation. + */ + abstract Locatable getAST(); + + /** + * Gets an identifier string for the variable. This identifier is unique + * within the function. + */ + abstract string getUniqueId(); + + /** + * Gets the source location of this variable. + */ + final Location getLocation() { + result = getAST().getLocation() + } + + /** + * Gets the IR for the function that references this variable. + */ + final FunctionIR getFunctionIR() { + result = funcIR + } + + /** + * Gets the function that references this variable. + */ + final Function getFunction() { + result = funcIR.getFunction() + } +} + +/** + * Represents a user-declared variable referenced by the IR for a function. + */ +abstract class IRUserVariable extends IRVariable { + Variable var; + + override final string toString() { + result = var.toString() + } + + override final Type getType() { + result = var.getType().getUnspecifiedType() + } + + override final Locatable getAST() { + result = var + } + + override final string getUniqueId() { + result = var.toString() + " " + var.getLocation().toString() + } + + /** + * Gets the original user-declared variable. + */ + final Variable getVariable() { + result = var + } +} + +/** + * Represents a variable (user-declared or temporary) that is allocated on the + * stack. This includes all parameters, non-static local variables, and + * temporary variables. + */ +abstract class IRAutomaticVariable extends IRVariable { +} + +class IRAutomaticUserVariable extends IRUserVariable, IRAutomaticVariable, + TIRAutomaticUserVariable { + LocalScopeVariable localVar; + + IRAutomaticUserVariable() { + this = TIRAutomaticUserVariable(localVar, funcIR) and + var = localVar + } + + final LocalScopeVariable getLocalVariable() { + result = localVar + } +} + +class IRStaticUserVariable extends IRUserVariable, TIRStaticUserVariable { + IRStaticUserVariable() { + this = TIRStaticUserVariable(var, funcIR) + } +} + +IRTempVariable getIRTempVariable(Locatable ast, TempVariableTag tag) { + result.getAST() = ast and + result.getTag() = tag +} + +class IRTempVariable extends IRVariable, IRAutomaticVariable, TIRTempVariable { + Locatable ast; + TempVariableTag tag; + Type type; + + IRTempVariable() { + this = TIRTempVariable(funcIR, ast, tag, type) + } + + override final Type getType() { + result = type + } + + override final Locatable getAST() { + result = ast + } + + override final string getUniqueId() { + result = "Temp: " + Construction::getTempVariableUniqueId(this) + } + + final TempVariableTag getTag() { + result = tag + } + + override string toString() { + result = getBaseString() + ast.getLocation().getStartLine().toString() + ":" + + ast.getLocation().getStartColumn().toString() + } + + string getBaseString() { + result = "#temp" + } +} + +class IRReturnVariable extends IRTempVariable { + IRReturnVariable() { + tag = ReturnValueTempVar() + } + + override final string toString() { + result = "#return" + } +} + +class IRThrowVariable extends IRTempVariable { + IRThrowVariable() { + tag = ThrowTempVar() + } + + override string getBaseString() { + result = "#throw" + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/Instruction.qll new file mode 100644 index 000000000000..d00999f894aa --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/internal/Instruction.qll @@ -0,0 +1,978 @@ +private import IRInternal +import FunctionIR +import IRBlock +import IRVariable +import OperandTag +import cpp +import semmle.code.cpp.ir.EdgeKind +import semmle.code.cpp.ir.MemoryAccessKind +import semmle.code.cpp.ir.Opcode +private import semmle.code.cpp.ir.internal.Opcode + +class InstructionTag = Construction::InstructionTagType; + +module InstructionSanity { + /** + * Holds if the instruction `instr` should be expected to have an operand + * with operand tag `tag`. Only holds for singleton operand tags. Tags with + * parameters, such as `PhiOperand` and `PositionalArgumentOperand` are handled + * separately in `unexpectedOperand`. + */ + private predicate expectsOperand(Instruction instr, OperandTag tag) { + exists(Opcode opcode | + opcode = instr.getOpcode() and + ( + opcode instanceof UnaryOpcode and tag instanceof UnaryOperand or + ( + opcode instanceof BinaryOpcode and + ( + tag instanceof LeftOperand or + tag instanceof RightOperand + ) + ) or + opcode instanceof CopyOpcode and tag instanceof CopySourceOperand or + opcode instanceof MemoryAccessOpcode and tag instanceof LoadStoreAddressOperand or + opcode instanceof OpcodeWithCondition and tag instanceof ConditionOperand or + opcode instanceof Opcode::ReturnValue and tag instanceof ReturnValueOperand or + opcode instanceof Opcode::ThrowValue and tag instanceof ExceptionOperand or + opcode instanceof Opcode::UnmodeledUse and tag instanceof UnmodeledUseOperand or + opcode instanceof Opcode::Invoke and tag instanceof CallTargetOperand + ) + ) + } + + /** + * Holds if instruction `instr` is missing an expected operand with tag `tag`. + */ + query predicate missingOperand(Instruction instr, OperandTag tag) { + expectsOperand(instr, tag) and not exists(instr.getOperand(tag)) + } + + /** + * Holds if instruction `instr` has an unexpected operand with tag `tag`. + */ + query predicate unexpectedOperand(Instruction instr, OperandTag tag) { + exists(instr.getOperand(tag)) and + not expectsOperand(instr, tag) and + not (instr instanceof InvokeInstruction and tag instanceof ArgumentOperand) and + not (instr instanceof BuiltInInstruction and tag instanceof PositionalArgumentOperand) and + not (instr instanceof PhiInstruction and tag instanceof PhiOperand) + } + + /** + * Holds if instruction `instr` has multiple operands with tag `tag`. + */ + query predicate duplicateOperand(Instruction instr, OperandTag tag) { + count(instr.getOperand(tag)) > 1 and + not tag instanceof UnmodeledUseOperand + } +} + +/** + * Represents a single operation in the IR. + */ +class Instruction extends Construction::TInstruction { + Opcode opcode; + Locatable ast; + InstructionTag instructionTag; + Type resultType; + FunctionIR funcIR; + boolean glvalue; + + Instruction() { + this = Construction::MkInstruction(funcIR, opcode, ast, instructionTag, resultType, glvalue) + } + + string toString() { + result = opcode.toString() + } + + /** + * 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 Function getFunction() { + result = funcIR.getFunction() + } + + /** + * Gets the FunctionIR object that contains the IR for this instruction. + */ + final FunctionIR getFunctionIR() { + result = funcIR + } + + /** + * Gets the AST that caused this instruction to be generated. + */ + final Locatable getAST() { + result = ast + } + + /** + * Gets the location of the source code for this instruction. + */ + final Location getLocation() { + result = ast.getLocation() + } + + /** + * Gets the type of the result produced by this instruction. If the + * instruction does not produce a result, its result type will be `VoidType`. + */ + final Type getResultType() { + result = resultType + } + + /** + * 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() { + glvalue = true + } + + /** + * Gets the size of the result produced by this instruction, in bytes. If the + * instruction does not produce a result, or 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() { + if isGLValue() then ( + // a glvalue is always pointer-sized. + exists(NullPointerType nullptr | + result = nullptr.getSize() + ) + ) + else if resultType instanceof UnknownType then + result = Construction::getInstructionResultSize(this) + else ( + not resultType instanceof VoidType and + result = resultType.getSize() + ) + } + + /** + * Gets the opcode that specifies the operation performed by this instruction. + */ + final Opcode getOpcode() { + result = opcode + } + + final InstructionTag getTag() { + result = instructionTag + } + + /** + * Gets the instruction that produced the value of the specified source + * operand. + */ + final Instruction getOperand(OperandTag tag) { + result = Construction::getInstructionOperand(this, tag) + } + + /** + * Gets all instructions consumed by this instruction's operands. + */ + final Instruction getAnOperand() { + result = getOperand(_) + } + + /** + * Holds if this instruction has a memory operand with the specified tag. + */ + final predicate isMemoryOperand(OperandTag tag) { + exists(getOperandMemoryAccess(tag)) + } + + /** + * Gets the kind of memory access performed by the specified operand. Holds + * only for memory operands. + */ + MemoryAccessKind getOperandMemoryAccess(OperandTag tag) { + none() + } + + /** + * 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. + */ + MemoryAccessKind getResultMemoryAccess() { + none() + } + + /** + * 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 is connected to the + * `UnmodeledUse` instruction. + * + * 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 + // An unmodeled result will have a use on the `UnmodeledUse` instruction. + not exists(UnmodeledUseOperand useTag | + hasUse(_, useTag) + ) + } + + /** + * 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 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(_) + } + + /** + * Holds if the result of this instruction is consumed by `useInstruction` as + * an operand with tag `useTag`. + */ + final predicate hasUse(Instruction useInstruction, OperandTag useTag) { + useInstruction.getFunctionIR() = funcIR and + this = useInstruction.getOperand(useTag) + } +} + +class VariableInstruction extends Instruction { + IRVariable var; + + VariableInstruction() { + var = Construction::getInstructionVariable(this) + } + + override final string toString() { + result = super.toString() + "[" + var.toString() + "]" + } + + final IRVariable getVariable() { + result = var + } +} + +class FieldInstruction extends Instruction { + Field field; + + FieldInstruction() { + field = Construction::getInstructionField(this) + } + + override final string toString() { + result = super.toString() + "[" + field.toString() + "]" + } + + final Field getField() { + result = field + } +} + +class FunctionInstruction extends Instruction { + Function funcSymbol; + + FunctionInstruction() { + funcSymbol = Construction::getInstructionFunction(this) + } + + override final string toString() { + result = super.toString() + "[" + funcSymbol.toString() + "]" + } + + final Function getFunctionSymbol() { + result = funcSymbol + } +} + +class ConstantValueInstruction extends Instruction { + string value; + + ConstantValueInstruction() { + value = Construction::getInstructionConstantValue(this) + } + + override final string toString() { + result = super.toString() + "[" + value + "]" + } + + final string getValue() { + result = value + } +} + +class EnterFunctionInstruction extends Instruction { + EnterFunctionInstruction() { + opcode instanceof Opcode::EnterFunction + } +} + +class VariableAddressInstruction extends VariableInstruction { + VariableAddressInstruction() { + opcode instanceof Opcode::VariableAddress + } +} + +class InitializeParameterInstruction extends VariableInstruction { + InitializeParameterInstruction() { + opcode instanceof Opcode::InitializeParameter + } + + final Parameter getParameter() { + result = var.(IRUserVariable).getVariable() + } +} + +class FieldAddressInstruction extends FieldInstruction { + FieldAddressInstruction() { + opcode instanceof Opcode::FieldAddress + } + + final Instruction getObjectAddress() { + result = getOperand(unaryOperand()) + } +} + +class UninitializedInstruction extends Instruction { + UninitializedInstruction() { + opcode instanceof Opcode::Uninitialized + } +} + +class NoOpInstruction extends Instruction { + NoOpInstruction() { + opcode instanceof Opcode::NoOp + } +} + +class ReturnInstruction extends Instruction { + ReturnInstruction() { + opcode instanceof ReturnOpcode + } +} + +class ReturnVoidInstruction extends ReturnInstruction { + ReturnVoidInstruction() { + opcode instanceof Opcode::ReturnVoid + } +} + +class ReturnValueInstruction extends ReturnInstruction { + ReturnValueInstruction() { + opcode instanceof Opcode::ReturnValue + } + + final Instruction getReturnValue() { + result = getOperand(returnValueOperand()) + } + + override final MemoryAccessKind getOperandMemoryAccess(OperandTag tag) { + tag instanceof ReturnValueOperand and + result instanceof IndirectMemoryAccess + } +} + +class CopyInstruction extends Instruction { + CopyInstruction() { + opcode instanceof CopyOpcode + } + + final Instruction getSourceValue() { + result = getOperand(copySourceOperand()) + } +} + +class CopyValueInstruction extends CopyInstruction { + CopyValueInstruction() { + opcode instanceof Opcode::CopyValue + } +} + +class LoadInstruction extends CopyInstruction { + LoadInstruction() { + opcode instanceof Opcode::Load + } + + override final MemoryAccessKind getOperandMemoryAccess(OperandTag tag) { + tag instanceof CopySourceOperand and + result instanceof IndirectMemoryAccess + } + + final Instruction getSourceAddress() { + result = getOperand(loadStoreAddressOperand()) + } +} + +class StoreInstruction extends CopyInstruction { + StoreInstruction() { + opcode instanceof Opcode::Store + } + + override final MemoryAccessKind getResultMemoryAccess() { + result instanceof IndirectMemoryAccess + } + + final Instruction getDestinationAddress() { + result = getOperand(loadStoreAddressOperand()) + } +} + +class ConditionalBranchInstruction extends Instruction { + ConditionalBranchInstruction() { + opcode instanceof Opcode::ConditionalBranch + } + + final Instruction getCondition() { + result = getOperand(conditionOperand()) + } + + final Instruction getTrueSuccessor() { + result = getSuccessor(trueEdge()) + } + + final Instruction getFalseSuccessor() { + result = getSuccessor(falseEdge()) + } +} + +class ExitFunctionInstruction extends Instruction { + ExitFunctionInstruction() { + opcode instanceof Opcode::ExitFunction + } +} + +class ConstantInstruction extends ConstantValueInstruction { + ConstantInstruction() { + opcode instanceof Opcode::Constant + } +} + +class IntegerConstantInstruction extends ConstantInstruction { + IntegerConstantInstruction() { + resultType instanceof IntegralType + } +} + +class FloatConstantInstruction extends ConstantInstruction { + FloatConstantInstruction() { + resultType instanceof FloatingPointType + } +} + +class StringConstantInstruction extends Instruction { + StringLiteral value; + + StringConstantInstruction() { + value = Construction::getInstructionStringLiteral(this) + } + + override final string toString() { + result = super.toString() + "[" + + value.getValueText().replaceAll("\n", " ").replaceAll("\r", "").replaceAll("\t", " ") + + "]" + } + + final StringLiteral getValue() { + result = value + } +} + +class BinaryInstruction extends Instruction { + BinaryInstruction() { + opcode instanceof BinaryOpcode + } + + final Instruction getLeftOperand() { + result = getOperand(leftOperand()) + } + + final Instruction getRightOperand() { + result = getOperand(rightOperand()) + } +} + +class AddInstruction extends BinaryInstruction { + AddInstruction() { + opcode instanceof Opcode::Add + } +} + +class SubInstruction extends BinaryInstruction { + SubInstruction() { + opcode instanceof Opcode::Sub + } +} + +class MulInstruction extends BinaryInstruction { + MulInstruction() { + opcode instanceof Opcode::Mul + } +} + +class DivInstruction extends BinaryInstruction { + DivInstruction() { + opcode instanceof Opcode::Div + } +} + +class RemInstruction extends BinaryInstruction { + RemInstruction() { + opcode instanceof Opcode::Rem + } +} + +class BitAndInstruction extends BinaryInstruction { + BitAndInstruction() { + opcode instanceof Opcode::BitAnd + } +} + +class BitOrInstruction extends BinaryInstruction { + BitOrInstruction() { + opcode instanceof Opcode::BitOr + } +} + +class BitXorInstruction extends BinaryInstruction { + BitXorInstruction() { + opcode instanceof Opcode::BitXor + } +} + +class ShiftLeftInstruction extends BinaryInstruction { + ShiftLeftInstruction() { + opcode instanceof Opcode::ShiftLeft + } +} + +class ShiftRightInstruction extends BinaryInstruction { + ShiftRightInstruction() { + opcode instanceof Opcode::ShiftRight + } +} + +class PointerArithmeticInstruction extends BinaryInstruction { + int elementSize; + + PointerArithmeticInstruction() { + opcode instanceof PointerArithmeticOpcode and + elementSize = Construction::getInstructionElementSize(this) + } + + override final string toString() { + result = super.toString() + "[" + elementSize.toString() + "]" + } + + final int getElementSize() { + result = elementSize + } +} + +class PointerOffsetInstruction extends PointerArithmeticInstruction { + PointerOffsetInstruction() { + opcode instanceof PointerOffsetOpcode + } +} + +class PointerAddInstruction extends PointerOffsetInstruction { + PointerAddInstruction() { + opcode instanceof Opcode::PointerAdd + } +} + +class PointerSubInstruction extends PointerOffsetInstruction { + PointerSubInstruction() { + opcode instanceof Opcode::PointerSub + } +} + +class PointerDiffInstruction extends PointerArithmeticInstruction { + PointerDiffInstruction() { + opcode instanceof Opcode::PointerDiff + } +} + +class UnaryInstruction extends Instruction { + UnaryInstruction() { + opcode instanceof UnaryOpcode + } + + final Instruction getOperand() { + result = getOperand(unaryOperand()) + } +} + +class ConvertInstruction extends UnaryInstruction { + ConvertInstruction() { + opcode instanceof Opcode::Convert + } +} + +/** + * Represents an instruction that converts between two addresses + * related by inheritance. + */ +class InheritanceConversionInstruction extends UnaryInstruction { + Class baseClass; + Class derivedClass; + + InheritanceConversionInstruction() { + Construction::getInstructionInheritance(this, baseClass, derivedClass) + } + + override final string toString() { + result = super.toString() + "[" + 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 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 Class getBaseClass() { + result = baseClass + } + + /** + * Gets the derived class of the conversion. + */ + final Class getDerivedClass() { + result = derivedClass + } +} + +/** + * Represents an instruction that converts from the address of a derived class + * to the address of a direct non-virtual base class. + */ +class ConvertToBaseInstruction extends InheritanceConversionInstruction { + ConvertToBaseInstruction() { + opcode instanceof Opcode::ConvertToBase + } +} + +/** + * Represents an instruction that converts from the address of a derived class + * to the address of a virtual base class. + */ +class ConvertToVirtualBaseInstruction extends InheritanceConversionInstruction { + ConvertToVirtualBaseInstruction() { + opcode 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() { + opcode instanceof Opcode::ConvertToDerived + } +} + +class BitComplementInstruction extends UnaryInstruction { + BitComplementInstruction() { + opcode instanceof Opcode::BitComplement + } +} + +class LogicalNotInstruction extends UnaryInstruction { + LogicalNotInstruction() { + opcode instanceof Opcode::LogicalNot + } +} + +class CompareInstruction extends BinaryInstruction { + CompareInstruction() { + opcode instanceof CompareOpcode + } +} + +class CompareEQInstruction extends CompareInstruction { + CompareEQInstruction() { + opcode instanceof Opcode::CompareEQ + } +} + +class CompareNEInstruction extends CompareInstruction { + CompareNEInstruction() { + opcode instanceof Opcode::CompareNE + } +} + +class CompareLTInstruction extends CompareInstruction { + CompareLTInstruction() { + opcode instanceof Opcode::CompareLT + } +} + +class CompareGTInstruction extends CompareInstruction { + CompareGTInstruction() { + opcode instanceof Opcode::CompareGT + } +} + +class CompareLEInstruction extends CompareInstruction { + CompareLEInstruction() { + opcode instanceof Opcode::CompareLE + } +} + +class CompareGEInstruction extends CompareInstruction { + CompareGEInstruction() { + opcode instanceof Opcode::CompareGE + } +} + +class SwitchInstruction extends Instruction { + SwitchInstruction() { + opcode instanceof Opcode::Switch + } + + final Instruction getExpression() { + result = getOperand(conditionOperand()) + } + + final Instruction getACaseSuccessor() { + exists(CaseEdge edge | + result = getSuccessor(edge) + ) + } + + final Instruction getDefaultSuccessor() { + result = getSuccessor(defaultEdge()) + } +} + +class InvokeInstruction extends Instruction { + InvokeInstruction() { + opcode instanceof Opcode::Invoke + } + + final Instruction getCallTarget() { + result = getOperand(callTargetOperand()) + } +} + +/** + * An instruction that throws an exception. + */ +class ThrowInstruction extends Instruction { + ThrowInstruction() { + opcode instanceof ThrowOpcode + } +} + +/** + * An instruction that throws a new exception. + */ +class ThrowValueInstruction extends ThrowInstruction { + ThrowValueInstruction() { + opcode instanceof Opcode::ThrowValue + } + + override final MemoryAccessKind getOperandMemoryAccess(OperandTag tag) { + tag instanceof ExceptionOperand and + result instanceof IndirectMemoryAccess + } + + + /** + * Gets the address of the exception thrown by this instruction. + */ + final Instruction getExceptionAddress() { + result = getOperand(loadStoreAddressOperand()) + } + + /** + * Gets the exception thrown by this instruction. + */ + final Instruction getException() { + result = getOperand(exceptionOperand()) + } +} + +/** + * An instruction that re-throws the current exception. + */ +class ReThrowInstruction extends ThrowInstruction { + ReThrowInstruction() { + opcode instanceof Opcode::ReThrow + } +} + +/** + * An instruction that exits the current function by propagating an exception. + */ +class UnwindInstruction extends Instruction { + UnwindInstruction() { + opcode instanceof Opcode::Unwind + } +} + +/** + * An instruction that starts a `catch` handler. + */ +class CatchInstruction extends Instruction { + CatchInstruction() { + opcode instanceof CatchOpcode + } +} + +/** + * An instruction that catches an exception of a specific type. + */ +class CatchByTypeInstruction extends CatchInstruction { + Type exceptionType; + + CatchByTypeInstruction() { + opcode instanceof Opcode::CatchByType and + exceptionType = Construction::getInstructionExceptionType(this) + } + + final override string toString() { + result = super.toString() + "[" + exceptionType.toString() + "]" + } + + /** + * Gets the type of exception to be caught. + */ + final Type getExceptionType() { + result = exceptionType + } +} + +/** + * An instruction that catches any exception. + */ +class CatchAnyInstruction extends CatchInstruction { + CatchAnyInstruction() { + opcode instanceof Opcode::CatchAny + } +} + +class UnmodeledDefinitionInstruction extends Instruction { + UnmodeledDefinitionInstruction() { + opcode instanceof Opcode::UnmodeledDefinition + } + + override final MemoryAccessKind getResultMemoryAccess() { + result instanceof UnmodeledMemoryAccess + } +} + +class UnmodeledUseInstruction extends Instruction { + UnmodeledUseInstruction() { + opcode instanceof Opcode::UnmodeledUse + } + + override final MemoryAccessKind getOperandMemoryAccess(OperandTag tag) { + tag instanceof UnmodeledUseOperand and + result instanceof UnmodeledMemoryAccess + } +} + +class PhiInstruction extends Instruction { + PhiInstruction() { + opcode instanceof Opcode::Phi + } + + override final MemoryAccessKind getOperandMemoryAccess(OperandTag tag) { + tag instanceof PhiOperand and + result instanceof PhiMemoryAccess + } + + override final MemoryAccessKind getResultMemoryAccess() { + result instanceof PhiMemoryAccess + } +} + +/** + * An instruction representing a built-in operation. This is used to represent + * operations such as access to variable argument lists. + */ +class BuiltInInstruction extends Instruction { + BuiltInInstruction() { + opcode instanceof BuiltInOpcode + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/InstructionTag.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/InstructionTag.qll new file mode 100644 index 000000000000..bec350fd22eb --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/internal/InstructionTag.qll @@ -0,0 +1,146 @@ +import cpp + +private predicate fieldIsInitialized(Field field) { + exists(ClassAggregateLiteral initList | + initList.isInitialized(field) + ) or + exists(ConstructorFieldInit init | + field = init.getTarget() + ) +} + +private predicate elementIsInitialized(int elementIndex) { + exists(ArrayAggregateLiteral initList | + initList.isInitialized(elementIndex) + ) +} + +newtype TInstructionTag = + OnlyInstructionTag() or // Single instruction (not including implicit Load) + InitializerUninitializedTag() or // Source expression of initializer + ParameterInitializerTag() or + InitializeThisTag() or + InitializerVariableAddressTag() or + InitializerLoadStringTag() or + InitializerStoreTag() or + ZeroPadStringConstantTag() or + ZeroPadStringElementIndexTag() or + ZeroPadStringElementAddressTag() or + ZeroPadStringStoreTag() or + AssignOperationLoadTag() or + AssignOperationConvertLeftTag() or + AssignOperationOpTag() or + AssignOperationConvertResultTag() or + AssignmentStoreTag() or + CrementLoadTag() or + CrementConstantTag() or + CrementOpTag() or + CrementStoreTag() or + EnterFunctionTag() or + ReturnValueAddressTag() or + ReturnTag() or + ExitFunctionTag() or + UnmodeledDefinitionTag() or + UnmodeledUseTag() or + SwitchBranchTag() or + CallTargetTag() or + CallTag() or + ValueConditionConditionalBranchTag() or + ConditionValueTrueTempAddressTag() or + ConditionValueTrueConstantTag() or + ConditionValueTrueStoreTag() or + ConditionValueFalseTempAddressTag() or + ConditionValueFalseConstantTag() or + ConditionValueFalseStoreTag() or + ConditionValueResultTempAddressTag() or + ConditionValueResultLoadTag() or + BoolConversionConstantTag() or + BoolConversionCompareTag() or + LoadTag() or // Implicit load due to lvalue-to-rvalue conversion + CatchTag() or + ThrowTag() or + UnwindTag() or + InitializerFieldAddressTag(Field field) { + fieldIsInitialized(field) + } or + InitializerFieldDefaultValueTag(Field field) { + fieldIsInitialized(field) + } or + InitializerFieldDefaultValueStoreTag(Field field) { + fieldIsInitialized(field) + } or + InitializerElementIndexTag(int elementIndex) { + elementIsInitialized(elementIndex) + } or + InitializerElementAddressTag(int elementIndex) { + elementIsInitialized(elementIndex) + } or + InitializerElementDefaultValueTag(int elementIndex) { + elementIsInitialized(elementIndex) + } or + InitializerElementDefaultValueStoreTag(int elementIndex) { + elementIsInitialized(elementIndex) + } + +/** + * Gets a unique string for the instruction tag. Primarily used for generating + * instruction IDs to ensure stable IR dumps. + */ +string getInstructionTagId(TInstructionTag tag) { + tag = OnlyInstructionTag() and result = "Only" or // Single instruction (not including implicit Load) + tag = InitializerUninitializedTag() and result = "InitUninit" or // Source expression of initializer + tag = ParameterInitializerTag() and result = "ParamInit" or + tag = InitializerVariableAddressTag() and result = "InitVarAddr" or + tag = InitializerStoreTag() and result = "InitStore" or + tag = AssignOperationLoadTag() and result = "AssignOpLoad" or + tag = AssignOperationConvertLeftTag() and result = "AssignOpConvLeft" or + tag = AssignOperationOpTag() and result = "AssignOpOp" or + tag = AssignOperationConvertResultTag() and result = "AssignOpConvRes" or + tag = AssignmentStoreTag() and result = "AssigStore" or + tag = CrementLoadTag() and result = "CrementLoad" or + tag = CrementConstantTag() and result = "CrementConst" or + tag = CrementOpTag() and result = "CrementOp" or + tag = CrementStoreTag() and result = "CrementStore" or + tag = EnterFunctionTag() and result = "EnterFunc" or + tag = ReturnValueAddressTag() and result = "RetValAddr" or + tag = ReturnTag() and result = "Ret" or + tag = ExitFunctionTag() and result = "ExitFunc" or + tag = UnmodeledDefinitionTag() and result = "UnmodeledDef" or + tag = UnmodeledUseTag() and result = "UnmodeledUse" or + tag = SwitchBranchTag() and result = "SwitchBranch" or + tag = CallTargetTag() and result = "CallTarget" or + tag = CallTag() and result = "Call" or + tag = ValueConditionConditionalBranchTag() and result = "ValCondCondBranch" or + tag = ConditionValueTrueTempAddressTag() and result = "CondValTrueTempAddr" or + tag = ConditionValueTrueConstantTag() and result = "CondValTrueConst" or + tag = ConditionValueTrueStoreTag() and result = "CondValTrueStore" or + tag = ConditionValueFalseTempAddressTag() and result = "CondValFalseTempAddr" or + tag = ConditionValueFalseConstantTag() and result = "CondValFalseConst" or + tag = ConditionValueFalseStoreTag() and result = "CondValFalseStore" or + tag = ConditionValueResultTempAddressTag() and result = "CondValResTempAddr" or + tag = ConditionValueResultLoadTag() and result = "CondValResLoad" or + tag = BoolConversionConstantTag() and result = "BoolConvConst" or + tag = BoolConversionCompareTag() and result = "BoolConvComp" or + tag = LoadTag() and result = "Load" or // Implicit load due to lvalue-to-rvalue conversion + tag = CatchTag() and result = "Catch" or + tag = ThrowTag() and result = "Throw" or + tag = UnwindTag() and result = "Unwind" or + exists(Field field, Class cls, int index, string tagName | + field = cls.getCanonicalMember(index) and + ( + tag = InitializerFieldAddressTag(field) and tagName = "InitFieldAddr" or + tag = InitializerFieldDefaultValueTag(field) and tagName = "InitFieldDefVal" or + tag = InitializerFieldDefaultValueStoreTag(field) and tagName = "InitFieldDefValStore" + ) and + result = tagName + "(" + index + ")" + ) or + exists(int index, string tagName | + ( + tag = InitializerElementIndexTag(index) and tagName = "InitElemIndex" or + tag = InitializerElementAddressTag(index) and tagName = "InitElemAddr" or + tag = InitializerElementDefaultValueTag(index) and tagName = "InitElemDefVal" or + tag = InitializerElementDefaultValueStoreTag(index) and tagName = "InitElemDefValStore" + ) and + result = tagName + "(" + index + ")" + ) +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/Opcode.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/Opcode.qll new file mode 100644 index 000000000000..922c2a0d95cc --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/internal/Opcode.qll @@ -0,0 +1,154 @@ +private newtype TOpcode = + TNoOp() or + TUninitialized() or + TInitializeParameter() or + TInitializeThis() or + TEnterFunction() or + TExitFunction() or + TReturnValue() or + TReturnVoid() 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 + TConvertToBase() or + TConvertToVirtualBase() or + TConvertToDerived() or + TCheckedConvertOrNull() or + TCheckedConvertOrThrow() or + TDynamicCastToVoid() or + TVariableAddress() or + TFieldAddress() or + TFunctionAddress() or + TConstant() or + TStringConstant() or + TConditionalBranch() or + TSwitch() or + TInvoke() or + TCatchByType() or + TCatchAny() or + TThrowValue() or + TReThrow() or + TUnwind() or + TUnmodeledDefinition() or + TUnmodeledUse() or + TPhi() or + TVarArgsStart() or + TVarArgsEnd() or + TVarArg() or + TVarArgCopy() + +class Opcode extends TOpcode { + string toString() { + result = "UnknownOpcode" + } +} + +abstract class UnaryOpcode extends Opcode {} + +abstract class BinaryOpcode extends Opcode {} + +abstract class PointerArithmeticOpcode extends BinaryOpcode {} + +abstract class PointerOffsetOpcode extends PointerArithmeticOpcode {} + +abstract class CompareOpcode extends BinaryOpcode {} + +abstract class CopyOpcode extends Opcode {} + +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 {} + +abstract class BuiltInOpcode extends Opcode {} + +module Opcode { + class NoOp extends Opcode, TNoOp { override final string toString() { result = "NoOp" } } + class Uninitialized extends Opcode, TUninitialized { override final string toString() { result = "Uninitialized" } } + class InitializeParameter extends Opcode, TInitializeParameter { override final string toString() { result = "InitializeParameter" } } + class InitializeThis extends Opcode, TInitializeThis { override final string toString() { result = "InitializeThis" } } + class EnterFunction extends Opcode, TEnterFunction { override final string toString() { result = "EnterFunction" } } + class ExitFunction extends Opcode, TExitFunction { override final string toString() { result = "ExitFunction" } } + class ReturnValue extends ReturnOpcode, MemoryAccessOpcode, TReturnValue { override final string toString() { result = "ReturnValue" } } + class ReturnVoid extends ReturnOpcode, TReturnVoid { override final string toString() { result = "ReturnVoid" } } + class CopyValue extends CopyOpcode, TCopyValue { override final string toString() { result = "CopyValue" } } + class Load extends CopyOpcode, MemoryAccessOpcode, TLoad { override final string toString() { result = "Load" } } + class Store extends CopyOpcode, MemoryAccessOpcode, TStore { override final string toString() { result = "Store" } } + class Add extends BinaryOpcode, TAdd { override final string toString() { result = "Add" } } + class Sub extends BinaryOpcode, TSub { override final string toString() { result = "Sub" } } + class Mul extends BinaryOpcode, TMul { override final string toString() { result = "Mul" } } + class Div extends BinaryOpcode, TDiv { override final string toString() { result = "Div" } } + class Rem extends BinaryOpcode, TRem { override final string toString() { result = "Rem" } } + class Negate extends UnaryOpcode, TNegate { override final string toString() { result = "Negate" } } + class ShiftLeft extends BinaryOpcode, TShiftLeft { override final string toString() { result = "ShiftLeft" } } + class ShiftRight extends BinaryOpcode, TShiftRight { override final string toString() { result = "ShiftRight" } } + class BitAnd extends BinaryOpcode, TBitAnd { override final string toString() { result = "BitAnd" } } + class BitOr extends BinaryOpcode, TBitOr { override final string toString() { result = "BitOr" } } + class BitXor extends BinaryOpcode, TBitXor { override final string toString() { result = "BitXor" } } + class BitComplement extends UnaryOpcode, TBitComplement { override final string toString() { result = "BitComplement" } } + class LogicalNot extends UnaryOpcode, TLogicalNot { override final string toString() { result = "LogicalNot" } } + class CompareEQ extends CompareOpcode, TCompareEQ { override final string toString() { result = "CompareEQ" } } + class CompareNE extends CompareOpcode, TCompareNE { override final string toString() { result = "CompareNE" } } + class CompareLT extends CompareOpcode, TCompareLT { override final string toString() { result = "CompareLT" } } + class CompareGT extends CompareOpcode, TCompareGT { override final string toString() { result = "CompareGT" } } + class CompareLE extends CompareOpcode, TCompareLE { override final string toString() { result = "CompareLE" } } + class CompareGE extends CompareOpcode, TCompareGE { override final string toString() { result = "CompareGE" } } + class PointerAdd extends PointerOffsetOpcode, TPointerAdd { override final string toString() { result = "PointerAdd" } } + class PointerSub extends PointerOffsetOpcode, TPointerSub { override final string toString() { result = "PointerSub" } } + class PointerDiff extends PointerArithmeticOpcode, TPointerDiff { override final string toString() { result = "PointerDiff" } } + class Convert extends UnaryOpcode, TConvert { override final string toString() { result = "Convert" } } + class ConvertToBase extends UnaryOpcode, TConvertToBase { override final string toString() { result = "ConvertToBase" } } + class ConvertToVirtualBase extends UnaryOpcode, TConvertToVirtualBase { override final string toString() { result = "ConvertToVirtualBase" } } + class ConvertToDerived extends UnaryOpcode, TConvertToDerived { override final string toString() { result = "ConvertToDerived" } } + class CheckedConvertOrNull extends UnaryOpcode, TCheckedConvertOrNull { override final string toString() { result = "CheckedConvertOrNull" } } + class CheckedConvertOrThrow extends UnaryOpcode, TCheckedConvertOrThrow { override final string toString() { result = "CheckedConvertOrThrow" } } + class DynamicCastToVoid extends UnaryOpcode, TDynamicCastToVoid { override final string toString() { result = "DynamicCastToVoid" } } + class VariableAddress extends Opcode, TVariableAddress { override final string toString() { result = "VariableAddress" } } + class FieldAddress extends UnaryOpcode, TFieldAddress { override final string toString() { result = "FieldAddress" } } + class FunctionAddress extends Opcode, TFunctionAddress { override final string toString() { result = "FunctionAddress" } } + class Constant extends Opcode, TConstant { override final string toString() { result = "Constant" } } + class StringConstant extends Opcode, TStringConstant { override final string toString() { result = "StringConstant" } } + class ConditionalBranch extends OpcodeWithCondition, TConditionalBranch { override final string toString() { result = "ConditionalBranch" } } + class Switch extends OpcodeWithCondition, TSwitch { override final string toString() { result = "Switch" } } + class Invoke extends Opcode, TInvoke { override final string toString() { result = "Invoke" } } + class CatchByType extends CatchOpcode, TCatchByType { override final string toString() { result = "CatchByType" } } + class CatchAny extends CatchOpcode, TCatchAny { override final string toString() { result = "CatchAny" } } + class ThrowValue extends ThrowOpcode, MemoryAccessOpcode, TThrowValue { override final string toString() { result = "ThrowValue" } } + class ReThrow extends ThrowOpcode, TReThrow { override final string toString() { result = "ReThrow" } } + class Unwind extends Opcode, TUnwind { override final string toString() { result = "Unwind" } } + class UnmodeledDefinition extends Opcode, TUnmodeledDefinition { override final string toString() { result = "UnmodeledDefinition" } } + class UnmodeledUse extends Opcode, TUnmodeledUse { override final string toString() { result = "UnmodeledUse" } } + class Phi extends Opcode, TPhi { override final string toString() { result = "Phi" } } + class VarArgsStart extends BuiltInOpcode, TVarArgsStart { override final string toString() { result = "VarArgsStart" } } + class VarArgsEnd extends BuiltInOpcode, TVarArgsEnd { override final string toString() { result = "VarArgsEnd" } } + class VarArg extends BuiltInOpcode, TVarArg { override final string toString() { result = "VarArg" } } + class VarArgCopy extends BuiltInOpcode, TVarArgCopy { override final string toString() { result = "VarArgCopy" } } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/OperandTag.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/OperandTag.qll new file mode 100644 index 000000000000..ece7fb5bd15b --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/internal/OperandTag.qll @@ -0,0 +1,299 @@ +private import IRInternal +import Instruction +import IRBlock +import cpp + +private newtype TOperandTag = + TLoadStoreAddressOperand() or + TCopySourceOperand() or + TUnaryOperand() or + TLeftOperand() or + TRightOperand() or + TReturnValueOperand() or + TExceptionOperand() or + TConditionOperand() or + TUnmodeledUseOperand() or + TCallTargetOperand() or + TThisArgumentOperand() or + TPositionalArgumentOperand(int argIndex) { + argIndex in [0..Construction::getMaxCallArgIndex()] or + exists(BuiltInOperation op | + exists(op.getChild(argIndex)) + ) + } or + TPhiOperand(IRBlock predecessorBlock) { + exists(PhiInstruction phi | + predecessorBlock = Construction::getPhiInstructionBlockStart(phi).getBlock().getAPredecessor() + ) + } + +/** + * Identifies the kind of operand on an instruction. Each `Instruction` has at + * most one operand of any single `OperandTag`. The set of `OperandTag`s used by + * an `Instruction` is determined by the instruction's opcode. + */ +abstract class OperandTag extends TOperandTag { + abstract string toString(); + abstract int getSortOrder(); +} + +// Note: individual subtypes are listed in the order that the operands should +// appear in the operand list of the instruction when printing. + +/** + * The address operand of an instruction that loads or stores a value from + * memory (e.g. `Load`, `Store`). + */ +class LoadStoreAddressOperand extends OperandTag, TLoadStoreAddressOperand { + override final string toString() { + result = "LoadStoreAddress" + } + + override final int getSortOrder() { + result = 0 + } +} + +LoadStoreAddressOperand loadStoreAddressOperand() { + result = TLoadStoreAddressOperand() +} + +/** + * The source value operand of an instruction that copies this value to its + * result (e.g. `Copy`, `Load`, `Store`). + */ +class CopySourceOperand extends OperandTag, TCopySourceOperand { + override final string toString() { + result = "CopySource" + } + + override final int getSortOrder() { + result = 1 + } +} + +CopySourceOperand copySourceOperand() { + result = TCopySourceOperand() +} + +/** + * The sole operand of a unary instruction (e.g. `Convert`, `Negate`). + */ +class UnaryOperand extends OperandTag, TUnaryOperand { + override final string toString() { + result = "Unary" + } + + override final int getSortOrder() { + result = 2 + } +} + +UnaryOperand unaryOperand() { + result = TUnaryOperand() +} + +/** + * The left operand of a binary instruction (e.g. `Add`, `CompareEQ`). + */ +class LeftOperand extends OperandTag, TLeftOperand { + override final string toString() { + result = "Left" + } + + override final int getSortOrder() { + result = 3 + } +} + +LeftOperand leftOperand() { + result = TLeftOperand() +} + +/** + * The right operand of a binary instruction (e.g. `Add`, `CompareEQ`). + */ +class RightOperand extends OperandTag, TRightOperand { + override final string toString() { + result = "Right" + } + + override final int getSortOrder() { + result = 4 + } +} + +RightOperand rightOperand() { + result = TRightOperand() +} + +/** + * The return value operand of a `ReturnValue` instruction. + */ +class ReturnValueOperand extends OperandTag, TReturnValueOperand { + override final string toString() { + result = "ReturnValue" + } + + override final int getSortOrder() { + result = 5 + } +} + +ReturnValueOperand returnValueOperand() { + result = TReturnValueOperand() +} + +/** + * The exception thrown by a `ThrowValue` instruction. + */ +class ExceptionOperand extends OperandTag, TExceptionOperand { + override final string toString() { + result = "Exception" + } + + override final int getSortOrder() { + result = 6 + } +} + +ExceptionOperand exceptionOperand() { + result = TExceptionOperand() +} + +/** + * The condition operand of a `ConditionalBranch` or `Switch` instruction. + */ +class ConditionOperand extends OperandTag, TConditionOperand { + override final string toString() { + result = "Condition" + } + + override final int getSortOrder() { + result = 7 + } +} + +ConditionOperand conditionOperand() { + result = TConditionOperand() +} + +/** + * An operand of the special `UnmodeledUse` instruction, representing a value + * whose set of uses is unknown. + */ +class UnmodeledUseOperand extends OperandTag, TUnmodeledUseOperand { + override final string toString() { + result = "UnmodeledUse" + } + + override final int getSortOrder() { + result = 8 + } +} + +UnmodeledUseOperand unmodeledUseOperand() { + result = TUnmodeledUseOperand() +} + +/** + * The operand representing the target function of an `Invoke` instruction. + */ +class CallTargetOperand extends OperandTag, TCallTargetOperand { + override final string toString() { + result = "CallTarget" + } + + override final int getSortOrder() { + result = 9 + } +} + +CallTargetOperand callTargetOperand() { + result = TCallTargetOperand() +} + +/** + * 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`). + */ +abstract class ArgumentOperand extends OperandTag { +} + +/** + * An operand representing the implicit 'this' argument to a member function + * call. + */ +class ThisArgumentOperand extends ArgumentOperand, TThisArgumentOperand { + ThisArgumentOperand() { + this = TThisArgumentOperand() + } + + override final string toString() { + result = "Arg(this)" + } + + override final int getSortOrder() { + result = 10 + } +} + +ThisArgumentOperand thisArgumentOperand() { + result = TThisArgumentOperand() +} + +/** + * An operand representing an argument to a function call. + */ +class PositionalArgumentOperand extends ArgumentOperand, + TPositionalArgumentOperand { + int argIndex; + + PositionalArgumentOperand() { + this = TPositionalArgumentOperand(argIndex) + } + + override final string toString() { + result = "Arg(" + argIndex + ")" + } + + override final int getSortOrder() { + result = 11 + argIndex + } + + final int getArgIndex() { + result = argIndex + } +} + +PositionalArgumentOperand positionalArgumentOperand(int argIndex) { + result = TPositionalArgumentOperand(argIndex) +} + +/** + * An operand of an SSA `Phi` instruction. + */ +class PhiOperand extends OperandTag, TPhiOperand { + IRBlock predecessorBlock; + + PhiOperand() { + this = TPhiOperand(predecessorBlock) + } + + override final string toString() { + result = "Phi" + } + + override final int getSortOrder() { + result = 11 + } + + final IRBlock getPredecessorBlock() { + result = predecessorBlock + } +} + +PhiOperand phiOperand(IRBlock predecessorBlock) { + result = TPhiOperand(predecessorBlock) +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/PrintIRImpl.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/PrintIRImpl.qll new file mode 100644 index 000000000000..9cf77f21c04a --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/internal/PrintIRImpl.qll @@ -0,0 +1,175 @@ +private import IRImpl +import cpp + +private int getInstructionIndexInBlock(Instruction instr) { + exists(IRBlock block | + block = instr.getBlock() and + ( + exists(int index, int phiCount | + phiCount = count(block.getAPhiInstruction()) and + instr = block.getInstruction(index) and + result = index + phiCount + ) or + ( + instr instanceof PhiInstruction and + instr = rank[result + 1](PhiInstruction phiInstr | + phiInstr = block.getAPhiInstruction() | + phiInstr order by phiInstr.getUniqueId() + ) + ) + ) + ) +} + +private string getInstructionResultId(Instruction instr) { + result = getResultPrefix(instr) + getBlockId(instr.getBlock()) + "_" + + getInstructionIndexInBlock(instr).toString() +} + +private string getResultPrefix(Instruction instr) { + if instr.hasMemoryResult() then + if instr.isResultModeled() then + result = "@m" + else + result = "@mu" + else + result = "@r" +} + +/** + * Gets the identifier of the specified function scope. + * Currently just returns the signature of the function. + */ +private string getScopeId(Function func) { + result = func.getFullSignature() +} + +/** + * Gets the unique identifier of the block within its function. + * Currently returns a string representation of an integer in the range + * [0..numBlocks - 1]. + */ +private string getBlockId(IRBlock block) { + exists(int rankIndex | + block = rank[rankIndex + 1](IRBlock funcBlock | + funcBlock.getFunction() = block.getFunction() | + funcBlock order by funcBlock.getUniqueId() + ) and + result = rankIndex.toString() + ) +} + +/** + * Prints the full signature and qualified name for each scope. This is primarily + * so that post-processing tools can identify function overloads, which will have + * different signatures but the same qualified name. + */ +query predicate printIRGraphScopes(string scopeId, string qualifiedName) { + exists(FunctionIR ir, Function func | + func = ir.getFunction() and + scopeId = getScopeId(func) and + qualifiedName = func.getQualifiedName() + ) +} + +query predicate printIRGraphNodes(string scopeId, string blockId, string label, string location) { + exists(IRBlock block | + scopeId = getScopeId(block.getFunction()) and + blockId = getBlockId(block) and + label = "" and + location = "" + ) +} + +query predicate printIRGraphInstructions(string scopeId, string blockId, + string id, string label, string location) { + exists(IRBlock block, Instruction instr | + instr = block.getAnInstruction() and + label = instr.toString() and + location = instr.getLocation().toString() and + scopeId = getScopeId(block.getFunction()) and + blockId = getBlockId(block) and + id = getInstructionIndexInBlock(instr).toString() + ) +} + +query predicate printIRGraphEdges(string scopeId, + string predecessorId, string successorId, string label) { + exists(IRBlock predecessor, IRBlock successor, EdgeKind kind | + scopeId = getScopeId(predecessor.getFunction()) and + predecessor.getSuccessor(kind) = successor and + predecessorId = getBlockId(predecessor) and + successorId = getBlockId(successor) and + label = kind.toString() + ) +} + +private string getValueCategoryString(Instruction instr) { + if instr.isGLValue() then + result = "glval:" + else + result = "" +} + +private string getResultTypeString(Instruction instr) { + exists(Type resultType, string valcat | + resultType = instr.getResultType() and + valcat = getValueCategoryString(instr) and + if resultType instanceof UnknownType and exists(instr.getResultSize()) then + result = valcat + resultType.toString() + "[" + instr.getResultSize().toString() + "]" + else + result = valcat + resultType.toString() + ) +} + +query predicate printIRGraphDestinationOperands(string scopeId, string blockId, + string instructionId, int operandId, string label) { + exists(IRBlock block, Instruction instr | + block.getAnInstruction() = instr and + scopeId = getScopeId(block.getFunction()) and + blockId = getBlockId(block) and + instructionId = getInstructionIndexInBlock(instr).toString() and + not instr.getResultType() instanceof VoidType and + operandId = 0 and + label = getInstructionResultId(instr) + + "(" + getResultTypeString(instr) + ")" + ) +} + +private string getOperandTagLabel(OperandTag tag) { + ( + tag instanceof PhiOperand and + result = "from " + getBlockId(tag.(PhiOperand).getPredecessorBlock()) + ": " + ) + or ( + tag instanceof ThisArgumentOperand and + result = "this:" + ) + or ( + not tag instanceof PhiOperand and + not tag instanceof ThisArgumentOperand and + result = "" + ) +} + +query predicate printIRGraphSourceOperands(string scopeId, string blockId, + string instructionId, int operandId, string label) { + exists(IRBlock block, Instruction instr | + block.getAnInstruction() = instr and + blockId = getBlockId(block) and + scopeId = getScopeId(block.getFunction()) and + instructionId = getInstructionIndexInBlock(instr).toString() and + if (instr instanceof UnmodeledUseInstruction) then ( + operandId = 0 and + label = "@mu*" + ) + else ( + exists(OperandTag tag, Instruction operandInstr | + operandInstr = instr.getOperand(tag) and + operandId = tag.getSortOrder() and + label = getOperandTagLabel(tag) + + getInstructionResultId(operandInstr) + ) + ) + ) +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/TempVariableTag.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/TempVariableTag.qll new file mode 100644 index 000000000000..e2decffc6c7b --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/internal/TempVariableTag.qll @@ -0,0 +1,12 @@ +import cpp + +newtype TTempVariableTag = + ConditionValueTempVar() or + ReturnValueTempVar() or + ThrowTempVar() + +string getTempVariableTagId(TTempVariableTag tag) { + tag = ConditionValueTempVar() and result = "CondVal" or + tag = ReturnValueTempVar() and result = "Ret" or + tag = ThrowTempVar() and result = "Throw" +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/TranslatedCondition.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/TranslatedCondition.qll new file mode 100644 index 000000000000..dd306632747b --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/internal/TranslatedCondition.qll @@ -0,0 +1,263 @@ +import cpp +private import InstructionTag +private import Opcode +private import TranslatedElement +private import TranslatedExpr + +abstract class ConditionContext extends TranslatedElement { + abstract Instruction getChildTrueSuccessor(TranslatedCondition child); + + abstract Instruction getChildFalseSuccessor(TranslatedCondition child); +} + +TranslatedCondition getTranslatedCondition(Expr expr) { + result.getExpr() = expr +} + +abstract class TranslatedCondition extends TranslatedElement { + Expr expr; + + override final string toString() { + result = expr.toString() + } + + override final Locatable getAST() { + result = expr + } + + final ConditionContext getConditionContext() { + result = getParent() + } + + final Expr getExpr() { + result = expr + } + + override final Function getFunction() { + result = expr.getEnclosingFunction() + } + + final Type getResultType() { + result = expr.getType().getUnspecifiedType() + } +} + +abstract class TranslatedFlexibleCondition extends TranslatedCondition, + ConditionContext, TTranslatedFlexibleCondition { + TranslatedFlexibleCondition() { + this = TTranslatedFlexibleCondition(expr) + } + + override final TranslatedElement getChild(int id) { + id = 0 and result = getOperand() + } + + override final Instruction getFirstInstruction() { + result = getOperand().getFirstInstruction() + } + + override final predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + none() + } + + override final Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + none() + } + + override final Instruction getChildSuccessor(TranslatedElement child) { + none() + } + + abstract TranslatedCondition getOperand(); +} + +class TranslatedParenthesisCondition extends TranslatedFlexibleCondition { + ParenthesisExpr paren; + + TranslatedParenthesisCondition() { + paren = expr + } + + final override Instruction getChildTrueSuccessor(TranslatedCondition child) { + child = getOperand() and + result = getConditionContext().getChildTrueSuccessor(this) + } + + final override Instruction getChildFalseSuccessor(TranslatedCondition child) { + child = getOperand() and + result = getConditionContext().getChildFalseSuccessor(this) + } + + final override TranslatedCondition getOperand() { + result = getTranslatedCondition(paren.getExpr()) + } +} + +class TranslatedNotCondition extends TranslatedFlexibleCondition { + NotExpr notExpr; + + TranslatedNotCondition() { + notExpr = expr + } + + override Instruction getChildTrueSuccessor(TranslatedCondition child) { + child = getOperand() and + result = getConditionContext().getChildFalseSuccessor(this) + } + + override Instruction getChildFalseSuccessor(TranslatedCondition child) { + child = getOperand() and + result = getConditionContext().getChildTrueSuccessor(this) + } + + override TranslatedCondition getOperand() { + result = getTranslatedCondition(notExpr.getOperand().getFullyConverted()) + } +} + +abstract class TranslatedNativeCondition extends TranslatedCondition, + TTranslatedNativeCondition { + TranslatedNativeCondition() { + this = TTranslatedNativeCondition(expr) + } + + override final Instruction getChildSuccessor(TranslatedElement child) { + none() + } +} + +abstract class TranslatedBinaryLogicalOperation extends + TranslatedNativeCondition, ConditionContext { + BinaryLogicalOperation op; + + TranslatedBinaryLogicalOperation() { + op = expr + } + + override final TranslatedElement getChild(int id) { + id = 0 and result = getLeftOperand() or + id = 1 and result = getRightOperand() + } + + override final Instruction getFirstInstruction() { + result = getLeftOperand().getFirstInstruction() + } + + override final predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + none() + } + + override final Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + none() + } + + final TranslatedCondition getLeftOperand() { + result = getTranslatedCondition(op.getLeftOperand().getFullyConverted()) + } + + final TranslatedCondition getRightOperand() { + result = getTranslatedCondition(op.getRightOperand().getFullyConverted()) + } +} + +class TranslatedLogicalAndExpr extends TranslatedBinaryLogicalOperation { + TranslatedLogicalAndExpr() { + op instanceof LogicalAndExpr + } + + override Instruction getChildTrueSuccessor(TranslatedCondition child) { + ( + child = getLeftOperand() and + result = getRightOperand().getFirstInstruction() + ) or + ( + child = getRightOperand() and + result = getConditionContext().getChildTrueSuccessor(this) + ) + } + + override Instruction getChildFalseSuccessor(TranslatedCondition child) { + (child = getLeftOperand() or child = getRightOperand()) and + result = getConditionContext().getChildFalseSuccessor(this) + } +} + +class TranslatedLogicalOrExpr extends TranslatedBinaryLogicalOperation { + TranslatedLogicalOrExpr() { + op instanceof LogicalOrExpr + } + + override Instruction getChildTrueSuccessor(TranslatedCondition child) { + (child = getLeftOperand() or child = getRightOperand()) and + result = getConditionContext().getChildTrueSuccessor(this) + } + + override Instruction getChildFalseSuccessor(TranslatedCondition child) { + ( + child = getLeftOperand() and + result = getRightOperand().getFirstInstruction() + ) or + ( + child = getRightOperand() and + result = getConditionContext().getChildFalseSuccessor(this) + ) + } +} + +class TranslatedValueCondition extends TranslatedCondition, + TTranslatedValueCondition { + TranslatedValueCondition() { + this = TTranslatedValueCondition(expr) + } + + override TranslatedElement getChild(int id) { + id = 0 and result = getValueExpr() + } + + override Instruction getFirstInstruction() { + result = getValueExpr().getFirstInstruction() + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + tag = ValueConditionConditionalBranchTag() and + opcode instanceof Opcode::ConditionalBranch and + resultType instanceof VoidType and + isGLValue = false + } + + override Instruction getChildSuccessor(TranslatedElement child) { + child = getValueExpr() and + result = getInstruction(ValueConditionConditionalBranchTag()) + } + + override Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + tag = ValueConditionConditionalBranchTag() and + ( + ( + kind instanceof TrueEdge and + result = getConditionContext().getChildTrueSuccessor(this) + ) or + ( + kind instanceof FalseEdge and + result = getConditionContext().getChildFalseSuccessor(this) + ) + ) + } + + override Instruction getInstructionOperand(InstructionTag tag, + OperandTag operandTag) { + tag = ValueConditionConditionalBranchTag() and + operandTag instanceof ConditionOperand and + result = getValueExpr().getResult() + } + + private TranslatedExpr getValueExpr() { + result = getTranslatedExpr(expr) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/TranslatedDeclarationEntry.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/TranslatedDeclarationEntry.qll new file mode 100644 index 000000000000..5e060fa08fb5 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/internal/TranslatedDeclarationEntry.qll @@ -0,0 +1,240 @@ +import cpp +private import InstructionTag +private import Opcode +private import TranslatedElement +private import TranslatedExpr +private import TranslatedInitialization + +/** + * Gets the `TranslatedDeclarationEntry` that represents the declaration + * `entry`. + */ +TranslatedDeclarationEntry getTranslatedDeclarationEntry( + DeclarationEntry entry) { + result.getAST() = entry +} + +/** + * Represents the IR translation of a declaration within the body of a function. + * Most often, this is the declaration of an automatic local variable, although + * it can also be the declaration of a static local variable, an extern + * variable, or an extern function. + */ +abstract class TranslatedDeclarationEntry extends TranslatedElement, + TTranslatedDeclarationEntry { + DeclarationEntry entry; + + TranslatedDeclarationEntry() { + this = TTranslatedDeclarationEntry(entry) + } + + override final Function getFunction() { + exists(DeclStmt stmt | + stmt.getADeclarationEntry() = entry and + result = stmt.getEnclosingFunction() + ) + } + + override final string toString() { + result = entry.toString() + } + + override final Locatable getAST() { + result = entry + } +} + +/** + * Represents the IR translation of a declaration within the body of a function, + * for declarations other than local variables. Since these have no semantic + * effect, they are translated as `NoOp`. + */ +class TranslatedNonVariableDeclaration extends + TranslatedDeclarationEntry { + TranslatedNonVariableDeclaration() { + not entry.getDeclaration() instanceof LocalVariable + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + opcode instanceof Opcode::NoOp and + tag = OnlyInstructionTag() and + resultType instanceof VoidType and + isGLValue = false + } + + override Instruction getFirstInstruction() { + result = getInstruction(OnlyInstructionTag()) + } + + override TranslatedElement getChild(int id) { + none() + } + + override Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + tag = OnlyInstructionTag() and + result = getParent().getChildSuccessor(this) and + kind instanceof GotoEdge + } + + override Instruction getChildSuccessor(TranslatedElement child) { + none() + } +} + +/** + * Represents the IR translation of the declaration of a local variable, + * including its initialization, if any. + */ +abstract class TranslatedVariableDeclaration extends + TranslatedDeclarationEntry { + LocalVariable var; + + TranslatedVariableDeclaration() { + entry.getDeclaration() = var + } +} + +/** + * Represents the IR translation of a local variable with no initializer. The + * generated IR stores the result of an `Uninitialized` instruction into the + * variable. + */ +class TranslatedUninitializedVariable extends + TranslatedVariableDeclaration { + TranslatedUninitializedVariable() { + not exists(Initializer init | + init.getDeclaration() = var + ) + } + + override TranslatedElement getChild(int id) { + none() + } + + override Instruction getFirstInstruction() { + result = getInstruction(InitializerVariableAddressTag()) + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + ( + tag = InitializerVariableAddressTag() and + opcode instanceof Opcode::VariableAddress and + resultType = var.getType().getUnspecifiedType() and + isGLValue = true + ) or + ( + tag = InitializerStoreTag() and + opcode instanceof Opcode::Store and + resultType = var.getType().getUnspecifiedType() and + isGLValue = false + ) or + ( + tag = InitializerUninitializedTag() and + opcode instanceof Opcode::Uninitialized and + resultType = var.getType().getUnspecifiedType() and + isGLValue = false + ) + } + + override Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + kind instanceof GotoEdge and + ( + ( + tag = InitializerVariableAddressTag() and + result = getInstruction(InitializerUninitializedTag()) + ) or + ( + tag = InitializerUninitializedTag() and + result = getInstruction(InitializerStoreTag()) + ) or + ( + tag = InitializerStoreTag() and + result = getParent().getChildSuccessor(this) + ) + ) + } + + override Instruction getChildSuccessor(TranslatedElement child) { + none() + } + + override Instruction getInstructionOperand(InstructionTag tag, + OperandTag operandTag) { + tag = InitializerStoreTag() and + ( + ( + operandTag instanceof LoadStoreAddressOperand and + result = getInstruction(InitializerVariableAddressTag()) + ) or + ( + operandTag instanceof CopySourceOperand and + result = getInstruction(InitializerUninitializedTag()) + ) + ) + } + + override IRVariable getInstructionVariable(InstructionTag tag) { + tag = InitializerVariableAddressTag() and + result = getIRUserVariable(var.getFunction(), var) + } +} + +/** + * Represents the IR translation of a local variable with an initializer. + */ +class TranslatedInitializedVariable extends + TranslatedVariableDeclaration, InitializationContext { + Initializer init; + + TranslatedInitializedVariable() { + init.getDeclaration() = var + } + + override TranslatedElement getChild(int id) { + id = 0 and result = getInitialization() + } + + override Instruction getFirstInstruction() { + result = getInstruction(InitializerVariableAddressTag()) + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + tag = InitializerVariableAddressTag() and + opcode instanceof Opcode::VariableAddress and + resultType = var.getType().getUnspecifiedType() and + isGLValue = true + } + + override Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + tag = InitializerVariableAddressTag() and + result = getInitialization().getFirstInstruction() and + kind instanceof GotoEdge + } + + override Instruction getChildSuccessor(TranslatedElement child) { + child = getInitialization() and result = getParent().getChildSuccessor(this) + } + + override IRVariable getInstructionVariable(InstructionTag tag) { + tag = InitializerVariableAddressTag() and + result = getIRUserVariable(var.getFunction(), var) + } + + override Instruction getTargetAddress() { + result = getInstruction(InitializerVariableAddressTag()) + } + + override Type getTargetType() { + result = var.getType().getUnspecifiedType() + } + + private TranslatedInitialization getInitialization() { + result = getTranslatedInitialization(init.getExpr().getFullyConverted()) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/TranslatedElement.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/TranslatedElement.qll new file mode 100644 index 000000000000..eb233bf6bae9 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/internal/TranslatedElement.qll @@ -0,0 +1,563 @@ +import cpp +import semmle.code.cpp.ir.IR +private import InstructionTag +private import Opcode +private import TempVariableTag +private import TranslatedCondition +private import TranslatedFunction +private import TranslatedStmt + +/** + * Gets the built-in `int` type. + */ +Type getIntType() { + result.(IntType).isImplicitlySigned() +} + +/** + * If `expr` is a conversion, gets the expression being converted. Otherwise, + * returns `expr`. + */ +private Expr getUnconvertedExpr(Expr expr) { + if expr instanceof Conversion then ( + result = getUnconvertedExpr(expr.(Conversion).getExpr()) + ) else ( + result = expr + ) +} + +/** + * Gets the "real" parent of `expr`. This predicate treats conversions as if + * they were explicit nodes in the expression tree, rather than as implicit + * nodes as in the regular AST representation. + */ +private Element getRealParent(Expr expr) { + if expr.hasConversion() then ( + // The expression has a conversion, so treat that as its parent + result = expr.getConversion() + ) + else ( + // Either the expression is a top-level conversion, or it's not a + // conversion. The real parent is the parent of the original unconverted + // expression. + result = getUnconvertedExpr(expr).getParent() or + // The parent of a DestructorDestruction is the destructor itself. + result.(Destructor).getADestruction() = expr + ) +} + +/** + * Holds if `expr` should be ignored for the purposes of code generation due to + * some property of `expr` itself. Unlike `ignoreExpr()`, this predicate does + * not ignore an expression solely because it is a descendant of an ignored + * element. + */ +private predicate ignoreExprLocal(Expr expr) { + // Ignore parentless expressions + not exists(getRealParent(expr)) or + // Ignore the constants in SwitchCase, since their values are embedded in the + // CaseEdge. + getRealParent(expr) instanceof SwitchCase or + // Ignore descendants of constant expressions, since we'll just substitute the + // constant value. + getRealParent(expr).(Expr).isConstant() or + // The `DestructorCall` node for a `DestructorFieldDestruction` has a `FieldAccess` + // node as its qualifier, but that `FieldAccess` does not have a child of its own. + // We'll ignore that `FieldAccess`, and supply the receiver as part of the calling + // context, much like we do with constructor calls. + expr.getParent().(DestructorCall).getParent() instanceof DestructorFieldDestruction +} + +/** + * Holds if `expr` should be ignored for the purposes of IR generation. + */ +private predicate ignoreExpr(Expr expr) { + ignoreExprLocal(expr) or + // Ignore all descendants of ignored elements as well. + ignoreElement(getRealParent(expr)) +} + +/** + * Holds if `element` should be ignored for the purposes of IR generation. + */ +private predicate ignoreElement(Element element) { + ignoreExpr(element.(Expr)) +} + +/** + * Holds if `expr` is most naturally evaluated as control flow, rather than as + * a value. + */ +private predicate isNativeCondition(Expr expr) { + expr instanceof BinaryLogicalOperation +} + +/** + * Holds if `expr` can be evaluated as either a condition or a value expression, + * depending on context. + */ +private predicate isFlexibleCondition(Expr expr) { + ( + expr instanceof ParenthesisExpr or + expr instanceof NotExpr + ) and + usedAsCondition(expr) +} + +/** + * Holds if `expr` is used in a condition context, i.e. the Boolean result of + * the expression is directly used to determine control flow. + */ +private predicate usedAsCondition(Expr expr) { + exists(BinaryLogicalOperation op | + op.getLeftOperand().getFullyConverted() = expr or + op.getRightOperand().getFullyConverted() = expr + ) or + exists(Loop loop | + loop.getCondition().getFullyConverted() = expr + ) or + exists(IfStmt ifStmt | + ifStmt.getCondition().getFullyConverted() = expr + ) or + exists(ConditionalExpr condExpr | + condExpr.getCondition().getFullyConverted() = expr + ) or + exists(NotExpr notExpr | + notExpr.getOperand().getFullyConverted() = expr and + usedAsCondition(notExpr) + ) or + exists(ParenthesisExpr paren | + paren.getExpr() = expr and + usedAsCondition(paren) + ) +} + +/** + * Holds if `expr` has an lvalue-to-rvalue conversion that should be ignored + * when generating IR. This occurs for conversion from an lvalue of function type + * to an rvalue of function pointer type. The conversion is represented in the + * AST as an lvalue-to-rvalue conversion, but the IR represents both a function + * lvalue and a function pointer prvalue the same. + */ +predicate ignoreLoad(Expr expr) { + expr.hasLValueToRValueConversion() and + ( + expr instanceof ThisExpr or + expr instanceof FunctionAccess or + expr.(PointerDereferenceExpr).getOperand().getFullyConverted().getType(). + getUnspecifiedType() instanceof FunctionPointerType or + expr.(ReferenceDereferenceExpr).getExpr().getType(). + getUnspecifiedType() instanceof FunctionReferenceType + ) +} + +newtype TTranslatedElement = + // An expression that is not being consumed as a condition + TTranslatedNonLoadExpr(Expr expr) { + not ignoreExpr(expr) and + not isNativeCondition(expr) and + not isFlexibleCondition(expr) + } or + // A separate element to handle the lvalue-to-rvalue conversion step of an + // expression. + TTranslatedLoad(Expr expr) { + not ignoreExpr(expr) and + not isNativeCondition(expr) and + not isFlexibleCondition(expr) and + expr.hasLValueToRValueConversion() and + not ignoreLoad(expr) + } or + // An expression most naturally translated as control flow. + TTranslatedNativeCondition(Expr expr) { + not ignoreExpr(expr) and + isNativeCondition(expr) + } or + // An expression that can best be translated as control flow given the context + // in which it is used. + TTranslatedFlexibleCondition(Expr expr) { + not ignoreExpr(expr) and + isFlexibleCondition(expr) + } or + // An expression that is not naturally translated as control flow, but is + // consumed in a condition context. This element adapts the original element + // to the condition context. + TTranslatedValueCondition(Expr expr) { + not ignoreExpr(expr) and + not isNativeCondition(expr) and + not isFlexibleCondition(expr) and + usedAsCondition(expr) + } or + // An expression that is naturally translated as control flow, but is used in + // a context where a simple value is expected. This element adapts the + // original condition to the value context. + TTranslatedConditionValue(Expr expr) { + not ignoreExpr(expr) and + isNativeCondition(expr) and + not usedAsCondition(expr) + } or + // An expression used as an initializer. + TTranslatedInitialization(Expr expr) { + not ignoreExpr(expr) and + ( + exists(Initializer init | + init.getExpr().getFullyConverted() = expr + ) or + exists(ClassAggregateLiteral initList | + initList.getFieldExpr(_).getFullyConverted() = expr + ) or + exists(ArrayAggregateLiteral initList | + initList.getElementExpr(_).getFullyConverted() = expr + ) or + exists(ReturnStmt returnStmt | + returnStmt.getExpr().getFullyConverted() = expr + ) or + exists(ConstructorFieldInit fieldInit | + fieldInit.getExpr().getFullyConverted() = expr + ) or + exists(ThrowExpr throw | + throw.getExpr().getFullyConverted() = expr + ) + ) + } or + // The initialization of a field via a member of an initializer list. + TTranslatedExplicitFieldInitialization(Expr ast, Field field, + Expr expr) { + exists(ClassAggregateLiteral initList | + not ignoreExpr(initList) and + ast = initList and + expr = initList.getFieldExpr(field).getFullyConverted() + ) or + exists(ConstructorFieldInit init | + not ignoreExpr(init) and + ast = init and + field = init.getTarget() and + expr = init.getExpr().getFullyConverted() + ) + } or + // The value initialization of a field due to an omitted member of an + // initializer list. + TTranslatedFieldValueInitialization(Expr ast, Field field) { + exists(ClassAggregateLiteral initList | + not ignoreExpr(initList) and + ast = initList and + initList.isValueInitialized(field) + ) + } or + // The initialization of an array element via a member of an initializer list. + TTranslatedExplicitElementInitialization( + ArrayAggregateLiteral initList, int elementIndex) { + not ignoreExpr(initList) and + exists(initList.getElementExpr(elementIndex)) + } or + // The value initialization of a range of array elements that were omitted + // from an initializer list. + TTranslatedElementValueInitialization(ArrayAggregateLiteral initList, + int elementIndex, int elementCount) { + not ignoreExpr(initList) and + isFirstValueInitializedElementInRange(initList, elementIndex) and + elementCount = + getNextExplicitlyInitializedElementAfter(initList, elementIndex) - + elementIndex + } or + // The initialization of a base class from within a constructor. + TTranslatedConstructorBaseInit(ConstructorBaseInit init) { + not ignoreExpr(init) + } or + // The destruction of a base class from within a destructor. + TTranslatedDestructorBaseDestruction(DestructorBaseDestruction destruction) { + not ignoreExpr(destruction) + } or + // The destruction of a field from within a destructor. + TTranslatedDestructorFieldDestruction(DestructorFieldDestruction destruction) { + not ignoreExpr(destruction) + } or + // A statement + TTranslatedStmt(Stmt stmt) or + // A function + TTranslatedFunction(Function func) { + func.hasEntryPoint() and + not func.isFromUninstantiatedTemplate(_) + } or + // A constructor init list + TTranslatedConstructorInitList(Function func) { + func.hasEntryPoint() + } or + // A destructor destruction list + TTranslatedDestructorDestructionList(Function func) { + func.hasEntryPoint() + } or + // A function parameter + TTranslatedParameter(Parameter param) { + param.getFunction().hasEntryPoint() or + exists(param.getCatchBlock()) + } or + // A local declaration + TTranslatedDeclarationEntry(DeclarationEntry entry) { + exists(DeclStmt declStmt | + declStmt.getADeclarationEntry() = entry + ) + } + +/** + * Gets the index of the first explicitly initialized element in `initList` + * whose index is greater than `afterElementIndex`. If there are no remaining + * explicitly initialized elements in `initList`, the result is the total number + * of elements in the array being initialized. + */ + private int getNextExplicitlyInitializedElementAfter( + ArrayAggregateLiteral initList, int afterElementIndex) { + if exists(int x | + x > afterElementIndex and + exists(initList.getElementExpr(x))) + then ( + if exists(initList.getElementExpr(afterElementIndex + 1)) + then result = afterElementIndex + 1 + else result = getNextExplicitlyInitializedElementAfter(initList, afterElementIndex+1)) + else + result = initList.getType().getUnspecifiedType().(ArrayType).getArraySize() and + // required for binding + initList.isInitialized(afterElementIndex) +} + +/** + * Holds if element `elementIndex` is the first value-initialized element in a + * range of one or more consecutive value-initialized elements in `initList`. + */ +private predicate isFirstValueInitializedElementInRange( + ArrayAggregateLiteral initList, int elementIndex) { + initList.isValueInitialized(elementIndex) and + ( + elementIndex = 0 or + not initList.isValueInitialized(elementIndex - 1) + ) +} + +/** + * Represents an AST node for which IR needs to be generated. + * + * In most cases, there is a single `TranslatedElement` for each AST node. + * However, when a single AST node performs two separable operations (e.g. + * a `VariableAccess` that is also a load), there may be multiple + * `TranslatedElement` nodes for a single AST node. + */ +abstract class TranslatedElement extends TTranslatedElement { + abstract string toString(); + + /** + * Gets the AST node being translated. + */ + abstract Locatable getAST(); + + /** + * Get the first instruction to be executed in the evaluation of this element. + */ + abstract Instruction getFirstInstruction(); + + /** + * Get the immediate child elements of this element. + */ + final TranslatedElement getAChild() { + result = getChild(_) + } + + /** + * Gets the immediate child element of this element. The `id` is unique + * among all children of this element, but the values are not necessarily + * consecutive. + */ + abstract TranslatedElement getChild(int id); + + /** + * Gets the an identifier string for the element. This string is unique within + * the scope of the element's function. + */ + final string getId() { + result = getUniqueId().toString() + } + + private TranslatedElement getChildByRank(int rankIndex) { + result = rank[rankIndex + 1](TranslatedElement child, int id | + child = getChild(id) | + child order by id + ) + } + + language[monotonicAggregates] + private int getDescendantCount() { + result = 1 + sum(TranslatedElement child | + child = getChildByRank(_) | + child.getDescendantCount() + ) + } + + private int getUniqueId() { + if not exists(getParent()) then + result = 0 + else + exists(TranslatedElement parent | + parent = getParent() and + if this = parent.getChildByRank(0) then + result = 1 + parent.getUniqueId() + else + exists(int childIndex, TranslatedElement previousChild | + this = parent.getChildByRank(childIndex) and + previousChild = parent.getChildByRank(childIndex - 1) and + result = previousChild.getUniqueId() + + previousChild.getDescendantCount() + ) + ) + } + + /** + * Holds if this element generates an instruction with opcode `opcode` and + * result type `resultType`. `tag` must be unique for each instruction + * generated from the same AST node (not just from the same + * `TranslatedElement`). + * If the instruction does not return a result, `resultType` should be + * `VoidType`. + */ + abstract predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue); + + /** + * Gets the `Function` that contains this element. + */ + abstract Function getFunction(); + + /** + * Gets the successor instruction of the instruction that was generated by + * this element for tag `tag`. The successor edge kind is specified by `kind`. + */ + abstract Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind); + + /** + * Gets the successor instruction to which control should flow after the + * child element specified by `child` has finished execution. + */ + abstract Instruction getChildSuccessor(TranslatedElement child); + + /** + * Gets the instruction to which control should flow if an exception is thrown + * within this element. This will generally return first `catch` block of the + * nearest enclosing `try`, or the `Unwind` instruction for the function if + * there is no enclosing `try`. + */ + Instruction getExceptionSuccessorInstruction() { + result = getParent().getExceptionSuccessorInstruction() + } + + /** + * Holds if this element generates a temporary variable with type `type`. + * `tag` must be unique for each variable generated from the same AST node + * (not just from the same `TranslatedElement`). + */ + predicate hasTempVariable(TempVariableTag tag, Type type) { + none() + } + + /** + * If the instruction specified by `tag` is a `FunctionInstruction`, gets the + * `Function` for that instruction. + */ + Function getInstructionFunction(InstructionTag tag) { + none() + } + + /** + * If the instruction specified by `tag` is a `VariableInstruction`, gets the + * `IRVariable` for that instruction. + */ + IRVariable getInstructionVariable(InstructionTag tag) { + none() + } + + /** + * If the instruction specified by `tag` is a `FieldInstruction`, gets the + * `Field` for that instruction. + */ + Field getInstructionField(InstructionTag tag) { + none() + } + + /** + * If the instruction specified by `tag` is a `ConstantValueInstruction`, gets + * the constant value for that instruction. + */ + string getInstructionConstantValue(InstructionTag tag) { + none() + } + + /** + * If the instruction specified by `tag` is a `PointerArithmeticInstruction`, + * gets the size of the type pointed to by the pointer. + */ + 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. + */ + int getInstructionResultSize(InstructionTag tag) { + none() + } + + /** + * If the instruction specified by `tag` is a `StringConstantInstruction`, + * gets the `StringLiteral` for that instruction. + */ + StringLiteral getInstructionStringLiteral(InstructionTag tag) { + none() + } + + /** + * If the instruction specified by `tag` is a `CatchByTypeInstruction`, + * gets the type of the exception to be caught. + */ + Type getInstructionExceptionType(InstructionTag tag) { + none() + } + + /** + * If the instruction specified by `tag` is an `InheritanceConversionInstruction`, + * gets the inheritance relationship for that instruction. + */ + predicate getInstructionInheritance(InstructionTag tag, Class baseClass, + Class derivedClass) { + none() + } + + /** + * Gets the instruction whose result is consumed as an operand of the + * instruction specified by `tag`, with the operand specified by `operandTag`. + */ + Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) { + none() + } + + /** + * Gets the instruction generated by this element with tag `tag`. + */ + final Instruction getInstruction(InstructionTag tag) { + result.getAST() = getAST() and + result.getTag() = tag + } + + /** + * Gets the temporary variable generated by this element with tag `tag`. + */ + final IRTempVariable getTempVariable(TempVariableTag tag) { + result.getAST() = getAST() and + result.getTag() = tag + } + + /** + * Gets the parent element of this element. + */ + final TranslatedElement getParent() { + result.getAChild() = this + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/TranslatedExpr.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/TranslatedExpr.qll new file mode 100644 index 000000000000..c259067867cb --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/internal/TranslatedExpr.qll @@ -0,0 +1,2499 @@ +import cpp +private import InstructionTag +private import Opcode +private import TempVariableTag +private import TranslatedCondition +private import TranslatedElement +private import TranslatedFunction +private import TranslatedInitialization + +/** + * Gets the TranslatedExpr for the specified expression. If `expr` is a load, + * the result is the TranslatedExpr for the load portion. + */ +TranslatedExpr getTranslatedExpr(Expr expr) { + result.getExpr() = expr and + result.producesExprResult() +} + +abstract class TranslatedExpr extends TranslatedElement { + Expr expr; + + override final string toString() { + result = expr.toString() + } + + override final Locatable getAST() { + result = expr + } + + final Expr getExpr() { + result = expr + } + + override final Function getFunction() { + result = expr.getEnclosingFunction() + } + + final TranslatedFunction getEnclosingFunction() { + result = getTranslatedFunction(expr.getEnclosingFunction()) + } + + final Type getResultType() { + result = expr.getType().getUnspecifiedType() + } + + abstract Instruction getResult(); + + /** + * Holds if this `TranslatedExpr` produces the final result of the original + * expression from the AST. + * + * For example, in `y = x;`, the TranslatedLoad for the VariableAccess `x` + * produces the result of that VariableAccess expression, but the + * TranslatedVariableAccess for `x` does not. The TranslatedVariableAccess + * for `y` does produce its result, however, because there is no load on `y`. + */ + final predicate producesExprResult() { + // A load always produces the result of the expression. + this instanceof TranslatedLoad or + // If there's no load, then this is the only TranslatedExpr for this + // expression. + not expr.hasLValueToRValueConversion() or + // If we're supposed to ignore the load on this expression, then this + // is the only TranslatedExpr. + ignoreLoad(expr) + } + + /** + * Returns `true` if the result of this `TranslatedExpr` is a glvalue, or + * `false` if the result is a prvalue. + * + * This predicate returns a `boolean` value instead of just a being a plain + * predicate because all of the subclass predicates that call it require a + * `boolean` value. + */ + final boolean isResultGLValue() { + if( + expr.isGLValueCategory() or + // If this TranslatedExpr doesn't produce the result, then it must represent + // a glvalue that is then loaded by a TranslatedLoad. + not producesExprResult() + ) then + result = true + else + result = false + } +} + +class TranslatedConditionValue extends TranslatedExpr, ConditionContext, + TTranslatedConditionValue { + TranslatedConditionValue() { + this = TTranslatedConditionValue(expr) + } + + override TranslatedElement getChild(int id) { + id = 0 and result = getCondition() + } + + override Instruction getFirstInstruction() { + result = getCondition().getFirstInstruction() + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + ( + ( + tag = ConditionValueTrueTempAddressTag() or + tag = ConditionValueFalseTempAddressTag() or + tag = ConditionValueResultTempAddressTag() + ) and + opcode instanceof Opcode::VariableAddress and + resultType = getResultType() and + isGLValue = true + ) or + ( + ( + tag = ConditionValueTrueConstantTag() or + tag = ConditionValueFalseConstantTag() + ) and + opcode instanceof Opcode::Constant and + resultType = getResultType() and + isGLValue = isResultGLValue() + ) or + ( + ( + tag = ConditionValueTrueStoreTag() or + tag = ConditionValueFalseStoreTag() + ) and + opcode instanceof Opcode::Store and + resultType = getResultType() and + isGLValue = isResultGLValue() + ) or + ( + tag = ConditionValueResultLoadTag() and + opcode instanceof Opcode::Load and + resultType = getResultType() and + isGLValue = isResultGLValue() + ) + } + + override Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + kind instanceof GotoEdge and + ( + ( + tag = ConditionValueTrueTempAddressTag() and + result = getInstruction(ConditionValueTrueConstantTag()) + ) or + ( + tag = ConditionValueTrueConstantTag() and + result = getInstruction(ConditionValueTrueStoreTag()) + ) or + ( + tag = ConditionValueTrueStoreTag() and + result = getInstruction(ConditionValueResultTempAddressTag()) + ) or + ( + tag = ConditionValueFalseTempAddressTag() and + result = getInstruction(ConditionValueFalseConstantTag()) + ) or + ( + tag = ConditionValueFalseConstantTag() and + result = getInstruction(ConditionValueFalseStoreTag()) + ) or + ( + tag = ConditionValueFalseStoreTag() and + result = getInstruction(ConditionValueResultTempAddressTag()) + ) or + ( + tag = ConditionValueResultTempAddressTag() and + result = getInstruction(ConditionValueResultLoadTag()) + ) or + ( + tag = ConditionValueResultLoadTag() and + result = getParent().getChildSuccessor(this) + ) + ) + } + + override Instruction getInstructionOperand(InstructionTag tag, + OperandTag operandTag) { + ( + tag = ConditionValueTrueStoreTag() and + ( + ( + operandTag instanceof LoadStoreAddressOperand and + result = getInstruction(ConditionValueTrueTempAddressTag()) + ) or + ( + operandTag instanceof CopySourceOperand and + result = getInstruction(ConditionValueTrueConstantTag()) + ) + ) + ) or + ( + tag = ConditionValueFalseStoreTag() and + ( + ( + operandTag instanceof LoadStoreAddressOperand and + result = getInstruction(ConditionValueFalseTempAddressTag()) + ) or + ( + operandTag instanceof CopySourceOperand and + result = getInstruction(ConditionValueFalseConstantTag()) + ) + ) + ) or + ( + tag = ConditionValueResultLoadTag() and + ( + ( + operandTag instanceof LoadStoreAddressOperand and + result = getInstruction(ConditionValueResultTempAddressTag()) + ) or + ( + operandTag instanceof CopySourceOperand and + result = getEnclosingFunction().getUnmodeledDefinitionInstruction() + ) + ) + ) + } + + override predicate hasTempVariable(TempVariableTag tag, Type type) { + tag = ConditionValueTempVar() and + type = getResultType() + } + + override IRVariable getInstructionVariable(InstructionTag tag) { + ( + tag = ConditionValueTrueTempAddressTag() or + tag = ConditionValueFalseTempAddressTag() or + tag = ConditionValueResultTempAddressTag() + ) and + result = getTempVariable(ConditionValueTempVar()) + } + + override string getInstructionConstantValue(InstructionTag tag) { + tag = ConditionValueTrueConstantTag() and result = "1" or + tag = ConditionValueFalseConstantTag() and result = "0" + } + + override Instruction getResult() { + result = getInstruction(ConditionValueResultLoadTag()) + } + + override Instruction getChildSuccessor(TranslatedElement child) { + none() + } + + override Instruction getChildTrueSuccessor(TranslatedCondition child) { + child = getCondition() and + result = getInstruction(ConditionValueTrueTempAddressTag()) + } + + override Instruction getChildFalseSuccessor(TranslatedCondition child) { + child = getCondition() and + result = getInstruction(ConditionValueFalseTempAddressTag()) + } + + private TranslatedCondition getCondition() { + result = getTranslatedCondition(expr) + } +} + +class TranslatedLoad extends TranslatedExpr, TTranslatedLoad { + TranslatedLoad() { + this = TTranslatedLoad(expr) + } + + override Instruction getFirstInstruction() { + result = getOperand().getFirstInstruction() + } + + override TranslatedElement getChild(int id) { + id = 0 and result = getOperand() + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + tag = LoadTag() and + opcode instanceof Opcode::Load and + resultType = getResultType() and + isGLValue = isResultGLValue() + } + + override Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + tag = LoadTag() and + result = getParent().getChildSuccessor(this) and + kind instanceof GotoEdge + } + + override Instruction getChildSuccessor(TranslatedElement child) { + child = getOperand() and result = getInstruction(LoadTag()) + } + + override Instruction getResult() { + result = getInstruction(LoadTag()) + } + + override Instruction getInstructionOperand(InstructionTag tag, + OperandTag operandTag) { + tag = LoadTag() and + ( + ( + operandTag instanceof LoadStoreAddressOperand and + result = getOperand().getResult() + ) or + ( + operandTag instanceof CopySourceOperand and + result = getEnclosingFunction().getUnmodeledDefinitionInstruction() + ) + ) + } + + private TranslatedExpr getOperand() { + result.getExpr() = expr and not result instanceof TranslatedLoad + } +} + +class TranslatedCommaExpr extends TranslatedNonConstantExpr { + CommaExpr comma; + + TranslatedCommaExpr() { + comma = expr + } + + override Instruction getFirstInstruction() { + result = getLeftOperand().getFirstInstruction() + } + + override TranslatedElement getChild(int id) { + id = 0 and result = getLeftOperand() or + id = 1 and result = getRightOperand() + } + + override Instruction getResult() { + result = getRightOperand().getResult() + } + + override Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + none() + } + + override Instruction getChildSuccessor(TranslatedElement child) { + ( + child = getLeftOperand() and + result = getRightOperand().getFirstInstruction() + ) or + child = getRightOperand() and result = getParent().getChildSuccessor(this) + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + none() + } + + override Instruction getInstructionOperand(InstructionTag tag, + OperandTag operandTag) { + none() + } + + private TranslatedExpr getLeftOperand() { + result = getTranslatedExpr(comma.getLeftOperand().getFullyConverted()) + } + + private TranslatedExpr getRightOperand() { + result = getTranslatedExpr(comma.getRightOperand().getFullyConverted()) + } +} + +abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr { + CrementOperation op; + + TranslatedCrementOperation() { + op = expr + } + + override final TranslatedElement getChild(int id) { + id = 0 and result = getOperand() + } + + override final string getInstructionConstantValue(InstructionTag tag) { + tag = CrementConstantTag() and + exists(Type resultType | + resultType = getResultType() and + ( + resultType instanceof IntegralType and result = "1" or + resultType instanceof FloatingPointType and result = "1.0" or + resultType instanceof PointerType and result = "1" + ) + ) + } + + private Type getConstantType() { + exists(Type resultType | + resultType = getResultType() and + ( + resultType instanceof ArithmeticType and result = resultType or + resultType instanceof PointerType and result = getIntType() + ) + ) + } + + override final predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + isGLValue = false and + ( + ( + tag = CrementLoadTag() and + opcode instanceof Opcode::Load and + resultType = getResultType() + ) or + ( + tag = CrementConstantTag() and + opcode instanceof Opcode::Constant and + resultType = getConstantType() + ) or + ( + tag = CrementOpTag() and + opcode = getOpcode() and + resultType = getResultType() + ) or + ( + tag = CrementStoreTag() and + opcode instanceof Opcode::Store and + resultType = getResultType() + ) + ) + } + + override final Instruction getInstructionOperand(InstructionTag tag, + OperandTag operandTag) { + ( + tag = CrementLoadTag() and + ( + ( + operandTag instanceof LoadStoreAddressOperand and + result = getOperand().getResult() + ) or + ( + operandTag instanceof CopySourceOperand and + result = getEnclosingFunction().getUnmodeledDefinitionInstruction() + ) + ) + ) or + ( + tag = CrementOpTag() and + ( + ( + operandTag instanceof LeftOperand and + result = getInstruction(CrementLoadTag()) + ) or + ( + operandTag instanceof RightOperand and + result = getInstruction(CrementConstantTag()) + ) + ) + ) or + ( + tag = CrementStoreTag() and + ( + ( + operandTag instanceof LoadStoreAddressOperand and + result = getOperand().getResult() + ) or + ( + operandTag instanceof CopySourceOperand and + result = getInstruction(CrementOpTag()) + ) + ) + ) + } + + override final Instruction getFirstInstruction() { + result = getOperand().getFirstInstruction() + } + + override final Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + kind instanceof GotoEdge and + ( + ( + tag = CrementLoadTag() and + result = getInstruction(CrementConstantTag()) + ) or + ( + tag = CrementConstantTag() and + result = getInstruction(CrementOpTag()) + ) or + ( + tag = CrementOpTag() and + result = getInstruction(CrementStoreTag()) + ) or + ( + tag = CrementStoreTag() and + result = getParent().getChildSuccessor(this) + ) + ) + } + + override final Instruction getChildSuccessor(TranslatedElement child) { + child = getOperand() and result = getInstruction(CrementLoadTag()) + } + + override final int getInstructionElementSize(InstructionTag tag) { + tag = CrementOpTag() and + ( + getOpcode() instanceof Opcode::PointerAdd or + getOpcode() instanceof Opcode::PointerSub + ) and + result = max(getResultType().(PointerType).getBaseType().getSize()) + } + + final TranslatedExpr getOperand() { + result = getTranslatedExpr(op.getOperand().getFullyConverted()) + } + + final Opcode getOpcode() { + exists(Type resultType | + resultType = getResultType() and + ( + ( + op instanceof IncrementOperation and + if resultType instanceof PointerType then + result instanceof Opcode::PointerAdd + else + result instanceof Opcode::Add + ) or + ( + op instanceof DecrementOperation and + if resultType instanceof PointerType then + result instanceof Opcode::PointerSub + else + result instanceof Opcode::Sub + ) + ) + ) + } +} + +class TranslatedPrefixCrementOperation extends TranslatedCrementOperation { + TranslatedPrefixCrementOperation() { + op instanceof PrefixCrementOperation + } + + override Instruction getResult() { + if expr.isPRValueCategory() then ( + // If this is C, then the result of a prefix crement is a prvalue for the + // new value assigned to the operand. If this is C++, then the result is + // an lvalue, but that lvalue is being loaded as part of this expression. + // EDG doesn't mark this as a load. + result = getInstruction(CrementOpTag()) + ) + else ( + // This is C++, where the result is an lvalue for the operand, and that + // lvalue is not being loaded as part of this expression. + result = getOperand().getResult() + ) + } +} + +class TranslatedPostfixCrementOperation extends TranslatedCrementOperation { + TranslatedPostfixCrementOperation() { + op instanceof PostfixCrementOperation + } + + override Instruction getResult() { + // The result is a prvalue copy of the original value + result = getInstruction(CrementLoadTag()) + } +} + +class TranslatedArrayExpr extends TranslatedNonConstantExpr { + ArrayExpr arrayExpr; + + TranslatedArrayExpr() { + arrayExpr = expr + } + + override Instruction getFirstInstruction() { + result = getBaseOperand().getFirstInstruction() + } + + override final TranslatedElement getChild(int id) { + id = 0 and result = getBaseOperand() or + id = 1 and result = getOffsetOperand() + } + + override Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + tag = OnlyInstructionTag() and + result = getParent().getChildSuccessor(this) and + kind instanceof GotoEdge + } + + override Instruction getChildSuccessor(TranslatedElement child) { + ( + child = getBaseOperand() and + result = getOffsetOperand().getFirstInstruction() + ) or + ( + child = getOffsetOperand() and + result = getInstruction(OnlyInstructionTag()) + ) + } + + override Instruction getResult() { + result = getInstruction(OnlyInstructionTag()) + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + tag = OnlyInstructionTag() and + opcode instanceof Opcode::PointerAdd and + resultType = getBaseOperand().getResultType() and + isGLValue = false + } + + override Instruction getInstructionOperand(InstructionTag tag, + OperandTag operandTag) { + tag = OnlyInstructionTag() and + ( + ( + operandTag instanceof LeftOperand and + result = getBaseOperand().getResult() + ) or + ( + operandTag instanceof RightOperand and + result = getOffsetOperand().getResult() + ) + ) + } + + override int getInstructionElementSize(InstructionTag tag) { + tag = OnlyInstructionTag() and + result = max(getBaseOperand().getResultType().(PointerType).getBaseType().getSize()) + } + + private TranslatedExpr getBaseOperand() { + result = getTranslatedExpr(arrayExpr.getArrayBase().getFullyConverted()) + } + + private TranslatedExpr getOffsetOperand() { + result = getTranslatedExpr(arrayExpr.getArrayOffset().getFullyConverted()) + } +} + +abstract class TranslatedTransparentExpr extends TranslatedNonConstantExpr { + override final Instruction getFirstInstruction() { + result = getOperand().getFirstInstruction() + } + + override final TranslatedElement getChild(int id) { + id = 0 and result = getOperand() + } + + override final Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + none() + } + + override final Instruction getChildSuccessor(TranslatedElement child) { + child = getOperand() and result = getParent().getChildSuccessor(this) + } + + override final predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + none() + } + + override final Instruction getResult() { + result = getOperand().getResult() + } + + override final Instruction getInstructionOperand(InstructionTag tag, + OperandTag operandTag) { + none() + } + + abstract TranslatedExpr getOperand(); +} + +class TranslatedTransparentUnaryOperation extends TranslatedTransparentExpr { + UnaryOperation op; + + TranslatedTransparentUnaryOperation() { + op = expr and + ( + // *p is the same as p until the result is loaded. + expr instanceof PointerDereferenceExpr or + // &x is the same as x. &x isn't loadable, but is included + // here to avoid having two nearly identical classes. + expr instanceof AddressOfExpr + ) + } + + override TranslatedExpr getOperand() { + result = getTranslatedExpr(op.getOperand().getFullyConverted()) + } +} + +class TranslatedTransparentConversion extends TranslatedTransparentExpr { + Conversion conv; + + TranslatedTransparentConversion() { + conv = expr and + ( + conv instanceof ParenthesisExpr or + conv instanceof ReferenceDereferenceExpr or + conv instanceof ReferenceToExpr + ) + } + + override TranslatedExpr getOperand() { + result = getTranslatedExpr(conv.getExpr()) + } +} + +class TranslatedThisExpr extends TranslatedNonConstantExpr { + TranslatedThisExpr() { + expr instanceof ThisExpr + } + + override final TranslatedElement getChild(int id) { + none() + } + + override final predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + tag = OnlyInstructionTag() and + opcode instanceof Opcode::CopyValue and + resultType = expr.getType().getUnspecifiedType() and + isGLValue = false + } + + override final Instruction getResult() { + result = getInstruction(OnlyInstructionTag()) + } + + override final Instruction getFirstInstruction() { + result = getInstruction(OnlyInstructionTag()) + } + + override final Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + kind instanceof GotoEdge and + tag = OnlyInstructionTag() and + result = getParent().getChildSuccessor(this) + } + + override final Instruction getChildSuccessor(TranslatedElement child) { + none() + } + + override final Instruction getInstructionOperand(InstructionTag tag, + OperandTag operandTag) { + tag = OnlyInstructionTag() and + operandTag instanceof CopySourceOperand and + result = getInitializeThisInstruction() + } + + private Instruction getInitializeThisInstruction() { + result = getTranslatedFunction(expr.getEnclosingFunction()).getInitializeThisInstruction() + } +} + +abstract class TranslatedVariableAccess extends TranslatedNonConstantExpr { + VariableAccess access; + + TranslatedVariableAccess() { + access = expr + } + + override final TranslatedElement getChild(int id) { + id = 0 and result = getQualifier() // Might not exist + } + + final TranslatedExpr getQualifier() { + result = getTranslatedExpr(access.getQualifier().getFullyConverted()) + } + + override Instruction getResult() { + result = getInstruction(OnlyInstructionTag()) + } + + override final Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + tag = OnlyInstructionTag() and + result = getParent().getChildSuccessor(this) and + kind instanceof GotoEdge + } + + override final Instruction getChildSuccessor(TranslatedElement child) { + child = getQualifier() and result = getInstruction(OnlyInstructionTag()) + } +} + +class TranslatedNonFieldVariableAccess extends TranslatedVariableAccess { + TranslatedNonFieldVariableAccess() { + not expr instanceof FieldAccess + } + + override Instruction getFirstInstruction() { + if exists(getQualifier()) then + result = getQualifier().getFirstInstruction() + else + result = getInstruction(OnlyInstructionTag()) + } + + override Instruction getInstructionOperand(InstructionTag tag, + OperandTag operandTag) { + none() + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + tag = OnlyInstructionTag() and + opcode instanceof Opcode::VariableAddress and + resultType = getResultType() and + isGLValue = true + } + + override IRVariable getInstructionVariable(InstructionTag tag) { + tag = OnlyInstructionTag() and + result = getIRUserVariable(access.getEnclosingFunction(), + access.getTarget()) + } +} + +class TranslatedFieldAccess extends TranslatedVariableAccess { + FieldAccess fieldAccess; + + TranslatedFieldAccess() { + //REVIEW: Implicit 'this'? + fieldAccess = access + } + + override Instruction getFirstInstruction() { + result = getQualifier().getFirstInstruction() + } + + override Instruction getInstructionOperand(InstructionTag tag, + OperandTag operandTag) { + tag = OnlyInstructionTag() and + operandTag instanceof UnaryOperand and + result = getQualifier().getResult() + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + tag = OnlyInstructionTag() and + opcode instanceof Opcode::FieldAddress and + resultType = getResultType() and + isGLValue = true + } + + override Field getInstructionField(InstructionTag tag) { + tag = OnlyInstructionTag() and + result = access.getTarget() + } +} + +class TranslatedFunctionAccess extends TranslatedNonConstantExpr { + FunctionAccess access; + + TranslatedFunctionAccess() { + access = expr + } + + override TranslatedElement getChild(int id) { + none() + } + + override Instruction getFirstInstruction() { + result = getInstruction(OnlyInstructionTag()) + } + + override Instruction getResult() { + result = getInstruction(OnlyInstructionTag()) + } + + override final Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + tag = OnlyInstructionTag() and + result = getParent().getChildSuccessor(this) and + kind instanceof GotoEdge + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + tag = OnlyInstructionTag() and + opcode instanceof Opcode::FunctionAddress and + resultType = access.getType().getUnspecifiedType() and + isGLValue = true + } + + override Function getInstructionFunction(InstructionTag tag) { + tag = OnlyInstructionTag() and + result = access.getTarget() + } + + override Instruction getChildSuccessor(TranslatedElement child) { + none() + } +} + +abstract class TranslatedNonLoadExpr extends TranslatedExpr, + TTranslatedNonLoadExpr { + TranslatedNonLoadExpr() { + this = TTranslatedNonLoadExpr(expr) + } +} + +abstract class TranslatedNonConstantExpr extends TranslatedNonLoadExpr { + TranslatedNonConstantExpr() { + not expr.isConstant() + } +} + +abstract class TranslatedConstantExpr extends TranslatedNonLoadExpr { + TranslatedConstantExpr() { + expr.isConstant() + } + + override final Instruction getFirstInstruction() { + result = getInstruction(OnlyInstructionTag()) + } + + override final TranslatedElement getChild(int id) { + none() + } + + override final Instruction getResult() { + result = getInstruction(OnlyInstructionTag()) + } + + override final Instruction getInstructionOperand(InstructionTag tag, + OperandTag operandTag) { + none() + } + + override final predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + tag = OnlyInstructionTag() and + opcode = getOpcode() and + resultType = getResultType() and + if(expr.isGLValueCategory() or expr.hasLValueToRValueConversion()) then + isGLValue = true + else + isGLValue = false + } + + override final Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + tag = OnlyInstructionTag() and + result = getParent().getChildSuccessor(this) and + kind instanceof GotoEdge + } + + override final Instruction getChildSuccessor(TranslatedElement child) { + none() + } + + abstract Opcode getOpcode(); +} + +class TranslatedArithmeticLiteral extends TranslatedConstantExpr { + TranslatedArithmeticLiteral() { + not expr instanceof StringLiteral + } + + override string getInstructionConstantValue(InstructionTag tag) { + tag = OnlyInstructionTag() and + result = expr.getValue() + } + + override Opcode getOpcode() { + result instanceof Opcode::Constant + } +} + +class TranslatedStringLiteral extends TranslatedConstantExpr { + StringLiteral stringLiteral; + + TranslatedStringLiteral() { + stringLiteral = expr + } + + override StringLiteral getInstructionStringLiteral(InstructionTag tag) { + tag = OnlyInstructionTag() and + result = stringLiteral + } + + override Opcode getOpcode() { + result instanceof Opcode::StringConstant + } +} + +abstract class TranslatedValueExpr extends TranslatedNonConstantExpr { + abstract Opcode getOpcode(); + + override final predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + opcode = getOpcode() and + tag = OnlyInstructionTag() and + resultType = getResultType() and + isGLValue = isResultGLValue() + } + + override final Instruction getResult() { + result = getInstruction(OnlyInstructionTag()) + } +} + +class TranslatedUnaryExpr extends TranslatedValueExpr { + TranslatedUnaryExpr() { + expr instanceof NotExpr or + expr instanceof ComplementExpr or + expr instanceof UnaryPlusExpr or + expr instanceof UnaryMinusExpr + } + + override final Instruction getFirstInstruction() { + result = getOperand().getFirstInstruction() + } + + override final TranslatedElement getChild(int id) { + id = 0 and result = getOperand() + } + + override final Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + tag = OnlyInstructionTag() and + result = getParent().getChildSuccessor(this) and + kind instanceof GotoEdge + } + + override final Instruction getChildSuccessor(TranslatedElement child) { + child = getOperand() and result = getInstruction(OnlyInstructionTag()) + } + + override final Instruction getInstructionOperand(InstructionTag tag, + OperandTag operandTag) { + tag = OnlyInstructionTag() and + result = getOperand().getResult() and + if getOpcode() instanceof Opcode::CopyValue then + operandTag instanceof CopySourceOperand + else + operandTag instanceof UnaryOperand + } + + override final Opcode getOpcode() { + expr instanceof NotExpr and result instanceof Opcode::LogicalNot or + expr instanceof ComplementExpr and result instanceof Opcode::BitComplement or + expr instanceof UnaryPlusExpr and result instanceof Opcode::CopyValue or + expr instanceof UnaryMinusExpr and result instanceof Opcode::Negate + } + + private TranslatedExpr getOperand() { + result = getTranslatedExpr( + expr.(UnaryOperation).getOperand().getFullyConverted() + ) + } +} + +abstract class TranslatedConversion extends TranslatedNonConstantExpr { + Conversion conv; + + TranslatedConversion() { + conv = expr + } + + override Instruction getFirstInstruction() { + result = getOperand().getFirstInstruction() + } + + override final TranslatedElement getChild(int id) { + id = 0 and result = getOperand() + } + + final TranslatedExpr getOperand() { + result = getTranslatedExpr(expr.(Conversion).getExpr()) + } +} + +/** + * Represents the translation of a conversion expression that generates a + * single instruction. + */ +abstract class TranslatedSingleInstructionConversion extends TranslatedConversion { + override Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + tag = OnlyInstructionTag() and + result = getParent().getChildSuccessor(this) and + kind instanceof GotoEdge + } + + override Instruction getChildSuccessor(TranslatedElement child) { + child = getOperand() and result = getInstruction(OnlyInstructionTag()) + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + tag = OnlyInstructionTag() and + opcode = getOpcode() and + resultType = getResultType() and + isGLValue = isResultGLValue() + } + + override Instruction getResult() { + result = getInstruction(OnlyInstructionTag()) + } + + override Instruction getInstructionOperand(InstructionTag tag, + OperandTag operandTag) { + tag = OnlyInstructionTag() and + operandTag instanceof UnaryOperand and + result = getOperand().getResult() + } + + /** + * Gets the opcode of the generated instruction. + */ + abstract Opcode getOpcode(); +} + +/** + * Represents the translation of a conversion expression that generates a + * `Convert` instruction. + */ +class TranslatedSimpleConversion extends TranslatedSingleInstructionConversion { + TranslatedSimpleConversion() { + conv instanceof ArithmeticConversion or + conv instanceof PointerConversion or + conv instanceof PointerToMemberConversion or + conv instanceof PointerToIntegralConversion or + conv instanceof IntegralToPointerConversion or + conv instanceof GlvalueConversion or + conv instanceof ArrayToPointerConversion or + conv instanceof PrvalueAdjustmentConversion + } + + override Opcode getOpcode() { + result instanceof Opcode::Convert + } +} + +/** + * Represents the translation of a dynamic_cast expression. + */ +class TranslatedDynamicCast extends TranslatedSingleInstructionConversion { + TranslatedDynamicCast() { + conv instanceof DynamicCast + } + + override Opcode getOpcode() { + exists(Type resultType | + resultType = getResultType() and + if resultType instanceof PointerType then ( + if resultType.(PointerType).getBaseType() instanceof VoidType then + result instanceof Opcode::DynamicCastToVoid + else + result instanceof Opcode::CheckedConvertOrNull + ) + else + result instanceof Opcode::CheckedConvertOrThrow + ) + } +} + +/** + * Represents the translation of a `BaseClassConversion` or `DerivedClassConversion` + * expression. + */ +class TranslatedInheritanceConversion extends TranslatedSingleInstructionConversion { + InheritanceConversion inheritanceConv; + + TranslatedInheritanceConversion() { + inheritanceConv = conv + } + + override predicate getInstructionInheritance(InstructionTag tag, Class baseClass, + Class derivedClass) { + tag = OnlyInstructionTag() and + baseClass = inheritanceConv.getBaseClass() and + derivedClass = inheritanceConv.getDerivedClass() + } + + override Opcode getOpcode() { + if inheritanceConv instanceof BaseClassConversion then ( + if inheritanceConv.(BaseClassConversion).isVirtual() then + result instanceof Opcode::ConvertToVirtualBase + else + result instanceof Opcode::ConvertToBase + ) + else + result instanceof Opcode::ConvertToDerived + } +} + +/** + * Represents the translation of a `BoolConversion` expression, which generates + * a comparison with zero. + */ +class TranslatedBoolConversion extends TranslatedConversion { + TranslatedBoolConversion() { + conv instanceof BoolConversion + } + + override Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + kind instanceof GotoEdge and + ( + ( + tag = BoolConversionConstantTag() and + result = getInstruction(BoolConversionCompareTag()) + ) or + ( + tag = BoolConversionCompareTag() and + result = getParent().getChildSuccessor(this) + ) + ) + } + + override Instruction getChildSuccessor(TranslatedElement child) { + child = getOperand() and result = getInstruction(BoolConversionConstantTag()) + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + isGLValue = false and + ( + ( + tag = BoolConversionConstantTag() and + opcode instanceof Opcode::Constant and + resultType = getOperand().getResultType() + ) or + ( + tag = BoolConversionCompareTag() and + opcode instanceof Opcode::CompareNE and + resultType instanceof BoolType + ) + ) + } + + override Instruction getResult() { + result = getInstruction(BoolConversionCompareTag()) + } + + override Instruction getInstructionOperand(InstructionTag tag, + OperandTag operandTag) { + tag = BoolConversionCompareTag() and + ( + ( + operandTag instanceof LeftOperand and + result = getOperand().getResult() + ) or + ( + operandTag instanceof RightOperand and + result = getInstruction(BoolConversionConstantTag()) + ) + ) + } + + override string getInstructionConstantValue(InstructionTag tag) { + tag = BoolConversionConstantTag() and + result = "0" + } +} + +private Opcode binaryBitwiseOpcode(BinaryBitwiseOperation expr) { + expr instanceof LShiftExpr and result instanceof Opcode::ShiftLeft or + expr instanceof RShiftExpr and result instanceof Opcode::ShiftRight or + expr instanceof BitwiseAndExpr and result instanceof Opcode::BitAnd or + expr instanceof BitwiseOrExpr and result instanceof Opcode::BitOr or + expr instanceof BitwiseXorExpr and result instanceof Opcode::BitXor +} + +private Opcode binaryArithmeticOpcode(BinaryArithmeticOperation expr) { + expr instanceof AddExpr and result instanceof Opcode::Add or + expr instanceof SubExpr and result instanceof Opcode::Sub or + expr instanceof MulExpr and result instanceof Opcode::Mul or + expr instanceof DivExpr and result instanceof Opcode::Div or + expr instanceof RemExpr and result instanceof Opcode::Rem or + expr instanceof PointerAddExpr and result instanceof Opcode::PointerAdd or + expr instanceof PointerSubExpr and result instanceof Opcode::PointerSub or + expr instanceof PointerDiffExpr and result instanceof Opcode::PointerDiff +} + +private Opcode comparisonOpcode(ComparisonOperation expr) { + expr instanceof EQExpr and result instanceof Opcode::CompareEQ or + expr instanceof NEExpr and result instanceof Opcode::CompareNE or + expr instanceof LTExpr and result instanceof Opcode::CompareLT or + expr instanceof GTExpr and result instanceof Opcode::CompareGT or + expr instanceof LEExpr and result instanceof Opcode::CompareLE or + expr instanceof GEExpr and result instanceof Opcode::CompareGE +} + +class TranslatedBinaryOperation extends TranslatedValueExpr { + TranslatedBinaryOperation() { + expr instanceof BinaryArithmeticOperation or + expr instanceof BinaryBitwiseOperation or + expr instanceof ComparisonOperation + } + + override Instruction getFirstInstruction() { + result = getLeftOperand().getFirstInstruction() + } + + override final TranslatedElement getChild(int id) { + id = 0 and result = getLeftOperand() or + id = 1 and result = getRightOperand() + } + + override final Instruction getInstructionOperand(InstructionTag tag, + OperandTag operandTag) { + tag = OnlyInstructionTag() and + if swapOperandsOnOp() then ( + ( + operandTag instanceof RightOperand and + result = getLeftOperand().getResult() + ) or + ( + operandTag instanceof LeftOperand and + result = getRightOperand().getResult() + ) + ) + else ( + ( + operandTag instanceof LeftOperand and + result = getLeftOperand().getResult() + ) or + ( + operandTag instanceof RightOperand and + result = getRightOperand().getResult() + ) + ) + } + + override Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + tag = OnlyInstructionTag() and + result = getParent().getChildSuccessor(this) and + kind instanceof GotoEdge + } + + override Instruction getChildSuccessor(TranslatedElement child) { + ( + child = getLeftOperand() and + result = getRightOperand().getFirstInstruction() + ) or + ( + child = getRightOperand() and + result = getInstruction(OnlyInstructionTag()) + ) + } + + override Opcode getOpcode() { + result = binaryArithmeticOpcode(expr.(BinaryArithmeticOperation)) or + result = binaryBitwiseOpcode(expr.(BinaryBitwiseOperation)) or + result = comparisonOpcode(expr.(ComparisonOperation)) + } + + override int getInstructionElementSize(InstructionTag tag) { + tag = OnlyInstructionTag() and + exists(Opcode opcode | + opcode = getOpcode() and + ( + opcode instanceof Opcode::PointerAdd or opcode instanceof Opcode::PointerSub or opcode instanceof Opcode::PointerDiff + ) and + result = max(getPointerOperand().getResultType().(PointerType).getBaseType().getSize()) + ) + } + + private TranslatedExpr getPointerOperand() { + if swapOperandsOnOp() then + result = getRightOperand() + else + result = getLeftOperand() + } + + private predicate swapOperandsOnOp() { + // Swap the operands on a pointer add 'i + p', so that the pointer operand + // always comes first. Note that we still evaluate the operands + // left-to-right. + exists(PointerAddExpr ptrAdd, Type rightType | + ptrAdd = expr and + rightType = ptrAdd.getRightOperand().getType().getUnspecifiedType() and + rightType instanceof PointerType + ) + } + + private TranslatedExpr getLeftOperand() { + result = getTranslatedExpr( + expr.(BinaryOperation).getLeftOperand().getFullyConverted() + ) + } + + private TranslatedExpr getRightOperand() { + result = getTranslatedExpr( + expr.(BinaryOperation).getRightOperand().getFullyConverted() + ) + } +} + +abstract class TranslatedAssignment extends TranslatedNonConstantExpr { + Assignment assign; + + TranslatedAssignment() { + expr = assign + } + + override final TranslatedElement getChild(int id) { + id = 0 and result = getLeftOperand() or + id = 1 and result = getRightOperand() + } + + override final Instruction getFirstInstruction() { + // Evaluation is right-to-left + result = getRightOperand().getFirstInstruction() + } + + override final Instruction getResult() { + if expr.isPRValueCategory() then ( + // If this is C, then the result of an assignment is a prvalue for the new + // value assigned to the left operand. If this is C++, then the result is + // an lvalue, but that lvalue is being loaded as part of this expression. + // EDG doesn't mark this as a load. + result = getStoredValue() + ) + else ( + // This is C++, where the result is an lvalue for the left operand, + // and that lvalue is not being loaded as part of this expression. + result = getLeftOperand().getResult() + ) + } + + abstract Instruction getStoredValue(); + + final TranslatedExpr getLeftOperand() { + result = getTranslatedExpr( + assign.getLValue().getFullyConverted() + ) + } + + final TranslatedExpr getRightOperand() { + result = getTranslatedExpr( + assign.getRValue().getFullyConverted() + ) + } +} + +class TranslatedAssignExpr extends TranslatedAssignment { + TranslatedAssignExpr() { + assign instanceof AssignExpr + } + + override Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + tag = AssignmentStoreTag() and + result = getParent().getChildSuccessor(this) and + kind instanceof GotoEdge + } + + override Instruction getChildSuccessor(TranslatedElement child) { + // Operands are evaluated right-to-left. + ( + child = getRightOperand() and + result = getLeftOperand().getFirstInstruction() + ) or + ( + child = getLeftOperand() and + result = getInstruction(AssignmentStoreTag()) + ) + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + tag = AssignmentStoreTag() and + opcode instanceof Opcode::Store and + resultType = getResultType() and + isGLValue = false + } + + override Instruction getInstructionOperand(InstructionTag tag, + OperandTag operandTag) { + tag = AssignmentStoreTag() and + ( + ( + operandTag instanceof LoadStoreAddressOperand and + result = getLeftOperand().getResult() + ) or + ( + operandTag instanceof CopySourceOperand and + result = getRightOperand().getResult() + ) + ) + } + + override Instruction getStoredValue() { + result = getRightOperand().getResult() + } +} + +class TranslatedAssignOperation extends TranslatedAssignment { + AssignOperation assignOp; + + TranslatedAssignOperation() { + expr = assignOp + } + + override Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + kind instanceof GotoEdge and + ( + ( + tag = AssignOperationLoadTag() and + if leftOperandNeedsConversion() then + result = getInstruction(AssignOperationConvertLeftTag()) + else + result = getInstruction(AssignOperationOpTag()) + ) or + ( + tag = AssignOperationConvertLeftTag() and + result = getInstruction(AssignOperationOpTag()) + ) or + ( + tag = AssignOperationOpTag() and + if leftOperandNeedsConversion() then + result = getInstruction(AssignOperationConvertResultTag()) + else + result = getInstruction(AssignmentStoreTag()) + ) or + ( + tag = AssignOperationConvertResultTag() and + result = getInstruction(AssignmentStoreTag()) + ) or + ( + tag = AssignmentStoreTag() and + result = getParent().getChildSuccessor(this) + ) + ) + } + + override Instruction getChildSuccessor(TranslatedElement child) { + // Operands are evaluated right-to-left. + ( + child = getRightOperand() and + result = getLeftOperand().getFirstInstruction() + ) or + ( + child = getLeftOperand() and + result = getInstruction(AssignOperationLoadTag()) + ) + } + + override Instruction getStoredValue() { + if leftOperandNeedsConversion() then + result = getInstruction(AssignOperationConvertResultTag()) + else + result = getInstruction(AssignOperationOpTag()) + } + + private Type getConvertedLeftOperandType() { + if(assignOp instanceof AssignLShiftExpr or + assignOp instanceof AssignRShiftExpr or + assignOp instanceof AssignPointerAddExpr or + assignOp instanceof AssignPointerSubExpr) then ( + // No need to convert for a shift. Technically, the left side should + // undergo integral promotion, and then the result would be converted back + // to the destination type. There's not much point to this, though, + // because the result will be the same for any well-defined program + // anyway. If we really want to model this case perfectly, we'll need the + // extractor to tell us what the promoted type of the left operand would + // be. + result = getLeftOperand().getResultType() + ) else ( + // The right operand has already been converted to the type of the op. + result = getRightOperand().getResultType() + ) + } + + private predicate leftOperandNeedsConversion() { + getConvertedLeftOperandType() != getLeftOperand().getResultType() + } + + private Opcode getOpcode() { + assignOp instanceof AssignAddExpr and result instanceof Opcode::Add or + assignOp instanceof AssignSubExpr and result instanceof Opcode::Sub or + assignOp instanceof AssignMulExpr and result instanceof Opcode::Mul or + assignOp instanceof AssignDivExpr and result instanceof Opcode::Div or + assignOp instanceof AssignRemExpr and result instanceof Opcode::Rem or + assignOp instanceof AssignAndExpr and result instanceof Opcode::BitAnd or + assignOp instanceof AssignOrExpr and result instanceof Opcode::BitOr or + assignOp instanceof AssignXorExpr and result instanceof Opcode::BitXor or + assignOp instanceof AssignLShiftExpr and result instanceof Opcode::ShiftLeft or + assignOp instanceof AssignRShiftExpr and result instanceof Opcode::ShiftRight or + assignOp instanceof AssignPointerAddExpr and result instanceof Opcode::PointerAdd or + assignOp instanceof AssignPointerSubExpr and result instanceof Opcode::PointerSub + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + isGLValue = false and + ( + ( + tag = AssignOperationLoadTag() and + opcode instanceof Opcode::Load and + resultType = getLeftOperand().getResultType() + ) or + ( + tag = AssignOperationOpTag() and + opcode = getOpcode() and + resultType = getConvertedLeftOperandType() + ) or + ( + tag = AssignmentStoreTag() and + opcode instanceof Opcode::Store and + resultType = getResultType() + ) or + ( + leftOperandNeedsConversion() and + opcode instanceof Opcode::Convert and + ( + ( + tag = AssignOperationConvertLeftTag() and + resultType = getConvertedLeftOperandType() + ) or + ( + tag = AssignOperationConvertResultTag() and + resultType = getLeftOperand().getResultType() + ) + ) + ) + ) + } + + override int getInstructionElementSize(InstructionTag tag) { + tag = AssignOperationOpTag() and + exists(Opcode opcode | + opcode = getOpcode() and + (opcode instanceof Opcode::PointerAdd or opcode instanceof Opcode::PointerSub) + ) and + result = max(getResultType().(PointerType).getBaseType().getSize()) + } + + override Instruction getInstructionOperand(InstructionTag tag, + OperandTag operandTag) { + ( + tag = AssignOperationLoadTag() and + ( + ( + operandTag instanceof LoadStoreAddressOperand and + result = getLeftOperand().getResult() + ) or + ( + operandTag instanceof CopySourceOperand and + result = getEnclosingFunction().getUnmodeledDefinitionInstruction() + ) + ) + ) or + ( + leftOperandNeedsConversion() and + tag = AssignOperationConvertLeftTag() and + operandTag instanceof UnaryOperand and + result = getInstruction(AssignOperationLoadTag()) + ) or + ( + tag = AssignOperationOpTag() and + ( + ( + operandTag instanceof LeftOperand and + if leftOperandNeedsConversion() then + result = getInstruction(AssignOperationConvertLeftTag()) + else + result = getInstruction(AssignOperationLoadTag()) + ) or + ( + operandTag instanceof RightOperand and + result = getRightOperand().getResult() + ) + ) + ) or + ( + leftOperandNeedsConversion() and + tag = AssignOperationConvertResultTag() and + operandTag instanceof UnaryOperand and + result = getInstruction(AssignOperationOpTag()) + ) or + ( + tag = AssignmentStoreTag() and + ( + ( + operandTag instanceof LoadStoreAddressOperand and + result = getLeftOperand().getResult() + ) or + ( + operandTag instanceof CopySourceOperand and + result = getStoredValue() + ) + ) + ) + } +} + +/** + * Represents the IR translation of a call to a function. + */ +abstract class TranslatedCall extends TranslatedNonConstantExpr { + Call call; + + TranslatedCall() { + expr = call + } + + override final Instruction getFirstInstruction() { + if exists(getQualifier()) then + result = getQualifier().getFirstInstruction() + else + result = getFirstCallTargetInstruction() + } + + override final Instruction getResult() { + result = getInstruction(CallTag()) + } + + override final TranslatedElement getChild(int id) { + // We choose the child's id in the order of evaluation. + // The qualifier is evaluated before the call target, because the value of + // the call target may depend on the value of the qualifier for virtual + // calls. + id = -2 and result = getQualifier() or + id = -1 and result = getCallTarget() or + result = getArgument(id) + } + + override Instruction getChildSuccessor(TranslatedElement child) { + ( + child = getQualifier() and + result = getFirstCallTargetInstruction() + ) or + ( + child = getCallTarget() and + result = getFirstArgumentOrCallInstruction() + ) or + exists(int argIndex | + child = getArgument(argIndex) and + if exists(getArgument(argIndex + 1)) then + result = getArgument(argIndex + 1).getFirstInstruction() + else + result = getInstruction(CallTag()) + ) + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + tag = CallTag() and + opcode instanceof Opcode::Invoke and + resultType = call.getType().getUnspecifiedType() and + isGLValue = false + } + + override Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + kind instanceof GotoEdge and + tag = CallTag() and + result = getParent().getChildSuccessor(this) + } + + override Instruction getInstructionOperand(InstructionTag tag, + OperandTag operandTag) { + tag = CallTag() and + ( + ( + operandTag instanceof CallTargetOperand and + result = getCallTargetResult() + ) or + ( + operandTag instanceof ThisArgumentOperand and + result = getQualifierResult() + ) or + exists(PositionalArgumentOperand argTag | + argTag = operandTag and + result = getArgument(argTag.getArgIndex()).getResult() + ) + ) + } + + /** + * Holds if the call has any arguments, not counting the `this` argument. + */ + final predicate hasArguments() { + exists(call.getArgument(0)) + } + + /** + * Holds if the call has a `this` argument. + */ + predicate hasQualifier() { + exists(getQualifier()) + } + + /** + * Gets the `TranslatedExpr` for the indirect target of the call, if any. + */ + TranslatedExpr getCallTarget() { + none() + } + + /** + * Gets the first instruction of the sequence to evaluate the call target. + * By default, this is just the first instruction of `getCallTarget()`, but + * it can be overridden by a subclass for cases where there is a call target + * that is not computed from an expression (e.g. a direct call). + */ + Instruction getFirstCallTargetInstruction() { + result = getCallTarget().getFirstInstruction() + } + + /** + * Gets the instruction whose result value is the target of the call. By + * default, this is just the result of `getCallTarget()`, but it can be + * overridden by a subclass for cases where there is a call target that is not + * computed from an expression (e.g. a direct call). + */ + Instruction getCallTargetResult() { + result = getCallTarget().getResult() + } + + /** + * Gets the `TranslatedExpr` for the qualifier of the call (i.e. the value + * that is passed as the `this` argument. + */ + final TranslatedExpr getQualifier() { + result = getTranslatedExpr(call.getQualifier().getFullyConverted()) + } + + /** + * Gets the instruction whose result value is the `this` argument of the call. + * By default, this is just the result of `getQualifier()`, but it can be + * overridden by a subclass for cases where there is a `this` argument that is + * not computed from a child expression (e.g. a constructor call). + */ + Instruction getQualifierResult() { + result = getQualifier().getResult() + } + + /** + * Gets the argument with the specified `index`. Does not include the `this` + * argument. + */ + final TranslatedExpr getArgument(int index) { + result = getTranslatedExpr(call.getArgument(index).getFullyConverted()) + } + + /** + * If there are any arguments, gets the first instruction of the first + * argument. Otherwise, returns the call instruction. + */ + final Instruction getFirstArgumentOrCallInstruction() { + if hasArguments() then + result = getArgument(0).getFirstInstruction() + else + result = getInstruction(CallTag()) + } +} + +/** + * Represents the IR translation of a call through a function pointer. + */ +class TranslatedExprCall extends TranslatedCall { + ExprCall exprCall; + + TranslatedExprCall() { + expr = exprCall + } + + override TranslatedExpr getCallTarget() { + result = getTranslatedExpr(exprCall.getExpr().getFullyConverted()) + } +} + +/** + * Represents the IR translation of a direct function call. + */ +class TranslatedFunctionCall extends TranslatedCall { + FunctionCall funcCall; + + TranslatedFunctionCall() { + expr = funcCall + } + + override final Instruction getFirstCallTargetInstruction() { + result = getInstruction(CallTargetTag()) + } + + override final Instruction getCallTargetResult() { + result = getInstruction(CallTargetTag()) + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + super.hasInstruction(opcode, tag, resultType, isGLValue) or + ( + tag = CallTargetTag() and + opcode instanceof Opcode::FunctionAddress and + resultType instanceof BoolType and //HACK + isGLValue = false + ) + } + + override Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + result = super.getInstructionSuccessor(tag, kind) or + ( + tag = CallTargetTag() and + kind instanceof GotoEdge and + result = getFirstArgumentOrCallInstruction() + ) + } + + override Function getInstructionFunction(InstructionTag tag) { + tag = CallTargetTag() and result = funcCall.getTarget() + } +} + +/** + * Abstract class implemented by any `TranslatedElement` that has a child + * expression that is a call to a constructor or destructor, in order to + * provide a pointer to the object being constructed or destroyed. + */ +abstract class StructorCallContext extends TranslatedElement { + /** + * Gets the instruction whose result value is the address of the object to be + * constructed or destroyed. + */ + abstract Instruction getReceiver(); +} + +/** + * Represents the IR translation of a call to a constructor. + */ +class TranslatedStructorCall extends TranslatedFunctionCall { + TranslatedStructorCall() { + funcCall instanceof ConstructorCall or + funcCall instanceof DestructorCall + } + + override Instruction getQualifierResult() { + exists(StructorCallContext context | + context = getParent() and + result = context.getReceiver() + ) + } + + override predicate hasQualifier() { + any() + } +} + +/** + * Represents the IR translation of the destruction of a field from within + * the destructor of the field's declaring class. + */ +class TranslatedDestructorFieldDestruction extends TranslatedNonConstantExpr, + StructorCallContext { + DestructorFieldDestruction destruction; + + TranslatedDestructorFieldDestruction() { + destruction = expr + } + + override final TranslatedElement getChild(int id) { + id = 0 and result = getDestructorCall() + } + + override final predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + tag = OnlyInstructionTag() and + opcode instanceof Opcode::FieldAddress and + resultType = destruction.getTarget().getType().getUnspecifiedType() and + isGLValue = true + } + + override final Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + tag = OnlyInstructionTag() and + kind instanceof GotoEdge and + result = getDestructorCall().getFirstInstruction() + } + + override final Instruction getChildSuccessor(TranslatedElement child) { + child = getDestructorCall() and + result = getParent().getChildSuccessor(this) + } + + override final Instruction getResult() { + none() + } + + override final Instruction getFirstInstruction() { + result = getInstruction(OnlyInstructionTag()) + } + + override final Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) { + tag = OnlyInstructionTag() and + operandTag instanceof UnaryOperand and + result = getTranslatedFunction(destruction.getEnclosingFunction()).getInitializeThisInstruction() + } + + override final Field getInstructionField(InstructionTag tag) { + tag = OnlyInstructionTag() and + result = destruction.getTarget() + } + + override final Instruction getReceiver() { + result = getInstruction(OnlyInstructionTag()) + } + + private TranslatedExpr getDestructorCall() { + result = getTranslatedExpr(destruction.getExpr()) + } +} + +class TranslatedConditionalExpr extends TranslatedNonConstantExpr, + ConditionContext { + ConditionalExpr condExpr; + + TranslatedConditionalExpr() { + condExpr = expr + } + + override final TranslatedElement getChild(int id) { + id = 0 and result = getCondition() or + id = 1 and result = getThen() or + id = 2 and result = getElse() + } + + override Instruction getFirstInstruction() { + result = getCondition().getFirstInstruction() + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + not resultIsVoid() and + ( + ( + ( + (not thenIsVoid() and (tag = ConditionValueTrueTempAddressTag())) or + (not elseIsVoid() and (tag = ConditionValueFalseTempAddressTag())) or + tag = ConditionValueResultTempAddressTag() + ) and + opcode instanceof Opcode::VariableAddress and + resultType = getResultType() and + isGLValue = true + ) or + ( + ( + (not thenIsVoid() and (tag = ConditionValueTrueStoreTag())) or + (not elseIsVoid() and (tag = ConditionValueFalseStoreTag())) + ) and + opcode instanceof Opcode::Store and + resultType = getResultType() and + isGLValue = false + ) or + ( + tag = ConditionValueResultLoadTag() and + opcode instanceof Opcode::Load and + resultType = getResultType() and + isGLValue = isResultGLValue() + ) + ) + } + + override Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + not resultIsVoid() and + kind instanceof GotoEdge and + ( + ( + not thenIsVoid() and + ( + ( + tag = ConditionValueTrueTempAddressTag() and + result = getInstruction(ConditionValueTrueStoreTag()) + ) or + ( + tag = ConditionValueTrueStoreTag() and + result = getInstruction(ConditionValueResultTempAddressTag()) + ) + ) + ) or + ( + not elseIsVoid() and + ( + ( + tag = ConditionValueFalseTempAddressTag() and + result = getInstruction(ConditionValueFalseStoreTag()) + ) or + ( + tag = ConditionValueFalseStoreTag() and + result = getInstruction(ConditionValueResultTempAddressTag()) + ) + ) + ) or + ( + tag = ConditionValueResultTempAddressTag() and + result = getInstruction(ConditionValueResultLoadTag()) + ) or + ( + tag = ConditionValueResultLoadTag() and + result = getParent().getChildSuccessor(this) + ) + ) + } + + override Instruction getInstructionOperand(InstructionTag tag, + OperandTag operandTag) { + not resultIsVoid() and + ( + ( + not thenIsVoid() and + tag = ConditionValueTrueStoreTag() and + ( + ( + operandTag instanceof LoadStoreAddressOperand and + result = getInstruction(ConditionValueTrueTempAddressTag()) + ) or + ( + operandTag instanceof CopySourceOperand and + result = getThen().getResult() + ) + ) + ) or + ( + not elseIsVoid() and + tag = ConditionValueFalseStoreTag() and + ( + ( + operandTag instanceof LoadStoreAddressOperand and + result = getInstruction(ConditionValueFalseTempAddressTag()) + ) or + ( + operandTag instanceof CopySourceOperand and + result = getElse().getResult() + ) + ) + ) or + ( + tag = ConditionValueResultLoadTag() and + ( + ( + operandTag instanceof LoadStoreAddressOperand and + result = getInstruction(ConditionValueResultTempAddressTag()) + ) or + ( + operandTag instanceof CopySourceOperand and + result = getEnclosingFunction().getUnmodeledDefinitionInstruction() + ) + ) + ) + ) + } + + override predicate hasTempVariable(TempVariableTag tag, Type type) { + not resultIsVoid() and + tag = ConditionValueTempVar() and + type = getResultType() + } + + override IRVariable getInstructionVariable(InstructionTag tag) { + not resultIsVoid() and + ( + tag = ConditionValueTrueTempAddressTag() or + tag = ConditionValueFalseTempAddressTag() or + tag = ConditionValueResultTempAddressTag() + ) and + result = getTempVariable(ConditionValueTempVar()) + } + + override Instruction getResult() { + not resultIsVoid() and + result = getInstruction(ConditionValueResultLoadTag()) + } + + override Instruction getChildSuccessor(TranslatedElement child) { + ( + child = getThen() and + if thenIsVoid() then + result = getParent().getChildSuccessor(this) + else + result = getInstruction(ConditionValueTrueTempAddressTag()) + ) or + ( + child = getElse() and + if elseIsVoid() then + result = getParent().getChildSuccessor(this) + else + result = getInstruction(ConditionValueFalseTempAddressTag()) + ) + } + + override Instruction getChildTrueSuccessor(TranslatedCondition child) { + child = getCondition() and + result = getThen().getFirstInstruction() + } + + override Instruction getChildFalseSuccessor(TranslatedCondition child) { + child = getCondition() and + result = getElse().getFirstInstruction() + } + + private TranslatedCondition getCondition() { + result = getTranslatedCondition(condExpr.getCondition().getFullyConverted()) + } + + private TranslatedExpr getThen() { + result = getTranslatedExpr(condExpr.getThen().getFullyConverted()) + } + + private TranslatedExpr getElse() { + result = getTranslatedExpr(condExpr.getElse().getFullyConverted()) + } + + private predicate thenIsVoid() { + getThen().getResultType() instanceof VoidType or + // A `ThrowExpr.getType()` incorrectly returns the type of exception being + // thrown, rather than `void`. Handle that case here. + condExpr.getThen() instanceof ThrowExpr + } + + private predicate elseIsVoid() { + getElse().getResultType() instanceof VoidType or + // A `ThrowExpr.getType()` incorrectly returns the type of exception being + // thrown, rather than `void`. Handle that case here. + condExpr.getElse() instanceof ThrowExpr + } + + private predicate resultIsVoid() { + getResultType() instanceof VoidType + } +} + +/** + * IR translation of a `throw` expression. + */ +abstract class TranslatedThrowExpr extends TranslatedNonConstantExpr { + ThrowExpr throw; + + TranslatedThrowExpr() { + throw = expr + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + tag = ThrowTag() and + opcode = getThrowOpcode() and + resultType instanceof VoidType and + isGLValue = false + } + + override Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + tag = ThrowTag() and + kind instanceof ExceptionEdge and + result = getParent().getExceptionSuccessorInstruction() + } + + override Instruction getResult() { + none() + } + + abstract Opcode getThrowOpcode(); +} + +/** + * IR translation of a `throw` expression with an argument + * (e.g. `throw std::bad_alloc()`). + */ +class TranslatedThrowValueExpr extends TranslatedThrowExpr, + InitializationContext { + TranslatedThrowValueExpr() { + not throw instanceof ReThrowExpr + } + + override TranslatedElement getChild(int id) { + id = 0 and result = getInitialization() + } + + override Instruction getFirstInstruction() { + result = getInstruction(InitializerVariableAddressTag()) + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + TranslatedThrowExpr.super.hasInstruction(opcode, tag, resultType, isGLValue) or + tag = InitializerVariableAddressTag() and + opcode instanceof Opcode::VariableAddress and + resultType = getExceptionType() and + isGLValue = true + } + + override Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + result = TranslatedThrowExpr.super.getInstructionSuccessor(tag, kind) or + ( + tag = InitializerVariableAddressTag() and + result = getInitialization().getFirstInstruction() and + kind instanceof GotoEdge + ) + } + + override Instruction getChildSuccessor(TranslatedElement child) { + child = getInitialization() and + result = getInstruction(ThrowTag()) + } + + override IRVariable getInstructionVariable(InstructionTag tag) { + tag = InitializerVariableAddressTag() and + result = getIRTempVariable(throw, ThrowTempVar()) + } + + override final predicate hasTempVariable(TempVariableTag tag, Type type) { + tag = ThrowTempVar() and + type = getExceptionType() + } + + override final Instruction getInstructionOperand(InstructionTag tag, + OperandTag operandTag) { + tag = ThrowTag() and + ( + ( + operandTag instanceof LoadStoreAddressOperand and + result = getInstruction(InitializerVariableAddressTag()) + ) or + ( + operandTag instanceof ExceptionOperand and + result = getEnclosingFunction().getUnmodeledDefinitionInstruction() + ) + ) + } + + override Instruction getTargetAddress() { + result = getInstruction(InitializerVariableAddressTag()) + } + + override Type getTargetType() { + result = getExceptionType() + } + + TranslatedInitialization getInitialization() { + result = getTranslatedInitialization( + throw.getExpr().getFullyConverted()) + } + + override final Opcode getThrowOpcode() { + result instanceof Opcode::ThrowValue + } + + private Type getExceptionType() { + result = throw.getType().getUnspecifiedType() + } +} + +/** + * IR translation of a `throw` expression with no argument (e.g. `throw;`). + */ +class TranslatedReThrowExpr extends TranslatedThrowExpr { + TranslatedReThrowExpr() { + throw instanceof ReThrowExpr + } + + override TranslatedElement getChild(int id) { + none() + } + + override Instruction getFirstInstruction() { + result = getInstruction(ThrowTag()) + } + + override Instruction getChildSuccessor(TranslatedElement child) { + none() + } + + override final Opcode getThrowOpcode() { + result instanceof Opcode::ReThrow + } +} + +/** + * The IR translation of a built-in operation (i.e. anything that extends + * `BuiltInOperation`). + */ +abstract class TranslatedBuiltInOperation extends TranslatedNonConstantExpr { + override final Instruction getResult() { + result = getInstruction(OnlyInstructionTag()) + } + + override final Instruction getFirstInstruction() { + if exists(getChild(0)) then + result = getChild(0).getFirstInstruction() + else + result = getInstruction(OnlyInstructionTag()) + } + + override final TranslatedElement getChild(int id) { + result = getTranslatedExpr(expr.getChild(id).getFullyConverted()) + } + + override final Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + tag = OnlyInstructionTag() and + kind instanceof GotoEdge and + result = getParent().getChildSuccessor(this) + } + + override final Instruction getChildSuccessor(TranslatedElement child) { + exists(int id | + child = getChild(id) and + ( + result = getChild(id + 1).getFirstInstruction() or + not exists(getChild(id + 1)) and result = getInstruction(OnlyInstructionTag()) + ) + ) + } + + override final predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + tag = OnlyInstructionTag() and + opcode = getOpcode() and + resultType = getResultType() and + isGLValue = isResultGLValue() + } + + override final Instruction getInstructionOperand(InstructionTag tag, + OperandTag operandTag) { + tag = OnlyInstructionTag() and + exists(int index | + operandTag = positionalArgumentOperand(index) and + result = getChild(index).(TranslatedExpr).getResult() + ) + } + + abstract Opcode getOpcode(); +} + +/** + * The IR translation of a `BuiltInVarArgsStart` expression. + */ +class TranslatedVarArgsStart extends TranslatedBuiltInOperation { + TranslatedVarArgsStart() { + expr instanceof BuiltInVarArgsStart + } + + override final Opcode getOpcode() { + result instanceof Opcode::VarArgsStart + } +} + +/** + * The IR translation of a `BuiltInVarArgsEnd` expression. + */ +class TranslatedVarArgsEnd extends TranslatedBuiltInOperation { + TranslatedVarArgsEnd() { + expr instanceof BuiltInVarArgsEnd + } + + override final Opcode getOpcode() { + result instanceof Opcode::VarArgsEnd + } +} + +/** + * The IR translation of a `BuiltInVarArg` expression. + */ +class TranslatedVarArg extends TranslatedBuiltInOperation { + TranslatedVarArg() { + expr instanceof BuiltInVarArg + } + + override final Opcode getOpcode() { + result instanceof Opcode::VarArg + } +} + +/** + * The IR translation of a `BuiltInVarArgCopy` expression. + */ +class TranslatedVarArgCopy extends TranslatedBuiltInOperation { + TranslatedVarArgCopy() { + expr instanceof BuiltInVarArgCopy + } + + override final Opcode getOpcode() { + result instanceof Opcode::VarArgCopy + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/TranslatedFunction.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/TranslatedFunction.qll new file mode 100644 index 000000000000..a13815fdbb14 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/internal/TranslatedFunction.qll @@ -0,0 +1,549 @@ +import cpp +import semmle.code.cpp.ir.IR +private import InstructionTag +private import Opcode +private import TempVariableTag +private import TranslatedElement +private import TranslatedExpr +private import TranslatedInitialization +private import TranslatedStmt + +/** + * Gets the `TranslatedFunction` that represents function `func`. + */ +TranslatedFunction getTranslatedFunction(Function func) { + result.getAST() = func +} + +/** + * Represents the IR translation of a function. This is the root elements for + * all other elements associated with this function. + */ +class TranslatedFunction extends TranslatedElement, + TTranslatedFunction { + Function func; + + TranslatedFunction() { + this = TTranslatedFunction(func) + } + + override final string toString() { + result = func.toString() + } + + override final Locatable getAST() { + result = func + } + + /** + * Gets the function being translated. + */ + override final Function getFunction() { + result = func + } + + override final TranslatedElement getChild(int id) { + id = -3 and result = getConstructorInitList() or + id = -2 and result = getBody() or + id = -1 and result = getDestructorDestructionList() or + id >= 0 and result = getParameter(id) + } + + private final TranslatedConstructorInitList getConstructorInitList() { + result = getTranslatedConstructorInitList(func) + } + + private final TranslatedDestructorDestructionList getDestructorDestructionList() { + result = getTranslatedDestructorDestructionList(func) + } + + private final TranslatedStmt getBody() { + result = getTranslatedStmt(func.getEntryPoint()) + } + + private final TranslatedParameter getParameter(int index) { + result = getTranslatedParameter(func.getParameter(index)) + } + + override final Instruction getFirstInstruction() { + result = getInstruction(EnterFunctionTag()) + } + + override final Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + kind instanceof GotoEdge and + ( + ( + tag = EnterFunctionTag() and + result = getInstruction(UnmodeledDefinitionTag()) + ) or + ( + tag = UnmodeledDefinitionTag() and + if exists(getThisType()) then + result = getInstruction(InitializeThisTag()) + else if exists(getParameter(0)) then + result = getParameter(0).getFirstInstruction() + else + result = getBody().getFirstInstruction() + ) or + ( + tag = InitializeThisTag() and + if exists(getParameter(0)) then + result = getParameter(0).getFirstInstruction() + else + result = getConstructorInitList().getFirstInstruction() + ) or + ( + tag = ReturnValueAddressTag() and + result = getInstruction(ReturnTag()) + ) or + ( + tag = ReturnTag() and + result = getInstruction(UnmodeledUseTag()) + ) or + ( + tag = UnwindTag() and + result = getInstruction(UnmodeledUseTag()) + ) or + ( + tag = UnmodeledUseTag() and + result = getInstruction(ExitFunctionTag()) + ) + ) + } + + override final Instruction getChildSuccessor(TranslatedElement child) { + exists(int paramIndex | + child = getParameter(paramIndex) and + if exists(func.getParameter(paramIndex + 1)) then + result = getParameter(paramIndex + 1).getFirstInstruction() + else + result = getConstructorInitList().getFirstInstruction() + ) or + ( + child = getConstructorInitList() and + result = getBody().getFirstInstruction() + ) or + ( + child = getBody() and + result = getReturnSuccessorInstruction() + ) or + ( + child = getDestructorDestructionList() and + if getReturnType() instanceof VoidType then + result = getInstruction(ReturnTag()) + else + result = getInstruction(ReturnValueAddressTag()) + ) + } + + override final predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + ( + ( + tag = EnterFunctionTag() and + opcode instanceof Opcode::EnterFunction and + resultType instanceof VoidType and + isGLValue = false + ) or + ( + tag = UnmodeledDefinitionTag() and + opcode instanceof Opcode::UnmodeledDefinition and + resultType instanceof UnknownType and + isGLValue = false + ) or + ( + tag = InitializeThisTag() and + opcode instanceof Opcode::InitializeThis and + resultType = getThisType() and + isGLValue = true + ) or + ( + tag = ReturnValueAddressTag() and + opcode instanceof Opcode::VariableAddress and + resultType = getReturnType() and + not resultType instanceof VoidType and + isGLValue = true + ) or + ( + tag = ReturnTag() and + resultType instanceof VoidType and + isGLValue = false and + if getReturnType() instanceof VoidType then + opcode instanceof Opcode::ReturnVoid + else + opcode instanceof Opcode::ReturnValue + ) or + ( + tag = UnwindTag() and + opcode instanceof Opcode::Unwind and + resultType instanceof VoidType and + isGLValue = false and + ( + // Only generate the `Unwind` instruction if there is any exception + // handling present in the function. + exists(TryStmt try | + try.getEnclosingFunction() = func + ) or + exists(ThrowExpr throw | + throw.getEnclosingFunction() = func + ) + ) + ) or + ( + tag = UnmodeledUseTag() and + opcode instanceof Opcode::UnmodeledUse and + resultType instanceof VoidType and + isGLValue = false + ) or + ( + tag = ExitFunctionTag() and + opcode instanceof Opcode::ExitFunction and + resultType instanceof VoidType and + isGLValue = false + ) + ) + } + + override final Instruction getExceptionSuccessorInstruction() { + result = getInstruction(UnwindTag()) + } + + override final Instruction getInstructionOperand(InstructionTag tag, + OperandTag operandTag) { + ( + tag = UnmodeledUseTag() and + operandTag instanceof UnmodeledUseOperand and + result.getFunction() = func and + result.hasMemoryResult() + ) or + ( + tag = ReturnTag() and + not getReturnType() instanceof VoidType and + ( + ( + operandTag instanceof LoadStoreAddressOperand and + result = getInstruction(ReturnValueAddressTag()) + ) or + ( + operandTag instanceof ReturnValueOperand and + result = getUnmodeledDefinitionInstruction() + ) + ) + ) + } + + override final IRVariable getInstructionVariable(InstructionTag tag) { + tag = ReturnValueAddressTag() and + result = getReturnVariable() + } + + override final predicate hasTempVariable(TempVariableTag tag, Type type) { + tag = ReturnValueTempVar() and + type = getReturnType() and + not type instanceof VoidType + } + + /** + * Gets the instruction to which control should flow after a `return` + * statement. + */ + final Instruction getReturnSuccessorInstruction() { + result = getDestructorDestructionList().getFirstInstruction() + } + + /** + * Gets the variable that represents the return value of this function. + */ + final IRReturnVariable getReturnVariable() { + result = getIRTempVariable(func, ReturnValueTempVar()) + } + + /** + * Gets the single `UnmodeledDefinition` instruction for this function. + */ + final Instruction getUnmodeledDefinitionInstruction() { + result = getInstruction(UnmodeledDefinitionTag()) + } + + /** + * Gets the single `InitializeThis` instruction for this function. Holds only + * if the function is an instance member function, constructor, or destructor. + */ + final Instruction getInitializeThisInstruction() { + result = getInstruction(InitializeThisTag()) + } + + /** + * Gets the type pointed to by the `this` pointer for this function (i.e. `*this`). + * Holds only if the function is an instance member function, constructor, or destructor. + */ + final Type getThisType() { + exists(MemberFunction mfunc | + mfunc = func and + not mfunc.isStatic() and + result = mfunc.getDeclaringType() + ) + } + + private final Type getReturnType() { + result = func.getType().getUnspecifiedType() + } +} + +/** + * Gets the `TranslatedParameter` that represents parameter `param`. + */ +TranslatedParameter getTranslatedParameter(Parameter param) { + result.getAST() = param +} + +/** + * Represents the IR translation of a function parameter, including the + * initialization of that parameter with the incoming argument. + */ +class TranslatedParameter extends TranslatedElement, TTranslatedParameter { + Parameter param; + + TranslatedParameter() { + this = TTranslatedParameter(param) + } + + override final string toString() { + result = param.toString() + } + + override final Locatable getAST() { + result = param + } + + override final Function getFunction() { + result = param.getFunction() or + result = param.getCatchBlock().getEnclosingFunction() + } + + override final Instruction getFirstInstruction() { + result = getInstruction(ParameterInitializerTag()) + } + + override final TranslatedElement getChild(int id) { + none() + } + + override final Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + kind instanceof GotoEdge and + ( + ( + tag = ParameterInitializerTag() and + result = getInstruction(InitializerVariableAddressTag()) + ) or + ( + tag = InitializerVariableAddressTag() and + result = getInstruction(InitializerStoreTag()) + ) or + ( + tag = InitializerStoreTag() and + result = getParent().getChildSuccessor(this) + ) + ) + } + + override final Instruction getChildSuccessor(TranslatedElement child) { + none() + } + + override final predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + ( + tag = ParameterInitializerTag() and + opcode instanceof Opcode::InitializeParameter and + resultType = param.getType().getUnspecifiedType() and + isGLValue = false + ) or + ( + tag = InitializerVariableAddressTag() and + opcode instanceof Opcode::VariableAddress and + resultType = param.getType().getUnspecifiedType() and + isGLValue = true + ) or + ( + tag = InitializerStoreTag() and + opcode instanceof Opcode::Store and + resultType = param.getType().getUnspecifiedType() and + isGLValue = false + ) + } + + override final IRVariable getInstructionVariable(InstructionTag tag) { + ( + tag = ParameterInitializerTag() or + tag = InitializerVariableAddressTag() + ) and + result = getIRUserVariable(getFunction(), param) + } + + override final Instruction getInstructionOperand(InstructionTag tag, + OperandTag operandTag) { + tag = InitializerStoreTag() and + ( + ( + operandTag instanceof LoadStoreAddressOperand and + result = getInstruction(InitializerVariableAddressTag()) + ) or + ( + operandTag instanceof CopySourceOperand and + result = getInstruction(ParameterInitializerTag()) + ) + ) + } +} + +private TranslatedConstructorInitList +getTranslatedConstructorInitList(Function func) { + result.getAST() = func +} + +/** + * Represents the IR translation of a constructor initializer list. To simplify + * the implementation of `TranslatedFunction`, a `TranslatedConstructorInitList` + * exists for every function, not just for constructors. Of course, only the + * instances for constructors can actually contain initializers. + */ +class TranslatedConstructorInitList extends TranslatedElement, + InitializationContext, TTranslatedConstructorInitList { + Function func; + + TranslatedConstructorInitList() { + this = TTranslatedConstructorInitList(func) + } + + override string toString() { + result = "ctor init: " + func.toString() + } + + override Locatable getAST() { + result = func + } + + override TranslatedElement getChild(int id) { + exists(ConstructorFieldInit fieldInit | + fieldInit = func.(Constructor).getInitializer(id) and + result = getTranslatedConstructorFieldInitialization(fieldInit) + ) or + exists(ConstructorBaseInit baseInit | + baseInit = func.(Constructor).getInitializer(id) and + result = getTranslatedConstructorBaseInit(baseInit) + ) + } + + override Instruction getFirstInstruction() { + if exists(getChild(0)) then + result = getChild(0).getFirstInstruction() + else + result = getParent().getChildSuccessor(this) + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + none() + } + + override Function getFunction() { + result = func + } + + override Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + none() + } + + override Instruction getChildSuccessor(TranslatedElement child) { + exists(int id | + child = getChild(id) and + if exists(getChild(id + 1)) then + result = getChild(id + 1).getFirstInstruction() + else + result = getParent().getChildSuccessor(this) + ) + } + + override Instruction getTargetAddress() { + result = getTranslatedFunction(func).getInitializeThisInstruction() + } + + override Type getTargetType() { + result = getTranslatedFunction(func).getThisType() + } +} + +private TranslatedDestructorDestructionList +getTranslatedDestructorDestructionList(Function func) { + result.getAST() = func +} + +/** + * Represents the IR translation of a destructor's implicit calls to destructors + * for fields and base classes. To simplify the implementation of `TranslatedFunction`, + * a `TranslatedDestructorDestructionList` exists for every function, not just for + * destructors. Of course, only the instances for destructors can actually contain + * destructions. + */ +class TranslatedDestructorDestructionList extends TranslatedElement, + TTranslatedDestructorDestructionList { + Function func; + + TranslatedDestructorDestructionList() { + this = TTranslatedDestructorDestructionList(func) + } + + override string toString() { + result = "dtor destruction: " + func.toString() + } + + override Locatable getAST() { + result = func + } + + override TranslatedElement getChild(int id) { + exists(DestructorFieldDestruction fieldDestruction | + fieldDestruction = func.(Destructor).getDestruction(id) and + result = getTranslatedExpr(fieldDestruction) + ) or + exists(DestructorBaseDestruction baseDestruction | + baseDestruction = func.(Destructor).getDestruction(id) and + result = getTranslatedDestructorBaseDestruction(baseDestruction) + ) + } + + override Instruction getFirstInstruction() { + if exists(getChild(0)) then + result = getChild(0).getFirstInstruction() + else + result = getParent().getChildSuccessor(this) + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + none() + } + + override Function getFunction() { + result = func + } + + override Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + none() + } + + override Instruction getChildSuccessor(TranslatedElement child) { + exists(int id | + child = getChild(id) and + if exists(getChild(id + 1)) then + result = getChild(id + 1).getFirstInstruction() + else + result = getParent().getChildSuccessor(this) + ) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/TranslatedInitialization.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/TranslatedInitialization.qll new file mode 100644 index 000000000000..8116b2cb302c --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/internal/TranslatedInitialization.qll @@ -0,0 +1,1074 @@ +import cpp +private import InstructionTag +private import Opcode +private import TranslatedElement +private import TranslatedExpr +private import TranslatedFunction + +/** + * Gets the `TranslatedInitialization` for the expression `expr`. + */ +TranslatedInitialization getTranslatedInitialization(Expr expr) { + result.getExpr() = expr +} + +/** + * Base class for any `TranslatedElement` that has an initialization as a child. + * Provides the child with the address and type of the location to be + * initialized. + */ +abstract class InitializationContext extends TranslatedElement { + /** + * Gets the instruction that produces the address of the location to be + * initialized. + */ + abstract Instruction getTargetAddress(); + + /** + * Gets the type of the location to be initialized. + */ + abstract Type getTargetType(); +} + +/** + * Represents the IR translation of any initialization, whether from an + * initializer list or from a direct initializer. + */ +abstract class TranslatedInitialization extends TranslatedElement, + TTranslatedInitialization { + Expr expr; + + TranslatedInitialization() { + this = TTranslatedInitialization(expr) + } + + override final string toString() { + result = "init: " + expr.toString() + } + + override final Function getFunction() { + result = expr.getEnclosingFunction() + } + + override final Locatable getAST() { + result = expr + } + + /** + * Gets the expression that is doing the initialization. + */ + final Expr getExpr() { + result = expr + } + + /** + * Gets the initialization context that describes the location being + * initialized. + */ + final InitializationContext getContext() { + result = getParent() + } + + final TranslatedFunction getEnclosingFunction() { + result = getTranslatedFunction(expr.getEnclosingFunction()) + } +} + +/** + * Represents the IR translation of an initialization from an initializer list. + */ +abstract class TranslatedListInitialization extends TranslatedInitialization, + InitializationContext { + override Instruction getFirstInstruction() { + result = getChild(0).getFirstInstruction() + } + + override Instruction getChildSuccessor(TranslatedElement child) { + exists(int index | + child = getChild(index) and + if exists(getChild(index + 1)) then + result = getChild(index + 1).getFirstInstruction() + else + result = getParent().getChildSuccessor(this) + ) + } + + override final predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + none() + } + + override final Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + none() + } + + override Instruction getTargetAddress() { + result = getContext().getTargetAddress() + } + + override Type getTargetType() { + result = getContext().getTargetType() + } +} + +/** + * Represents the IR translation of an initialization of a class object from an + * initializer list. + */ +class TranslatedClassListInitialization extends + TranslatedListInitialization +{ + ClassAggregateLiteral initList; + + TranslatedClassListInitialization() { + initList = expr + } + + override TranslatedElement getChild(int id) { + exists(TranslatedFieldInitialization fieldInit | + result = fieldInit and + fieldInit = getTranslatedFieldInitialization(initList, _) and + fieldInit.getOrder() = id + ) + } +} + +/** + * Represents the IR translation of an initialization of an array from an + * initializer list. + */ +class TranslatedArrayListInitialization extends + TranslatedListInitialization { + ArrayAggregateLiteral initList; + + TranslatedArrayListInitialization() { + initList = expr + } + + override TranslatedElement getChild(int id) { + result = getTranslatedElementInitialization(initList, id) + } +} + +/** + * Represents the IR translation of an initialization from a single initializer + * expression. + */ +abstract class TranslatedDirectInitialization extends TranslatedInitialization { + TranslatedDirectInitialization() { + not expr instanceof AggregateLiteral + } + + override TranslatedElement getChild(int id) { + id = 0 and result = getInitializer() + } + + override Instruction getFirstInstruction() { + result = getInitializer().getFirstInstruction() + } + + final TranslatedExpr getInitializer() { + result = getTranslatedExpr(expr) + } +} + +/** + * Represents the IR translation of an initialization from a single initializer + * expression, where the initialization is performed via bitwise copy (as + * opposed to a constructor). + */ +class TranslatedSimpleDirectInitialization extends + TranslatedDirectInitialization { + TranslatedSimpleDirectInitialization() { + not expr instanceof ConstructorCall and + not expr instanceof StringLiteral + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + tag = InitializerStoreTag() and + opcode instanceof Opcode::Store and + resultType = getContext().getTargetType() and + isGLValue = false + } + + override Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + tag = InitializerStoreTag() and + result = getParent().getChildSuccessor(this) and + kind instanceof GotoEdge + } + + override Instruction getChildSuccessor(TranslatedElement child) { + child = getInitializer() and result = getInstruction(InitializerStoreTag()) + } + + override Instruction getInstructionOperand(InstructionTag tag, + OperandTag operandTag) { + tag = InitializerStoreTag() and + ( + ( + operandTag instanceof LoadStoreAddressOperand and + result = getContext().getTargetAddress() + ) or + ( + operandTag instanceof CopySourceOperand and + result = getInitializer().getResult() + ) + ) + } +} + +/** + * Represents the IR translation of an initialization of an array from a string + * literal. + */ +class TranslatedStringLiteralInitialization extends + TranslatedDirectInitialization { + TranslatedStringLiteralInitialization() { + expr instanceof StringLiteral + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + ( + // Load the string literal to make it a prvalue of type `char[len]` + tag = InitializerLoadStringTag() and + opcode instanceof Opcode::Load and + resultType = getInitializer().getResultType() and + isGLValue = false + ) or + ( + // Store the string into the target. + tag = InitializerStoreTag() and + opcode instanceof Opcode::Store and + resultType = getInitializer().getResultType() and + isGLValue = false + ) or + exists(int startIndex, int elementCount | + // If the initializer string isn't large enough to fill the target, then + // we have to generate another instruction sequence to store a constant + // zero into the remainder of the array. + zeroInitRange(startIndex, elementCount) and + ( + ( + // Create a constant zero whose size is the size of the remaining + // space in the target array. + tag = ZeroPadStringConstantTag() and + opcode instanceof Opcode::Constant and + resultType instanceof UnknownType and + isGLValue = false + ) or + ( + // The index of the first element to be zero initialized. + tag = ZeroPadStringElementIndexTag() and + opcode instanceof Opcode::Constant and + resultType = getIntType() and + isGLValue = false + ) or + ( + // Compute the address of the first element to be zero initialized. + tag = ZeroPadStringElementAddressTag() and + opcode instanceof Opcode::PointerAdd and + resultType = getElementType() and + isGLValue = true + ) or + ( + // Store the constant zero into the remainder of the string. + tag = ZeroPadStringStoreTag() and + opcode instanceof Opcode::Store and + resultType instanceof UnknownType and + isGLValue = false + ) + ) + ) + } + + override Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + kind instanceof GotoEdge and + ( + ( + tag = InitializerLoadStringTag() and + result = getInstruction(InitializerStoreTag()) + ) or + if zeroInitRange(_, _) then ( + ( + tag = InitializerStoreTag() and + result = getInstruction(ZeroPadStringConstantTag()) + ) or + ( + tag = ZeroPadStringConstantTag() and + result = getInstruction(ZeroPadStringElementIndexTag()) + ) or + ( + tag = ZeroPadStringElementIndexTag() and + result = getInstruction(ZeroPadStringElementAddressTag()) + ) or + ( + tag = ZeroPadStringElementAddressTag() and + result = getInstruction(ZeroPadStringStoreTag()) + ) or + ( + tag = ZeroPadStringStoreTag() and + result = getParent().getChildSuccessor(this) + ) + ) + else ( + tag = InitializerStoreTag() and + result = getParent().getChildSuccessor(this) + ) + ) + } + + override Instruction getChildSuccessor(TranslatedElement child) { + child = getInitializer() and result = getInstruction(InitializerLoadStringTag()) + } + + override Instruction getInstructionOperand(InstructionTag tag, + OperandTag operandTag) { + ( + tag = InitializerLoadStringTag() and + ( + ( + operandTag instanceof LoadStoreAddressOperand and + result = getInitializer().getResult() + ) or + ( + operandTag instanceof CopySourceOperand and + result = getEnclosingFunction().getUnmodeledDefinitionInstruction() + ) + ) + ) or + ( + tag = InitializerStoreTag() and + ( + ( + operandTag instanceof LoadStoreAddressOperand and + result = getContext().getTargetAddress() + ) or + ( + operandTag instanceof CopySourceOperand and + result = getInstruction(InitializerLoadStringTag()) + ) + ) + ) or + ( + tag = ZeroPadStringElementAddressTag() and + ( + ( + operandTag instanceof LeftOperand and + result = getContext().getTargetAddress() + ) or + ( + operandTag instanceof RightOperand and + result = getInstruction(ZeroPadStringElementIndexTag()) + ) + ) + ) or + ( + tag = ZeroPadStringStoreTag() and + ( + ( + operandTag instanceof LoadStoreAddressOperand and + result = getInstruction(ZeroPadStringElementAddressTag()) + ) or + ( + operandTag instanceof CopySourceOperand and + result = getInstruction(ZeroPadStringConstantTag()) + ) + ) + ) + } + + override string getInstructionConstantValue(InstructionTag tag) { + exists(int startIndex | + zeroInitRange(startIndex, _) and + ( + ( + tag = ZeroPadStringConstantTag() and + result = "0" + ) or + ( + tag = ZeroPadStringElementIndexTag() and + result = startIndex.toString() + ) + ) + ) + } + + 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().(ArrayType).getBaseType().getUnspecifiedType() + } + + /** + * Holds if the `elementCount` array elements starting at `startIndex` must be + * zero initialized. + */ + private predicate zeroInitRange(int startIndex, int elementCount) { + exists(int targetCount | + startIndex = expr.getType().getUnspecifiedType().(ArrayType).getArraySize() and + targetCount = getContext().getTargetType().(ArrayType).getArraySize() and + elementCount = targetCount - startIndex and + elementCount > 0 + ) + } +} + +class TranslatedConstructorInitialization extends + TranslatedDirectInitialization, StructorCallContext { + ConstructorCall ctorCall; + + TranslatedConstructorInitialization() { + ctorCall = expr + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + none() + } + + override Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + none() + } + + override Instruction getChildSuccessor(TranslatedElement child) { + child = getInitializer() and result = getParent().getChildSuccessor(this) + } + + override Instruction getInstructionOperand(InstructionTag tag, + OperandTag operandTag) { + none() + } + + override Instruction getReceiver() { + result = getContext().getTargetAddress() + } +} + +/** + * Gets the `TranslatedFieldInitialization` for field `field` within initializer + * list `initList`. + */ +TranslatedFieldInitialization getTranslatedFieldInitialization( + ClassAggregateLiteral initList, Field field) { + result.getAST() = initList and result.getField() = field +} + +TranslatedFieldInitialization getTranslatedConstructorFieldInitialization( + ConstructorFieldInit init) { + result.getAST() = init +} + +/** + * Represents the IR translation of the initialization of a field from an + * element of an initializer list. + */ +abstract class TranslatedFieldInitialization extends TranslatedElement { + Expr ast; + Field field; + + override final string toString() { + result = ast.toString() + "." + field.toString() + } + + override final Locatable getAST() { + result = ast + } + + override final Function getFunction() { + result = ast.getEnclosingFunction() + } + + override final Instruction getFirstInstruction() { + result = getInstruction(getFieldAddressTag()) + } + + /** + * Gets the zero-based index describing the order in which this field is to be + * initialized relative to the other fields in the class. + */ + final int getOrder() { + result = field.getInitializationOrder() + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + tag = getFieldAddressTag() and + opcode instanceof Opcode::FieldAddress and + resultType = field.getType().getUnspecifiedType() and + isGLValue = true + } + + override Instruction getInstructionOperand(InstructionTag tag, + OperandTag operandTag) { + tag = getFieldAddressTag() and + operandTag instanceof UnaryOperand and + result = getParent().(InitializationContext).getTargetAddress() + } + + override Field getInstructionField(InstructionTag tag) { + tag = getFieldAddressTag() and result = field + } + + final InstructionTag getFieldAddressTag() { + result = InitializerFieldAddressTag(field) + } + + final Field getField() { + result = field + } +} + +/** + * Represents the IR translation of the initialization of a field from an + * explicit element in an initializer list. + */ +class TranslatedExplicitFieldInitialization extends + TranslatedFieldInitialization, InitializationContext, + TTranslatedExplicitFieldInitialization { + Expr expr; + + TranslatedExplicitFieldInitialization() { + this = TTranslatedExplicitFieldInitialization(ast, field, expr) + } + + override Instruction getTargetAddress() { + result = getInstruction(getFieldAddressTag()) + } + + override Type getTargetType() { + result = field.getType().getUnspecifiedType() + } + + override Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + tag = getFieldAddressTag() and + result = getInitialization().getFirstInstruction() and + kind instanceof GotoEdge + } + + override Instruction getChildSuccessor(TranslatedElement child) { + child = getInitialization() and result = getParent().getChildSuccessor(this) + } + + override TranslatedElement getChild(int id) { + id = 0 and result = getInitialization() + } + + private TranslatedInitialization getInitialization() { + result = getTranslatedInitialization(expr) + } +} + +private string getZeroValue(Type type) { + if type instanceof FloatingPointType then + result = "0.0" + else + result = "0" +} + +/** + * Represents the IR translation of the initialization of a field without a + * corresponding element in the initializer list. + */ +class TranslatedFieldValueInitialization extends + TranslatedFieldInitialization, TTranslatedFieldValueInitialization { + TranslatedFieldValueInitialization() { + this = TTranslatedFieldValueInitialization(ast, field) + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + TranslatedFieldInitialization.super.hasInstruction(opcode, tag, resultType, isGLValue) or + ( + tag = getFieldDefaultValueTag() and + opcode instanceof Opcode::Constant and + resultType = field.getType().getUnspecifiedType() and + isGLValue = false + ) or + ( + tag = getFieldDefaultValueStoreTag() and + opcode instanceof Opcode::Store and + resultType = field.getType().getUnspecifiedType() and + isGLValue = false + ) + } + + override Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + kind instanceof GotoEdge and + ( + ( + tag = getFieldAddressTag() and + result = getInstruction(getFieldDefaultValueTag()) + ) or + ( + tag = getFieldDefaultValueTag() and + result = getInstruction(getFieldDefaultValueStoreTag()) + ) or + ( + tag = getFieldDefaultValueStoreTag() and + result = getParent().getChildSuccessor(this) + ) + ) + } + + override string getInstructionConstantValue(InstructionTag tag) { + tag = getFieldDefaultValueTag() and + result = getZeroValue(field.getType().getUnspecifiedType()) + } + + override Instruction getInstructionOperand(InstructionTag tag, + OperandTag operandTag) { + result = TranslatedFieldInitialization.super.getInstructionOperand(tag, operandTag) or + ( + tag = getFieldDefaultValueStoreTag() and + ( + ( + operandTag instanceof LoadStoreAddressOperand and + result = getInstruction(getFieldAddressTag()) + ) or + ( + operandTag instanceof CopySourceOperand and + result = getInstruction(getFieldDefaultValueTag()) + ) + ) + ) + } + + override Instruction getChildSuccessor(TranslatedElement child) { + none() + } + + override TranslatedElement getChild(int id) { + none() + } + + private InstructionTag getFieldDefaultValueTag() { + result = InitializerFieldDefaultValueTag(field) + } + + private InstructionTag getFieldDefaultValueStoreTag() { + result = InitializerFieldDefaultValueStoreTag(field) + } +} + +/** + * Gets the `TranslatedElementInitialization` for element `elementIndex` in + * initializer list `initList`. + */ +TranslatedElementInitialization getTranslatedElementInitialization( + ArrayAggregateLiteral initList, int elementIndex) { + result.getInitList() = initList and result.getElementIndex() = elementIndex +} + +/** + * Represents the IR translation of the initialization of an array element from + * an element of an initializer list. + */ +abstract class TranslatedElementInitialization extends TranslatedElement { + ArrayAggregateLiteral initList; + + override final string toString() { + result = initList.toString() + "[" + getElementIndex().toString() + "]" + } + + override final Locatable getAST() { + result = initList + } + + override final Function getFunction() { + result = initList.getEnclosingFunction() + } + + override final Instruction getFirstInstruction() { + result = getInstruction(getElementIndexTag()) + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + ( + tag = getElementIndexTag() and + opcode instanceof Opcode::Constant and + resultType = getIntType() and + isGLValue = false + ) or + ( + tag = getElementAddressTag() and + opcode instanceof Opcode::PointerAdd and + resultType = getElementType() and + isGLValue = true + ) + } + + override Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + tag = getElementIndexTag() and + result = getInstruction(getElementAddressTag()) and + kind instanceof GotoEdge + } + + override Instruction getInstructionOperand(InstructionTag tag, + OperandTag operandTag) { + tag = getElementAddressTag() and + ( + ( + operandTag instanceof LeftOperand and + result = getParent().(InitializationContext).getTargetAddress() + ) or + ( + operandTag instanceof RightOperand and + result = getInstruction(getElementIndexTag()) + ) + ) + } + + override string getInstructionConstantValue(InstructionTag tag) { + tag = getElementIndexTag() and + result = getElementIndex().toString() + } + + abstract int getElementIndex(); + + final InstructionTag getElementAddressTag() { + result = InitializerElementAddressTag(getElementIndex()) + } + + final InstructionTag getElementIndexTag() { + result = InitializerElementIndexTag(getElementIndex()) + } + + final ArrayAggregateLiteral getInitList() { + result = initList + } + + final Type getElementType() { + result = initList.getType().getUnspecifiedType().(ArrayType). + getBaseType().getUnspecifiedType() + } +} + +/** + * Represents the IR translation of the initialization of an array element from + * an explicit element in an initializer list. + */ +class TranslatedExplicitElementInitialization extends + TranslatedElementInitialization, TTranslatedExplicitElementInitialization, + InitializationContext { + int elementIndex; + + TranslatedExplicitElementInitialization() { + this = TTranslatedExplicitElementInitialization(initList, elementIndex) + } + + override Instruction getTargetAddress() { + result = getInstruction(getElementAddressTag()) + } + + override Type getTargetType() { + result = getElementType() + } + + override Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + result = TranslatedElementInitialization.super.getInstructionSuccessor(tag, kind) or + ( + tag = getElementAddressTag() and + result = getInitialization().getFirstInstruction() and + kind instanceof GotoEdge + ) + } + + override Instruction getChildSuccessor(TranslatedElement child) { + child = getInitialization() and result = getParent().getChildSuccessor(this) + } + + override TranslatedElement getChild(int id) { + id = 0 and result = getInitialization() + } + + override int getElementIndex() { + result = elementIndex + } + + TranslatedInitialization getInitialization() { + result = getTranslatedInitialization( + initList.getElementExpr(elementIndex).getFullyConverted()) + } +} + +/** + * Represents the IR translation of the initialization of a range of array + * elements without corresponding elements in the initializer list. + */ +class TranslatedElementValueInitialization extends + TranslatedElementInitialization, TTranslatedElementValueInitialization { + int elementIndex; + int elementCount; + + TranslatedElementValueInitialization() { + this = TTranslatedElementValueInitialization(initList, elementIndex, + elementCount) + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + TranslatedElementInitialization.super.hasInstruction(opcode, tag, resultType, isGLValue) or + ( + tag = getElementDefaultValueTag() and + opcode instanceof Opcode::Constant and + resultType = getDefaultValueType() and + isGLValue = false + ) or + ( + tag = getElementDefaultValueStoreTag() and + opcode instanceof Opcode::Store and + resultType = getDefaultValueType() and + isGLValue = false + ) + } + + override Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + result = TranslatedElementInitialization.super.getInstructionSuccessor(tag, kind) or + ( + kind instanceof GotoEdge and + ( + ( + tag = getElementAddressTag() and + result = getInstruction(getElementDefaultValueTag()) + ) or + ( + tag = getElementDefaultValueTag() and + result = getInstruction(getElementDefaultValueStoreTag()) + ) or + ( + tag = getElementDefaultValueStoreTag() and + result = getParent().getChildSuccessor(this) + ) + ) + ) + } + + override string getInstructionConstantValue(InstructionTag tag) { + result = TranslatedElementInitialization.super.getInstructionConstantValue(tag) or + ( + tag = getElementDefaultValueTag() and + result = getZeroValue(getElementType()) + ) + } + + override int getInstructionResultSize(InstructionTag tag) { + elementCount > 1 and + ( + tag = getElementDefaultValueTag() or + tag = getElementDefaultValueStoreTag() + ) and + result = elementCount * getElementType().getSize() + } + + override Instruction getInstructionOperand(InstructionTag tag, + OperandTag operandTag) { + result = TranslatedElementInitialization.super.getInstructionOperand(tag, operandTag) or + ( + tag = getElementDefaultValueStoreTag() and + ( + ( + operandTag instanceof LoadStoreAddressOperand and + result = getInstruction(getElementAddressTag()) + ) or + ( + operandTag instanceof CopySourceOperand and + result = getInstruction(getElementDefaultValueTag()) + ) + ) + ) + } + + override Instruction getChildSuccessor(TranslatedElement child) { + none() + } + + override TranslatedElement getChild(int id) { + none() + } + + override int getElementIndex() { + result = elementIndex + } + + private InstructionTag getElementDefaultValueTag() { + result = InitializerElementDefaultValueTag(elementIndex) + } + + private InstructionTag getElementDefaultValueStoreTag() { + result = InitializerElementDefaultValueStoreTag(elementIndex) + } + + private Type getDefaultValueType() { + if elementCount = 1 then + result = getElementType() + else + result instanceof UnknownType + } +} + +abstract class TranslatedStructorCallFromStructor extends TranslatedElement, StructorCallContext { + FunctionCall call; + + override final Locatable getAST() { + result = call + } + + override final TranslatedElement getChild(int id) { + id = 0 and + result = getStructorCall() + } + + override final Function getFunction() { + result = call.getEnclosingFunction() + } + + override final Instruction getChildSuccessor(TranslatedElement child) { + child = getStructorCall() and + result = getParent().getChildSuccessor(this) + } + + final TranslatedExpr getStructorCall() { + result = getTranslatedExpr(call) + } +} + +/** + * Represents the IR translation of a call to a base class constructor or + * destructor from within a derived class constructor or destructor. + */ +abstract class TranslatedBaseStructorCall extends TranslatedStructorCallFromStructor { + override final Instruction getFirstInstruction() { + result = getInstruction(OnlyInstructionTag()) + } + + override final predicate hasInstruction(Opcode opcode, InstructionTag tag, Type resultType, + boolean isGLValue) { + tag = OnlyInstructionTag() and + opcode instanceof Opcode::ConvertToBase and + resultType = call.getTarget().getDeclaringType().getUnspecifiedType() and + isGLValue = true + } + + override final Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { + tag = OnlyInstructionTag() and + kind instanceof GotoEdge and + result = getStructorCall().getFirstInstruction() + } + + override final Instruction getReceiver() { + result = getInstruction(OnlyInstructionTag()) + } + + override final Instruction getInstructionOperand(InstructionTag tag, + OperandTag operandTag) { + tag = OnlyInstructionTag() and + operandTag instanceof UnaryOperand and + result = getTranslatedFunction(getFunction()).getInitializeThisInstruction() + } + + override final predicate getInstructionInheritance(InstructionTag tag, + Class baseClass, Class derivedClass) { + tag = OnlyInstructionTag() and + baseClass = call.getTarget().getDeclaringType().getUnspecifiedType() and + derivedClass = getFunction().getDeclaringType().getUnspecifiedType() + } +} + +/** + * Represents a call to a delegating or base class constructor from within a constructor. + */ +abstract class TranslatedConstructorCallFromConstructor extends TranslatedStructorCallFromStructor, + TTranslatedConstructorBaseInit { + TranslatedConstructorCallFromConstructor() { + this = TTranslatedConstructorBaseInit(call) + } +} + +TranslatedConstructorCallFromConstructor getTranslatedConstructorBaseInit(ConstructorBaseInit init) { + result.getAST() = init +} + +/** + * Represents the IR translation of a delegating constructor call from within a constructor. + */ +class TranslatedConstructorDelegationInit extends TranslatedConstructorCallFromConstructor { + TranslatedConstructorDelegationInit() { + call instanceof ConstructorDelegationInit + } + + override final string toString() { + result = "delegation construct: " + call.toString() + } + + override final Instruction getFirstInstruction() { + result = getStructorCall().getFirstInstruction() + } + + override final predicate hasInstruction(Opcode opcode, InstructionTag tag, Type resultType, + boolean isGLValue) { + none() + } + + override final Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { + none() + } + + override final Instruction getReceiver() { + result = getTranslatedFunction(getFunction()).getInitializeThisInstruction() + } +} + +/** + * Represents the IR translation of a call to a base class constructor from within a + * derived class constructor + */ +class TranslatedConstructorBaseInit extends TranslatedConstructorCallFromConstructor, + TranslatedBaseStructorCall { + TranslatedConstructorBaseInit() { + not call instanceof ConstructorDelegationInit + } + + override final string toString() { + result = "construct base: " + call.toString() + } +} + +TranslatedDestructorBaseDestruction getTranslatedDestructorBaseDestruction(DestructorBaseDestruction destruction) { + result.getAST() = destruction +} + +/** + * Represents the IR translation of a call to a base class destructor from within a + * derived class destructor. + */ +class TranslatedDestructorBaseDestruction extends TranslatedBaseStructorCall, + TTranslatedDestructorBaseDestruction { + TranslatedDestructorBaseDestruction() { + this = TTranslatedDestructorBaseDestruction(call) + } + + override final string toString() { + result = "destroy base: " + call.toString() + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/TranslatedStmt.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/TranslatedStmt.qll new file mode 100644 index 000000000000..407507531da4 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/internal/TranslatedStmt.qll @@ -0,0 +1,753 @@ +import cpp +private import InstructionTag +private import Opcode +private import TranslatedCondition +private import TranslatedDeclarationEntry +private import TranslatedElement +private import TranslatedExpr +private import TranslatedFunction +private import TranslatedInitialization + +TranslatedStmt getTranslatedStmt(Stmt stmt) { + result.getAST() = stmt +} + +abstract class TranslatedStmt extends TranslatedElement, TTranslatedStmt { + Stmt stmt; + + TranslatedStmt() { + this = TTranslatedStmt(stmt) + } + + override final string toString() { + result = stmt.toString() + } + + override final Locatable getAST() { + result = stmt + } + + override final Function getFunction() { + result = stmt.getEnclosingFunction() + } +} + +class TranslatedEmptyStmt extends TranslatedStmt { + TranslatedEmptyStmt() { + stmt instanceof EmptyStmt or + stmt instanceof LabelStmt or + stmt instanceof SwitchCase + } + + override TranslatedElement getChild(int id) { + none() + } + + override Instruction getFirstInstruction() { + result = getInstruction(OnlyInstructionTag()) + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + tag = OnlyInstructionTag() and + opcode instanceof Opcode::NoOp and + resultType instanceof VoidType and + isGLValue = false + } + + override Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + tag = OnlyInstructionTag() and + result = getParent().getChildSuccessor(this) and + kind instanceof GotoEdge + } + + override Instruction getChildSuccessor(TranslatedElement child) { + none() + } +} + +class TranslatedDeclStmt extends TranslatedStmt { + DeclStmt declStmt; + + TranslatedDeclStmt() { + declStmt = stmt + } + + override TranslatedElement getChild(int id) { + result = getDeclarationEntry(id) + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + none() + } + + override Instruction getFirstInstruction() { + result = getDeclarationEntry(0).getFirstInstruction() //REVIEW: Empty? + } + + private int getChildCount() { + result = declStmt.getNumDeclarations() + } + + private TranslatedDeclarationEntry getDeclarationEntry(int index) { + result = getTranslatedDeclarationEntry(declStmt.getDeclarationEntry(index)) + } + + override Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + none() + } + + override Instruction getChildSuccessor(TranslatedElement child) { + exists(int index | + child = getDeclarationEntry(index) and + if index = (getChildCount() - 1) then + result = getParent().getChildSuccessor(this) + else + result = getDeclarationEntry(index + 1).getFirstInstruction() + ) + } +} + +class TranslatedExprStmt extends TranslatedStmt { + TranslatedExprStmt() { + stmt instanceof ExprStmt + } + + TranslatedExpr getExpr() { + result = getTranslatedExpr(stmt.(ExprStmt).getExpr().getFullyConverted()) + } + + override TranslatedElement getChild(int id) { + id = 0 and result = getExpr() + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + none() + } + + override Instruction getFirstInstruction() { + result = getExpr().getFirstInstruction() + } + + override Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + none() + } + + override Instruction getChildSuccessor(TranslatedElement child) { + child = getExpr() and + result = getParent().getChildSuccessor(this) + } +} + +abstract class TranslatedReturnStmt extends TranslatedStmt { + ReturnStmt returnStmt; + + TranslatedReturnStmt() { + returnStmt = stmt + } + + final TranslatedFunction getEnclosingFunction() { + result = getTranslatedFunction(returnStmt.getEnclosingFunction()) + } +} + +class TranslatedReturnValueStmt extends TranslatedReturnStmt, + InitializationContext { + TranslatedReturnValueStmt() { + returnStmt.hasExpr() + } + + override TranslatedElement getChild(int id) { + id = 0 and result = getInitialization() + } + + override Instruction getFirstInstruction() { + result = getInstruction(InitializerVariableAddressTag()) + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + tag = InitializerVariableAddressTag() and + opcode instanceof Opcode::VariableAddress and + resultType = getEnclosingFunction().getReturnVariable().getType() and + isGLValue = true + } + + override Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + tag = InitializerVariableAddressTag() and + result = getInitialization().getFirstInstruction() and + kind instanceof GotoEdge + } + + override Instruction getChildSuccessor(TranslatedElement child) { + child = getInitialization() and + result = getEnclosingFunction().getReturnSuccessorInstruction() + } + + override IRVariable getInstructionVariable(InstructionTag tag) { + tag = InitializerVariableAddressTag() and + result = getEnclosingFunction().getReturnVariable() + } + + override Instruction getTargetAddress() { + result = getInstruction(InitializerVariableAddressTag()) + } + + override Type getTargetType() { + result = getEnclosingFunction().getReturnVariable().getType() + } + + TranslatedInitialization getInitialization() { + result = getTranslatedInitialization( + returnStmt.getExpr().getFullyConverted()) + } +} + +class TranslatedReturnVoidStmt extends TranslatedReturnStmt { + TranslatedReturnVoidStmt() { + not returnStmt.hasExpr() + } + + override TranslatedElement getChild(int id) { + none() + } + + override Instruction getFirstInstruction() { + result = getInstruction(OnlyInstructionTag()) + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + tag = OnlyInstructionTag() and + opcode instanceof Opcode::NoOp and + resultType instanceof VoidType and + isGLValue = false + } + + override Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + tag = OnlyInstructionTag() and + result = getEnclosingFunction().getReturnSuccessorInstruction() and + kind instanceof GotoEdge + } + + override Instruction getChildSuccessor(TranslatedElement child) { + none() + } +} + +/** + * The IR translation of a C++ `try` statement. + */ +class TranslatedTryStmt extends TranslatedStmt { + TryStmt try; + + TranslatedTryStmt() { + try = stmt + } + + override TranslatedElement getChild(int id) { + id = 0 and result = getBody() or + result = getHandler(id - 1) + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + none() + } + + override Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + none() + } + + override Instruction getFirstInstruction() { + result = getBody().getFirstInstruction() + } + + override Instruction getChildSuccessor(TranslatedElement child) { + // All children go to the successor of the `try`. + child = getAChild() and result = getParent().getChildSuccessor(this) + } + + final Instruction getNextHandler(TranslatedHandler handler) { + exists(int index | + handler = getHandler(index) and + result = getHandler(index + 1).getFirstInstruction() + ) or + ( + // The last catch clause flows to the exception successor of the parent + // of the `try`, because the exception successor of the `try` itself is + // the first catch clause. + handler = getHandler(try.getNumberOfCatchClauses()) and + result = getParent().getExceptionSuccessorInstruction() + ) + } + + override final Instruction getExceptionSuccessorInstruction() { + result = getHandler(0).getFirstInstruction() + } + + private TranslatedHandler getHandler(int index) { + result = getTranslatedStmt(try.getChild(index + 1)) + } + + private TranslatedStmt getBody() { + result = getTranslatedStmt(try.getStmt()) + } +} + +class TranslatedBlock extends TranslatedStmt { + Block block; + + TranslatedBlock() { + block = stmt + } + + override TranslatedElement getChild(int id) { + result = getStmt(id) + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + isEmpty() and + opcode instanceof Opcode::NoOp and + tag = OnlyInstructionTag() and + resultType instanceof VoidType and + isGLValue = false + } + + override Instruction getFirstInstruction() { + if isEmpty() then + result = getInstruction(OnlyInstructionTag()) + else + result = getStmt(0).getFirstInstruction() + } + + private predicate isEmpty() { + not exists(block.getStmt(0)) + } + + private TranslatedStmt getStmt(int index) { + result = getTranslatedStmt(block.getStmt(index)) + } + + private int getStmtCount() { + result = block.getNumStmt() + } + + override Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + tag = OnlyInstructionTag() and + result = getParent().getChildSuccessor(this) and + kind instanceof GotoEdge + } + + override Instruction getChildSuccessor(TranslatedElement child) { + exists(int index | + child = getStmt(index) and + if index = (getStmtCount() - 1) then + result = getParent().getChildSuccessor(this) + else + result = getStmt(index + 1).getFirstInstruction() + ) + } +} + +/** + * The IR translation of a C++ `catch` handler. + */ +abstract class TranslatedHandler extends TranslatedStmt { + Handler handler; + + TranslatedHandler() { + handler = stmt + } + + override TranslatedElement getChild(int id) { + id = 1 and result = getBlock() + } + + override Instruction getFirstInstruction() { + result = getInstruction(CatchTag()) + } + + override Instruction getChildSuccessor(TranslatedElement child) { + child = getBlock() and result = getParent().getChildSuccessor(this) + } + + override Instruction getExceptionSuccessorInstruction() { + // A throw from within a `catch` block flows to the handler for the parent of + // the `try`. + result = getParent().getParent().getExceptionSuccessorInstruction() + } + + TranslatedStmt getBlock() { + result = getTranslatedStmt(handler.getBlock()) + } +} + +/** + * The IR translation of a C++ `catch` block that catches an exception with a + * specific type (e.g. `catch (const std::exception&)`). + */ +class TranslatedCatchByTypeHandler extends TranslatedHandler { + TranslatedCatchByTypeHandler() { + exists(handler.getParameter()) + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + tag = CatchTag() and + opcode instanceof Opcode::CatchByType and + resultType instanceof VoidType and + isGLValue = false + } + + override TranslatedElement getChild(int id) { + result = super.getChild(id) or + id = 0 and result = getParameter() + } + + override Instruction getChildSuccessor(TranslatedElement child) { + result = super.getChildSuccessor(child) or + child = getParameter() and result = getBlock().getFirstInstruction() + } + + override Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + tag = CatchTag() and + ( + ( + kind instanceof GotoEdge and + result = getParameter().getFirstInstruction() + ) or + ( + kind instanceof ExceptionEdge and + result = getParent().(TranslatedTryStmt).getNextHandler(this) + ) + ) + } + + override Type getInstructionExceptionType(InstructionTag tag) { + tag = CatchTag() and + result = handler.getParameter().getType() + } + + private TranslatedParameter getParameter() { + result = getTranslatedParameter(handler.getParameter()) + } +} + +/** + * The IR translation of a C++ `catch (...)` block. + */ +class TranslatedCatchAnyHandler extends TranslatedHandler { + TranslatedCatchAnyHandler() { + not exists(handler.getParameter()) + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + tag = CatchTag() and + opcode instanceof Opcode::CatchAny and + resultType instanceof VoidType and + isGLValue = false + } + + override Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + tag = CatchTag() and + kind instanceof GotoEdge and + result = getBlock().getFirstInstruction() + } +} + +class TranslatedIfStmt extends TranslatedStmt, ConditionContext { + IfStmt ifStmt; + + TranslatedIfStmt() { + stmt = ifStmt + } + + override Instruction getFirstInstruction() { + result = getCondition().getFirstInstruction() + } + + override TranslatedElement getChild(int id) { + id = 0 and result = getCondition() or + id = 1 and result = getThen() or + id = 2 and result = getElse() + } + + private TranslatedCondition getCondition() { + result = getTranslatedCondition(ifStmt.getCondition().getFullyConverted()) + } + + private TranslatedStmt getThen() { + result = getTranslatedStmt(ifStmt.getThen()) + } + + private TranslatedStmt getElse() { + result = getTranslatedStmt(ifStmt.getElse()) + } + + private predicate hasElse() { + exists(ifStmt.getElse()) + } + + override Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + none() + } + + override Instruction getChildTrueSuccessor(TranslatedCondition child) { + child = getCondition() and + result = getThen().getFirstInstruction() + } + + override Instruction getChildFalseSuccessor(TranslatedCondition child) { + child = getCondition() and + if hasElse() then + result = getElse().getFirstInstruction() + else + result = getParent().getChildSuccessor(this) + } + + override Instruction getChildSuccessor(TranslatedElement child) { + (child = getThen() or child = getElse()) and + result = getParent().getChildSuccessor(this) + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + none() + } +} + +abstract class TranslatedLoop extends TranslatedStmt, ConditionContext { + Loop loop; + + TranslatedLoop() { + loop = stmt + } + + final TranslatedCondition getCondition() { + result = getTranslatedCondition(loop.getCondition().getFullyConverted()) + } + + final TranslatedStmt getBody() { + result = getTranslatedStmt(loop.getStmt()) + } + + final Instruction getFirstConditionInstruction() { + if hasCondition() then + result = getCondition().getFirstInstruction() + else + result = getBody().getFirstInstruction() + } + + final predicate hasCondition() { + exists(loop.getCondition()) + } + + override TranslatedElement getChild(int id) { + id = 0 and result = getCondition() or + id = 1 and result = getBody() + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + none() + } + + override final Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + none() + } + + override final Instruction getChildTrueSuccessor(TranslatedCondition child) { + child = getCondition() and result = getBody().getFirstInstruction() + } + + override final Instruction getChildFalseSuccessor(TranslatedCondition child) { + child = getCondition() and result = getParent().getChildSuccessor(this) + } +} + +class TranslatedWhileStmt extends TranslatedLoop { + TranslatedWhileStmt() { + stmt instanceof WhileStmt + } + + override Instruction getFirstInstruction() { + result = getFirstConditionInstruction() + } + + override Instruction getChildSuccessor(TranslatedElement child) { + child = getBody() and result = getFirstConditionInstruction() + } +} + +class TranslatedDoStmt extends TranslatedLoop { + TranslatedDoStmt() { + stmt instanceof DoStmt + } + + override Instruction getFirstInstruction() { + result = getBody().getFirstInstruction() + } + + override Instruction getChildSuccessor(TranslatedElement child) { + child = getBody() and result = getFirstConditionInstruction() + } +} + +class TranslatedForStmt extends TranslatedLoop { + ForStmt forStmt; + + TranslatedForStmt() { + forStmt = stmt + } + + override TranslatedElement getChild(int id) { + id = 0 and result = getInitialization() or + id = 1 and result = getCondition() or + id = 2 and result = getUpdate() or + id = 3 and result = getBody() + } + + private TranslatedStmt getInitialization() { + result = getTranslatedStmt(forStmt.getInitialization()) + } + + private predicate hasInitialization() { + exists(forStmt.getInitialization()) + } + + private TranslatedExpr getUpdate() { + result = getTranslatedExpr(forStmt.getUpdate().getFullyConverted()) + } + + private predicate hasUpdate() { + exists(forStmt.getUpdate()) + } + + override Instruction getFirstInstruction() { + if hasInitialization() then + result = getInitialization().getFirstInstruction() + else + result = getFirstConditionInstruction() + } + + override Instruction getChildSuccessor(TranslatedElement child) { + ( + child = getInitialization() and + result = getFirstConditionInstruction() + ) or + ( + child = getBody() and + if hasUpdate() then + result = getUpdate().getFirstInstruction() + else + result = getFirstConditionInstruction() + ) or + child = getUpdate() and result = getFirstConditionInstruction() + } +} + +class TranslatedJumpStmt extends TranslatedStmt { + JumpStmt jump; + + TranslatedJumpStmt() { + stmt = jump + } + + override Instruction getFirstInstruction() { + result = getInstruction(OnlyInstructionTag()) + } + + override TranslatedElement getChild(int id) { + none() + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + tag = OnlyInstructionTag() and + opcode instanceof Opcode::NoOp and + resultType instanceof VoidType and + isGLValue = false + } + + override Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + tag = OnlyInstructionTag() and + kind instanceof GotoEdge and + result = getTranslatedStmt(jump.getTarget()).getFirstInstruction() + } + + override Instruction getChildSuccessor(TranslatedElement child) { + none() + } +} + +class TranslatedSwitchStmt extends TranslatedStmt { + SwitchStmt switchStmt; + + TranslatedSwitchStmt() { + switchStmt = stmt + } + + private TranslatedExpr getExpr() { + result = getTranslatedExpr(switchStmt.getExpr().getFullyConverted()) + } + + private TranslatedStmt getBody() { + result = getTranslatedStmt(switchStmt.getStmt()) + } + + override Instruction getFirstInstruction() { + result = getExpr().getFirstInstruction() + } + + override TranslatedElement getChild(int id) { + id = 0 and result = getExpr() or + id = 1 and result = getBody() + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, + Type resultType, boolean isGLValue) { + tag = SwitchBranchTag() and + opcode instanceof Opcode::Switch and + resultType instanceof VoidType and + isGLValue = false + } + + override Instruction getInstructionOperand(InstructionTag tag, + OperandTag operandTag) { + tag = SwitchBranchTag() and + operandTag instanceof ConditionOperand and + result = getExpr().getResult() + } + + override Instruction getInstructionSuccessor(InstructionTag tag, + EdgeKind kind) { + tag = SwitchBranchTag() and + exists(SwitchCase switchCase | + switchCase = switchStmt.getASwitchCase() and + kind = getCaseEdge(switchCase) and + result = getTranslatedStmt(switchCase).getFirstInstruction() + ) + } + + override Instruction getChildSuccessor(TranslatedElement child) { + child = getExpr() and result = getInstruction(SwitchBranchTag()) or + child = getBody() and result = getParent().getChildSuccessor(this) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/metrics/MetricClass.qll b/cpp/ql/src/semmle/code/cpp/metrics/MetricClass.qll new file mode 100644 index 000000000000..18d5ade90d4c --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/metrics/MetricClass.qll @@ -0,0 +1,525 @@ +import cpp + +/** + * A wrapper that provides metrics for a C++ class. + */ +class MetricClass extends Class { + + /** + * Gets the nesting level of this class. A class that is _not_ nested + * directly inside another class has nesting level 0. + */ + int getNestingLevel() { + if not this instanceof NestedClass then + result = 0 + else + result = this.(NestedClass).getDeclaringType().(MetricClass).getNestingLevel() + 1 + } + + /** + * Gets the length of *some* path to a root of the hierarchy. A class with no + * base class has depth 0. + */ + int getADepth() { + not this.getABaseClass+() = this and + if not exists(this.getABaseClass()) then + result = 0 + else + result = this.getABaseClass().(MetricClass).getADepth() + 1 + } + + /** + * Gets the maximum depth of inheritance of this class. A class with no base + * class has depth 0. + */ + int getInheritanceDepth() { + result = max(this.getADepth()) + } + + /** Gets the number of member functions in this class. */ + int getNumberOfMemberFunctions() { + result = count(MemberFunction mf | mf.getDeclaringType() = this) + } + + /** Gets the number of nested classes defined in this class. */ + int getNumberOfNestedClasses() { + result = count(NestedClass nc | nc.getDeclaringType() = this) + } + + /** Gets the number of non-static data members defined in this class. */ + int getNumberOfFields() { + result = count(Field f | f.getDeclaringType() = this) + } + + /** Gets the total number of members defined in this class. */ + int getNumberOfMembers() { + result = count(Declaration m | m.getDeclaringType() = this) + } + + /** Gets the number of incoming class dependencies. */ + int getAfferentCoupling() { + result = count(MetricClass that | that.getAClassDependency() = this) + } + + /** Gets the number of outgoing class dependencies. */ + int getEfferentCoupling() { + result = count(MetricClass that | this.getAClassDependency() = that) + } + + /** Gets the number of outgoing source class dependencies. */ + int getEfferentSourceCoupling() { + result = count(MetricClass that | this.getAClassDependency() = that and that.fromSource()) + } + + /** Gets a class dependency of this element. */ + Class getAClassDependency() { + dependsOnClassSimple(this, result) + } + + /* -------- HENDERSON-SELLERS LACK OF COHESION IN METHODS -------- */ + + /* The aim of this metric is to try and determine whether a class + represents one abstraction (good) or multiple abstractions (bad). + If a class represents multiple abstractions, it should be split + up into multiple classes. + + In the Henderson-Sellers method, this is measured as follows: + M = set of methods in class + F = set of fields in class + r(f) = number of methods that access field f + = mean of r(f) over f in F + The lack of cohesion is then given by + + - |M| + --------- + 1 - |M| + + We follow the Eclipse metrics plugin by restricting M to methods + that access some field in the same class, and restrict F to + fields that are read by methods in the same class. + + Classes where the value of this metric is higher than 0.9 ought + to be scrutinised for possible splitting. Here is a query + to find such classes: + + from MetricRefType t, float loc + where loc = t.getLackOfCohesionHS() and loc > 0.9 + select t, loc order by loc desc + */ + + /** Holds if `func` accesses field `f` defined in the same type. */ + predicate accessesLocalField(Function func, Field f) { + func.accesses(f) and + this.getAMemberFunction() = func and + f.getDeclaringType() = this + } + + /** Gets any method that accesses some local field. */ + Function getAccessingMethod() { + exists(Field f | this.accessesLocalField(result,f)) + } + + /** Gets any field that is accessed by a local method. */ + Field getAccessedField() { + exists(Function func | this.accessesLocalField(func,result)) + } + + /** Gets the Henderson-Sellers lack-of-cohesion metric. */ + float getLackOfCohesionHS() { + exists(int m, float r | + // m = number of methods that access some field + m = count(this.getAccessingMethod()) + and + // r = average (over f) of number of methods that access field f + r = avg(Field f | + f = this.getAccessedField() | + count(Function x | this.accessesLocalField(x,f))) + and + // avoid division by zero + m != 1 + and + // compute LCOM + result = ((r-m)/(1-m)) + ) + } + + /* -------- CHIDAMBER AND KEMERER LACK OF COHESION IN METHODS ------------ */ + + /* The aim of this metric is to try and determine whether a class + represents one abstraction (good) or multiple abstractions (bad). + If a class represents multiple abstractions, it should be split + up into multiple classes. + + In the Chidamber and Kemerer method, this is measured as follows: + n1 = number of pairs of distinct methods in a class that do *not* + have at least one commonly accessed field + n2 = number of pairs of distinct methods in a class that do + have at least one commonly accessed field + lcom = ((n1 - n2)/2 max 0) + + We divide by 2 because each pair (m1,m2) is counted twice in n1 and n2. + + */ + + /** Holds if `f` should be excluded from the CK cohesion computation. */ + predicate ignoreLackOfCohesionCK(Function f) { + none() // by default, nothing is ignored + } + + /** Holds if `m1` and `m2` are distinct member functions of this class. */ + predicate distinctMembers(MemberFunction m1, MemberFunction m2) { + m1.getDeclaringType() = this and + m2.getDeclaringType() = this and + m1 != m2 + } + + /** + * Holds if `m1` and `m2` are distinct member functions of this class that + * both access a common field. + */ + predicate shareField(MemberFunction m1, MemberFunction m2) { + exists(Field f | + m1.accesses(f) and + m1.getDeclaringType() = this and + m2.accesses(f) and + m2.getDeclaringType() = this) and + m1 != m2 + } + + /** Gets the Chidamber and Kemerer lack-of-cohesion metric. */ + float getLackOfCohesionCK() { + exists(int n1, int n2, float n | + n1 = count(MemberFunction m1, MemberFunction m2 | + not this.ignoreLackOfCohesionCK(m1) and + not this.ignoreLackOfCohesionCK(m2) and + this.distinctMembers(m1,m2) and + not(this.shareField(m1,m2))) + and + n2 = count(MemberFunction m1, MemberFunction m2 | + this.shareField(m1,m2)) + and + n = (n1 - n2)/2.0 + and + ( (n < 0 and result = 0) + or + (n>=0 and result = n) ) + ) + } + + /* ----------------- RESPONSE FOR A CLASS --------------------------------- */ + + /** + * Gets the _response_ for this class. This estimates the number of + * different functions that can be executed when a function is invoked on + * this class. + */ + int getResponse() { + result = sum(MemberFunction f | f.getDeclaringType()=this | count(Call call | call.getEnclosingFunction() = f)) + } + + /* ----------------- SPECIALIZATION INDEX -------------------------------- */ + + /** + * Gets a function that should be excluded when reporting the number of + * overriding methods. By default, no functions are excluded. + */ + predicate ignoreOverride(MemberFunction m) { + none() + } + + /** Gets some method that overrides a non-abstract method in a base class. */ + MemberFunction getOverrides() { + this.getAMemberFunction() = result and + exists(MemberFunction c | result.overrides(c) and + not c instanceof PureVirtualFunction) and + not this.ignoreOverride(result) + } + + /** Gets the number of methods that are overridden by this class (NORM). */ + int getNumberOverridden() { + result = count(this.getOverrides()) + } + + /** + * Gets the _specialization index_ of this class. + * + * The specialization index metric measures the extent to which derived + * classes override (replace) the behavior of their base classes. If they + * override many methods, it is an indication that the original abstraction + * in the base classes may have been inappropriate. On the whole, derived + * classes should add behavior to their base classes, but not alter that + * behavior dramatically. + */ + float getSpecialisationIndex() { + this.getNumberOfMemberFunctions() != 0 + and + result = (this.getNumberOverridden() * this.getInheritanceDepth()) + / + this.getNumberOfMemberFunctions().(float) + } + + /* + * HALSTEAD METRICS + */ + + /** + * Gets the Halstead "N1" metric for this class. This is the total number of + * operators in the class. Operators are taken to be all operators in + * expressions (`+`, `*`, `&`, `->`, `=`, ...) as well as most statements. + */ + int getHalsteadN1() { + result = + 1 + // account for the class itself + sum(MetricFunction mf, int toSum | (mf.(MemberFunction).getDeclaringType() = this) and (toSum = mf.getHalsteadN1()) | toSum) + + // Each member variable declaration counts once as an operator + count(MemberVariable mv | mv.getDeclaringType() = this) + + // Friend declarations + count(FriendDecl f | f.getDeclaringClass() = this) + } + + /** + * Gets the Halstead "N2" metric for this class: this is the total number of operands. + * An operand is either a variable, constant, type name, class name, or function name. + */ + int getHalsteadN2() { + result = + 1 + // the class itself + sum(MetricFunction mf, int toSum | (mf.(MemberFunction).getDeclaringType() = this) and (toSum = mf.getHalsteadN2()) | toSum) + + // Each variable declaration that is not in a function counts once as an operand + count(MemberVariable mv | mv.getDeclaringType() = this) + } + + /** + * Gets an expression contained anywhere in this class: member functions (including + * constructors, destructors and operators), initializers... + */ + Expr getAnEnclosedExpression() { + exists(MemberFunction mf | mf.getDeclaringType() = this and + result.getEnclosingFunction() = mf) + or exists(MemberVariable mv | mv.getDeclaringType() = this and + mv.getInitializer().getExpr().getAChild*() = result) + } + + /** Gets a statement in a member function of this class. */ + Stmt getAnEnclosedStmt() { + result.getEnclosingFunction().(MemberFunction).getDeclaringType() = this + } + + private string getAUsedHalsteadN1Operator() { + exists(CommaExpr e | e = this.getAnEnclosedExpression()) and result = "comma" or + exists(ReferenceToExpr e | e = this.getAnEnclosedExpression()) and result = "refTo" or + exists(PointerDereferenceExpr e | e = this.getAnEnclosedExpression()) and result = "dereference" or + exists(CStyleCast e | e = this.getAnEnclosedExpression()) and result = "cCast" or + exists(StaticCast e | e = this.getAnEnclosedExpression()) and result = "staticCast" or + exists(ConstCast e | e = this.getAnEnclosedExpression()) and result = "constCast" or + exists(ReinterpretCast e | e = this.getAnEnclosedExpression()) and result = "reinterpretCast" or + exists(DynamicCast e | e = this.getAnEnclosedExpression()) and result = "dynamicCast" or + exists(SizeofExprOperator e | e = this.getAnEnclosedExpression()) and result = "sizeofExpr" or + exists(SizeofTypeOperator e | e = this.getAnEnclosedExpression()) and result = "sizeofType" or + exists(IfStmt e | e = this.getAnEnclosedStmt()) and result = "ifVal" or + exists(SwitchStmt e | e = this.getAnEnclosedStmt()) and result = "switchVal" or + exists(ForStmt e | e = this.getAnEnclosedStmt()) and result = "forVal" or + exists(DoStmt e | e = this.getAnEnclosedStmt()) and result = "doVal" or + exists(WhileStmt e | e = this.getAnEnclosedStmt()) and result = "whileVal" or + exists(GotoStmt e | e = this.getAnEnclosedStmt()) and result = "gotoVal" or + exists(ContinueStmt e | e = this.getAnEnclosedStmt()) and result = "continueVal" or + exists(BreakStmt e | e = this.getAnEnclosedStmt()) and result = "breakVal" or + exists(ReturnStmt e | e = this.getAnEnclosedStmt()) and result = "returnVal" or + exists(SwitchCase e | e = this.getAnEnclosedStmt()) and result = "caseVal" or + exists(IfStmt s | s = this.getAnEnclosedStmt() and s.hasElse()) and result = "elseVal" or + exists(MemberFunction f | f.getDeclaringType() = this) and result = "function" or + exists(FriendDecl e | e.getDeclaringClass() = this) and result = "friendDecl" + } + + /** + * Gets the Halstead "n1" metric: this is the total number of distinct operators + * in this class. Operators are defined as in the "N1" metric (`getHalsteadN1`). + */ + int getHalsteadN1Distinct() { + result = + 1 + // avoid 0 values + count(string s | exists(Operation op | op = this.getAnEnclosedExpression() and s = op.getOperator())) + + count(string s | s = getAUsedHalsteadN1Operator()) + } + + /** + * Gets the Halstead "n2" metric: this is the number of distinct operands in this + * class. An operand is either a variable, constant, type name, or function name. + */ + int getHalsteadN2Distinct() { + result = + 1 + // avoid 0 values + count(string s | exists(Access a | a = this.getAnEnclosedExpression() and s = a.getTarget().getName())) + + count(Function f | exists(FunctionCall fc | fc = this.getAnEnclosedExpression() and f = fc.getTarget())) + + // Approximate: count declarations once more to account for the type name + count(Declaration d | d.getParentScope*() = this) + } + + /** + * Gets the Halstead length of this class. This is the sum of the N1 and N2 Halstead metrics. + */ + int getHalsteadLength() { + result = this.getHalsteadN1() + this.getHalsteadN2() + } + + /** + * Gets the Halstead vocabulary size of this class. This is the sum of the n1 and n2 Halstead metrics. + */ + int getHalsteadVocabulary() { + result = this.getHalsteadN1Distinct() + this.getHalsteadN2Distinct() + } + + /** + * Gets the Halstead volume of this class. This is the Halstead size multiplied by the log of the + * Halstead vocabulary. It represents the information content of the class. + */ + float getHalsteadVolume() { + result = this.getHalsteadLength().(float) * this.getHalsteadVocabulary().log2() + } + + /** + * Gets the Halstead difficulty value of this class. This is proportional to the number of unique + * operators, and further proportional to the ratio of total operands to unique operands. + */ + float getHalsteadDifficulty() { + result = (float)(this.getHalsteadN1Distinct() * this.getHalsteadN2()) / (float)(2 * this.getHalsteadN2Distinct()) + } + + /** + * Gets the Halstead level of this class. This is the inverse of the _difficulty_ of the class. + */ + float getHalsteadLevel() { + exists(float difficulty | + difficulty = this.getHalsteadDifficulty() and + if difficulty != 0.0 then + result = 1.0 / difficulty + else + result = 0.0 + ) + } + + /** + * Gets the Halstead implementation effort for this class. This is the product of the volume and difficulty. + */ + float getHalsteadEffort() { + result = this.getHalsteadVolume() * this.getHalsteadDifficulty() + } + + /** + * Gets the Halstead _delivered bugs_ metric for this class. This metric correlates with the complexity of + * the software but is known to be an underestimate of bug counts. + */ + float getHalsteadDeliveredBugs() { + result = this.getHalsteadEffort().pow(2.0/3.0) / 3000.0 + } +} + +private pragma[noopt] +predicate dependsOnClassSimple(Class source, Class dest) { + ( + // a class depends on the classes it inherits from + source.derivesFrom(dest) + + // a nested class depends on its enclosing class + or source.getDeclaringType() = dest and source instanceof Class + + // a class depends on its friend classes + or exists(FriendDecl fd | source.getAFriendDecl() = fd and fd.getFriend() = dest) + // a friend functions return type + or exists(FriendDecl fd, Function f, Type t | source.getAFriendDecl() = fd and fd.getFriend() = f and f.getType() = t and t.refersTo(dest)) + // the type of the arguments to a friend function + or exists(FriendDecl fd, Function f, Parameter p, Type t | source.getAFriendDecl() = fd and fd.getFriend() = f and f.getAParameter() = p and p.getType() = t and t.refersTo(dest)) + + + // a class depends on the types of its member variables + or exists(MemberVariable v, Type t | v.getDeclaringType() = source and v.getType() = t and t.refersTo(dest)) + + // a class depends on the return types of its member functions + or exists(MemberFunction f, Type t | f.getDeclaringType() = source and f instanceof MemberFunction and f.getType() = t and t.refersTo(dest)) + // a class depends on the argument types of its member functions + or exists(MemberFunction f, Parameter p, Type t | f.getDeclaringType() = source and f instanceof MemberFunction and f.getAParameter() = p and p.getType() = t and t.refersTo(dest)) + + // a class depends on the base types of type def types nested in it + or exists(NestedTypedefType t, Type td | t.getDeclaringType() = source and t.getBaseType() = td and t instanceof NestedTypedefType and td.refersTo(dest)) + + // a class depends on the type names used in a casts in functions nested in it + or exists(Cast c, Function m, Type t | m.getDeclaringType() = source and m = c.getEnclosingFunction() and c instanceof Cast and c.getType() = t and t.refersTo(dest)) + // a class depends on the type names used in casts in initialization of member variables + or exists(Cast c, Variable m, Type t | m.getDeclaringType() = source and m = c.getEnclosingVariable() and c instanceof Cast and c.getType() = t and t.refersTo(dest)) + + // a class depends on classes for which a call to its member function is done from a function + or exists(MemberFunction target, MemberFunction f, Locatable l | + f.getDeclaringType() = source + and f instanceof MemberFunction + and f.calls(target, l) + and target instanceof MemberFunction + and target.getDeclaringType() = dest) + // a class depends on classes for which a call to its member function is done from a member variable initializer + or exists(MemberFunction target, FunctionCall c, MemberVariable v | + v.getDeclaringType() = source + and v instanceof MemberVariable + and c.getEnclosingVariable() = v + and c instanceof FunctionCall + and c.getTarget() = target + and target instanceof MemberFunction + and target.getDeclaringType() = dest) + + // a class(source) depends on classes(dest) for which its member functions(mf) are accessed(fa) from a member function(f) + or exists(MemberFunction f, FunctionAccess fa, MemberFunction mf | + f.getDeclaringType() = source + and f instanceof MemberFunction + and fa.getEnclosingFunction() = f + and fa.getTarget() = mf + and mf.getDeclaringType() = dest + and mf instanceof MemberFunction) + + // a class depends on classes for which its member functions are accessed from a member variable initializer + or exists(MemberVariable v, FunctionAccess fa, MemberFunction mf | + v.getDeclaringType() = source + and v instanceof MemberVariable + and fa.getEnclosingVariable() = v + and fa.getTarget() = mf + and mf.getDeclaringType() = dest + and mf instanceof MemberFunction) + + // a class depends on classes for which its member variables are accessed from a member function + or exists(MemberFunction f, VariableAccess va, MemberVariable mv | + f.getDeclaringType() = source + and f instanceof MemberFunction + and va.getEnclosingFunction() = f + and va instanceof VariableAccess + and va.getTarget() = mv + and mv.getDeclaringType() = dest + and mv instanceof MemberVariable) + + // a class depends on classes for which its member variables are accessed from a member variable initializer + or exists(MemberVariable v, VariableAccess va, MemberVariable mv | + v.getDeclaringType() = source + and v instanceof MemberVariable + and va.getEnclosingVariable() = v + and va instanceof VariableAccess + and va.getTarget() = mv + and mv.getDeclaringType() = dest + and mv instanceof MemberVariable) + + // a class depends on enums for which its enum constants are accessed from a member function + or exists(MemberFunction f, EnumConstantAccess ea, EnumConstant e | + f.getDeclaringType() = source + and f instanceof MemberFunction + and ea.getEnclosingFunction() = f + and ea.getTarget() = e + and e.getDeclaringEnum() = dest + and ea instanceof EnumConstantAccess) + + // a class depends on enums for which its enum constants are accessed from a member variable initializer + or exists(MemberVariable v, EnumConstantAccess ea, EnumConstant e | + v.getDeclaringType() = source + and v instanceof MemberVariable + and ea.getEnclosingVariable() = v + and ea instanceof EnumConstantAccess + and ea.getTarget() = e + and e.getDeclaringEnum() = dest) + + ) + and dest instanceof Class +} diff --git a/cpp/ql/src/semmle/code/cpp/metrics/MetricFile.qll b/cpp/ql/src/semmle/code/cpp/metrics/MetricFile.qll new file mode 100644 index 000000000000..12607d1a7a38 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/metrics/MetricFile.qll @@ -0,0 +1,269 @@ +import cpp + +/** + * A wrapper that provides metrics for a C/C++ file. + */ +class MetricFile extends File { + + /** Gets the number of functions defined in this file. */ + int getNumberOfTopLevelFunctions() { + result = count(Function f | f.isTopLevel() and f.getFile() = this) + } + + /** Gets the number of classes defined in this file. */ + int getNumberOfClasses() { + result = count(Class c | c.getFile() = this) + } + + /** Gets the number of user-defined types defined in this file. */ + int getNumberOfUserTypes() { + result = count(UserType t | t.getFile() = this) + } + + /** Gets the number of lines in this file. */ + int getNumberOfLines() { + numlines(this,result,_,_) + } + + /** Gets the number of lines of code in this file. */ + int getNumberOfLinesOfCode() { + numlines(this,_,result,_) + } + + /** Gets the number of lines of comments in this file. */ + int getNumberOfLinesOfComments() { + numlines(this,_,_,result) + } + + /** Gets the number of incoming file dependencies. */ + int getAfferentCoupling() { + result = count(MetricFile that | that.getAFileDependency() = this) + } + + /** Gets the number of outgoing file dependencies. */ + int getEfferentCoupling() { + result = count(MetricFile that | this.getAFileDependency() = that) + } + + /* + * HALSTEAD METRICS + */ + + /** + * Gets the Halstead "N1" metric for this file. This is the total number of + * operators in the file. Operators are taken to be all operators in + * expressions (`+`, `*`, `&`, `->`, `=`, ...) as well as most statements. + */ + int getHalsteadN1() { + result = + sum(MetricFunction mf, int toSum | (mf.getFile() = this) and (toSum = mf.getHalsteadN1()) | toSum) + + count(Class c | c.getFile() = this) + // Each class counts once as an operator + // Each variable declaration that is not in a function counts once as an operator + count(GlobalVariable gv | gv.getFile() = this) + + count(MemberVariable mv | mv.getFile() = this) + + // Type declarations - to count the definition tokens + count(TypeDeclarationEntry decl | decl.getFile() = this) + + // Friend declarations + count(FriendDecl f | f.getFile() = this) + } + + /** + * Gets the Halstead "N2" metric for this file: this is the total number of operands. + * An operand is either a variable, constant, type name, class name, or function name. + */ + int getHalsteadN2() { + result = + sum(MetricFunction mf, int toSum | (mf.getFile() = this) and (toSum = mf.getHalsteadN2()) | toSum) + + count(Class c | c.getFile() = this) + // Each class counts once as an operand + // Each variable declaration that is not in a function counts once as an operand + count(GlobalVariable gv | gv.getFile() = this) + + count(MemberVariable mv | mv.getFile() = this) + + // Type declarations - to count the type names + count(TypeDeclarationEntry decl | decl.getFile() = this) + + // Enum constant declarations to count the name + count(EnumConstant ec | ec.getFile() = this) + } + + private string getAUsedHalsteadN1Operator() { + exists(CommaExpr e | e.getFile() = this) and result = "comma" or + exists(ReferenceToExpr e | e.getFile() = this) and result = "refTo" or + exists(PointerDereferenceExpr e | e.getFile() = this) and result = "dereference" or + exists(CStyleCast e | e.getFile() = this) and result = "cCast" or + exists(StaticCast e | e.getFile() = this) and result = "staticCast" or + exists(ConstCast e | e.getFile() = this) and result = "constCast" or + exists(ReinterpretCast e | e.getFile() = this) and result = "reinterpretCast" or + exists(DynamicCast e | e.getFile() = this) and result = "dynamicCast" or + exists(SizeofExprOperator e | e.getFile() = this) and result = "sizeofExpr" or + exists(SizeofTypeOperator e | e.getFile() = this) and result = "sizeofType" or + exists(IfStmt e | e.getFile() = this) and result = "ifVal" or + exists(SwitchStmt e | e.getFile() = this) and result = "switchVal" or + exists(ForStmt e | e.getFile() = this) and result = "forVal" or + exists(DoStmt e | e.getFile() = this) and result = "doVal" or + exists(WhileStmt e | e.getFile() = this) and result = "whileVal" or + exists(GotoStmt e | e.getFile() = this) and result = "gotoVal" or + exists(ContinueStmt e | e.getFile() = this) and result = "continueVal" or + exists(BreakStmt e | e.getFile() = this) and result = "breakVal" or + exists(ReturnStmt e | e.getFile() = this) and result = "returnVal" or + exists(SwitchCase e | e.getFile() = this) and result = "caseVal" or + exists(IfStmt s | s.getFile() = this and s.hasElse()) and result = "elseVal" or + exists(Function f | f.getFile() = this) and result = "function" or + exists(Class c | c.getFile() = this) and result = "classDef" or + exists(TypeDeclarationEntry e | e.getFile() = this) and result = "typeDecl" or + exists(FriendDecl e | e.getFile() = this) and result = "friendDecl" + } + + /** + * Gets the Halstead "n1" metric: this is the total number of distinct operators + * in this file. Operators are defined as in the "N1" metric (`getHalsteadN1`). + */ + int getHalsteadN1Distinct() { + result = + 1 + // avoid 0 values + count(string s | exists(Operation op | op.getFile() = this and s = op.getOperator())) + + count(string s | s = getAUsedHalsteadN1Operator()) + } + + /** + * Gets the Halstead "n2" metric: this is the number of distinct operands in this + * file. An operand is either a variable, constant, type name, or function name. + */ + int getHalsteadN2Distinct() { + result = + 1 + // avoid 0 values + count(string s | exists(Access a | a.getFile() = this and s = a.getTarget().getName())) + + count(Function f | exists(FunctionCall fc | fc.getFile() = this and f = fc.getTarget())) + + // Approximate: count declarations once more to account for the type name + count(Declaration d | d.getFile() = this) + } + + /** + * Gets the Halstead length of this file. This is the sum of the N1 and N2 Halstead metrics. + */ + int getHalsteadLength() { + result = this.getHalsteadN1() + this.getHalsteadN2() + } + + /** + * Gets the Halstead vocabulary size of this file. This is the sum of the n1 and n2 Halstead metrics. + */ + int getHalsteadVocabulary() { + result = this.getHalsteadN1Distinct() + this.getHalsteadN2Distinct() + } + + /** + * Gets the Halstead volume of this file. This is the Halstead size multiplied by the log of the + * Halstead vocabulary. It represents the information content of the file. + */ + float getHalsteadVolume() { + result = this.getHalsteadLength().(float) * this.getHalsteadVocabulary().log2() + } + + /** + * Gets the Halstead difficulty value of this file. This is proportional to the number of unique + * operators, and further proportional to the ratio of total operands to unique operands. + */ + float getHalsteadDifficulty() { + result = (float)(this.getHalsteadN1Distinct() * this.getHalsteadN2()) / (float)(2 * this.getHalsteadN2Distinct()) + } + + /** + * Gets the Halstead level of this file. This is the inverse of the difficulty of the file. + */ + float getHalsteadLevel() { + exists(float difficulty | + difficulty = this.getHalsteadDifficulty() and + if difficulty != 0.0 then result = 1.0 / difficulty else result = 0.0 + ) + } + + /** + * Gets the Halstead implementation effort for this file. This is the product of the volume and difficulty. + */ + float getHalsteadEffort() { + result = this.getHalsteadVolume() * this.getHalsteadDifficulty() + } + + /** + * Gets the Halstead 'delivered bugs' metric for this file. This metric correlates with the complexity of + * the software, but is known to be an underestimate of bug counts. + */ + float getHalsteadDeliveredBugs() { + result = this.getHalsteadEffort().pow(2.0/3.0) / 3000.0 + } + + + /** Gets a file dependency of this file. */ + File getAFileDependency() { + dependsOnFileSimple(this, result.getMetrics()) + } + +} + +private predicate aClassFile(Class c, File file) +{ + c.getDefinitionLocation().getFile() = file +} + +private pragma[noopt] +predicate dependsOnFileSimple(MetricFile source, MetricFile dest) { + // class derives from classs + exists(Class fromClass, Class toClass | + aClassFile(fromClass, source) and + fromClass.derivesFrom(toClass) and + aClassFile(toClass, dest) + ) + or + // class nested in another class + exists(Class fromClass, Class toClass | + aClassFile(fromClass, source) and + fromClass.getDeclaringType() = toClass and + toClass.getFile() = dest + ) + or + // class has friend class + exists(Class fromClass, Class toClass, FriendDecl fd | + aClassFile(fromClass, source) and + fromClass.getAFriendDecl() = fd and + fd.getFriend() = toClass and + toClass instanceof Class and + dest = toClass.getFile() + ) + or exists(FunctionCall ca, Function f | + ca instanceof FunctionCall and + ca.getFile() = source and ca.getTarget() = f and f.getFile() = dest + and not f.isMultiplyDefined() + and not exists(Function ef | ef = ca.getEnclosingFunction() and ef.isMultiplyDefined()) + ) + or exists(Access a, Declaration d | + a instanceof Access and + a.getFile() = source and a.getTarget() = d and d.getFile() = dest + and not exists(Function ef | ef = a.getEnclosingFunction() and ef.isMultiplyDefined()) + ) + or exists(Variable v, VariableDeclarationEntry e, Type vt, UserType t | + e instanceof VariableDeclarationEntry and + e.getFile() = source and v.getADeclarationEntry() = e and vt = v.getType() and + vt.refersTo(t) and t instanceof UserType and t.getFile() = dest + ) + or exists(Function f, FunctionDeclarationEntry e, Type ft, UserType t | + e instanceof FunctionDeclarationEntry and + e.getFile() = source and f.getADeclarationEntry() = e and ft = f.getType() and + ft.refersTo(t) and t instanceof UserType and t.getFile() = dest + ) + or exists(MacroInvocation mi, Macro m | + mi instanceof MacroInvocation and + mi.getFile() = source and mi.getMacro() = m and + m.getFile() = dest + ) + + or exists(TypedefType t, TypeDeclarationEntry e, Type bt, UserType u | + e instanceof TypeDeclarationEntry and + e.getFile() = source and t.getADeclarationEntry() = e and bt = t.getBaseType() and + bt.refersTo(u) and u instanceof UserType and u.getFile() = dest + ) + or exists(Cast c, Type t, UserType u | + c instanceof Cast and + c.getFile() = source and c.getType() = t and + t.refersTo(u) and u instanceof UserType and u.getFile() = dest + and not exists(Function ef | ef = c.getEnclosingFunction() and ef.isMultiplyDefined()) + ) + } diff --git a/cpp/ql/src/semmle/code/cpp/metrics/MetricFunction.qll b/cpp/ql/src/semmle/code/cpp/metrics/MetricFunction.qll new file mode 100644 index 000000000000..8530491980e5 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/metrics/MetricFunction.qll @@ -0,0 +1,354 @@ +import cpp + +/** + * A wrapper that provides metrics for a C/C++ function. + */ +class MetricFunction extends Function { + + /** Gets the number of parameters. */ + override int getNumberOfParameters() { + result = count(this.getAParameter()) + } + + /** Gets the number of lines in this function. */ + int getNumberOfLines() { + numlines(this,result,_,_) + } + + /** Gets the number of lines of code in this function. */ + int getNumberOfLinesOfCode() { + numlines(this,_,result,_) + } + + /** Gets the number of lines of comments in this function. */ + int getNumberOfLinesOfComments() { + numlines(this,_,_,result) + } + + /** Gets the ratio of lines of comments to total lines in this function (between 0.0 and 1.0). */ + float getCommentRatio() { + if this.getNumberOfLines() = 0 then + result = 0.0 + else + result = this.getNumberOfLinesOfComments().(float) / this.getNumberOfLines().(float) + } + + /** Gets the number of function calls in this function. */ + int getNumberOfCalls() { + // Checking that the name of the target exists is a workaround for a DB inconsistency + result = count(FunctionCall c | c.getEnclosingFunction() = this and not (c.getTarget() instanceof Operator) and exists(c.getTarget().getName())) + } + + /** + * Gets the cyclomatic complexity of this function. This is defined as the + * number of branching statements (`if`, `while`, `do`, `for`, and + * non-fallthrough `case`) plus the number of branching expressions (`?`, + * `&&`, and `||`) plus one. + */ + int getCyclomaticComplexity() { + result = 1 + cyclomaticComplexityBranches(getBlock()) + and not this.isMultiplyDefined() + } + + /** + * Gets the branching complexity of this function. This is a measure derived + * from cyclomatic complexity, but it reflects only the branches that make + * the code difficult to read (as opposed to cyclomatic complexity, which + * attempts to evaluate how difficult the code is to test). + */ + int getBranchingComplexity() { + result = + count(IfStmt stmt | stmt.getEnclosingFunction() = this and not stmt.isInMacroExpansion()) + + count(WhileStmt stmt | stmt.getEnclosingFunction() = this and not stmt.isInMacroExpansion()) + + count(DoStmt stmt | stmt.getEnclosingFunction() = this and not stmt.isInMacroExpansion()) + + count(ForStmt stmt | stmt.getEnclosingFunction() = this and not stmt.isInMacroExpansion()) + + count(SwitchStmt stmt | stmt.getEnclosingFunction() = this and not stmt.isInMacroExpansion()) + + 1 + and not this.isMultiplyDefined() + } + + /** + * Gets the number of incoming dependencies: functions that call or access + * this function. + */ + int getAfferentCoupling() { + result = count(Function f | + exists(Locatable l | + f.calls(this, l) or + f.accesses(this, l) + ) + ) + } + + /** + * Gets the number of outgoing dependencies: functions that are called or + * accessed by this function. + */ + int getEfferentCoupling() { + result = count(Function f | + exists(Locatable l | + this.calls(f, l) or + this.accesses(f, l) + ) + ) + } + + /* + * Halstead Metrics + */ + + /** + * Gets the Halstead "N1" metric: this is the total number of operators in + * this function. Operators are taken to be all operators in expressions + * (`+`, `*`, `&`, `->`, `=`, ...) as well as most statements. + */ + int getHalsteadN1() { + result = + 1 + // account for the function itself + count(Operation op | op.getEnclosingFunction() = this) + + count(CommaExpr e | e.getEnclosingFunction() = this) + + count(ReferenceToExpr e | e.getEnclosingFunction() = this) + + count(PointerDereferenceExpr e | e.getEnclosingFunction() = this) + + count(Cast e | e.getEnclosingFunction() = this) + + count(SizeofOperator e | e.getEnclosingFunction() = this) + + count(TypeidOperator e | e.getEnclosingFunction() = this) + + count(ControlStructure s | s.getEnclosingFunction() = this) + + count(JumpStmt s | s.getEnclosingFunction() = this) + + count(ReturnStmt s | s.getEnclosingFunction() = this) + + count(SwitchCase c | c.getEnclosingFunction() = this) + + // Count the 'else' branches + count(IfStmt s | s.getEnclosingFunction() = this and s.hasElse()) + } + + /** + * Gets the Halstead "N2" metric: this is the total number of operands in this + * function. An operand is either a variable, constant, type name, or function name. + */ + int getHalsteadN2() { + result = + 1 + // account for the function itself + count(Access a | a.getEnclosingFunction() = this) + + count(FunctionCall fc | fc.getEnclosingFunction() = this) + + // Approximate: count declarations twice to account for the type name + // and the identifier + 2 * count(Declaration d | d.getParentScope+() = this) + } + + /** + * Gets the Halstead "n1" metric: this is the total number of distinct operators + * in this function. Operators (as in the N1 metric) are all operators in expressions + * as well as most statements. + */ + int getHalsteadN1Distinct() { + exists( + int comma, int refTo, int dereference, int cCast, int staticCast, int constCast, int reinterpretCast, int dynamicCast, + int sizeofExpr, int sizeofType, int ifVal, int switchVal, int forVal, int doVal, int whileVal, int gotoVal, int continueVal, + int breakVal, int returnVal, int caseVal, int elseVal | + + (if exists(CommaExpr e | e.getEnclosingFunction() = this) then comma = 1 else comma = 0) and + (if exists(ReferenceToExpr e | e.getEnclosingFunction() = this) then refTo = 1 else refTo = 0) and + (if exists(PointerDereferenceExpr e | e.getEnclosingFunction() = this) then dereference = 1 else dereference = 0) and + (if exists(CStyleCast e | e.getEnclosingFunction() = this) then cCast = 1 else cCast = 0) and + (if exists(StaticCast e | e.getEnclosingFunction() = this) then staticCast = 1 else staticCast = 0) and + (if exists(ConstCast e | e.getEnclosingFunction() = this) then constCast = 1 else constCast = 0) and + (if exists(ReinterpretCast e | e.getEnclosingFunction() = this) then reinterpretCast = 1 else reinterpretCast = 0) and + (if exists(DynamicCast e | e.getEnclosingFunction() = this) then dynamicCast = 1 else dynamicCast = 0) and + (if exists(SizeofExprOperator e | e.getEnclosingFunction() = this) then sizeofExpr = 1 else sizeofExpr = 0) and + (if exists(SizeofTypeOperator e | e.getEnclosingFunction() = this) then sizeofType = 1 else sizeofType = 0) and + (if exists(IfStmt e | e.getEnclosingFunction() = this) then ifVal = 1 else ifVal = 0) and + (if exists(SwitchStmt e | e.getEnclosingFunction() = this) then switchVal = 1 else switchVal = 0) and + (if exists(ForStmt e | e.getEnclosingFunction() = this) then forVal = 1 else forVal = 0) and + (if exists(DoStmt e | e.getEnclosingFunction() = this) then doVal = 1 else doVal = 0) and + (if exists(WhileStmt e | e.getEnclosingFunction() = this) then whileVal = 1 else whileVal = 0) and + (if exists(GotoStmt e | e.getEnclosingFunction() = this) then gotoVal = 1 else gotoVal = 0) and + (if exists(ContinueStmt e | e.getEnclosingFunction() = this) then continueVal = 1 else continueVal = 0) and + (if exists(BreakStmt e | e.getEnclosingFunction() = this) then breakVal = 1 else breakVal = 0) and + (if exists(ReturnStmt e | e.getEnclosingFunction() = this) then returnVal = 1 else returnVal = 0) and + (if exists(SwitchCase e | e.getEnclosingFunction() = this) then caseVal = 1 else caseVal = 0) and + (if exists(IfStmt s | s.getEnclosingFunction() = this and s.hasElse()) then elseVal = 1 else elseVal = 0) + and + result = + 1 + // account for the function itself + count(string s | exists(Operation op | op.getEnclosingFunction() = this and s = op.getOperator())) + + comma + + refTo + + dereference + + cCast + staticCast + constCast + reinterpretCast + dynamicCast + + sizeofExpr + sizeofType + + ifVal + switchVal + forVal + doVal + whileVal + + gotoVal + continueVal + breakVal + + returnVal + + caseVal + + elseVal + ) + } + + /** + * Gets the Halstead "n2" metric: this is the number of distinct operands in this + * function. An operand is either a variable, constant, type name, or function name. + */ + int getHalsteadN2Distinct() { + result = + 1 + // account for the function itself + count(string s | exists(Access a | a.getEnclosingFunction() = this and s = a.getTarget().getName())) + + count(Function f | exists(FunctionCall fc | fc.getEnclosingFunction() = this and f = fc.getTarget())) + + // Approximate: count declarations once more to account for the type name + count(Declaration d | d.getParentScope+() = this) + } + + /** + * Gets the Halstead length of this function. This is the sum of the N1 and N2 Halstead metrics. + */ + int getHalsteadLength() { + result = this.getHalsteadN1() + this.getHalsteadN2() + } + + /** + * Gets the Halstead vocabulary size of this function. This is the sum of the n1 and n2 Halstead metrics. + */ + int getHalsteadVocabulary() { + result = this.getHalsteadN1Distinct() + this.getHalsteadN2Distinct() + } + + /** + * Gets the Halstead volume of this function. This is the Halstead size multiplied by the log of the + * Halstead vocabulary. It represents the information content of the function. + */ + float getHalsteadVolume() { + result = this.getHalsteadLength().(float) * this.getHalsteadVocabulary().log2() + } + + /** + * Gets the Halstead difficulty value of this function. This is proportional to the number of unique + * operators, and further proportional to the ratio of total operands to unique operands. + */ + float getHalsteadDifficulty() { + result = (float)(this.getHalsteadN1Distinct() * this.getHalsteadN2()) / (float)(2 * this.getHalsteadN2Distinct()) + } + + /** + * Gets the Halstead level of this function. This is the inverse of the difficulty of the function. + */ + float getHalsteadLevel() { + exists(float difficulty | + difficulty = this.getHalsteadDifficulty() and + if difficulty != 0.0 then result = 1.0 / difficulty else result = 0.0 + ) + } + + /** + * Gets the Halstead implementation effort for this function. This is the product of the volume and difficulty. + */ + float getHalsteadEffort() { + result = this.getHalsteadVolume() * this.getHalsteadDifficulty() + } + + /** + * Gets the Halstead 'delivered bugs' metric for this function. This metric correlates with the complexity of + * the software, but is known to be an underestimate of bug counts. + */ + float getHalsteadDeliveredBugs() { + result = this.getHalsteadEffort().pow(2.0/3.0) / 3000.0 + } + + /** + * Gets the maximum nesting level of complex statements such as if, while in the function. A nesting depth of + * 2 would mean that there is, for example, an if statement nested in another if statement. + */ + int getNestingDepth() { + result = max(Stmt s, int aDepth | s.getEnclosingFunction() = this and nestingDepth(s, aDepth) | aDepth) + and not isMultiplyDefined() + } + +} + +// Branching points in the sense of cyclomatic complexity are binary, +// so there should be a branching point for each non-default switch +// case (ignoring those that just fall through to the next case). +private +predicate branchingSwitchCase(SwitchCase sc) { + not sc.isDefault() and + not sc.getASuccessor() instanceof SwitchCase and + not defaultFallThrough(sc) +} + +private +predicate defaultFallThrough(SwitchCase sc) { + sc.isDefault() or + defaultFallThrough(sc.getAPredecessor()) +} + +// A branching statement used for the computation of cyclomatic complexity. +private +predicate branchingStmt(Stmt stmt) { + stmt instanceof IfStmt or + stmt instanceof WhileStmt or + stmt instanceof DoStmt or + stmt instanceof ForStmt or + branchingSwitchCase(stmt) +} + +// A branching expression used for the computation of cyclomatic complexity. +private +predicate branchingExpr(Expr expr) { + expr instanceof NotExpr or + expr instanceof LogicalAndExpr or + expr instanceof LogicalOrExpr or + expr instanceof ConditionalExpr +} + +/** + * Gets the number of branching statements and expressions in a block. This is + * for computing cyclomatic complexity. + */ +int cyclomaticComplexityBranches(Block b) { + result = + count(Stmt stmt | + branchingStmt(stmt) and b.getAChild+() = stmt + and not stmt.isInMacroExpansion()) + + count(Expr expr | + branchingExpr(expr) and b.getAChild+() = expr.getEnclosingStmt() + and not expr.isInMacroExpansion()) +} + +/** + * Gets the parent of a statement, excluding some common cases that don't really + * make sense for nesting depth. An example is: + * `if (...) { } else if (...) { }`: we don't consider the second if nested. + * Blocks are also skipped, as are parents that have the same location as the + * child (typically they come from macros). + * */ +private +predicate realParent(Stmt inner, Stmt outer) { + if skipParent(inner) then + realParent(inner.getParentStmt(), outer) + else + outer = inner.getParentStmt() +} + +private predicate startsAt(Stmt s, File f, int line, int col) { + exists(Location loc | loc = s.getLocation() | + f = loc.getFile() and + line = loc.getStartLine() and + col = loc.getStartColumn() + ) +} + +private +predicate skipParent(Stmt s) { + exists(Stmt parent | parent = s.getParentStmt() | + (s instanceof IfStmt and parent.(IfStmt).getElse() = s) + or + parent instanceof Block + or + exists(File f, int startLine, int startCol | + startsAt(s, f, startLine, startCol) and + startsAt(parent, f, startLine, startCol) + ) + ) +} + +private +predicate nestingDepth(Stmt s, int depth) { + depth = count(Stmt enclosing | realParent+(s, enclosing)) +} diff --git a/cpp/ql/src/semmle/code/cpp/metrics/MetricNamespace.qll b/cpp/ql/src/semmle/code/cpp/metrics/MetricNamespace.qll new file mode 100644 index 000000000000..0aaa3bf18c70 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/metrics/MetricNamespace.qll @@ -0,0 +1,83 @@ +import cpp + +/** + * A wrapper that provides metrics for a C/C++ namespace. + */ +class MetricNamespace extends Namespace { + + /** Gets the number of incoming dependencies from other namespaces. */ + int getAfferentCoupling() { + result = count(MetricNamespace that | that.getANamespaceDependency() = this) + } + + /** Gets the number of outgoing dependencies on other namespaces. */ + int getEfferentCoupling() { + result = count(MetricNamespace that | this.getANamespaceDependency() = that) + } + + /** + * Gets the _instability_ of this namespace. Instability is a measure of how + * likely a namespace is to be influenced by changes to other namespace. If + * this metric value is high, it is easily influenced, if it is low, the + * impact is likely to be minimal. Instability is estimated as the number of + * outgoing dependencies relative to the total number of dependencies. + */ + float getInstability() { + exists(int ecoupling, int sumcoupling | + ecoupling = this.getEfferentCoupling() and + sumcoupling = ecoupling + this.getAfferentCoupling() and + sumcoupling > 0 and + result = ecoupling / ((float)sumcoupling)) + } + + /** + * Gets the _abstractness_ of this namespace. Abstractness measures the + * proportion of abstract classes in a namespace relative to the total number + * of classes in that namespace. A highly abstract namespace (where the + * metric value is close 1) that is furthermore instable is likely to be + * useless: the class hierarchy has been over-engineered, and all those + * abstract classes are not heavily used. + */ + float getAbstractness() { + exists(int i, int j | i = count(Class c | c.getNamespace()=this) and + j = count(Class c | c.getNamespace()=this and + c.isAbstract()) and + result = j / ((float)i) and i > 0) + } + + /** + * Gets the _distance from main sequence_ of this namespace. This measure + * intends to capture the tradeoff between abstractness and instability: the + * ideal situation occurs when the sum of abstractness and instability is + * one. That is, a namespace is completely abstract and stable + * (abstractness=1 and instability=0) or it is concrete and instable + * (abstractness=0 and instability=1). We thus measure the distance from that + * ideal situation. + */ + float getDistanceFromMain() { + exists(float r | + r = this.getAbstractness() + this.getInstability() - 1 + and + ( (r >= 0 and result = r) + or + (r < 0 and result = -r) ) ) + } + + /** Gets a namespace dependency of this element. */ + MetricNamespace getANamespaceDependency() { + exists(MetricClass c | c.getNamespace() = this + and c.getAClassDependency().getNamespace() = result) + or exists(FunctionCall c | c.getEnclosingFunction().getNamespace() = this + and c.getTarget().getNamespace() = result) + or exists(FunctionCall c | c.getEnclosingVariable().getNamespace() = this + and c.getTarget().getNamespace() = result) + or exists(Access a | a.getEnclosingFunction().getNamespace() = this + and a.getTarget().getNamespace() = result) + or exists(Access a | a.getEnclosingVariable().getNamespace() = this + and a.getTarget().getNamespace() = result) + or exists(Variable v, UserType t | v.getNamespace() = this + and v.getType().refersTo(t) and t.getNamespace() = result) + or exists(Function f, UserType t | f.getNamespace() = this + and f.getType().refersTo(t) and t.getNamespace() = result) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/models/Models.qll b/cpp/ql/src/semmle/code/cpp/models/Models.qll new file mode 100644 index 000000000000..9fefd5243f1c --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/models/Models.qll @@ -0,0 +1,7 @@ +private import implementations.Inet +private import implementations.Memcpy +private import implementations.Printf +private import implementations.Pure +private import implementations.Strcat +private import implementations.Strcpy +private import implementations.Strftime diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Inet.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Inet.qll new file mode 100644 index 000000000000..3d47e5c88166 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Inet.qll @@ -0,0 +1,187 @@ +import semmle.code.cpp.models.interfaces.Taint +import semmle.code.cpp.models.interfaces.ArrayFunction + +class InetNtoa extends TaintFunction { + InetNtoa() { + hasQualifiedName("inet_ntoa") + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isInParameter(0) and + output.isOutReturnPointer() + } +} + +class InetAton extends TaintFunction, ArrayFunction { + InetAton() { + hasQualifiedName("inet_aton") + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isInParameterPointer(0) and + output.isOutParameterPointer(1) + } + + override predicate hasArrayInput(int bufParam) { + bufParam = 0 + } + + override predicate hasArrayOutput(int bufParam) { + bufParam = 1 + } + + override predicate hasArrayWithNullTerminator(int bufParam) { + bufParam = 0 + } + + override predicate hasArrayWithFixedSize(int bufParam, int elemCount) { + bufParam = 1 and + elemCount = 1 + } +} + +class InetAddr extends TaintFunction, ArrayFunction { + InetAddr() { + hasQualifiedName("inet_addr") + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isInParameterPointer(0) and + output.isOutReturnValue() + } + + override predicate hasArrayInput(int bufParam) { + bufParam = 0 + } + + override predicate hasArrayWithNullTerminator(int bufParam) { + bufParam = 0 + } +} + +class InetNetwork extends TaintFunction, ArrayFunction { + InetNetwork() { + hasQualifiedName("inet_network") + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isInParameterPointer(1) and + output.isOutReturnValue() + } + + override predicate hasArrayInput(int bufParam) { + bufParam = 0 + } + + override predicate hasArrayWithNullTerminator(int bufParam) { + bufParam = 0 + } +} + +class InetMakeaddr extends TaintFunction { + InetMakeaddr() { + hasQualifiedName("inet_makeaddr") + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + ( + input.isInParameter(0) or + input.isInParameter(1) + ) and + output.isOutReturnValue() + } +} + +class InetLnaof extends TaintFunction { + InetLnaof() { + hasQualifiedName("inet_lnaof") + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isInParameter(0) and + output.isOutReturnValue() + } +} + +class InetNetof extends TaintFunction { + InetNetof() { + hasQualifiedName("inet_netof") + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isInParameter(0) and + output.isOutReturnValue() + } +} + +class InetPton extends TaintFunction, ArrayFunction { + InetPton() { + hasQualifiedName("inet_pton") + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + ( + input.isInParameter(0) or + input.isInParameterPointer(1) + ) and + output.isOutParameterPointer(2) + } + + override predicate hasArrayInput(int bufParam) { + bufParam = 1 + } + + override predicate hasArrayOutput(int bufParam) { + bufParam = 2 + } + + override predicate hasArrayWithNullTerminator(int bufParam) { + bufParam = 1 + } + + override predicate hasArrayWithUnknownSize(int bufParam) { + bufParam = 2 + } +} + +class Gethostbyname extends TaintFunction, ArrayFunction { + Gethostbyname() { + hasQualifiedName("gethostbyname") + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isInParameterPointer(0) and + output.isOutReturnPointer() + } + + override predicate hasArrayInput(int bufParam) { + bufParam = 0 + } + + override predicate hasArrayWithNullTerminator(int bufParam) { + bufParam = 0 + } +} + +class Gethostbyaddr extends TaintFunction, ArrayFunction { + Gethostbyaddr() { + hasQualifiedName("gethostbyaddr") + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + ( + input.isInParameterPointer(0) or + input.isInParameter(1) or + input.isInParameter(2) + ) and + output.isOutReturnPointer() + } + + override predicate hasArrayInput(int bufParam) { + bufParam = 0 + } + + override predicate hasArrayWithNullTerminator(int bufParam) { + bufParam = 0 + } +} \ No newline at end of file diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Memcpy.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Memcpy.qll new file mode 100644 index 000000000000..e4cbe6cd3db7 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Memcpy.qll @@ -0,0 +1,55 @@ +import semmle.code.cpp.Function +import semmle.code.cpp.models.interfaces.ArrayFunction +import semmle.code.cpp.models.interfaces.DataFlow +import semmle.code.cpp.models.interfaces.Taint + +/** + * The standard functions `memcpy` and `memmove`, and the gcc variant + * `__builtin___memcpy_chk` + */ +class MemcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction { + MemcpyFunction() { + this.hasName("memcpy") or + this.hasName("memmove") or + this.hasName("__builtin___memcpy_chk") + } + + override predicate hasArrayInput(int bufParam) { + bufParam = 1 + } + + override predicate hasArrayOutput(int bufParam) { + bufParam = 0 + } + + override predicate hasDataFlow(FunctionInput input, FunctionOutput output) { + ( + input.isInParameterPointer(1) and + output.isOutParameterPointer(0) + ) or ( + input.isInParameterPointer(1) and + output.isOutReturnPointer() + ) or ( + input.isInParameter(0) and + output.isOutReturnValue() + ) + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + ( + input.isInParameter(2) and + output.isOutParameterPointer(0) + ) or ( + input.isInParameter(2) and + output.isOutReturnPointer() + ) + } + + override predicate hasArrayWithVariableSize(int bufParam, int countParam) { + ( + bufParam = 0 or + bufParam = 1 + ) and + countParam = 2 + } +} \ No newline at end of file diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Printf.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Printf.qll new file mode 100644 index 000000000000..06c6a86e4a10 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Printf.qll @@ -0,0 +1,194 @@ +import semmle.code.cpp.models.interfaces.FormattingFunction + +/** + * The standard functions `printf`, `wprintf` and their glib variants. + */ +class Printf extends FormattingFunction { + Printf() { + this instanceof TopLevelFunction and + ( + hasGlobalName("printf") or + hasGlobalName("printf_s") or + hasGlobalName("wprintf") or + hasGlobalName("wprintf_s") or + hasGlobalName("g_printf") + ) + } + + override int getFormatParameterIndex() { result=0 } + override predicate isWideCharDefault() { + hasGlobalName("wprintf") or + hasGlobalName("wprintf_s") + } +} + +/** + * The standard functions `fprintf`, `fwprintf` and their glib variants. + */ +class Fprintf extends FormattingFunction { + Fprintf() { this instanceof TopLevelFunction and (hasGlobalName("fprintf") or hasGlobalName("fwprintf") or hasGlobalName("g_fprintf"))} + + override int getFormatParameterIndex() { result=1 } + override predicate isWideCharDefault() { hasGlobalName("fwprintf") } + override int getOutputParameterIndex() { result=0 } +} + +/** + * The standard function `sprintf` and its Microsoft and glib variants. + */ +class Sprintf extends FormattingFunction { + Sprintf() { + this instanceof TopLevelFunction and + ( + hasGlobalName("sprintf") or + hasGlobalName("_sprintf_l") or + hasGlobalName("__swprintf_l") or + hasGlobalName("wsprintf") or + hasGlobalName("g_strdup_printf") or + hasGlobalName("g_sprintf") or + hasGlobalName("__builtin___sprintf_chk") + ) + } + + override predicate isWideCharDefault() { + getParameter(getFormatParameterIndex()).getType().getUnspecifiedType().(PointerType).getBaseType().getSize() > 1 + } + + override int getFormatParameterIndex() { + hasGlobalName("g_strdup_printf") and result = 0 + or + hasGlobalName("__builtin___sprintf_chk") and result = 3 + or + getQualifiedName() != "g_strdup_printf" and + getQualifiedName() != "__builtin___sprintf_chk" and + result = 1 + } + override int getOutputParameterIndex() { + not hasGlobalName("g_strdup_printf") and result = 0 + } + + override int getFirstFormatArgumentIndex() { + if hasGlobalName("__builtin___sprintf_chk") then result = 4 + else result = getNumberOfParameters() + } +} + +/** + * The standard functions `snprintf` and `swprintf`, and their + * Microsoft and glib variants. + */ +class Snprintf extends FormattingFunction { + Snprintf() { + this instanceof TopLevelFunction and ( + hasGlobalName("snprintf") // C99 defines snprintf + or hasGlobalName("swprintf") // The s version of wide-char printf is also always the n version + // Microsoft has _snprintf as well as several other variations + or hasGlobalName("sprintf_s") + or hasGlobalName("snprintf_s") + or hasGlobalName("swprintf_s") + or hasGlobalName("_snprintf") + or hasGlobalName("_snprintf_s") + or hasGlobalName("_snprintf_l") + or hasGlobalName("_snprintf_s_l") + or hasGlobalName("_snwprintf") + or hasGlobalName("_snwprintf_s") + or hasGlobalName("_snwprintf_l") + or hasGlobalName("_snwprintf_s_l") + or hasGlobalName("_sprintf_s_l") + or hasGlobalName("_swprintf_l") + or hasGlobalName("_swprintf_s_l") + or hasGlobalName("g_snprintf") + or hasGlobalName("wnsprintf") + or hasGlobalName("__builtin___snprintf_chk") + ) + } + + override int getFormatParameterIndex() { + if getName().matches("%\\_l") + then result = getFirstFormatArgumentIndex() - 2 + else result = getFirstFormatArgumentIndex() - 1 + } + + override predicate isWideCharDefault() { + getParameter(getFormatParameterIndex()).getType().getUnspecifiedType().(PointerType).getBaseType().getSize() > 1 + } + override int getOutputParameterIndex() { result=0 } + + override int getFirstFormatArgumentIndex() { + exists(string name | + name = getQualifiedName() + and ( + name = "__builtin___snprintf_chk" and + result = 5 + or + name != "__builtin___snprintf_chk" and + result = getNumberOfParameters() + ) + ) + } + + /** + * Holds if this function returns the length of the formatted string + * that would have been output, regardless of the amount of space + * in the buffer. + */ + predicate returnsFullFormatLength() { + hasGlobalName("snprintf") or + hasGlobalName("g_snprintf") or + hasGlobalName("__builtin___snprintf_chk") or + hasGlobalName("snprintf_s") + } + + override int getSizeParameterIndex() { + result = 1 + } +} + +/** + * The Microsoft `StringCchPrintf` function and variants. + */ +class StringCchPrintf extends FormattingFunction { + StringCchPrintf() { + this instanceof TopLevelFunction and ( + hasGlobalName("StringCchPrintf") + or hasGlobalName("StringCchPrintfEx") + or hasGlobalName("StringCchPrintf_l") + or hasGlobalName("StringCchPrintf_lEx") + or hasGlobalName("StringCbPrintf") + or hasGlobalName("StringCbPrintfEx") + or hasGlobalName("StringCbPrintf_l") + or hasGlobalName("StringCbPrintf_lEx") + ) + } + + override int getFormatParameterIndex() { + if getName().matches("%Ex") + then result = 5 + else result = 2 + } + + override predicate isWideCharDefault() { + getParameter(getFormatParameterIndex()).getType().getUnspecifiedType().(PointerType).getBaseType().getSize() > 1 + } + + override int getOutputParameterIndex() { + result = 0 + } + + override int getSizeParameterIndex() { + result = 1 + } +} + +/** + * The standard function `syslog`. + */ +class Syslog extends FormattingFunction { + Syslog() { + this instanceof TopLevelFunction and ( + hasGlobalName("syslog") + ) + } + + override int getFormatParameterIndex() { result=1 } +} diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Pure.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Pure.qll new file mode 100644 index 000000000000..7bbf09536b94 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Pure.qll @@ -0,0 +1,57 @@ +import semmle.code.cpp.models.interfaces.ArrayFunction +import semmle.code.cpp.models.interfaces.Taint + +class PureFunction extends ArrayFunction, TaintFunction { + PureFunction() { + exists(string name | + hasName(name) and + ( + name = "abs" + or name = "atof" + or name = "atoi" + or name = "atol" + or name = "atoll" + or name = "labs" + or name = "strcasestr" + or name = "strchnul" + or name = "strchr" + or name = "strchrnul" + or name = "strcmp" + or name = "strcspn" + or name = "strlen" + or name = "strncmp" + or name = "strnlen" + or name = "strrchr" + or name = "strspn" + or name = "strstr" + or name = "strtod" + or name = "strtof" + or name = "strtol" + or name = "strtoll" + or name = "strtoq" + or name = "strtoul" + ) + ) + } + + override predicate hasArrayInput(int bufParam) { + getParameter(bufParam).getType().getUnspecifiedType() instanceof PointerType + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + exists (ParameterIndex i | + input.isInParameter(i) or + ( + input.isInParameterPointer(i) and + getParameter(i).getType().getUnspecifiedType() instanceof PointerType + ) + ) and + ( + ( + output.isOutReturnPointer() and + getType().getUnspecifiedType() instanceof PointerType + ) or + output.isOutReturnValue() + ) + } +} \ No newline at end of file diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll new file mode 100644 index 000000000000..5e02939dfe66 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll @@ -0,0 +1,69 @@ + +import semmle.code.cpp.models.interfaces.ArrayFunction +import semmle.code.cpp.models.interfaces.DataFlow +import semmle.code.cpp.models.interfaces.Taint + + +/** + * The standard function `strcat` and its wide, sized, and Microsoft variants. + */ +class StrcatFunction extends TaintFunction, DataFlowFunction, ArrayFunction { + StrcatFunction() { + exists(string name | name = getName()| + name = "strcat" // strcat(dst, src) + or name = "strncat" // strncat(dst, src, max_amount) + or name = "wcscat" // wcscat(dst, src) + or name = "_mbscat" // _mbscat(dst, src) + or name = "wcsncat" // wcsncat(dst, src, max_amount) + or name = "_mbsncat" // _mbsncat(dst, src, max_amount) + or name = "_mbsncat_l" // _mbsncat_l(dst, src, max_amount, locale) + ) + } + + override predicate hasDataFlow(FunctionInput input, FunctionOutput output) { + input.isInParameter(0) and + output.isOutReturnValue() + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + exists(string name | name = getName() + | ( + ( + name = "strncat" or + name = "wcsncat" or + name = "_mbsncat" or + name = "_mbsncat_l" + ) and + input.isInParameter(2) and + output.isOutParameterPointer(0) + ) or ( + name = "_mbsncat_l" and + input.isInParameter(3) and + output.isOutParameterPointer(0) + ) + ) or ( + input.isInParameterPointer(0) and + output.isOutParameterPointer(0) + ) or ( + input.isInParameter(1) and + output.isOutParameterPointer(0) + ) + } + + override predicate hasArrayInput(int param) { + param = 0 or + param = 1 + } + + override predicate hasArrayOutput(int param) { + param = 0 + } + + override predicate hasArrayWithNullTerminator(int param) { + param = 1 + } + + override predicate hasArrayWithUnknownSize(int param) { + param = 0 + } +} \ No newline at end of file diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Strcpy.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Strcpy.qll new file mode 100644 index 000000000000..11043d1fbe73 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Strcpy.qll @@ -0,0 +1,89 @@ +import semmle.code.cpp.models.interfaces.ArrayFunction +import semmle.code.cpp.models.interfaces.DataFlow +import semmle.code.cpp.models.interfaces.Taint + + +/** + * The standard function `stract` and its wide, sized, and Microsoft variants. + */ +class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction { + StrcpyFunction() { + this.hasName("strcpy") or + this.hasName("_mbscpy") or + this.hasName("wcscpy") or + this.hasName("strncpy") or + this.hasName("_mbsncpy") or + this.hasName("wcsncpy") + } + + override predicate hasArrayInput(int bufParam) { + bufParam = 1 + } + + override predicate hasArrayOutput(int bufParam) { + bufParam = 0 + } + + override predicate hasArrayWithNullTerminator(int bufParam) { + bufParam = 1 + } + + override predicate hasArrayWithVariableSize(int bufParam, int countParam) { + ( + this.hasName("strncpy") or + this.hasName("_mbsncpy") or + this.hasName("wcsncpy") + ) and + bufParam = 0 and + countParam = 2 + } + + override predicate hasArrayWithUnknownSize(int bufParam) { + ( + this.hasName("strcpy") or + this.hasName("_mbscpy") or + this.hasName("wcscpy") + ) and + bufParam = 0 + } + + + override predicate hasDataFlow(FunctionInput input, FunctionOutput output) { + ( + ( + // These always copy the full value of the input buffer to the output + // buffer + this.hasName("strcpy") or + this.hasName("_mbscpy") or + this.hasName("wcscpy") + ) and ( + ( + input.isInParameterPointer(1) and + output.isOutParameterPointer(0) + ) or ( + input.isInParameterPointer(1) and + output.isOutReturnPointer() + ) + ) + ) or ( + input.isInParameter(0) and + output.isOutReturnValue() + ) + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + ( + // these may do only a partial copy of the input buffer to the output + // buffer + this.hasName("strncpy") or + this.hasName("_mbsncpy") or + this.hasName("wcsncpy") + ) and ( + input.isInParameter(2) or + input.isInParameterPointer(1) + ) and ( + output.isOutParameterPointer(0) or + output.isOutReturnPointer() + ) + } +} \ No newline at end of file diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Strftime.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Strftime.qll new file mode 100644 index 000000000000..da64cec94db5 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Strftime.qll @@ -0,0 +1,44 @@ +import semmle.code.cpp.models.interfaces.Taint +import semmle.code.cpp.models.interfaces.ArrayFunction + +class Strftime extends TaintFunction, ArrayFunction { + Strftime() { + hasQualifiedName("strftime") + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + ( + input.isInParameter(1) or + input.isInParameterPointer(2) or + input.isInParameterPointer(3) + ) + and + ( + output.isOutParameterPointer(0) or + output.isOutReturnValue() + ) + } + + override predicate hasArrayWithNullTerminator(int bufParam) { + bufParam = 2 + } + + override predicate hasArrayWithFixedSize(int bufParam, int elemCount) { + bufParam = 3 and + elemCount = 1 + } + + override predicate hasArrayWithVariableSize(int bufParam, int countParam) { + bufParam = 0 and + countParam = 1 + } + + override predicate hasArrayInput(int bufParam) { + bufParam = 2 or + bufParam = 3 + } + + override predicate hasArrayOutput(int bufParam) { + bufParam = 0 + } +} diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/ArrayFunction.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/ArrayFunction.qll new file mode 100644 index 000000000000..05dc013aaac4 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/ArrayFunction.qll @@ -0,0 +1,70 @@ +/** + * Provides an abstract class for accurate modeling of input and output buffers + * in library functions when source code is not available. To use this QL + * library, create a QL class extending `BufferFunction` with a characteristic + * predicate that selects the function or set of functions you are trying to + * model. Within that class, override the predicates provided by `BufferFunction` + * to match the flow within that function. Finally, add a private import + * statement to `CustomModels.qll` + */ + + import semmle.code.cpp.Function + import semmle.code.cpp.models.Models + + +/** + * A library function with input and/or output buffer parameters + */ +abstract class ArrayFunction extends Function { + /** + * Holds if parameter `bufParam` is a null-terminated buffer and the + * null-terminator will not be written past. + */ + predicate hasArrayWithNullTerminator(int bufParam) { + none() + } + + /** + * Holds if parameter `bufParam` should always point to a buffer with exactly + * `elemCount` elements. + */ + predicate hasArrayWithFixedSize(int bufParam, int elemCount) { + none() + } + + /** + * Holds if parameter `bufParam` should always point to a buffer with the + * number of elements indicated by `countParam`. + */ + predicate hasArrayWithVariableSize(int bufParam, int countParam) { + none() + } + + /** + * Holds if parameter `bufParam` points to a buffer with no fixed size and no + * size parameter, which is not null-terminated or which is null-terminated + * but for which the null value may be written past. For example, the first + * parameters of `sprintf` and `strcat`. + */ + predicate hasArrayWithUnknownSize(int bufParam) { + none() + } + + /** + * Holds if parameter `bufParam` is used as an input buffer. + * + * Note that this is not mutually exclusive with isOutBuffer. + */ + predicate hasArrayInput(int bufParam) { + none() + } + + /** + * Holds if parameter `bufParam` is used as an output buffer. + * + * Note that this is not mutually exclusive with isInBuffer. + */ + predicate hasArrayOutput(int bufParam) { + none() + } +} \ No newline at end of file diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/DataFlow.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/DataFlow.qll new file mode 100644 index 000000000000..b7aecc8cb25e --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/DataFlow.qll @@ -0,0 +1,24 @@ +/** + * Provides an abstract class for accurate dataflow modeling of library + * functions when source code is not available. To use this QL library, + * create a QL class extending `DataFlowFunction` with a characteristic + * predicate that selects the function or set of functions you are modeling. + * Within that class, override the predicates provided by `DataFlowFunction` + * to match the flow within that function. + */ + +import semmle.code.cpp.Function +import FunctionInputsAndOutputs +import semmle.code.cpp.models.Models + + +/** + * A library function for which a value is copied from a parameter or qualifier + * to an output buffer, return value, or qualifier. + * + * Note that this does not include partial copying of values or partial writes + * to destinations; that is covered by `TaintModel.qll`. + */ +abstract class DataFlowFunction extends Function { + abstract predicate hasDataFlow(FunctionInput input, FunctionOutput output); +} \ No newline at end of file diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/FormattingFunction.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/FormattingFunction.qll new file mode 100644 index 000000000000..2712348d880d --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/FormattingFunction.qll @@ -0,0 +1,38 @@ +/** + * Provides a class for modeling `printf`-style formatting functions. To use + * this QL library, create a QL class extending `DataFlowFunction` 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. + */ + +import semmle.code.cpp.Function +/** + * A standard library function that uses a `printf`-like formatting string. + */ +abstract class FormattingFunction extends Function { + /** Gets the position at which the format parameter occurs. */ + abstract int getFormatParameterIndex(); + + /** + * Holds if the default meaning of `%s` is a `wchar_t *`, rather than + * a `char *` (either way, `%S` will have the opposite meaning). + */ + predicate isWideCharDefault() { none() } + + /** + * Gets the position at which the output parameter, if any, occurs. + */ + int getOutputParameterIndex() { none() } + + /** + * Gets the position of the first format argument, corresponding with + * the first format specifier in the format string. + */ + int getFirstFormatArgumentIndex() { result = getNumberOfParameters() } + + /** + * Gets the position of the buffer size argument, if any. + */ + int getSizeParameterIndex() { none() } +} \ No newline at end of file diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/FunctionInputsAndOutputs.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/FunctionInputsAndOutputs.qll new file mode 100644 index 000000000000..db4e5a0faa01 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/FunctionInputsAndOutputs.qll @@ -0,0 +1,167 @@ +/** + * Provides a set of QL clcasses for indicating dataflows through a particular + * parameter, return value, or qualifier, as well as flows at one level of + * pointer indirection. + */ + +import semmle.code.cpp.Parameter + +/** + * An `int` that is a parameter index for some function. This is needed for binding in certain cases. + */ +class ParameterIndex extends int { + ParameterIndex() { exists(Parameter p | this = p.getIndex()) } +} + +newtype TFunctionInput = + TInParameter(ParameterIndex i) + or + TInParameterPointer(ParameterIndex i) + or + TInQualifier() + +class FunctionInput extends TFunctionInput { + abstract string toString(); + + predicate isInParameter(ParameterIndex index) { + none() + } + + predicate isInParameterPointer(ParameterIndex index) { + none() + } + + predicate isInQualifier() { + none() + } +} + +class InParameter extends FunctionInput, TInParameter { + ParameterIndex index; + + InParameter() { + this = TInParameter(index) + } + + override string toString() { + result = "InParameter " + index.toString() + } + + ParameterIndex getIndex() { + result = index + } + + override predicate isInParameter(ParameterIndex i) { + i = index + } +} + +class InParameterPointer extends FunctionInput, TInParameterPointer { + ParameterIndex index; + + InParameterPointer() { + this = TInParameterPointer(index) + } + + override string toString() { + result = "InParameterPointer " + index.toString() + } + + ParameterIndex getIndex() { + result = index + } + + override predicate isInParameterPointer(ParameterIndex i) { + i = index + } +} + +class InQualifier extends FunctionInput, TInQualifier { + override string toString() { + result = "InQualifier" + } + + override predicate isInQualifier() { + any() + } +} + +newtype TFunctionOutput = + TOutParameterPointer(ParameterIndex i) + or + TOutQualifier() + or + TOutReturnValue() + or + TOutReturnPointer() + + +class FunctionOutput extends TFunctionOutput { + abstract string toString(); + + predicate isOutParameterPointer(ParameterIndex i) { + none() + } + + predicate isOutQualifier() { + none() + } + + predicate isOutReturnValue() { + none() + } + + predicate isOutReturnPointer() { + none() + } +} + +class OutParameterPointer extends FunctionOutput, TOutParameterPointer { + ParameterIndex index; + + OutParameterPointer() { + this = TOutParameterPointer(index) + } + + override string toString() { + result = "OutParameterPointer " + index.toString() + } + + ParameterIndex getIndex() { + result = index + } + + override predicate isOutParameterPointer(ParameterIndex i) { + i = index + } +} + +class OutQualifier extends FunctionOutput, TOutQualifier { + override string toString() { + result = "OutQualifier" + } + + override predicate isOutQualifier() { + any() + } +} + +class OutReturnValue extends FunctionOutput, TOutReturnValue { + override string toString() { + result = "OutReturnValue" + } + + override predicate isOutReturnValue() { + any() + } +} + +class OutReturnPointer extends FunctionOutput, TOutReturnPointer { + override string toString() { + result = "OutReturnPointer" + } + + override predicate isOutReturnPointer() { + any() + } +} \ No newline at end of file diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/Taint.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/Taint.qll new file mode 100644 index 000000000000..d70c9d707c1d --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/Taint.qll @@ -0,0 +1,23 @@ +/** + * Provides an abstract class for accurate taint modeling of library + * functions when source code is not available. To use this QL library, + * create a QL class extending `TaintFunction` with a characteristic predicate + * that selects the function or set of functions you are modeling. Within that + * class, override the predicates provided by `TaintFunction` to match the flow + * within that function. + */ + +import semmle.code.cpp.Function +import FunctionInputsAndOutputs +import semmle.code.cpp.models.Models + +/** + * A library function for which a taint-tracking library should propagate taint + * from a parameter or qualifier to an output buffer, return value, or qualifier. + * + * Note that this does not include direct copying of values; that is covered by + * DataFlowModel.qll + */ +abstract class TaintFunction extends Function { + abstract predicate hasTaintFlow(FunctionInput input, FunctionOutput output); +} \ No newline at end of file diff --git a/cpp/ql/src/semmle/code/cpp/padding/Padding.qll b/cpp/ql/src/semmle/code/cpp/padding/Padding.qll new file mode 100644 index 000000000000..699badcbe885 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/padding/Padding.qll @@ -0,0 +1,654 @@ +import cpp + +/** + * Align the specified offset up to the specified alignment boundary. + * The result is the smallest integer i such that (i % alignment) = 0 + * and (i >= offset) + */ +bindingset[offset, alignment] +private +int alignUp(int offset, int alignment) { + result = (offset.(float) / alignment).ceil() * alignment +} + +private +Type stripSpecifiers(Type t) { + result = t.getUnspecifiedType() or + (result = t and not exists(t.getUnspecifiedType())) +} + +/** + * A string that represents an architecture. + * An "architecture" defines the sizes of variable-sized types and + * the properties of alignment for fields of various + * types. Two are provided out-of-the-box: ILP32 and LP64, + * corresponding to gcc's behavior on x86 and amd64. + */ +abstract class Architecture extends string { + bindingset[this] + Architecture() { any() } + + /** Gets the size of a pointer, in bits. */ + abstract int pointerSize(); + /** Gets the size of a 'long int', in bits. */ + abstract int longSize(); + /** Gets the size of a 'long double', in bits. */ + abstract int longDoubleSize(); + /** Gets the size of a 'long long', in bits. */ + abstract int longLongSize(); + /** Gets the size of a 'wchar_t', in bits. */ + abstract int wideCharSize(); + + /** Gets the alignment boundary for doubles, in bits. */ + abstract int doubleAlign(); + /** Gets the alignment boundary for long doubles, in bits. */ + abstract int longDoubleAlign(); + /** Gets the alignment boundary for long longs, in bits. */ + abstract int longLongAlign(); + + /** + * Holds if this architecture allow bitfields with declared types of different sizes + * to be packed together. + */ + abstract predicate allowHeterogeneousBitfields(); + + /** + * Gets the bit size of class `cd.getBaseClass()` when used as a base class of + * class `cd.getDerivedClass()`. + */ + abstract int baseClassSize(ClassDerivation cd); + + /** + * Gets the bit size of type `t`. Only holds if `t` is an integral or enum type. + */ + cached + int integralBitSize(Type t) { + (t instanceof BoolType and result = 8) or + (t instanceof CharType and result = 8) or + (t instanceof WideCharType and result = wideCharSize()) or + (t instanceof Char16Type and result = 16) or + (t instanceof Char32Type and result = 32) or + (t instanceof ShortType and result = 16) or + (t instanceof IntType and result = 32) or + (t instanceof LongType and result = longSize()) or + (t instanceof LongLongType and result = longLongSize()) or + result = enumBitSize(t.(Enum)) or + result = integralBitSize(t.(SpecifiedType).getBaseType()) or + result = integralBitSize(t.(TypedefType).getBaseType()) + } + + /** + * Gets the bit size of enum type `e`. + */ + int enumBitSize(Enum e) { + result = integralBitSize(e.getExplicitUnderlyingType()) or + not exists(e.getExplicitUnderlyingType()) and result = 32 + } + + /** + * Gets the alignment of enum type `e`. + */ + int enumAlignment(Enum e) { + result = alignment(e.getExplicitUnderlyingType()) or + not exists(e.getExplicitUnderlyingType()) and result = 32 + } + + /** + * Gets the bit size of type `t`; that is, the number of bits a value + * with type `t` takes up on this architecture, without any trailing + * padding on structs and unions. + */ + cached + int bitSize(Type t) { + result = integralBitSize(t) or + (t instanceof FloatType and result = 32) or + (t instanceof DoubleType and result = 64) or + (t instanceof LongDoubleType and result = longDoubleSize()) or + (t instanceof PointerType and result = pointerSize()) or + (t instanceof ReferenceType and result = pointerSize()) or + (t instanceof FunctionPointerType and result = pointerSize()) or + result = bitSize(t.(SpecifiedType).getBaseType()) or + result = bitSize(t.(TypedefType).getBaseType()) or + exists(ArrayType array | array = t | + result = array.getArraySize() * paddedSize(array.getBaseType())) or + result = t.(PaddedType).typeBitSize(this) + } + + /** + * Gets the desired alignment boundary of type `t` as a struct field + * on this architecture, in bits. + */ + cached + int alignment(Type t) { + (t instanceof BoolType and result = 8) or + (t instanceof CharType and result = 8) or + (t instanceof WideCharType and result = wideCharSize()) or + (t instanceof Char16Type and result = 16) or + (t instanceof Char32Type and result = 32) or + (t instanceof ShortType and result = 16) or + (t instanceof IntType and result = 32) or + (t instanceof FloatType and result = 32) or + (t instanceof DoubleType and result = doubleAlign()) or + (t instanceof LongType and result = longSize()) or + (t instanceof LongDoubleType and result = longDoubleAlign()) or + (t instanceof LongLongType and result = longLongAlign()) or + (t instanceof PointerType and result = pointerSize()) or + (t instanceof FunctionPointerType and result = pointerSize()) or + (t instanceof ReferenceType and result = pointerSize()) or + result = enumAlignment(t.(Enum)) or + result = alignment(t.(SpecifiedType).getBaseType()) or + result = alignment(t.(TypedefType).getBaseType()) or + result = alignment(t.(ArrayType).getBaseType()) or + result = t.(PaddedType).typeAlignment(this) + } + + /** + * Gets the padded size of type `t` on this architecture; that is, + * the number of bits that 'sizeof' should return, taking into account + * any trailing padding on top of the bit size. + */ + int paddedSize(Type t) { + exists(Type realType | realType = stripSpecifiers(t) | + if realType instanceof PaddedType then + result = realType.(PaddedType).paddedSize(this) + else + result = bitSize(realType) + ) + } + + /** + * Gets the wasted space of type `t`; that is, the number of bits + * spent on padding. This is zero for primitive types, and depends on + * struct fields and their alignment otherwise. Trailing padding is + * counted. + */ + int wastedSpace(Type t) { + if t instanceof PaddedType then + result = t.(PaddedType).wastedSpace(this) + else + result = 0 + } +} + +/** + * Gets an initial field of type `t`. If `t` is not a union, an initial field is + * either the first field declared in type `t`, or an initial field of the type + * of the first field declared in `t`. If `t` is a union, an initial field is + * either any field declared in type `t`, or an initial field of the type of any + * field declared in `t`. + */ +private +Field getAnInitialField(PaddedType t) { + if (t instanceof Union) then ( + // Any field of the union is an initial field + result = t.getAField() or + // Initial field of the type of a field of the union + result = getAnInitialField( + t.getAField().getType().getUnspecifiedType().(PaddedType)) + ) + else ( + exists(Field firstField | + t.fieldIndex(firstField) = 1 | + // The first field of `t` + result = firstField or + // Initial field of the first field of `t` + result = getAnInitialField( + firstField.getType().getUnspecifiedType().(PaddedType)) + ) + ) +} + +/** + * Base class for architectures that follow the Itanium ABI. This includes + * pretty much everything except Windows, so we'll refer to this as + * "UnixArchitecture" to avoid any confusion due to the use of the name + * "Itanium". + */ +abstract class UnixArchitecture extends Architecture { + bindingset[this] + UnixArchitecture() { any() } + + override int baseClassSize(ClassDerivation cd) { + if(not exists(cd.getBaseClass().getABaseClass*().getAField()) and + not exists(PaddedType fieldType | + fieldType = getAnInitialField(cd.getDerivedClass()). + getType().getUnspecifiedType() and ( + // Check if the type of the field is a base type of the class, or + // vice versa. This is an approximation of the actual rule, which is + // that the field type and the class must not share a common + // ancestor. This approximation should be sufficient for the vast + // majority of cases. + (fieldType.getABaseClass*() = cd.getBaseClass()) or + (fieldType = cd.getBaseClass().getABaseClass*()) + ) + )) then ( + // No fields in this class or any base classes. + result = 0 + ) + else ( + result = cd.getBaseClass().(PaddedType).paddedSize(this) + ) + } + + override int longLongSize() { result = 64 } + override int wideCharSize() { result = 32 } + + override predicate allowHeterogeneousBitfields() { any() } +} + +/** + * The ILP32 architecture has ints, longs and pointers + * of 32 bits. + */ +class ILP32 extends UnixArchitecture { + ILP32() { this = "ILP32" } + + override int pointerSize() { result = 32 } + override int longSize() { result = 32 } + override int longDoubleSize() { result = 96 } + + override int doubleAlign() { result = 32 } + override int longLongAlign() { result = 32 } + override int longDoubleAlign() { result = 32 } +} + +/** + * The LP64 architecture has longs and pointers of 64 bits. + */ +class LP64 extends UnixArchitecture { + LP64() { this = "LP64" } + + override int pointerSize() { result = 64 } + override int longSize() { result = 64 } + override int longDoubleSize() { result = 128 } + + override int doubleAlign() { result = 64 } + override int longLongAlign() { result = 64 } + override int longDoubleAlign() { result = 128 } +} + +/** + * Base class for Windows architectures. + */ +abstract class WindowsArchitecture extends Architecture { + bindingset[this] + WindowsArchitecture() { any() } + + override int baseClassSize(ClassDerivation cd) { + if not exists(cd.getBaseClass().getABaseClass*().getAField()) then ( + // No fields in this class or any base classes. + result = 0 + ) + else ( + result = cd.getBaseClass().(PaddedType).paddedSize(this) + ) + } + + override int longSize() { result = 32 } + override int longDoubleSize() { result = 64 } + override int longLongSize() { result = 64 } + override int wideCharSize() { result = 16 } + + override int doubleAlign() { result = 64 } + override int longLongAlign() { result = 64 } + override int longDoubleAlign() { result = 64 } + + override predicate allowHeterogeneousBitfields() { none() } +} + +/** + * The ILP32_MS architecture is essentially the same as the + * ILP32 architecture, except that long doubles are 64 bits. + */ +class ILP32_MS extends WindowsArchitecture { + ILP32_MS() { this = "ILP32_MS" } + + override int pointerSize() { result = 32 } +} + +/** + * The LLP64_MS architecture has pointers of 64 bits, but both + * long and int are still 32 bits. + */ +class LLP64_MS extends WindowsArchitecture { + LLP64_MS() { this = "LLP64_MS" } + + override int pointerSize() { result = 64 } +} + +/** + * A class that is subject to padding by the compiler, and hence can + * introduce waste. Does not include types with virtual member functions, + * virtual base classes, or multiple base classes. These are excluded due + * to the complexity of the implementation. + */ +class PaddedType extends Class { + PaddedType() { + // We can't talk about bit size of template types. + not this instanceof TemplateClass and + // If the class has any virtual functions, the layout will be more + // complicated due to the presence of a virtual function table pointer. + not exists(MemberFunction f | + f = this.getAMemberFunction() and f.isVirtual()) and + not exists(ClassDerivation cd | + cd = this.getADerivation() | + // If the class has any virtual functions, the layout will be more + // complicated due to the presence of a virtual base table pointer. + cd.hasSpecifier("virtual") or + // If one of the base classes was not a PaddedType, then we should not + // attempt to lay out the derived class, either. + not (cd.getBaseClass() instanceof PaddedType) + ) and + // Support only single inheritance for now. If multiple inheritance is + // supported, be sure to fix up the calls to getABaseClass*() to correctly + // handle the presence of multiple base class subojects with the same type. + not exists(ClassDerivation cd | cd = this.getDerivation(1)) + } + + /** + * Holds if, for each architecture, a single padded size is + * calculated for this type. + * This is normally the case, but sometimes the same type can be + * defined in different compilations with different sizes, normally + * due to use of the preprocessor in its definition. + */ + predicate isPrecise() { + forex(Architecture arch | 1 = strictcount(arch.paddedSize(this))) + } + + /** + * Gets the padded size of this type on architecture `arch`, in bits. + * This is its `bitSize`, rounded up to the next multiple of its + * `alignment`. + */ + int paddedSize(Architecture arch) { + // Struct padding is weird: It needs to be such that struct arrays + // can be allocated contiguously. That means that the trailing padding + // has to bring the alignment up to the smallest common multiple of + // the alignment values of all fields. In practice, since valid + // alignment values are 1, 2, 4, 8 and 16, this means "the largest + // alignment value". + // If the class is empty, the size is rounded up to one byte. + result = alignUp(arch.bitSize(this), arch.alignment(this)).maximum(8) + } + + /** + * Gets the number of bits wasted by padding at the end of this + * struct. + */ + int trailingPadding(Architecture arch) { + result = paddedSize(arch) - arch.bitSize(this) + } + + /** + * Gets the number of bits wasted in this struct definition; that is. + * the waste between fields plus any waste from trailing padding. + * Only the space wasted directly in this type is counted, not space + * wasted in nested structs. Note that for unions, the wasted space + * is simply the amount of trailing padding, as other fields are not + * laid out one after another, and hence there is no padding between + * them. + */ + int wastedSpace(Architecture arch) { + result = arch.paddedSize(this) - dataSize(arch) + } + + /** + * Gets the total size of all fields declared in this class, not including any + * padding between fields. + */ + private + int fieldDataSize(Architecture arch) { + if this instanceof Union then + result = max(Field f | + f = this.getAMember() | + fieldSize(f, arch)) + else + result = sum(Field f | + f = this.getAMember() | + fieldSize(f, arch)) + } + + /** + * Gets the data size of this type on architecture `arch`; that is, + * the number of bits taken up by data, rather than any kind of + * padding. Padding of fields that have struct type is + * considered "data" for the purposes of this definition, since + * removing it requires reorganizing other parts of the code. + */ + int dataSize(Architecture arch) { + result = sum(PaddedType c | c = this.getABaseClass*() | c.fieldDataSize(arch)) + } + + /** + * Gets the optimal size of this type on architecture `arch`, that is, + * the sum of the sizes of all fields, ignoring padding + * between them, but adding any trailing padding required to align + * the type properly. This is a lower bound on the actual size that + * can be achieved just by reordering fields, and without + * reorganizing member structs' field layouts. + */ + int optimalSize(Architecture arch) { + result = alignUp(dataSize(arch), arch.alignment(this)).maximum(8) + } + + /** + * Gets the bit size of this type on architecture `arch`, that is, the + * size its fields and required padding take up, without including + * any trailing padding that is necessary. + */ + int typeBitSize(Architecture arch) { + if this instanceof Union then + // A correct implementation for unions would be + // result = max(fieldSize(_, arch)) + // but that uses a recursive aggregate, which isn't supported in + // QL. We therefore use this slightly more complex implementation + // instead. + result = biggestFieldSizeUpTo(lastFieldIndex(), arch) + else + // If we're not a union type, the size is the padded + // sum of field sizes, padded. + result = fieldEnd(lastFieldIndex(), arch) + } + + /** + * Gets the alignment, in bits, of the entire struct/union type for + * architecture `arch`. + */ + language[monotonicAggregates] + int typeAlignment(Architecture arch) { + // The alignment of the type is the largest alignment of any of its fields, + // including fields from base class subobjects. + result = max(PaddedType c | + c = this.getABaseClass*() | + c.biggestAlignmentUpTo(c.lastFieldIndex(), arch) + ) + } + + /** + * Gets the largest size, in bits, of the size of a field with + * (1-based) index less than or equal to `index` on architecture + * `arch`. + */ + int biggestFieldSizeUpTo(int index, Architecture arch) { + if index = 0 then ( + result = 0 + ) + else ( + exists(Field f, int fSize | + index = fieldIndex(f) and fSize = fieldSize(f, arch) | + result = fSize.maximum(biggestFieldSizeUpTo(index-1, arch)) + ) + ) + } + + /** + * Gets the largest alignment boundary, in bits, required by a field + * with (1-based) index less than or equal to `index` on architecture + * `arch`. + */ + int biggestAlignmentUpTo(int index, Architecture arch) { + if index = 0 then ( + result = 1 // Minimum possible alignment + ) + else ( + exists(Field f, int fAlign | + index = fieldIndex(f) and fAlign = arch.alignment(f.getType()) | + result = fAlign.maximum(biggestAlignmentUpTo(index-1, arch)) + ) + ) + } + + /** + * Gets the 1-based index for each field. + */ + int fieldIndex(Field f) { + memberIndex(f) = rank[result](Field field, int index | + memberIndex(field) = index | + index + ) + } + + private + int memberIndex(Field f) { + result = min(int i | getCanonicalMember(i) = f) + } + + /** + * Gets the 1-based index for the last field. + */ + int lastFieldIndex() { + if exists(lastField()) then ( + result = fieldIndex(lastField()) + ) + else ( + // Field indices are 1-based, so return 0 to represent the lack of fields. + result = 0 + ) + } + + /** + * Gets the size, in bits, of field `f` on architecture + * `arch`. + */ + int fieldSize(Field f, Architecture arch) { + exists(fieldIndex(f)) and + if f instanceof BitField then + result = f.(BitField).getNumBits() + else + result = arch.paddedSize(f.getType()) + } + + /** Gets the last field of this type. */ + Field lastField() { + fieldIndex(result) = max(Field other | | fieldIndex(other)) + } + + /** + * Gets the offset, in bits, of the end of the class' last base class + * subobject, or zero if the class has no base classes. + */ + int baseClassEnd(Architecture arch) { + if exists(getABaseClass()) then ( + result = arch.baseClassSize(getADerivation()) + ) + else ( + result = 0 + ) + } + + /** Gets the bitfield at field index `index`, if that field is a bitfield. */ + private + BitField bitFieldAt(int index) { + fieldIndex(result) = index + } + + /** + * Gets the 0-based offset, in bits, of the first free bit after + * field `f` (which is the `index`th field of + * this type), taking padding into account, on architecture `arch`. + */ + int fieldEnd(int index, Architecture arch) { + if index = 0 then ( + // Base case: No fields seen yet, so return the offset of the end of the + // base class subojects. + result = baseClassEnd(arch) + ) + else ( + exists(Field f | index = fieldIndex(f) | + exists(int fSize | fSize = fieldSize(f, arch) | + // Recursive case: Take previous field's end point, pad and add + // this field's size + exists(int firstFree | + firstFree = fieldEnd(index-1, arch) | + if (f instanceof BitField) then ( + // Bitfield packing: + // (1) A struct containing a bitfield with declared type T (e.g. T bf : 7) will be aligned as if it + // contained an actual field of type T. Thus, a struct containing a bitfield 'unsigned int bf : 8' + // will have an alignment of at least alignof(unsigned int), even though the bitfield was only 8 bits. + // (2) If a bitfield with declared type T would straddle a sizeof(T) boundary, padding is inserted + // before the bitfield to align it on an alignof(T) boundary. Note the subtle distinction between alignof + // and sizeof. This matters for 32-bit Linux, where sizeof(long long) == 8, but alignof(long long) == 4. + // (3) [MSVC only!] If a bitfield with declared type T immediately follows another bitfield with declared type P, + // and sizeof(P) != sizeof(T), padding will be inserted to align the new bitfield to a boundary of + // max(alignof(P), alignof(T)). + exists(int nextSizeofBoundary, int nextAlignofBoundary | + (nextSizeofBoundary = alignUp(firstFree, arch.bitSize(f.getType()))) and + (nextAlignofBoundary = alignUp(firstFree, arch.alignment(f.getType()))) | + if (arch.allowHeterogeneousBitfields()) then ( + if (nextSizeofBoundary < (firstFree + fSize)) then ( + // Straddles a sizeof(T) boundary, so pad for alignment. + result = nextAlignofBoundary + fSize + ) + else ( + // No additional restrictions, so just pack it in with no padding. + result = firstFree + fSize + ) + ) + else ( + if exists(bitFieldAt(index-1)) then ( + exists (BitField previousBitField | + previousBitField = bitFieldAt(index-1) | + // Previous field was a bitfield. + if ((nextSizeofBoundary >= (firstFree + fSize)) and arch.integralBitSize(previousBitField.getType()) = arch.integralBitSize(f.getType())) then ( + // The new bitfield can be stored in the same allocation unit as the previous one, + // so we can avoid padding. + result = firstFree + fSize + ) + else ( + // Either we switched types, or we would overlap a sizeof(T) boundary, so we have to insert padding. + // Note that we have to align to max(alignof(T), alignof(P)), where P is the type of the previous + // bitfield. Without the alignof(P) term, we'll get the wrong layout for: + // struct S { + // unsigned int x : 7; + // unsigned short y : 1; + // }; + // If we only aligned to sizeof(T), we'd align 'y' to a 2-byte boundary. This is incorrect. The allocation + // unit that started with 'x' has to consume an entire unsigned int (4 bytes). + result = max(int boundary | + boundary = nextAlignofBoundary or + boundary = alignUp(firstFree, + arch.alignment(previousBitField.getType())) | + boundary + ) + fSize + ) + ) + ) + else ( + // Previous field was not a bitfield. Align up to an + // alignof(T) boundary. + result = nextSizeofBoundary + fSize + ) + ) + ) + ) + else ( + // Normal case: Pad as necessary, then add the field. + result = alignUp(firstFree, arch.alignment(f.getType())) + fSize + ) + ) + ) + ) + ) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/padding/SanityCheck.ql b/cpp/ql/src/semmle/code/cpp/padding/SanityCheck.ql new file mode 100644 index 000000000000..a4b775ccb5e3 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/padding/SanityCheck.ql @@ -0,0 +1,31 @@ +/** + * @name Padding Sanity Check + * @description Performs sanity checks for the padding library. This query should have no results. + * @kind problem + * @id cpp/padding-sanity-check + */ + +import Padding + +/* + * Sanity-check: Find discrepancies between computed and actual size on LP64. + * +from Type t, LP64 a, int padded, int bit, int real, MemberVariable v +where padded = a.paddedSize(t) and bit = a.bitSize(t) +and real = t.getSize() * 8 and padded != real and count(t.getSize()) = 1 +select t, a.paddedSize(t) as Padded, real, v, t.(PaddedType).memberSize(v, a) + /** / + +from PaddedType t, LP64 a, MemberVariable v +where t instanceof Union and v = t.getAMember() and not exists(t.memberSize(v, a)) +select t, v, v.getType().explain() +/** +from PaddedType t, LP64 a, MemberVariable v +where not exists(a.paddedSize(t)) +select t, t.fieldIndex(v) as i, v, t.memberSize(v, a) order by t, i +/**/ +from PaddedType t, LP64 a +where a.wastedSpace(t) != 0 +select t, a.paddedSize(t) as size, a.wastedSpace(t) as waste order by waste desc +/**/ + diff --git a/cpp/ql/src/semmle/code/cpp/pointsto/CallGraph.qll b/cpp/ql/src/semmle/code/cpp/pointsto/CallGraph.qll new file mode 100644 index 000000000000..57a718e8ae9b --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/pointsto/CallGraph.qll @@ -0,0 +1,77 @@ +import cpp +import semmle.code.cpp.pointsto.PointsTo + +// a TargetPointsToExpr has points-to information for expressions +// that will help build an accurate call-graph (i.e. expressions +// in function-pointer calls and qualifiers of virtual calls) +class TargetPointsToExpr extends PointsToExpr { + override predicate interesting() { + exists(ExprCall ec | ec.getExpr() = this) + or + exists(Call c | c.getQualifier() = this) + or + exists(DeleteExpr d | d.getExpr() = this) + } + + // resolve a virtual-call where this is the qualifier + VirtualFunction resolve() + { + pointstosets(this.resolveToSet(), result) + } + + int resolveToSet() + { + exists(int cset, VirtualFunction static | + this.interesting() and + parentSetFor(cset, this) and + static = this.staticTarget() and + childrenByElement(cset, static, result) + ) + } + + VirtualFunction staticTarget() + { + exists(Function f, DeleteExpr d | + f.calls(result, d) and + d.getExpr() = this) + or + exists(Function f, FunctionCall c | + f.calls(result, c) and + c.getQualifier() = this) + } +} + +predicate resolvedCall(Call call, Function called) +{ + call.(FunctionCall).getTarget() = called + or + call.(DestructorCall).getTarget() = called + or + exists(ExprCall ec, TargetPointsToExpr pte | + ec = call and ec.getExpr() = pte and pte.pointsTo() = called) + or + exists(TargetPointsToExpr pte | + call.getQualifier() = pte and + pte.resolve() = called) +} + +predicate ptrCalls(Function f, Function g) { + exists(ExprCall ec | + ec.getEnclosingFunction() = f and + ec.getExpr().(TargetPointsToExpr).pointsTo() = g) +} + +predicate virtualCalls(Function f, VirtualFunction g) { + exists(DeleteExpr d, TargetPointsToExpr ptexpr, VirtualFunction static | + f.calls(static, d) and + d.getExpr() = ptexpr and + ptexpr.resolve() = g) or + exists(Call c, TargetPointsToExpr ptexpr, VirtualFunction static | + f.calls(static, c) and + c.getQualifier() = ptexpr and + ptexpr.resolve() = g) +} + +predicate allCalls(Function f, Function g) { + f.calls(g) or ptrCalls(f,g) or virtualCalls(f,g) +} diff --git a/cpp/ql/src/semmle/code/cpp/pointsto/PointsTo.qll b/cpp/ql/src/semmle/code/cpp/pointsto/PointsTo.qll new file mode 100644 index 000000000000..83766d45ca80 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/pointsto/PointsTo.qll @@ -0,0 +1,636 @@ +/** + * Provides classes and predicates implementing a points-to analysis + * based on Steensgaard's algorithm, extended to support fields. + * + * A pointer set can be represented in one of two ways: an expression, or + * the combination of an expression and a label. In the former case, + * the expression represents the values the expression might evaluate to. + * In the latter case, the (expr, label) pair is called a "compound", and it + * represents a field of the value with the name of the given label. The label + * can be either a string or another element. + * + * The various "flow" predicates (`flow`, `flowToCompound`, etc.) represent + * direct flow from a source set to a destination set. The various "pointer" + * predicates (`pointer`, `pointerFromCompound`, etc.) indicate that one set + * contains values pointing to the locations represented by the other set. + * + * The individual flow and pointer predicates only hold tuples describing + * one step of flow; they do not include transitive closures. The + * `pointstoinfo` predicate determines the transitively implied points-to + * information by collapsing pointers into equivalence classes. These + * equivalence classes are called "points-to sets". + */ + +import semmle.code.cpp.commons.File +import semmle.code.cpp.exprs.Expr + +/** + * Holds if `actual` is the override of `resolved` for a value of type + * `dynamic`. + */ +predicate resolve(Class dynamic, VirtualFunction resolved, VirtualFunction actual) { + if resolved.getAnOverridingFunction*().getDeclaringType() = dynamic + then (actual = resolved.getAnOverridingFunction*() and + dynamic = actual.getDeclaringType()) + else resolve(dynamic.getABaseClass(), resolved, actual) +} + +/** + * Holds if `e` is evaluated just for its location. This includes + * expressions that are used in a reference expression (`&foo`), + * expressions that are used on the left side of an assignment, + * and some non-expression types such as `Initializer`. + * + * For expressions, this is similar to, but different than, + * `e.(Expr).isLValue()`, which holds if `e` *has* an address. + * + * This relation pervasively influences the interpretation of + * expressions throughout this module. An element evaluated for its + * lvalue is treated as evaluating to its location, not its value. + */ +predicate lvalue(Element e) { + exists(AssignExpr assign | assign.getLValue().getFullyConverted() = e) + or + exists(AddressOfExpr addof | e = addof.getOperand().getFullyConverted()) + or + exists(FieldAccess fa | fa.getQualifier().getFullyConverted() = e + and not(pointerValue(e))) + or + exists(Call c | c.getQualifier().getFullyConverted() = e + and not(pointerValue(e))) + or + e.(Expr).getConversion() instanceof ArrayToPointerConversion + or + exists(ParenthesisExpr paren | exprconv(e, paren) and lvalue(paren)) + or + exists(Cast c | lvalue(c) and e.(Expr).getConversion() = c) + or + exists(ReferenceToExpr toref | e.(Expr).getConversion() = toref) + or + // If f is a function-pointer, then the following two + // calls are equivalent: f() and (*f)() + exists(PointerDereferenceExpr deref | e = deref and + deref.getUnderlyingType() instanceof FunctionPointerType) + or + exists(Variable v | e = v.getInitializer() and + v.getType().getUnderlyingType() instanceof Struct) + or + exists(Variable v | e = v.getInitializer() and + v.getType().getUnderlyingType() instanceof ArrayType) + or + e instanceof AggregateLiteral +} + +/** + * Gets an access for the value of `p` on line `line`. + */ +private +VariableAccess param_rvalue_access_line(Parameter p, int line) { + p.getAnAccess() = result and + not lvalue(result) and + result.getLocation().getStartLine() = line +} + +/** + * Gets an access for the value of `p`. + * + * The choice is arbitrary, and it doesn't matter if this returns more + * than one access, but we try to have few results to cut down the + * number of flow edges. + */ +private +VariableAccess pick_rvalue_access(Parameter p) { + result = min(int line | | param_rvalue_access_line(p, line) order by line) +} + +/** + * Holds if there is an access for the value of `p`. + * + * Usually we can just add a flow edge from a function argument to a + * value access of the corresponding parameter. If all accesses to the + * parameter are lvalues, however, we have to add a pointer edge from + * the parameter to the function argument. This is less precise, because + * it can equate more points-to sets. + */ +private +predicate has_rvalue_access(Parameter p) { + exists(VariableAccess a | a = p.getAnAccess() | not lvalue(a)) +} + +/** + * Holds if `e` has a pointer type. + */ +predicate pointerValue(Expr e) { + exists(Type t | t = e.getType().getUnderlyingType() and + (t instanceof PointerType or + t instanceof ArrayType or + t instanceof ReferenceType)) +} + +/** + * The source is a pointer to the destination. + */ +predicate pointer(Element src, Element dest) +{ + exists(Variable v | not(lvalue(dest)) and + src = v and (dest = v.getAnAccess() or dest = v.getInitializer())) + or + exists(AssignExpr assign | not lvalue(assign) and + src = assign.getLValue().getFullyConverted() and dest = assign) + or + exists(AssignExpr assign | + src = assign.getLValue().getFullyConverted() and + dest = assign.getRValue().getFullyConverted()) + or + exists(FunctionCall c, Function f, Parameter p, int i | + p = f.getParameter(i) and not has_rvalue_access(p) and + dest = c.getArgument(i).getFullyConverted() and not(f.isVirtual()) and + src = p.getAnAccess() and c.getTarget() = f) + or + exists(PointerDereferenceExpr deref | not(lvalue(deref)) and + src = deref.getOperand().getFullyConverted() and dest = deref) + or + exists(ArrayExpr ae | not(lvalue(dest)) and dest = ae and + src = ae.getArrayBase().getFullyConverted() and pointerValue(src)) + or + exists(ArrayExpr ae | not(lvalue(dest)) and dest = ae and + src = ae.getArrayOffset().getFullyConverted() and pointerValue(src)) + or + exists(ReferenceDereferenceExpr deref | not(lvalue(deref)) and + dest = deref and exprconv(src, deref)) + or + exists(AggregateLiteral agg | not(lvalue(dest)) and + agg.getType().getUnderlyingType() instanceof ArrayType and + src = agg and dest = agg.getAChild().getFullyConverted()) + or + // field points to constructor field initializer + exists(ConstructorFieldInit cfi | + dest = cfi and + src = cfi.getTarget() and + not(lvalue(dest)) + ) + // + // add more cases here + // +} + +/** + * The value held in the source flows to the value held in the destination. + */ +predicate flow(Element src, Element dest) +{ + exists(Variable v | lvalue(dest) and + src = v and (dest = v.getAnAccess() or dest = v.getInitializer())) + or + exists(FunctionAccess fa | src = fa.getTarget() and dest = fa) + or + exists(AssignExpr assign | lvalue(assign) and + src = assign.getLValue().getFullyConverted() and dest = assign) + or + exists(AddressOfExpr addof | dest = addof and + src = addof.getOperand().getFullyConverted()) + or + exists(FunctionCall c, Function f, int i | not lvalue(dest) and + src = c.getArgument(i).getFullyConverted() and not(f.isVirtual()) and + dest = pick_rvalue_access(f.getParameter(i)) and c.getTarget() = f) + or + exists(FunctionCall c, Function f, int i | + src = c.getArgument(i).getFullyConverted() and not(f.isVirtual()) and + c.getTarget() = f and i >= f.getNumberOfParameters() and + varArgRead(f, dest)) + or + exists(FunctionCall c, Function f, ReturnStmt r | + c.getTarget() = f and + not(f.isVirtual()) and + r.getEnclosingFunction() = f and + src = r.getExpr().getFullyConverted() and dest = c) + or + exists(PointerDereferenceExpr deref | lvalue(deref) and + src = deref.getAChild().getFullyConverted() and dest = deref) + or + exists(Variable v | dest = v.getInitializer() and + src = v.getInitializer().getExpr().getFullyConverted()) + or + exists(ArrayExpr ae | lvalue(dest) and dest = ae and + src = ae.getArrayBase().getFullyConverted() and pointerValue(src)) + or + exists(ArrayExpr ae | lvalue(dest) and dest = ae and + src = ae.getArrayOffset().getFullyConverted() and pointerValue(src)) + or + exists(Expr arg, BinaryArithmeticOperation binop | dest = binop and src = arg and + pointerValue(binop) and pointerValue(arg) and + ( arg = binop.getLeftOperand().getFullyConverted() or + arg = binop.getRightOperand().getFullyConverted())) + or + exists(Cast c | src = c.getExpr() and dest = c) + or + exists(ReferenceToExpr toref | exprconv(src, toref) and dest = toref) + or + exists(ReferenceDereferenceExpr deref | lvalue(deref) and + dest = deref and exprconv(src, deref)) + or + exists(ArrayToPointerConversion conv | exprconv(src, conv) and dest = conv) + or + exists(ParenthesisExpr paren | + // these can appear on the LHS of an assignment + (exprconv(src, paren) and dest = paren) or + (exprconv(dest, paren) and src = paren)) + or + exists(ConditionalExpr cond | dest = cond and + ( src = cond.getThen().getFullyConverted() or + src = cond.getElse().getFullyConverted())) + or + exists(IncrementOperation inc | dest = inc and + src = inc.getOperand().getFullyConverted()) + or + exists(IncrementOperation dec | dest = dec and + src = dec.getOperand().getFullyConverted()) + or + exists(CommaExpr comma | dest = comma and + src = comma.getRightOperand().getFullyConverted()) + or + exists(ParenthesisExpr paren | dest = paren and exprconv(src, paren)) + or + // "vtable" for new-expressions + exists(NewExpr new | src = new and dest = new.getAllocatedType()) + or + // "vtable" for class-typed variables + exists(Variable v, Class c | v.getType().getUnderlyingType() = c and src = v and dest = c) + or + exists(AggregateLiteral agg | lvalue(dest) and + agg.getType().getUnderlyingType() instanceof ArrayType and + src = agg and dest = agg.getAChild().getFullyConverted()) + or + // contained expr -> constructor field initializer + exists(ConstructorFieldInit cfi | + src = cfi.getExpr().getFullyConverted() and + dest = cfi + ) + // + // add more cases here + // +} + +// Try to find the expression corresponding to the return value +// of va_arg(...,...) - which is a macro. +predicate varArgRead(Function f, Expr e) +{ + exists(Macro m, MacroInvocation mi | + m.getHead().matches("va\\_arg(%") and + mi.getMacro() = m and + e = mi.getAGeneratedElement() and + not(e.getParent() = mi.getAGeneratedElement()) + and e.getEnclosingFunction() = f) +} + +/** + * There is a flow from src to the compound (destParent, destLabel). + */ +predicate flowToCompound(Element destParent, string destLabel, Element src) +{ + exists(ExprCall call, int i | + src = call.getArgument(i).getFullyConverted() and + destParent = call.getExpr().getFullyConverted() and + if i < call.getType().(FunctionPointerType).getNumberOfParameters() then + destLabel = "+arg" + i.toString() + else + destLabel = "+vararg") + or + exists(Function f, ReturnStmt ret | + f = ret.getEnclosingFunction() and + src = ret.getExpr().getFullyConverted() and + destLabel = "+ret" and destParent = f) + or + exists(AggregateLiteral agg, Struct s, int i | + destParent = agg and lvalue(src) and + aggregateLiteralChild(agg, s, i, src) and + destLabel = s.getCanonicalMember(i).getName() + ) + or + exists(FunctionCall c, Function f | + c.getTarget() = f and + not(f.isVirtual()) and + src = c.getQualifier().getFullyConverted() and + destParent = f and + destLabel = "+this" + ) + or + exists(ConstructorCall c, Function f, Variable v | + c.getTarget() = f and + not(f.isVirtual()) and + v.getAnAssignedValue() = c and + src = v and + destParent = f and + destLabel = "+this" + ) + or + exists(NewExpr ne, ConstructorCall c, Function f | + c.getTarget() = f and + not(f.isVirtual()) and + ne.getInitializer() = c and + src = ne and + destParent = f and + destLabel = "+this" + ) + // in C, &s == &s.firstfield + // exists(FieldAccess fa, Field f | + // parent = fa.getQualifier().getFullyConverted() and src = parent and + // f = fa.getTarget() and not exists(f.previous()) and + // label = f.getName() + // ) + // + // add more cases here + // +} + +/** + * There is a flow from the compound (parent, label) to dest. + */ +predicate flowFromCompound(Element parent, string label, Element dest) +{ + exists(ExprCall call | + dest = call and label = "+ret" and parent = call.getExpr().getFullyConverted()) + or + exists(Function f, int i | dest = f.getParameter(i).getAnAccess() + and label = "+arg" + i.toString() and parent = f) + or + exists(Function f | + parent = f and label = "+vararg" and varArgRead(f, dest)) + or + exists(FieldAccess fa | dest = fa and parent = fa.getQualifier().getFullyConverted() + and label = fa.getTarget().getName() and lvalue(dest)) + or + exists(ThisExpr thisexpr | dest = thisexpr and label = "+this" + and parent = thisexpr.getEnclosingFunction()) + // + // add more cases here + // +} + +/* + * The values stored in src point to the compounds (destParent, destLabel). + */ +predicate pointerToCompound(Element destParent, string destLabel, Element src) +{ + none() + // + // add more cases here + // +} + +/** + * The type of agg is s, and the expression initializing the ith member + * of s is child. + */ +pragma[noopt] +predicate aggregateLiteralChild(AggregateLiteral agg, Struct s, int i, Expr child) { + // s = agg.getType().getUnderlyingType() + exists(Type t | + t = agg.getType() and agg instanceof AggregateLiteral and s = t.getUnderlyingType() and s instanceof Struct + ) + and + exists(Expr beforeConversion | + beforeConversion = agg.getChild(i) and + child = beforeConversion.getFullyConverted() + ) +} + +/** + * The compound (parent, label) holds pointers to dest. + */ +predicate pointerFromCompound(Element parent, string label, Element dest) +{ + exists(FieldAccess fa | dest = fa and parent = fa.getQualifier().getFullyConverted() + and label = fa.getTarget().getName() and not(lvalue(dest))) + or + exists(AggregateLiteral agg, Struct s, int i | + parent = agg and not(lvalue(dest)) and + aggregateLiteralChild(agg, s, i, dest) and + label = s.getCanonicalMember(i).getName() + ) + // + // add more cases here + // +} + +predicate virtualArg(Expr receiver, VirtualFunction called, string arglabel, Expr arg) +{ + exists(FunctionCall c, int i | + receiver = c.getQualifier().getFullyConverted() and called = c.getTarget() and called.isVirtual() + and arg = c.getArgument(i) and i >= 0 and + if i < called.getNumberOfParameters() then + arglabel = "+arg" + i.toString() + else + arglabel = "+vararg") +} + +predicate virtualThis(Expr receiver, VirtualFunction called, string thislabel, Expr thisexpr) +{ + exists(FunctionCall c | + receiver = c.getQualifier().getFullyConverted() and called = c.getTarget() and thislabel = "+this" + and called.isVirtual() and thisexpr = receiver) +} + +predicate virtualRet(Expr receiver, VirtualFunction called, string retlabel, FunctionCall c) +{ + receiver = c.getQualifier().getFullyConverted() and called = c.getTarget() + and called.isVirtual() and retlabel = "+ret" +} + +/** + * This relation combines all pointer and flow relations that + * go to or from a compound set. + * + * The "kind" of each tuple determines what relation the other + * four elements of the tuple indicate: + * + * 0 - flow from to other + * 1 - flow from other to + * 2 - pointer from to other + * 3 - pointer from other to + * + * 4 - flow from to other + * 5 - flow from other to + * 6 - flow from to other + * 7 - flow from other to + * + * 8 - flow from <,label> to other + * 9 - flow from other to <,label> + * 10 - pointer from <,label> to other + * 11 - pointer from other to <,label> + */ +predicate compoundEdge(Element parent, Element element, string label, Element other, int kind) +{ + (flowFromCompound(parent, label, other) and element = parent and kind = 0) + or + (flowToCompound(parent, label, other) and element = parent and kind = 1) + or + (pointerFromCompound(parent, label, other) and element = parent and kind = 2) + or + (pointerToCompound(parent, label, other) and element = parent and kind = 3) + or + (resolve(parent, element, other) and label = "" and kind = 5) + or + (virtualRet(parent, element, label, other) and kind = 8) + or + (virtualArg(parent, element, label, other) and kind = 9) + or + (virtualThis(parent, element, label, other) and kind = 9) +} + +/** + * A summary of the points-to information for the program, computed by + * collapsing the various flow and pointer relations using the Java + * class PointsToCalculator. This relation combines several kinds of information; + * the different kinds are filtered out by several relations further + * in the file: pointstosets, setflow, children, childrenByElement, + * parentSetFor. + * + * The information represented by each tuple in the relation depends on + * the "label" element. + * + * If the label is the empty string, then the tuple describes membership of + * element "elem" in points-to set "ptset", and that children of the element + * are children of set "parent". + * + * If the label is "--flow--", then the tuple describes flow from the "parent" + * points-to set to the "ptset" points-to set. + * + * If the label is "--element--", then the tuple declares that the set "ptset" is + * a child of "parent", where the label of the child is "elem". + * + * In any other case, the tuple declares that set "ptset" is a child of + * "parent", where the label is "label". + */ +cached +predicate pointstoinfo(int parent, @element elem, string label, int ptset) = + collapse(flow/2, pointer/2, compoundEdge/5, location/1)(parent, elem, label, ptset) + +/** + * Which elements are in which points-to sets. + */ +cached +predicate pointstosets(int ptset, @element elem) +{ + pointstoinfo(_, elem, "", ptset) +} + +/** + * The points-to set src flows to the points-to set dest. + * This relation is not transitively closed. + */ +predicate setflow(int src, int dest) +{ + pointstoinfo(src, _, "--flow--", dest) +} + +/** + * The points-to set parentset, when dereferenced using the + * given label, gives values in the points-to set childset. + */ +predicate children(int parentset, string label, int childset) +{ + pointstoinfo(parentset, _, label, childset) + and label != "" and label != "--element--" and label != "--flow--" +} + +/** + * The same as children(), except that the label is an element. + */ +predicate childrenByElement(int parentset, Element label, int childset) +{ + pointstoinfo(parentset, label, "--element--", childset) +} + +/** + * The ID of the parent set for the given expression. Children + * of the given element should be looked up with children() and + * childrenByElement() using this ID. + */ +pragma[noopt] +predicate parentSetFor(int cset, @element expr) +{ + exists(string s | s = "" and pointstoinfo(cset, expr, s, _)) +} + +/** + * Things that are elements of points-to sets. + */ +predicate location(Element location) +{ + location instanceof Variable or + location instanceof Function or + isAllocationExpr(location) or + fopenCall(location) or + allocateDescriptorCall(location) +} + +/** + * A call to the Unix system function socket(2). + */ +predicate allocateDescriptorCall(FunctionCall fc) +{ + exists(string name | + name = "socket" and + fc.getTarget().hasQualifiedName(name)) +} + +/** + * A points-to set that contains at least one interesting element, or + * flows to one that does. + */ +private int interestingSet() +{ + exists(PointsToExpr e | + e.interesting() and + pointstosets(result, e) + ) or ( + setflow(result, interestingSet()) + ) +} + +/** + * The elements that are either in the given points-to set, or + * which flow into it from another set. The results are restricted + * to sets which are interesting. + */ +cached +predicate setlocations(int set, @element location) +{ + set = interestingSet() and + ( + ( + location(location) and pointstosets(set, location) + ) or + exists(int middle | setlocations(middle, location) and setflow(middle, set)) + ) +} + +class PointsToExpr extends Expr +{ + /* + * This predicate is empty by default. It should be overridden and defined to + * include just those expressions for which points-to information is desired. + */ + predicate interesting() { none() } + + pragma[noopt] + Element pointsTo() + { + this.interesting() and exists(int set | pointstosets(set, this) and setlocations(set, result)) + } + + float confidence() { result = 1.0 / count(this.pointsTo()) } +} + +/** + * Holds if anything points to an element, that is, is equivalent to: + * ``` + * exists(PointsToExpr e | e.pointsTo() = elem) + * ``` + */ +predicate anythingPointsTo(Element elem) +{ + location(elem) and pointstosets(interestingSet(), elem) +} + diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/PointlessComparison.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/PointlessComparison.qll new file mode 100644 index 000000000000..f927beb86895 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/rangeanalysis/PointlessComparison.qll @@ -0,0 +1,166 @@ +/** + * Provides utility predicates used by the pointless comparison queries. + */ +import cpp +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis + +/** Gets the lower bound of the fully converted expression. */ +private float lowerBoundFC(Expr expr) { + result = lowerBound(expr.getFullyConverted()) +} + +/** Gets the upper bound of the fully converted expression. */ +private float upperBoundFC(Expr expr) { + result = upperBound(expr.getFullyConverted()) +} + +newtype SmallSide = LeftIsSmaller() or RightIsSmaller() + +/** + * Holds if `cmp` is a comparison operation in which the left hand + * argument (which is at most `left`) is always strictly less than the + * right hand argument (which is at least `right`), and `ss` is + * `LeftIsSmaller`. + * + * Note that the comparison operation could be any binary comparison + * operator, for example,`==`, `>`, or `<=`. + */ +private +predicate alwaysLT(ComparisonOperation cmp, float left, float right, SmallSide ss) { + ss = LeftIsSmaller() and + left = upperBoundFC(cmp.getLeftOperand()) and + right = lowerBoundFC(cmp.getRightOperand()) and + left < right +} + +/** + * Holds if `cmp` is a comparison operation in which the left hand + * argument (which is at most `left`) is always less than or equal to + * the right hand argument (which is at least `right`), and `ss` is + * `LeftIsSmaller`. + * + * Note that the comparison operation could be any binary comparison + * operator, for example,`==`, `>`, or `<=`. + */ +private +predicate alwaysLE(ComparisonOperation cmp, float left, float right, SmallSide ss) { + ss = LeftIsSmaller() and + left = upperBoundFC(cmp.getLeftOperand()) and + right = lowerBoundFC(cmp.getRightOperand()) and + left <= right and + + // Range analysis is not able to precisely represent large 64 bit numbers, + // because it stores the range as a `float`, which only has a 53 bit mantissa. + // For example, the number `2^64-1` is rounded to `2^64`. This means that we + // cannot trust the result if the numbers are large. Note: there is only + // a risk of a rounding error causing an incorrect result if `left == right`. + // If `left` is strictly less than `right` then there is enough of a gap + // that we don't need to worry about rounding errors. + left.ulp() <= 1 +} + +/** + * Holds if `cmp` is a comparison operation in which the left hand + * argument (which is at least `left`) is always strictly greater than + * the right hand argument (which is at most `right`), and `ss` is + * `RightIsSmaller`. + * + * Note that the comparison operation could be any binary comparison + * operator, for example,`==`, `>`, or `<=`. + */ +private +predicate alwaysGT(ComparisonOperation cmp, float left, float right, SmallSide ss) { + ss = RightIsSmaller() and + left = lowerBoundFC(cmp.getLeftOperand()) and + right = upperBoundFC(cmp.getRightOperand()) and + left > right +} + +/** + * Holds if `cmp` is a comparison operation in which the left hand + * argument (which is at least `left`) is always greater than or equal + * to the right hand argument (which is at most `right`), and `ss` is + * `RightIsSmaller`. + * + * Note that the comparison operation could be any binary comparison + * operator, for example,`==`, `>`, or `<=`. + */ +private +predicate alwaysGE(ComparisonOperation cmp, float left, float right, SmallSide ss) { + ss = RightIsSmaller() and + left = lowerBoundFC(cmp.getLeftOperand()) and + right = upperBoundFC(cmp.getRightOperand()) and + left >= right and + + // Range analysis is not able to precisely represent large 64 bit numbers, + // because it stores the range as a `float`, which only has a 53 bit mantissa. + // For example, the number 2^64-1 is rounded to 2^64. This means that we + // cannot trust the result if the numbers are large. Note: there is only + // a risk of a rounding error causing an incorrect result if `left == right`. + // If `left` is strictly less than `right` then there is enough of a gap + // that we don't need to worry about rounding errors. + left.ulp() <= 1 +} + +/** + * Holds if `cmp` is a comparison operation that always has the + * result `value`, and either + * * `ss` is `LeftIsSmaller`, and the left hand argument is always at + * most `left`, the right hand argument at least `right`, and `left` + * is less than or equal to `right`; or + * * `ss` is `RightIsSmaller`, and the left hand argument is always at + * least `left`, the right hand argument at most `right`, and `left` + * is greater than or equal to `right`. + * + * For example, if the comparison `x < y` is always true because + * `x <= 3` and `5 <= y` then + * `pointlessComparison(x < y, 3, 5, true, LeftIsSmaller)` holds. + * + * Similarly, if the comparison `x < y` is always false because `x >= 9` + * and `7 >= y` then + * `pointlessComparison(x < y, 9, 7, false, RightIsSmaller)` holds. + */ +predicate pointlessComparison(ComparisonOperation cmp, + float left, float right, + boolean value, SmallSide ss) { + (alwaysLT(cmp.(LTExpr), left, right, ss) and value = true) or + (alwaysLE(cmp.(LEExpr), left, right, ss) and value = true) or + (alwaysGT(cmp.(GTExpr), left, right, ss) and value = true) or + (alwaysGE(cmp.(GEExpr), left, right, ss) and value = true) or + (alwaysLT(cmp.(NEExpr), left, right, ss) and value = true) or + (alwaysGT(cmp.(NEExpr), left, right, ss) and value = true) or + (alwaysGE(cmp.(LTExpr), left, right, ss) and value = false) or + (alwaysGT(cmp.(LEExpr), left, right, ss) and value = false) or + (alwaysLE(cmp.(GTExpr), left, right, ss) and value = false) or + (alwaysLT(cmp.(GEExpr), left, right, ss) and value = false) or + (alwaysLT(cmp.(EQExpr), left, right, ss) and value = false) or + (alwaysGT(cmp.(EQExpr), left, right, ss) and value = false) +} + +/** + * Holds if `cmp` is a pointless comparison (see `pointlessComparison` + * above) and `cmp` occurs in reachable code. The reason for excluding + * expressions that occur in unreachable code is that range analysis + * sometimes can deduce impossible ranges for them. For example: + * + * if (10 < x) { + * if (x < 5) { + * // Unreachable code + * return x; // x has an empty range: 10 < x && x < 5 + * } + * } + + */ +predicate reachablePointlessComparison(ComparisonOperation cmp, + float left, float right, + boolean value, SmallSide ss) { + pointlessComparison(cmp, left, right, value, ss) + + // Reachable according to control flow analysis. + and + reachable(cmp) + + // Reachable according to range analysis. + and + not (exprWithEmptyRange(cmp.getAChild+())) +} diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeAnalysisUtils.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeAnalysisUtils.qll new file mode 100644 index 000000000000..a850b20c1387 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeAnalysisUtils.qll @@ -0,0 +1,363 @@ +import cpp + +/** + * Describes whether a relation is 'strict' (that is, a `<` or `>` + * relation) or 'non-strict' (a `<=` or `>=` relation). + */ +newtype RelationStrictness = Strict() or Nonstrict() + +/** + * Describes whether a relation is 'greater' (that is, a `>` or `>=` + * relation) or 'lesser' (a `<` or `<=` relation). + */ +newtype RelationDirection = Greater() or Lesser() + +private +RelationStrictness negateStrictness(RelationStrictness strict) { + (strict = Strict() and result = Nonstrict()) or + (strict = Nonstrict() and result = Strict()) +} + +private +RelationDirection negateDirection(RelationDirection dir) { + (dir = Greater() and result = Lesser()) or + (dir = Lesser() and result = Greater()) +} + +boolean directionIsGreater(RelationDirection dir) { + (dir = Greater() and result = true) or + (dir = Lesser() and result = false) +} + +boolean directionIsLesser(RelationDirection dir) { + (dir = Greater() and result = false) or + (dir = Lesser() and result = true) +} + +/** + * Holds if `rel` is a relational operation (`<`, `>`, `<=` or `>=`) + * with left operand `lhs` and right operand `rhs`, described by + * `dir` and `strict`. + * + * For example, if `rel` is `x < 5` then + * `relOp(rel, x, 5, Lesser(), Strict())` holds. + */ +private +predicate relOp( + RelationalOperation rel, Expr lhs, Expr rhs, + RelationDirection dir, RelationStrictness strict +) { + lhs = rel.getLeftOperand() and + rhs = rel.getRightOperand() and + ((rel instanceof LTExpr and dir = Lesser() and strict = Strict()) or + (rel instanceof LEExpr and dir = Lesser() and strict = Nonstrict()) or + (rel instanceof GTExpr and dir = Greater() and strict = Strict()) or + (rel instanceof GEExpr and dir = Greater() and strict = Nonstrict())) +} + +/** + * Holds if `rel` is a relational operation (`<`, `>`, `<=` or `>=`) + * with children `a` and `b`, described by `dir` and `strict`. + * + * This allows for the relation to be either as written, or with its + * arguments reversed; for example, if `rel` is `x < 5` then both + * `relOpWithSwap(rel, x, 5, Lesser(), Strict())` and + * `relOpWithSwap(rel, 5, x, Greater(), Strict())` hold. + */ +predicate relOpWithSwap( + RelationalOperation rel, Expr a, Expr b, + RelationDirection dir, RelationStrictness strict +) { + relOp(rel, a, b, dir, strict) or + relOp(rel, b, a, negateDirection(dir), strict) +} + +/** + * Holds if `rel` is a comparison operation (`<`, `>`, `<=` or `>=`) + * with children `a` and `b`, described by `dir` and `strict`, with + * result `branch`. + * + * This allows for the relation to be either as written, or with its + * arguments reversed; for example, if `rel` is `x < 5` then + * `relOpWithSwapAndNegate(rel, x, 5, Lesser(), Strict(), true)`, + * `relOpWithSwapAndNegate(rel, 5, x, Greater(), Strict(), true)`, + * `relOpWithSwapAndNegate(rel, x, 5, Greater(), Nonstrict(), false)` and + * `relOpWithSwapAndNegate(rel, 5, x, Lesser(), Nonstrict(), false)` hold. + */ +predicate relOpWithSwapAndNegate( + RelationalOperation rel, Expr a, Expr b, + RelationDirection dir, RelationStrictness strict, boolean branch +) { + (relOpWithSwap(rel, a, b, dir, strict) and branch = true) + or + (relOpWithSwap(rel, a, b, negateDirection(dir), negateStrictness(strict)) and + branch = false) +} + +/** + * Holds if `cmp` is an equality operation (`==` or `!=`) with left + * operand `lhs`, right operand `rhs`, and `isEQ` is true if `cmp` is an + * `==` operation and false if it is an `!=` operation. + * + * For example, if `rel` is `x == 5` then + * `eqOpWithSwap(cmp, x, 5, true)` holds. + */ +private +predicate eqOp(EqualityOperation cmp, Expr lhs, Expr rhs, boolean isEQ) { + lhs = cmp.getLeftOperand() and + rhs = cmp.getRightOperand() and + ((cmp instanceof EQExpr and isEQ = true) or + (cmp instanceof NEExpr and isEQ = false)) +} + +/** + * Holds if `cmp` is an equality operation (`==` or `!=`) with operands + * `a` and `b`, and `isEQ` is true if `cmp` is an `==` operation and + * false if it is an `!=` operation. + * + * This allows for the equality to be either as written, or with its + * arguments reversed; for example, if `cmp` is `x == 5` then both + * `eqOpWithSwap(cmp, x, 5, true)` and + * `eqOpWithSwap(cmp, 5, x, true)` hold. + */ +private +predicate eqOpWithSwap(EqualityOperation cmp, Expr a, Expr b, boolean isEQ) { + eqOp(cmp, a, b, isEQ) or + eqOp(cmp, b, a, isEQ) +} + +/** + * Holds if `cmp` is an equality operation (`==` or `!=`) with operands + * `a` and `b`, `isEQ` is true if `cmp` is an `==` operation and + * false if it is an `!=` operation, and the result is `branch`. + * + * This allows for the comparison to be either as written, or with its + * arguments reversed; for example, if `cmp` is `x == 5` then + * `eqOpWithSwapAndNegate(cmp, x, 5, true, true)`, + * `eqOpWithSwapAndNegate(cmp, 5, x, true, true)`, + * `eqOpWithSwapAndNegate(cmp, x, 5, false, false)` and + * `eqOpWithSwapAndNegate(cmp, 5, x, false, false)` hold. + */ +predicate eqOpWithSwapAndNegate( + EqualityOperation cmp, Expr a, Expr b, boolean isEQ, boolean branch +) { + (eqOpWithSwap(cmp, a, b, branch) and isEQ = true) or + (eqOpWithSwap(cmp, a, b, 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 + * distributivity of arithmetic operations. + */ +predicate linearAccess(Expr expr, VariableAccess v, float p, float q) { + // Exclude 0 and NaN. + (p < 0 or p > 0) and + linearAccessImpl(expr, v, p, q) +} + +/** + * Holds if `expr` is equivalent to `p*v + q`. + * This takes into account the associativity, commutativity and + * distributivity of arithmetic operations. + */ +private +predicate linearAccessImpl(Expr expr, VariableAccess v, float p, float q) { + // Base case + (expr = 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 + a = addExpr.getLeftOperand().getFullyConverted().getValue().toFloat() and + linearAccess(addExpr.getRightOperand(), v, p, b) and + expr = addExpr and + q = a+b) + or + // (p*v+a)+b == p*v + (a+b) + exists (AddExpr addExpr, float a, float b + | addExpr.getRightOperand().isConstant() and + b = addExpr.getRightOperand().getFullyConverted().getValue().toFloat() and + linearAccess(addExpr.getLeftOperand(), v, p, a) and + expr = addExpr and + q = a+b) + or + // a-(m*v+b) == -m*v + (a-b) + exists (SubExpr subExpr, float a, float b, float m + | subExpr.getLeftOperand().isConstant() and + a = subExpr.getLeftOperand().getFullyConverted().getValue().toFloat() and + linearAccess(subExpr.getRightOperand(), v, m, b) and + expr = subExpr and + p = -m and + q = a-b) + or + // (p*v+a)-b == p*v + (a-b) + exists (SubExpr subExpr, float a, float b + | subExpr.getRightOperand().isConstant() and + b = subExpr.getRightOperand().getFullyConverted().getValue().toFloat() and + linearAccess(subExpr.getLeftOperand(), v, p, a) and + expr = subExpr and + q = a-b) + or + // +(p*v+q) == p*v + q + exists (UnaryPlusExpr unaryPlusExpr + | linearAccess(unaryPlusExpr.getOperand(), v, p, q) and + expr = unaryPlusExpr) + or + // -(a*v+b) == -a*v + (-b) + exists (UnaryMinusExpr unaryMinusExpr, float a, float b + | linearAccess(unaryMinusExpr.getOperand(), v, a, b) and + expr = unaryMinusExpr and + p = -a and + q = -b) + or + // m*(a*v+b) == (m*a)*v + (m*b) + exists (MulExpr mulExpr, float a, float b, float m + | mulExpr.getLeftOperand().isConstant() and + m = mulExpr.getLeftOperand().getFullyConverted().getValue().toFloat() and + linearAccess(mulExpr.getRightOperand(), v, a, b) and + expr = mulExpr and + p = m*a and + q = m*b) + or + // (a*v+b)*m == (m*a)*v + (m*b) + exists (MulExpr mulExpr, float a, float b, float m + | mulExpr.getRightOperand().isConstant() and + m = mulExpr.getRightOperand().getFullyConverted().getValue().toFloat() and + linearAccess(mulExpr.getLeftOperand(), v, a, b) and + expr = mulExpr and + p = m*a and + q = m*b) +} + + +/** + * Holds if `guard` is a comparison operation (`<`, `<=`, `>`, `>=`, + * `==` or `!=`), one of its arguments is equivalent (up to + * associativity, commutativity and distributivity or the simple + * arithmetic operations) to `p*v + q` (for some `p` and `q`), + * `direction` describes whether `guard` give an upper or lower bound + * on `v`, and `branch` indicates which control-flow branch this + * bound is valid on. + * + * For example, if `guard` is `2*v + 3 > 10` then + * `cmpWithLinearBound(guard, v, Greater(), true)` and + * `cmpWithLinearBound(guard, v, Lesser(), false)` hold. + * If `guard` is `4 - v > 5` then + * `cmpWithLinearBound(guard, v, Lesser(), false)` and + * `cmpWithLinearBound(guard, v, Greater(), true)` hold. + * + * A more sophisticated predicate, such as `boundFromGuard`, is needed + * to compute an actual bound for `v`. This predicate can be used if + * you just want to check whether a variable is bounded, or to restrict + * a more expensive analysis to just guards that bound a variable. + */ +predicate cmpWithLinearBound( + ComparisonOperation guard, VariableAccess v, + RelationDirection direction, // Is this a lower or an upper bound? + boolean branch // Which control-flow branch is this bound valid on? +) { + exists (Expr lhs, float p, RelationDirection dir + | linearAccess(lhs, v, p, _) and + relOpWithSwapAndNegate(guard, lhs, _, dir, _, branch) and + ((p > 0 and direction = dir) or + (p < 0 and direction = negateDirection(dir)))) + + or + exists (Expr lhs + | linearAccess(lhs, v, _, _) and + eqOpWithSwap(guard, lhs, _, branch)) +} + +/** + * Holds if `lb` and `ub` are the lower and upper bounds of type `t` + * respectively. + * + * For example, if `t` is a signed 32-bit type then holds if `lb` is + * `-2^31` and `ub` is `2^31 - 1`. + */ +private +predicate typeBounds(Type t, float lb, float ub) { + exists (IntegralType integralType, float limit + | integralType = t and limit = 2.pow(8 * integralType.getSize()) + | if integralType instanceof BoolType + then (lb = 0 and ub = 1) + else if integralType.isSigned() + then (lb = -(limit/2) and ub = (limit/2)-1) + else (lb = 0 and ub = limit-1)) + or + // This covers all floating point types. The range is (-Inf, +Inf). + (t instanceof FloatingPointType and lb = -(1.0/0.0) and ub = 1.0/0.0) +} + +/** + * Gets the lower bound for type `t`. + * + * For example, if `t` is a signed 32-bit type then the result is + * `-2^31`. + */ +float typeLowerBound(Type t) { + typeBounds(t, result, _) +} + +/** + * Gets the upper bound for type `t`. + * + * For example, if `t` is a signed 32-bit type then the result is + * `2^31 - 1`. + */ +float typeUpperBound(Type t) { + typeBounds(t, _, result) +} + +/** + * Gets the minimum value that this expression could represent, based on + * its type. + * + * For example, if `expr` has a signed 32-bit type then the result is + * `-2^31`. + * + * Note: Due to the way casts are represented, rather than calling + * `exprMinVal(expr)` you will normally want to call + * `exprMinVal(expr.getFullyConverted())`. + */ +float exprMinVal(Expr expr) { + result = typeLowerBound(expr.getType().getUnspecifiedType()) +} + +/** + * Gets the maximum value that this expression could represent, based on + * its type. + * + * For example, if `expr` has a signed 32-bit type then the result is + * `2^31 - 1`. + * + * Note: Due to the way casts are represented, rather than calling + * `exprMaxVal(expr)` you will normally want to call + * `exprMaxVal(expr.getFullyConverted())`. + */ +float exprMaxVal(Expr expr) { + result = typeUpperBound(expr.getType().getUnspecifiedType()) +} + +/** + * Gets the minimum value that this variable could represent, based on + * its type. + * + * For example, if `v` has a signed 32-bit type then the result is + * `-2^31`. + */ +float varMinVal(Variable v) { + result = typeLowerBound(v.getType().getUnspecifiedType()) +} + +/** + * Gets the maximum value that this variable could represent, based on + * its type. + * + * For example, if `v` has a signed 32-bit type then the result is + * `2^31 - 1`. + */ +float varMaxVal(Variable v) { + result = typeUpperBound(v.getType().getUnspecifiedType()) +} diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeSSA.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeSSA.qll new file mode 100644 index 000000000000..fc39690d054c --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeSSA.qll @@ -0,0 +1,179 @@ +/** + * This library is a clone of semmle.code.cpp.controlflow.SSA, with + * only one difference: extra phi definitions are added after + * guards. For example: + * + * x = f(); + * if (x < 10) { + * // Block 1 + * ... + * } else { + * // Block 2 + * ... + * } + * + * In standard SSA, basic blocks 1 and 2 do not need phi definitions + * for x, because they are dominated by the definition of x on the + * first line. In RangeSSA, however, we add phi definitions for x at + * the beginning of blocks 1 and 2. This is useful for range analysis + * because it enables us to deduce a more accurate range for x in the + * two branches of the if-statement. + */ + +import cpp +import semmle.code.cpp.controlflow.Dominance +import semmle.code.cpp.controlflow.SSAUtils +private import RangeAnalysisUtils + +library class RangeSSA extends SSAHelper { + RangeSSA() { this = 1 } + + /** + * Add a phi node on the out-edge of a guard. + */ + override predicate custom_phi_node(LocalScopeVariable v, BasicBlock b) { guard_defn(v.getAnAccess(),_,b,_) } +} + +private predicate guard_defn( + VariableAccess v, ComparisonOperation guard, BasicBlock b, boolean branch +) { + guardCondition(guard, v, branch) + and + guardSuccessor(guard, branch, b) +} + +private predicate guardCondition( + ComparisonOperation guard, VariableAccess v, boolean branch +) { + exists (Expr lhs + | linearAccess(lhs, v, _, _) + | relOpWithSwapAndNegate(guard, lhs, _, _, _, branch) or + eqOpWithSwapAndNegate(guard, lhs, _, _, branch)) +} + +private predicate guardSuccessor(ComparisonOperation guard, boolean branch, BasicBlock succ) { + (branch = true and succ = guard.getATrueSuccessor()) + or + (branch = false and succ = guard.getAFalseSuccessor()) +} + +/** + * A definition of one or more SSA variables, including phi node + * definitions. An SSA variable is effectively the pair of a definition + * and the (non-SSA) variable that it defines. Note definitions and uses + * can be coincident, due to the presence of parameter definitions and phi + * nodes. + */ +class RangeSsaDefinition extends @cfgnode { + + RangeSsaDefinition() { + exists(RangeSSA x | x.ssa_defn(_, (ControlFlowNode)this, _, _)) + } + + /** + * Gets a variable corresponding to a SSA LocalScopeVariable defined by + * this definition. + */ + LocalScopeVariable getAVariable() { + exists(RangeSSA x | x.ssa_defn(result, (ControlFlowNode)this, _, _)) + } + + string toString() { + result = "SSA definition" + } + + /** + * A string representation of the SSA variable represented by the pair + * (this, v). + */ + string toString(LocalScopeVariable v) { + exists(RangeSSA x | result = x.toString((ControlFlowNode)this, v)) + } + + /** Gets a use of the SSA variable represented by the pair (this, v) */ + VariableAccess getAUse(LocalScopeVariable v) { + exists(RangeSSA x | result = x.getAUse((ControlFlowNode)this, v)) + } + + /** Gets the control flow node for this definition */ + ControlFlowNode getDefinition() { + result = this + } + + BasicBlock getBasicBlock() { + result.contains(getDefinition()) + } + + /** Whether this definition is a phi node for variable v */ + predicate isPhiNode(LocalScopeVariable v) { + exists(RangeSSA x | x.phi_node(v, (BasicBlock)this)) + } + + /** + * 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) { + guard_defn(v, guard, this, branch) + } + + Location getLocation() { + result = this.(ControlFlowNode).getLocation() + } + + /** Whether this definition is from a parameter */ + predicate definedByParameter(Parameter p) { + this = p.getFunction().getEntryPoint() + } + + RangeSsaDefinition getAPhiInput(LocalScopeVariable v) { + this.isPhiNode(v) + and + exists (BasicBlock pred + | pred = this.(BasicBlock).getAPredecessor() and + result.reachesEndOfBB(v, pred) and + + // Suppose we have a CFG like this: + // + // 1: x_0 = ; + // 2: if () { + // 3: if (x_0 > 1) { + // 4: x_1 = phi(x_0); + // 5: } + // 6: } + // 7: x_2 = phi(x_0, x_1); + // + // The phi nodes on lines 4 and 7 are both guard phi nodes, + // because they have an incoming edge from the condition on + // line 3. Definition x_0 on line 1 should be considered a + // phi-input on line 7, but not on line 4. This is because + // the only CFG path from line 1 to line 4 goes through the + // condition on line 3, but there is a path from line 1 to + // line 7 which does not go through the condition. The logic + // below excludes definitions which can only reach guard phi + // nodes by going through the corresponding guard. + not exists (VariableAccess access + | v = access.getTarget() and + pred.contains(access) and + this.isGuardPhi(access, _, _))) + } + + /** Gets the expression assigned to this SsaDefinition */ + Expr getDefiningValue(LocalScopeVariable v) { + exists(ControlFlowNode def | def = this.getDefinition() | + def = v.getInitializer().getExpr() and def = result + or + exists(AssignExpr assign | def = assign and + assign.getLValue() = v.getAnAccess() and result = assign.getRValue() + ) + or + exists(AssignOperation assign | def = assign and + assign.getLValue() = v.getAnAccess() and result = assign + ) + ) + } + + predicate reachesEndOfBB(LocalScopeVariable v, BasicBlock b) { + exists(RangeSSA x | x.ssaDefinitionReachesEndOfBB(v, (ControlFlowNode)this, b)) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll new file mode 100644 index 000000000000..f1312ad8b996 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll @@ -0,0 +1,1195 @@ +/** + * Simple range analysis library. Range analysis is usually done as an + * abstract interpretation over the lattice of range values. (A range is a + * pair, containing a lower and upper bound for the value.) The problem + * with this approach is that the lattice is very tall, which means it can + * take an extremely large number of iterations to find the least fixed + * point. This example illustrates the problem: + * + * int count = 0; + * for (; p; p = p->next) { + * count = count+1; + * } + * + * The range of 'count' is initially (0,0), then (0,1) on the second + * iteration, (0,2) on the third iteration, and so on until we eventually + * reach maxInt. + * + * This library uses a crude solution to the problem described above: if + * the upper (or lower) bound of an expression might depend recursively on + * itself then we round it up (down for lower bounds) to one of a fixed set + * of values, such as 0, 1, 2, 256, and +Inf. This limits the height of the + * lattice which ensures that the analysis will terminate in a reasonable + * amount of time. This solution is similar to the abstract interpretation + * technique known as 'widening', but it is less precise because we are + * unable to inspect the bounds from the previous iteration of the fixed + * point computation. For example, widening might be able to deduce that + * the lower bound is -11 but we would approximate it to -16. + * + * QL does not allow us to compute an aggregate over a recursive + * sub-expression, so we cannot compute the minimum lower bound and maximum + * upper bound during the recursive phase of the query. Instead, the + * recursive phase computes a set of lower bounds and a set of upper bounds + * for each expression. We compute the minimum lower bound and maximum + * upper bound after the recursion is finished. This is another reason why + * we need to limit the number of bounds per expression, because they will + * all be stored until the recursive phase is finished. + * + * The ranges are represented using a pair of floating point numbers. This + * is simpler than using integers because floating point numbers cannot + * overflow and wrap. It is also convenient because we can detect overflow + * and negative overflow by looking for bounds that are outside the range + * of the type. + */ +import cpp +private import RangeAnalysisUtils +import RangeSSA + +/** + * This fixed set of lower bounds is used when the lower bounds of an + * expression are recursively defined. The inferred lower bound is rounded + * down to the nearest lower bound in the fixed set. This restricts the + * height of the lattice, which prevents the analysis from exploding. + * + * Note: these bounds were chosen fairly arbitrarily. Feel free to add more + * bounds to the set if it helps on specific examples and does not make + * performance dramatically worse on large codebases, such as libreoffice. + */ +private +float wideningLowerBounds(ArithmeticType t) { + result = 2.0 or + result = 1.0 or + result = 0.0 or + result = -1.0 or + result = -2.0 or + result = -8.0 or + result = -16.0 or + result = -128.0 or + result = -256.0 or + result = -32768.0 or + result = -65536.0 or + result = typeLowerBound(t) or + result = -(1.0/0.0) // -Inf +} + +/** See comment for `wideningLowerBounds`, above. */ +private +float wideningUpperBounds(ArithmeticType t) { + result = -2.0 or + result = -1.0 or + result = 0.0 or + result = 1.0 or + result = 2.0 or + result = 7.0 or + result = 15.0 or + result = 127.0 or + result = 255.0 or + result = 32767.0 or + result = 65535.0 or + result = typeUpperBound(t) or + result = 1.0/0.0 // +Inf +} + +/** 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 + // `exprMinVal` to check this. + exists (exprMinVal(e)) and + ((exists (e.getValue().toFloat())) or + (e instanceof UnaryPlusExpr) or + (e instanceof UnaryMinusExpr) or + (e instanceof MinExpr) or + (e instanceof MaxExpr) or + (e instanceof ConditionalExpr) or + (e instanceof AddExpr) or + (e instanceof SubExpr) or + (e instanceof CrementOperation) or + (e instanceof RemExpr) or + (e instanceof CommaExpr) or + (e instanceof StmtExpr) 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 + // any bounds.) Rather than checking whether the type of the child is + // arithmetic, we reuse the logic that is already encoded in + // `exprMinVal`. + exists(exprMinVal(e.(Conversion).getExpr())) or + + // Also allow variable accesses, provided that they have SSA + // information. + exists (RangeSsaDefinition def, LocalScopeVariable v | e = def.getAUse(v))) +} + +/** + * Set of definitions that this definition depends on. The transitive + * closure of this relation is used to detect definitions which are + * recursively defined, so that we can prevent the analysis from exploding. + * + * The structure of `defDependsOnDef` and its helper predicates matches the + * structure of `getDefLowerBoundsImpl` and + * `getDefUpperBoundsImpl`. Therefore, if changes are made to the structure + * of the main analysis algorithm then matching changes need to be made + * here. + */ +private +predicate defDependsOnDef( + RangeSsaDefinition def, LocalScopeVariable v, + RangeSsaDefinition srcDef, LocalScopeVariable srcVar +) { + // 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)) + or + exists (CrementOperation crem + | def = crem and + crem.getOperand() = v.getAnAccess() and + exprDependsOnDef(crem.getOperand(), srcDef, srcVar)) + or + // Phi nodes. + phiDependsOnDef(def, v, srcDef, srcVar) +} + +/** + * Helper predicate for `defDependsOnDef`. This predicate matches + * the structure of `getLowerBoundsImpl` and `getUpperBoundsImpl`. + */ +private +predicate exprDependsOnDef( + Expr e, RangeSsaDefinition srcDef, LocalScopeVariable srcVar +) { + exists (UnaryMinusExpr negateExpr + | e = negateExpr + | exprDependsOnDef(negateExpr.getOperand(), srcDef, srcVar)) + or + exists (UnaryPlusExpr plusExpr + | e = plusExpr + | exprDependsOnDef(plusExpr.getOperand(), srcDef, srcVar)) + or + exists (MinExpr minExpr + | e = minExpr + | exprDependsOnDef(minExpr.getAnOperand(), srcDef, srcVar)) + or + exists (MaxExpr maxExpr + | e = maxExpr + | exprDependsOnDef(maxExpr.getAnOperand(), srcDef, srcVar)) + or + exists (ConditionalExpr condExpr + | e = condExpr + | exprDependsOnDef(condExpr.getAnOperand(), srcDef, srcVar)) + or + exists (AddExpr addExpr + | e = addExpr + | exprDependsOnDef(addExpr.getAnOperand(), srcDef, srcVar)) + or + exists (SubExpr subExpr + | e = subExpr + | exprDependsOnDef(subExpr.getAnOperand(), srcDef, srcVar)) + or + exists (CrementOperation crementExpr + | e = crementExpr + | exprDependsOnDef(crementExpr.getOperand(), srcDef, srcVar)) + or + exists (RemExpr remExpr + | e = remExpr + | exprDependsOnDef(remExpr.getAnOperand(), srcDef, srcVar)) + or + exists (CommaExpr commaExpr + | e = commaExpr + | exprDependsOnDef(commaExpr.getRightOperand(), srcDef, srcVar)) + or + exists (StmtExpr stmtExpr + | e = stmtExpr + | exprDependsOnDef(stmtExpr.getResultExpr(), srcDef, srcVar)) + or + exists (Conversion convExpr + | e = convExpr + | exprDependsOnDef(convExpr.getExpr(), srcDef, srcVar)) + or + e = srcDef.getAUse(srcVar) +} + +/** + * Helper predicate for `defDependsOnDef`. This predicate matches + * the structure of `getPhiLowerBounds` and `getPhiUpperBounds`. + */ +private +predicate phiDependsOnDef( + RangeSsaDefinition phi, LocalScopeVariable v, + RangeSsaDefinition srcDef, LocalScopeVariable srcVar +) { + exists (VariableAccess access, ComparisonOperation guard + | access = v.getAnAccess() and + phi.isGuardPhi(access, guard, _) + | exprDependsOnDef(guard.getAnOperand(), srcDef, srcVar) or + exprDependsOnDef(access, srcDef, srcVar)) + or + (srcDef = phi.getAPhiInput(v) and srcVar = v) +} + +/** The transitive closure of `defDependsOnDef`. */ +private +predicate defDependsOnDefTransitively( + RangeSsaDefinition def, LocalScopeVariable v, + RangeSsaDefinition srcDef, LocalScopeVariable srcVar +) { + defDependsOnDef(def, v, srcDef, srcVar) or + exists (RangeSsaDefinition midDef, LocalScopeVariable midVar + | defDependsOnDef(def, v, midDef, midVar) + | defDependsOnDefTransitively(midDef, midVar, srcDef, srcVar)) +} + +/** The set of definitions that depend recursively on themselves. */ +private +predicate isRecursiveDef(RangeSsaDefinition def, LocalScopeVariable v) { + defDependsOnDefTransitively(def, v, def, v) +} + +/** + * We distinguish 3 kinds of RangeSsaDefinition: + * + * 1. Definitions with a defining value. + * For example: x = y+3 is a definition of x with defining value y+3. + * + * 2. Phi nodes: x3 = phi(x0,x1,x2) + * + * 3. Unanalyzable definitions. + * For example: a parameter is unanalyzable because we know nothing + * about its value. + * + * This predicate finds all the definitions in the first set. + */ +private +predicate assignmentDef(RangeSsaDefinition def, LocalScopeVariable v, Expr expr) { + v.getType().getUnspecifiedType() instanceof ArithmeticType + and + ((def = v.getInitializer().getExpr() and def = expr) + or + exists(AssignExpr assign + | def = assign and + assign.getLValue() = v.getAnAccess() and + expr = assign.getRValue())) +} + +/** See comment above sourceDef. */ +private +predicate analyzableDef(RangeSsaDefinition def, LocalScopeVariable v) { + assignmentDef(def, v, _) or defDependsOnDef(def, v, _, _) +} + +/** + * 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. + * + * Most of the work of computing the lower bounds is done by + * `getLowerBoundsImpl`. However, the lower bounds computed by + * `getLowerBoundsImpl` may not be representable by the result type of the + * expression. For example, if `x` and `y` are of type `int32` and each + * have lower bound -2147483648, then getLowerBoundsImpl` will compute a + * lower bound -4294967296 for the expression `x+y`, even though + * -4294967296 cannot be represented as an `int32`. Such unrepresentable + * bounds are replaced with `exprMinVal(expr)`. This predicate also adds + * `exprMinVal(expr)` as a lower bound if the expression might overflow + * positively, or if it is unanalyzable. + * + * Note: most callers should use `getFullyConvertedLowerBounds` rather than + * this predicate. + */ +private +float getTruncatedLowerBounds(Expr expr) { + if analyzableExpr(expr) + then // If the expression evaluates to a constant, then there is no + // need to call getLowerBoundsImpl. + if exists(expr.getValue().toFloat()) + then result = expr.getValue().toFloat() + else (// Some of the bounds computed by getLowerBoundsImpl might + // overflow, so we replace invalid bounds with exprMinVal. + exists (float newLB + | newLB = getLowerBoundsImpl(expr) + | if exprMinVal(expr) <= newLB and newLB <= exprMaxVal(expr) + then result = newLB + else result = exprMinVal(expr)) + or + // The expression might overflow and wrap. If so, the + // lower bound is exprMinVal. + (exprMightOverflowPositively(expr) and + result = exprMinVal(expr))) + else // The expression is not analyzable, so its lower bound is + // unknown. Note that the call to exprMinVal restricts the + // expressions to just those with arithmetic types. There is no + // need to return results for non-arithmetic expressions. + 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. + * + * Most of the work of computing the upper bounds is done by + * `getUpperBoundsImpl`. However, the upper bounds computed by + * `getUpperBoundsImpl` may not be representable by the result type of the + * expression. For example, if `x` and `y` are of type `int32` and each + * have upper bound 2147483647, then getUpperBoundsImpl` will compute an + * upper bound 4294967294 for the expression `x+y`, even though 4294967294 + * cannot be represented as an `int32`. Such unrepresentable bounds are + * replaced with `exprMaxVal(expr)`. This predicate also adds + * `exprMaxVal(expr)` as an upper bound if the expression might overflow + * negatively, or if it is unanalyzable. + * + * Note: most callers should use `getFullyConvertedUpperBounds` rather than + * this predicate. + */ +private +float getTruncatedUpperBounds(Expr expr) { + if analyzableExpr(expr) + then // If the expression evaluates to a constant, then there is no + // need to call getUpperBoundsImpl. + if exists(expr.getValue().toFloat()) + then result = expr.getValue().toFloat() + else (// Some of the bounds computed by `getUpperBoundsImpl` + // might overflow, so we replace invalid bounds with + // `exprMaxVal`. + exists (float newUB + | newUB = getUpperBoundsImpl(expr) + | if exprMinVal(expr) <= newUB and newUB <= exprMaxVal(expr) + then result = newUB + else result = exprMaxVal(expr)) + or + // The expression might overflow negatively and wrap. If so, + // the upper bound is `exprMaxVal`. + (exprMightOverflowNegatively(expr) and + result = exprMaxVal(expr))) + else // The expression is not analyzable, so its upper bound is + // unknown. Note that the call to exprMaxVal restricts the + // expressions to just those with arithmetic types. There is no + // need to return results for non-arithmetic expressions. + result = exprMaxVal(expr) +} + +/** + * Holds if the expression might overflow negatively. This predicate + * does not consider the possibility that the expression might overflow + * due to a conversion. + * + * DEPRECATED: use `exprMightOverflowNegatively` instead. + */ +deprecated predicate negative_overflow(Expr expr) { + exprMightOverflowNegatively(expr) +} + +/** + * Holds if the expression might overflow negatively. This predicate + * does not consider the possibility that the expression might overflow + * due to a conversion. + */ +cached +predicate exprMightOverflowNegatively(Expr expr) { + getLowerBoundsImpl(expr) < exprMinVal(expr) +} + +/** + * Holds if the expression might overflow negatively. Conversions + * are also taken into account. For example the expression + * `(int16)(x+y)` might overflow due to the `(int16)` cast, rather than + * due to the addition. + */ +cached +predicate convertedExprMightOverflowNegatively(Expr expr) { + exprMightOverflowNegatively(expr) or + convertedExprMightOverflowNegatively(expr.getConversion()) +} + +/** + * Holds if the expression might overflow positively. This predicate + * does not consider the possibility that the expression might overflow + * due to a conversion. + * + * DEPRECATED: use `exprMightOverflowPositively` instead. + */ +deprecated predicate positive_overflow(Expr expr) { + exprMightOverflowPositively(expr) +} + +/** + * Holds if the expression might overflow positively. This predicate + * does not consider the possibility that the expression might overflow + * due to a conversion. + */ +cached +predicate exprMightOverflowPositively(Expr expr) { + getUpperBoundsImpl(expr) > exprMaxVal(expr) +} + +/** + * Holds if the expression might overflow positively. Conversions + * are also taken into account. For example the expression + * `(int16)(x+y)` might overflow due to the `(int16)` cast, rather than + * due to the addition. + */ +cached +predicate convertedExprMightOverflowPositively(Expr expr) { + exprMightOverflowPositively(expr) or + convertedExprMightOverflowPositively(expr.getConversion()) +} + +/** + * Holds if the expression might overflow (either positively or + * negatively). The possibility that the expression might overflow + * due to an implicit or explicit cast is also considered. + */ +cached +predicate convertedExprMightOverflow(Expr expr) { + convertedExprMightOverflowNegatively(expr) or + convertedExprMightOverflowPositively(expr) +} + +/** 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)) + 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 = xLow+yLow) + or + exists (SubExpr subExpr, float xLow, float yHigh + | expr = subExpr and + xLow = getFullyConvertedLowerBounds(subExpr.getLeftOperand()) and + yHigh = getFullyConvertedUpperBounds(subExpr.getRightOperand()) and + result = 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 = 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()) + 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.getType().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, LocalScopeVariable v + | expr = def.getAUse(v) + | result = getDefLowerBounds(def, v)) +} + +/** 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)) + 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 = xHigh+yHigh) + or + exists (SubExpr subExpr, float xHigh, float yLow + | expr = subExpr and + xHigh = getFullyConvertedUpperBounds(subExpr.getLeftOperand()) and + yLow = getFullyConvertedLowerBounds(subExpr.getRightOperand()) and + result = xHigh-yLow) + or + exists (PrefixIncrExpr incrExpr, float xHigh + | expr = incrExpr and + xHigh = getFullyConvertedUpperBounds(incrExpr.getOperand()) and + result = 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) + | 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.getType().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, LocalScopeVariable v + | expr = def.getAUse(v) + | result = getDefUpperBounds(def, v)) +} + +/** + * Holds if `expr` is converted to `bool` or if it is the child of a + * logical operation. + * + * The purpose of this predicate is to optimize `boolConversionLowerBound` + * and `boolConversionUpperBound` by preventing them from computing + * unnecessary results. In other words, `exprIsUsedAsBool(expr)` holds if + * `expr` is an expression that might be passed as an argument to + * `boolConversionLowerBound` or `boolConversionUpperBound`. + */ +private predicate exprIsUsedAsBool(Expr expr) { + expr = any(BinaryLogicalOperation op).getAnOperand().getFullyConverted() or + expr = any(UnaryLogicalOperation op).getOperand().getFullyConverted() or + expr = any(ConditionalExpr c).getCondition().getFullyConverted() or + exists (Conversion cast + | cast.getType().getUnspecifiedType() instanceof BoolType + | expr = cast.getExpr()) +} + +/** + * Gets the lower bound of the conversion `(bool)expr`. If we can prove that + * the value of `expr` is never 0 then `lb = 1`. Otherwise `lb = 0`. + */ +private float boolConversionLowerBound(Expr expr) { + // Case 1: if the range for `expr` includes the value 0, + // then `result = 0`. + (exprIsUsedAsBool(expr) and + exists (float lb | lb = getTruncatedLowerBounds(expr) and not (lb > 0)) and + exists (float ub | ub = getTruncatedUpperBounds(expr) and not (ub < 0)) and + result = 0) + or + // Case 2a: if the range for `expr` does not include the value 0, + // then `result = 1`. + (exprIsUsedAsBool(expr) and getTruncatedLowerBounds(expr) > 0 and result = 1) + or + // Case 2b: if the range for `expr` does not include the value 0, + // then `result = 1`. + (exprIsUsedAsBool(expr) and getTruncatedUpperBounds(expr) < 0 and result = 1) + or + // Case 3: the type of `expr` is not arithmetic. For example, it might + // be a pointer. + (exprIsUsedAsBool(expr) and not exists(exprMinVal(expr)) and result = 0) +} + +/** + * Gets the upper bound of the conversion `(bool)expr`. If we can prove that + * the value of `expr` is always 0 then `ub = 0`. Otherwise `ub = 1`. + */ +private float boolConversionUpperBound(Expr expr) { + // Case 1a: if the upper bound of the operand is <= 0, then the upper + // bound might be 0. + (exprIsUsedAsBool(expr) and getTruncatedUpperBounds(expr) <= 0 and result = 0) + or + // Case 1b: if the upper bound of the operand is not <= 0, then the upper + // bound is 1. + (exprIsUsedAsBool(expr) and + exists (float ub | ub = getTruncatedUpperBounds(expr) and not (ub <= 0)) and + result = 1) + or + // Case 2a: if the lower bound of the operand is >= 0, then the upper + // bound might be 0. + (exprIsUsedAsBool(expr) and getTruncatedLowerBounds(expr) >= 0 and result = 0) + or + // Case 2b: if the lower bound of the operand is not >= 0, then the upper + // bound is 1. + (exprIsUsedAsBool(expr) and + exists (float lb | lb = getTruncatedLowerBounds(expr) and not (lb >= 0)) and + result = 1) + or + // Case 3: the type of `expr` is not arithmetic. For example, it might + // be a pointer. + (exprIsUsedAsBool(expr) and not exists(exprMaxVal(expr)) and result = 1) +} + +/** + * This predicate computes the lower bounds of a phi definition. If the + * phi definition corresponds to a guard, then the guard is used to + * deduce a better lower bound. + * For example: + * + * def: x = y % 10; + * guard: if (x >= 2) { + * block: f(x) + * } + * + * In this example, the lower bound of x is 0, but we can + * use the guard to deduce that the lower bound is 2 inside the block. + */ +private +float getPhiLowerBounds(LocalScopeVariable v, RangeSsaDefinition phi) { + exists ( + VariableAccess access, ComparisonOperation guard, boolean branch, + float defLB, float guardLB + | access = v.getAnAccess() and + phi.isGuardPhi(access, guard, branch) and + lowerBoundFromGuard(guard, access, guardLB, branch) and + defLB = getFullyConvertedLowerBounds(access) + | // Compute the maximum of `guardLB` and `defLB`. + if guardLB > defLB + then result = guardLB + else result = defLB) + or + result = getDefLowerBounds(phi.getAPhiInput(v), v) +} + +/** See comment for `getPhiLowerBounds`, above. */ +private +float getPhiUpperBounds(LocalScopeVariable v, RangeSsaDefinition phi) { + exists ( + VariableAccess access, ComparisonOperation guard, boolean branch, + float defUB, float guardUB + | access = v.getAnAccess() and + phi.isGuardPhi(access, guard, branch) and + upperBoundFromGuard(guard, access, guardUB, branch) and + defUB = getFullyConvertedUpperBounds(access) + | // Compute the minimum of `guardUB` and `defUB`. + if guardUB < defUB + then result = guardUB + else result = defUB) + or + result = getDefUpperBounds(phi.getAPhiInput(v), v) +} + +/** Only to be called by `getDefLowerBounds`. */ +private +float getDefLowerBoundsImpl(RangeSsaDefinition def, LocalScopeVariable 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 = 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 = lhsLB - rhsUB) + or + exists (IncrementOperation incr, float newLB + | def = incr and + incr.getOperand() = v.getAnAccess() and + newLB = getFullyConvertedLowerBounds(incr.getOperand()) and + result = newLB+1) + or + exists (DecrementOperation decr, float newLB + | def = decr and + decr.getOperand() = v.getAnAccess() and + newLB = getFullyConvertedLowerBounds(decr.getOperand()) and + result = newLB-1) + or + // Phi nodes. + result = getPhiLowerBounds(v, def) + or + // Unanalyzable definitions. + unanalyzableDefBounds(def, v, result, _) +} + +/** Only to be called by `getDefUpperBounds`. */ +private +float getDefUpperBoundsImpl(RangeSsaDefinition def, LocalScopeVariable 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 = 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 = lhsUB - rhsLB) + or + exists (IncrementOperation incr, float newUB + | def = incr and + incr.getOperand() = v.getAnAccess() and + newUB = getFullyConvertedUpperBounds(incr.getOperand()) and + result = newUB+1) + or + exists (DecrementOperation decr, float newUB + | def = decr and + decr.getOperand() = v.getAnAccess() and + newUB = getFullyConvertedUpperBounds(decr.getOperand()) and + result = newUB-1) + or + // Phi nodes. + result = getPhiUpperBounds(v, def) + or + // Unanalyzable definitions. + unanalyzableDefBounds(def, v, _, result) +} + +/** Holds if the definition might overflow negatively. */ +cached +predicate defMightOverflowNegatively(RangeSsaDefinition def, LocalScopeVariable v) { + getDefLowerBoundsImpl(def, v) < varMinVal(v) +} + +/** Holds if the definition might overflow positively. */ +cached +predicate defMightOverflowPositively(RangeSsaDefinition def, LocalScopeVariable v) { + getDefUpperBoundsImpl(def, v) > varMaxVal(v) +} + +/** + * Holds if the definition might overflow (either positively or + * negatively). + */ +cached +predicate defMightOverflow(RangeSsaDefinition def, LocalScopeVariable v) { + defMightOverflowNegatively(def, v) or + defMightOverflowPositively(def, v) +} + +/** + * 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, LocalScopeVariable 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.getType().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, LocalScopeVariable 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.getType().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 + * bounds unknown. + */ +private +predicate unanalyzableDefBounds( + RangeSsaDefinition def, LocalScopeVariable v, float lb, float ub) { + v = def.getAVariable() and + not analyzableDef(def, v) and + lb = varMinVal(v) and + ub = varMaxVal(v) +} + +/** + * If the guard is a comparison of the form `p*v + q r`, then this + * 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 +) { + exists (float childLB, RelationStrictness strictness + | boundFromGuard(guard, v, childLB, true, strictness, branch) + | if (strictness = Nonstrict() or + not (v.getType().getUnspecifiedType() instanceof IntegralType)) + then lb = childLB + else lb = childLB+1) +} + +/** + * If the guard is a comparison of the form `p*v + q r`, then this + * 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 +) { + exists (float childUB, RelationStrictness strictness + | boundFromGuard(guard, v, childUB, false, strictness, branch) + | if (strictness = Nonstrict() or + not (v.getType().getUnspecifiedType() instanceof IntegralType)) + then ub = childUB + else ub = childUB-1) +} + +/** + * This predicate simplifies the results returned by + * `linearBoundFromGuard`. + */ +private +predicate boundFromGuard( + ComparisonOperation guard, VariableAccess v, + float boundValue, + boolean isLowerBound, RelationStrictness strictness, boolean branch +) { + exists ( + float p, float q, float r, boolean isLB + | linearBoundFromGuard( + guard, v, p, q, r, isLB, strictness, branch) and + boundValue = (r - q)/p + | // If the multiplier is negative then the direction of the comparison + // needs to be flipped. + (p > 0 and isLowerBound = isLB) or + (p < 0 and isLowerBound = isLB.booleanNot())) +} + +/** + * This predicate finds guards of the form `p*v + q < r or p*v + q == r` + * and decomposes them into a tuple of values which can be used to deduce a + * lower or upper bound for `v`. + */ +private +predicate linearBoundFromGuard( + ComparisonOperation guard, VariableAccess v, + float p, float q, float boundValue, + boolean isLowerBound, // Is this a lower or an upper bound? + RelationStrictness strictness, + boolean branch // Which control-flow branch is this bound valid on? +) { + // For the comparison x < RHS, we create two bounds: + // + // 1. x < upperbound(RHS) + // 2. x >= typeLowerBound(RHS.getType().getUnspecifiedType()) + // + exists (Expr lhs, Expr rhs, RelationDirection dir, RelationStrictness st + | linearAccess(lhs, v, p, q) and + relOpWithSwapAndNegate(guard, lhs, rhs, dir, st, branch) + | (isLowerBound = directionIsGreater(dir) and + strictness = st and + getBounds(rhs, boundValue, isLowerBound)) + or + (isLowerBound = directionIsLesser(dir) and + strictness = Nonstrict() and + exprTypeBounds(rhs, boundValue, isLowerBound))) + + // For x == RHS, we create the following bounds: + // + // 1. x <= upperbound(RHS) + // 2. x >= lowerbound(RHS) + // + // For x != RHS, we create trivial bounds: + // + // 1. x <= typeUpperBound(RHS.getType().getUnspecifiedType()) + // 2. x >= typeLowerBound(RHS.getType().getUnspecifiedType()) + // + or + exists (Expr lhs, Expr rhs, boolean isEQ + | linearAccess(lhs, v, p, q) and + eqOpWithSwapAndNegate(guard, lhs, rhs, isEQ, branch) 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))) +} + +/** Utility for `linearBoundFromGuard`. */ +private +predicate getBounds(Expr expr, float boundValue, boolean isLowerBound) { + (isLowerBound = true and boundValue = getFullyConvertedLowerBounds(expr)) or + (isLowerBound = false and boundValue = getFullyConvertedUpperBounds(expr)) +} + +/** Utility for `linearBoundFromGuard`. */ +private +predicate exprTypeBounds(Expr expr, float boundValue, boolean isLowerBound) { + (isLowerBound = true and boundValue = exprMinVal(expr.getFullyConverted())) or + (isLowerBound = false and boundValue = exprMaxVal(expr.getFullyConverted())) +} + +/** + * Gets the lower bound of the expression. + * + * Note: expressions in C/C++ are often implicitly or explicitly cast to a + * different result type. Such casts can cause the value of the expression + * to overflow or to be truncated. This predicate computes the lower bound + * of the expression without including the effect of the casts. To compute + * the lower bound of the expression after all the casts have been applied, + * call `lowerBound` like this: + * + * `lowerBound(expr.getFullyConverted())` + */ +cached +float lowerBound(Expr expr) { + // Combine the lower bounds returned by getTruncatedLowerBounds into a + // single minimum value. + result = min(float lb | lb = getTruncatedLowerBounds(expr) | lb) +} + +/** + * Gets the upper bound of the expression. + * + * Note: expressions in C/C++ are often implicitly or explicitly cast to a + * different result type. Such casts can cause the value of the expression + * to overflow or to be truncated. This predicate computes the upper bound + * of the expression without including the effect of the casts. To compute + * the upper bound of the expression after all the casts have been applied, + * call `upperBound` like this: + * + * `upperBound(expr.getFullyConverted())` + */ +cached +float upperBound(Expr expr) { + // Combine the upper bounds returned by getTruncatedUpperBounds into a + // single maximum value. + result = max(float ub | ub = getTruncatedUpperBounds(expr) | ub) +} + +/** + * Holds if `expr` has a provably empty range. For example: + * + * 10 < expr and expr < 5 + * + * The range of an expression can only be empty if it can never be + * executed. For example: + * + * if (10 < x) { + * if (x < 5) { + * // Unreachable code + * return x; // x has an empty range: 10 < x && x < 5 + * } + * } + */ +cached +predicate exprWithEmptyRange(Expr expr) { + analyzableExpr(expr) and + (not exists(lowerBound(expr)) or + not exists(upperBound(expr)) or + lowerBound(expr) > upperBound(expr)) +} diff --git a/cpp/ql/src/semmle/code/cpp/security/BufferAccess.qll b/cpp/ql/src/semmle/code/cpp/security/BufferAccess.qll new file mode 100644 index 000000000000..997c53fd8d0d --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/security/BufferAccess.qll @@ -0,0 +1,333 @@ +import cpp + +/** + * Returns the size of the pointed-to type, counting void types as size 1. + */ +int getPointedSize(Type t) +{ + result = t.getUnspecifiedType().(PointerType).getBaseType().getSize().maximum(1) +} + +/** + * An operation that reads data from or writes data to a buffer. + * + * See the BufferWrite class for an explanation of how BufferAccess and + * BufferWrite differ. + */ +abstract class BufferAccess extends Expr { + abstract string getName(); + abstract Expr getBuffer(string bufferDesc, int accessType); + /* + * accessType: + * 1 = buffer range [0, getSize) is accessed entirely + * 2 = buffer range [0, getSize) may be accessed partially or entirely + * 3 = buffer is accessed at offset getSize - 1 + */ + abstract int getSize(); +} + +/** + * Calls to memcpy and similar functions. + * memcpy(dest, src, num) + * wmemcpy(dest, src, num) + * memmove(dest, src, num) + * wmemmove(dest, src, num) + * mempcpy(dest, src, num); + * wmempcpy(dest, src, num); + */ +class MemcpyBA extends BufferAccess { + MemcpyBA() { + this.(FunctionCall).getTarget().getName() = "memcpy" or + this.(FunctionCall).getTarget().getName() = "wmemcpy" or + this.(FunctionCall).getTarget().getName() = "memmove" or + this.(FunctionCall).getTarget().getName() = "wmemmove" or + this.(FunctionCall).getTarget().getName() = "mempcpy" or + this.(FunctionCall).getTarget().getName() = "wmempcpy" + } + + override string getName() { + result = this.(FunctionCall).getTarget().getName() + } + + override Expr getBuffer(string bufferDesc, int accessType) { + ( + result = this.(FunctionCall).getArgument(0) and + bufferDesc = "destination buffer" and + accessType = 1 + ) or ( + result = this.(FunctionCall).getArgument(1) and + bufferDesc = "source buffer" and + accessType = 1 + ) + } + + override int getSize() { + result = + this.(FunctionCall).getArgument(2).getValue().toInt() * + getPointedSize(this.(FunctionCall).getTarget().getParameter(0).getType()) + } +} + +/** + * Calls to bcopy. + * bcopy(src, dest, num) + */ +class BCopyBA extends BufferAccess { + BCopyBA() { + this.(FunctionCall).getTarget().getName() = "bcopy" + } + + override string getName() { + result = this.(FunctionCall).getTarget().getName() + } + + override Expr getBuffer(string bufferDesc, int accessType) { + ( + result = this.(FunctionCall).getArgument(0) and + bufferDesc = "source buffer" and + accessType = 1 + ) or ( + result = this.(FunctionCall).getArgument(1) and + bufferDesc = "destination buffer" and + accessType = 1 + ) + } + + override int getSize() { + result = + this.(FunctionCall).getArgument(2).getValue().toInt() * + getPointedSize(this.(FunctionCall).getTarget().getParameter(0).getType()) + } +} + +/** + * Calls to strncpy. + * strncpy(dest, src, num) + */ +class StrncpyBA extends BufferAccess { + StrncpyBA() { + this.(FunctionCall).getTarget().getName() = "strncpy" + } + + override string getName() { + result = this.(FunctionCall).getTarget().getName() + } + + override Expr getBuffer(string bufferDesc, int accessType) { + ( + result = this.(FunctionCall).getArgument(0) and + bufferDesc = "destination buffer" and + accessType = 2 + ) or ( + result = this.(FunctionCall).getArgument(1) and + bufferDesc = "source buffer" and + accessType = 2 + ) + } + + override int getSize() { + result = + this.(FunctionCall).getArgument(2).getValue().toInt() * + getPointedSize(this.(FunctionCall).getTarget().getParameter(0).getType()) + } +} + +/** + * Calls to memccpy. + * memccpy(dest, src, c, n) + */ +class MemccpyBA extends BufferAccess { + MemccpyBA() { + this.(FunctionCall).getTarget().getName() = "memccpy" + } + + override string getName() { + result = this.(FunctionCall).getTarget().getName() + } + + override Expr getBuffer(string bufferDesc, int accessType) { + ( + result = this.(FunctionCall).getArgument(0) and + bufferDesc = "destination buffer" and + accessType = 2 + ) or ( + result = this.(FunctionCall).getArgument(1) and + bufferDesc = "source buffer" and + accessType = 2 + ) + } + + override int getSize() { + result = + this.(FunctionCall).getArgument(3).getValue().toInt() * + getPointedSize(this.(FunctionCall).getTarget().getParameter(0).getType()) + } +} + +/** + * Calls to memcmp and similar functions. + * memcmp(buffer1, buffer2, num) + * wmemcmp(buffer1, buffer2, num) + * _memicmp(buffer1, buffer2, count) + * _memicmp_l(buffer1, buffer2, count, locale) + */ +class MemcmpBA extends BufferAccess { + MemcmpBA() { + this.(FunctionCall).getTarget().getName() = "memcmp" or + this.(FunctionCall).getTarget().getName() = "wmemcmp" or + this.(FunctionCall).getTarget().getName() = "_memicmp" or + this.(FunctionCall).getTarget().getName() = "_memicmp_l" + } + + override string getName() { + result = this.(FunctionCall).getTarget().getName() + } + + override Expr getBuffer(string bufferDesc, int accessType) { + ( + result = this.(FunctionCall).getArgument(0) and + bufferDesc = "first buffer" and + accessType = 2 + ) or ( + result = this.(FunctionCall).getArgument(1) and + bufferDesc = "second buffer" and + accessType = 2 + ) + } + + override int getSize() { + result = + this.(FunctionCall).getArgument(2).getValue().toInt() * + getPointedSize(this.(FunctionCall).getTarget().getParameter(0).getType()) + } +} + +/** + * Calls to swab and similar functions. + * swab(src, dest, num) + * _swab(src, dest, num) + */ +class SwabBA extends BufferAccess { + SwabBA() { + this.(FunctionCall).getTarget().getName() = "swab" or + this.(FunctionCall).getTarget().getName() = "_swab" + } + + override string getName() { + result = this.(FunctionCall).getTarget().getName() + } + + override Expr getBuffer(string bufferDesc, int accessType) { + ( + result = this.(FunctionCall).getArgument(0) and + bufferDesc = "source buffer" and + accessType = 1 + ) or ( + result = this.(FunctionCall).getArgument(1) and + bufferDesc = "destination buffer" and + accessType = 1 + ) + } + + override int getSize() { + result = + this.(FunctionCall).getArgument(2).getValue().toInt() * + getPointedSize(this.(FunctionCall).getTarget().getParameter(0).getType()) + } +} + +/** + * Calls to memset and similar functions. + * memset(dest, value, num) + * wmemset(dest, value, num) + */ +class MemsetBA extends BufferAccess { + MemsetBA() { + this.(FunctionCall).getTarget().getName() = "memset" or + this.(FunctionCall).getTarget().getName() = "wmemset" + } + + override string getName() { + result = this.(FunctionCall).getTarget().getName() + } + + override Expr getBuffer(string bufferDesc, int accessType) { + result = this.(FunctionCall).getArgument(0) and + bufferDesc = "destination buffer" and + accessType = 1 + } + + override int getSize() { + result = + this.(FunctionCall).getArgument(2).getValue().toInt() * + getPointedSize(this.(FunctionCall).getTarget().getParameter(0).getType()) + } +} + +/** + * Calls to memchr and similar functions. + * memchr(buffer, value, num) + * wmemchr(buffer, value, num) + */ +class MemchrBA extends BufferAccess { + MemchrBA() { + this.(FunctionCall).getTarget().getName() = "memchr" or + this.(FunctionCall).getTarget().getName() = "wmemchr" + } + + override string getName() { + result = this.(FunctionCall).getTarget().getName() + } + + override Expr getBuffer(string bufferDesc, int accessType) { + result = this.(FunctionCall).getArgument(0) and + bufferDesc = "source buffer" and + accessType = 2 + } + + override int getSize() { + result = + this.(FunctionCall).getArgument(2).getValue().toInt() * + getPointedSize(this.(FunctionCall).getTarget().getParameter(0).getType()) + } +} + +/** + * A array access on a buffer: + * buffer[ix] + * but not: + * &buffer[ix] + */ +class ArrayExprBA extends BufferAccess { + ArrayExprBA() { + exists(this.(ArrayExpr).getArrayOffset().getValue().toInt()) + and + not exists(AddressOfExpr aoe | aoe.getAChild() = this) + and + + // exclude accesses in macro implementation of `strcmp`, + // which are carefully controlled but can look dangerous. + not exists(Macro m | + m.getName() = "strcmp" and + m.getAnInvocation().getAnExpandedElement() = this + ) + } + + override string getName() { + result = "array indexing" + } + + override Expr getBuffer(string bufferDesc, int accessType) { + result = this.(ArrayExpr).getArrayBase() and + bufferDesc = "array" and + accessType = 3 + } + + override int getSize() { + // byte size of the buffer that would be required to support this + // access + result = + (1 + this.(ArrayExpr).getArrayOffset().getValue().toInt()) * + this.(ArrayExpr).getType().getSize() + } +} diff --git a/cpp/ql/src/semmle/code/cpp/security/BufferWrite.qll b/cpp/ql/src/semmle/code/cpp/security/BufferWrite.qll new file mode 100644 index 000000000000..bed73b5a49e3 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/security/BufferWrite.qll @@ -0,0 +1,550 @@ +/** + * @name CWE-120 + * @description Buffer Copy without Checking Size of Input ('Classic Buffer Overflow'). + * @kind problem + * @problem.severity recommendation + */ +import cpp +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 + +// --- BufferWrite framework --- + +/** + * An operation that writes a variable amount of data to a buffer + * (strcpy, strncat, sprintf etc). + * + * Note that there are two related class frameworks: + * - BufferWrite provides detailed coverage of null-terminated + * buffer write operations. + * - BufferAccess provides general coverage of buffer read and write + * operations whose size is either not data-dependent, or has an upper + * bound which is not data-dependent. + * This design has some overlaps between the two classes, for example + * the write of a 'strncpy'. + */ +abstract class BufferWrite extends Expr +{ + // --- derived classes override these --- + + /** + * Gets the (unspecified) type of the buffer this operation works + * with (for example `char *`). + */ + abstract Type getBufferType(); + + /** + * Gets a data source of this operation (e.g. the source string, + * format string; not necessarily copied as-is). + */ + Expr getASource() { none() } + + /** + * Gets the destination buffer of this operation. + */ + abstract Expr getDest(); + + /** + * Holds if the operation has an explicit parameter that limits the amount + * of data written (e.g. `strncpy` does, whereas `strcpy` does not); this + * is not the same as exists(getExplicitLimit()) because the limit may exist + * though it's value is unknown. + */ + predicate hasExplicitLimit() { none() } + + /** + * Gets the explicit limit of bytes copied by this operation, if it exists + * and it's value can be determined. + */ + int getExplicitLimit() { none() } + + /** + * Gets an upper bound to the amount of data that's being written (if one + * can be found). + */ + int getMaxData() { none() } + + /** + * Gets an upper bound to the amount of data that's being written (if one + * can be found), except that float to string conversions are assumed to be + * much smaller (8 bytes) than their true maximum length. This can be + * helpful in determining the cause of a buffer overflow issue. + */ + int getMaxDataLimited() { result = getMaxData() } + + /** + * Gets the size of a single character of the type this + * operation works with, in bytes. + */ + int getCharSize() + { + result = getBufferType().(PointerType).getBaseType().getSize() or + result = getBufferType().(ArrayType).getBaseType().getSize() + } + + /** + * Gets a description of this buffer write. + */ + string getBWDesc() + { + result = toString() + } +} + +/** + * A `BufferWrite` that is also a `FunctionCall` (most cases). + */ +abstract class BufferWriteCall extends BufferWrite, FunctionCall +{ +} + +// --- BufferWrite classes --- + +/** + * A call to a variant of `strcpy`. + */ +class StrCopyBW extends BufferWriteCall +{ + StrCopyBW() + { + exists(TopLevelFunction fn, string name | (fn = getTarget()) and (name = fn.getName()) and ( + (name = "strcpy") // strcpy(dst, src) + or (name = "wcscpy") // wcscpy(dst, src) + or (name = "_mbscpy") // _mbscpy(dst, src) + 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 + fn.getNumberOfParameters() = 3 // exclude the 2-parameter template versions + // that find the size of a fixed size destination buffer. + ) + or (name = "strncpy") // strncpy(dst, src, max_amount) + or (name = "strncpy_l") // strncpy_l(dst, src, max_amount, locale) + or (name = "wcsncpy") // wcsncpy(dst, src, max_amount) + or (name = "_wcsncpy_l") // _wcsncpy_l(dst, src, max_amount, locale) + or (name = "_mbsncpy") // _mbsncpy(dst, src, max_amount) + or (name = "_mbsncpy_l") // _mbsncpy_l(dst, src, max_amount, locale) + )) + } + + 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() + ) + )) + } + + 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 + ) + )) + } + + override Type getBufferType() + { + result = this.getTarget().getParameter(getParamSrc()).getType().getUnspecifiedType() + } + + override Expr getASource() + { + result = getArgument(getParamSrc()) + } + + override Expr getDest() + { + result = getArgument(0) + } + + override predicate hasExplicitLimit() + { + exists(getParamSize()) + } + + override int getExplicitLimit() + { + result = getArgument(getParamSize()).getValue().toInt() * getCharSize() + } + + override int getMaxData() + { + result = getArgument(getParamSrc()).(AnalysedString).getMaxLength() * getCharSize() + } +} + +/** + * A call to a variant of `strcat`. + */ +class StrCatBW extends BufferWriteCall +{ + StrCatBW() + { + exists(TopLevelFunction fn | fn = getTarget() and fn instanceof StrcatFunction) + } + + int getParamSize() + { + if exists(getArgument(2)) then ( + result = 2 + ) else ( + none() + ) + } + + int getParamSrc() + { + result = 1 + } + + override Type getBufferType() + { + result = this.getTarget().getParameter(getParamSrc()).getType().getUnspecifiedType() + } + + override Expr getASource() + { + result = getArgument(getParamSrc()) + } + + override Expr getDest() + { + result = getArgument(0) + } + + override predicate hasExplicitLimit() + { + exists(getParamSize()) + } + + override int getExplicitLimit() + { + result = getArgument(getParamSize()).getValue().toInt() * getCharSize() + } + + override int getMaxData() + { + result = getArgument(getParamSrc()).(AnalysedString).getMaxLength() * getCharSize() + } +} + +/** + * A call to a variant of `sprintf`. + */ +class SprintfBW extends BufferWriteCall +{ + SprintfBW() + { + exists(TopLevelFunction fn, string name | (fn = getTarget()) and (name = fn.getName()) and ( + // C sprintf variants + (name = "sprintf") // sprintf(dst, format, args...) + or (name = "vsprintf") // vsprintf(dst, format, va_list) + or (name = "wsprintf") // wsprintf(dst, format, args...) + or (name = "vwsprintf") // vwsprintf(dst, format, va_list) + + // Microsoft sprintf variants + or (name.regexpMatch("_sprintf_l")) // _sprintf_l(dst, format, locale, args...) + or (name.regexpMatch("_vsprintf_l")) // _vsprintf_l(dst, format, locale, va_list)) + or (name.regexpMatch("__swprintf_l")) // __swprintf_l(dst, format, locale, args...) + or (name.regexpMatch("__vswprintf_l")) // __vswprintf_l(dst, format, locale, va_list) + )) + } + + override Type getBufferType() + { + exists(FormattingFunction f | + f = this.getTarget() and + result = f.getParameter(f.getFormatParameterIndex()).getType().getUnspecifiedType() + ) + } + + override Expr getASource() + { + (result = this.(FormattingFunctionCall).getFormat()) + or + (result = this.(FormattingFunctionCall).getFormatArgument(_)) + } + + override Expr getDest() + { + result = getArgument(0) + } + + override int getMaxData() + { + exists(FormatLiteral fl | + (fl = this.(FormattingFunctionCall).getFormat()) + and (result = fl.getMaxConvertedLength() * getCharSize()) + ) + } + + override int getMaxDataLimited() + { + exists(FormatLiteral fl | + (fl = this.(FormattingFunctionCall).getFormat()) + and (result = fl.getMaxConvertedLengthLimited() * getCharSize()) + ) + } +} + +/** + * A call to a variant of `snprintf`. + */ +class SnprintfBW extends BufferWriteCall +{ + SnprintfBW() + { + exists(TopLevelFunction fn, string name | (fn = getTarget()) and (name = fn.getName()) and ( + // C snprintf variants + (name = "snprintf") // snprintf(dst, max_amount, format, args...) + or (name = "vsnprintf") // vsnprintf(dst, max_amount, format, va_list) + or (name = "swprintf") // swprintf(dst, max_amount, format, args...) + or (name = "vswprintf") // vswprintf(dst, max_amount, format, va_list) + + // Microsoft snprintf variants + or (name = "sprintf_s") // sprintf_s(dst, max_amount, format, locale, args...) + or (name = "vsprintf_s") // vsprintf_s(dst, max_amount, format, va_list) + or (name = "swprintf_s") // swprintf_s(dst, max_amount, format, args...) + or (name = "vswprintf_s") // vswprintf_s(dst, max_amount, format, va_list) + + // Microsoft snprintf variants with '_' + or ( + (name.regexpMatch("_v?sn?w?printf(_s)?(_p)?(_l)?")) + and (not this instanceof SprintfBW) + ) + // _sprintf_s_l(dst, max_amount, format, locale, args...) + // _swprintf_l(dst, max_amount, format, locale, args...) + // _swprintf_s_l(dst, max_amount, format, locale, args...) + // _snprintf(dst, max_amount, format, args...) + // _snprintf_l(dst, max_amount, format, locale, args...) + // _snwprintf(dst, max_amount, format, args...) + // _snwprintf_l(buffer, max_amount, format, locale, args...) + // _vsprintf_s_l(dst, max_amount, format, locale, va_list) + // _vsprintf_p(dst, max_amount, format, va_list) + // _vsprintf_p_l(dst, max_amount, format, locale, va_list) + // _vswprintf_l(dst, max_amount, format, locale, va_list) + // _vswprintf_s_l(buffer, max_amount, format, locale, va_list) + // _vswprintf_p(dst, max_amount, format, va_list) + // _vswprintf_p_l(dst, max_amount, format, locale, va_list) + // _vsnprintf(dst, max_amount, format, va_list) + // _vsnprintf_l(dst, max_amount, format, locale, va_list) + // _vsnwprintf(dst, max_amount, format, va_list) + // _vsnwprintf_l(dst, max_amount, format, locale, va_list) + )) + } + + int getParamSize() + { + result = 1 + } + + override Type getBufferType() + { + exists(FormattingFunction f | + f = this.getTarget() and + result = f.getParameter(f.getFormatParameterIndex()).getType().getUnspecifiedType() + ) + } + + override Expr getASource() + { + (result = this.(FormattingFunctionCall).getFormat()) + or + (result = this.(FormattingFunctionCall).getFormatArgument(_)) + } + + override Expr getDest() + { + result = getArgument(0) + } + + override predicate hasExplicitLimit() + { + exists(getParamSize()) + } + + override int getExplicitLimit() + { + result = getArgument(getParamSize()).getValue().toInt() * getCharSize() + } + + override int getMaxData() + { + exists(FormatLiteral fl | + (fl = this.(FormattingFunctionCall).getFormat()) + and (result = fl.getMaxConvertedLength() * getCharSize()) + ) + } + + override int getMaxDataLimited() + { + exists(FormatLiteral fl | + (fl = this.(FormattingFunctionCall).getFormat()) + and (result = fl.getMaxConvertedLengthLimited() * getCharSize()) + ) + } +} + +/** + * A call to a variant of `gets`. + */ +class GetsBW extends BufferWriteCall +{ + GetsBW() + { + exists(TopLevelFunction fn, string name | (fn = getTarget()) and (name = fn.getName()) and ( + (name = "gets") // gets(dst) + or (name = "fgets") // fgets(dst, max_amount, src_stream) + or (name = "fgetws") // fgetws(dst, max_amount, src_stream) + )) + } + + int getParamSize() + { + if exists(getArgument(1)) then ( + result = 1 + ) else ( + none() + ) + } + + override Type getBufferType() + { + result = this.getTarget().getParameter(0).getType().getUnspecifiedType() + } + + override Expr getASource() + { + if exists(getArgument(2)) then ( + result = getArgument(2) + ) else ( + result = this // the source is input inside the 'gets' call itself + ) + } + + override Expr getDest() + { + result = getArgument(0) + } + + override predicate hasExplicitLimit() + { + exists(getParamSize()) + } + + override int getExplicitLimit() + { + result = getArgument(getParamSize()).getValue().toInt() * getCharSize() + } +} + +/** + * A string that is written by a `scanf`-like function. + */ +class ScanfBW extends BufferWrite +{ + ScanfBW() + { + exists(ScanfFunctionCall fc, ScanfFormatLiteral fl, int arg, int args_pos | + (this = fc.getArgument(arg)) + and (args_pos = fc.getTarget().getNumberOfParameters()) + and (arg >= args_pos) + and (fl = fc.getFormat()) + and (fl.getConversionChar(arg - args_pos) = "s") + ) + } + + int getParamArgs() + { + exists(FunctionCall fc | this = fc.getArgument(_) + and (result = fc.getTarget().getNumberOfParameters()) + ) + } + + override Type getBufferType() + { + exists(ScanfFunction f, ScanfFunctionCall fc | + this = fc.getArgument(_) and + f = fc.getTarget() and + result = f.getParameter(f.getFormatParameterIndex()).getType().getUnspecifiedType() + ) + } + + override Expr getASource() + { + exists(ScanfFunctionCall fc | + (this = fc.getArgument(_)) and ( + // inputs are: the format string, input or the argument itself (if there's no explicit input) + (result = fc.getFormat()) or + (result = fc.getArgument(fc.getInputParameterIndex())) or + (not exists(fc.getInputParameterIndex()) and (result = this)) + ) + ) + } + + override Expr getDest() + { + result = this + } + + override int getMaxData() + { + exists(ScanfFunctionCall fc, ScanfFormatLiteral fl, int arg | + (this = fc.getArgument(arg)) + and (fl = fc.getFormat()) + and (result = (fl.getMaxConvertedLength(arg - getParamArgs()) + 1) * getCharSize()) // +1 is for the terminating null + ) + } + + override string getBWDesc() + { + exists(FunctionCall fc | (this = fc.getArgument(_)) + and (result = fc.getTarget().getName() + " string argument") + ) + } +} + +/** + * A detected definition of PATH_MAX + */ +private int path_max() { + result = max(Macro macro | + macro.getName() = "PATH_MAX" + | macro.getBody().toInt()) +} + +/** + * A call to `realpath`. + */ +class RealpathBW extends BufferWriteCall { + RealpathBW() { + exists(path_max()) and // Ignore realpath() calls if PATH_MAX cannot be determined + getTarget().getQualifiedName() = "realpath" // realpath(path, resolved_path); + } + + override Type getBufferType() + { + result = this.getTarget().getParameter(0).getType().getUnspecifiedType() + } + + override Expr getDest() { result = getArgument(1) } + override Expr getASource() { result = getArgument(0) } + + override int getMaxData() { + result = path_max() + and this = this // Suppress a compiler warning + } +} diff --git a/cpp/ql/src/semmle/code/cpp/security/CommandExecution.qll b/cpp/ql/src/semmle/code/cpp/security/CommandExecution.qll new file mode 100644 index 000000000000..2b4eee59267a --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/security/CommandExecution.qll @@ -0,0 +1,220 @@ +/* Definitions related to execution of commands */ + +import cpp + +import semmle.code.cpp.security.FunctionWithWrappers + +/** + * A function for running a command using a command interpreter. + */ +class SystemFunction extends FunctionWithWrappers { + SystemFunction() { + hasQualifiedName("system") + or hasQualifiedName("popen") + + // Windows variants + or hasQualifiedName("_popen") + or hasQualifiedName("_wpopen") + or hasQualifiedName("_wsystem") + } + + override predicate interestingArg(int arg) { + arg = 0 + } +} + + +/** + * A function for running a command via varargs. Note that, at the time + * of writing, FunctionWithWrappers doesn't really support varargs + * arguments, because it requires a finite version of interestingArg(). + */ +class VarargsExecFunctionCall extends FunctionCall { + VarargsExecFunctionCall() { + getTarget().hasQualifiedName("execl") + or getTarget().hasQualifiedName("execle") + or getTarget().hasQualifiedName("execlp") + + // Windows + or getTarget().hasQualifiedName("_execl") + or getTarget().hasQualifiedName("_execle") + or getTarget().hasQualifiedName("_execlp") + or getTarget().hasQualifiedName("_execlpe") + or getTarget().hasQualifiedName("_spawnl") + or getTarget().hasQualifiedName("_spawnle") + or getTarget().hasQualifiedName("_spawnlp") + or getTarget().hasQualifiedName("_spawnlpe") + or getTarget().hasQualifiedName("_wexecl") + or getTarget().hasQualifiedName("_wexecle") + or getTarget().hasQualifiedName("_wexeclp") + or getTarget().hasQualifiedName("_wexeclpe") + or getTarget().hasQualifiedName("_wspawnl") + or getTarget().hasQualifiedName("_wspawnle") + or getTarget().hasQualifiedName("_wspawnlp") + or getTarget().hasQualifiedName("_wspawnlpe") + } + + /** Whether the last argument to the function is an environment pointer */ + predicate hasEnvironmentArgument() { + getTarget().hasQualifiedName("execle") + or getTarget().hasQualifiedName("_execle") + or getTarget().hasQualifiedName("_execlpe") + or getTarget().hasQualifiedName("_wexecle") + or getTarget().hasQualifiedName("_wexeclpe") + } + + /** The arguments passed to the command. The 0th such argument is conventionally + * the name of the command. */ + Expr getCommandArgument(int idx) { + exists (int underlyingIdx | + result = getArgument(underlyingIdx) + and underlyingIdx > getCommandIdx() + and ( + underlyingIdx < getNumberOfArguments() - 1 + or not hasEnvironmentArgument() + ) + and idx = underlyingIdx - getCommandIdx() - 1) + } + + /** The expression denoting the program to execute */ + Expr getCommand() { + result = getArgument(getCommandIdx()) + } + + /** The index of the command. The spawn variants start with a mode, whereas + * all the other ones start with the command. */ + private int getCommandIdx() { + if ( + getTarget().getQualifiedName().matches("\\_spawn%") + or getTarget().getQualifiedName().matches("\\_wspawn%")) + then result = 1 + else result = 0 + } +} + + +/** + * A function for running a command using an array of arguments. Note that + * FunctionWithWrappers does not support tracking multiple interesting + * arguments all the way to the call site. + */ +class ArrayExecFunctionCall extends FunctionCall { + ArrayExecFunctionCall() { + getTarget().hasQualifiedName("execv") + or getTarget().hasQualifiedName("execvp") + or getTarget().hasQualifiedName("execvpe") + + // Windows variants + or getTarget().hasQualifiedName("_execv") + or getTarget().hasQualifiedName("_execve") + or getTarget().hasQualifiedName("_execvp") + or getTarget().hasQualifiedName("_execvpe") + or getTarget().hasQualifiedName("_spawnv") + or getTarget().hasQualifiedName("_spawnve") + or getTarget().hasQualifiedName("_spawnvp") + or getTarget().hasQualifiedName("_spawnvpe") + or getTarget().hasQualifiedName("_wexecv") + or getTarget().hasQualifiedName("_wexecve") + or getTarget().hasQualifiedName("_wexecvp") + or getTarget().hasQualifiedName("_wexecvpe") + or getTarget().hasQualifiedName("_wspawnv") + or getTarget().hasQualifiedName("_wspawnve") + or getTarget().hasQualifiedName("_wspawnvp") + or getTarget().hasQualifiedName("_wspawnvpe") + } + + /** The argument with the array of command arguments */ + Expr getArrayArgument() { + result = getArgument(getCommandIdx() + 1) + } + + /** The expression denoting the program to execute */ + Expr getCommand() { + result = getArgument(getCommandIdx()) + } + + /** The index of the command. The spawn variants start with a mode, whereas + * all the other ones start with the command. */ + private int getCommandIdx() { + if ( + getTarget().getQualifiedName().matches("\\_spawn%") + or getTarget().getQualifiedName().matches("\\_wspawn%")) + then result = 1 + else result = 0 + } +} + + +/** The name of a shell and the flag used to preface a command that should be parsed. Public + * for testing purposes. */ +predicate shellCommandPreface(string cmd, string flag) { + ( + (cmd = "sh" or cmd = "/bin/sh" or cmd = "bash" or cmd = "/bin/bash") + and (flag = "-c") + ) or ( + (cmd = "cmd" or cmd = "cmd.exe" or cmd = "CMD" or cmd = "CMD.EXE" + or cmd = "%WINDIR%\\system32\\cmd.exe" // used in Juliet tests + ) + and (flag = "/c" or flag = "/C") + ) +} + +/** + * An array element. This supports multiple kinds of array syntax. + */ +private predicate arrayElement(Expr arrayLit, int idx, Expr element) { + exists (ArrayLiteral lit | lit = arrayLit | + lit.getElement(idx) = element) + or exists (MessageExpr arrayWithObjects | arrayWithObjects = arrayLit | + arrayWithObjects.getStaticTarget().getQualifiedName().matches("NSArray%::+arrayWithObjects:") and + arrayWithObjects.getArgument(idx) = element) +} + +/** + * A command that is used as a command, or component of a command, + * that will be executed by a general-purpose command interpreter + * such as sh or cmd.exe. + */ +predicate shellCommand(Expr command, string callChain) { + // A call to a function like system() + exists (SystemFunction systemFunction | + systemFunction.outermostWrapperFunctionCall(command, callChain)) + + // A call to a function like execl(), passing "sh", then "-c", and then a command. + or exists (VarargsExecFunctionCall execCall, StringLiteral commandInterpreter, StringLiteral flag, int commandIdx | + callChain = execCall.getTarget().getName() + and execCall.getCommand() = commandInterpreter + and execCall.getCommandArgument(1) = flag + and execCall.getCommandArgument(commandIdx) = command + and commandIdx > 1 + and shellCommandPreface(commandInterpreter.getValue(), flag.getValue())) + + // A call to a function like execv(), where the array being passed is + // initialized to an array literal + or exists( + ArrayExecFunctionCall execCall, StringLiteral commandInterpreter, Variable arrayVariable, + AggregateLiteral arrayInitializer, StringLiteral flag, int idx + | + callChain = execCall.getTarget().getName() + and execCall.getCommand() = commandInterpreter + and execCall.getArrayArgument() = arrayVariable.getAnAccess() + and arrayVariable.getInitializer().getExpr() = arrayInitializer + and arrayInitializer.getChild(1) = flag + and arrayInitializer.getChild(idx) = command + and shellCommandPreface(commandInterpreter.getValue(), flag.getValue()) + and idx > 1) + + // Creation of NSTask + or exists( + MessageExpr launchedTaskCall, TextLiteral commandInterpreter, + Expr arrayLiteral, TextLiteral flag + | + launchedTaskCall.getStaticTarget().getQualifiedName().matches("NSTask%::+launchedTaskWithLaunchPath:arguments:") + and commandInterpreter = launchedTaskCall.getArgument(0) + and arrayLiteral = launchedTaskCall.getArgument(1) + and arrayElement(arrayLiteral, 0, flag) + and arrayElement(arrayLiteral, 1, command) + and shellCommandPreface(commandInterpreter.getValue(), flag.getValue()) + and callChain = "NSTask") +} + diff --git a/cpp/ql/src/semmle/code/cpp/security/Encryption.qll b/cpp/ql/src/semmle/code/cpp/security/Encryption.qll new file mode 100644 index 000000000000..577a5c140b66 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/security/Encryption.qll @@ -0,0 +1,57 @@ +// Common predicates relating to encryption in C and C++ + +import cpp + +/** A blacklist of algorithms that are known to be insecure */ +string algorithmBlacklist() { + result = "DES" or + result = "RC2" or + result = "RC4" or + result = "RC5" or + 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() { + result = "SHA1" or + result = "MD5" +} + +/** A regex for matching strings that look like they contain a blacklisted algorithm */ +string algorithmBlacklistRegex() { + // 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])" + algorithmBlacklist() + "([^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 + or result = "(^|.*[A-Z]{2}|.*[^a-zA-Z])" + algorithmBlacklist().toLowerCase() + "([^a-z].*|$)" +} + +/** A whitelist of algorithms that are known to be secure */ +string algorithmWhitelist() { + result = "RSA" or + result = "SHA256" or + result = "CCM" or + result = "GCM" or + result = "AES" or + result = "Blowfish" or + 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].*|$)" + // 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 + or result = "(^|.*[A-Z]{2}|.*[^a-zA-Z])" + algorithmWhitelist().toLowerCase() + "([^a-z].*|$)" +} diff --git a/cpp/ql/src/semmle/code/cpp/security/FileWrite.qll b/cpp/ql/src/semmle/code/cpp/security/FileWrite.qll new file mode 100644 index 000000000000..85ca02aee563 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/security/FileWrite.qll @@ -0,0 +1,184 @@ +import cpp + +/** + * A function call that writes to a file + */ +class FileWrite extends Expr { + FileWrite() { + fileWrite(this, _ ,_) + } + + Expr getASource() { + fileWrite(this, result, _) + } + + Expr getDest() { + fileWrite(this, _, result) + } +} + +/** + * A `std::basic_ostream` class, or something that can be used + * as one. + */ +class BasicOStreamClass extends Type { + BasicOStreamClass() { + ( + this.(Class).getName().matches("basic\\_ostream%") + ) or ( + this.getUnspecifiedType() instanceof BasicOStreamClass + ) or ( + this.(Class).getABaseClass() instanceof BasicOStreamClass + ) or ( + this.(ReferenceType).getBaseType() instanceof BasicOStreamClass + ) + } +} + +/** + * A call to a member of `std::basic_ostream`, or something related, + * or a call with one of those objects as the first parameter. + */ +class BasicOStreamCall extends FunctionCall { + BasicOStreamCall() { + if (getTarget() instanceof MemberFunction) then ( + getQualifier().getType() instanceof BasicOStreamClass + ) else ( + getArgument(0).getType() instanceof BasicOStreamClass + ) + } +} + +/** + * Output by a function that can be chained, such as `operator<<`. + */ +abstract class ChainedOutputCall extends BasicOStreamCall { + /** + * The source expression of this output. + */ + abstract Expr getSource(); + + /** + * The immediate destination expression of this output. + */ + abstract Expr getDest(); + + /** + * The destination at the far left-hand end of the output chain. + */ + Expr getEndDest() + { + ( + // recurse into the destination + result = getDest().(ChainedOutputCall).getEndDest() + ) or ( + // or return something other than a ChainedOutputCall + result = getDest() and + not result instanceof ChainedOutputCall + ) + } +} + +/** + * A call to `operator<<` on an output stream. + */ +class OperatorLShiftCall extends ChainedOutputCall { + OperatorLShiftCall() { + getTarget().(Operator).hasName("operator<<") + } + + override Expr getSource() { + if (getTarget() instanceof MemberFunction) then ( + result = getArgument(0) + ) else ( + result = getArgument(1) + ) + } + + override Expr getDest() { + if (getTarget() instanceof MemberFunction) then ( + result = getQualifier() + ) else ( + result = getArgument(0) + ) + } +} + +/** + * A call to 'put'. + */ +class PutFunctionCall extends ChainedOutputCall { + PutFunctionCall() { + getTarget().(MemberFunction).hasName("put") + } + + override Expr getSource() { + result = getArgument(0) + } + + override Expr getDest() { + result = getQualifier() + } +} + +/** + * A call to 'write'. + */ +class WriteFunctionCall extends ChainedOutputCall { + WriteFunctionCall() { + getTarget().(MemberFunction).hasName("write") + } + + override Expr getSource() { + result = getArgument(0) + } + + override Expr getDest() { + result = getQualifier() + } +} + +/** + * Whether the function call is a call to << 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 + ) +} + +/** + * Whether the function call is a write to file 'dest' from 'source'. + */ +private predicate fileWrite(Call write, Expr source, Expr dest) { + exists(Function f, int s, int d | f = write.getTarget() and source = write.getArgument(s) and dest = write.getArgument(d) | + exists(string name | name = f.getQualifiedName() | + // 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 s = 0 and d = 1 or + name.matches("NSFileManager%::-createFileAtPath:contents:attributes:") and s = 1 and d = 0 or + ( + // methods that write into the receiver + dest = write.getQualifier() and + source = write.getArgument(0) and + name.matches("NSFileHandle%::-writeData:") + ) + ) or ( + // fprintf + s >= f.(Fprintf).getFormatParameterIndex() and + d = f.(Fprintf).getOutputParameterIndex() + ) + ) or + // file stream using '<<', 'put' or 'write' + fileStreamChain(write, source, dest) +} diff --git a/cpp/ql/src/semmle/code/cpp/security/FunctionWithWrappers.qll b/cpp/ql/src/semmle/code/cpp/security/FunctionWithWrappers.qll new file mode 100644 index 000000000000..b541658a2b42 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/security/FunctionWithWrappers.qll @@ -0,0 +1,161 @@ +import cpp +import PrintfLike +private import TaintTracking + +private +string toCause(Function func, int index) +{ + result = func.getQualifiedName() + "(" + func.getParameter(index).getName() + ")" +} + +/** + * Whether the parameter at index 'sourceParamIndex' of function 'source' is passed + * (without any evident changes) to the parameter at index 'targetParamIndex' of function 'target'. + */ +private +predicate wrapperFunctionStep(Function source, int sourceParamIndex, Function target, int targetParamIndex) +{ + not target.isVirtual() and + not source.isVirtual() and + source.isDefined() and + + exists(Call call, Expr arg, Parameter sourceParam | + // there is a 'call' to 'target' with argument 'arg' at index 'targetParamIndex' + target = resolveCall(call) and + arg = call.getArgument(targetParamIndex) and + + // 'call' is enclosed in 'source' + source = call.getEnclosingFunction() and + + // 'arg' is an access to the parameter at index 'sourceParamIndex' of function 'source' + sourceParam = source.getParameter(sourceParamIndex) and + not exists(sourceParam.getAnAssignedValue()) and + arg = sourceParam.getAnAccess() + ) +} + +/** + * An abstract class for representing functions that may have wrapper functions. + * Wrapper functions propagate an argument (without any evident changes) to this function + * through one or more steps in a call chain. + * + * The design motivation is to report a violation at the location of the argument + * in a call to the wrapper function rather than the function being wrapped, since + * that is usually the more appropriate place to fix the violation. + * + * Subclasses should override the characteristic predicate and 'interestingArg'. + */ +abstract class FunctionWithWrappers extends Function { + + /** + * Which argument indices are relevant for wrapper function detection. + */ + predicate interestingArg(int arg) { + none() + } + + /** + * Whether 'func' is a (possibly nested) wrapper function that feeds a parameter at the given index + * through to an interesting parameter of 'this' function at the given call chain 'depth'. + * The call chain depth is limited to 4. + */ + private + predicate wrapperFunctionLimitedDepth(Function func, int paramIndex, string callChain, int depth) + { + // base case + ( + func = this and + interestingArg(paramIndex) and + callChain = toCause(func, paramIndex) and + depth = 0 + ) + // recursive step + or + exists(Function target, int targetParamIndex, string targetCause, int targetDepth | + this.wrapperFunctionLimitedDepth(target, targetParamIndex, targetCause, targetDepth) + and targetDepth < 4 + and wrapperFunctionStep(func, paramIndex, target, targetParamIndex) + and callChain = toCause(func, paramIndex) + ", which calls " + targetCause + and depth = targetDepth + 1 + ) + } + + /** + * Whether 'func' is a (possibly nested) wrapper function that feeds a parameter at the given index + * through to an interesting parameter of 'this' function. + * + * The 'cause' gives the name of 'this' interesting function and its relevant parameter + * at the end of the call chain. + */ + private + predicate wrapperFunctionAnyDepth(Function func, int paramIndex, string cause) + { + // base case + ( + func = this and + interestingArg(paramIndex) and + cause = toCause(func, paramIndex) + ) + // recursive step + or + exists(Function target, int targetParamIndex | + this.wrapperFunctionAnyDepth(target, targetParamIndex, cause) + and wrapperFunctionStep(func, paramIndex, target, targetParamIndex) + ) + } + + /** + * Whether 'func' is a (possibly nested) wrapper function that feeds a parameter at the given index + * through to an interesting parameter of 'this' function. + * + * If there exists a call chain with depth at most 4, the 'cause' reports the smallest call chain. + * Otherwise, the 'cause' merely reports the name of 'this' interesting function and its relevant + * parameter at the end of the call chain. + * + * If there is more than one possible 'cause', a unique one is picked (by lexicographic order). + */ + predicate wrapperFunction(Function func, int paramIndex, string cause) + { + ( + cause = min(string callChain, int depth | + this.wrapperFunctionLimitedDepth(func, paramIndex, callChain, depth) and + depth = min(int d | this.wrapperFunctionLimitedDepth(func, paramIndex, _, d) | d) + | callChain + ) + ) + or + ( + not this.wrapperFunctionLimitedDepth(func, paramIndex, _, _) and + cause = min(string targetCause, string possibleCause | + this.wrapperFunctionAnyDepth(func, paramIndex, targetCause) and + possibleCause = toCause(func, paramIndex) + ", which ends up calling " + targetCause + | possibleCause + ) + ) + } + + /** + * Whether 'arg' is an argument in a call to an outermost wrapper function of 'this' function. + */ + predicate outermostWrapperFunctionCall(Expr arg, string callChain) + { + exists(Function func, Call call, int argIndex | + func = resolveCall(call) + and this.wrapperFunction(func, argIndex, callChain) + and not wrapperFunctionStep(call.getEnclosingFunction(), _, func, argIndex) + and arg = call.getArgument(argIndex) + ) + } + +} + + +class PrintfLikeFunction extends FunctionWithWrappers { + PrintfLikeFunction() { + printfLikeFunction(this, _) + } + + override predicate interestingArg(int arg) { + printfLikeFunction(this, arg) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/security/OutputWrite.qll b/cpp/ql/src/semmle/code/cpp/security/OutputWrite.qll new file mode 100644 index 000000000000..f1a98c62be62 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/security/OutputWrite.qll @@ -0,0 +1,88 @@ +import cpp +import FileWrite + +/** + * A function call that writes to standard output or standard error + */ +class OutputWrite extends Expr { + OutputWrite() { + outputWrite(this, _) + } + + Expr getASource() { + outputWrite(this, result) + } +} + +/** + * A standard output or standard error variable. + */ +private predicate outputVariable(Variable v) { + // standard output + v.hasName("cout") or + v.hasName("wcout") or + + // standard error + v.hasName("cerr") or + v.hasName("clog") or + v.hasName("wcerr") or + v.hasName("wclog") +} + +/** + * An expr representing standard output or standard error. + */ +private predicate outputExpr(ChainedOutputCall out) { + // output chain ending in an access to standard output / standard error + outputVariable(out.getEndDest().(VariableAccess).getTarget()) +} + +/** + * A file representing standard output or standard error. + */ +private predicate outputFile(Expr e) { + exists(string name | + ( + name = e.(VariableAccess).getTarget().(GlobalVariable).toString() or + name = e.findRootCause().(Macro).getName() + ) and ( + name = "stdout" or + name = "stderr" + ) + ) +} + +/** + * is the function call a write to standard output or standard error from 'source' + */ +private predicate outputWrite(Expr write, Expr source) { + exists(Function f, int arg | f = write.(Call).getTarget() and source = write.(Call).getArgument(arg) | + ( + // printf + arg >= f.(Printf).getFormatParameterIndex() + ) or ( + // syslog + arg >= f.(Syslog).getFormatParameterIndex() + ) or ( + // puts, putchar + ( + f.getQualifiedName() = "puts" or + f.getQualifiedName() = "putchar" + ) and arg = 0 + ) or exists(Call wrappedCall, Expr wrappedSource | + // wrapped output call (recursive case) + outputWrite(wrappedCall, wrappedSource) and + wrappedCall.getEnclosingFunction() = f and + parameterUsePair(f.getParameter(arg), wrappedSource) + ) + ) or ( + // output to standard output / standard error using operator<<, put or write + outputExpr(write) and + source = write.(ChainedOutputCall).getSource() + ) or exists(FileWrite fileWrite | + // output to stdout, stderr as a file (using FileWrite.qll logic) + write = fileWrite and + outputFile(fileWrite.getDest()) and + source = fileWrite.getASource() + ) +} diff --git a/cpp/ql/src/semmle/code/cpp/security/Overflow.qll b/cpp/ql/src/semmle/code/cpp/security/Overflow.qll new file mode 100644 index 000000000000..72a9126d77fb --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/security/Overflow.qll @@ -0,0 +1,83 @@ +import cpp +import semmle.code.cpp.controlflow.Dominance + +/* Guarding */ + +/** is the size of this use guarded using 'abs'? */ +predicate guardedAbs(BinaryArithmeticOperation e, Expr use) { + exists(FunctionCall fc | + fc.getTarget().getName() = "abs" | + fc.getArgument(0).getAChild*() = use + and guardedLesser(e, fc) + ) +} + +/** is the size of this use guarded to be less than something? */ +predicate guardedLesser(BinaryArithmeticOperation e, Expr use) { + exists(IfStmt c, RelationalOperation guard | + use = guard.getLesserOperand().getAChild*() and + guard = c.getControllingExpr().getAChild*() and + iDominates*(c.getThen(), e.getEnclosingStmt()) + ) + or exists(Loop c, RelationalOperation guard | + use = guard.getLesserOperand().getAChild*() and + guard = c.getControllingExpr().getAChild*() and + iDominates*(c.getStmt(), e.getEnclosingStmt()) + ) + or exists(ConditionalExpr c, RelationalOperation guard | + use = guard.getLesserOperand().getAChild*() and + guard = c.getCondition().getAChild*() and + c.getThen().getAChild*() = e + ) + or guardedAbs(e, use) +} + +/** is the size of this use guarded to be greater than something? */ +predicate guardedGreater(BinaryArithmeticOperation e, Expr use) { + exists(IfStmt c, RelationalOperation guard | + use = guard.getGreaterOperand().getAChild*() and + guard = c.getControllingExpr().getAChild*() and + iDominates*(c.getThen(), e.getEnclosingStmt()) + ) + or exists(Loop c, RelationalOperation guard | + use = guard.getGreaterOperand().getAChild*() and + guard = c.getControllingExpr().getAChild*() and + iDominates*(c.getStmt(), e.getEnclosingStmt()) + ) + or exists(ConditionalExpr c, RelationalOperation guard | + use = guard.getGreaterOperand().getAChild*() and + guard = c.getCondition().getAChild*() and + c.getThen().getAChild*() = e + ) + or guardedAbs(e, use) +} + +/** a use of a given variable */ +VariableAccess varUse(LocalScopeVariable v) { + result = v.getAnAccess() +} + +/** is e not guarded against overflow by use? */ +predicate missingGuardAgainstOverflow(BinaryArithmeticOperation e, VariableAccess use) { + use = e.getAnOperand() and + exists(LocalScopeVariable v | use.getTarget() = v | + // overflow possible if large + (e instanceof AddExpr and not guardedLesser(e, varUse(v))) or + // overflow possible if large or small + (e instanceof MulExpr and + not (guardedLesser(e, varUse(v)) and guardedGreater(e, varUse(v)))) + ) +} + +/** is e not guarded against underflow by use? */ +predicate missingGuardAgainstUnderflow(BinaryArithmeticOperation e, VariableAccess use) { + use = e.getAnOperand() and + exists(LocalScopeVariable v | use.getTarget() = v | + // underflow possible if use is left operand and small + (e instanceof SubExpr and + (use = e.getLeftOperand() and not guardedGreater(e, varUse(v)))) or + // underflow possible if large or small + (e instanceof MulExpr and + not (guardedLesser(e, varUse(v)) and guardedGreater(e, varUse(v)))) + ) +} diff --git a/cpp/ql/src/semmle/code/cpp/security/PrintfLike.qll b/cpp/ql/src/semmle/code/cpp/security/PrintfLike.qll new file mode 100644 index 000000000000..daf7266092ec --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/security/PrintfLike.qll @@ -0,0 +1,15 @@ +import semmle.code.cpp.commons.Printf +import external.ExternalArtifact + +predicate printfLikeFunction(Function func, int formatArg) { + (formatArg = func.(FormattingFunction).getFormatParameterIndex() and not func instanceof UserDefinedFormattingFunction) + or + primitiveVariadicFormatter(func, formatArg, _) + or + exists(ExternalData data | + // TODO Do this \ to / conversion in the toolchain? + data.getDataPath().replaceAll("\\", "/") = "cert/formatingFunction.csv" + and func.getName() = data.getField(0) + and formatArg = data.getFieldAsInt(1) + ) +} diff --git a/cpp/ql/src/semmle/code/cpp/security/Security.qll b/cpp/ql/src/semmle/code/cpp/security/Security.qll new file mode 100644 index 000000000000..aad3b34e957a --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/security/Security.qll @@ -0,0 +1,230 @@ +/** + * Definitions related to security queries. + * These can be extended for specific code bases. + */ + +import semmle.code.cpp.exprs.Expr +import semmle.code.cpp.commons.Environment +import semmle.code.cpp.security.SecurityOptions + + +/** + * Extend this class to customize the security queries for + * a particular code base. Provide no constructor in the + * subclass, and override any methods that need customizing. + */ +class SecurityOptions extends string { + SecurityOptions() { + this = "SecurityOptions" + } + + /** + * This predicate should hold if the function with the given + * name is a pure function of its arguments. + */ + predicate isPureFunction(string name) { + name = "abs" + or name = "atof" + or name = "atoi" + or name = "atol" + or name = "atoll" + or name = "labs" + or name = "strcasestr" + or name = "strcat" + or name = "strchnul" + or name = "strchr" + or name = "strchrnul" + or name = "strcmp" + or name = "strcpy" + or name = "strcspn" + or name = "strdup" + or name = "strlen" + or name = "strncat" + or name = "strncmp" + or name = "strncpy" + or name = "strndup" + or name = "strnlen" + or name = "strrchr" + or name = "strspn" + or name = "strstr" + or name = "strtod" + or name = "strtof" + or name = "strtol" + or name = "strtoll" + or name = "strtoq" + or name = "strtoul" + } + + /** + * An argument to a function that is passed to a SQL server. + */ + predicate sqlArgument(string function, int arg) { + // MySQL C API + (function = "mysql_query" and arg = 1) or + (function = "mysql_real_query" and arg = 1) or + + // SQLite3 C API + (function = "sqlite3_exec" and arg = 1) + } + + /** + * The argument of the given function is filled in from user input. + */ + predicate userInputArgument(FunctionCall functionCall, int arg) + { + exists(string fname | + functionCall.getTarget().hasGlobalName(fname) and + exists(functionCall.getArgument(arg)) and + ( + (fname = "read" and arg = 1) or + (fname = "fread" and arg = 0) or + (fname = "fgets" and arg = 0) or + (fname = "fgetws" and arg = 0) or + (fname = "gets" and arg = 0) or + (fname = "getaddrinfo" and arg = 3) or + (fname = "recv" and arg = 1) or + (fname = "recvfrom" and (arg = 1 or arg = 4 or arg = 5)) or + (fname = "recvmsg" and arg = 1) or + (fname = "scanf" and arg >= 1) or + (fname = "fscanf" and arg >= 2) + ) + ) + } + + /** + * The return value of the given function is filled in from user input. + */ + predicate userInputReturned(FunctionCall functionCall) + { + exists(string fname | + functionCall.getTarget().getName() = fname and + ( + fname = "fgets" or + fname = "gets" or + userInputReturn(fname) + ) + ) + } + + /** + * DEPRECATED: Users should override `userInputReturned()` instead. + * + * note: this function is not formally tagged as `deprecated` since the + * new `userInputReturned` uses it to provide compatibility with older + * custom SecurityOptions.qll files. + */ + predicate userInputReturn(string function) + { + none() + } + + /** + * The argument of the given function is used for running a process or loading + * a library. + */ + predicate isProcessOperationArgument(string function, int arg) { + // POSIX + (function = "system" and arg = 0) + or (function = "popen" and arg = 0) + or (function = "execl" and arg = 0) + or (function = "execlp" and arg = 0) + or (function = "execle" and arg = 0) + or (function = "execv" and arg = 0) + or (function = "execvp" and arg = 0) + or (function = "execvpe" and arg = 0) + or (function = "dlopen" and arg = 0) + + // Windows + or (function = "LoadLibrary" and arg = 0) + or (function = "LoadLibraryA" and arg = 0) + or (function = "LoadLibraryW" and arg = 0) + } + + /** + * This predicate should hold if the expression is directly + * computed from user input. Such expressions are treated as + * sources of taint. + */ + predicate isUserInput(Expr expr, string cause) { + exists(FunctionCall fc, int i | + this.userInputArgument(fc, i) + and expr = fc.getArgument(i) + and cause = fc.getTarget().getName()) + or exists(FunctionCall fc | + this.userInputReturned(fc) + and expr = fc + and cause = fc.getTarget().getName()) + or (commandLineArg(expr) and cause = "argv") + or expr.(EnvironmentRead).getSourceDescription() = cause + } + + /** + * This predicate should hold if the expression raises privilege for the + * current session. The default definition only holds true for some + * example code in the test suite. This predicate must be extended for + * a particular code base to be useful. + */ + predicate raisesPrivilege(Expr expr) { + exists (ReturnStmt ret | ret.getExpr() = expr | + ret.getEnclosingFunction().getName() = "checkPinCode" + and ret.getExpr().getValue() = "1") + or exists (AssignExpr assign, Variable adminPrivileges | + assign = expr + and adminPrivileges.hasName("adminPrivileges") + and assign.getLValue().(Access).getTarget() = adminPrivileges + and not (assign.getRValue().(Literal).getValue() = "0")) + } +} + +/** + * An access to the argv argument to main(). + */ +private predicate commandLineArg(Expr e) +{ + exists(Parameter argv | + argv(argv) and + argv.getAnAccess() = e) +} + +/** The argv parameter to the main function */ +predicate argv(Parameter argv) +{ + exists(Function f | + f.hasGlobalName("main") + and f.getParameter(1) = argv) +} + +/** Convenience accessor for SecurityOptions.isPureFunction */ +predicate isPureFunction(string name) { + exists (SecurityOptions opts | opts.isPureFunction(name)) +} + +/** Convenience accessor for SecurityOptions.userInputArgument */ +predicate userInputArgument(FunctionCall functionCall, int arg) { + exists (SecurityOptions opts | opts.userInputArgument(functionCall, arg)) +} + +/** Convenience accessor for SecurityOptions.userInputReturn */ +predicate userInputReturned(FunctionCall functionCall) { + exists (SecurityOptions opts | opts.userInputReturned(functionCall)) +} + +/** Convenience accessor for SecurityOptions.isUserInput */ +predicate isUserInput(Expr expr, string cause) { + exists (SecurityOptions opts | opts.isUserInput(expr, cause)) +} + +/** Convenience accessor for SecurityOptions.isProcessOperationArgument */ +predicate isProcessOperationArgument(string function, int arg) { + exists (SecurityOptions opts | opts.isProcessOperationArgument(function, arg)) +} + +/** Convenient accessor for SecurityOptions.raisesPrivilege */ +predicate raisesPrivilege(Expr expr) { + exists (SecurityOptions opts | opts.raisesPrivilege(expr)) +} + +/** Convenience accessor for SecurityOptions.sqlArgument */ +predicate sqlArgument(string function, int arg) { + exists (SecurityOptions opts | opts.sqlArgument(function, arg)) +} diff --git a/cpp/ql/src/semmle/code/cpp/security/SecurityOptions.qll b/cpp/ql/src/semmle/code/cpp/security/SecurityOptions.qll new file mode 100644 index 000000000000..47889a1f42eb --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/security/SecurityOptions.qll @@ -0,0 +1,47 @@ +/* + * Security pack options. + * + * see https://semmle.com/wiki/display/SD/_Configuring+SecurityOptions+for+your+code+base + * + * Please note that functions for MySql and SQLite are included by default and do not + * require any customization here. + */ + +import semmle.code.cpp.security.Security + +class CustomSecurityOptions extends SecurityOptions { + override predicate sqlArgument(string function, int arg) { + SecurityOptions.super.sqlArgument(function, arg) or + // --- custom functions that access SQL code via one of their arguments: + // 'arg' is the 0-based index of the argument that contains an SQL string + // for example: (function = "MySpecialSqlFunction" and arg = 0) + none() // rules to match custom functions replace this line + } + + override predicate userInputArgument(FunctionCall functionCall, int arg) + { + SecurityOptions.super.userInputArgument(functionCall, arg) or + exists(string fname | + functionCall.getTarget().hasGlobalName(fname) and + exists(functionCall.getArgument(arg)) and ( + // --- custom functions that return user input via one of their arguments: + // 'arg' is the 0-based index of the argument that is used to return user input + // for example: (fname = "readXmlInto" and arg = 1) + none() // rules to match custom functions replace this line + ) + ) + } + + override predicate userInputReturned(FunctionCall functionCall) + { + SecurityOptions.super.userInputReturned(functionCall) or + exists(string fname | + functionCall.getTarget().hasGlobalName(fname) and + ( + // --- custom functions that return user input via their return value: + // for example: fname = "xmlReadAttribute" + none() // rules to match custom functions replace this line + ) + ) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/security/SensitiveExprs.qll b/cpp/ql/src/semmle/code/cpp/security/SensitiveExprs.qll new file mode 100644 index 000000000000..e42f891fab48 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/security/SensitiveExprs.qll @@ -0,0 +1,59 @@ +import cpp + +private string suspicious() { + result = "%password%" or + result = "%passwd%" or + result = "%account%" or + result = "%accnt%" or + result = "%trusted%" +} + +private string nonSuspicious() { + result = "%hashed%" or + result = "%encrypted%" or + result = "%crypt%" +} + +abstract class SensitiveExpr extends Expr {} + +class SensitiveVarAccess extends SensitiveExpr { + SensitiveVarAccess() { + this instanceof VariableAccess and + exists(string s | this.toString().toLowerCase() = s | + s.matches(suspicious())and + not s.matches(nonSuspicious()) + ) + } +} + +class SensitiveCall extends SensitiveExpr { + SensitiveCall() { + this instanceof FunctionCall and + exists(string s | this.toString().toLowerCase() = s | + s.matches(suspicious())and + not s.matches(nonSuspicious()) + ) + } +} + +class SensitivePropAccess extends SensitiveExpr { + SensitivePropAccess() { + exists (PropertyAccess acc, string name | + acc = this and + name = acc.getProperty().getName().toLowerCase() and + name.matches(suspicious()) and + not name.matches(nonSuspicious())) + } +} + +/** + * A read from the value of a text widget. + */ +class SensitiveTextRead extends SensitiveExpr { + SensitiveTextRead() { + exists (PropertyAccess facc | + facc = this and + facc.getReceiver() instanceof SensitiveExpr and + facc.getProperty().getName() = "text") + } +} diff --git a/cpp/ql/src/semmle/code/cpp/security/TaintTracking.qll b/cpp/ql/src/semmle/code/cpp/security/TaintTracking.qll new file mode 100644 index 000000000000..4e65172c8235 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/security/TaintTracking.qll @@ -0,0 +1,630 @@ +/* + * Support for tracking tainted data through the program. + */ + +import cpp +import Security + +/** Expressions that change the value of a variable */ +private +predicate valueSource(Expr expr) +{ + exists(AssignExpr ae | expr = ae.getLValue()) + or + exists(FunctionCall fc, int i | + userInputArgument(fc, i) + and expr = fc.getArgument(i)) + or + exists(FunctionCall c, int arg | + copyValueBetweenArguments(c.getTarget(), _, arg) and + expr = c.getArgument(arg)) + or + exists(FunctionCall c, int arg | + c.getTarget().getParameter(arg).getType() instanceof ReferenceType and + expr = c.getArgument(arg)) +} + +/** Expressions that are inside an expression that changes the value of a variable */ +private +predicate insideValueSource(Expr expr) +{ + valueSource(expr) or + ( + insideValueSource(expr.getParent()) and + + // A modification of array[offset] does not modify offset + not expr.getParent().(ArrayExpr).getArrayOffset() = expr + ) +} + +private +predicate isPointer(Type type) +{ + type instanceof PointerType + or isPointer(type.(ReferenceType).getBaseType()) +} + +/** + * Tracks data flow from src to dest. + * If this is used in the left side of an assignment src and dest should be swapped + */ +private +predicate moveToDependingOnSide(Expr src, Expr dest) { + exists(ParenthesisExpr e | + src = e.getAChild() and + dest = e + ) + or + exists(ArrayExpr e | + src = e.getArrayBase() and + dest = e + ) + or + exists(PointerDereferenceExpr e | + src = e.getOperand() and + dest = e + ) + or + exists(AddressOfExpr e | + src = e.getOperand() and + dest = e + ) + // if var+offset is tainted, then so is var + or exists (VariableAccess base, BinaryOperation binop | + dest = binop + and (base = binop.getLeftOperand() or base = binop.getRightOperand()) + and isPointer(base.getType()) + and base.getTarget() instanceof LocalScopeVariable + and src = base) + or exists (UnaryOperation unop | + dest = unop + and unop.getAnOperand() = src) + or (exists (BinaryOperation binop | + dest = binop + and binop.getLeftOperand() = src + and predictable(binop.getRightOperand()))) + or (exists (BinaryOperation binop | + dest = binop + and binop.getRightOperand() = src + and predictable(binop.getLeftOperand()))) + or exists (Cast cast | + dest = cast + and src = cast.getExpr()) + or exists (ConditionalExpr cond | + cond = dest + and ( + cond.getThen() = src + or cond.getElse() = src)) +} + +/** + * Track value flow between functions. + * Handles the following cases: + * - If an argument to a function is tainted, all the usages of the parameter inside the function are tainted + * - If a function obtains input from the user internally and returns it, all calls to the function are tainted + * - If an argument to a function is tainted and that parameter is returned, all calls to the function are not tainted + * (this is done to avoid false positives). Because of this we need to track if the tainted element came from an argument + * or not, and for that we use destFromArg + */ +private +predicate betweenFunctionsValueMoveTo(Element src, Element dest, boolean destFromArg) +{ + not unreachable(src) + and not unreachable(dest) + and ( + exists(Call call, Function called, int i | + src = call.getArgument(i) + and resolveCallWithParam(call, called, i, dest) + and destFromArg = true) + + // Only move the return of the function to the function itself if the value didn't came from an + // argument, or else we would taint all the calls to one function if one argument is tainted + // somewhere + or exists(Function f, ReturnStmt ret | + ret.getEnclosingFunction() = f + and src = ret.getExpr() + and destFromArg = false + and dest = f) + or exists(Call call, Function f | + f = resolveCall(call) + and src = f + and dest = call + and destFromArg = false) + + // If a parameter of type reference is tainted inside a function, taint the argument too + or exists(Call call, Function f, int pi, Parameter p | + resolveCallWithParam(call, f, pi, p) + and p.getType() instanceof ReferenceType + and src = p + and dest = call.getArgument(pi) + and destFromArg = false) + ) +} + +// predicate folding for proper join-order +pragma [nomagic] // bad magic: pushes down predicate that ruins join-order +private predicate resolveCallWithParam(Call call, Function called, int i, Parameter p) { + called = resolveCall(call) + and + p = called.getParameter(i) +} + +/** A variable for which flow through is allowed. */ +library class FlowVariable extends Variable { + FlowVariable() { + ( + this instanceof LocalScopeVariable + or this instanceof GlobalOrNamespaceVariable + ) + and not argv(this) + } +} + +/** A local scope variable for which flow through is allowed. */ +library class FlowLocalScopeVariable extends Variable { + FlowLocalScopeVariable() { + this instanceof LocalScopeVariable + } +} + +private +predicate insideFunctionValueMoveTo(Element src, Element dest) +{ + not unreachable(src) + and not unreachable(dest) + and ( + // Taint all variable usages when one is tainted + // This function taints global variables but doesn't taint from a global variable (see globalVariableValueMoveTo) + exists(FlowLocalScopeVariable v | + src = v + and dest = v.getAnAccess() + and not insideValueSource(dest)) + or exists(FlowVariable v | + src = v.getAnAccess() + and dest = v + and insideValueSource(src)) + + // Taint all union usages when one is tainted + // This function taints global variables but doesn't taint from a global variable (see globalVariableValueMoveTo) + or exists(FlowLocalScopeVariable v, FieldAccess a | + unionAccess(v, _, a) + and src = v + and dest = a + and not insideValueSource(dest)) + or exists(FlowVariable v, FieldAccess a | + unionAccess(v, _, a) + and src = a + and dest = v + and insideValueSource(src)) + + // If a pointer is tainted, taint the original variable + or exists(FlowVariable p, FlowVariable v, AddressOfExpr e | + p.getAnAssignedValue() = e + and e.getOperand() = v.getAnAccess() + and src = p + and dest = v) + // If a reference is tainted, taint the original variable + or exists(FlowVariable r, FlowVariable v | + r.getType() instanceof ReferenceType + and r.getInitializer().getExpr() = v.getAnAccess() + and src = r + and dest = v) + + or exists (Variable var | + var = dest + and var.getInitializer().getExpr() = src) + or exists(AssignExpr ae | + src = ae.getRValue() + and dest = ae.getLValue()) + or exists (CommaExpr comma | + comma = dest + and comma.getRightOperand() = src) + or exists(FunctionCall c, int sourceArg, int destArg | + copyValueBetweenArguments(c.getTarget(), sourceArg, destArg) + // Only consider copies from `printf`-like functions if the format is a string + and ( + exists(FormattingFunctionCall ffc, FormatLiteral format, string argFormat | + ffc = c + and format = ffc.getFormat() + and format.getConversionChar(sourceArg - ffc.getTarget().getNumberOfParameters()) = argFormat + and (argFormat = "s" or argFormat = "S") + ) + or not exists(FormatLiteral fl | fl = c.(FormattingFunctionCall).getFormat()) + or not c instanceof FormattingFunctionCall + ) + and src = c.getArgument(sourceArg) + and dest = c.getArgument(destArg)) + or exists(FunctionCall c, int sourceArg | + returnArgument(c.getTarget(), sourceArg) + and src = c.getArgument(sourceArg) + and dest = c) + or exists (MessageExpr send | + methodReturningAnyArgument(send.getStaticTarget()) + and not send instanceof FormattingFunctionCall + and src = send.getAnArgument() + and dest = send) + or exists(FormattingFunctionCall formattingSend, int arg, FormatLiteral format, string argFormat | + 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 = "@")) + or exists (ExprMessageExpr send | + methodReturningReceiver(send.getStaticTarget()) + and src = send.getReceiver() + and dest = send) + // Expressions computed from tainted data are also tainted + or (exists (FunctionCall call | dest = call and isPureFunction(call.getTarget().getName()) | + call.getAnArgument() = src + and forall(Expr arg | arg = call.getAnArgument() | arg = src or predictable(arg)))) + or exists(Element a, Element b | + moveToDependingOnSide(a, b) and + if insideValueSource(a) then + (src = b and dest = a) + else + (src = a and dest = b) + ) + ) +} + +/** + * Handles data flow from global variables to its usages. + * The tainting for the global variable itself is done at insideFunctionValueMoveTo. + */ +private +predicate globalVariableValueMoveTo(GlobalOrNamespaceVariable src, Expr dest) +{ + not unreachable(dest) + and ( + exists(GlobalOrNamespaceVariable v | + src = v + and dest = v.getAnAccess() + and not insideValueSource(dest)) + or exists(GlobalOrNamespaceVariable v, FieldAccess a | + unionAccess(v, _, a) + and src = v + and dest = a + and not insideValueSource(dest)) + ) +} + +private +predicate unionAccess(Variable v, Field f, FieldAccess a) +{ + f.getDeclaringType() instanceof Union + and a.getTarget() = f + and a.getQualifier() = v.getAnAccess() +} + +GlobalOrNamespaceVariable globalVarFromId(string id) { + if result instanceof NamespaceVariable then + id = result.getNamespace() + "::" + result.getName() + else + id = result.getName() +} + + +/** + * A variable that has any kind of upper-bound check anywhere in the program + */ +private +predicate hasUpperBoundsCheck(Variable var) { + exists (BinaryOperation oper, VariableAccess access | + (oper.getOperator() = "<" or oper.getOperator() = "<=" or oper.getOperator() = ">" or oper.getOperator() = ">=") + and oper.getLeftOperand() = access + and access.getTarget() = var + + // Comparing to 0 is not an upper bound check + and not oper.getRightOperand().getValue() = "0") +} + +private +cached +predicate taintedWithArgsAndGlobalVars(Element src, Element dest, boolean destFromArg, string globalVar) +{ + ( + isUserInput(src, _) + and not unreachable(src) + and dest = src + and destFromArg = false + and globalVar = "" + ) + or exists (Element other, boolean otherFromArg, string otherGlobalVar | taintedWithArgsAndGlobalVars(src, other, otherFromArg, otherGlobalVar) | + not unreachable(dest) + and not hasUpperBoundsCheck(dest) + and ( + // Direct flow from one expression to another. + ( + betweenFunctionsValueMoveTo(other, dest, destFromArg) + and (destFromArg = true or otherFromArg = false) + and globalVar = otherGlobalVar + ) + or + ( + insideFunctionValueMoveTo(other, dest) + and destFromArg = otherFromArg + and globalVar = otherGlobalVar + ) + or + exists(GlobalOrNamespaceVariable v | + v = other + and globalVariableValueMoveTo(v, dest) + and destFromArg = false + and v = globalVarFromId(globalVar) + ) + ) + ) +} + +/* + * A tainted expression is either directly user input, or is + * computed from user input in a way that users can probably + * control the exact output of the computation. + * + * This doesn't include data flow through global variables. + * If you need that you must call taintedIncludingGlobalVars. + */ +predicate tainted(Expr source, Element tainted) { + taintedWithArgsAndGlobalVars(source, tainted, _, "") +} + +/* + * A tainted expression is either directly user input, or is + * computed from user input in a way that users can probably + * control the exact output of the computation. + * + * This version gives the same results as tainted but also includes + * data flow through global variables. + * + * @param globalVar the name of the last global variable used to move the + * value from source to tainted. + */ +predicate taintedIncludingGlobalVars(Expr source, Element tainted, string globalVar) { + taintedWithArgsAndGlobalVars(source, tainted, _, globalVar) +} + +/* + * A predictable expression is one where an external user can predict + * the value. For example, a literal in the source code is considered + * predictable. + */ +private predicate predictable(Expr expr) { + (expr instanceof Literal) + or exists (BinaryOperation binop | binop = expr | + predictable(binop.getLeftOperand()) and predictable(binop.getRightOperand())) + or exists (UnaryOperation unop | unop = expr | + predictable(unop.getOperand())) +} + +private int maxArgIndex(Function f) +{ + result = max(FunctionCall fc, int toMax | (fc.getTarget() = f) and (toMax = fc.getNumberOfArguments() - 1) | toMax) +} + +/** Functions that copy the value of one argument to another */ +private predicate copyValueBetweenArguments(Function f, int sourceArg, int destArg) +{ + (f.hasGlobalName("memcpy") and sourceArg = 1 and destArg = 0) + or (f.hasGlobalName("__builtin___memcpy_chk") and sourceArg = 1 and destArg = 0) + or (f.hasGlobalName("memmove") and sourceArg = 1 and destArg = 0) + or (f.hasGlobalName("strcat") and sourceArg = 1 and destArg = 0) + or (f.hasGlobalName("_mbscat") and sourceArg = 1 and destArg = 0) + or (f.hasGlobalName("wcsncat") and sourceArg = 1 and destArg = 0) + or (f.hasGlobalName("strncat") and sourceArg = 1 and destArg = 0) + or (f.hasGlobalName("_mbsncat") and sourceArg = 1 and destArg = 0) + or (f.hasGlobalName("wcsncat") and sourceArg = 1 and destArg = 0) + or (f.hasGlobalName("strcpy") and sourceArg = 1 and destArg = 0) + or (f.hasGlobalName("_mbscpy") and sourceArg = 1 and destArg = 0) + or (f.hasGlobalName("wcscpy") and sourceArg = 1 and destArg = 0) + or (f.hasGlobalName("strncpy") and sourceArg = 1 and destArg = 0) + or (f.hasGlobalName("_mbsncpy") and sourceArg = 1 and destArg = 0) + or (f.hasGlobalName("wcsncpy") and sourceArg = 1 and destArg = 0) + or (f.hasGlobalName("inet_aton") and sourceArg = 0 and destArg = 1) + or (f.hasGlobalName("inet_pton") and sourceArg = 1 and destArg = 2) + or (f.hasGlobalName("strftime") and sourceArg in [2 .. maxArgIndex(f)] and destArg = 0) + or exists(FormattingFunction ff | ff = f | + sourceArg in [ff.getFormatParameterIndex() .. maxArgIndex(f)] + and destArg = ff.getOutputParameterIndex() + ) +} + +/** Functions where if one of the arguments is tainted, the result should be tainted */ +private predicate returnArgument(Function f, int sourceArg) +{ + (f.hasGlobalName("memcpy") and sourceArg = 0) + or (f.hasGlobalName("__builtin___memcpy_chk") and sourceArg = 0) + or (f.hasGlobalName("memmove") and sourceArg = 0) + or (f.hasGlobalName("strcat") and sourceArg = 0) + or (f.hasGlobalName("_mbscat") and sourceArg = 0) + or (f.hasGlobalName("wcsncat") and sourceArg = 0) + or (f.hasGlobalName("strncat") and sourceArg = 0) + or (f.hasGlobalName("_mbsncat") and sourceArg = 0) + or (f.hasGlobalName("wcsncat") and sourceArg = 0) + or (f.hasGlobalName("strcpy") and sourceArg = 0) + or (f.hasGlobalName("_mbscpy") and sourceArg = 0) + or (f.hasGlobalName("wcscpy") and sourceArg = 0) + or (f.hasGlobalName("strncpy") and sourceArg = 0) + or (f.hasGlobalName("_mbsncpy") and sourceArg = 0) + or (f.hasGlobalName("wcsncpy") and sourceArg = 0) + or (f.hasGlobalName("inet_ntoa") and sourceArg = 0) + or (f.hasGlobalName("inet_addr") and sourceArg = 0) + or (f.hasGlobalName("inet_network") and sourceArg = 0) + or (f.hasGlobalName("inet_ntoa") and sourceArg = 0) + or (f.hasGlobalName("inet_makeaddr") and (sourceArg = 0 or sourceArg = 1)) + or (f.hasGlobalName("inet_lnaof") and sourceArg = 0) + or (f.hasGlobalName("inet_netof") and sourceArg = 0) + or (f.hasGlobalName("gethostbyname") and sourceArg = 0) + or (f.hasGlobalName("gethostbyaddr") and sourceArg = 0) +} + +/** A method where if any argument is tainted, the return value should be, too */ +private predicate methodReturningAnyArgument(MemberFunction method) { + method.getQualifiedName().matches("NS%Array%::+array%") or + method.getQualifiedName().matches("NS%Array%::-arrayBy%") or + method.getQualifiedName().matches("NS%Array%::-componentsJoinedByString:") or + method.getQualifiedName().matches("NS%Array%::-init%") or + method.getQualifiedName().matches("NS%Data%::+dataWith%") or + method.getQualifiedName().matches("NS%Data%::-initWith%") or + method.getQualifiedName().matches("NS%String%::+pathWithComponents:") or + method.getQualifiedName().matches("NS%String%::+stringWith%") or + method.getQualifiedName().matches("NS%String%::-initWithCString:") or + method.getQualifiedName().matches("NS%String%::-initWithCString:length:") or + method.getQualifiedName().matches("NS%String%::-initWithCStringNoCopy:length:") or + method.getQualifiedName().matches("NS%String%::-initWithCharacters:length:") or + method.getQualifiedName().matches("NS%String%::-initWithCharactersNoCopy:length:freeWhenDone:") or + method.getQualifiedName().matches("NS%String%::-initWithFormat:") or + method.getQualifiedName().matches("NS%String%::-initWithFormat:arguments:") or + method.getQualifiedName().matches("NS%String%::-initWithString:") or + method.getQualifiedName().matches("NS%String%::-initWithUTF8String:") or + method.getQualifiedName().matches("NS%String%::-stringByAppendingFormat:") or + method.getQualifiedName().matches("NS%String%::-stringByAppendingString:") or + method.getQualifiedName().matches("NS%String%::-stringByPaddingToLength:withString:startingAtIndex:") or + method.getQualifiedName().matches("NS%String%::-stringByReplacing%") or + method.getQualifiedName().matches("NS%String%::-stringsByAppendingPaths:") +} + +/** A method where if the receiver is tainted, the return value should be, too */ +private predicate methodReturningReceiver(MemberFunction method) { + method.getQualifiedName().matches("NS%Array%::-arrayBy%") or + method.getQualifiedName().matches("NS%Array%::-componentsJoinedByString:") or + method.getQualifiedName().matches("NS%Array%::-firstObject") or + method.getQualifiedName().matches("NS%Array%::-lastObject") or + method.getQualifiedName().matches("NS%Array%::-objectAt%") or + method.getQualifiedName().matches("NS%Array%::-pathsMatchingExtensions:") or + method.getQualifiedName().matches("NS%Array%::-sortedArray%") or + method.getQualifiedName().matches("NS%Array%::-subarrayWithRange:") or + method.getQualifiedName().matches("NS%Data%::-bytes") or + method.getQualifiedName().matches("NS%Data%::-subdataWithRange:") or + method.getQualifiedName().matches("NS%String%::-capitalizedString%") or + method.getQualifiedName().matches("NS%String%::-componentsSeparatedByCharactersInSet:") or + method.getQualifiedName().matches("NS%String%::-componentsSeparatedByString:") or + method.getQualifiedName().matches("NS%String%::-cStringUsingEncoding:") or + method.getQualifiedName().matches("NS%String%::-dataUsingEncoding:%") or + method.getQualifiedName().matches("NS%String%::-lowercaseString%") or + method.getQualifiedName().matches("NS%String%::-pathComponents") or + method.getQualifiedName().matches("NS%String%::-stringBy%") or + method.getQualifiedName().matches("NS%String%::-stringsByAppendingPaths:") or + method.getQualifiedName().matches("NS%String%::-substringFromIndex:") or + method.getQualifiedName().matches("NS%String%::-substringToIndex:") or + method.getQualifiedName().matches("NS%String%::-substringWithRange:") or + method.getQualifiedName().matches("NS%String%::-uppercaseString%") or + method.getQualifiedName().matches("NS%String%::-UTF8String") +} + +/** + * Resolve potential target function(s) for `call`. + * + * If `call` is a call through a function pointer (`ExprCall`) or + * targets a virtual method, simple data flow analysis is performed + * in order to identify target(s). + */ +Function resolveCall(Call call) { + result = call.getTarget() + or + result = call.(DataSensitiveCallExpr).resolve() +} + +/** A data sensitive call expression. */ +library abstract class DataSensitiveCallExpr extends @expr { + DataSensitiveCallExpr() { not unreachable(this) } + + abstract Expr getSrc(); + cached abstract Function resolve(); + abstract string toString(); + + /** + * Whether `src` can flow to this call expression. + * + * Searches backwards from `getSrc()` to `src`. + */ + predicate flowsFrom(Element src, boolean allowFromArg) { + src = getSrc() and allowFromArg = true + or + exists(Element other, boolean allowOtherFromArg | flowsFrom(other, allowOtherFromArg) | + exists(boolean otherFromArg | + betweenFunctionsValueMoveToStatic(src, other, otherFromArg) | + otherFromArg = true and allowOtherFromArg = true and allowFromArg = true + or + otherFromArg = false and allowFromArg = false + ) + or + insideFunctionValueMoveTo(src, other) and allowFromArg = allowOtherFromArg + or + globalVariableValueMoveTo(src, other) and allowFromArg = true + ) + } +} + +/** Call through a function pointer. */ +library class DataSensitiveExprCall extends DataSensitiveCallExpr, ExprCall { + override Expr getSrc() { result = getExpr() } + + override Function resolve() { + exists(FunctionAccess fa | flowsFrom(fa, true) | result = fa.getTarget()) + } + + override string toString() { result = ExprCall.super.toString() } +} + +/** Call to a virtual function. */ +library class DataSensitiveOverriddenFunctionCall extends DataSensitiveCallExpr, FunctionCall { + DataSensitiveOverriddenFunctionCall() { + exists(getTarget().(VirtualFunction).getAnOverridingFunction()) + } + + override Expr getSrc() { result = getQualifier() } + + override MemberFunction resolve() { + exists(NewExpr new | + flowsFrom(new, true) + and + memberFunctionFromNewExpr(new, result) + and + result.overrides*(getTarget().(VirtualFunction)) + ) + } + + override string toString() { result = FunctionCall.super.toString() } +} + +private predicate memberFunctionFromNewExpr(NewExpr new, MemberFunction f) { + f = new.getAllocatedType().(Class).getAMemberFunction() +} + +/** Same as `betweenFunctionsValueMoveTo`, but calls are resolved to their static target. */ +private +predicate betweenFunctionsValueMoveToStatic(Element src, Element dest, boolean destFromArg) +{ + not unreachable(src) + and not unreachable(dest) + and ( + exists (FunctionCall call, Function called, int i | + src = call.getArgument(i) + and called = call.getTarget() + and dest = called.getParameter(i) + and destFromArg = true) + + // Only move the return of the function to the function itself if the value didn't came from an + // argument, or else we would taint all the calls to one function if one argument is tainted + // somewhere + or exists(Function f, ReturnStmt ret | + ret.getEnclosingFunction() = f + and src = ret.getExpr() + and destFromArg = false + and dest = f) + or exists(FunctionCall call, Function f | + call.getTarget() = f + and src = f + and dest = call + and destFromArg = false) + + // If a parameter of type reference is tainted inside a function, taint the argument too + or exists(FunctionCall call, Function f, int pi, Parameter p | + call.getTarget() = f + and f.getParameter(pi) = p + and p.getType() instanceof ReferenceType + and src = p + and dest = call.getArgument(pi) + and destFromArg = false) + ) +} diff --git a/cpp/ql/src/semmle/code/cpp/ssa/AliasedSSAIR.qll b/cpp/ql/src/semmle/code/cpp/ssa/AliasedSSAIR.qll new file mode 100644 index 000000000000..ca6ec999c6fc --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ssa/AliasedSSAIR.qll @@ -0,0 +1 @@ +import internal.aliased_ssa.IRImpl diff --git a/cpp/ql/src/semmle/code/cpp/ssa/AliasedSSAIRSanity.ql b/cpp/ql/src/semmle/code/cpp/ssa/AliasedSSAIRSanity.ql new file mode 100644 index 000000000000..ee0e94374c62 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ssa/AliasedSSAIRSanity.ql @@ -0,0 +1,8 @@ +/** + * @name Aliased SSA IR Sanity Check + * @description Performs sanity checks on the Intermediate Representation. This query should have no results. + * @kind problem + * @id cpp/aliased-ssa-ir-sanity-check + */ + +import internal.aliased_ssa.IRSanityImpl diff --git a/cpp/ql/src/semmle/code/cpp/ssa/PrintAliasedSSAIR.qll b/cpp/ql/src/semmle/code/cpp/ssa/PrintAliasedSSAIR.qll new file mode 100644 index 000000000000..8498bff95502 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ssa/PrintAliasedSSAIR.qll @@ -0,0 +1 @@ +import internal.aliased_ssa.PrintIRImpl diff --git a/cpp/ql/src/semmle/code/cpp/ssa/PrintSSAIR.qll b/cpp/ql/src/semmle/code/cpp/ssa/PrintSSAIR.qll new file mode 100644 index 000000000000..6389c4214981 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ssa/PrintSSAIR.qll @@ -0,0 +1 @@ +import internal.ssa.PrintIRImpl diff --git a/cpp/ql/src/semmle/code/cpp/ssa/SSAIR.qll b/cpp/ql/src/semmle/code/cpp/ssa/SSAIR.qll new file mode 100644 index 000000000000..7ac7aea2c84b --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ssa/SSAIR.qll @@ -0,0 +1 @@ +import internal.ssa.IRImpl diff --git a/cpp/ql/src/semmle/code/cpp/ssa/SSAIRSanity.ql b/cpp/ql/src/semmle/code/cpp/ssa/SSAIRSanity.ql new file mode 100644 index 000000000000..4f1ed9a92618 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ssa/SSAIRSanity.ql @@ -0,0 +1,8 @@ +/** + * @name SSA IR Sanity Check + * @description Performs sanity checks on the Intermediate Representation. This query should have no results. + * @kind problem + * @id cpp/ssa-ir-sanity-check + */ + +import internal.ssa.IRSanityImpl diff --git a/cpp/ql/src/semmle/code/cpp/ssa/internal/IntegerConstant.qll b/cpp/ql/src/semmle/code/cpp/ssa/internal/IntegerConstant.qll new file mode 100644 index 000000000000..22fb8dea824a --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ssa/internal/IntegerConstant.qll @@ -0,0 +1,111 @@ +import cpp + +class IntValue = int; + +/** + * Returns the value of the maximum representable integer. + */ +int maxValue() { + result = 2147483647 +} + +/** + * Returns the value of the minimum representable integer. + */ +int minValue() { + result = -2147483647 +} + +/** + * Returns a value representing an unknown integer. + */ +IntValue unknown() { + result = -2147483648 +} + +/** + * Holds if `n` has a known value. + */ +bindingset[n] +predicate hasValue(IntValue n) { + n != unknown() +} + +/** + * Holds if the value `f` is within the range of representable integers. + */ +pragma[inline] +bindingset[f] +private predicate isRepresentable(float f) { + (f >= minValue()) and (f <= maxValue()) +} + +/** + * Gets the value of `n`. Holds only if `n` has a known value. + */ +bindingset[n] +int getValue(IntValue n) { + hasValue(n) and result = n +} + +/** + * Returns `a + b`. If either input is unknown, or if the addition overflows, + * the result is unknown. + */ +bindingset[a, b] +IntValue add(IntValue a, IntValue b) { + if hasValue(a) and hasValue(b) and isRepresentable((float)a + (float)b) then + result = a + b + else + result = unknown() +} + +/** + * Returns `a - b`. If either input is unknown, or if the subtraction overflows, + * the result is unknown. + */ +bindingset[a, b] +IntValue sub(IntValue a, IntValue b) { + if hasValue(a) and hasValue(b) and isRepresentable((float)a - (float)b) then + result = a - b + else + result = unknown() +} + +/** + * Returns `a * b`. If the multiplication overflows, the result is unknown. If + * either input is unknown and the other input is non-zero, the result is + * unknown. + */ +bindingset[a, b] +IntValue mul(IntValue a, IntValue b) { + if (a = 0) or (b = 0) then + result = 0 + else if hasValue(a) and hasValue(b) and isRepresentable((float)a * (float)b) then + result = a * b + else + result = unknown() +} + +/** + * Returns `a / b`. If either input is unknown, or if `b` is zero, the result is + * unknown. + */ +bindingset[a, b] +IntValue div(IntValue a, IntValue b) { + // Normally, integer division has to worry about overflow for INT_MIN/-1. + // However, since we use INT_MIN to represent an unknown value anyway, we only + // have to worry about division by zero. + if hasValue(a) and hasValue(b) and (b != 0) then + result = a / b + else + result = unknown() +} + +/** + * Return `-a`. If `a` is unknown, the result is unknown. + */ +bindingset[a] +IntValue neg(IntValue a) { + result = -a // -INT_MIN = INT_MIN, so this preserves unknown +} diff --git a/cpp/ql/src/semmle/code/cpp/ssa/internal/Overlap.qll b/cpp/ql/src/semmle/code/cpp/ssa/internal/Overlap.qll new file mode 100644 index 000000000000..5786a136345b --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ssa/internal/Overlap.qll @@ -0,0 +1,28 @@ +import cpp + +private newtype TOverlap = + TMayPartiallyOverlap() or + TMustTotallyOverlap() or + TMustExactlyOverlap() + +abstract class Overlap extends TOverlap { + abstract string toString(); +} + +class MayPartiallyOverlap extends Overlap, TMayPartiallyOverlap { + override final string toString() { + result = "MayPartiallyOverlap" + } +} + +class MustTotallyOverlap extends Overlap, TMustTotallyOverlap { + override final string toString() { + result = "MustTotallyOverlap" + } +} + +class MustExactlyOverlap extends Overlap, TMustExactlyOverlap { + override final string toString() { + result = "MustExactlyOverlap" + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/AliasAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/AliasAnalysis.qll new file mode 100644 index 000000000000..c5fd6e1d5ae6 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/AliasAnalysis.qll @@ -0,0 +1,214 @@ +private import AliasAnalysisInternal +import cpp +private import IR +private import semmle.code.cpp.ssa.internal.IntegerConstant as Ints + +private class IntValue = Ints::IntValue; + +/** + * Converts the bit count in `bits` to a byte count and a bit count in the form + * bytes:bits. + */ +bindingset[bits] +string bitsToBytesAndBits(int bits) { + result = (bits / 8).toString() + ":" + (bits % 8).toString() +} + +/** + * Gets a printable string for a bit offset with possibly unknown value. + */ +bindingset[bitOffset] +string getBitOffsetString(IntValue bitOffset) { + if Ints::hasValue(bitOffset) then + if bitOffset >= 0 then + result = "+" + bitsToBytesAndBits(bitOffset) + else + result = "-" + bitsToBytesAndBits(Ints::neg(bitOffset)) + else + result = "+?" +} + +/** + * Gets the offset of field `field` in bits. + */ +private IntValue getFieldBitOffset(Field field) { + if (field instanceof BitField) then ( + result = Ints::add(Ints::mul(field.getByteOffset(), 8), + field.(BitField).getBitOffset()) + ) + else ( + result = Ints::mul(field.getByteOffset(), 8) + ) +} + +/** + * Holds if the operand `tag` of instruction `instr` is used in a way that does + * not result in any address held in that operand from escaping beyond the + * instruction. + */ +predicate operandIsConsumedWithoutEscaping(Instruction instr, OperandTag tag) { + exists(instr.getOperand(tag)) and + ( + // The source/destination address of a Load/Store does not escape (but the + // loaded/stored value could). + tag instanceof LoadStoreAddressOperand or + // Neither operand of a Compare escapes. + instr instanceof CompareInstruction or + // Neither operand of a PointerDiff escapes. + instr instanceof PointerDiffInstruction or + // Converting an address to a `bool` does not escape the address. + instr.(ConvertInstruction).getResultType() instanceof BoolType + ) +} + +/** + * If the result of instruction `instr` is an integer constant, returns the + * value of that constant. Otherwise, returns unknown. + */ +IntValue getConstantValue(Instruction instr) { + if instr instanceof IntegerConstantInstruction then + result = instr.(IntegerConstantInstruction).getValue().toInt() + else + result = Ints::unknown() +} + +/** + * Computes the offset, in bits, by which the result of `instr` differs from the + * pointer argument to `instr`, if that offset is a constant. Otherwise, returns + * unknown. + */ +IntValue getPointerBitOffset(PointerOffsetInstruction instr) { + exists(IntValue bitOffset | + ( + bitOffset = Ints::mul(Ints::mul(getConstantValue(instr.getRightOperand()), + instr.getElementSize()), 8) + ) and + ( + instr instanceof PointerAddInstruction and result = bitOffset or + instr instanceof PointerSubInstruction and result = Ints::neg(bitOffset) + ) + ) +} + +/** + * Holds if any address held in operand `tag` of instruction `instr` is + * propagated to the result of `instr`, offset by the number of bits in + * `bitOffset`. If the address is propagated, but the offset is not known to be + * a constant, then `bitOffset` is unknown. + */ +predicate operandIsPropagated(Instruction instr, OperandTag tag, + IntValue bitOffset) { + exists(instr.getOperand(tag)) and + ( + // Converting to a non-virtual base class adds the offset of the base class. + exists(ConvertToBaseInstruction convert | + convert = instr and + bitOffset = Ints::mul(convert.getDerivation().getByteOffset(), 8) + ) or + // Converting to a derived class subtracts the offset of the base class. + exists(ConvertToDerivedInstruction convert | + convert = instr and + bitOffset = Ints::neg(Ints::mul(convert.getDerivation().getByteOffset(), 8)) + ) or + // Converting to a virtual base class adds an unknown offset. + ( + instr instanceof ConvertToVirtualBaseInstruction and + bitOffset = Ints::unknown() + ) or + // Conversion to another pointer type propagates the source address. + exists(ConvertInstruction convert, Type resultType | + convert = instr and + resultType = convert.getResultType() and + ( + resultType instanceof PointerType or + resultType instanceof Class //REVIEW: Remove when all glvalues are pointers + ) and + bitOffset = 0 + ) or + // Adding an integer to or subtracting an integer from a pointer propagates + // the address with an offset. + bitOffset = getPointerBitOffset(instr.(PointerOffsetInstruction)) or + // Computing a field address from a pointer propagates the address plus the + // offset of the field. + bitOffset = getFieldBitOffset(instr.(FieldAddressInstruction).getField()) or + // A copy propagates the source value. + tag instanceof CopySourceOperand and bitOffset = 0 + ) +} + +/** + * Holds if any address held in operand number `tag` of instruction `instr` + * escapes outside the domain of the analysis. + */ +predicate operandEscapes(Instruction instr, OperandTag tag) { + exists(instr.getOperand(tag)) and + // Conservatively assume that the address escapes unless one of the following + // holds: + not ( + // The operand is used in a way that does not escape the instruction + operandIsConsumedWithoutEscaping(instr, tag) or + // The address is propagated to the result of the instruction, but that + // result does not itself escape. + operandIsPropagated(instr, tag, _) and not resultEscapes(instr) + ) +} + +/** + * Holds if any address held in the result of instruction `instr` escapes + * outside the domain of the analysis. + */ +predicate resultEscapes(Instruction instr) { + // The result escapes if it has at least one use that escapes. + exists(Instruction useInstr, OperandTag useOperandTag | + instr.hasUse(useInstr, useOperandTag) and + operandEscapes(useInstr, useOperandTag) + ) +} + +/** + * Holds if the address of the specified local variable or parameter escapes the + * domain of the analysis. + */ +private predicate automaticVariableAddressEscapes(IRAutomaticVariable var) { + exists(FunctionIR funcIR | + funcIR = var.getFunctionIR() and + // The variable's address escapes if the result of any + // VariableAddressInstruction that computes the variable's address escapes. + exists(VariableAddressInstruction instr | + instr.getFunctionIR() = funcIR and + instr.getVariable() = var and + resultEscapes(instr) + ) + ) +} + +/** + * Holds if the address of the specified variable escapes the domain of the + * analysis. + */ +predicate variableAddressEscapes(IRVariable var) { + automaticVariableAddressEscapes(var.(IRAutomaticVariable)) or + // All variables with static storage duration have their address escape. + not var instanceof IRAutomaticVariable +} + +/** + * Holds if the result of instruction `instr` points within variable `var`, at + * bit offset `bitOffset` within the variable. If the result points within + * `var`, but at an unknown or non-constant offset, then `bitOffset` is unknown. + */ +predicate resultPointsTo(Instruction instr, IRVariable var, IntValue bitOffset) { + ( + // The address of a variable points to that variable, at offset 0. + instr.(VariableAddressInstruction).getVariable() = var and + bitOffset = 0 + ) or + exists(OperandTag operandTag, IntValue originalBitOffset, + IntValue propagatedBitOffset | + // If an operand is propagated, then the result points to the same variable, + // offset by the bit offset from the propagation. + resultPointsTo(instr.getOperand(operandTag), var, originalBitOffset) and + operandIsPropagated(instr, operandTag, propagatedBitOffset) and + bitOffset = Ints::add(originalBitOffset, propagatedBitOffset) + ) +} diff --git a/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/AliasAnalysisInternal.qll b/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/AliasAnalysisInternal.qll new file mode 100644 index 000000000000..13f43d1d1f4f --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/AliasAnalysisInternal.qll @@ -0,0 +1 @@ +import semmle.code.cpp.ssa.SSAIR as IR diff --git a/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/FunctionIR.qll b/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/FunctionIR.qll new file mode 100644 index 000000000000..ba8f524f202f --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/FunctionIR.qll @@ -0,0 +1,91 @@ +private import IRInternal +import Instruction +import cpp + +private newtype TFunctionIR = + MkFunctionIR(Function func) { + Construction::functionHasIR(func) + } + +/** + * Represents the IR for a function. + */ +class FunctionIR extends TFunctionIR { + Function func; + + FunctionIR() { + this = MkFunctionIR(func) + } + + final string toString() { + result = "IR: " + func.toString() + } + + /** + * Gets the function whose IR is represented. + */ + final Function getFunction() { + result = func + } + + /** + * Gets the location of the function. + */ + final Location getLocation() { + result = func.getLocation() + } + + /** + * Gets the entry point for this function. + */ + final EnterFunctionInstruction getEnterFunctionInstruction() { + result.getFunctionIR() = this + } + + /** + * Gets the exit point for this function. + */ + final ExitFunctionInstruction getExitFunctionInstruction() { + result.getFunctionIR() = this + } + + final UnmodeledDefinitionInstruction getUnmodeledDefinitionInstruction() { + result.getFunctionIR() = this + } + + /** + * Gets the single return instruction for this function. + */ + final ReturnInstruction getReturnInstruction() { + result.getFunctionIR() = 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. + */ + final IRReturnVariable getReturnVariable() { + result.getFunctionIR() = this + } + + /** + * Gets the block containing the entry point of this function. + */ + final IRBlock getEntryBlock() { + result.getFirstInstruction() = getEnterFunctionInstruction() + } + + /** + * Gets all instructions in this function. + */ + final Instruction getAnInstruction() { + result.getFunctionIR() = this + } + + /** + * Gets all blocks in this function. + */ + final IRBlock getABlock() { + result.getFunctionIR() = this + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/IRBlock.qll b/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/IRBlock.qll new file mode 100644 index 000000000000..98ec650036db --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/IRBlock.qll @@ -0,0 +1,131 @@ +private import IRInternal +import Instruction +import cpp +import semmle.code.cpp.ir.EdgeKind + +private predicate startsBasicBlock(Instruction instr) { + not instr instanceof PhiInstruction and + ( + count(Instruction predecessor | + instr = predecessor.getASuccessor() + ) != 1 or // Multiple predecessors or no predecessor + exists(Instruction predecessor | + instr = predecessor.getASuccessor() and + strictcount(Instruction other | + other = predecessor.getASuccessor() + ) > 1 + ) or // Predecessor has multiple successors + exists(Instruction predecessor, EdgeKind kind | + instr = predecessor.getSuccessor(kind) and + not kind instanceof GotoEdge + ) // Incoming edge is not a GotoEdge + ) +} + +private newtype TIRBlock = + MkIRBlock(Instruction firstInstr) { + startsBasicBlock(firstInstr) + } + +cached private predicate isEntryBlock(IRBlock block) { + block.getFirstInstruction() instanceof EnterFunctionInstruction +} + +cached private predicate blockSuccessor(IRBlock pred, IRBlock succ) { + succ = pred.getASuccessor() +} + +private predicate blockImmediatelyDominates(IRBlock dominator, IRBlock block) = + idominance(isEntryBlock/1, blockSuccessor/2)(_, dominator, block) + +class IRBlock extends TIRBlock { + Instruction firstInstr; + + IRBlock() { + this = MkIRBlock(firstInstr) + } + + final string toString() { + result = firstInstr.toString() + } + + final Location getLocation() { + result = getFirstInstruction().getLocation() + } + + final string getUniqueId() { + result = firstInstr.getUniqueId() + } + + final cached Instruction getInstruction(int index) { + index = 0 and result = firstInstr or + ( + index > 0 and + not startsBasicBlock(result) and + exists(Instruction predecessor, GotoEdge edge | + predecessor = getInstruction(index - 1) and + result = predecessor.getSuccessor(edge) + ) + ) + } + + final PhiInstruction getAPhiInstruction() { + Construction::getPhiInstructionBlockStart(result) = + getFirstInstruction() + } + + final Instruction getAnInstruction() { + result = getInstruction(_) or + result = getAPhiInstruction() + } + + final Instruction getFirstInstruction() { + result = firstInstr + } + + final Instruction getLastInstruction() { + result = getInstruction(getInstructionCount() - 1) + } + + final int getInstructionCount() { + result = strictcount(getInstruction(_)) + } + + final FunctionIR getFunctionIR() { + result = firstInstr.getFunctionIR() + } + + final Function getFunction() { + result = firstInstr.getFunction() + } + + final IRBlock getASuccessor() { + result.getFirstInstruction() = getLastInstruction().getASuccessor() + } + + final IRBlock getAPredecessor() { + firstInstr = result.getLastInstruction().getASuccessor() + } + + final IRBlock getSuccessor(EdgeKind kind) { + result.getFirstInstruction() = getLastInstruction().getSuccessor(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) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/IRImpl.qll b/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/IRImpl.qll new file mode 100644 index 000000000000..97c027cc1280 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/IRImpl.qll @@ -0,0 +1,7 @@ +import FunctionIR +import Instruction +import IRBlock +import IRVariable +import OperandTag +import semmle.code.cpp.ir.EdgeKind +import semmle.code.cpp.ir.MemoryAccessKind diff --git a/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/IRInternal.qll b/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/IRInternal.qll new file mode 100644 index 000000000000..7517660a58f0 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/IRInternal.qll @@ -0,0 +1 @@ +import SSAConstruction as Construction diff --git a/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/IRSanityImpl.qll b/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/IRSanityImpl.qll new file mode 100644 index 000000000000..744b28704604 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/IRSanityImpl.qll @@ -0,0 +1,3 @@ +private import IRImpl +import InstructionSanity + diff --git a/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/IRVariable.qll b/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/IRVariable.qll new file mode 100644 index 000000000000..a903e227b26c --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/IRVariable.qll @@ -0,0 +1,202 @@ +private import IRInternal +import FunctionIR +import cpp +import semmle.code.cpp.ir.TempVariableTag +private import semmle.code.cpp.ir.internal.TempVariableTag + +private newtype TIRVariable = + TIRAutomaticUserVariable(LocalScopeVariable var, FunctionIR funcIR) { + exists(Function func | + func = funcIR.getFunction() and + ( + var.getFunction() = func or + var.(Parameter).getCatchBlock().getEnclosingFunction() = func + ) + ) + } or + TIRStaticUserVariable(Variable var, FunctionIR funcIR) { + ( + var instanceof GlobalOrNamespaceVariable or + var instanceof MemberVariable and not var instanceof Field + ) and + exists(VariableAccess access | + access.getTarget() = var and + access.getEnclosingFunction() = funcIR.getFunction() + ) + } or + TIRTempVariable(FunctionIR funcIR, Locatable ast, TempVariableTag tag, + Type type) { + Construction::hasTempVariable(funcIR.getFunction(), ast, tag, type) + } + +IRUserVariable getIRUserVariable(Function func, Variable var) { + result.getVariable() = var and + result.getFunction() = func +} + +/** + * Represents 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`). + */ +abstract class IRVariable extends TIRVariable { + FunctionIR funcIR; + + abstract string toString(); + + /** + * Gets the type of the variable. + */ + abstract Type getType(); + + /** + * Gets the AST node that declared this variable, or that introduced this + * variable as part of the AST-to-IR translation. + */ + abstract Locatable getAST(); + + /** + * Gets an identifier string for the variable. This identifier is unique + * within the function. + */ + abstract string getUniqueId(); + + /** + * Gets the source location of this variable. + */ + final Location getLocation() { + result = getAST().getLocation() + } + + /** + * Gets the IR for the function that references this variable. + */ + final FunctionIR getFunctionIR() { + result = funcIR + } + + /** + * Gets the function that references this variable. + */ + final Function getFunction() { + result = funcIR.getFunction() + } +} + +/** + * Represents a user-declared variable referenced by the IR for a function. + */ +abstract class IRUserVariable extends IRVariable { + Variable var; + + override final string toString() { + result = var.toString() + } + + override final Type getType() { + result = var.getType().getUnspecifiedType() + } + + override final Locatable getAST() { + result = var + } + + override final string getUniqueId() { + result = var.toString() + " " + var.getLocation().toString() + } + + /** + * Gets the original user-declared variable. + */ + final Variable getVariable() { + result = var + } +} + +/** + * Represents a variable (user-declared or temporary) that is allocated on the + * stack. This includes all parameters, non-static local variables, and + * temporary variables. + */ +abstract class IRAutomaticVariable extends IRVariable { +} + +class IRAutomaticUserVariable extends IRUserVariable, IRAutomaticVariable, + TIRAutomaticUserVariable { + LocalScopeVariable localVar; + + IRAutomaticUserVariable() { + this = TIRAutomaticUserVariable(localVar, funcIR) and + var = localVar + } + + final LocalScopeVariable getLocalVariable() { + result = localVar + } +} + +class IRStaticUserVariable extends IRUserVariable, TIRStaticUserVariable { + IRStaticUserVariable() { + this = TIRStaticUserVariable(var, funcIR) + } +} + +IRTempVariable getIRTempVariable(Locatable ast, TempVariableTag tag) { + result.getAST() = ast and + result.getTag() = tag +} + +class IRTempVariable extends IRVariable, IRAutomaticVariable, TIRTempVariable { + Locatable ast; + TempVariableTag tag; + Type type; + + IRTempVariable() { + this = TIRTempVariable(funcIR, ast, tag, type) + } + + override final Type getType() { + result = type + } + + override final Locatable getAST() { + result = ast + } + + override final string getUniqueId() { + result = "Temp: " + Construction::getTempVariableUniqueId(this) + } + + final TempVariableTag getTag() { + result = tag + } + + override string toString() { + result = getBaseString() + ast.getLocation().getStartLine().toString() + ":" + + ast.getLocation().getStartColumn().toString() + } + + string getBaseString() { + result = "#temp" + } +} + +class IRReturnVariable extends IRTempVariable { + IRReturnVariable() { + tag = ReturnValueTempVar() + } + + override final string toString() { + result = "#return" + } +} + +class IRThrowVariable extends IRTempVariable { + IRThrowVariable() { + tag = ThrowTempVar() + } + + override string getBaseString() { + result = "#throw" + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/Instruction.qll new file mode 100644 index 000000000000..d00999f894aa --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/Instruction.qll @@ -0,0 +1,978 @@ +private import IRInternal +import FunctionIR +import IRBlock +import IRVariable +import OperandTag +import cpp +import semmle.code.cpp.ir.EdgeKind +import semmle.code.cpp.ir.MemoryAccessKind +import semmle.code.cpp.ir.Opcode +private import semmle.code.cpp.ir.internal.Opcode + +class InstructionTag = Construction::InstructionTagType; + +module InstructionSanity { + /** + * Holds if the instruction `instr` should be expected to have an operand + * with operand tag `tag`. Only holds for singleton operand tags. Tags with + * parameters, such as `PhiOperand` and `PositionalArgumentOperand` are handled + * separately in `unexpectedOperand`. + */ + private predicate expectsOperand(Instruction instr, OperandTag tag) { + exists(Opcode opcode | + opcode = instr.getOpcode() and + ( + opcode instanceof UnaryOpcode and tag instanceof UnaryOperand or + ( + opcode instanceof BinaryOpcode and + ( + tag instanceof LeftOperand or + tag instanceof RightOperand + ) + ) or + opcode instanceof CopyOpcode and tag instanceof CopySourceOperand or + opcode instanceof MemoryAccessOpcode and tag instanceof LoadStoreAddressOperand or + opcode instanceof OpcodeWithCondition and tag instanceof ConditionOperand or + opcode instanceof Opcode::ReturnValue and tag instanceof ReturnValueOperand or + opcode instanceof Opcode::ThrowValue and tag instanceof ExceptionOperand or + opcode instanceof Opcode::UnmodeledUse and tag instanceof UnmodeledUseOperand or + opcode instanceof Opcode::Invoke and tag instanceof CallTargetOperand + ) + ) + } + + /** + * Holds if instruction `instr` is missing an expected operand with tag `tag`. + */ + query predicate missingOperand(Instruction instr, OperandTag tag) { + expectsOperand(instr, tag) and not exists(instr.getOperand(tag)) + } + + /** + * Holds if instruction `instr` has an unexpected operand with tag `tag`. + */ + query predicate unexpectedOperand(Instruction instr, OperandTag tag) { + exists(instr.getOperand(tag)) and + not expectsOperand(instr, tag) and + not (instr instanceof InvokeInstruction and tag instanceof ArgumentOperand) and + not (instr instanceof BuiltInInstruction and tag instanceof PositionalArgumentOperand) and + not (instr instanceof PhiInstruction and tag instanceof PhiOperand) + } + + /** + * Holds if instruction `instr` has multiple operands with tag `tag`. + */ + query predicate duplicateOperand(Instruction instr, OperandTag tag) { + count(instr.getOperand(tag)) > 1 and + not tag instanceof UnmodeledUseOperand + } +} + +/** + * Represents a single operation in the IR. + */ +class Instruction extends Construction::TInstruction { + Opcode opcode; + Locatable ast; + InstructionTag instructionTag; + Type resultType; + FunctionIR funcIR; + boolean glvalue; + + Instruction() { + this = Construction::MkInstruction(funcIR, opcode, ast, instructionTag, resultType, glvalue) + } + + string toString() { + result = opcode.toString() + } + + /** + * 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 Function getFunction() { + result = funcIR.getFunction() + } + + /** + * Gets the FunctionIR object that contains the IR for this instruction. + */ + final FunctionIR getFunctionIR() { + result = funcIR + } + + /** + * Gets the AST that caused this instruction to be generated. + */ + final Locatable getAST() { + result = ast + } + + /** + * Gets the location of the source code for this instruction. + */ + final Location getLocation() { + result = ast.getLocation() + } + + /** + * Gets the type of the result produced by this instruction. If the + * instruction does not produce a result, its result type will be `VoidType`. + */ + final Type getResultType() { + result = resultType + } + + /** + * 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() { + glvalue = true + } + + /** + * Gets the size of the result produced by this instruction, in bytes. If the + * instruction does not produce a result, or 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() { + if isGLValue() then ( + // a glvalue is always pointer-sized. + exists(NullPointerType nullptr | + result = nullptr.getSize() + ) + ) + else if resultType instanceof UnknownType then + result = Construction::getInstructionResultSize(this) + else ( + not resultType instanceof VoidType and + result = resultType.getSize() + ) + } + + /** + * Gets the opcode that specifies the operation performed by this instruction. + */ + final Opcode getOpcode() { + result = opcode + } + + final InstructionTag getTag() { + result = instructionTag + } + + /** + * Gets the instruction that produced the value of the specified source + * operand. + */ + final Instruction getOperand(OperandTag tag) { + result = Construction::getInstructionOperand(this, tag) + } + + /** + * Gets all instructions consumed by this instruction's operands. + */ + final Instruction getAnOperand() { + result = getOperand(_) + } + + /** + * Holds if this instruction has a memory operand with the specified tag. + */ + final predicate isMemoryOperand(OperandTag tag) { + exists(getOperandMemoryAccess(tag)) + } + + /** + * Gets the kind of memory access performed by the specified operand. Holds + * only for memory operands. + */ + MemoryAccessKind getOperandMemoryAccess(OperandTag tag) { + none() + } + + /** + * 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. + */ + MemoryAccessKind getResultMemoryAccess() { + none() + } + + /** + * 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 is connected to the + * `UnmodeledUse` instruction. + * + * 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 + // An unmodeled result will have a use on the `UnmodeledUse` instruction. + not exists(UnmodeledUseOperand useTag | + hasUse(_, useTag) + ) + } + + /** + * 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 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(_) + } + + /** + * Holds if the result of this instruction is consumed by `useInstruction` as + * an operand with tag `useTag`. + */ + final predicate hasUse(Instruction useInstruction, OperandTag useTag) { + useInstruction.getFunctionIR() = funcIR and + this = useInstruction.getOperand(useTag) + } +} + +class VariableInstruction extends Instruction { + IRVariable var; + + VariableInstruction() { + var = Construction::getInstructionVariable(this) + } + + override final string toString() { + result = super.toString() + "[" + var.toString() + "]" + } + + final IRVariable getVariable() { + result = var + } +} + +class FieldInstruction extends Instruction { + Field field; + + FieldInstruction() { + field = Construction::getInstructionField(this) + } + + override final string toString() { + result = super.toString() + "[" + field.toString() + "]" + } + + final Field getField() { + result = field + } +} + +class FunctionInstruction extends Instruction { + Function funcSymbol; + + FunctionInstruction() { + funcSymbol = Construction::getInstructionFunction(this) + } + + override final string toString() { + result = super.toString() + "[" + funcSymbol.toString() + "]" + } + + final Function getFunctionSymbol() { + result = funcSymbol + } +} + +class ConstantValueInstruction extends Instruction { + string value; + + ConstantValueInstruction() { + value = Construction::getInstructionConstantValue(this) + } + + override final string toString() { + result = super.toString() + "[" + value + "]" + } + + final string getValue() { + result = value + } +} + +class EnterFunctionInstruction extends Instruction { + EnterFunctionInstruction() { + opcode instanceof Opcode::EnterFunction + } +} + +class VariableAddressInstruction extends VariableInstruction { + VariableAddressInstruction() { + opcode instanceof Opcode::VariableAddress + } +} + +class InitializeParameterInstruction extends VariableInstruction { + InitializeParameterInstruction() { + opcode instanceof Opcode::InitializeParameter + } + + final Parameter getParameter() { + result = var.(IRUserVariable).getVariable() + } +} + +class FieldAddressInstruction extends FieldInstruction { + FieldAddressInstruction() { + opcode instanceof Opcode::FieldAddress + } + + final Instruction getObjectAddress() { + result = getOperand(unaryOperand()) + } +} + +class UninitializedInstruction extends Instruction { + UninitializedInstruction() { + opcode instanceof Opcode::Uninitialized + } +} + +class NoOpInstruction extends Instruction { + NoOpInstruction() { + opcode instanceof Opcode::NoOp + } +} + +class ReturnInstruction extends Instruction { + ReturnInstruction() { + opcode instanceof ReturnOpcode + } +} + +class ReturnVoidInstruction extends ReturnInstruction { + ReturnVoidInstruction() { + opcode instanceof Opcode::ReturnVoid + } +} + +class ReturnValueInstruction extends ReturnInstruction { + ReturnValueInstruction() { + opcode instanceof Opcode::ReturnValue + } + + final Instruction getReturnValue() { + result = getOperand(returnValueOperand()) + } + + override final MemoryAccessKind getOperandMemoryAccess(OperandTag tag) { + tag instanceof ReturnValueOperand and + result instanceof IndirectMemoryAccess + } +} + +class CopyInstruction extends Instruction { + CopyInstruction() { + opcode instanceof CopyOpcode + } + + final Instruction getSourceValue() { + result = getOperand(copySourceOperand()) + } +} + +class CopyValueInstruction extends CopyInstruction { + CopyValueInstruction() { + opcode instanceof Opcode::CopyValue + } +} + +class LoadInstruction extends CopyInstruction { + LoadInstruction() { + opcode instanceof Opcode::Load + } + + override final MemoryAccessKind getOperandMemoryAccess(OperandTag tag) { + tag instanceof CopySourceOperand and + result instanceof IndirectMemoryAccess + } + + final Instruction getSourceAddress() { + result = getOperand(loadStoreAddressOperand()) + } +} + +class StoreInstruction extends CopyInstruction { + StoreInstruction() { + opcode instanceof Opcode::Store + } + + override final MemoryAccessKind getResultMemoryAccess() { + result instanceof IndirectMemoryAccess + } + + final Instruction getDestinationAddress() { + result = getOperand(loadStoreAddressOperand()) + } +} + +class ConditionalBranchInstruction extends Instruction { + ConditionalBranchInstruction() { + opcode instanceof Opcode::ConditionalBranch + } + + final Instruction getCondition() { + result = getOperand(conditionOperand()) + } + + final Instruction getTrueSuccessor() { + result = getSuccessor(trueEdge()) + } + + final Instruction getFalseSuccessor() { + result = getSuccessor(falseEdge()) + } +} + +class ExitFunctionInstruction extends Instruction { + ExitFunctionInstruction() { + opcode instanceof Opcode::ExitFunction + } +} + +class ConstantInstruction extends ConstantValueInstruction { + ConstantInstruction() { + opcode instanceof Opcode::Constant + } +} + +class IntegerConstantInstruction extends ConstantInstruction { + IntegerConstantInstruction() { + resultType instanceof IntegralType + } +} + +class FloatConstantInstruction extends ConstantInstruction { + FloatConstantInstruction() { + resultType instanceof FloatingPointType + } +} + +class StringConstantInstruction extends Instruction { + StringLiteral value; + + StringConstantInstruction() { + value = Construction::getInstructionStringLiteral(this) + } + + override final string toString() { + result = super.toString() + "[" + + value.getValueText().replaceAll("\n", " ").replaceAll("\r", "").replaceAll("\t", " ") + + "]" + } + + final StringLiteral getValue() { + result = value + } +} + +class BinaryInstruction extends Instruction { + BinaryInstruction() { + opcode instanceof BinaryOpcode + } + + final Instruction getLeftOperand() { + result = getOperand(leftOperand()) + } + + final Instruction getRightOperand() { + result = getOperand(rightOperand()) + } +} + +class AddInstruction extends BinaryInstruction { + AddInstruction() { + opcode instanceof Opcode::Add + } +} + +class SubInstruction extends BinaryInstruction { + SubInstruction() { + opcode instanceof Opcode::Sub + } +} + +class MulInstruction extends BinaryInstruction { + MulInstruction() { + opcode instanceof Opcode::Mul + } +} + +class DivInstruction extends BinaryInstruction { + DivInstruction() { + opcode instanceof Opcode::Div + } +} + +class RemInstruction extends BinaryInstruction { + RemInstruction() { + opcode instanceof Opcode::Rem + } +} + +class BitAndInstruction extends BinaryInstruction { + BitAndInstruction() { + opcode instanceof Opcode::BitAnd + } +} + +class BitOrInstruction extends BinaryInstruction { + BitOrInstruction() { + opcode instanceof Opcode::BitOr + } +} + +class BitXorInstruction extends BinaryInstruction { + BitXorInstruction() { + opcode instanceof Opcode::BitXor + } +} + +class ShiftLeftInstruction extends BinaryInstruction { + ShiftLeftInstruction() { + opcode instanceof Opcode::ShiftLeft + } +} + +class ShiftRightInstruction extends BinaryInstruction { + ShiftRightInstruction() { + opcode instanceof Opcode::ShiftRight + } +} + +class PointerArithmeticInstruction extends BinaryInstruction { + int elementSize; + + PointerArithmeticInstruction() { + opcode instanceof PointerArithmeticOpcode and + elementSize = Construction::getInstructionElementSize(this) + } + + override final string toString() { + result = super.toString() + "[" + elementSize.toString() + "]" + } + + final int getElementSize() { + result = elementSize + } +} + +class PointerOffsetInstruction extends PointerArithmeticInstruction { + PointerOffsetInstruction() { + opcode instanceof PointerOffsetOpcode + } +} + +class PointerAddInstruction extends PointerOffsetInstruction { + PointerAddInstruction() { + opcode instanceof Opcode::PointerAdd + } +} + +class PointerSubInstruction extends PointerOffsetInstruction { + PointerSubInstruction() { + opcode instanceof Opcode::PointerSub + } +} + +class PointerDiffInstruction extends PointerArithmeticInstruction { + PointerDiffInstruction() { + opcode instanceof Opcode::PointerDiff + } +} + +class UnaryInstruction extends Instruction { + UnaryInstruction() { + opcode instanceof UnaryOpcode + } + + final Instruction getOperand() { + result = getOperand(unaryOperand()) + } +} + +class ConvertInstruction extends UnaryInstruction { + ConvertInstruction() { + opcode instanceof Opcode::Convert + } +} + +/** + * Represents an instruction that converts between two addresses + * related by inheritance. + */ +class InheritanceConversionInstruction extends UnaryInstruction { + Class baseClass; + Class derivedClass; + + InheritanceConversionInstruction() { + Construction::getInstructionInheritance(this, baseClass, derivedClass) + } + + override final string toString() { + result = super.toString() + "[" + 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 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 Class getBaseClass() { + result = baseClass + } + + /** + * Gets the derived class of the conversion. + */ + final Class getDerivedClass() { + result = derivedClass + } +} + +/** + * Represents an instruction that converts from the address of a derived class + * to the address of a direct non-virtual base class. + */ +class ConvertToBaseInstruction extends InheritanceConversionInstruction { + ConvertToBaseInstruction() { + opcode instanceof Opcode::ConvertToBase + } +} + +/** + * Represents an instruction that converts from the address of a derived class + * to the address of a virtual base class. + */ +class ConvertToVirtualBaseInstruction extends InheritanceConversionInstruction { + ConvertToVirtualBaseInstruction() { + opcode 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() { + opcode instanceof Opcode::ConvertToDerived + } +} + +class BitComplementInstruction extends UnaryInstruction { + BitComplementInstruction() { + opcode instanceof Opcode::BitComplement + } +} + +class LogicalNotInstruction extends UnaryInstruction { + LogicalNotInstruction() { + opcode instanceof Opcode::LogicalNot + } +} + +class CompareInstruction extends BinaryInstruction { + CompareInstruction() { + opcode instanceof CompareOpcode + } +} + +class CompareEQInstruction extends CompareInstruction { + CompareEQInstruction() { + opcode instanceof Opcode::CompareEQ + } +} + +class CompareNEInstruction extends CompareInstruction { + CompareNEInstruction() { + opcode instanceof Opcode::CompareNE + } +} + +class CompareLTInstruction extends CompareInstruction { + CompareLTInstruction() { + opcode instanceof Opcode::CompareLT + } +} + +class CompareGTInstruction extends CompareInstruction { + CompareGTInstruction() { + opcode instanceof Opcode::CompareGT + } +} + +class CompareLEInstruction extends CompareInstruction { + CompareLEInstruction() { + opcode instanceof Opcode::CompareLE + } +} + +class CompareGEInstruction extends CompareInstruction { + CompareGEInstruction() { + opcode instanceof Opcode::CompareGE + } +} + +class SwitchInstruction extends Instruction { + SwitchInstruction() { + opcode instanceof Opcode::Switch + } + + final Instruction getExpression() { + result = getOperand(conditionOperand()) + } + + final Instruction getACaseSuccessor() { + exists(CaseEdge edge | + result = getSuccessor(edge) + ) + } + + final Instruction getDefaultSuccessor() { + result = getSuccessor(defaultEdge()) + } +} + +class InvokeInstruction extends Instruction { + InvokeInstruction() { + opcode instanceof Opcode::Invoke + } + + final Instruction getCallTarget() { + result = getOperand(callTargetOperand()) + } +} + +/** + * An instruction that throws an exception. + */ +class ThrowInstruction extends Instruction { + ThrowInstruction() { + opcode instanceof ThrowOpcode + } +} + +/** + * An instruction that throws a new exception. + */ +class ThrowValueInstruction extends ThrowInstruction { + ThrowValueInstruction() { + opcode instanceof Opcode::ThrowValue + } + + override final MemoryAccessKind getOperandMemoryAccess(OperandTag tag) { + tag instanceof ExceptionOperand and + result instanceof IndirectMemoryAccess + } + + + /** + * Gets the address of the exception thrown by this instruction. + */ + final Instruction getExceptionAddress() { + result = getOperand(loadStoreAddressOperand()) + } + + /** + * Gets the exception thrown by this instruction. + */ + final Instruction getException() { + result = getOperand(exceptionOperand()) + } +} + +/** + * An instruction that re-throws the current exception. + */ +class ReThrowInstruction extends ThrowInstruction { + ReThrowInstruction() { + opcode instanceof Opcode::ReThrow + } +} + +/** + * An instruction that exits the current function by propagating an exception. + */ +class UnwindInstruction extends Instruction { + UnwindInstruction() { + opcode instanceof Opcode::Unwind + } +} + +/** + * An instruction that starts a `catch` handler. + */ +class CatchInstruction extends Instruction { + CatchInstruction() { + opcode instanceof CatchOpcode + } +} + +/** + * An instruction that catches an exception of a specific type. + */ +class CatchByTypeInstruction extends CatchInstruction { + Type exceptionType; + + CatchByTypeInstruction() { + opcode instanceof Opcode::CatchByType and + exceptionType = Construction::getInstructionExceptionType(this) + } + + final override string toString() { + result = super.toString() + "[" + exceptionType.toString() + "]" + } + + /** + * Gets the type of exception to be caught. + */ + final Type getExceptionType() { + result = exceptionType + } +} + +/** + * An instruction that catches any exception. + */ +class CatchAnyInstruction extends CatchInstruction { + CatchAnyInstruction() { + opcode instanceof Opcode::CatchAny + } +} + +class UnmodeledDefinitionInstruction extends Instruction { + UnmodeledDefinitionInstruction() { + opcode instanceof Opcode::UnmodeledDefinition + } + + override final MemoryAccessKind getResultMemoryAccess() { + result instanceof UnmodeledMemoryAccess + } +} + +class UnmodeledUseInstruction extends Instruction { + UnmodeledUseInstruction() { + opcode instanceof Opcode::UnmodeledUse + } + + override final MemoryAccessKind getOperandMemoryAccess(OperandTag tag) { + tag instanceof UnmodeledUseOperand and + result instanceof UnmodeledMemoryAccess + } +} + +class PhiInstruction extends Instruction { + PhiInstruction() { + opcode instanceof Opcode::Phi + } + + override final MemoryAccessKind getOperandMemoryAccess(OperandTag tag) { + tag instanceof PhiOperand and + result instanceof PhiMemoryAccess + } + + override final MemoryAccessKind getResultMemoryAccess() { + result instanceof PhiMemoryAccess + } +} + +/** + * An instruction representing a built-in operation. This is used to represent + * operations such as access to variable argument lists. + */ +class BuiltInInstruction extends Instruction { + BuiltInInstruction() { + opcode instanceof BuiltInOpcode + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/OperandTag.qll b/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/OperandTag.qll new file mode 100644 index 000000000000..ece7fb5bd15b --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/OperandTag.qll @@ -0,0 +1,299 @@ +private import IRInternal +import Instruction +import IRBlock +import cpp + +private newtype TOperandTag = + TLoadStoreAddressOperand() or + TCopySourceOperand() or + TUnaryOperand() or + TLeftOperand() or + TRightOperand() or + TReturnValueOperand() or + TExceptionOperand() or + TConditionOperand() or + TUnmodeledUseOperand() or + TCallTargetOperand() or + TThisArgumentOperand() or + TPositionalArgumentOperand(int argIndex) { + argIndex in [0..Construction::getMaxCallArgIndex()] or + exists(BuiltInOperation op | + exists(op.getChild(argIndex)) + ) + } or + TPhiOperand(IRBlock predecessorBlock) { + exists(PhiInstruction phi | + predecessorBlock = Construction::getPhiInstructionBlockStart(phi).getBlock().getAPredecessor() + ) + } + +/** + * Identifies the kind of operand on an instruction. Each `Instruction` has at + * most one operand of any single `OperandTag`. The set of `OperandTag`s used by + * an `Instruction` is determined by the instruction's opcode. + */ +abstract class OperandTag extends TOperandTag { + abstract string toString(); + abstract int getSortOrder(); +} + +// Note: individual subtypes are listed in the order that the operands should +// appear in the operand list of the instruction when printing. + +/** + * The address operand of an instruction that loads or stores a value from + * memory (e.g. `Load`, `Store`). + */ +class LoadStoreAddressOperand extends OperandTag, TLoadStoreAddressOperand { + override final string toString() { + result = "LoadStoreAddress" + } + + override final int getSortOrder() { + result = 0 + } +} + +LoadStoreAddressOperand loadStoreAddressOperand() { + result = TLoadStoreAddressOperand() +} + +/** + * The source value operand of an instruction that copies this value to its + * result (e.g. `Copy`, `Load`, `Store`). + */ +class CopySourceOperand extends OperandTag, TCopySourceOperand { + override final string toString() { + result = "CopySource" + } + + override final int getSortOrder() { + result = 1 + } +} + +CopySourceOperand copySourceOperand() { + result = TCopySourceOperand() +} + +/** + * The sole operand of a unary instruction (e.g. `Convert`, `Negate`). + */ +class UnaryOperand extends OperandTag, TUnaryOperand { + override final string toString() { + result = "Unary" + } + + override final int getSortOrder() { + result = 2 + } +} + +UnaryOperand unaryOperand() { + result = TUnaryOperand() +} + +/** + * The left operand of a binary instruction (e.g. `Add`, `CompareEQ`). + */ +class LeftOperand extends OperandTag, TLeftOperand { + override final string toString() { + result = "Left" + } + + override final int getSortOrder() { + result = 3 + } +} + +LeftOperand leftOperand() { + result = TLeftOperand() +} + +/** + * The right operand of a binary instruction (e.g. `Add`, `CompareEQ`). + */ +class RightOperand extends OperandTag, TRightOperand { + override final string toString() { + result = "Right" + } + + override final int getSortOrder() { + result = 4 + } +} + +RightOperand rightOperand() { + result = TRightOperand() +} + +/** + * The return value operand of a `ReturnValue` instruction. + */ +class ReturnValueOperand extends OperandTag, TReturnValueOperand { + override final string toString() { + result = "ReturnValue" + } + + override final int getSortOrder() { + result = 5 + } +} + +ReturnValueOperand returnValueOperand() { + result = TReturnValueOperand() +} + +/** + * The exception thrown by a `ThrowValue` instruction. + */ +class ExceptionOperand extends OperandTag, TExceptionOperand { + override final string toString() { + result = "Exception" + } + + override final int getSortOrder() { + result = 6 + } +} + +ExceptionOperand exceptionOperand() { + result = TExceptionOperand() +} + +/** + * The condition operand of a `ConditionalBranch` or `Switch` instruction. + */ +class ConditionOperand extends OperandTag, TConditionOperand { + override final string toString() { + result = "Condition" + } + + override final int getSortOrder() { + result = 7 + } +} + +ConditionOperand conditionOperand() { + result = TConditionOperand() +} + +/** + * An operand of the special `UnmodeledUse` instruction, representing a value + * whose set of uses is unknown. + */ +class UnmodeledUseOperand extends OperandTag, TUnmodeledUseOperand { + override final string toString() { + result = "UnmodeledUse" + } + + override final int getSortOrder() { + result = 8 + } +} + +UnmodeledUseOperand unmodeledUseOperand() { + result = TUnmodeledUseOperand() +} + +/** + * The operand representing the target function of an `Invoke` instruction. + */ +class CallTargetOperand extends OperandTag, TCallTargetOperand { + override final string toString() { + result = "CallTarget" + } + + override final int getSortOrder() { + result = 9 + } +} + +CallTargetOperand callTargetOperand() { + result = TCallTargetOperand() +} + +/** + * 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`). + */ +abstract class ArgumentOperand extends OperandTag { +} + +/** + * An operand representing the implicit 'this' argument to a member function + * call. + */ +class ThisArgumentOperand extends ArgumentOperand, TThisArgumentOperand { + ThisArgumentOperand() { + this = TThisArgumentOperand() + } + + override final string toString() { + result = "Arg(this)" + } + + override final int getSortOrder() { + result = 10 + } +} + +ThisArgumentOperand thisArgumentOperand() { + result = TThisArgumentOperand() +} + +/** + * An operand representing an argument to a function call. + */ +class PositionalArgumentOperand extends ArgumentOperand, + TPositionalArgumentOperand { + int argIndex; + + PositionalArgumentOperand() { + this = TPositionalArgumentOperand(argIndex) + } + + override final string toString() { + result = "Arg(" + argIndex + ")" + } + + override final int getSortOrder() { + result = 11 + argIndex + } + + final int getArgIndex() { + result = argIndex + } +} + +PositionalArgumentOperand positionalArgumentOperand(int argIndex) { + result = TPositionalArgumentOperand(argIndex) +} + +/** + * An operand of an SSA `Phi` instruction. + */ +class PhiOperand extends OperandTag, TPhiOperand { + IRBlock predecessorBlock; + + PhiOperand() { + this = TPhiOperand(predecessorBlock) + } + + override final string toString() { + result = "Phi" + } + + override final int getSortOrder() { + result = 11 + } + + final IRBlock getPredecessorBlock() { + result = predecessorBlock + } +} + +PhiOperand phiOperand(IRBlock predecessorBlock) { + result = TPhiOperand(predecessorBlock) +} diff --git a/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/PrintIRImpl.qll b/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/PrintIRImpl.qll new file mode 100644 index 000000000000..9cf77f21c04a --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/PrintIRImpl.qll @@ -0,0 +1,175 @@ +private import IRImpl +import cpp + +private int getInstructionIndexInBlock(Instruction instr) { + exists(IRBlock block | + block = instr.getBlock() and + ( + exists(int index, int phiCount | + phiCount = count(block.getAPhiInstruction()) and + instr = block.getInstruction(index) and + result = index + phiCount + ) or + ( + instr instanceof PhiInstruction and + instr = rank[result + 1](PhiInstruction phiInstr | + phiInstr = block.getAPhiInstruction() | + phiInstr order by phiInstr.getUniqueId() + ) + ) + ) + ) +} + +private string getInstructionResultId(Instruction instr) { + result = getResultPrefix(instr) + getBlockId(instr.getBlock()) + "_" + + getInstructionIndexInBlock(instr).toString() +} + +private string getResultPrefix(Instruction instr) { + if instr.hasMemoryResult() then + if instr.isResultModeled() then + result = "@m" + else + result = "@mu" + else + result = "@r" +} + +/** + * Gets the identifier of the specified function scope. + * Currently just returns the signature of the function. + */ +private string getScopeId(Function func) { + result = func.getFullSignature() +} + +/** + * Gets the unique identifier of the block within its function. + * Currently returns a string representation of an integer in the range + * [0..numBlocks - 1]. + */ +private string getBlockId(IRBlock block) { + exists(int rankIndex | + block = rank[rankIndex + 1](IRBlock funcBlock | + funcBlock.getFunction() = block.getFunction() | + funcBlock order by funcBlock.getUniqueId() + ) and + result = rankIndex.toString() + ) +} + +/** + * Prints the full signature and qualified name for each scope. This is primarily + * so that post-processing tools can identify function overloads, which will have + * different signatures but the same qualified name. + */ +query predicate printIRGraphScopes(string scopeId, string qualifiedName) { + exists(FunctionIR ir, Function func | + func = ir.getFunction() and + scopeId = getScopeId(func) and + qualifiedName = func.getQualifiedName() + ) +} + +query predicate printIRGraphNodes(string scopeId, string blockId, string label, string location) { + exists(IRBlock block | + scopeId = getScopeId(block.getFunction()) and + blockId = getBlockId(block) and + label = "" and + location = "" + ) +} + +query predicate printIRGraphInstructions(string scopeId, string blockId, + string id, string label, string location) { + exists(IRBlock block, Instruction instr | + instr = block.getAnInstruction() and + label = instr.toString() and + location = instr.getLocation().toString() and + scopeId = getScopeId(block.getFunction()) and + blockId = getBlockId(block) and + id = getInstructionIndexInBlock(instr).toString() + ) +} + +query predicate printIRGraphEdges(string scopeId, + string predecessorId, string successorId, string label) { + exists(IRBlock predecessor, IRBlock successor, EdgeKind kind | + scopeId = getScopeId(predecessor.getFunction()) and + predecessor.getSuccessor(kind) = successor and + predecessorId = getBlockId(predecessor) and + successorId = getBlockId(successor) and + label = kind.toString() + ) +} + +private string getValueCategoryString(Instruction instr) { + if instr.isGLValue() then + result = "glval:" + else + result = "" +} + +private string getResultTypeString(Instruction instr) { + exists(Type resultType, string valcat | + resultType = instr.getResultType() and + valcat = getValueCategoryString(instr) and + if resultType instanceof UnknownType and exists(instr.getResultSize()) then + result = valcat + resultType.toString() + "[" + instr.getResultSize().toString() + "]" + else + result = valcat + resultType.toString() + ) +} + +query predicate printIRGraphDestinationOperands(string scopeId, string blockId, + string instructionId, int operandId, string label) { + exists(IRBlock block, Instruction instr | + block.getAnInstruction() = instr and + scopeId = getScopeId(block.getFunction()) and + blockId = getBlockId(block) and + instructionId = getInstructionIndexInBlock(instr).toString() and + not instr.getResultType() instanceof VoidType and + operandId = 0 and + label = getInstructionResultId(instr) + + "(" + getResultTypeString(instr) + ")" + ) +} + +private string getOperandTagLabel(OperandTag tag) { + ( + tag instanceof PhiOperand and + result = "from " + getBlockId(tag.(PhiOperand).getPredecessorBlock()) + ": " + ) + or ( + tag instanceof ThisArgumentOperand and + result = "this:" + ) + or ( + not tag instanceof PhiOperand and + not tag instanceof ThisArgumentOperand and + result = "" + ) +} + +query predicate printIRGraphSourceOperands(string scopeId, string blockId, + string instructionId, int operandId, string label) { + exists(IRBlock block, Instruction instr | + block.getAnInstruction() = instr and + blockId = getBlockId(block) and + scopeId = getScopeId(block.getFunction()) and + instructionId = getInstructionIndexInBlock(instr).toString() and + if (instr instanceof UnmodeledUseInstruction) then ( + operandId = 0 and + label = "@mu*" + ) + else ( + exists(OperandTag tag, Instruction operandInstr | + operandInstr = instr.getOperand(tag) and + operandId = tag.getSortOrder() and + label = getOperandTagLabel(tag) + + getInstructionResultId(operandInstr) + ) + ) + ) +} diff --git a/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/SSAConstruction.qll new file mode 100644 index 000000000000..cfc220ba368e --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/SSAConstruction.qll @@ -0,0 +1,424 @@ +import SSAConstructionInternal +import cpp +private import semmle.code.cpp.ir.internal.Opcode +import NewIR + +import Cached +cached private module Cached { + + private OldIR::OperandTag getOldOperandTag(OperandTag newTag) { + newTag instanceof LoadStoreAddressOperand and result instanceof OldIR::LoadStoreAddressOperand or + newTag instanceof CopySourceOperand and result instanceof OldIR::CopySourceOperand or + newTag instanceof UnaryOperand and result instanceof OldIR::UnaryOperand or + newTag instanceof LeftOperand and result instanceof OldIR::LeftOperand or + newTag instanceof RightOperand and result instanceof OldIR::RightOperand or + newTag instanceof ReturnValueOperand and result instanceof OldIR::ReturnValueOperand or + newTag instanceof ExceptionOperand and result instanceof OldIR::ExceptionOperand or + newTag instanceof ConditionOperand and result instanceof OldIR::ConditionOperand or + newTag instanceof UnmodeledUseOperand and result instanceof OldIR::UnmodeledUseOperand or + newTag instanceof CallTargetOperand and result instanceof OldIR::CallTargetOperand or + newTag instanceof ThisArgumentOperand and result instanceof OldIR::ThisArgumentOperand or + exists(PositionalArgumentOperand newArg | + newArg = newTag and + result.(OldIR::PositionalArgumentOperand).getArgIndex() = newArg.getArgIndex() + ) + } + + private IRBlock getNewBlock(OldIR::IRBlock oldBlock) { + result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction()) + } + + cached newtype TInstructionTag = + WrappedInstructionTag(OldIR::Instruction oldInstruction) or + PhiTag(Alias::VirtualVariable vvar, OldIR::IRBlock block) { + hasPhiNode(vvar, block) + } + + cached class InstructionTagType extends TInstructionTag { + cached final string toString() { + result = "Tag" + } + } + + cached predicate functionHasIR(Function func) { + exists(OldIR::FunctionIR funcIR | + funcIR.getFunction() = func + ) + } + + cached int getMaxCallArgIndex() { + result = max(int argIndex | + exists(OldIR::PositionalArgumentOperand oldOperand | + argIndex = oldOperand.getArgIndex() + ) + ) + } + + cached OldIR::Instruction getOldInstruction(Instruction instr) { + instr.getTag() = WrappedInstructionTag(result) + } + + private Instruction getNewInstruction(OldIR::Instruction instr) { + result.getTag() = WrappedInstructionTag(instr) + } + + private PhiInstruction getPhiInstruction(Function func, OldIR::IRBlock oldBlock, + Alias::VirtualVariable vvar) { + result.getFunction() = func and + result.getAST() = oldBlock.getFirstInstruction().getAST() and + result.getTag() = PhiTag(vvar, oldBlock) + } + + private IRVariable getNewIRVariable(OldIR::IRVariable var) { + result.getFunction() = var.getFunction() and + ( + exists(OldIR::IRUserVariable userVar, IRUserVariable newUserVar | + userVar = var and + newUserVar.getVariable() = userVar.getVariable() and + result = newUserVar + ) or + exists(OldIR::IRTempVariable tempVar, IRTempVariable newTempVar | + tempVar = var and + newTempVar.getAST() = tempVar.getAST() and + newTempVar.getTag() = tempVar.getTag() and + result = newTempVar + ) + ) + } + + cached newtype TInstruction = + MkInstruction(FunctionIR funcIR, Opcode opcode, Locatable ast, + InstructionTag tag, Type resultType, boolean isGLValue) { + hasInstruction(funcIR.getFunction(), opcode, ast, tag, + resultType, isGLValue) + } + + private predicate hasInstruction(Function func, Opcode opcode, Locatable ast, + InstructionTag tag, Type resultType, boolean isGLValue) { + exists(OldIR::Instruction instr | + instr.getFunction() = func and + instr.getOpcode() = opcode and + instr.getAST() = ast and + WrappedInstructionTag(instr) = tag and + instr.getResultType() = resultType and + if instr.isGLValue() then + isGLValue = true + else + isGLValue = false + ) or + exists(OldIR::IRBlock block, Alias::VirtualVariable vvar | + hasPhiNode(vvar, block) and + block.getFunction() = func and + opcode instanceof Opcode::Phi and + ast = block.getFirstInstruction().getAST() and + tag = PhiTag(vvar, block) and + resultType = vvar.getType() and + isGLValue = false + ) + } + + cached predicate hasTempVariable(Function func, Locatable ast, TempVariableTag tag, + Type type) { + exists(OldIR::IRTempVariable var | + var.getFunction() = func and + var.getAST() = ast and + var.getTag() = tag and + var.getType() = type + ) + } + + cached predicate hasModeledMemoryResult(Instruction instruction) { + exists(Alias::getResultMemoryAccess(getOldInstruction(instruction))) or + instruction instanceof PhiInstruction // Phis always have modeled results + } + + cached Instruction getInstructionOperand(Instruction instruction, OperandTag tag) { + exists(OldIR::Instruction oldUse, OldIR::OperandTag oldTag | + oldUse = getOldInstruction(instruction) and + oldTag = getOldOperandTag(tag) and + if oldUse.isMemoryOperand(oldTag) then ( + ( + if exists(Alias::getOperandMemoryAccess(oldUse, oldTag)) then ( + exists(OldIR::IRBlock useBlock, int useRank, Alias::VirtualVariable vvar, + OldIR::IRBlock defBlock, int defRank, int defIndex | + vvar = Alias::getOperandMemoryAccess(oldUse, oldTag).getVirtualVariable() and + hasDefinitionAtRank(vvar, defBlock, defRank, defIndex) and + hasUseAtRank(vvar, useBlock, useRank, oldUse) and + definitionReachesUse(vvar, defBlock, defRank, useBlock, useRank) and + if defIndex >= 0 then + result = getNewInstruction(defBlock.getInstruction(defIndex)) + else + result = getPhiInstruction(instruction.getFunction(), defBlock, vvar) + ) + ) + else ( + result = instruction.getFunctionIR().getUnmodeledDefinitionInstruction() + ) + ) or + // Connect any definitions that are not being modeled in SSA to the + // `UnmodeledUse` instruction. + exists(OldIR::Instruction oldDefinition | + instruction instanceof UnmodeledUseInstruction and + tag instanceof UnmodeledUseOperand and + oldDefinition = oldUse.getOperand(oldTag) and + not exists(Alias::getResultMemoryAccess(oldDefinition)) and + result = getNewInstruction(oldDefinition) + ) + ) + else + result = getNewInstruction(oldUse.getOperand(oldTag)) + ) or + result = getPhiInstructionOperand(instruction.(PhiInstruction), tag.(PhiOperand)) + } + + cached Instruction getPhiInstructionOperand(PhiInstruction instr, PhiOperand tag) { + exists(Alias::VirtualVariable vvar, OldIR::IRBlock phiBlock, + OldIR::IRBlock defBlock, int defRank, int defIndex, OldIR::IRBlock predBlock | + hasPhiNode(vvar, phiBlock) and + predBlock = phiBlock.getAPredecessor() and + instr.getTag() = PhiTag(vvar, phiBlock) and + tag.getPredecessorBlock() = getNewBlock(predBlock) and + hasDefinitionAtRank(vvar, defBlock, defRank, defIndex) and + definitionReachesEndOfBlock(vvar, defBlock, defRank, predBlock) and + if defIndex >= 0 then + result = getNewInstruction(defBlock.getInstruction(defIndex)) + else + result = getPhiInstruction(instr.getFunction(), defBlock, vvar) + ) + } + + cached Instruction getPhiInstructionBlockStart(PhiInstruction instr) { + exists(OldIR::IRBlock oldBlock | + instr.getTag() = PhiTag(_, oldBlock) and + result = getNewInstruction(oldBlock.getFirstInstruction()) + ) + } + + cached Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) { + result = getNewInstruction(getOldInstruction(instruction).getSuccessor(kind)) + } + + cached IRVariable getInstructionVariable(Instruction instruction) { + result = getNewIRVariable(getOldInstruction(instruction).(OldIR::VariableInstruction).getVariable()) + } + + cached Field getInstructionField(Instruction instruction) { + result = getOldInstruction(instruction).(OldIR::FieldInstruction).getField() + } + + cached Function getInstructionFunction(Instruction instruction) { + result = getOldInstruction(instruction).(OldIR::FunctionInstruction).getFunctionSymbol() + } + + cached string getInstructionConstantValue(Instruction instruction) { + result = getOldInstruction(instruction).(OldIR::ConstantValueInstruction).getValue() + } + + cached StringLiteral getInstructionStringLiteral(Instruction instruction) { + result = getOldInstruction(instruction).(OldIR::StringConstantInstruction).getValue() + } + + cached Type getInstructionExceptionType(Instruction instruction) { + result = getOldInstruction(instruction).(OldIR::CatchByTypeInstruction).getExceptionType() + } + + cached int getInstructionElementSize(Instruction instruction) { + result = getOldInstruction(instruction).(OldIR::PointerArithmeticInstruction).getElementSize() + } + + cached int getInstructionResultSize(Instruction instruction) { + // Only return a result for instructions that needed an explicit result size. + instruction.getResultType() instanceof UnknownType and + result = getOldInstruction(instruction).getResultSize() + } + + cached predicate getInstructionInheritance(Instruction instruction, Class baseClass, + Class derivedClass) { + exists(OldIR::InheritanceConversionInstruction oldInstr | + oldInstr = getOldInstruction(instruction) and + baseClass = oldInstr.getBaseClass() and + derivedClass = oldInstr.getDerivedClass() + ) + } + + private predicate ssa_variableUpdate(Alias::VirtualVariable vvar, + OldIR::Instruction instr, OldIR::IRBlock block, int index) { + block.getInstruction(index) = instr and + Alias::getResultMemoryAccess(instr).getVirtualVariable() = vvar + } + + private predicate hasDefinition(Alias::VirtualVariable vvar, OldIR::IRBlock block, int index) { + ( + hasPhiNode(vvar, block) and + index = -1 + ) or + exists(Alias::MemoryAccess access, OldIR::Instruction def | + access = Alias::getResultMemoryAccess(def) and + block.getInstruction(index) = def and + vvar = access.getVirtualVariable() + ) + } + + private predicate defUseRank(Alias::VirtualVariable vvar, OldIR::IRBlock block, int rankIndex, int index) { + index = rank[rankIndex](int j | hasDefinition(vvar, block, j) or hasUse(vvar, _, block, j)) + } + + private predicate hasUse(Alias::VirtualVariable vvar, + OldIR::Instruction use, OldIR::IRBlock block, int index) { + exists(Alias::MemoryAccess access | + access = Alias::getOperandMemoryAccess(use, _) and + block.getInstruction(index) = use and + vvar = access.getVirtualVariable() + ) + } + + private predicate variableLiveOnEntryToBlock(Alias::VirtualVariable vvar, OldIR::IRBlock block) { + exists (int index | hasUse(vvar, _, block, index) | + not exists (int j | ssa_variableUpdate(vvar, _, block, j) | j < index) + ) or + (variableLiveOnExitFromBlock(vvar, block) and not ssa_variableUpdate(vvar, _, block, _)) + } + + pragma[noinline] + private predicate variableLiveOnExitFromBlock(Alias::VirtualVariable vvar, OldIR::IRBlock block) { + variableLiveOnEntryToBlock(vvar, block.getASuccessor()) + } + + /** + * Gets the rank index of a hyphothetical use one instruction past the end of + * the block. This index can be used to determine if a definition reaches the + * end of the block, even if the definition is the last instruction in the + * block. + */ + private int exitRank(Alias::VirtualVariable vvar, OldIR::IRBlock block) { + result = max(int rankIndex | defUseRank(vvar, block, rankIndex, _)) + 1 + } + + private predicate hasDefinitionAtRank(Alias::VirtualVariable vvar, + OldIR::IRBlock block, int rankIndex, int instructionIndex) { + hasDefinition(vvar, block, instructionIndex) and + defUseRank(vvar, block, rankIndex, instructionIndex) + } + + private predicate hasUseAtRank(Alias::VirtualVariable vvar, OldIR::IRBlock block, + int rankIndex, OldIR::Instruction use) { + exists(int index | + hasUse(vvar, use, block, index) and + defUseRank(vvar, block, rankIndex, index) + ) + } + + /** + * Holds if the definition of `vvar` at `(block, defRank)` reaches the rank + * index `reachesRank` in block `block`. + */ + private predicate definitionReachesRank(Alias::VirtualVariable vvar, + OldIR::IRBlock block, int defRank, int reachesRank) { + hasDefinitionAtRank(vvar, block, defRank, _) and + reachesRank <= exitRank(vvar, block) and // Without this, the predicate would be infinite. + ( + // The def always reaches the next use, even if there is also a def on the + // use instruction. + reachesRank = defRank + 1 or + ( + // If the def reached the previous rank, it also reaches the current rank, + // unless there was another def at the previous rank. + definitionReachesRank(vvar, block, defRank, reachesRank - 1) and + not hasDefinitionAtRank(vvar, block, reachesRank - 1, _) + ) + ) + } + + /** + * Holds if the definition of `vvar` at `(defBlock, defRank)` reaches the end of + * block `block`. + */ + private predicate definitionReachesEndOfBlock(Alias::VirtualVariable vvar, + OldIR::IRBlock defBlock, int defRank, OldIR::IRBlock block) { + hasDefinitionAtRank(vvar, defBlock, defRank, _) and + ( + ( + // If we're looking at the def's own block, just see if it reaches the exit + // rank of the block. + block = defBlock and + variableLiveOnExitFromBlock(vvar, defBlock) and + definitionReachesRank(vvar, defBlock, defRank, exitRank(vvar, defBlock)) + ) or + exists(OldIR::IRBlock idom | + definitionReachesEndOfBlock(vvar, defBlock, defRank, idom) and + noDefinitionsSinceIDominator(vvar, idom, block) + ) + ) + } + + pragma[noinline] + private predicate noDefinitionsSinceIDominator(Alias::VirtualVariable vvar, + OldIR::IRBlock idom, OldIR::IRBlock block) { + idom.immediatelyDominates(block) and // It is sufficient to traverse the dominator graph, cf. discussion above. + variableLiveOnExitFromBlock(vvar, block) and + not hasDefinition(vvar, block, _) + } + + private predicate definitionReachesUseWithinBlock( + Alias::VirtualVariable vvar, OldIR::IRBlock defBlock, int defRank, + OldIR::IRBlock useBlock, int useRank) { + defBlock = useBlock and + hasDefinitionAtRank(vvar, defBlock, defRank, _) and + hasUseAtRank(vvar, useBlock, useRank, _) and + definitionReachesRank(vvar, defBlock, defRank, useRank) + } + + private predicate definitionReachesUse(Alias::VirtualVariable vvar, + OldIR::IRBlock defBlock, int defRank, OldIR::IRBlock useBlock, int useRank) { + hasUseAtRank(vvar, useBlock, useRank, _) and + ( + definitionReachesUseWithinBlock(vvar, defBlock, defRank, useBlock, + useRank) or + ( + definitionReachesEndOfBlock(vvar, defBlock, defRank, + useBlock.getAPredecessor()) and + not definitionReachesUseWithinBlock(vvar, useBlock, _, useBlock, useRank) + ) + ) + } + + private predicate hasFrontierPhiNode(Alias::VirtualVariable vvar, + OldIR::IRBlock phiBlock) { + exists(OldIR::IRBlock defBlock | + phiBlock = defBlock.dominanceFrontier() and + hasDefinition(vvar, defBlock, _) and + /* We can also eliminate those nodes where the variable is not live on any incoming edge */ + variableLiveOnEntryToBlock(vvar, phiBlock) + ) + } + + private predicate hasPhiNode(Alias::VirtualVariable vvar, + OldIR::IRBlock phiBlock) { + hasFrontierPhiNode(vvar, phiBlock) + //or ssa_sanitized_custom_phi_node(vvar, block) + } +} + +import CachedForDebugging +cached private module CachedForDebugging { + cached string getTempVariableUniqueId(IRTempVariable var) { + result = getOldTempVariable(var).getUniqueId() + } + + cached string getInstructionUniqueId(Instruction instr) { + exists(OldIR::Instruction oldInstr | + oldInstr = getOldInstruction(instr) and + result = "NonSSA: " + oldInstr.getUniqueId() + ) or + exists(Alias::VirtualVariable vvar, OldIR::IRBlock phiBlock | + instr.getTag() = PhiTag(vvar, phiBlock) and + result = "Phi Block(" + phiBlock.getUniqueId() + "): " + vvar.getUniqueId() + ) + } + + private OldIR::IRTempVariable getOldTempVariable(IRTempVariable var) { + result.getFunction() = var.getFunction() and + result.getAST() = var.getAST() and + result.getTag() = var.getTag() + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/SSAConstructionInternal.qll b/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/SSAConstructionInternal.qll new file mode 100644 index 000000000000..32b79304b11b --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/SSAConstructionInternal.qll @@ -0,0 +1,3 @@ +import semmle.code.cpp.ssa.SSAIR as OldIR +import semmle.code.cpp.ssa.AliasedSSAIR as NewIR +import SimpleSSA as Alias diff --git a/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/SimpleSSA.qll b/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/SimpleSSA.qll new file mode 100644 index 000000000000..27287312f16a --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/SimpleSSA.qll @@ -0,0 +1,83 @@ +import SimpleSSAInternal +import cpp +import Alias +import IR +import semmle.code.cpp.ssa.internal.Overlap + +private newtype TVirtualVariable = + MkVirtualVariable(IRVariable var) { + not variableAddressEscapes(var) + } + +private VirtualVariable getVirtualVariable(IRVariable var) { + result.getIRVariable() = var +} + +class VirtualVariable extends TVirtualVariable { + IRVariable var; + + VirtualVariable() { + this = MkVirtualVariable(var) + } + + final string toString() { + result = var.toString() + } + + final IRVariable getIRVariable() { + result = var + } + + // REVIEW: This should just be on MemoryAccess + final Type getType() { + result = var.getType() + } + + final string getUniqueId() { + result = var.getUniqueId() + } +} + +private newtype TMemoryAccess = + MkMemoryAccess(VirtualVariable vvar) + +private MemoryAccess getMemoryAccess(IRVariable var) { + result.getVirtualVariable() = getVirtualVariable(var) +} + +class MemoryAccess extends TMemoryAccess { + VirtualVariable vvar; + + MemoryAccess() { + this = MkMemoryAccess(vvar) + } + + string toString() { + result = vvar.toString() + } + + VirtualVariable getVirtualVariable() { + result = vvar + } +} + +Overlap getOverlap(MemoryAccess def, MemoryAccess use) { + def.getVirtualVariable() = use.getVirtualVariable() and + result instanceof MustExactlyOverlap +} + +MemoryAccess getResultMemoryAccess(Instruction instr) { + exists(IRVariable var | + instr.getResultMemoryAccess() instanceof IndirectMemoryAccess and + resultPointsTo(instr.getOperand(loadStoreAddressOperand()), var, 0) and + result = getMemoryAccess(var) + ) +} + +MemoryAccess getOperandMemoryAccess(Instruction instr, OperandTag tag) { + exists(IRVariable var | + instr.getOperandMemoryAccess(tag) instanceof IndirectMemoryAccess and + resultPointsTo(instr.getOperand(loadStoreAddressOperand()), var, 0) and + result = getMemoryAccess(var) + ) +} diff --git a/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/SimpleSSAInternal.qll b/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/SimpleSSAInternal.qll new file mode 100644 index 000000000000..c3dfbc26594c --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/SimpleSSAInternal.qll @@ -0,0 +1,3 @@ +import AliasAnalysis as Alias +import semmle.code.cpp.ssa.SSAIR as IR + diff --git a/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/AliasAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/AliasAnalysis.qll new file mode 100644 index 000000000000..c5fd6e1d5ae6 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/AliasAnalysis.qll @@ -0,0 +1,214 @@ +private import AliasAnalysisInternal +import cpp +private import IR +private import semmle.code.cpp.ssa.internal.IntegerConstant as Ints + +private class IntValue = Ints::IntValue; + +/** + * Converts the bit count in `bits` to a byte count and a bit count in the form + * bytes:bits. + */ +bindingset[bits] +string bitsToBytesAndBits(int bits) { + result = (bits / 8).toString() + ":" + (bits % 8).toString() +} + +/** + * Gets a printable string for a bit offset with possibly unknown value. + */ +bindingset[bitOffset] +string getBitOffsetString(IntValue bitOffset) { + if Ints::hasValue(bitOffset) then + if bitOffset >= 0 then + result = "+" + bitsToBytesAndBits(bitOffset) + else + result = "-" + bitsToBytesAndBits(Ints::neg(bitOffset)) + else + result = "+?" +} + +/** + * Gets the offset of field `field` in bits. + */ +private IntValue getFieldBitOffset(Field field) { + if (field instanceof BitField) then ( + result = Ints::add(Ints::mul(field.getByteOffset(), 8), + field.(BitField).getBitOffset()) + ) + else ( + result = Ints::mul(field.getByteOffset(), 8) + ) +} + +/** + * Holds if the operand `tag` of instruction `instr` is used in a way that does + * not result in any address held in that operand from escaping beyond the + * instruction. + */ +predicate operandIsConsumedWithoutEscaping(Instruction instr, OperandTag tag) { + exists(instr.getOperand(tag)) and + ( + // The source/destination address of a Load/Store does not escape (but the + // loaded/stored value could). + tag instanceof LoadStoreAddressOperand or + // Neither operand of a Compare escapes. + instr instanceof CompareInstruction or + // Neither operand of a PointerDiff escapes. + instr instanceof PointerDiffInstruction or + // Converting an address to a `bool` does not escape the address. + instr.(ConvertInstruction).getResultType() instanceof BoolType + ) +} + +/** + * If the result of instruction `instr` is an integer constant, returns the + * value of that constant. Otherwise, returns unknown. + */ +IntValue getConstantValue(Instruction instr) { + if instr instanceof IntegerConstantInstruction then + result = instr.(IntegerConstantInstruction).getValue().toInt() + else + result = Ints::unknown() +} + +/** + * Computes the offset, in bits, by which the result of `instr` differs from the + * pointer argument to `instr`, if that offset is a constant. Otherwise, returns + * unknown. + */ +IntValue getPointerBitOffset(PointerOffsetInstruction instr) { + exists(IntValue bitOffset | + ( + bitOffset = Ints::mul(Ints::mul(getConstantValue(instr.getRightOperand()), + instr.getElementSize()), 8) + ) and + ( + instr instanceof PointerAddInstruction and result = bitOffset or + instr instanceof PointerSubInstruction and result = Ints::neg(bitOffset) + ) + ) +} + +/** + * Holds if any address held in operand `tag` of instruction `instr` is + * propagated to the result of `instr`, offset by the number of bits in + * `bitOffset`. If the address is propagated, but the offset is not known to be + * a constant, then `bitOffset` is unknown. + */ +predicate operandIsPropagated(Instruction instr, OperandTag tag, + IntValue bitOffset) { + exists(instr.getOperand(tag)) and + ( + // Converting to a non-virtual base class adds the offset of the base class. + exists(ConvertToBaseInstruction convert | + convert = instr and + bitOffset = Ints::mul(convert.getDerivation().getByteOffset(), 8) + ) or + // Converting to a derived class subtracts the offset of the base class. + exists(ConvertToDerivedInstruction convert | + convert = instr and + bitOffset = Ints::neg(Ints::mul(convert.getDerivation().getByteOffset(), 8)) + ) or + // Converting to a virtual base class adds an unknown offset. + ( + instr instanceof ConvertToVirtualBaseInstruction and + bitOffset = Ints::unknown() + ) or + // Conversion to another pointer type propagates the source address. + exists(ConvertInstruction convert, Type resultType | + convert = instr and + resultType = convert.getResultType() and + ( + resultType instanceof PointerType or + resultType instanceof Class //REVIEW: Remove when all glvalues are pointers + ) and + bitOffset = 0 + ) or + // Adding an integer to or subtracting an integer from a pointer propagates + // the address with an offset. + bitOffset = getPointerBitOffset(instr.(PointerOffsetInstruction)) or + // Computing a field address from a pointer propagates the address plus the + // offset of the field. + bitOffset = getFieldBitOffset(instr.(FieldAddressInstruction).getField()) or + // A copy propagates the source value. + tag instanceof CopySourceOperand and bitOffset = 0 + ) +} + +/** + * Holds if any address held in operand number `tag` of instruction `instr` + * escapes outside the domain of the analysis. + */ +predicate operandEscapes(Instruction instr, OperandTag tag) { + exists(instr.getOperand(tag)) and + // Conservatively assume that the address escapes unless one of the following + // holds: + not ( + // The operand is used in a way that does not escape the instruction + operandIsConsumedWithoutEscaping(instr, tag) or + // The address is propagated to the result of the instruction, but that + // result does not itself escape. + operandIsPropagated(instr, tag, _) and not resultEscapes(instr) + ) +} + +/** + * Holds if any address held in the result of instruction `instr` escapes + * outside the domain of the analysis. + */ +predicate resultEscapes(Instruction instr) { + // The result escapes if it has at least one use that escapes. + exists(Instruction useInstr, OperandTag useOperandTag | + instr.hasUse(useInstr, useOperandTag) and + operandEscapes(useInstr, useOperandTag) + ) +} + +/** + * Holds if the address of the specified local variable or parameter escapes the + * domain of the analysis. + */ +private predicate automaticVariableAddressEscapes(IRAutomaticVariable var) { + exists(FunctionIR funcIR | + funcIR = var.getFunctionIR() and + // The variable's address escapes if the result of any + // VariableAddressInstruction that computes the variable's address escapes. + exists(VariableAddressInstruction instr | + instr.getFunctionIR() = funcIR and + instr.getVariable() = var and + resultEscapes(instr) + ) + ) +} + +/** + * Holds if the address of the specified variable escapes the domain of the + * analysis. + */ +predicate variableAddressEscapes(IRVariable var) { + automaticVariableAddressEscapes(var.(IRAutomaticVariable)) or + // All variables with static storage duration have their address escape. + not var instanceof IRAutomaticVariable +} + +/** + * Holds if the result of instruction `instr` points within variable `var`, at + * bit offset `bitOffset` within the variable. If the result points within + * `var`, but at an unknown or non-constant offset, then `bitOffset` is unknown. + */ +predicate resultPointsTo(Instruction instr, IRVariable var, IntValue bitOffset) { + ( + // The address of a variable points to that variable, at offset 0. + instr.(VariableAddressInstruction).getVariable() = var and + bitOffset = 0 + ) or + exists(OperandTag operandTag, IntValue originalBitOffset, + IntValue propagatedBitOffset | + // If an operand is propagated, then the result points to the same variable, + // offset by the bit offset from the propagation. + resultPointsTo(instr.getOperand(operandTag), var, originalBitOffset) and + operandIsPropagated(instr, operandTag, propagatedBitOffset) and + bitOffset = Ints::add(originalBitOffset, propagatedBitOffset) + ) +} diff --git a/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/AliasAnalysisInternal.qll b/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/AliasAnalysisInternal.qll new file mode 100644 index 000000000000..b7541fac50ee --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/AliasAnalysisInternal.qll @@ -0,0 +1 @@ +import semmle.code.cpp.ir.IR as IR diff --git a/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/FunctionIR.qll b/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/FunctionIR.qll new file mode 100644 index 000000000000..ba8f524f202f --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/FunctionIR.qll @@ -0,0 +1,91 @@ +private import IRInternal +import Instruction +import cpp + +private newtype TFunctionIR = + MkFunctionIR(Function func) { + Construction::functionHasIR(func) + } + +/** + * Represents the IR for a function. + */ +class FunctionIR extends TFunctionIR { + Function func; + + FunctionIR() { + this = MkFunctionIR(func) + } + + final string toString() { + result = "IR: " + func.toString() + } + + /** + * Gets the function whose IR is represented. + */ + final Function getFunction() { + result = func + } + + /** + * Gets the location of the function. + */ + final Location getLocation() { + result = func.getLocation() + } + + /** + * Gets the entry point for this function. + */ + final EnterFunctionInstruction getEnterFunctionInstruction() { + result.getFunctionIR() = this + } + + /** + * Gets the exit point for this function. + */ + final ExitFunctionInstruction getExitFunctionInstruction() { + result.getFunctionIR() = this + } + + final UnmodeledDefinitionInstruction getUnmodeledDefinitionInstruction() { + result.getFunctionIR() = this + } + + /** + * Gets the single return instruction for this function. + */ + final ReturnInstruction getReturnInstruction() { + result.getFunctionIR() = 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. + */ + final IRReturnVariable getReturnVariable() { + result.getFunctionIR() = this + } + + /** + * Gets the block containing the entry point of this function. + */ + final IRBlock getEntryBlock() { + result.getFirstInstruction() = getEnterFunctionInstruction() + } + + /** + * Gets all instructions in this function. + */ + final Instruction getAnInstruction() { + result.getFunctionIR() = this + } + + /** + * Gets all blocks in this function. + */ + final IRBlock getABlock() { + result.getFunctionIR() = this + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/IRBlock.qll b/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/IRBlock.qll new file mode 100644 index 000000000000..98ec650036db --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/IRBlock.qll @@ -0,0 +1,131 @@ +private import IRInternal +import Instruction +import cpp +import semmle.code.cpp.ir.EdgeKind + +private predicate startsBasicBlock(Instruction instr) { + not instr instanceof PhiInstruction and + ( + count(Instruction predecessor | + instr = predecessor.getASuccessor() + ) != 1 or // Multiple predecessors or no predecessor + exists(Instruction predecessor | + instr = predecessor.getASuccessor() and + strictcount(Instruction other | + other = predecessor.getASuccessor() + ) > 1 + ) or // Predecessor has multiple successors + exists(Instruction predecessor, EdgeKind kind | + instr = predecessor.getSuccessor(kind) and + not kind instanceof GotoEdge + ) // Incoming edge is not a GotoEdge + ) +} + +private newtype TIRBlock = + MkIRBlock(Instruction firstInstr) { + startsBasicBlock(firstInstr) + } + +cached private predicate isEntryBlock(IRBlock block) { + block.getFirstInstruction() instanceof EnterFunctionInstruction +} + +cached private predicate blockSuccessor(IRBlock pred, IRBlock succ) { + succ = pred.getASuccessor() +} + +private predicate blockImmediatelyDominates(IRBlock dominator, IRBlock block) = + idominance(isEntryBlock/1, blockSuccessor/2)(_, dominator, block) + +class IRBlock extends TIRBlock { + Instruction firstInstr; + + IRBlock() { + this = MkIRBlock(firstInstr) + } + + final string toString() { + result = firstInstr.toString() + } + + final Location getLocation() { + result = getFirstInstruction().getLocation() + } + + final string getUniqueId() { + result = firstInstr.getUniqueId() + } + + final cached Instruction getInstruction(int index) { + index = 0 and result = firstInstr or + ( + index > 0 and + not startsBasicBlock(result) and + exists(Instruction predecessor, GotoEdge edge | + predecessor = getInstruction(index - 1) and + result = predecessor.getSuccessor(edge) + ) + ) + } + + final PhiInstruction getAPhiInstruction() { + Construction::getPhiInstructionBlockStart(result) = + getFirstInstruction() + } + + final Instruction getAnInstruction() { + result = getInstruction(_) or + result = getAPhiInstruction() + } + + final Instruction getFirstInstruction() { + result = firstInstr + } + + final Instruction getLastInstruction() { + result = getInstruction(getInstructionCount() - 1) + } + + final int getInstructionCount() { + result = strictcount(getInstruction(_)) + } + + final FunctionIR getFunctionIR() { + result = firstInstr.getFunctionIR() + } + + final Function getFunction() { + result = firstInstr.getFunction() + } + + final IRBlock getASuccessor() { + result.getFirstInstruction() = getLastInstruction().getASuccessor() + } + + final IRBlock getAPredecessor() { + firstInstr = result.getLastInstruction().getASuccessor() + } + + final IRBlock getSuccessor(EdgeKind kind) { + result.getFirstInstruction() = getLastInstruction().getSuccessor(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) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/IRImpl.qll b/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/IRImpl.qll new file mode 100644 index 000000000000..97c027cc1280 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/IRImpl.qll @@ -0,0 +1,7 @@ +import FunctionIR +import Instruction +import IRBlock +import IRVariable +import OperandTag +import semmle.code.cpp.ir.EdgeKind +import semmle.code.cpp.ir.MemoryAccessKind diff --git a/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/IRInternal.qll b/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/IRInternal.qll new file mode 100644 index 000000000000..7517660a58f0 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/IRInternal.qll @@ -0,0 +1 @@ +import SSAConstruction as Construction diff --git a/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/IRSanityImpl.qll b/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/IRSanityImpl.qll new file mode 100644 index 000000000000..744b28704604 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/IRSanityImpl.qll @@ -0,0 +1,3 @@ +private import IRImpl +import InstructionSanity + diff --git a/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/IRVariable.qll b/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/IRVariable.qll new file mode 100644 index 000000000000..a903e227b26c --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/IRVariable.qll @@ -0,0 +1,202 @@ +private import IRInternal +import FunctionIR +import cpp +import semmle.code.cpp.ir.TempVariableTag +private import semmle.code.cpp.ir.internal.TempVariableTag + +private newtype TIRVariable = + TIRAutomaticUserVariable(LocalScopeVariable var, FunctionIR funcIR) { + exists(Function func | + func = funcIR.getFunction() and + ( + var.getFunction() = func or + var.(Parameter).getCatchBlock().getEnclosingFunction() = func + ) + ) + } or + TIRStaticUserVariable(Variable var, FunctionIR funcIR) { + ( + var instanceof GlobalOrNamespaceVariable or + var instanceof MemberVariable and not var instanceof Field + ) and + exists(VariableAccess access | + access.getTarget() = var and + access.getEnclosingFunction() = funcIR.getFunction() + ) + } or + TIRTempVariable(FunctionIR funcIR, Locatable ast, TempVariableTag tag, + Type type) { + Construction::hasTempVariable(funcIR.getFunction(), ast, tag, type) + } + +IRUserVariable getIRUserVariable(Function func, Variable var) { + result.getVariable() = var and + result.getFunction() = func +} + +/** + * Represents 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`). + */ +abstract class IRVariable extends TIRVariable { + FunctionIR funcIR; + + abstract string toString(); + + /** + * Gets the type of the variable. + */ + abstract Type getType(); + + /** + * Gets the AST node that declared this variable, or that introduced this + * variable as part of the AST-to-IR translation. + */ + abstract Locatable getAST(); + + /** + * Gets an identifier string for the variable. This identifier is unique + * within the function. + */ + abstract string getUniqueId(); + + /** + * Gets the source location of this variable. + */ + final Location getLocation() { + result = getAST().getLocation() + } + + /** + * Gets the IR for the function that references this variable. + */ + final FunctionIR getFunctionIR() { + result = funcIR + } + + /** + * Gets the function that references this variable. + */ + final Function getFunction() { + result = funcIR.getFunction() + } +} + +/** + * Represents a user-declared variable referenced by the IR for a function. + */ +abstract class IRUserVariable extends IRVariable { + Variable var; + + override final string toString() { + result = var.toString() + } + + override final Type getType() { + result = var.getType().getUnspecifiedType() + } + + override final Locatable getAST() { + result = var + } + + override final string getUniqueId() { + result = var.toString() + " " + var.getLocation().toString() + } + + /** + * Gets the original user-declared variable. + */ + final Variable getVariable() { + result = var + } +} + +/** + * Represents a variable (user-declared or temporary) that is allocated on the + * stack. This includes all parameters, non-static local variables, and + * temporary variables. + */ +abstract class IRAutomaticVariable extends IRVariable { +} + +class IRAutomaticUserVariable extends IRUserVariable, IRAutomaticVariable, + TIRAutomaticUserVariable { + LocalScopeVariable localVar; + + IRAutomaticUserVariable() { + this = TIRAutomaticUserVariable(localVar, funcIR) and + var = localVar + } + + final LocalScopeVariable getLocalVariable() { + result = localVar + } +} + +class IRStaticUserVariable extends IRUserVariable, TIRStaticUserVariable { + IRStaticUserVariable() { + this = TIRStaticUserVariable(var, funcIR) + } +} + +IRTempVariable getIRTempVariable(Locatable ast, TempVariableTag tag) { + result.getAST() = ast and + result.getTag() = tag +} + +class IRTempVariable extends IRVariable, IRAutomaticVariable, TIRTempVariable { + Locatable ast; + TempVariableTag tag; + Type type; + + IRTempVariable() { + this = TIRTempVariable(funcIR, ast, tag, type) + } + + override final Type getType() { + result = type + } + + override final Locatable getAST() { + result = ast + } + + override final string getUniqueId() { + result = "Temp: " + Construction::getTempVariableUniqueId(this) + } + + final TempVariableTag getTag() { + result = tag + } + + override string toString() { + result = getBaseString() + ast.getLocation().getStartLine().toString() + ":" + + ast.getLocation().getStartColumn().toString() + } + + string getBaseString() { + result = "#temp" + } +} + +class IRReturnVariable extends IRTempVariable { + IRReturnVariable() { + tag = ReturnValueTempVar() + } + + override final string toString() { + result = "#return" + } +} + +class IRThrowVariable extends IRTempVariable { + IRThrowVariable() { + tag = ThrowTempVar() + } + + override string getBaseString() { + result = "#throw" + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/Instruction.qll new file mode 100644 index 000000000000..d00999f894aa --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/Instruction.qll @@ -0,0 +1,978 @@ +private import IRInternal +import FunctionIR +import IRBlock +import IRVariable +import OperandTag +import cpp +import semmle.code.cpp.ir.EdgeKind +import semmle.code.cpp.ir.MemoryAccessKind +import semmle.code.cpp.ir.Opcode +private import semmle.code.cpp.ir.internal.Opcode + +class InstructionTag = Construction::InstructionTagType; + +module InstructionSanity { + /** + * Holds if the instruction `instr` should be expected to have an operand + * with operand tag `tag`. Only holds for singleton operand tags. Tags with + * parameters, such as `PhiOperand` and `PositionalArgumentOperand` are handled + * separately in `unexpectedOperand`. + */ + private predicate expectsOperand(Instruction instr, OperandTag tag) { + exists(Opcode opcode | + opcode = instr.getOpcode() and + ( + opcode instanceof UnaryOpcode and tag instanceof UnaryOperand or + ( + opcode instanceof BinaryOpcode and + ( + tag instanceof LeftOperand or + tag instanceof RightOperand + ) + ) or + opcode instanceof CopyOpcode and tag instanceof CopySourceOperand or + opcode instanceof MemoryAccessOpcode and tag instanceof LoadStoreAddressOperand or + opcode instanceof OpcodeWithCondition and tag instanceof ConditionOperand or + opcode instanceof Opcode::ReturnValue and tag instanceof ReturnValueOperand or + opcode instanceof Opcode::ThrowValue and tag instanceof ExceptionOperand or + opcode instanceof Opcode::UnmodeledUse and tag instanceof UnmodeledUseOperand or + opcode instanceof Opcode::Invoke and tag instanceof CallTargetOperand + ) + ) + } + + /** + * Holds if instruction `instr` is missing an expected operand with tag `tag`. + */ + query predicate missingOperand(Instruction instr, OperandTag tag) { + expectsOperand(instr, tag) and not exists(instr.getOperand(tag)) + } + + /** + * Holds if instruction `instr` has an unexpected operand with tag `tag`. + */ + query predicate unexpectedOperand(Instruction instr, OperandTag tag) { + exists(instr.getOperand(tag)) and + not expectsOperand(instr, tag) and + not (instr instanceof InvokeInstruction and tag instanceof ArgumentOperand) and + not (instr instanceof BuiltInInstruction and tag instanceof PositionalArgumentOperand) and + not (instr instanceof PhiInstruction and tag instanceof PhiOperand) + } + + /** + * Holds if instruction `instr` has multiple operands with tag `tag`. + */ + query predicate duplicateOperand(Instruction instr, OperandTag tag) { + count(instr.getOperand(tag)) > 1 and + not tag instanceof UnmodeledUseOperand + } +} + +/** + * Represents a single operation in the IR. + */ +class Instruction extends Construction::TInstruction { + Opcode opcode; + Locatable ast; + InstructionTag instructionTag; + Type resultType; + FunctionIR funcIR; + boolean glvalue; + + Instruction() { + this = Construction::MkInstruction(funcIR, opcode, ast, instructionTag, resultType, glvalue) + } + + string toString() { + result = opcode.toString() + } + + /** + * 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 Function getFunction() { + result = funcIR.getFunction() + } + + /** + * Gets the FunctionIR object that contains the IR for this instruction. + */ + final FunctionIR getFunctionIR() { + result = funcIR + } + + /** + * Gets the AST that caused this instruction to be generated. + */ + final Locatable getAST() { + result = ast + } + + /** + * Gets the location of the source code for this instruction. + */ + final Location getLocation() { + result = ast.getLocation() + } + + /** + * Gets the type of the result produced by this instruction. If the + * instruction does not produce a result, its result type will be `VoidType`. + */ + final Type getResultType() { + result = resultType + } + + /** + * 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() { + glvalue = true + } + + /** + * Gets the size of the result produced by this instruction, in bytes. If the + * instruction does not produce a result, or 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() { + if isGLValue() then ( + // a glvalue is always pointer-sized. + exists(NullPointerType nullptr | + result = nullptr.getSize() + ) + ) + else if resultType instanceof UnknownType then + result = Construction::getInstructionResultSize(this) + else ( + not resultType instanceof VoidType and + result = resultType.getSize() + ) + } + + /** + * Gets the opcode that specifies the operation performed by this instruction. + */ + final Opcode getOpcode() { + result = opcode + } + + final InstructionTag getTag() { + result = instructionTag + } + + /** + * Gets the instruction that produced the value of the specified source + * operand. + */ + final Instruction getOperand(OperandTag tag) { + result = Construction::getInstructionOperand(this, tag) + } + + /** + * Gets all instructions consumed by this instruction's operands. + */ + final Instruction getAnOperand() { + result = getOperand(_) + } + + /** + * Holds if this instruction has a memory operand with the specified tag. + */ + final predicate isMemoryOperand(OperandTag tag) { + exists(getOperandMemoryAccess(tag)) + } + + /** + * Gets the kind of memory access performed by the specified operand. Holds + * only for memory operands. + */ + MemoryAccessKind getOperandMemoryAccess(OperandTag tag) { + none() + } + + /** + * 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. + */ + MemoryAccessKind getResultMemoryAccess() { + none() + } + + /** + * 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 is connected to the + * `UnmodeledUse` instruction. + * + * 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 + // An unmodeled result will have a use on the `UnmodeledUse` instruction. + not exists(UnmodeledUseOperand useTag | + hasUse(_, useTag) + ) + } + + /** + * 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 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(_) + } + + /** + * Holds if the result of this instruction is consumed by `useInstruction` as + * an operand with tag `useTag`. + */ + final predicate hasUse(Instruction useInstruction, OperandTag useTag) { + useInstruction.getFunctionIR() = funcIR and + this = useInstruction.getOperand(useTag) + } +} + +class VariableInstruction extends Instruction { + IRVariable var; + + VariableInstruction() { + var = Construction::getInstructionVariable(this) + } + + override final string toString() { + result = super.toString() + "[" + var.toString() + "]" + } + + final IRVariable getVariable() { + result = var + } +} + +class FieldInstruction extends Instruction { + Field field; + + FieldInstruction() { + field = Construction::getInstructionField(this) + } + + override final string toString() { + result = super.toString() + "[" + field.toString() + "]" + } + + final Field getField() { + result = field + } +} + +class FunctionInstruction extends Instruction { + Function funcSymbol; + + FunctionInstruction() { + funcSymbol = Construction::getInstructionFunction(this) + } + + override final string toString() { + result = super.toString() + "[" + funcSymbol.toString() + "]" + } + + final Function getFunctionSymbol() { + result = funcSymbol + } +} + +class ConstantValueInstruction extends Instruction { + string value; + + ConstantValueInstruction() { + value = Construction::getInstructionConstantValue(this) + } + + override final string toString() { + result = super.toString() + "[" + value + "]" + } + + final string getValue() { + result = value + } +} + +class EnterFunctionInstruction extends Instruction { + EnterFunctionInstruction() { + opcode instanceof Opcode::EnterFunction + } +} + +class VariableAddressInstruction extends VariableInstruction { + VariableAddressInstruction() { + opcode instanceof Opcode::VariableAddress + } +} + +class InitializeParameterInstruction extends VariableInstruction { + InitializeParameterInstruction() { + opcode instanceof Opcode::InitializeParameter + } + + final Parameter getParameter() { + result = var.(IRUserVariable).getVariable() + } +} + +class FieldAddressInstruction extends FieldInstruction { + FieldAddressInstruction() { + opcode instanceof Opcode::FieldAddress + } + + final Instruction getObjectAddress() { + result = getOperand(unaryOperand()) + } +} + +class UninitializedInstruction extends Instruction { + UninitializedInstruction() { + opcode instanceof Opcode::Uninitialized + } +} + +class NoOpInstruction extends Instruction { + NoOpInstruction() { + opcode instanceof Opcode::NoOp + } +} + +class ReturnInstruction extends Instruction { + ReturnInstruction() { + opcode instanceof ReturnOpcode + } +} + +class ReturnVoidInstruction extends ReturnInstruction { + ReturnVoidInstruction() { + opcode instanceof Opcode::ReturnVoid + } +} + +class ReturnValueInstruction extends ReturnInstruction { + ReturnValueInstruction() { + opcode instanceof Opcode::ReturnValue + } + + final Instruction getReturnValue() { + result = getOperand(returnValueOperand()) + } + + override final MemoryAccessKind getOperandMemoryAccess(OperandTag tag) { + tag instanceof ReturnValueOperand and + result instanceof IndirectMemoryAccess + } +} + +class CopyInstruction extends Instruction { + CopyInstruction() { + opcode instanceof CopyOpcode + } + + final Instruction getSourceValue() { + result = getOperand(copySourceOperand()) + } +} + +class CopyValueInstruction extends CopyInstruction { + CopyValueInstruction() { + opcode instanceof Opcode::CopyValue + } +} + +class LoadInstruction extends CopyInstruction { + LoadInstruction() { + opcode instanceof Opcode::Load + } + + override final MemoryAccessKind getOperandMemoryAccess(OperandTag tag) { + tag instanceof CopySourceOperand and + result instanceof IndirectMemoryAccess + } + + final Instruction getSourceAddress() { + result = getOperand(loadStoreAddressOperand()) + } +} + +class StoreInstruction extends CopyInstruction { + StoreInstruction() { + opcode instanceof Opcode::Store + } + + override final MemoryAccessKind getResultMemoryAccess() { + result instanceof IndirectMemoryAccess + } + + final Instruction getDestinationAddress() { + result = getOperand(loadStoreAddressOperand()) + } +} + +class ConditionalBranchInstruction extends Instruction { + ConditionalBranchInstruction() { + opcode instanceof Opcode::ConditionalBranch + } + + final Instruction getCondition() { + result = getOperand(conditionOperand()) + } + + final Instruction getTrueSuccessor() { + result = getSuccessor(trueEdge()) + } + + final Instruction getFalseSuccessor() { + result = getSuccessor(falseEdge()) + } +} + +class ExitFunctionInstruction extends Instruction { + ExitFunctionInstruction() { + opcode instanceof Opcode::ExitFunction + } +} + +class ConstantInstruction extends ConstantValueInstruction { + ConstantInstruction() { + opcode instanceof Opcode::Constant + } +} + +class IntegerConstantInstruction extends ConstantInstruction { + IntegerConstantInstruction() { + resultType instanceof IntegralType + } +} + +class FloatConstantInstruction extends ConstantInstruction { + FloatConstantInstruction() { + resultType instanceof FloatingPointType + } +} + +class StringConstantInstruction extends Instruction { + StringLiteral value; + + StringConstantInstruction() { + value = Construction::getInstructionStringLiteral(this) + } + + override final string toString() { + result = super.toString() + "[" + + value.getValueText().replaceAll("\n", " ").replaceAll("\r", "").replaceAll("\t", " ") + + "]" + } + + final StringLiteral getValue() { + result = value + } +} + +class BinaryInstruction extends Instruction { + BinaryInstruction() { + opcode instanceof BinaryOpcode + } + + final Instruction getLeftOperand() { + result = getOperand(leftOperand()) + } + + final Instruction getRightOperand() { + result = getOperand(rightOperand()) + } +} + +class AddInstruction extends BinaryInstruction { + AddInstruction() { + opcode instanceof Opcode::Add + } +} + +class SubInstruction extends BinaryInstruction { + SubInstruction() { + opcode instanceof Opcode::Sub + } +} + +class MulInstruction extends BinaryInstruction { + MulInstruction() { + opcode instanceof Opcode::Mul + } +} + +class DivInstruction extends BinaryInstruction { + DivInstruction() { + opcode instanceof Opcode::Div + } +} + +class RemInstruction extends BinaryInstruction { + RemInstruction() { + opcode instanceof Opcode::Rem + } +} + +class BitAndInstruction extends BinaryInstruction { + BitAndInstruction() { + opcode instanceof Opcode::BitAnd + } +} + +class BitOrInstruction extends BinaryInstruction { + BitOrInstruction() { + opcode instanceof Opcode::BitOr + } +} + +class BitXorInstruction extends BinaryInstruction { + BitXorInstruction() { + opcode instanceof Opcode::BitXor + } +} + +class ShiftLeftInstruction extends BinaryInstruction { + ShiftLeftInstruction() { + opcode instanceof Opcode::ShiftLeft + } +} + +class ShiftRightInstruction extends BinaryInstruction { + ShiftRightInstruction() { + opcode instanceof Opcode::ShiftRight + } +} + +class PointerArithmeticInstruction extends BinaryInstruction { + int elementSize; + + PointerArithmeticInstruction() { + opcode instanceof PointerArithmeticOpcode and + elementSize = Construction::getInstructionElementSize(this) + } + + override final string toString() { + result = super.toString() + "[" + elementSize.toString() + "]" + } + + final int getElementSize() { + result = elementSize + } +} + +class PointerOffsetInstruction extends PointerArithmeticInstruction { + PointerOffsetInstruction() { + opcode instanceof PointerOffsetOpcode + } +} + +class PointerAddInstruction extends PointerOffsetInstruction { + PointerAddInstruction() { + opcode instanceof Opcode::PointerAdd + } +} + +class PointerSubInstruction extends PointerOffsetInstruction { + PointerSubInstruction() { + opcode instanceof Opcode::PointerSub + } +} + +class PointerDiffInstruction extends PointerArithmeticInstruction { + PointerDiffInstruction() { + opcode instanceof Opcode::PointerDiff + } +} + +class UnaryInstruction extends Instruction { + UnaryInstruction() { + opcode instanceof UnaryOpcode + } + + final Instruction getOperand() { + result = getOperand(unaryOperand()) + } +} + +class ConvertInstruction extends UnaryInstruction { + ConvertInstruction() { + opcode instanceof Opcode::Convert + } +} + +/** + * Represents an instruction that converts between two addresses + * related by inheritance. + */ +class InheritanceConversionInstruction extends UnaryInstruction { + Class baseClass; + Class derivedClass; + + InheritanceConversionInstruction() { + Construction::getInstructionInheritance(this, baseClass, derivedClass) + } + + override final string toString() { + result = super.toString() + "[" + 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 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 Class getBaseClass() { + result = baseClass + } + + /** + * Gets the derived class of the conversion. + */ + final Class getDerivedClass() { + result = derivedClass + } +} + +/** + * Represents an instruction that converts from the address of a derived class + * to the address of a direct non-virtual base class. + */ +class ConvertToBaseInstruction extends InheritanceConversionInstruction { + ConvertToBaseInstruction() { + opcode instanceof Opcode::ConvertToBase + } +} + +/** + * Represents an instruction that converts from the address of a derived class + * to the address of a virtual base class. + */ +class ConvertToVirtualBaseInstruction extends InheritanceConversionInstruction { + ConvertToVirtualBaseInstruction() { + opcode 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() { + opcode instanceof Opcode::ConvertToDerived + } +} + +class BitComplementInstruction extends UnaryInstruction { + BitComplementInstruction() { + opcode instanceof Opcode::BitComplement + } +} + +class LogicalNotInstruction extends UnaryInstruction { + LogicalNotInstruction() { + opcode instanceof Opcode::LogicalNot + } +} + +class CompareInstruction extends BinaryInstruction { + CompareInstruction() { + opcode instanceof CompareOpcode + } +} + +class CompareEQInstruction extends CompareInstruction { + CompareEQInstruction() { + opcode instanceof Opcode::CompareEQ + } +} + +class CompareNEInstruction extends CompareInstruction { + CompareNEInstruction() { + opcode instanceof Opcode::CompareNE + } +} + +class CompareLTInstruction extends CompareInstruction { + CompareLTInstruction() { + opcode instanceof Opcode::CompareLT + } +} + +class CompareGTInstruction extends CompareInstruction { + CompareGTInstruction() { + opcode instanceof Opcode::CompareGT + } +} + +class CompareLEInstruction extends CompareInstruction { + CompareLEInstruction() { + opcode instanceof Opcode::CompareLE + } +} + +class CompareGEInstruction extends CompareInstruction { + CompareGEInstruction() { + opcode instanceof Opcode::CompareGE + } +} + +class SwitchInstruction extends Instruction { + SwitchInstruction() { + opcode instanceof Opcode::Switch + } + + final Instruction getExpression() { + result = getOperand(conditionOperand()) + } + + final Instruction getACaseSuccessor() { + exists(CaseEdge edge | + result = getSuccessor(edge) + ) + } + + final Instruction getDefaultSuccessor() { + result = getSuccessor(defaultEdge()) + } +} + +class InvokeInstruction extends Instruction { + InvokeInstruction() { + opcode instanceof Opcode::Invoke + } + + final Instruction getCallTarget() { + result = getOperand(callTargetOperand()) + } +} + +/** + * An instruction that throws an exception. + */ +class ThrowInstruction extends Instruction { + ThrowInstruction() { + opcode instanceof ThrowOpcode + } +} + +/** + * An instruction that throws a new exception. + */ +class ThrowValueInstruction extends ThrowInstruction { + ThrowValueInstruction() { + opcode instanceof Opcode::ThrowValue + } + + override final MemoryAccessKind getOperandMemoryAccess(OperandTag tag) { + tag instanceof ExceptionOperand and + result instanceof IndirectMemoryAccess + } + + + /** + * Gets the address of the exception thrown by this instruction. + */ + final Instruction getExceptionAddress() { + result = getOperand(loadStoreAddressOperand()) + } + + /** + * Gets the exception thrown by this instruction. + */ + final Instruction getException() { + result = getOperand(exceptionOperand()) + } +} + +/** + * An instruction that re-throws the current exception. + */ +class ReThrowInstruction extends ThrowInstruction { + ReThrowInstruction() { + opcode instanceof Opcode::ReThrow + } +} + +/** + * An instruction that exits the current function by propagating an exception. + */ +class UnwindInstruction extends Instruction { + UnwindInstruction() { + opcode instanceof Opcode::Unwind + } +} + +/** + * An instruction that starts a `catch` handler. + */ +class CatchInstruction extends Instruction { + CatchInstruction() { + opcode instanceof CatchOpcode + } +} + +/** + * An instruction that catches an exception of a specific type. + */ +class CatchByTypeInstruction extends CatchInstruction { + Type exceptionType; + + CatchByTypeInstruction() { + opcode instanceof Opcode::CatchByType and + exceptionType = Construction::getInstructionExceptionType(this) + } + + final override string toString() { + result = super.toString() + "[" + exceptionType.toString() + "]" + } + + /** + * Gets the type of exception to be caught. + */ + final Type getExceptionType() { + result = exceptionType + } +} + +/** + * An instruction that catches any exception. + */ +class CatchAnyInstruction extends CatchInstruction { + CatchAnyInstruction() { + opcode instanceof Opcode::CatchAny + } +} + +class UnmodeledDefinitionInstruction extends Instruction { + UnmodeledDefinitionInstruction() { + opcode instanceof Opcode::UnmodeledDefinition + } + + override final MemoryAccessKind getResultMemoryAccess() { + result instanceof UnmodeledMemoryAccess + } +} + +class UnmodeledUseInstruction extends Instruction { + UnmodeledUseInstruction() { + opcode instanceof Opcode::UnmodeledUse + } + + override final MemoryAccessKind getOperandMemoryAccess(OperandTag tag) { + tag instanceof UnmodeledUseOperand and + result instanceof UnmodeledMemoryAccess + } +} + +class PhiInstruction extends Instruction { + PhiInstruction() { + opcode instanceof Opcode::Phi + } + + override final MemoryAccessKind getOperandMemoryAccess(OperandTag tag) { + tag instanceof PhiOperand and + result instanceof PhiMemoryAccess + } + + override final MemoryAccessKind getResultMemoryAccess() { + result instanceof PhiMemoryAccess + } +} + +/** + * An instruction representing a built-in operation. This is used to represent + * operations such as access to variable argument lists. + */ +class BuiltInInstruction extends Instruction { + BuiltInInstruction() { + opcode instanceof BuiltInOpcode + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/OperandTag.qll b/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/OperandTag.qll new file mode 100644 index 000000000000..ece7fb5bd15b --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/OperandTag.qll @@ -0,0 +1,299 @@ +private import IRInternal +import Instruction +import IRBlock +import cpp + +private newtype TOperandTag = + TLoadStoreAddressOperand() or + TCopySourceOperand() or + TUnaryOperand() or + TLeftOperand() or + TRightOperand() or + TReturnValueOperand() or + TExceptionOperand() or + TConditionOperand() or + TUnmodeledUseOperand() or + TCallTargetOperand() or + TThisArgumentOperand() or + TPositionalArgumentOperand(int argIndex) { + argIndex in [0..Construction::getMaxCallArgIndex()] or + exists(BuiltInOperation op | + exists(op.getChild(argIndex)) + ) + } or + TPhiOperand(IRBlock predecessorBlock) { + exists(PhiInstruction phi | + predecessorBlock = Construction::getPhiInstructionBlockStart(phi).getBlock().getAPredecessor() + ) + } + +/** + * Identifies the kind of operand on an instruction. Each `Instruction` has at + * most one operand of any single `OperandTag`. The set of `OperandTag`s used by + * an `Instruction` is determined by the instruction's opcode. + */ +abstract class OperandTag extends TOperandTag { + abstract string toString(); + abstract int getSortOrder(); +} + +// Note: individual subtypes are listed in the order that the operands should +// appear in the operand list of the instruction when printing. + +/** + * The address operand of an instruction that loads or stores a value from + * memory (e.g. `Load`, `Store`). + */ +class LoadStoreAddressOperand extends OperandTag, TLoadStoreAddressOperand { + override final string toString() { + result = "LoadStoreAddress" + } + + override final int getSortOrder() { + result = 0 + } +} + +LoadStoreAddressOperand loadStoreAddressOperand() { + result = TLoadStoreAddressOperand() +} + +/** + * The source value operand of an instruction that copies this value to its + * result (e.g. `Copy`, `Load`, `Store`). + */ +class CopySourceOperand extends OperandTag, TCopySourceOperand { + override final string toString() { + result = "CopySource" + } + + override final int getSortOrder() { + result = 1 + } +} + +CopySourceOperand copySourceOperand() { + result = TCopySourceOperand() +} + +/** + * The sole operand of a unary instruction (e.g. `Convert`, `Negate`). + */ +class UnaryOperand extends OperandTag, TUnaryOperand { + override final string toString() { + result = "Unary" + } + + override final int getSortOrder() { + result = 2 + } +} + +UnaryOperand unaryOperand() { + result = TUnaryOperand() +} + +/** + * The left operand of a binary instruction (e.g. `Add`, `CompareEQ`). + */ +class LeftOperand extends OperandTag, TLeftOperand { + override final string toString() { + result = "Left" + } + + override final int getSortOrder() { + result = 3 + } +} + +LeftOperand leftOperand() { + result = TLeftOperand() +} + +/** + * The right operand of a binary instruction (e.g. `Add`, `CompareEQ`). + */ +class RightOperand extends OperandTag, TRightOperand { + override final string toString() { + result = "Right" + } + + override final int getSortOrder() { + result = 4 + } +} + +RightOperand rightOperand() { + result = TRightOperand() +} + +/** + * The return value operand of a `ReturnValue` instruction. + */ +class ReturnValueOperand extends OperandTag, TReturnValueOperand { + override final string toString() { + result = "ReturnValue" + } + + override final int getSortOrder() { + result = 5 + } +} + +ReturnValueOperand returnValueOperand() { + result = TReturnValueOperand() +} + +/** + * The exception thrown by a `ThrowValue` instruction. + */ +class ExceptionOperand extends OperandTag, TExceptionOperand { + override final string toString() { + result = "Exception" + } + + override final int getSortOrder() { + result = 6 + } +} + +ExceptionOperand exceptionOperand() { + result = TExceptionOperand() +} + +/** + * The condition operand of a `ConditionalBranch` or `Switch` instruction. + */ +class ConditionOperand extends OperandTag, TConditionOperand { + override final string toString() { + result = "Condition" + } + + override final int getSortOrder() { + result = 7 + } +} + +ConditionOperand conditionOperand() { + result = TConditionOperand() +} + +/** + * An operand of the special `UnmodeledUse` instruction, representing a value + * whose set of uses is unknown. + */ +class UnmodeledUseOperand extends OperandTag, TUnmodeledUseOperand { + override final string toString() { + result = "UnmodeledUse" + } + + override final int getSortOrder() { + result = 8 + } +} + +UnmodeledUseOperand unmodeledUseOperand() { + result = TUnmodeledUseOperand() +} + +/** + * The operand representing the target function of an `Invoke` instruction. + */ +class CallTargetOperand extends OperandTag, TCallTargetOperand { + override final string toString() { + result = "CallTarget" + } + + override final int getSortOrder() { + result = 9 + } +} + +CallTargetOperand callTargetOperand() { + result = TCallTargetOperand() +} + +/** + * 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`). + */ +abstract class ArgumentOperand extends OperandTag { +} + +/** + * An operand representing the implicit 'this' argument to a member function + * call. + */ +class ThisArgumentOperand extends ArgumentOperand, TThisArgumentOperand { + ThisArgumentOperand() { + this = TThisArgumentOperand() + } + + override final string toString() { + result = "Arg(this)" + } + + override final int getSortOrder() { + result = 10 + } +} + +ThisArgumentOperand thisArgumentOperand() { + result = TThisArgumentOperand() +} + +/** + * An operand representing an argument to a function call. + */ +class PositionalArgumentOperand extends ArgumentOperand, + TPositionalArgumentOperand { + int argIndex; + + PositionalArgumentOperand() { + this = TPositionalArgumentOperand(argIndex) + } + + override final string toString() { + result = "Arg(" + argIndex + ")" + } + + override final int getSortOrder() { + result = 11 + argIndex + } + + final int getArgIndex() { + result = argIndex + } +} + +PositionalArgumentOperand positionalArgumentOperand(int argIndex) { + result = TPositionalArgumentOperand(argIndex) +} + +/** + * An operand of an SSA `Phi` instruction. + */ +class PhiOperand extends OperandTag, TPhiOperand { + IRBlock predecessorBlock; + + PhiOperand() { + this = TPhiOperand(predecessorBlock) + } + + override final string toString() { + result = "Phi" + } + + override final int getSortOrder() { + result = 11 + } + + final IRBlock getPredecessorBlock() { + result = predecessorBlock + } +} + +PhiOperand phiOperand(IRBlock predecessorBlock) { + result = TPhiOperand(predecessorBlock) +} diff --git a/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/PrintIRImpl.qll b/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/PrintIRImpl.qll new file mode 100644 index 000000000000..9cf77f21c04a --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/PrintIRImpl.qll @@ -0,0 +1,175 @@ +private import IRImpl +import cpp + +private int getInstructionIndexInBlock(Instruction instr) { + exists(IRBlock block | + block = instr.getBlock() and + ( + exists(int index, int phiCount | + phiCount = count(block.getAPhiInstruction()) and + instr = block.getInstruction(index) and + result = index + phiCount + ) or + ( + instr instanceof PhiInstruction and + instr = rank[result + 1](PhiInstruction phiInstr | + phiInstr = block.getAPhiInstruction() | + phiInstr order by phiInstr.getUniqueId() + ) + ) + ) + ) +} + +private string getInstructionResultId(Instruction instr) { + result = getResultPrefix(instr) + getBlockId(instr.getBlock()) + "_" + + getInstructionIndexInBlock(instr).toString() +} + +private string getResultPrefix(Instruction instr) { + if instr.hasMemoryResult() then + if instr.isResultModeled() then + result = "@m" + else + result = "@mu" + else + result = "@r" +} + +/** + * Gets the identifier of the specified function scope. + * Currently just returns the signature of the function. + */ +private string getScopeId(Function func) { + result = func.getFullSignature() +} + +/** + * Gets the unique identifier of the block within its function. + * Currently returns a string representation of an integer in the range + * [0..numBlocks - 1]. + */ +private string getBlockId(IRBlock block) { + exists(int rankIndex | + block = rank[rankIndex + 1](IRBlock funcBlock | + funcBlock.getFunction() = block.getFunction() | + funcBlock order by funcBlock.getUniqueId() + ) and + result = rankIndex.toString() + ) +} + +/** + * Prints the full signature and qualified name for each scope. This is primarily + * so that post-processing tools can identify function overloads, which will have + * different signatures but the same qualified name. + */ +query predicate printIRGraphScopes(string scopeId, string qualifiedName) { + exists(FunctionIR ir, Function func | + func = ir.getFunction() and + scopeId = getScopeId(func) and + qualifiedName = func.getQualifiedName() + ) +} + +query predicate printIRGraphNodes(string scopeId, string blockId, string label, string location) { + exists(IRBlock block | + scopeId = getScopeId(block.getFunction()) and + blockId = getBlockId(block) and + label = "" and + location = "" + ) +} + +query predicate printIRGraphInstructions(string scopeId, string blockId, + string id, string label, string location) { + exists(IRBlock block, Instruction instr | + instr = block.getAnInstruction() and + label = instr.toString() and + location = instr.getLocation().toString() and + scopeId = getScopeId(block.getFunction()) and + blockId = getBlockId(block) and + id = getInstructionIndexInBlock(instr).toString() + ) +} + +query predicate printIRGraphEdges(string scopeId, + string predecessorId, string successorId, string label) { + exists(IRBlock predecessor, IRBlock successor, EdgeKind kind | + scopeId = getScopeId(predecessor.getFunction()) and + predecessor.getSuccessor(kind) = successor and + predecessorId = getBlockId(predecessor) and + successorId = getBlockId(successor) and + label = kind.toString() + ) +} + +private string getValueCategoryString(Instruction instr) { + if instr.isGLValue() then + result = "glval:" + else + result = "" +} + +private string getResultTypeString(Instruction instr) { + exists(Type resultType, string valcat | + resultType = instr.getResultType() and + valcat = getValueCategoryString(instr) and + if resultType instanceof UnknownType and exists(instr.getResultSize()) then + result = valcat + resultType.toString() + "[" + instr.getResultSize().toString() + "]" + else + result = valcat + resultType.toString() + ) +} + +query predicate printIRGraphDestinationOperands(string scopeId, string blockId, + string instructionId, int operandId, string label) { + exists(IRBlock block, Instruction instr | + block.getAnInstruction() = instr and + scopeId = getScopeId(block.getFunction()) and + blockId = getBlockId(block) and + instructionId = getInstructionIndexInBlock(instr).toString() and + not instr.getResultType() instanceof VoidType and + operandId = 0 and + label = getInstructionResultId(instr) + + "(" + getResultTypeString(instr) + ")" + ) +} + +private string getOperandTagLabel(OperandTag tag) { + ( + tag instanceof PhiOperand and + result = "from " + getBlockId(tag.(PhiOperand).getPredecessorBlock()) + ": " + ) + or ( + tag instanceof ThisArgumentOperand and + result = "this:" + ) + or ( + not tag instanceof PhiOperand and + not tag instanceof ThisArgumentOperand and + result = "" + ) +} + +query predicate printIRGraphSourceOperands(string scopeId, string blockId, + string instructionId, int operandId, string label) { + exists(IRBlock block, Instruction instr | + block.getAnInstruction() = instr and + blockId = getBlockId(block) and + scopeId = getScopeId(block.getFunction()) and + instructionId = getInstructionIndexInBlock(instr).toString() and + if (instr instanceof UnmodeledUseInstruction) then ( + operandId = 0 and + label = "@mu*" + ) + else ( + exists(OperandTag tag, Instruction operandInstr | + operandInstr = instr.getOperand(tag) and + operandId = tag.getSortOrder() and + label = getOperandTagLabel(tag) + + getInstructionResultId(operandInstr) + ) + ) + ) +} diff --git a/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/SSAConstruction.qll new file mode 100644 index 000000000000..cfc220ba368e --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/SSAConstruction.qll @@ -0,0 +1,424 @@ +import SSAConstructionInternal +import cpp +private import semmle.code.cpp.ir.internal.Opcode +import NewIR + +import Cached +cached private module Cached { + + private OldIR::OperandTag getOldOperandTag(OperandTag newTag) { + newTag instanceof LoadStoreAddressOperand and result instanceof OldIR::LoadStoreAddressOperand or + newTag instanceof CopySourceOperand and result instanceof OldIR::CopySourceOperand or + newTag instanceof UnaryOperand and result instanceof OldIR::UnaryOperand or + newTag instanceof LeftOperand and result instanceof OldIR::LeftOperand or + newTag instanceof RightOperand and result instanceof OldIR::RightOperand or + newTag instanceof ReturnValueOperand and result instanceof OldIR::ReturnValueOperand or + newTag instanceof ExceptionOperand and result instanceof OldIR::ExceptionOperand or + newTag instanceof ConditionOperand and result instanceof OldIR::ConditionOperand or + newTag instanceof UnmodeledUseOperand and result instanceof OldIR::UnmodeledUseOperand or + newTag instanceof CallTargetOperand and result instanceof OldIR::CallTargetOperand or + newTag instanceof ThisArgumentOperand and result instanceof OldIR::ThisArgumentOperand or + exists(PositionalArgumentOperand newArg | + newArg = newTag and + result.(OldIR::PositionalArgumentOperand).getArgIndex() = newArg.getArgIndex() + ) + } + + private IRBlock getNewBlock(OldIR::IRBlock oldBlock) { + result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction()) + } + + cached newtype TInstructionTag = + WrappedInstructionTag(OldIR::Instruction oldInstruction) or + PhiTag(Alias::VirtualVariable vvar, OldIR::IRBlock block) { + hasPhiNode(vvar, block) + } + + cached class InstructionTagType extends TInstructionTag { + cached final string toString() { + result = "Tag" + } + } + + cached predicate functionHasIR(Function func) { + exists(OldIR::FunctionIR funcIR | + funcIR.getFunction() = func + ) + } + + cached int getMaxCallArgIndex() { + result = max(int argIndex | + exists(OldIR::PositionalArgumentOperand oldOperand | + argIndex = oldOperand.getArgIndex() + ) + ) + } + + cached OldIR::Instruction getOldInstruction(Instruction instr) { + instr.getTag() = WrappedInstructionTag(result) + } + + private Instruction getNewInstruction(OldIR::Instruction instr) { + result.getTag() = WrappedInstructionTag(instr) + } + + private PhiInstruction getPhiInstruction(Function func, OldIR::IRBlock oldBlock, + Alias::VirtualVariable vvar) { + result.getFunction() = func and + result.getAST() = oldBlock.getFirstInstruction().getAST() and + result.getTag() = PhiTag(vvar, oldBlock) + } + + private IRVariable getNewIRVariable(OldIR::IRVariable var) { + result.getFunction() = var.getFunction() and + ( + exists(OldIR::IRUserVariable userVar, IRUserVariable newUserVar | + userVar = var and + newUserVar.getVariable() = userVar.getVariable() and + result = newUserVar + ) or + exists(OldIR::IRTempVariable tempVar, IRTempVariable newTempVar | + tempVar = var and + newTempVar.getAST() = tempVar.getAST() and + newTempVar.getTag() = tempVar.getTag() and + result = newTempVar + ) + ) + } + + cached newtype TInstruction = + MkInstruction(FunctionIR funcIR, Opcode opcode, Locatable ast, + InstructionTag tag, Type resultType, boolean isGLValue) { + hasInstruction(funcIR.getFunction(), opcode, ast, tag, + resultType, isGLValue) + } + + private predicate hasInstruction(Function func, Opcode opcode, Locatable ast, + InstructionTag tag, Type resultType, boolean isGLValue) { + exists(OldIR::Instruction instr | + instr.getFunction() = func and + instr.getOpcode() = opcode and + instr.getAST() = ast and + WrappedInstructionTag(instr) = tag and + instr.getResultType() = resultType and + if instr.isGLValue() then + isGLValue = true + else + isGLValue = false + ) or + exists(OldIR::IRBlock block, Alias::VirtualVariable vvar | + hasPhiNode(vvar, block) and + block.getFunction() = func and + opcode instanceof Opcode::Phi and + ast = block.getFirstInstruction().getAST() and + tag = PhiTag(vvar, block) and + resultType = vvar.getType() and + isGLValue = false + ) + } + + cached predicate hasTempVariable(Function func, Locatable ast, TempVariableTag tag, + Type type) { + exists(OldIR::IRTempVariable var | + var.getFunction() = func and + var.getAST() = ast and + var.getTag() = tag and + var.getType() = type + ) + } + + cached predicate hasModeledMemoryResult(Instruction instruction) { + exists(Alias::getResultMemoryAccess(getOldInstruction(instruction))) or + instruction instanceof PhiInstruction // Phis always have modeled results + } + + cached Instruction getInstructionOperand(Instruction instruction, OperandTag tag) { + exists(OldIR::Instruction oldUse, OldIR::OperandTag oldTag | + oldUse = getOldInstruction(instruction) and + oldTag = getOldOperandTag(tag) and + if oldUse.isMemoryOperand(oldTag) then ( + ( + if exists(Alias::getOperandMemoryAccess(oldUse, oldTag)) then ( + exists(OldIR::IRBlock useBlock, int useRank, Alias::VirtualVariable vvar, + OldIR::IRBlock defBlock, int defRank, int defIndex | + vvar = Alias::getOperandMemoryAccess(oldUse, oldTag).getVirtualVariable() and + hasDefinitionAtRank(vvar, defBlock, defRank, defIndex) and + hasUseAtRank(vvar, useBlock, useRank, oldUse) and + definitionReachesUse(vvar, defBlock, defRank, useBlock, useRank) and + if defIndex >= 0 then + result = getNewInstruction(defBlock.getInstruction(defIndex)) + else + result = getPhiInstruction(instruction.getFunction(), defBlock, vvar) + ) + ) + else ( + result = instruction.getFunctionIR().getUnmodeledDefinitionInstruction() + ) + ) or + // Connect any definitions that are not being modeled in SSA to the + // `UnmodeledUse` instruction. + exists(OldIR::Instruction oldDefinition | + instruction instanceof UnmodeledUseInstruction and + tag instanceof UnmodeledUseOperand and + oldDefinition = oldUse.getOperand(oldTag) and + not exists(Alias::getResultMemoryAccess(oldDefinition)) and + result = getNewInstruction(oldDefinition) + ) + ) + else + result = getNewInstruction(oldUse.getOperand(oldTag)) + ) or + result = getPhiInstructionOperand(instruction.(PhiInstruction), tag.(PhiOperand)) + } + + cached Instruction getPhiInstructionOperand(PhiInstruction instr, PhiOperand tag) { + exists(Alias::VirtualVariable vvar, OldIR::IRBlock phiBlock, + OldIR::IRBlock defBlock, int defRank, int defIndex, OldIR::IRBlock predBlock | + hasPhiNode(vvar, phiBlock) and + predBlock = phiBlock.getAPredecessor() and + instr.getTag() = PhiTag(vvar, phiBlock) and + tag.getPredecessorBlock() = getNewBlock(predBlock) and + hasDefinitionAtRank(vvar, defBlock, defRank, defIndex) and + definitionReachesEndOfBlock(vvar, defBlock, defRank, predBlock) and + if defIndex >= 0 then + result = getNewInstruction(defBlock.getInstruction(defIndex)) + else + result = getPhiInstruction(instr.getFunction(), defBlock, vvar) + ) + } + + cached Instruction getPhiInstructionBlockStart(PhiInstruction instr) { + exists(OldIR::IRBlock oldBlock | + instr.getTag() = PhiTag(_, oldBlock) and + result = getNewInstruction(oldBlock.getFirstInstruction()) + ) + } + + cached Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) { + result = getNewInstruction(getOldInstruction(instruction).getSuccessor(kind)) + } + + cached IRVariable getInstructionVariable(Instruction instruction) { + result = getNewIRVariable(getOldInstruction(instruction).(OldIR::VariableInstruction).getVariable()) + } + + cached Field getInstructionField(Instruction instruction) { + result = getOldInstruction(instruction).(OldIR::FieldInstruction).getField() + } + + cached Function getInstructionFunction(Instruction instruction) { + result = getOldInstruction(instruction).(OldIR::FunctionInstruction).getFunctionSymbol() + } + + cached string getInstructionConstantValue(Instruction instruction) { + result = getOldInstruction(instruction).(OldIR::ConstantValueInstruction).getValue() + } + + cached StringLiteral getInstructionStringLiteral(Instruction instruction) { + result = getOldInstruction(instruction).(OldIR::StringConstantInstruction).getValue() + } + + cached Type getInstructionExceptionType(Instruction instruction) { + result = getOldInstruction(instruction).(OldIR::CatchByTypeInstruction).getExceptionType() + } + + cached int getInstructionElementSize(Instruction instruction) { + result = getOldInstruction(instruction).(OldIR::PointerArithmeticInstruction).getElementSize() + } + + cached int getInstructionResultSize(Instruction instruction) { + // Only return a result for instructions that needed an explicit result size. + instruction.getResultType() instanceof UnknownType and + result = getOldInstruction(instruction).getResultSize() + } + + cached predicate getInstructionInheritance(Instruction instruction, Class baseClass, + Class derivedClass) { + exists(OldIR::InheritanceConversionInstruction oldInstr | + oldInstr = getOldInstruction(instruction) and + baseClass = oldInstr.getBaseClass() and + derivedClass = oldInstr.getDerivedClass() + ) + } + + private predicate ssa_variableUpdate(Alias::VirtualVariable vvar, + OldIR::Instruction instr, OldIR::IRBlock block, int index) { + block.getInstruction(index) = instr and + Alias::getResultMemoryAccess(instr).getVirtualVariable() = vvar + } + + private predicate hasDefinition(Alias::VirtualVariable vvar, OldIR::IRBlock block, int index) { + ( + hasPhiNode(vvar, block) and + index = -1 + ) or + exists(Alias::MemoryAccess access, OldIR::Instruction def | + access = Alias::getResultMemoryAccess(def) and + block.getInstruction(index) = def and + vvar = access.getVirtualVariable() + ) + } + + private predicate defUseRank(Alias::VirtualVariable vvar, OldIR::IRBlock block, int rankIndex, int index) { + index = rank[rankIndex](int j | hasDefinition(vvar, block, j) or hasUse(vvar, _, block, j)) + } + + private predicate hasUse(Alias::VirtualVariable vvar, + OldIR::Instruction use, OldIR::IRBlock block, int index) { + exists(Alias::MemoryAccess access | + access = Alias::getOperandMemoryAccess(use, _) and + block.getInstruction(index) = use and + vvar = access.getVirtualVariable() + ) + } + + private predicate variableLiveOnEntryToBlock(Alias::VirtualVariable vvar, OldIR::IRBlock block) { + exists (int index | hasUse(vvar, _, block, index) | + not exists (int j | ssa_variableUpdate(vvar, _, block, j) | j < index) + ) or + (variableLiveOnExitFromBlock(vvar, block) and not ssa_variableUpdate(vvar, _, block, _)) + } + + pragma[noinline] + private predicate variableLiveOnExitFromBlock(Alias::VirtualVariable vvar, OldIR::IRBlock block) { + variableLiveOnEntryToBlock(vvar, block.getASuccessor()) + } + + /** + * Gets the rank index of a hyphothetical use one instruction past the end of + * the block. This index can be used to determine if a definition reaches the + * end of the block, even if the definition is the last instruction in the + * block. + */ + private int exitRank(Alias::VirtualVariable vvar, OldIR::IRBlock block) { + result = max(int rankIndex | defUseRank(vvar, block, rankIndex, _)) + 1 + } + + private predicate hasDefinitionAtRank(Alias::VirtualVariable vvar, + OldIR::IRBlock block, int rankIndex, int instructionIndex) { + hasDefinition(vvar, block, instructionIndex) and + defUseRank(vvar, block, rankIndex, instructionIndex) + } + + private predicate hasUseAtRank(Alias::VirtualVariable vvar, OldIR::IRBlock block, + int rankIndex, OldIR::Instruction use) { + exists(int index | + hasUse(vvar, use, block, index) and + defUseRank(vvar, block, rankIndex, index) + ) + } + + /** + * Holds if the definition of `vvar` at `(block, defRank)` reaches the rank + * index `reachesRank` in block `block`. + */ + private predicate definitionReachesRank(Alias::VirtualVariable vvar, + OldIR::IRBlock block, int defRank, int reachesRank) { + hasDefinitionAtRank(vvar, block, defRank, _) and + reachesRank <= exitRank(vvar, block) and // Without this, the predicate would be infinite. + ( + // The def always reaches the next use, even if there is also a def on the + // use instruction. + reachesRank = defRank + 1 or + ( + // If the def reached the previous rank, it also reaches the current rank, + // unless there was another def at the previous rank. + definitionReachesRank(vvar, block, defRank, reachesRank - 1) and + not hasDefinitionAtRank(vvar, block, reachesRank - 1, _) + ) + ) + } + + /** + * Holds if the definition of `vvar` at `(defBlock, defRank)` reaches the end of + * block `block`. + */ + private predicate definitionReachesEndOfBlock(Alias::VirtualVariable vvar, + OldIR::IRBlock defBlock, int defRank, OldIR::IRBlock block) { + hasDefinitionAtRank(vvar, defBlock, defRank, _) and + ( + ( + // If we're looking at the def's own block, just see if it reaches the exit + // rank of the block. + block = defBlock and + variableLiveOnExitFromBlock(vvar, defBlock) and + definitionReachesRank(vvar, defBlock, defRank, exitRank(vvar, defBlock)) + ) or + exists(OldIR::IRBlock idom | + definitionReachesEndOfBlock(vvar, defBlock, defRank, idom) and + noDefinitionsSinceIDominator(vvar, idom, block) + ) + ) + } + + pragma[noinline] + private predicate noDefinitionsSinceIDominator(Alias::VirtualVariable vvar, + OldIR::IRBlock idom, OldIR::IRBlock block) { + idom.immediatelyDominates(block) and // It is sufficient to traverse the dominator graph, cf. discussion above. + variableLiveOnExitFromBlock(vvar, block) and + not hasDefinition(vvar, block, _) + } + + private predicate definitionReachesUseWithinBlock( + Alias::VirtualVariable vvar, OldIR::IRBlock defBlock, int defRank, + OldIR::IRBlock useBlock, int useRank) { + defBlock = useBlock and + hasDefinitionAtRank(vvar, defBlock, defRank, _) and + hasUseAtRank(vvar, useBlock, useRank, _) and + definitionReachesRank(vvar, defBlock, defRank, useRank) + } + + private predicate definitionReachesUse(Alias::VirtualVariable vvar, + OldIR::IRBlock defBlock, int defRank, OldIR::IRBlock useBlock, int useRank) { + hasUseAtRank(vvar, useBlock, useRank, _) and + ( + definitionReachesUseWithinBlock(vvar, defBlock, defRank, useBlock, + useRank) or + ( + definitionReachesEndOfBlock(vvar, defBlock, defRank, + useBlock.getAPredecessor()) and + not definitionReachesUseWithinBlock(vvar, useBlock, _, useBlock, useRank) + ) + ) + } + + private predicate hasFrontierPhiNode(Alias::VirtualVariable vvar, + OldIR::IRBlock phiBlock) { + exists(OldIR::IRBlock defBlock | + phiBlock = defBlock.dominanceFrontier() and + hasDefinition(vvar, defBlock, _) and + /* We can also eliminate those nodes where the variable is not live on any incoming edge */ + variableLiveOnEntryToBlock(vvar, phiBlock) + ) + } + + private predicate hasPhiNode(Alias::VirtualVariable vvar, + OldIR::IRBlock phiBlock) { + hasFrontierPhiNode(vvar, phiBlock) + //or ssa_sanitized_custom_phi_node(vvar, block) + } +} + +import CachedForDebugging +cached private module CachedForDebugging { + cached string getTempVariableUniqueId(IRTempVariable var) { + result = getOldTempVariable(var).getUniqueId() + } + + cached string getInstructionUniqueId(Instruction instr) { + exists(OldIR::Instruction oldInstr | + oldInstr = getOldInstruction(instr) and + result = "NonSSA: " + oldInstr.getUniqueId() + ) or + exists(Alias::VirtualVariable vvar, OldIR::IRBlock phiBlock | + instr.getTag() = PhiTag(vvar, phiBlock) and + result = "Phi Block(" + phiBlock.getUniqueId() + "): " + vvar.getUniqueId() + ) + } + + private OldIR::IRTempVariable getOldTempVariable(IRTempVariable var) { + result.getFunction() = var.getFunction() and + result.getAST() = var.getAST() and + result.getTag() = var.getTag() + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/SSAConstructionInternal.qll b/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/SSAConstructionInternal.qll new file mode 100644 index 000000000000..4bdc7fb0461c --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/SSAConstructionInternal.qll @@ -0,0 +1,3 @@ +import semmle.code.cpp.ir.IR as OldIR +import semmle.code.cpp.ssa.SSAIR as NewIR +import SimpleSSA as Alias diff --git a/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/SimpleSSA.qll b/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/SimpleSSA.qll new file mode 100644 index 000000000000..27287312f16a --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/SimpleSSA.qll @@ -0,0 +1,83 @@ +import SimpleSSAInternal +import cpp +import Alias +import IR +import semmle.code.cpp.ssa.internal.Overlap + +private newtype TVirtualVariable = + MkVirtualVariable(IRVariable var) { + not variableAddressEscapes(var) + } + +private VirtualVariable getVirtualVariable(IRVariable var) { + result.getIRVariable() = var +} + +class VirtualVariable extends TVirtualVariable { + IRVariable var; + + VirtualVariable() { + this = MkVirtualVariable(var) + } + + final string toString() { + result = var.toString() + } + + final IRVariable getIRVariable() { + result = var + } + + // REVIEW: This should just be on MemoryAccess + final Type getType() { + result = var.getType() + } + + final string getUniqueId() { + result = var.getUniqueId() + } +} + +private newtype TMemoryAccess = + MkMemoryAccess(VirtualVariable vvar) + +private MemoryAccess getMemoryAccess(IRVariable var) { + result.getVirtualVariable() = getVirtualVariable(var) +} + +class MemoryAccess extends TMemoryAccess { + VirtualVariable vvar; + + MemoryAccess() { + this = MkMemoryAccess(vvar) + } + + string toString() { + result = vvar.toString() + } + + VirtualVariable getVirtualVariable() { + result = vvar + } +} + +Overlap getOverlap(MemoryAccess def, MemoryAccess use) { + def.getVirtualVariable() = use.getVirtualVariable() and + result instanceof MustExactlyOverlap +} + +MemoryAccess getResultMemoryAccess(Instruction instr) { + exists(IRVariable var | + instr.getResultMemoryAccess() instanceof IndirectMemoryAccess and + resultPointsTo(instr.getOperand(loadStoreAddressOperand()), var, 0) and + result = getMemoryAccess(var) + ) +} + +MemoryAccess getOperandMemoryAccess(Instruction instr, OperandTag tag) { + exists(IRVariable var | + instr.getOperandMemoryAccess(tag) instanceof IndirectMemoryAccess and + resultPointsTo(instr.getOperand(loadStoreAddressOperand()), var, 0) and + result = getMemoryAccess(var) + ) +} diff --git a/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/SimpleSSAInternal.qll b/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/SimpleSSAInternal.qll new file mode 100644 index 000000000000..c71f5acdf79e --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/SimpleSSAInternal.qll @@ -0,0 +1,3 @@ +import AliasAnalysis as Alias +import semmle.code.cpp.ir.IR as IR + diff --git a/cpp/ql/src/semmle/code/cpp/stmts/Block.qll b/cpp/ql/src/semmle/code/cpp/stmts/Block.qll new file mode 100644 index 000000000000..fb08d740671d --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/stmts/Block.qll @@ -0,0 +1,128 @@ +import semmle.code.cpp.Element +import semmle.code.cpp.stmts.Stmt + +/** + * A C/C++ block statement. + * + * For example, + * ``` + * { int a; int b = 1; a = b; } + * ``` + */ +class Block extends Stmt, @stmt_block { + + /** + * Gets a child declaration of this block. + * + * For example, for the block + * ``` + * { int a; int b = 1; a = b; } + * ``` + * it would have 2 results, for the declarations of `a` and `b`. + */ + Declaration getADeclaration() { + result = this.getAStmt().(DeclStmt).getADeclaration() + } + + /** + * Gets a body statement of this block. + * + * For example, for the block + * ``` + * { int a; int b = 1; a = b; } + * ``` + * it would have 3 results, for the declarations of `a` and `b` and + * for the expression statement `a = b`. + */ + Stmt getAStmt() { result = this.getAChild() } + + /** + * Gets the `n`th body statement of this block, indexed from 0. + * + * For example, for the block + * ``` + * { int a; int b = 1; a = b; } + * ``` + * `getStmt(2)`'s result is the expression statement `a = b`. + */ + Stmt getStmt(int n) { result = this.getChild(n) } + + /** + * Gets the last body statement of this block. + * + * For example, for the block + * ``` + * { int a; int b = 1; a = b; } + * ``` + * the result is the expression statement `a = b`. + */ + Stmt getLastStmt() { result = this.getStmt(this.getNumStmt()-1) } + + /** + * Gets the last body statement of this block. If this last statement + * is itself a block, returns the last statement of that block, and so on. + * + * For example, for the block + * ``` + * { int a; int b = 1; { a = b; } } + * ``` + * the result is the expression statement `a = b`. + */ + Stmt getLastStmtIn() { + if getLastStmt() instanceof Block then ( + result = getLastStmt().(Block).getLastStmtIn() + ) else ( + result = getLastStmt() + ) + } + + /** + * Gets the number of body statements in this block. + * + * For example, for the block + * ``` + * { int a; int b = 1; a = b; } + * ``` + * the result is 3. + */ + int getNumStmt() { result = count(this.getAStmt()) } + + /** + * Holds if the block has no statements. + * + * For example, the block + * ``` + * { } + * ``` + * is empty, as is the block + * ``` + * { + * // a comment + * } + * ``` + */ + predicate isEmpty() { this.getNumStmt() = 0 } + + /** + * Gets the index of the given statement within this block, indexed from 0. + * + * For example, for the block + * ``` + * { int a; int b = 1; a = b; } + * ``` + * if `s` is the expression statement `a = b` then `getIndexOfStmt(s)` + * has result 2. + */ + int getIndexOfStmt(Stmt s) { this.getStmt(result) = s } + + override string toString() { result = "{ ... }" } + + override predicate mayBeImpure() { + this.getAStmt().mayBeImpure() + } + + override predicate mayBeGloballyImpure() { + this.getAStmt().mayBeGloballyImpure() + } + +} diff --git a/cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll b/cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll new file mode 100644 index 000000000000..82065f121668 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll @@ -0,0 +1,1901 @@ +import semmle.code.cpp.Element +private import semmle.code.cpp.Enclosing +private import semmle.code.cpp.internal.Type + +/** + * A C/C++ statement. + */ +class Stmt extends StmtParent, @stmt { + + /** Gets the `n`th child of this statement. */ + Element getChild(int n) { + stmtparents(result,n,this) or + exprparents(result,n,this) + } + + /** Holds if `e` is the `n`th child of this statement. */ + predicate hasChild(Element e, int n) { this.getChild(n) = e } + + /** Gets the enclosing function of this statement, if any. */ + Function getEnclosingFunction() { result = stmtEnclosingElement(this) } + + /** + * Gets the nearest enclosing block of this statement in the source, if any. + */ + Block getEnclosingBlock() { + if getParentStmt() instanceof Block and + not (getParentStmt().(Block).getLocation() instanceof UnknownLocation) + then + result = getParentStmt() + else + result = getParentStmt().getEnclosingBlock() + } + + /** Gets a child of this statement. */ + Element getAChild() { exists (int n | result = this.getChild(n)) } + + /** Gets the parent of this statement, if any. */ + StmtParent getParent() { stmtparents(this,_,result) } + + /** Gets the parent statement of this statement, if any. */ + Stmt getParentStmt() { stmtparents(this,_,result) } + + /** Gets a child statement of this statement. */ + Stmt getChildStmt() { result.getParentStmt() = this } + + /** + * Gets the statement following this statement in the same block, if any. + */ + Stmt getFollowingStmt() { + exists(Block b, int i | this = b.getStmt(i) and + result = b.getStmt(i+1)) + } + + override Location getLocation() { stmts(this,_,result) } + + /** Gets an int indicating the type of statement that this represents. */ + int getKind() { stmts(this,result,_) } + + override string toString() { none() } + + override Function getControlFlowScope() { + result = this.getEnclosingFunction() + } + + override Stmt getEnclosingStmt() { + result = this + } + + /** + * Holds if this statement is side-effect free (a conservative + * approximation; that is, it may be side-effect free even if this + * predicate doesn't hold). + * + * This predicate cannot be overridden; override `mayBeImpure()` + * instead. + * + * Note that this predicate only considers whether the statement has + * any side-effects, such as writing to a file. Even if it holds, the + * statement may be impure in the sense that its behavior is affected + * by external factors, such as the contents of global variables. + */ + final predicate isPure() { + not this.mayBeImpure() + } + /** + * Holds if it is possible that this statement is impure. If we are not + * sure, then it holds. + */ + predicate mayBeImpure() { + any() + } + /** + * Holds if it is possible that this statement is globally impure. + * + * Similar to `mayBeImpure()`, except that `mayBeGloballyImpure()` + * does not consider modifications to temporary local variables to be + * impure. That is, if you call a function in which + * `mayBeGloballyImpure()` doesn't hold for any statement, then the + * function as a whole will have no side-effects, even if it mutates + * its own fresh stack variables. + */ + predicate mayBeGloballyImpure() { + any() + } + + /** + * Gets an attribute of this statement, for example + * `[[clang::fallthrough]]`. + */ + Attribute getAnAttribute() { + stmtattributes(this, result) + } + + /** + * Gets a macro invocation that generates this entire statement. + * + * For example, given + * ``` + * #define SOMEFUN a() + * #define FOO do { SOMEFUN; b(); } while (0) + * void f(void) { + * FOO; + * } + * ``` + * this predicate would have results of `SOMEFUN` and `FOO` for the + * function call `a()`, and just `FOO` for the function call `b()`, + * the block within the 'do' statement, and the entire 'do' statement. + * + * Note that, unlike `isInMacroExpansion()` it is not necessary for + * the macro to generate the terminating semi-colon. + */ + MacroInvocation getGeneratingMacro() { + result.getAnExpandedElement() = this + } + + /** Holds if this statement was generated by the compiler. */ + predicate isCompilerGenerated() { + compgenerated(this) + } + +} + +/** + * 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 { +} + +/** + * A C/C++ 'expression' statement. + * + * For example, + * ``` + * x = 1; + * ``` + * is an assignment expression inside an 'expression' statement. + */ +class ExprStmt extends Stmt, @stmt_expr { + + /** + * Gets the expression of this 'expression' statement. + * + * For example, for + * ``` + * x = 1; + * ``` + * the result would be an `AssignExpr`. + */ + Expr getExpr() { result = this.getChild(0) } + + override string toString() { result = "ExprStmt" } + + override predicate mayBeImpure() { this.getExpr().mayBeImpure() } + override predicate mayBeGloballyImpure() { + this.getExpr().mayBeGloballyImpure() + } + + override MacroInvocation getGeneratingMacro() { + // We only need the expression to be in the macro, not the semicolon. + result.getAnExpandedElement() = this.getExpr() + } +} + +/** + * A C/C++ control structure, that is, either a conditional statement or + * a loop. + */ +abstract class ControlStructure extends Stmt { + /** + * 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(); + + /** Gets a child declaration of this scope. */ + Declaration getADeclaration() { none() } +} + +/** + * A C/C++ conditional statement, that is, either an 'if' statement or a + * 'switch' statement. + */ +abstract class ConditionalStmt extends ControlStructure { +} + +/** + * A C/C++ 'if' statement. + */ +class IfStmt extends ConditionalStmt, @stmt_if { + + /** + * Gets the condition expression of this 'if' statement. + * + * For example, for + * ``` + * if (b) { x = 1; } + * ``` + * the result is `b`. + */ + Expr getCondition() { result = this.getChild(0) } + + override Expr getControllingExpr() { result = this.getCondition() } + + /** + * Gets the 'then' statement of this 'if' statement. + * + * For example, for + * ``` + * if (b) { x = 1; } + * ``` + * the result is the `Block` `{ x = 1; }`. + */ + Stmt getThen() { if_then(this, result) } + + /** + * Gets the 'else' statement of this 'if' statement, if any. + * + * For example, for + * ``` + * if (b) { x = 1; } else { x = 2; } + * ``` + * the result is the `Block` `{ x = 2; }`, and for + * ``` + * if (b) { x = 1; } + * ``` + * there is no result. + */ + Stmt getElse() { if_else(this, result) } + + /** + * Holds if this 'if' statement has an 'else' statement. + * + * For example, this holds for + * ``` + * if (b) { x = 1; } else { x = 2; } + * ``` + * but not for + * ``` + * if (b) { x = 1; } + * ``` + */ + predicate hasElse() { exists(Stmt s | this.getElse() = s) } + + override string toString() { result = "if (...) ... " } + + override predicate mayBeImpure() { + this.getCondition().mayBeImpure() or + this.getThen().mayBeImpure() or + this.getElse().mayBeImpure() + } + override predicate mayBeGloballyImpure() { + this.getCondition().mayBeGloballyImpure() or + this.getThen().mayBeGloballyImpure() or + this.getElse().mayBeGloballyImpure() + } + + override MacroInvocation getGeneratingMacro() { + result.getAnExpandedElement() = this.getCondition() and + this.getThen().getGeneratingMacro() = result and + (this.hasElse() implies this.getElse().getGeneratingMacro() = result) + } +} + +/** + * A C/C++ loop, that is, either a 'while' loop, a 'for' loop, or a + * 'do' loop. + */ +abstract class Loop extends ControlStructure { + /** Gets the condition expression of this loop. */ + abstract Expr getCondition(); + /** Gets the body statement of this loop. */ + abstract Stmt getStmt(); +} + +/** + * A C/C++ 'while' statement. + * + * For example, + * ``` + * while (b) { f(); } + * ``` + */ +class WhileStmt extends Loop, @stmt_while { + override Expr getCondition() { result = this.getChild(0) } + override Expr getControllingExpr() { result = this.getCondition() } + override Stmt getStmt() { while_body(this, result) } + + override string toString() { result = "while (...) ..." } + + override predicate mayBeImpure() { + this.getCondition().mayBeImpure() or + this.getStmt().mayBeImpure() + } + override predicate mayBeGloballyImpure() { + this.getCondition().mayBeGloballyImpure() or + this.getStmt().mayBeGloballyImpure() + } + + override MacroInvocation getGeneratingMacro() { + result.getAnExpandedElement() = this.getCondition() and + this.getStmt().getGeneratingMacro() = result + } + + /** + * Holds if the loop condition is provably `true`. + * + * For example, this holds for + * ``` + * while(1) { ...; if(b) break; ...; } + * ``` + */ + predicate conditionAlwaysTrue() { + conditionAlwaysTrue(getCondition()) + } + + /** Holds if the loop condition is provably `false`. + * + * For example, this holds for + * ``` + * while(0) { ...; } + * ``` + */ + predicate conditionAlwaysFalse() { + conditionAlwaysFalse(getCondition()) + } + + /** + * Holds if the loop condition is provably `true` upon entry, + * that is, at least one iteration of the loop is guaranteed. + * + * For example, with + * ``` + * bool done = false; + * while (!done) { ... done = true; ... } + * ``` + * the condition `!done` always evaluates to `true` upon entry since + * `done = false`, but the condition may evaluate to `false` after + * some iterations. + */ + predicate conditionAlwaysTrueUponEntry() { + loopConditionAlwaysTrueUponEntry(this, _) + } +} + +/** + * A C/C++ jump statement. + */ +abstract class JumpStmt extends Stmt, @jump { + + /** Gets the target of this jump statement. */ + Stmt getTarget() { jumpinfo(this,_,result) } +} + +/** + * A C/C++ 'goto' statement which jumps to a label. + * + * For example, + * ``` + * goto someLabel; + * ``` + */ +class GotoStmt extends JumpStmt, @stmt_goto { + + /** + * Gets the name of the label this 'goto' statement refers to. + * + * For example, for + * ``` + * goto someLabel; + * ``` + * the result is `"someLabel"`. + */ + string getName() { jumpinfo(this,result,_) and result != "" } + + /** Holds if this 'goto' statement refers to a label. */ + predicate hasName() { exists(string s | jumpinfo(this,s,_) and s != "") } + + override string toString() { result = "goto ..." } + + /** + * Holds if this 'goto' statement breaks out of two or more nested + * loops. + * + * For example, for + * ``` + * while(b) { + * while(b) { + * if(b) goto middle; + * if(b) goto end; + * } + * if(b) goto end; + * middle: + * } + * end: + * ``` + * this holds for the second `goto`, but not the first or third. + */ + predicate breaksFromNestedLoops() { + exists(Loop l1, Loop l2 | + this.getParentStmt+() = l1 and + l1.getParentStmt+() = l2 and + l2.getParentStmt+() = this.getASuccessor().(Stmt).getParentStmt()) + } + + override predicate mayBeImpure() { none() } + override predicate mayBeGloballyImpure() { none() } +} + +/** + * A 'goto' statement whose target is computed by a non-constant + * expression (a non-standard extension to C/C++). + * + * For example, + * ``` + * goto *ptr; + * ``` + */ +class ComputedGotoStmt extends Stmt, @stmt_assigned_goto { + + /** + * Gets the expression used to compute the target of this 'goto' + * statement. + * + * For example, for + * ``` + * goto *ptr; + * ``` + * the result is `ptr`. + */ + Expr getExpr() { result = this.getChild(0) } + + override string toString() { result = "computed goto ..." } + + override predicate mayBeImpure() { this.getExpr().mayBeImpure() } + override predicate mayBeGloballyImpure() { + this.getExpr().mayBeGloballyImpure() + } + + override MacroInvocation getGeneratingMacro() { + // We only need the expression to be in the macro, not the semicolon. + result.getAnExpandedElement() = this.getExpr() + } +} + +/** + * A C/C++ 'continue' statement. + * + * For example, + * ``` + * continue; + * ``` + */ +class ContinueStmt extends JumpStmt, @stmt_continue { + + override string toString() { result = "continue;" } + + override predicate mayBeImpure() { none() } + override predicate mayBeGloballyImpure() { none() } + + /** + * Gets the loop that this continue statement will jump to the beginning of. + */ + Stmt getContinuable() { + result = getEnclosingContinuable(this) + } +} + +private Stmt getEnclosingContinuable(Stmt s) { + if s.getParent().getEnclosingStmt() instanceof Loop + then result = s.getParent().getEnclosingStmt() + else result = getEnclosingContinuable(s.getParent().getEnclosingStmt()) +} + +/** + * A C/C++ 'break' statement. + * + * For example, + * ``` + * break; + * ``` + */ +class BreakStmt extends JumpStmt, @stmt_break { + + override string toString() { result = "break;" } + + override predicate mayBeImpure() { none() } + override predicate mayBeGloballyImpure() { none() } + + /** + * Gets the loop or switch statement that this break statement will exit. + */ + Stmt getBreakable() { + result = getEnclosingBreakable(this) + } +} + +private Stmt getEnclosingBreakable(Stmt s) { + if s.getParent().getEnclosingStmt() instanceof Loop or s.getParent().getEnclosingStmt() instanceof SwitchStmt + then result = s.getParent().getEnclosingStmt() + else result = getEnclosingBreakable(s.getParent().getEnclosingStmt()) +} + +/** + * A C/C++ 'label' statement. + * + * For example, + * ``` + * someLabel: + * ``` + */ +class LabelStmt extends Stmt, @stmt_label { + + /** Gets the name of this 'label' statement. */ + string getName() { jumpinfo(this,result,_) and result != "" } + + /** Holds if this 'label' statement is named. */ + predicate isNamed() { exists(this.getName()) } + + override string toString() { result = "label ...:" } + + override predicate mayBeImpure() { none() } + override predicate mayBeGloballyImpure() { none() } +} + +/** + * A C/C++ 'return' statement. + * + * For example, + * ``` + * return 1+2; + * ``` + * or + * ``` + * return; + * ``` + */ +class ReturnStmt extends Stmt, @stmt_return { + + /** + * Gets the expression of this 'return' statement. + * + * For example, for + * ``` + * return 1+2; + * ``` + * the result is `1+2`, and there is no result for + * ``` + * return; + * ``` + */ + Expr getExpr() { result = this.getChild(0) } + + /** + * Holds if this 'return' statement has an expression. + * + * For example, this holds for + * ``` + * return 1+2; + * ``` + * but not for + * ``` + * return; + * ``` + */ + predicate hasExpr() { exists(this.getExpr()) } + + override string toString() { result = "return ..." } + + override predicate mayBeImpure() { + this.getExpr().mayBeImpure() + } + override predicate mayBeGloballyImpure() { + this.getExpr().mayBeGloballyImpure() + } +} + +/** + * A C/C++ 'do' statement. + * + * For example, + * ``` + * do { + * x = x + 1; + * } while (x < 10); + * ``` + */ +class DoStmt extends Loop, @stmt_end_test_while { + + override Expr getCondition() { result = this.getChild(0) } + override Expr getControllingExpr() { result = this.getCondition() } + override Stmt getStmt() { do_body(this, result) } + + override string toString() { result = "do (...) ..." } + + override predicate mayBeImpure() { + this.getCondition().mayBeImpure() or + this.getStmt().mayBeImpure() + } + override predicate mayBeGloballyImpure() { + this.getCondition().mayBeGloballyImpure() or + this.getStmt().mayBeGloballyImpure() + } + + override MacroInvocation getGeneratingMacro() { + result.getAnExpandedElement() = this.getCondition() and + this.getStmt().getGeneratingMacro() = result + } +} + +/** + * A C++11 range-based 'for' statement. + * + * For example, + * ``` + * for (int x : xs) { y += x; } + * ``` + * + * This example would be desugared to + * ``` + * { + * auto && __range = xs; + * for (auto __begin = begin_expr, __end = end_expr; + * __begin != __end; + * ++__begin) { + * int x = *__begin; + * y += x; + * } + * } + * ``` + * where `begin_expr` and `end_expr` depend on the type of `xs`. + */ +class RangeBasedForStmt extends Loop, @stmt_range_based_for { + /** + * Gets the 'body' statement of this range-based 'for' statement. + * + * For example, for + * ``` + * for (int x : xs) { y += x; } + * ``` + * the result is the `Block` `{ y += x; }`. + */ + override Stmt getStmt() { result = this.getChild(5) } + + override string toString() { result = "for(...:...) ..." } + + /** + * Gets the variable introduced by the for-range-declaration. + * + * For example, for + * ``` + * for (int x : xs) { y += x; } + * ``` + * the result is `int x`. + */ + Variable getVariable() { result = getChild(4).(DeclStmt).getADeclaration() } + + /** + * Gets the expression giving the range to iterate over. + * + * For example, for + * ``` + * for (int x : xs) { y += x; } + * ``` + * the result is `xs`. + */ + Expr getRange() { result = getRangeVariable().getInitializer().getExpr() } + + /** Gets the compiler-generated `__range` variable after desugaring. */ + Variable getRangeVariable() { + result = getChild(0).(DeclStmt).getADeclaration() + } + + /** + * Gets the compiler-generated `__begin != __end` which is the + * condition expression of this for statement after desugaring. + * It will be either an `NEExpr` or a call to a user-defined + * `operator!=`. + */ + override Expr getCondition() { result = this.getChild(2) } + override Expr getControllingExpr() { result = this.getCondition() } + + /** + * Gets the compiler-generated `++__begin` which is the update + * expression of this for statement after desugaring. It will + * be either a `PrefixIncrExpr` or a call to a user-defined + * `operator++`. + */ + Expr getUpdate() { result = this.getChild(3) } + + /** Gets the compiler-generated `__begin` variable after desugaring. */ + Variable getAnIterationVariable() { + result = getUpdate().getAChild().(VariableAccess).getTarget() + } +} + +/** + * A C/C++ 'for' statement. + * + * This only represents "traditional" 'for' statements and not C++11 + * range-based 'for' statements or Objective C 'for-in' statements. + * + * For example, + * ``` + * for (i = 0; i < 10; i++) { j++; } + * ``` + */ +class ForStmt extends Loop, @stmt_for { + + /** + * Gets the initialization statement of this 'for' statement. + * + * For example, for + * ``` + * for (i = 0; i < 10; i++) { j++; } + * ``` + * the result is `i = 0;`. + * + * Does not hold if the initialization statement is an empty statement, as in + * ``` + * for (; i < 10; i++) { j++ } + * ``` + */ + Stmt getInitialization() { for_initialization(this, result) } + + /** + * Gets the condition expression of this 'for' statement. + * + * For example, for + * ``` + * for (i = 0; i < 10; i++) { j++; } + * ``` + * the result is `i < 10`. + * + * Does not hold if the condition expression is omitted, as in + * ``` + * for (i = 0;; i++) { if (i >= 10) break; } + * ``` + */ + override Expr getCondition() { for_condition(this, result) } + override Expr getControllingExpr() { result = this.getCondition() } + + /** + * Gets the update expression of this 'for' statement. + * + * For example, for + * ``` + * for (i = 0; i < 10; i++) { j++; } + * ``` + * the result is `i++`. + * + * Does not hold if the update expression is omitted, as in + * ``` + * for (i = 0; i < 10;) { i++; } + * ``` + */ + Expr getUpdate() { for_update(this, result) } + + override Stmt getStmt() { for_body(this, result) } + + override string toString() { result = "for(...;...;...) ..." } + + /** + * Gets a variable that is used as an iteration variable. That is, a + * variables that is defined, updated or tested in the head of this + * for statement. + * + * This only has results that are quite certainly loop variables: for + * complex iterations, it may not return anything. + * + * For example, for + * ``` + * for (i = 0; i < 10; i++) { j++; } + * ``` + * the result is `i`. + */ + pragma[noopt] + Variable getAnIterationVariable() { + this instanceof ForStmt and + + // check that it is assigned to, incremented or decremented in the update + exists(Expr updateOpRoot, Expr updateOp | + updateOpRoot = this.getUpdate() and + inForUpdate(updateOpRoot, updateOp) | + exists(CrementOperation op, VariableAccess va | + op = updateOp and + op instanceof CrementOperation and + op.getOperand() = va and + va = result.getAnAccess() + ) + or + updateOp = result.getAnAssignedValue() + ) + and + result instanceof Variable + and + // checked or used in the condition + exists(Expr e, VariableAccess va | + va = result.getAnAccess() and + inForCondition(e, va) and + e = this.getCondition() + ) + } + + /** + * Gets a declaration from the initialization statement of this 'for' + * statement. + * + * For example, for + * ``` + * for(int x = 0, y = 10; x != y; ++x) { sum += x; } + * ``` + * the results are `x` and `y`, while for + * ``` + * for (i = 0; i < 10; i++) { j++; } + * ``` + * there are no results. + */ + override Declaration getADeclaration() { + result = this.getInitialization().(DeclStmt).getADeclaration() + } + + override predicate mayBeImpure() { + this.getInitialization().mayBeImpure() or + this.getCondition().mayBeImpure() or + this.getUpdate().mayBeImpure() or + this.getStmt().mayBeImpure() + } + override predicate mayBeGloballyImpure() { + this.getInitialization().mayBeGloballyImpure() or + this.getCondition().mayBeGloballyImpure() or + this.getUpdate().mayBeGloballyImpure() or + this.getStmt().mayBeGloballyImpure() + } + + override MacroInvocation getGeneratingMacro() { + (exists(this.getInitialization()) implies result = this.getInitialization().getGeneratingMacro()) and + (exists(this.getCondition()) implies this.getCondition() = result.getAnExpandedElement()) and + (exists(this.getUpdate()) implies this.getUpdate() = result.getAnExpandedElement()) and + this.getStmt().getGeneratingMacro() = result + } + + /** + * Holds if the loop condition is provably `true`. + * + * For example, this holds for + * ``` + * for(x = 0; 1; ++x) { sum += x; } + * ``` + */ + predicate conditionAlwaysTrue() { + conditionAlwaysTrue(getCondition()) + } + + /** + * Holds if the loop condition is provably `false`. + * + * For example, this holds for + * ``` + * for(x = 0; 0; ++x) { sum += x; } + * ``` + */ + predicate conditionAlwaysFalse() { + conditionAlwaysFalse(getCondition()) + } + + /** + * Holds if the loop condition is provably `true` upon entry, + * that is, at least one iteration of the loop is guaranteed. + * + * For example, with + * ``` + * for (int i = 0; i < 10; i++) { ... } + * ``` + * the condition `i < 10` always evaluates to `true` upon entry since + * `i = 0`, but the condition will evaluate to `false` after 10 + * iterations. + */ + predicate conditionAlwaysTrueUponEntry() { + loopConditionAlwaysTrueUponEntry(this, _) + } +} + +/** + * Holds if `child` is in the condition `forCondition` of a 'for' + * statement. + * + * For example, if a program includes + * ``` + * for (i = 0; i < 10; i++) { j++; } + * ``` + * then this predicate will hold with `forCondition` as `i < 10`, + * and `child` as any of `i`, `10` and `i < 10`. + */ +pragma[noopt] +private predicate inForCondition(Expr forCondition, Expr child) { + exists (ForStmt for | forCondition = for.getCondition() and + child = forCondition and + for instanceof ForStmt) + or + exists (Expr mid | inForCondition(forCondition, mid) and + child.getParent() = mid) +} + +/** + * Holds if `child` is in the update `forUpdate` of a 'for' statement. + * + * For example, if a program includes + * ``` + * for (i = 0; i < 10; i += 1) { j++; } + * ``` + * then this predicate will hold with `forUpdate` as `i += 1`, + * and `child` as any of `i`, `1` and `i += 1`. + */ +pragma[noopt] +private predicate inForUpdate(Expr forUpdate, Expr child) { + exists (ForStmt for | forUpdate = for.getUpdate() and child = forUpdate) + or + exists (Expr mid | inForUpdate(forUpdate, mid) and child.getParent() = mid) +} + +/** + * A C/C++ 'switch case' statement. + * + * For example, + * ``` + * case 5: + * ``` + * or + * ``` + * default: + * ``` + */ +class SwitchCase extends Stmt, @stmt_switch_case { + + /** + * Gets the expression of this 'switch case' statement (or the start of + * the range if there is a GNU case range). Does not exist for a + * `DefaultCase`. + * + * For example, for + * ``` + * case 5: + * ``` + * the result is `5`, for + * ``` + * case 6 ... 7: + * ``` + * the result is 6, and there is no result for + * ``` + * default: + * ``` + */ + Expr getExpr() { result = this.getChild(0) } + + /** + * Gets the end of the range, if this is a GNU case range. Otherwise + * has no result. + * + * For example, for + * ``` + * case 6 ... 7: + * ``` + * the result is `7`, while for + * ``` + * case 5: + * ``` + * and + * ``` + * default: + * ``` + * there is no result. + */ + Expr getEndExpr() { result = this.getChild(1) } + + /** + * Gets the 'switch' statement of this 'switch case' statement. + * + * For example, with + * ``` + * switch(i) { + * case 5: + * x = 1; + * } + * ``` + * the result of this predicate on `case 5:` is the whole + * `switch(i) { ... }` statement. + */ + SwitchStmt getSwitchStmt() { result.getASwitchCase() = this } + + /** + * Gets the 0-based index of this 'switch case' statement within its + * 'switch' statement. + * + * For example, for + * ``` + * switch(i) { + * case 5: + * case 6: + * default: + * } + * ``` + * the `case 5:` has result 0, `case 6:` has result 1, and `default:` + * has result 2. + */ + int getChildNum() { + switch_case(_, result, this) + } + + /** + * DEPRECATED: use `SwitchCase.getAStmt` or `ControlFlowNode.getASuccessor` + * rather than this predicate. + * + * Gets the `Block` statement immediately following this 'switch case' + * statement, if any. + * + * For example, for + * ``` + * switch (i) { + * case 5: + * x = 1; + * break; + * case 6: + * case 7: + * { x = 2; break; } + * default: + * { x = 3; } + * x = 4; + * break; + * } + * ``` + * the `case 7:` has result `{ x = 2; break; }`, `default:` has result + * `{ x = 3; }`, and the others have no result. + */ + deprecated Block getLabelledStmt() { + exists(int i, Stmt parent | + this = parent.getChild(i) and + result = parent.getChild(i+1)) + } + + /** + * Gets the next `SwitchCase` belonging to the same 'switch' + * statement, if any. + * + * For example, for + * ``` + * switch (i) { + * case 5: + * x = 1; + * break; + * case 6: + * case 7: + * { x = 2; break; } + * default: + * { x = 3; } + * x = 4; + * break; + * } + * ``` + * the `case 5:` has result `case 6:`, which has result `case 7:`, + * which has result `default:`, which has no result. + */ + SwitchCase getNextSwitchCase() { + result.getSwitchStmt() = this.getSwitchStmt() and + result.getChildNum() = this.getChildNum() + 1 + } + + /** + * Gets the previous `SwitchCase` belonging to the same 'switch' + * statement, if any. + * + * For example, for + * ``` + * switch (i) { + * case 5: + * x = 1; + * break; + * case 6: + * case 7: + * { x = 2; break; } + * default: + * { x = 3; } + * x = 4; + * break; + * } + * ``` + * the `default:` has result `case 7:`, which has result `case 6:`, + * which has result `case 5:`, which has no result. + */ + SwitchCase getPreviousSwitchCase() { + result.getNextSwitchCase() = this + } + + /** + * Gets a statement belonging under this 'switch case' statement. + * + * For example, for + * ``` + * switch (i) { + * case 5: + * x = 1; + * break; + * case 6: + * case 7: + * { x = 2; break; } + * default: + * { x = 3; } + * x = 4; + * break; + * } + * ``` + * the `case 5:` has results `x = 1;` and `break;`, `case 6:` has no + * results, `case 7:` has a single result `{ x = 2; break; }`, and + * `default:` has results `{ x = 3; }, `x = 4;` and `break;`. + */ + Stmt getAStmt() { + exists(Block b, int i, int j | + b.getStmt(i) = this and + b.getStmt(j) = result and + i < j and + not result instanceof SwitchCase and + not exists(SwitchCase sc, int k | + b.getStmt(k) = sc and + i < k and + j > k)) + } + + /** + * Gets the last statement under this 'switch case' statement. If the + * last statement is wrapped in one or more blocks then the result is + * the last statement in those blocks instead. + * + * For example, for + * ``` + * switch (i) { + * case 5: + * x = 1; + * break; + * case 6: + * case 7: + * { x = 2; break; } + * default: + * { x = 3; { x = 4; break; } } + * } + * ``` + * the `case 5:` has result `break;`, the `case 6:` has no result, + * the `case 7:` has results `break;`, and the `default:` has result + * `break;`. + */ + Stmt getLastStmt() { + exists(Stmt lastStmt | + lastStmt = this.getAStmt() and + not lastStmt.getFollowingStmt() = this.getAStmt() and + if lastStmt instanceof Block then + result = lastStmt.(Block).getLastStmtIn() + else + result = lastStmt + ) + } + + /** + * Holds if the last statement, as determined by `getLastStmt`, under + * this 'switch case' statement is a 'break' statement. + * + * For example, for + * ``` + * switch (i) { + * case 5: + * x = 1; + * break; + * case 6: + * case 7: + * { x = 2; break; } + * default: + * { x = 3; { x = 4; break; } } + * } + * ``` + * this holds for `case 5:`, `case 7:` and `default:`, but not for `case 6:`. + */ + predicate terminatesInBreakStmt() { + this.getLastStmt() instanceof BreakStmt + } + + /** + * Holds if the last statement, as determined by `getLastStmt`, under + * this 'switch case' statement is a 'return' statement. + * + * For example, for + * ``` + * switch (i) { + * case 5: + * x = 1; + * return; + * case 6: + * case 7: + * { x = 2; return; } + * default: + * { x = 3; { x = 4; return; } } + * } + * ``` + * this holds for `case 5:`, `case 7:` and `default:`, but not for `case 6:`. + */ + predicate terminatesInReturnStmt() { + this.getLastStmt() instanceof ReturnStmt + } + + /** + * Holds if the last statement, as determined by `getLastStmt`, under + * this 'switch case' statement is a 'throw' statement. + * + * For example, for + * ``` + * switch (i) { + * case 5: + * x = 1; + * throw 1; + * case 6: + * case 7: + * { x = 2; throw 2; } + * default: + * { x = 3; { x = 4; throw 3; } } + * } + * ``` + * this holds for `case 5:`, `case 7:` and `default:`, but not for `case 6:`. + */ + predicate terminatesInThrowStmt() { + exists(ThrowExpr t | t.getEnclosingStmt() = this.getLastStmt()) + } + + /** + * Holds if this 'switch case' statement is a 'default' statement. + * + * For example, for + * ``` + * switch (i) { + * case 5: + * case 6: + * case 7: + * default: + * } + * ``` + * this holds for `default:`, but not for `case 5:`, `case 6:`, + * or `case 7:`. + */ + predicate isDefault() { + this instanceof DefaultCase + } + + override string toString() { result = "case ...:" } + + override predicate mayBeImpure() { + this.getExpr().mayBeImpure() + } + override predicate mayBeGloballyImpure() { + this.getExpr().mayBeGloballyImpure() + } +} + +/** + * A C/C++ 'default case' statement. + * + * For example, + * ``` + * default: + * ``` + */ +class DefaultCase extends SwitchCase { + + DefaultCase() { not exists(this.getExpr()) } + + override string toString() { result = "default: " } + + override predicate mayBeImpure() { + none() + } + override predicate mayBeGloballyImpure() { + none() + } +} + +/** + * A C/C++ 'switch' statement. + * + * For example, + * ``` + * switch(i) { + * case 1: + * case 2: + * break; + * default: + * break; + * } + * ``` + */ +class SwitchStmt extends ConditionalStmt, @stmt_switch { + /** + * Gets the expression that this 'switch' statement switches on. + * + * For example, for + * ``` + * switch(i) { + * case 1: + * case 2: + * break; + * default: + * break; + * } + * ``` + * the result is `i`. + */ + Expr getExpr() { result = this.getChild(0) } + override Expr getControllingExpr() { result = this.getExpr() } + + /** + * Gets the body statement of this 'switch' statement. + * + * In almost all cases the result will be a `BlockStmt`, but there are + * other syntactically valid constructions. + * + * For example, for + * ``` + * switch(i) { + * case 1: + * case 2: + * break; + * default: + * break; + * } + * ``` + * the result is + * ``` + * { + * case 1: + * case 2: + * break; + * default: + * break; + * } + * ``` + */ + Stmt getStmt() { switch_body(this, result) } + + /** + * Gets a 'switch case' statement of this 'switch' statement. + * + * For example, for + * ``` + * switch(i) { + * case 1: + * case 2: + * break; + * default: + * break; + * } + * ``` + * the results are `case 1:`, `case 2:` and `default:`. + */ + SwitchCase getASwitchCase() { switch_case(this, _, result) } + + /** + * Gets the 'default case' statement of this 'switch' statement, + * if any. + * + * For example, for + * ``` + * switch(i) { + * case 1: + * case 2: + * break; + * default: + * break; + * } + * ``` + * the result is `default:`, but there is no result for + * ``` + * switch(i) { + * case 1: + * case 2: + * break; + * } + * ``` + */ + DefaultCase getDefaultCase() { result = this.getASwitchCase() } + + /** + * Holds if this 'switch' statement has a 'default case' statement. + * + * For example, this holds for + * ``` + * switch(i) { + * case 1: + * case 2: + * break; + * default: + * break; + * } + * ``` + * but not for + * ``` + * switch(i) { + * case 1: + * case 2: + * break; + * } + * ``` + */ + predicate hasDefaultCase() { exists(this.getDefaultCase()) } + + override string toString() { result = "switch (...) ... " } + + override predicate mayBeImpure() { + this.getExpr().mayBeImpure() or + this.getStmt().mayBeImpure() + } + override predicate mayBeGloballyImpure() { + this.getExpr().mayBeGloballyImpure() or + this.getStmt().mayBeGloballyImpure() + } + + override MacroInvocation getGeneratingMacro() { + result.getAnExpandedElement() = this.getExpr() and + forall(SwitchCase c | c = this.getASwitchCase() + | exists(c.getGeneratingMacro())) + } +} + +/** + * A C/C++ 'switch' statement where the controlling expression has an + * enum type. + * + * For example, given + * ``` + * enum color { RED, GREEN, BLUE }; + * enum color c; + * ``` + * the 'switch' statement + * ``` + * switch (c) { + * case RED: + * return 1; + * default: + * return 2; + * } + * ``` + */ +class EnumSwitch extends SwitchStmt { + EnumSwitch() { + this.getExpr().getType().getUnderlyingType() instanceof Enum + } + + /** + * Gets a constant from the enum type that does not have a case in this + * 'switch' statement. + * + * For example, with + * ``` + * enum color { RED, GREEN, BLUE }; + * enum color c; + * switch (c) { + * case RED: + * return 1; + * default: + * return 2; + * } + * ``` + * there are results `GREEN` and `BLUE`. + */ + pragma[noopt] + EnumConstant getAMissingCase() { + exists(Enum et | + exists(Expr e, Type t | + e = this.getExpr() and + this instanceof EnumSwitch and + t = e.getType() and + et = t.getUnderlyingType() + ) and + result = et.getAnEnumConstant() and + not exists(string value | + exists(SwitchCase sc, Expr e | sc = this.getASwitchCase() and + e = sc.getExpr() and + value = e.getValue()) + and + exists(Initializer init, Expr e | init = result.getInitializer() and + e = init.getExpr() and + e.getValue() = value) + ) + ) + } +} + +/** + * A handler for a 'try' statement. + * + * This corresponds to a 'catch block' in the source. If the exception + * is of a type that can be handled by this 'catch block', then + * execution continues with the associated `CatchBlock`. Otherwise, + * execution continues with the next `Handler`. + * + * This has no concrete representation in the source, but makes the + * control flow graph easier to use. + */ +class Handler extends Stmt, @stmt_handler { + + override string toString() { result = "" } + + /** + * Gets the block containing the implementation of this handler. + */ + CatchBlock getBlock() { result = getChild(0) } + + /** Gets the 'try' statement corresponding to this 'catch block'. */ + TryStmt getTryStmt() { result = getParent() } + + /** + * Gets the parameter introduced by this 'catch block', if any. + * + * For example, `catch(std::exception& e)` introduces a + * parameter `e`, whereas `catch(...)` does not introduce a parameter. + */ + Parameter getParameter() { result = getBlock().getParameter() } + + override predicate mayBeImpure() { + none() + } + override predicate mayBeGloballyImpure() { + none() + } +} + +/** + * DEPRECATED: Objective-C is no longer supported. + * The end of a 'finally' clause. + * + * This has no concrete representation in the source, but makes the + * control flow graph easier to use. + */ +deprecated class FinallyEnd extends Stmt { + FinallyEnd() { none() } + + override string toString() { result = "" } + + override predicate mayBeImpure() { + none() + } + override predicate mayBeGloballyImpure() { + none() + } +} + +/** + * A C/C++ 'try' statement. + * + * For example, + * ``` + * try { f(); } catch (...) { g(); } + * ``` + */ +class TryStmt extends Stmt, @stmt_try_block { + + override string toString() { result = "try { ... }" } + + /** + * Gets the 'body' statement of this 'try' statement. + * + * For example, for + * ``` + * try { f(); } catch (...) { g(); } + * ``` + * the result is `{ f(); }`. + */ + Stmt getStmt() { result = this.getChild(0) } + + /** + * Gets the `n`th 'catch block' of this 'try' statement. + * + * For example, for + * ``` + * try { f(); } catch (...) { g(); } + * ``` + * the result of `getCatchClause(0)` is `{ g(); }`. + */ + CatchBlock getCatchClause(int n) { + result = this.getChild(n + 1).(Handler).getBlock() + } + + /** + * Gets a 'catch block' of this 'try' statement. + * + * For example, for + * ``` + * try { f(); } catch (...) { g(); } + * ``` + * the result is `{ g(); }`. + */ + CatchBlock getACatchClause() { result = this.getCatchClause(_) } + + /** + * Gets the number of 'catch block's of this 'try' statement. + * + * For example, for + * ``` + * try { f(); } catch (...) { g(); } + * ``` + * the result is 1. + */ + int getNumberOfCatchClauses() { + result = count(this.getACatchClause()) + } + + override predicate mayBeImpure() { + this.getStmt().mayBeImpure() or + this.getACatchClause().mayBeImpure() + } + override predicate mayBeGloballyImpure() { + this.getStmt().mayBeGloballyImpure() or + this.getACatchClause().mayBeGloballyImpure() + } +} + +/** + * A C++ 'function try' statement. + * + * This is a 'try' statement wrapped around an entire function body, + * for example: + * ``` + * void foo() try { + * f(); + * } catch(...) { + * g(); + * } + * ``` + */ +class FunctionTryStmt extends TryStmt { + FunctionTryStmt() { + not exists(this.getEnclosingBlock()) + } +} + +/** + * A 'catch block', from either C++'s `catch` or Objective C's `@catch`. + */ +class CatchBlock extends Block { + CatchBlock() { ishandler(this) } + + /** + * Gets the parameter introduced by this 'catch block', if any. + * + * For example, `catch(std::exception& e)` introduces a parameter + * `e`, whereas `catch(...)` does not introduce a parameter. + */ + Parameter getParameter() { result.getCatchBlock() = this } + + /** Gets the try statement corresponding to this 'catch block'. */ + TryStmt getTryStmt() { + result.getACatchClause() = this + } +} + +/** + * A C++ 'catch-any block', that is, `catch(...) {stmts}`. + */ +class CatchAnyBlock extends CatchBlock { + CatchAnyBlock() { + not exists(this.getParameter()) + } +} + +/** + * A structured exception handling 'try' statement, that is, a + * `__try __except` or `__try __finally` statement. This is a Microsoft + * C/C++ extension. + */ +class MicrosoftTryStmt extends Stmt, @stmt_microsoft_try { + /** Gets the body statement of this __try statement. */ + Stmt getStmt() { result = this.getChild(0) } +} + +/** + * A structured exception handling 'try except' statement, that is, + * a `__try __except` statement. This is a Microsoft C/C++ extension. + */ +class MicrosoftTryExceptStmt extends MicrosoftTryStmt { + MicrosoftTryExceptStmt() { + getChild(1) instanceof Expr + } + + override string toString() { + result = "__try { ... } __except( ... ) { ... }" + } + + /** Gets the expression guarding the `__except` statement. */ + Expr getCondition() { result = getChild(1) } + + /** Gets the `__except` statement (usually a `Block`). */ + Stmt getExcept() { result = getChild(2) } +} + +/** + * A structured exception handling 'try finally' statement, that is, + * a `__try __finally` statement. This is a Microsoft C/C++ extension. + */ +class MicrosoftTryFinallyStmt extends MicrosoftTryStmt { + MicrosoftTryFinallyStmt() { + not getChild(1) instanceof Expr + } + + override string toString() { result = "__try { ... } __finally { ... }" } + + /** Gets the `__finally` statement (usually a `Block`). */ + Stmt getFinally() { result = getChild(1) } +} + +/** + * A C/C++ 'declaration' statement. + * + * For example, + * ``` + * int i, j; + * ``` + */ +class DeclStmt extends Stmt, @stmt_decl { + + /** + * Gets the `i`th declaration entry declared by this 'declaration' statement. + * + * For example, for + * ``` + * int i, j; + * ``` + * the result of `getDeclarationEntry(0)` is `i`. + */ + DeclarationEntry getDeclarationEntry(int i) { + stmt_decl_entry_bind(this, i, result) + } + + /** + * Gets a declaration entry declared by this 'declaration' statement. + * + * For example, for + * ``` + * int i, j; + * ``` + * the results are `i` and `j`. + */ + DeclarationEntry getADeclarationEntry() { + result = this.getDeclarationEntry(_) + } + + /** + * Gets the number of declarations declared by this 'declaration' statement. + * + * For example, for + * ``` + * int i, j; + * ``` + * the result of `getNumDeclarations()` is `2`. + */ + int getNumDeclarations() { result = count(this.getADeclaration()) } + + /** + * Gets the `i`th declaration declared by this 'declaration' statement. + * + * For example, for + * ``` + * int i, j; + * ``` + * the result of `getDeclaration(0)` is `i`. + */ + Declaration getDeclaration(int i) { stmt_decl_bind(this, i, result) } + + /** + * Gets a declaration declared by this 'declaration' statement. + * + * For example, for + * ``` + * int i, j; + * ``` + * the results are `i` and `j`. + */ + Declaration getADeclaration() { result = this.getDeclaration(_) } + + override string toString() { result = "declaration" } + + override predicate mayBeImpure() { + this.getADeclaration().(LocalVariable).getInitializer().getExpr().mayBeImpure() + } + override predicate mayBeGloballyImpure() { + this.getADeclaration().(LocalVariable).getInitializer().getExpr().mayBeGloballyImpure() + } +} + +/** + * A C/C++ 'empty' statement. + * + * For example, + * ``` + * ; + * ``` + */ +class EmptyStmt extends Stmt, @stmt_empty { + + override string toString() { result = ";" } + + override predicate mayBeImpure() { none() } + override predicate mayBeGloballyImpure() { none() } +} + +/** + * A C/C++ 'asm' statement. + * + * For example, + * ``` + * __asm__("movb %bh (%eax)"); + * ``` + */ +class AsmStmt extends Stmt, @stmt_asm { + override string toString() { + result = "asm statement" + } +} + +/** + * A C99 statement which computes the size of a single dimension of + * a variable length array. + * + * Each `VlaDeclStmt` is preceded by one `VlaDimensionStmt` for each + * variable length dimension of the array. + */ +class VlaDimensionStmt extends Stmt, @stmt_set_vla_size { + override string toString() { result = "VLA dimension size" } + + /** Gets the expression which gives the size. */ + Expr getDimensionExpr() { result = this.getChild(0) } +} + +/** + * A C99 statement which declares a variable length array. + * + * Each `VlaDeclStmt` is preceded by one `VlaDimensionStmt` for each + * variable length dimension of the array. + */ +class VlaDeclStmt extends Stmt, @stmt_vla_decl { + override string toString() { result = "VLA declaration" } + + /** + * Gets the number of VLA dimension statements in this VLA + * declaration statement. + */ + int getNumberOfVlaDimensionStmts() { + exists(Block b, int j | + this = b.getStmt(j) and + result = j - 1 - max(int i | + i in [0 .. j - 1] and + not b.getStmt(i) instanceof VlaDimensionStmt)) + } + + /** + * Gets the `i`th VLA dimension statement in this VLA + * declaration statement. + */ + VlaDimensionStmt getVlaDimensionStmt(int i) { + i in [0 .. this.getNumberOfVlaDimensionStmts() - 1] and + exists(Block b, int j | + this = b.getStmt(j) and + result = b.getStmt(j - this.getNumberOfVlaDimensionStmts() + i)) + } + + /** + * Gets the type that this VLA declaration statement relates to, + * if any. + */ + Type getType() { type_vla(unresolve(result), this) } + + /** + * Gets the variable that this VLA declaration statement relates to, + * if any. + */ + Variable getVariable() { variable_vla(result, this) } +} diff --git a/cpp/ql/src/semmle/code/cpp/valuenumbering/GlobalValueNumbering.qll b/cpp/ql/src/semmle/code/cpp/valuenumbering/GlobalValueNumbering.qll new file mode 100644 index 000000000000..1a3ac62e84bf --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/valuenumbering/GlobalValueNumbering.qll @@ -0,0 +1,593 @@ +/** + * Provides an implementation of Global Value Numbering. + * See https://en.wikipedia.org/wiki/Global_value_numbering + * + * The predicate `globalValueNumber` converts an expression into a `GVN`, + * which is an abstract type presenting the value of the expression. If + * two expressions have the `GVN` then they compute the same value. + * For example: + * + * ``` + * void f(int x, int y) { + * g(x+y, x+y); + * } + * ``` + * + * In this example, both arguments in the call to `g` compute the same value, + * so both arguments have the same `GVN`. In other words, we can find + * this call with the following query: + * + * ``` + * from FunctionCall call, GVN v + * where v = globalValueNumber(call.getArgument(0)) + * and v = globalValueNumber(call.getArgument(1)) + * select call + * ``` + * + * The analysis is conservative, so two expressions might have different + * `GVN`s even though the actually always compute the same value. The most + * common reason for this is that the analysis cannot prove that there + * are no side-effects that might cause the computed value to change. + */ + +/* + * Note to developers: the correctness of this module depends on the + * definitions of GVN, globalValueNumber, and analyzableExpr being kept in + * sync with each other. If you change this module then make sure that the + * change is symmetric across all three. + */ + +import cpp +private import semmle.code.cpp.controlflow.SSA + +/** + * Holds if the result is a control flow node that might change the + * value of any global variable. This is used in the implementation + * of `GVN_OtherVariable`, because we need to be quite conservative when + * we assign a value number to a global variable. For example: + * + * ``` + * x = g+1; + * dosomething(); + * y = g+1; + * ``` + * + * It is not safe to assign the same value number to both instances + * of `g+1` in this example, because the call to `dosomething` might + * change the value of `g`. + */ +private ControlFlowNode nodeWithPossibleSideEffect() { + result instanceof Call + or + // If the lhs of an assignment is not analyzable by SSA, then + // we need to treat the assignment as having a possible side-effect. + (result instanceof Assignment and not result instanceof SsaDefinition) + or + (result instanceof CrementOperation and not result instanceof SsaDefinition) + or + exists (LocalVariable v + | result = v.getInitializer().getExpr() and not result instanceof SsaDefinition) + or + result instanceof AsmStmt +} + +/** + * Gets the entry node of the control flow graph of which `node` is a + * member. + */ +private cached ControlFlowNode getControlFlowEntry(ControlFlowNode node) { + result = node.getControlFlowScope().getEntryPoint() and + result.getASuccessor*() = node +} + +/** + * Holds if there is a control flow edge from `src` to `dst` or + * if `dst` is an expression with a possible side-effect. The idea + * is to treat side effects as entry points in the control flow + * graph so that we can use the dominator tree to find the most recent + * side-effect. + */ +private predicate sideEffectCFG(ControlFlowNode src, ControlFlowNode dst) { + src.getASuccessor() = dst + or + // Add an edge from the entry point to any node that might have a side + // effect. + (dst = nodeWithPossibleSideEffect() and + src = getControlFlowEntry(dst)) +} + +/** + * Holds if `dominator` is the immediate dominator of `node` in + * the side-effect CFG. + */ +private predicate iDomEffect(ControlFlowNode dominator, ControlFlowNode node) = + idominance(functionEntry/1,sideEffectCFG/2)(_, dominator, node) + +/** + * Gets the most recent side effect. To be more precise, `result` is a + * dominator of `node` and no side-effects can occur between `result` and + * `node`. + * + * `sideEffectCFG` has an edge from the function entry to every node with a + * side-effect. This means that every node with a side-effect has the + * function entry as its immediate dominator. So if node `x` dominates node + * `y` then there can be no side effects between `x` and `y` unless `x` is + * the function entry. So the optimal choice for `result` has the function + * entry as its immediate dominator. + * + * Example: + * + * ``` + * 000: int f(int a, int b, int *p) { + * 001: int r = 0; + * 002: if (a) { + * 003: if (b) { + * 004: sideEffect1(); + * 005: } + * 006: } else { + * 007: sideEffect2(); + * 008: } + * 009: if (a) { + * 010: r++; // Not a side-effect, because r is an SSA variable. + * 011: } + * 012: if (b) { + * 013: r++; // Not a side-effect, because r is an SSA variable. + * 014: } + * 015: return *p; + * 016: } + * ``` + * + * Suppose we want to find the most recent side-effect for the dereference + * of `p` on line 015. The `sideEffectCFG` has an edge from the function + * entry (line 000) to the side effects at lines 004 and 007. Therefore, + * the immediate dominator tree looks like this: + * + * 000 - 001 - 002 - 003 + * - 004 + * - 007 + * - 009 - 010 + * - 012 - 013 + * - 015 + * + * The immediate dominator path to line 015 is 000 - 009 - 012 - 015. + * Therefore, the most recent side effect for line 015 is line 009. + */ +private cached ControlFlowNode mostRecentSideEffect(ControlFlowNode node) { + exists (ControlFlowNode entry + | functionEntry(entry) and + iDomEffect(entry, result) and + iDomEffect*(result, node)) +} + +/** Used to represent the "global value number" of an expression. */ +private cached newtype GVNBase = + GVN_IntConst(int val, Type t) { mk_IntConst(val,t,_) } + or + GVN_FloatConst(float val, Type t) { mk_FloatConst(val,t,_) } + or + // If the local variable does not have a defining value, then + // we use the SsaDefinition as its global value number. + GVN_UndefinedLocalScopeVariable(LocalScopeVariable x, SsaDefinition def) { + mk_UndefinedLocalScopeVariable(x, def, _) + } + or + // Variables with no SSA information. As a crude (but safe) + // approximation, we use `mostRecentSideEffect` to compute a definition + // location for the variable. This ensures that two instances of the same + // global variable will only get the same value number if they are + // guaranteed to have the same value. + GVN_OtherVariable(Variable x, ControlFlowNode dominator) { + mk_OtherVariable(x, dominator, _) + } + or + GVN_FieldAccess(GVN s, Field f) { + mk_DotFieldAccess(s,f,_) or + mk_PointerFieldAccess_with_deref(s,f,_) or + mk_ImplicitThisFieldAccess_with_deref(s,f,_) + } + or + // Dereference a pointer. The value might have changed since the last + // time the pointer was dereferenced, so we need to include a definition + // location. As a crude (but safe) approximation, we use + // `mostRecentSideEffect` to compute a definition location. + GVN_Deref(GVN p, ControlFlowNode dominator) { + mk_Deref(p,dominator,_) or + mk_PointerFieldAccess(p,_,dominator,_) or + mk_ImplicitThisFieldAccess_with_qualifier(p,_,dominator,_) + } + or + GVN_ThisExpr(Function fcn) { + mk_ThisExpr(fcn,_) or + mk_ImplicitThisFieldAccess(fcn,_,_,_) + } + or + GVN_Conversion(Type t, GVN child) { mk_Conversion(t, child, _) } + or + GVN_BinaryOp(GVN lhs, GVN rhs, string opname) { + mk_BinaryOp(lhs, rhs, opname, _) + } + or + GVN_UnaryOp(GVN child, string opname) { mk_UnaryOp(child, opname, _) } + or + GVN_ArrayAccess(GVN x, GVN i, ControlFlowNode dominator) { + mk_ArrayAccess(x,i,dominator,_) + } + or + // Any expression that is not handled by the cases above is + // given a unique number based on the expression itself. + GVN_Unanalyzable(Expr e) { not analyzableExpr(e) } + +/** + * A Global Value Number. A GVN is an abstract representation of the value + * computed by an expression. The relationship between `Expr` and `GVN` is + * many-to-one: every `Expr` has exactly one `GVN`, but multiple + * expressions can have the same `GVN`. If two expressions have the same + * `GVN`, it means that they compute the same value at run time. The `GVN` + * is an opaque value, so you cannot deduce what the run-time value of an + * expression will be from its `GVN`. The only use for the `GVN` of an + * expression is to find other expressions that compute the same value. + * Use the predicate `globalValueNumber` to get the `GVN` for an `Expr`. + * + * Note: `GVN` has `toString` and `getLocation` methods, so that it can be + * displayed in a results list. These work by picking an arbitrary + * expression with this `GVN` and using its `toString` and `getLocation` + * methods. + */ +class GVN extends GVNBase { + GVN() { this instanceof GVNBase } + + /** Gets an expression that has this GVN. */ + Expr getAnExpr() { + this = globalValueNumber(result) + } + + /** Gets the kind of the GVN. This can be useful for debugging. */ + string getKind() { + if this instanceof GVN_IntConst then result = "IntConst" else + if this instanceof GVN_FloatConst then result = "FloatConst" else + if this instanceof GVN_UndefinedLocalScopeVariable then + result = "UndefinedLocalScopeVariable" else + if this instanceof GVN_OtherVariable then result = "OtherVariable" else + if this instanceof GVN_FieldAccess then result = "FieldAccess" else + if this instanceof GVN_Deref then result = "Deref" else + if this instanceof GVN_ThisExpr then result = "ThisExpr" else + if this instanceof GVN_Conversion then result = "Conversion" else + if this instanceof GVN_BinaryOp then result = "BinaryOp" else + if this instanceof GVN_UnaryOp then result = "UnaryOp" else + if this instanceof GVN_ArrayAccess then result = "ArrayAccess" else + if this instanceof GVN_Unanalyzable then result = "Unanalyzable" else + result = "error" + } + + /** + * Gets an example of an expression with this GVN. + * This is useful for things like implementing toString(). + */ + private Expr exampleExpr() { + // Pick the expression with the minimum source location string. This is + // just an arbitrary way to pick an expression with this `GVN`. + result = + min(Expr e + | this = globalValueNumber(e) + | e order by e.getLocation().toString()) + } + + /** Gets a textual representation of this element. */ + string toString() { + result = exampleExpr().toString() + } + + /** Gets the primary location of this element. */ + Location getLocation() { + result = exampleExpr().getLocation() + } +} + +private predicate analyzableIntConst(Expr e) { + strictcount (e.getValue().toInt()) = 1 and + strictcount (e.getType().getUnspecifiedType()) = 1 +} + +private predicate mk_IntConst(int val, Type t, Expr e) { + analyzableIntConst(e) and + val = e.getValue().toInt() and + t = e.getType().getUnspecifiedType() +} + +private predicate analyzableFloatConst(Expr e) { + strictcount (e.getValue().toFloat()) = 1 and + strictcount (e.getType().getUnspecifiedType()) = 1 and + not analyzableIntConst(e) +} + +private predicate mk_FloatConst(float val, Type t, Expr e) { + analyzableFloatConst(e) and + val = e.getValue().toFloat() and + t = e.getType().getUnspecifiedType() +} + + +private predicate analyzableLocalScopeVariable(VariableAccess access) { + strictcount (SsaDefinition def | def.getAUse(_) = access | def) = 1 and + strictcount (SsaDefinition def, Variable v | def.getAUse(v) = access | v) = 1 and + count (SsaDefinition def, Variable v + | def.getAUse(v) = access + | def.getDefiningValue(v)) <= 1 and + not analyzableConst(access) +} + +// Note: this predicate only has a result if the access has no +// defining value. If there is a defining value, then there is no +// need to generate a fresh `GVN` for the access because `globalValueNumber` +// will follow the chain and use the GVN of the defining value. +private predicate mk_UndefinedLocalScopeVariable( + LocalScopeVariable x, SsaDefinition def, VariableAccess access) { + analyzableLocalScopeVariable(access) and + access = def.getAUse(x) and + not exists (def.getDefiningValue(x)) +} + +private predicate analyzableDotFieldAccess(DotFieldAccess access) { + strictcount (access.getTarget()) = 1 and + strictcount (access.getQualifier().getFullyConverted()) = 1 and + not analyzableConst(access) +} + +private predicate mk_DotFieldAccess( + GVN qualifier, Field target, DotFieldAccess access) { + analyzableDotFieldAccess(access) and + target = access.getTarget() and + qualifier = globalValueNumber(access.getQualifier().getFullyConverted()) +} + +private predicate analyzablePointerFieldAccess(PointerFieldAccess access) { + strictcount (mostRecentSideEffect(access)) = 1 and + strictcount (access.getTarget()) = 1 and + strictcount (access.getQualifier().getFullyConverted()) = 1 and + not analyzableConst(access) +} + +private predicate mk_PointerFieldAccess( + GVN qualifier, Field target, ControlFlowNode dominator, + PointerFieldAccess access) { + analyzablePointerFieldAccess(access) and + dominator = mostRecentSideEffect(access) and + target = access.getTarget() and + qualifier = globalValueNumber(access.getQualifier().getFullyConverted()) +} + +/* + * `obj->field` is equivalent to `(*obj).field`, so we need to wrap an + * extra `GVN_Deref` around the qualifier. + */ +private predicate mk_PointerFieldAccess_with_deref( + GVN new_qualifier, Field target, PointerFieldAccess access) { + exists (GVN qualifier, ControlFlowNode dominator + | mk_PointerFieldAccess(qualifier, target, dominator, access) and + new_qualifier = GVN_Deref(qualifier, dominator)) +} + +private predicate analyzableImplicitThisFieldAccess( + ImplicitThisFieldAccess access) { + strictcount (mostRecentSideEffect(access)) = 1 and + strictcount (access.getTarget()) = 1 and + strictcount (access.getEnclosingFunction()) = 1 and + not analyzableConst(access) +} + +private predicate mk_ImplicitThisFieldAccess( + Function fcn, Field target, ControlFlowNode dominator, + ImplicitThisFieldAccess access) { + analyzableImplicitThisFieldAccess(access) and + dominator = mostRecentSideEffect(access) and + target = access.getTarget() and + fcn = access.getEnclosingFunction() +} + +private predicate mk_ImplicitThisFieldAccess_with_qualifier( + GVN qualifier, Field target, ControlFlowNode dominator, + ImplicitThisFieldAccess access) { + exists (Function fcn + | mk_ImplicitThisFieldAccess(fcn, target, dominator, access) and + qualifier = GVN_ThisExpr(fcn)) +} + +private predicate mk_ImplicitThisFieldAccess_with_deref( + GVN new_qualifier, Field target, ImplicitThisFieldAccess access) { + exists (GVN qualifier, ControlFlowNode dominator + | mk_ImplicitThisFieldAccess_with_qualifier( + qualifier, target, dominator, access) and + new_qualifier = GVN_Deref(qualifier, dominator)) +} + +/** + * Holds if `access` is an access of a variable that does + * not have SSA information. (For example, because the variable + * is global.) + */ +private predicate analyzableOtherVariable(VariableAccess access) { + not (access instanceof FieldAccess) and + not exists (SsaDefinition def | access = def.getAUse(_)) and + strictcount (access.getTarget()) = 1 and + strictcount (mostRecentSideEffect(access)) = 1 and + not analyzableConst(access) +} + +private predicate mk_OtherVariable( + Variable x, ControlFlowNode dominator, VariableAccess access) { + analyzableOtherVariable(access) and + x = access.getTarget() and + dominator = mostRecentSideEffect(access) +} + +private predicate analyzableConversion(Conversion conv) { + strictcount (conv.getType().getUnspecifiedType()) = 1 and + strictcount (conv.getExpr()) = 1 and + not analyzableConst(conv) +} + +private predicate mk_Conversion(Type t, GVN child, Conversion conv) { + analyzableConversion(conv) and + t = conv.getType().getUnspecifiedType() and + child = globalValueNumber(conv.getExpr()) +} + +private predicate analyzableBinaryOp(BinaryOperation op) { + op.isPure() and + strictcount (op.getLeftOperand().getFullyConverted()) = 1 and + strictcount (op.getRightOperand().getFullyConverted()) = 1 and + strictcount (op.getOperator()) = 1 and + not analyzableConst(op) +} + +private predicate mk_BinaryOp( + GVN lhs, GVN rhs, string opname, BinaryOperation op) { + analyzableBinaryOp(op) and + lhs = globalValueNumber(op.getLeftOperand().getFullyConverted()) and + rhs = globalValueNumber(op.getRightOperand().getFullyConverted()) and + opname = op.getOperator() +} + +private predicate analyzableUnaryOp(UnaryOperation op) { + not (op instanceof PointerDereferenceExpr) and + op.isPure() and + strictcount (op.getOperand().getFullyConverted()) = 1 and + strictcount (op.getOperator()) = 1 and + not analyzableConst(op) +} + +private predicate mk_UnaryOp(GVN child, string opname, UnaryOperation op) { + analyzableUnaryOp(op) and + child = globalValueNumber(op.getOperand().getFullyConverted()) and + opname = op.getOperator() +} + +private predicate analyzableThisExpr(ThisExpr thisExpr) { + strictcount(thisExpr.getEnclosingFunction()) = 1 and + not analyzableConst(thisExpr) +} + +private predicate mk_ThisExpr(Function fcn, ThisExpr thisExpr) { + analyzableThisExpr(thisExpr) and + fcn = thisExpr.getEnclosingFunction() +} + +private predicate analyzableArrayAccess(ArrayExpr ae) { + strictcount (ae.getArrayBase().getFullyConverted()) = 1 and + strictcount (ae.getArrayOffset().getFullyConverted()) = 1 and + strictcount (mostRecentSideEffect(ae)) = 1 and + not analyzableConst(ae) +} + +private predicate mk_ArrayAccess( + GVN base, GVN offset, ControlFlowNode dominator, ArrayExpr ae) { + analyzableArrayAccess(ae) and + base = globalValueNumber(ae.getArrayBase().getFullyConverted()) and + offset = globalValueNumber(ae.getArrayOffset().getFullyConverted()) and + dominator = mostRecentSideEffect(ae) +} + +private predicate analyzablePointerDereferenceExpr( + PointerDereferenceExpr deref) { + strictcount (deref.getOperand().getFullyConverted()) = 1 and + strictcount (mostRecentSideEffect(deref)) = 1 and + not analyzableConst(deref) +} + +private predicate mk_Deref( + GVN p, ControlFlowNode dominator, PointerDereferenceExpr deref) { + analyzablePointerDereferenceExpr(deref) and + p = globalValueNumber(deref.getOperand().getFullyConverted()) and + dominator = mostRecentSideEffect(deref) +} + +/** Gets the global value number of expression `e`. */ +cached GVN globalValueNumber(Expr e) { + exists (int val, Type t + | mk_IntConst(val, t, e) and + result = GVN_IntConst(val, t)) + or + exists (float val, Type t + | mk_FloatConst(val, t, e) and + result = GVN_FloatConst(val, t)) + or + // Local variable with a defining value. + exists (LocalScopeVariable x, SsaDefinition def + | analyzableLocalScopeVariable(e) and + e = def.getAUse(x) and + result = globalValueNumber(def.getDefiningValue(x).getFullyConverted())) + or + // Local variable without a defining value. + exists (LocalScopeVariable x, SsaDefinition def + | mk_UndefinedLocalScopeVariable(x, def, e) and + result = GVN_UndefinedLocalScopeVariable(x, def)) + or + // Variable with no SSA information. + exists (Variable x, ControlFlowNode dominator + | mk_OtherVariable(x, dominator, e) and + result = GVN_OtherVariable(x, dominator)) + or + exists (GVN qualifier, Field target + | mk_DotFieldAccess(qualifier, target, e) and + result = GVN_FieldAccess(qualifier, target)) + or + exists (GVN qualifier, Field target + | mk_PointerFieldAccess_with_deref(qualifier, target, e) and + result = GVN_FieldAccess(qualifier, target)) + or + exists (GVN qualifier, Field target + | mk_ImplicitThisFieldAccess_with_deref(qualifier, target, e) and + result = GVN_FieldAccess(qualifier, target)) + or + exists (Function fcn + | mk_ThisExpr(fcn, e) and + result = GVN_ThisExpr(fcn)) + or + exists (Type t, GVN child + | mk_Conversion(t, child, e) and + result = GVN_Conversion(t, child)) + or + exists (GVN lhs, GVN rhs, string opname + | mk_BinaryOp(lhs, rhs, opname, e) and + result = GVN_BinaryOp(lhs, rhs, opname)) + or + exists (GVN child, string opname + | mk_UnaryOp(child, opname, e) and + result = GVN_UnaryOp(child, opname)) + or + exists (GVN x, GVN i, ControlFlowNode dominator + | mk_ArrayAccess(x, i, dominator, e) and + result = GVN_ArrayAccess(x, i, dominator)) + or + exists (GVN p, ControlFlowNode dominator + | mk_Deref(p, dominator, e) and + result = GVN_Deref(p, dominator)) + or + (not analyzableExpr(e) and result = GVN_Unanalyzable(e)) +} + +private predicate analyzableConst(Expr e) { + analyzableIntConst(e) or + analyzableFloatConst(e) +} + +/** + * Holds if the expression is explicitly handled by `globalValueNumber`. + * Unanalyzable expressions still need to be given a global value number, + * but it will be a unique number that is not shared with any other + * expression. + */ +private predicate analyzableExpr(Expr e) { + analyzableConst(e) or + analyzableLocalScopeVariable(e) or + analyzableDotFieldAccess(e) or + analyzablePointerFieldAccess(e) or + analyzableImplicitThisFieldAccess(e) or + analyzableOtherVariable(e) or + analyzableConversion(e) or + analyzableBinaryOp(e) or + analyzableUnaryOp(e) or + analyzableThisExpr(e) or + analyzableArrayAccess(e) or + analyzablePointerDereferenceExpr(e) +} diff --git a/cpp/ql/src/semmle/files/FileSystem.qll b/cpp/ql/src/semmle/files/FileSystem.qll new file mode 100644 index 000000000000..4dfb0ae670a6 --- /dev/null +++ b/cpp/ql/src/semmle/files/FileSystem.qll @@ -0,0 +1,3 @@ +/** Provides classes for working with files and folders. */ + +import semmle.code.cpp.File diff --git a/cpp/ql/src/semmle/uml/MagicDraw.qll b/cpp/ql/src/semmle/uml/MagicDraw.qll new file mode 100644 index 000000000000..9dcd497d9f38 --- /dev/null +++ b/cpp/ql/src/semmle/uml/MagicDraw.qll @@ -0,0 +1,344 @@ +/** + * Provides classes for working with UML diagrams generated by MagicDraw. + * + * Diagrams need to be saved in the `.mdxml` or `.xml` format. + * + * See [the MagicDraw website](http://www.nomagic.com/products/magicdraw.html) + * for more information. + */ + +import cpp + +/** + * An element of the UML diagram. + * + * Includes any XML element that is in a document whose root has namespace + * `http://schema.omg.org/spec/XMI/2.1`. + */ +class UMLElement extends XMLElement { + UMLElement() { + this.getFile().getARootElement().getNamespace().getURI() = "http://schema.omg.org/spec/XMI/2.1" + } + + /** + * Gets a UML element that refers to this element, that is, which has an + * `idref` attribute whose value is the same as the value of this + * element's `id` attribute. + */ + UMLElement getUMLReference() { + result.getAttributeValue("idref") = this.getAttributeValue("id") + } + + /** + * Gets the name of a stereotype that applies to this element. + */ + string getAStereotype() { + exists(UMLElement stereotype | + stereotype.getName() = "Stereotype" and + stereotype.getAttribute("name").getValue() = result and + stereotype.getAChild().getAChild() = this.getUMLReference() + ) + } + + /** + * Gets the name of a constraint that applies to this element. + */ + string getAConstraint() { + exists(UMLElement constraint, UMLElement constrained | + constraint.getName() = "Constraint" and + constrained.getName() = "Constraint.constrainedElement" and + constraint.getAChild() = constrained and + constrained.getAChild() = this.getUMLReference() and + constraint.getAttribute("name").getValue() = result) + } + + /** + * Gets the name of this element, that is, the value of its `name` attribute. + */ + string getUMLName() { + result = this.getAttribute("name").getValue() + } +} + +/** + * A UML element representing a type. + * + * In other words, a `packagedElement` whose `type` attribute has value + * `uml:Class`, `uml:Interface` or `uml:PrimitiveType`. + */ +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")) + } + + /** + * Gets the package that contains this type. + */ + UMLPackage getUMLPackage() { + result.getAClass() = this + } + + /** + * Gets a property directly contained in this type. + */ + UMLProperty getUMLProperty() { + this.getAChild() = result + } + + /** + * Gets an operation directly contained in this type. + */ + UMLOperation getUMLOperation() { + this.getAChild() = result + } + + /** + * Holds if this is an enum type, that is, if `enum` is one of its + * stereotypes. + */ + predicate isEnum() { + this.getAStereotype() = "enum" + } + + /** + * Gets the C class, struct or union type corresponding to this UML type. + */ + Class getCType() { + result.getQualifiedName() = this.getUMLQualifiedName() + } + + /** + * Gets the qualified name of this type. If this type is in a package + * then this is `package.name`; otherwise it is just `name`. + */ + string getUMLQualifiedName() { + if exists(this.getUMLPackage()) then + result = this.getUMLPackage().getUMLQualifiedName() + "." + this.getUMLName() + else + result = this.getUMLName() + } + + string toString() { + result = this.getUMLName() + } +} + +/** + * A UML type representing a class, that is, whose `type` attribute has + * value `uml:Class`. + */ +class UMLClass extends UMLType { + UMLClass() { this.getAttribute("type").getValue() = "uml:Class" } +} + +/** + * A UML type representing an interface, that is, whose `type` attribute + * has value `uml:Interface`. + */ +class UMLInterface extends UMLElement { + UMLInterface() { this.getAttribute("type").getValue() = "uml:Interface" } +} + +/** + * A UML element representing a property of a UML type. + * + * In other words, an `ownedAttribute` directly contained in a `UMLType` + * whose `type` attribute has value `uml:Property`. + */ +class UMLProperty extends UMLElement { + UMLProperty() { + this.getName() = "ownedAttribute" and + this.getAttribute("type").getValue() = "uml:Property" and + this.getParent() instanceof UMLType + } + + /** + * Gets the type that contains this property. + */ + UMLType getUMLType() { + result.getUMLProperty() = this + } + + /** + * Holds if this property represents an enum constant, that is, if it + * has a stereotype with name `enum+constant`. + */ + predicate isEnumConstant() { + this.getAStereotype() = "enum+constant" + } + + /** + * Gets the C field corresponding to this property, if any. + */ + Field getCField() { + result.hasName(this.getUMLName()) and + result.getDeclaringType() = this.getUMLType().getCType() + } + + string toString() { + if this.isEnumConstant() then + result = "- <> " + this.getUMLName() + else + result = "- " + this.getUMLName() + } +} + +/** + * A UML element representing an operation of a UML type. + * + * In other words, an `ownedAttribute` directly contained in a `UMLType` + * whose `type` attribute has value `uml:Operation`. + */ +class UMLOperation extends UMLElement { + UMLOperation() { + this.getName() = "ownedOperation" and + this.getAttribute("type").getValue() = "uml:Operation" and + this.getParent() instanceof UMLType + } + + /** + * Gets the type that contains this operation. + */ + UMLType getUMLType() { + result.getUMLOperation() = this + } + + /** + * Gets the C function corresponding to this operation, if any. + */ + Function getCFunction() { + result.hasName(this.getUMLName()) and + result.getDeclaringType() = this.getUMLType().getCType() + } + + string toString() { + result = "+ " + this.getUMLName() + } +} + +/** + * A UML property that has an association. + */ +class UMLAssociation extends UMLProperty { + UMLAssociation() { + this.hasAttribute("association") + } + + /** + * Gets the property that this property is associated with. + */ + UMLAssociation getConverse() { + this.getAttribute("association").getValue() = result.getAttribute("association").getValue() and + this != result + } + + /** + * Gets the name of this property. + */ + string getLabel() { + result = this.getAttribute("name").getValue() + } + + /** + * Gets the C field corresponding to this property, if any. + */ + Field getCField() { + result.hasName(this.getLabel()) and + result.getDeclaringType() = this.getSource().getCType() + } + + /** + * Gets the class that this association is contained in. + */ + UMLClass getSource() { + result = this.getParent() + } + + /** + * Gets the class that this association is associated with. + */ + UMLClass getDest() { + result = this.getConverse().getParent() + } +} + +/** + * A UML element representing inheritance. + * + * In other words, an `interfaceRealization` whose `type` attribute has + * value `uml:InterfaceRealization`. + */ +class UMLInheritance extends UMLElement { + UMLInheritance() { + this.getName() = "interfaceRealization" and + this.getAttributeValue("type") = "uml:InterfaceRealization" + } + + /** + * Gets the type that is being inherited from. + */ + UMLType getUMLSupplier() { + exists(UMLElement e | + e.getName() = "supplier" and + result.getUMLReference() = e and + e = this.getAChild()) + } + + /** + * Gets the type that is inheriting. + */ + UMLType getUMLClient() { + exists(UMLElement e | + e.getName() = "client" and + result.getUMLReference() = e and + e = this.getAChild()) + } + + string toString() { + result = this.getUMLClient().getUMLName() + " implements " + this.getUMLSupplier().getUMLName() + } +} + +/** + * A UML element representing a package. + */ +class UMLPackage extends UMLElement { + UMLPackage() { + this.getName() = "Package" + } + + /** + * Gets a class in this package. + */ + UMLClass getAClass() { + result.getAChild().getAChild() = this.getUMLReference() + } + + /** + * Gets the parent package of this package, if any. + */ + UMLPackage parentPackage() { + result.getAChild().getAChild() = this + } + + /** + * Gets the qualified name of this package. If this package is in a + * parent package then this is `parent.package`; otherwise it is just + * `package`. + */ + string getUMLQualifiedName() { + if exists(this.parentPackage()) then + result = this.parentPackage().getUMLQualifiedName() + "." + this.getUMLName() + else + result = this.getUMLName() + } + + string toString() { + result = this.getUMLQualifiedName() + } +} diff --git a/cpp/ql/src/semmlecode.cpp.dbscheme b/cpp/ql/src/semmlecode.cpp.dbscheme new file mode 100644 index 000000000000..afff19e455d6 --- /dev/null +++ b/cpp/ql/src/semmlecode.cpp.dbscheme @@ -0,0 +1,1755 @@ + +/** + * 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 + */ +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. + */ +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 + */ +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 diagnosstic 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. + */ +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 extractino 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. + */ +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 +); + +/* + * Mapping of header files to packages + */ + +external_packages( + unique int id: @external_package, + string namespace : string ref, // "dpkg", "yum", ... + string package_name : string ref, + string version : string ref +); + +header_to_external_package( + int fileid : @file ref, + int package : @external_package ref +); + +/* + * Version history + */ + +svnentries( + 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( + 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 + * [LGTM locations](https://lgtm.com/docs/ql/locations). + */ +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 + * [LGTM locations](https://lgtm.com/docs/ql/locations). + */ +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 + * [LGTM locations](https://lgtm.com/docs/ql/locations). + */ +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, 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); + + + +fun_decls( + unique 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 +) +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 +); + +var_decls( + unique 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 +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); + +membervariables( + unique int id: @membervariable, + int type_id: @type ref, + string name: string ref +); + +globalvariables( + unique int id: @globalvariable, + int type_id: @type ref, + string name: string ref +); + +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, i.e., integral, floating, and void. + kind(1) = error, kind(2) = unknown, kind(3) = void, kind(4) = boolean, + kind(5) = char, kind(6) = unsigned char, kind(7) = signed char + kind(8) = short, kind(9) = unsigned short, kind(10) = signed short + kind(11) = int, kind(12) = unsigned int, kind(13) = signed int, + kind(14) = long, kind(15) = unsigned long, kind(16) = signed long, + kind(17) = long long, kind(18) = unsigned long long, kind(19) = signed long long, + kind(20) = __int8, kind(21) = __int16, kind(22) = __int32, kind(23) = __int64, // Microsoft specific + kind(24) = float, kind(25) = double, kind(26) = long double, + kind(27) = _Complex float, kind(28) = _Complex double, kind(29) = _Complex long double, //C99 specific + kind(30) = _Imaginary float, kind(31) = _Imaginary double, kind(32) = _Imaginary long double, //C99 specific + kind(33) = wchar_t, // MS specific + kind(34) = decltype(nullptr), // C++11 +*/ +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 + | 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 + ; +*/ +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 +); + +is_pod_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 +); + +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 +); + +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 +); + +/* + 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 +); + +@specifiable = @type + | @function + | @variable + | @enumconstant + | @frienddecl; + +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; + +@scope = @stmt | @function | @namedscope; +@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; + +@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); + + +namespaces( + unique int id: @namespace, + string name: string 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 +); + +/* +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; + +/* + 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 +); + +// the second field is a string representation of the value +// the third field is the actual text in the source or the same as the second field +values( + unique int id: @value, + string str: string 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 +// ... +| 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 +; + +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 +); + +@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, + 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 ; +@callable = @function | @variable; // only for variables with routine types + +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 +; + +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 +); + +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 +); + +switch_case( + int switch_stmt: @stmt_switch ref, + int index: int ref, + int case_id: @stmt_switch_case ref +); + +switch_body( + int switch_stmt: @stmt_switch ref, + int body_id: @stmt ref +); + +for_initialization( + int for_stmt: @stmt_for ref, + int init_id: @stmt ref +); + +for_condition( + int for_stmt: @stmt_for ref, + int condition_id: @expr ref +); + +for_update( + int for_stmt: @stmt_for ref, + int update_id: @expr ref +); + +for_body( + 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( + 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/ql/src/semmlecode.cpp.dbscheme.stats b/cpp/ql/src/semmlecode.cpp.dbscheme.stats new file mode 100644 index 000000000000..4b25ede6c982 --- /dev/null +++ b/cpp/ql/src/semmlecode.cpp.dbscheme.stats @@ -0,0 +1,39867 @@ + + +@compilation +9298 + + +@externalDataElement +66 + + +@duplication +202049 + + +@similarity +238457 + + +@external_package +4 + + +@svnentry +575525 + + +@location_default +8232279 + + +@location_stmt +5631060 + + +@location_expr +22023911 + + +@diagnostic +66951 + + +@file +57465 + + +@folder +7966 + + +@macroinvocation +37650204 + + +@function +3698710 + + +@fun_decl +3050240 + + +@var_decl +4647089 + + +@type_decl +1388703 + + +@namespace_decl +130885 + + +@using +201199 + + +@static_assert +18201 + + +@parameter +4568553 + + +@membervariable +352723 + + +@globalvariable +336079 + + +@localvariable +664877 + + +@enumconstant +97072 + + +@builtintype +510 + + +@derivedtype +3614061 + + +@decltype +79334 + + +@usertype +3394431 + + +@type_mention +7771692 + + +@routinetype +532763 + + +@ptrtomember +13636 + + +@specifier +466 + + +@gnuattribute +425139 + + +@stdattribute +44 + + +@alignas +288 + + +@declspec +39467 + + +@msattribute +9 + + +@attribute_arg_token +1139 + + +@attribute_arg_constant +135091 + + +@attribute_arg_type +99 + + +@attribute_arg_empty +1 + + +@derivation +220295 + + +@frienddecl +88113 + + +@comment +1490251 + + +@namespace +7878 + + +@specialnamequalifyingelement +11 + + +@namequalifier +942519 + + +@value +10189744 + + +@initialiser +1576583 + + +@delete_array_expr +1342 + + +@new_array_expr +915 + + +@ctordirectinit +122231 + + +@ctorvirtualinit +4848 + + +@ctorfieldinit +270204 + + +@ctordelegatinginit +909 + + +@dtordirectdestruct +30546 + + +@dtorvirtualdestruct +1542 + + +@dtorfielddestruct +33231 + + +@static_cast +248467 + + +@reinterpret_cast +30257 + + +@const_cast +10086 + + +@dynamic_cast +1098 + + +@c_style_cast +5246346 + + +@lambdaexpr +2392 + + +@param_ref +64610 + + +@errorexpr +53204 + + +@address_of +513989 + + +@reference_to +1373246 + + +@indirect +340254 + + +@ref_indirect +1545996 + + +@array_to_pointer +1277596 + + +@vacuous_destructor_call +5048 + + +@parexpr +3645230 + + +@arithnegexpr +735809 + + +@unaryplusexpr +984 + + +@complementexpr +34398 + + +@notexpr +327608 + + +@realpartexpr +66 + + +@imagpartexpr +66 + + +@postincrexpr +63274 + + +@postdecrexpr +7969 + + +@preincrexpr +76272 + + +@predecrexpr +31068 + + +@conditionalexpr +205957 + + +@addexpr +234262 + + +@subexpr +175561 + + +@mulexpr +97668 + + +@divexpr +45104 + + +@remexpr +5192 + + +@paddexpr +120798 + + +@psubexpr +31652 + + +@pdiffexpr +29647 + + +@lshiftexpr +308891 + + +@rshiftexpr +71546 + + +@andexpr +247150 + + +@orexpr +137662 + + +@xorexpr +16063 + + +@eqexpr +292382 + + +@neexpr +115492 + + +@gtexpr +68024 + + +@ltexpr +76105 + + +@geexpr +30625 + + +@leexpr +271958 + + +@assignexpr +709791 + + +@assignaddexpr +77403 + + +@assignsubexpr +9543 + + +@assignmulexpr +6457 + + +@assigndivexpr +1786 + + +@assignremexpr +266 + + +@assignlshiftexpr +767 + + +@assignrshiftexpr +4528 + + +@assignandexpr +10782 + + +@assignorexpr +26430 + + +@assignxorexpr +4415 + + +@assignpaddexpr +12023 + + +@assignpsubexpr +865 + + +@andlogicalexpr +131744 + + +@orlogicalexpr +72311 + + +@commaexpr +21573 + + +@subscriptexpr +253161 + + +@callexpr +208589 + + +@vastartexpr +681 + + +@vaargexpr +1891 + + +@vaendexpr +744 + + +@varaccess +6694687 + + +@thisaccess +1393374 + + +@new_expr +36283 + + +@delete_expr +8320 + + +@throw_expr +23833 + + +@condition_decl +12582 + + +@braced_init_list +33 + + +@type_id +4405 + + +@runtime_sizeof +272313 + + +@runtime_alignof +1126 + + +@sizeof_pack +155 + + +@expr_stmt +199692 + + +@routineexpr +2804180 + + +@type_operand +50536 + + +@offsetofexpr +43233 + + +@hastrivialdestructor +99 + + +@literal +5209090 + + +@aggregateliteral +1094155 + + +@istrivialexpr +2596 + + +@istriviallycopyableexpr +55 + + +@noexceptexpr +76 + + +@assume +39 + + +@conjugation +1 + + +@jmulexpr +1 + + +@jdivexpr +1 + + +@fjaddexpr +1 + + +@jfaddexpr +1 + + +@fjsubexpr +1 + + +@jfsubexpr +1 + + +@minexpr +1 + + +@maxexpr +1 + + +@virtfunptrexpr +1 + + +@vacopyexpr +34 + + +@hasassignexpr +2 + + +@hascopyexpr +2 + + +@hasnothrowassign +3 + + +@hasnothrowconstr +3 + + +@hasnothrowcopy +3 + + +@hastrivialassign +2 + + +@hastrivialconstr +3 + + +@hastrivialcopy +2 + + +@hasuserdestr +3 + + +@hasvirtualdestr +3 + + +@isabstractexpr +3 + + +@isbaseofexpr +12 + + +@isclassexpr +5 + + +@isconvtoexpr +12 + + +@isemptyexpr +5 + + +@isenumexpr +642 + + +@ispodexpr +3 + + +@ispolyexpr +3 + + +@isunionexpr +5 + + +@typescompexpr +3651 + + +@intaddrexpr +1 + + +@uuidof +121 + + +@noopexpr +41 + + +@istriviallyconstructibleexpr +3 + + +@isdestructibleexpr +4 + + +@isnothrowdestructibleexpr +5 + + +@istriviallydestructibleexpr +5 + + +@istriviallyassignableexpr +3 + + +@isnothrowassignableexpr +3 + + +@isstandardlayoutexpr +2 + + +@isliteraltypeexpr +2 + + +@hastrivialmoveconstructorexpr +3 + + +@hastrivialmoveassignexpr +3 + + +@hasnothrowmoveassignexpr +4 + + +@isconstructibleexpr +3 + + +@isnothrowconstructibleexpr +3 + + +@hasfinalizerexpr +1 + + +@isdelegateexpr +1 + + +@isinterfaceclassexpr +1 + + +@isrefarrayexpr +1 + + +@isrefclassexpr +1 + + +@issealedexpr +1 + + +@issimplevalueclassexpr +1 + + +@isvalueclassexpr +1 + + +@isfinalexpr +2 + + +@builtinshufflevector +1 + + +@builtinchooseexpr +3070 + + +@lambdacapture +266 + + +@stmt_expr +1572664 + + +@stmt_if +658624 + + +@stmt_while +41808 + + +@stmt_goto +126716 + + +@stmt_label +118849 + + +@stmt_return +1367598 + + +@stmt_block +1616111 + + +@stmt_end_test_while +220170 + + +@stmt_for +42104 + + +@stmt_switch_case +388126 + + +@stmt_switch +76075 + + +@stmt_asm +297358 + + +@stmt_try_block +19473 + + +@stmt_decl +694074 + + +@stmt_empty +88844 + + +@stmt_continue +12084 + + +@stmt_break +321362 + + +@stmt_range_based_for +23 + + +@stmt_handler +21514 + + +@stmt_microsoft_try +268 + + +@stmt_set_vla_size +104 + + +@stmt_vla_decl +104 + + +@stmt_assigned_goto +2 + + +@ppd_if +133815 + + +@ppd_ifdef +49931 + + +@ppd_ifndef +76782 + + +@ppd_elif +16754 + + +@ppd_else +51062 + + +@ppd_endif +260551 + + +@ppd_plain_include +281755 + + +@ppd_define +313511 + + +@ppd_undef +17098 + + +@ppd_line +13915 + + +@ppd_error +44 + + +@ppd_pragma +7101 + + +@ppd_include_next +77 + + +@ppd_warning +22 + + +@ppd_objc_import +2 + + +@link_target +1295 + + +@xmldtd +1 + + +@xmlelement +1270313 + + +@xmlattribute +1202020 + + +@xmlnamespace +4185 + + +@xmlcomment +26812 + + +@xmlcharacters +439958 + + + +compilations +9298 + + +id +9298 + + +cwd +22 + + + + +id +cwd + + +12 + + +1 +2 +9298 + + + + + + +cwd +id + + +12 + + +2 +3 +11 + + +836 +837 +11 + + + + + + + + +compilation_args +535255 + + +id +6859 + + +num +255 + + +arg +28821 + + + + +id +num + + +12 + + +10 +43 +514 + + +49 +72 +170 + + +83 +84 +5040 + + +84 +85 +624 + + +85 +94 +509 + + + + + + +id +arg + + +12 + + +10 +42 +514 + + +49 +72 +170 + + +83 +84 +5051 + + +84 +85 +613 + + +85 +90 +509 + + + + + + +num +id + + +12 + + +2 +4 +22 + + +185 +413 +5 + + +2244 +2245 +33 + + +2269 +2281 +19 + + +2282 +2293 +19 + + +2293 +2305 +19 + + +2305 +2307 +22 + + +2312 +2334 +19 + + +2336 +2356 +16 + + +2359 +2364 +22 + + +2384 +2397 +19 + + +2404 +2412 +11 + + +2493 +2494 +27 + + + + + + +num +arg + + +12 + + +1 +2 +13 + + +2 +3 +27 + + +3 +6 +22 + + +6 +7 +16 + + +7 +9 +22 + + +9 +10 +11 + + +10 +11 +44 + + +11 +12 +13 + + +12 +14 +16 + + +14 +18 +16 + + +18 +27 +22 + + +27 +1403 +19 + + +1653 +2303 +11 + + + + + + +arg +id + + +12 + + +1 +2 +27511 + + +2 +2494 +1309 + + + + + + +arg +num + + +12 + + +1 +2 +28329 + + +2 +49 +492 + + + + + + + + +compilation_compiling_files +10341 + + +id +9298 + + +num +543 + + +file +5226 + + + + +id +num + + +12 + + +1 +2 +9276 + + +47 +50 +22 + + + + + + +id +file + + +12 + + +1 +2 +9276 + + +47 +50 +22 + + + + + + +num +id + + +12 + + +1 +2 +22 + + +2 +3 +510 + + +838 +839 +11 + + + + + + +num +file + + +12 + + +1 +2 +122 + + +2 +3 +410 + + +423 +424 +11 + + + + + + +file +id + + +12 + + +1 +2 +255 + + +2 +3 +4937 + + +3 +14 +33 + + + + + + +file +num + + +12 + + +1 +2 +4815 + + +2 +3 +410 + + + + + + + + +compilation_time +41187 + + +id +9253 + + +num +543 + + +kind +44 + + +seconds +12982 + + + + +id +num + + +12 + + +1 +2 +9231 + + +47 +50 +22 + + + + + + +id +kind + + +12 + + +4 +5 +9253 + + + + + + +id +seconds + + +12 + + +2 +3 +11 + + +3 +4 +1797 + + +4 +5 +7423 + + +52 +59 +22 + + + + + + +num +id + + +12 + + +1 +2 +22 + + +2 +3 +510 + + +834 +835 +11 + + + + + + +num +kind + + +12 + + +4 +5 +543 + + + + + + +num +seconds + + +12 + + +3 +4 +22 + + +4 +5 +33 + + +5 +6 +388 + + +6 +7 +88 + + +1158 +1159 +11 + + + + + + +kind +id + + +12 + + +834 +835 +44 + + + + + + +kind +num + + +12 + + +49 +50 +44 + + + + + + +kind +seconds + + +12 + + +17 +18 +11 + + +18 +19 +11 + + +502 +503 +11 + + +781 +782 +11 + + + + + + +seconds +id + + +12 + + +1 +2 +9364 + + +2 +3 +1830 + + +3 +5 +1165 + + +5 +598 +621 + + + + + + +seconds +num + + +12 + + +1 +2 +12116 + + +2 +50 +865 + + + + + + +seconds +kind + + +12 + + +1 +2 +11350 + + +2 +3 +1619 + + +3 +4 +11 + + + + + + + + +diagnostic_for +767073 + + +diagnostic +66951 + + +compilation +3650 + + +file_number +11 + + +file_number_diagnostic_number +6391 + + + + +diagnostic +compilation + + +12 + + +1 +2 +4327 + + +2 +3 +59750 + + +4 +307 +2873 + + + + + + +diagnostic +file_number + + +12 + + +1 +2 +66951 + + + + + + +diagnostic +file_number_diagnostic_number + + +12 + + +1 +2 +66951 + + + + + + +compilation +diagnostic + + +12 + + +2 +3 +499 + + +13 +18 +332 + + +93 +94 +22 + + +230 +231 +1864 + + +246 +279 +310 + + +294 +375 +288 + + +386 +561 +288 + + +576 +577 +44 + + + + + + +compilation +file_number + + +12 + + +1 +2 +3650 + + + + + + +compilation +file_number_diagnostic_number + + +12 + + +2 +3 +499 + + +13 +18 +332 + + +93 +94 +22 + + +230 +231 +1864 + + +246 +279 +310 + + +294 +375 +288 + + +386 +561 +288 + + +576 +577 +44 + + + + + + +file_number +diagnostic + + +12 + + +6034 +6035 +11 + + + + + + +file_number +compilation + + +12 + + +329 +330 +11 + + + + + + +file_number +file_number_diagnostic_number + + +12 + + +576 +577 +11 + + + + + + +file_number_diagnostic_number +diagnostic + + +12 + + +1 +2 +1520 + + +2 +3 +1020 + + +3 +5 +399 + + +5 +6 +1065 + + +7 +12 +488 + + +12 +19 +466 + + +19 +21 +488 + + +22 +29 +399 + + +39 +43 +532 + + +329 +330 +11 + + + + + + +file_number_diagnostic_number +compilation + + +12 + + +4 +9 +565 + + +10 +11 +1065 + + +14 +23 +477 + + +30 +39 +576 + + +40 +49 +443 + + +56 +79 +532 + + +84 +85 +177 + + +252 +253 +1520 + + +254 +255 +843 + + +268 +330 +188 + + + + + + +file_number_diagnostic_number +file_number + + +12 + + +1 +2 +6391 + + + + + + + + +compilation_finished +9298 + + +id +9298 + + +cpu_seconds +8676 + + +elapsed_seconds +299 + + + + +id +cpu_seconds + + +12 + + +1 +2 +9298 + + + + + + +id +elapsed_seconds + + +12 + + +1 +2 +9298 + + + + + + +cpu_seconds +id + + +12 + + +1 +2 +8155 + + +2 +5 +521 + + + + + + +cpu_seconds +elapsed_seconds + + +12 + + +1 +2 +8488 + + +2 +3 +188 + + + + + + +elapsed_seconds +id + + +12 + + +1 +2 +110 + + +2 +3 +22 + + +3 +6 +22 + + +7 +8 +22 + + +8 +9 +22 + + +15 +24 +22 + + +27 +44 +22 + + +105 +115 +22 + + +115 +157 +22 + + +188 +189 +11 + + + + + + +elapsed_seconds +cpu_seconds + + +12 + + +1 +2 +110 + + +2 +3 +22 + + +3 +6 +22 + + +7 +8 +22 + + +8 +9 +22 + + +15 +24 +22 + + +26 +44 +22 + + +100 +105 +22 + + +110 +151 +22 + + +176 +177 +11 + + + + + + + + +externalData +133 + + +id +66 + + +path +11 + + +column +22 + + +value +133 + + + + +id +path + + +12 + + +1 +2 +66 + + + + + + +id +column + + +12 + + +2 +3 +66 + + + + + + +id +value + + +12 + + +2 +3 +66 + + + + + + +path +id + + +12 + + +6 +7 +11 + + + + + + +path +column + + +12 + + +2 +3 +11 + + + + + + +path +value + + +12 + + +12 +13 +11 + + + + + + +column +id + + +12 + + +6 +7 +22 + + + + + + +column +path + + +12 + + +1 +2 +22 + + + + + + +column +value + + +12 + + +6 +7 +22 + + + + + + +value +id + + +12 + + +1 +2 +133 + + + + + + +value +path + + +12 + + +1 +2 +133 + + + + + + +value +column + + +12 + + +1 +2 +133 + + + + + + + + +snapshotDate +11 + + +snapshotDate +11 + + + + + +sourceLocationPrefix +11 + + +prefix +11 + + + + + +duplicateCode +202049 + + +id +202049 + + +relativePath +785 + + +equivClass +81466 + + + + +id +relativePath + + +12 + + +1 +2 +202049 + + + + + + +id +equivClass + + +12 + + +1 +2 +202049 + + + + + + +relativePath +id + + +12 + + +1 +2 +104 + + +2 +3 +215 + + +3 +4 +69 + + +4 +5 +83 + + +5 +8 +55 + + +8 +15 +62 + + +16 +22 +62 + + +23 +32 +62 + + +38 +6340 +62 + + +18087 +18088 +6 + + + + + + +relativePath +equivClass + + +12 + + +1 +2 +292 + + +2 +3 +146 + + +3 +4 +62 + + +4 +7 +62 + + +7 +12 +69 + + +13 +18 +62 + + +18 +109 +62 + + +150 +8251 +27 + + + + + + +equivClass +id + + +12 + + +1 +2 +20493 + + +2 +3 +34763 + + +3 +4 +11967 + + +4 +5 +6529 + + +5 +9 +6717 + + +9 +11 +994 + + + + + + +equivClass +relativePath + + +12 + + +1 +2 +80465 + + +2 +6 +1001 + + + + + + + + +similarCode +238457 + + +id +238457 + + +relativePath +2646 + + +equivClass +63734 + + + + +id +relativePath + + +12 + + +1 +2 +238457 + + + + + + +id +equivClass + + +12 + + +1 +2 +238457 + + + + + + +relativePath +id + + +12 + + +1 +2 +114 + + +2 +4 +205 + + +4 +7 +190 + + +7 +11 +221 + + +11 +16 +231 + + +16 +21 +238 + + +21 +28 +218 + + +28 +38 +201 + + +38 +52 +201 + + +52 +75 +199 + + +75 +125 +199 + + +125 +231 +199 + + +236 +1038 +199 + + +1055 +5166 +26 + + + + + + +relativePath +equivClass + + +12 + + +1 +2 +153 + + +2 +3 +154 + + +3 +6 +228 + + +6 +9 +192 + + +9 +13 +207 + + +13 +17 +201 + + +17 +22 +207 + + +22 +30 +210 + + +30 +38 +201 + + +38 +54 +205 + + +54 +79 +203 + + +79 +128 +203 + + +128 +330 +199 + + +342 +1467 +78 + + + + + + +equivClass +id + + +12 + + +1 +2 +22 + + +2 +3 +26416 + + +3 +4 +12363 + + +4 +5 +7511 + + +5 +6 +5122 + + +6 +7 +3776 + + +7 +9 +5168 + + +9 +11 +3354 + + + + + + +equivClass +relativePath + + +12 + + +1 +2 +27115 + + +2 +3 +17565 + + +3 +4 +7524 + + +4 +5 +4245 + + +5 +8 +5730 + + +8 +11 +1554 + + + + + + + + +tokens +44513159 + + +id +313175 + + +offset +23149 + + +beginLine +843295 + + +beginColumn +1599 + + +endLine +843295 + + +endColumn +1592 + + + + +id +offset + + +12 + + +100 +101 +8518 + + +101 +102 +30750 + + +102 +105 +25173 + + +105 +108 +28907 + + +108 +112 +28907 + + +112 +115 +14867 + + +115 +117 +24234 + + +117 +124 +24624 + + +124 +132 +25709 + + +132 +150 +24060 + + +150 +184 +24248 + + +184 +200 +23907 + + +200 +338 +23504 + + +339 +3330 +5757 + + + + + + +id +beginLine + + +12 + + +4 +5 +1460 + + +5 +6 +115916 + + +6 +7 +18963 + + +7 +8 +30980 + + +8 +12 +27531 + + +12 +17 +28727 + + +17 +19 +20882 + + +19 +22 +28295 + + +22 +27 +23581 + + +27 +525 +16835 + + + + + + +id +beginColumn + + +12 + + +3 +25 +23574 + + +25 +30 +24575 + + +30 +32 +7301 + + +32 +33 +172446 + + +33 +50 +23595 + + +50 +60 +25396 + + +60 +72 +23664 + + +72 +120 +12621 + + + + + + +id +endLine + + +12 + + +4 +5 +1460 + + +5 +6 +115916 + + +6 +7 +18963 + + +7 +8 +30980 + + +8 +12 +27531 + + +12 +17 +28727 + + +17 +19 +20882 + + +19 +22 +28295 + + +22 +27 +23581 + + +27 +525 +16835 + + + + + + +id +endColumn + + +12 + + +3 +25 +26807 + + +25 +31 +28365 + + +31 +32 +1801 + + +32 +33 +173350 + + +33 +54 +24888 + + +54 +64 +25375 + + +64 +78 +23782 + + +78 +126 +8803 + + + + + + +offset +id + + +12 + + +4 +5 +660 + + +8 +9 +3525 + + +10 +11 +1613 + + +12 +13 +2823 + + +14 +18 +2030 + + +19 +22 +1821 + + +24 +48 +1641 + + +55 +88 +1974 + + +89 +186 +1738 + + +189 +332 +1780 + + +333 +1477 +1738 + + +1489 +45036 +1801 + + + + + + +offset +beginLine + + +12 + + +2 +3 +660 + + +4 +5 +2913 + + +6 +7 +1835 + + +8 +9 +2572 + + +10 +13 +1752 + + +13 +16 +1689 + + +17 +27 +1780 + + +28 +58 +1870 + + +59 +116 +1828 + + +117 +226 +1773 + + +227 +567 +1738 + + +569 +10989 +1738 + + +11187 +32851 +994 + + + + + + +offset +beginColumn + + +12 + + +1 +3 +744 + + +3 +4 +3588 + + +4 +5 +1933 + + +5 +6 +3546 + + +6 +8 +1870 + + +8 +12 +2002 + + +12 +15 +1279 + + +15 +17 +1912 + + +17 +23 +1947 + + +23 +47 +1738 + + +47 +143 +1738 + + +143 +178 +848 + + + + + + +offset +endLine + + +12 + + +2 +3 +660 + + +4 +5 +2913 + + +6 +7 +1835 + + +8 +9 +2572 + + +10 +13 +1752 + + +13 +16 +1689 + + +17 +27 +1780 + + +28 +58 +1870 + + +59 +116 +1828 + + +117 +226 +1773 + + +227 +567 +1738 + + +569 +10989 +1738 + + +11187 +32851 +994 + + + + + + +offset +endColumn + + +12 + + +1 +3 +737 + + +3 +4 +3546 + + +4 +5 +1884 + + +5 +6 +3664 + + +6 +8 +1898 + + +8 +12 +1961 + + +12 +15 +1446 + + +15 +17 +1884 + + +17 +24 +1870 + + +24 +50 +1752 + + +50 +152 +1745 + + +152 +181 +757 + + + + + + +beginLine +id + + +12 + + +1 +2 +413751 + + +2 +3 +110270 + + +3 +4 +48991 + + +4 +5 +43080 + + +5 +7 +71988 + + +7 +10 +70632 + + +10 +23 +63907 + + +23 +136 +20674 + + + + + + +beginLine +offset + + +12 + + +1 +7 +72189 + + +7 +12 +69985 + + +12 +23 +63358 + + +23 +32 +39950 + + +32 +33 +268864 + + +33 +41 +67377 + + +41 +54 +64060 + + +54 +67 +63796 + + +67 +88 +63393 + + +88 +153 +63351 + + +153 +253 +6967 + + + + + + +beginLine +beginColumn + + +12 + + +1 +5 +72057 + + +5 +9 +63935 + + +9 +14 +64651 + + +14 +26 +66466 + + +26 +32 +18588 + + +32 +33 +351957 + + +33 +37 +71619 + + +37 +42 +69164 + + +42 +85 +63295 + + +85 +126 +1557 + + + + + + +beginLine +endLine + + +12 + + +1 +2 +843267 + + +2 +3 +27 + + + + + + +beginLine +endColumn + + +12 + + +1 +5 +71988 + + +5 +9 +63879 + + +9 +14 +64623 + + +14 +26 +66411 + + +26 +32 +18602 + + +32 +33 +352298 + + +33 +37 +74067 + + +37 +42 +65367 + + +42 +82 +63393 + + +82 +131 +2663 + + + + + + +beginColumn +id + + +12 + + +2 +22 +125 + + +22 +48 +125 + + +49 +143 +125 + + +146 +246 +125 + + +250 +353 +125 + + +355 +643 +125 + + +654 +1045 +125 + + +1083 +2655 +125 + + +2775 +8521 +125 + + +8632 +13241 +125 + + +13451 +16577 +125 + + +26032 +34843 +125 + + +35215 +40498 +97 + + + + + + +beginColumn +offset + + +12 + + +2 +8 +146 + + +8 +27 +125 + + +27 +53 +125 + + +53 +70 +125 + + +70 +96 +125 + + +97 +150 +125 + + +150 +181 +125 + + +182 +273 +125 + + +277 +447 +125 + + +451 +557 +125 + + +563 +816 +125 + + +830 +962 +125 + + +964 +1333 +76 + + + + + + +beginColumn +beginLine + + +12 + + +2 +8 +125 + + +8 +13 +125 + + +13 +34 +125 + + +35 +63 +125 + + +63 +108 +132 + + +108 +267 +125 + + +270 +396 +125 + + +502 +1252 +125 + + +1433 +4481 +125 + + +4551 +9574 +125 + + +9924 +85543 +125 + + +85608 +89662 +125 + + +89973 +98415 +90 + + + + + + +beginColumn +endLine + + +12 + + +2 +8 +125 + + +8 +13 +125 + + +13 +34 +125 + + +35 +63 +125 + + +63 +108 +132 + + +108 +267 +125 + + +270 +396 +125 + + +502 +1252 +125 + + +1433 +4481 +125 + + +4551 +9574 +125 + + +9924 +85543 +125 + + +85608 +89662 +125 + + +89973 +98415 +90 + + + + + + +beginColumn +endColumn + + +12 + + +1 +2 +146 + + +2 +3 +69 + + +3 +4 +125 + + +4 +7 +125 + + +7 +9 +111 + + +9 +14 +139 + + +14 +19 +125 + + +19 +24 +146 + + +24 +27 +132 + + +27 +33 +125 + + +33 +37 +125 + + +37 +42 +132 + + +44 +69 +97 + + + + + + +endLine +id + + +12 + + +1 +2 +413737 + + +2 +3 +110270 + + +3 +4 +48998 + + +4 +5 +43080 + + +5 +7 +71988 + + +7 +10 +70632 + + +10 +23 +63914 + + +23 +136 +20674 + + + + + + +endLine +offset + + +12 + + +1 +7 +72189 + + +7 +12 +69985 + + +12 +23 +63351 + + +23 +32 +39957 + + +32 +33 +268864 + + +33 +41 +67377 + + +41 +54 +64060 + + +54 +67 +63796 + + +67 +88 +63393 + + +88 +153 +63351 + + +153 +253 +6967 + + + + + + +endLine +beginLine + + +12 + + +1 +2 +843267 + + +2 +3 +27 + + + + + + +endLine +beginColumn + + +12 + + +1 +5 +72057 + + +5 +9 +63935 + + +9 +14 +64658 + + +14 +26 +66459 + + +26 +32 +18588 + + +32 +33 +351957 + + +33 +37 +71619 + + +37 +42 +69164 + + +42 +85 +63295 + + +85 +126 +1557 + + + + + + +endLine +endColumn + + +12 + + +1 +5 +71995 + + +5 +9 +63872 + + +9 +14 +64623 + + +14 +26 +66411 + + +26 +32 +18602 + + +32 +33 +352298 + + +33 +37 +74067 + + +37 +42 +65367 + + +42 +82 +63393 + + +82 +131 +2663 + + + + + + +endColumn +id + + +12 + + +2 +28 +132 + + +30 +58 +132 + + +62 +176 +125 + + +180 +270 +125 + + +278 +431 +125 + + +432 +846 +125 + + +857 +1304 +125 + + +1365 +3518 +125 + + +3737 +9376 +125 + + +9525 +13591 +125 + + +14018 +26850 +125 + + +26982 +35719 +125 + + +35858 +41835 +76 + + + + + + +endColumn +offset + + +12 + + +2 +9 +125 + + +9 +25 +132 + + +27 +52 +125 + + +53 +75 +125 + + +75 +100 +125 + + +112 +159 +125 + + +159 +191 +132 + + +191 +274 +125 + + +294 +455 +125 + + +461 +580 +125 + + +584 +789 +125 + + +791 +956 +125 + + +978 +1265 +76 + + + + + + +endColumn +beginLine + + +12 + + +2 +8 +104 + + +9 +13 +125 + + +13 +35 +125 + + +37 +61 +125 + + +65 +114 +125 + + +118 +269 +125 + + +279 +454 +125 + + +455 +1242 +125 + + +1257 +3972 +125 + + +4012 +9064 +125 + + +9096 +14469 +125 + + +14530 +88904 +125 + + +89377 +97930 +111 + + + + + + +endColumn +beginColumn + + +12 + + +1 +2 +104 + + +2 +3 +90 + + +3 +5 +146 + + +5 +7 +118 + + +7 +10 +125 + + +10 +15 +111 + + +15 +19 +111 + + +19 +24 +146 + + +24 +27 +125 + + +27 +33 +139 + + +33 +35 +48 + + +35 +37 +132 + + +37 +41 +146 + + +41 +43 +48 + + + + + + +endColumn +endLine + + +12 + + +2 +8 +104 + + +9 +13 +125 + + +13 +35 +125 + + +37 +61 +125 + + +65 +114 +125 + + +118 +269 +125 + + +279 +454 +125 + + +455 +1242 +125 + + +1257 +3972 +125 + + +4012 +9064 +125 + + +9096 +14469 +125 + + +14530 +88904 +125 + + +89377 +97930 +111 + + + + + + + + +external_packages +4 + + +id +4 + + +namespace +1 + + +package_name +4 + + +version +4 + + + + +id +namespace + + +12 + + +1 +2 +4 + + + + + + +id +package_name + + +12 + + +1 +2 +4 + + + + + + +id +version + + +12 + + +1 +2 +4 + + + + + + +namespace +id + + +12 + + +4 +5 +1 + + + + + + +namespace +package_name + + +12 + + +4 +5 +1 + + + + + + +namespace +version + + +12 + + +4 +5 +1 + + + + + + +package_name +id + + +12 + + +1 +2 +4 + + + + + + +package_name +namespace + + +12 + + +1 +2 +4 + + + + + + +package_name +version + + +12 + + +1 +2 +4 + + + + + + +version +id + + +12 + + +1 +2 +4 + + + + + + +version +namespace + + +12 + + +1 +2 +4 + + + + + + +version +package_name + + +12 + + +1 +2 +4 + + + + + + + + +header_to_external_package +92 + + +fileid +92 + + +package +4 + + + + +fileid +package + + +12 + + +1 +2 +92 + + + + + + +package +fileid + + +12 + + +1 +2 +1 + + +5 +6 +1 + + +6 +7 +1 + + +80 +81 +1 + + + + + + + + +svnentries +575525 + + +id +575525 + + +revision +575525 + + +author +19539 + + +revisionDate +547759 + + +changeSize +1 + + + + +id +revision + + +12 + + +1 +2 +575525 + + + + + + +id +author + + +12 + + +1 +2 +575525 + + + + + + +id +revisionDate + + +12 + + +1 +2 +575525 + + + + + + +id +changeSize + + +12 + + +1 +2 +575525 + + + + + + +revision +id + + +12 + + +1 +2 +575525 + + + + + + +revision +author + + +12 + + +1 +2 +575525 + + + + + + +revision +revisionDate + + +12 + + +1 +2 +575525 + + + + + + +revision +changeSize + + +12 + + +1 +2 +575525 + + + + + + +author +id + + +12 + + +1 +2 +7913 + + +2 +3 +2531 + + +3 +4 +1388 + + +4 +6 +1523 + + +6 +10 +1529 + + +10 +20 +1509 + + +20 +52 +1488 + + +52 +568 +1466 + + +569 +16582 +192 + + + + + + +author +revision + + +12 + + +1 +2 +7913 + + +2 +3 +2531 + + +3 +4 +1388 + + +4 +6 +1523 + + +6 +10 +1529 + + +10 +20 +1509 + + +20 +52 +1488 + + +52 +568 +1466 + + +569 +16582 +192 + + + + + + +author +revisionDate + + +12 + + +1 +2 +7996 + + +2 +3 +2509 + + +3 +4 +1379 + + +4 +6 +1520 + + +6 +10 +1529 + + +10 +20 +1507 + + +20 +52 +1474 + + +52 +662 +1466 + + +663 +16573 +159 + + + + + + +author +changeSize + + +12 + + +1 +2 +19539 + + + + + + +revisionDate +id + + +12 + + +1 +2 +531878 + + +2 +100 +15881 + + + + + + +revisionDate +revision + + +12 + + +1 +2 +531878 + + +2 +100 +15881 + + + + + + +revisionDate +author + + +12 + + +1 +2 +542505 + + +2 +17 +5254 + + + + + + +revisionDate +changeSize + + +12 + + +1 +2 +547759 + + + + + + +changeSize +id + + +12 + + +575525 +575526 +1 + + + + + + +changeSize +revision + + +12 + + +575525 +575526 +1 + + + + + + +changeSize +author + + +12 + + +19539 +19540 +1 + + + + + + +changeSize +revisionDate + + +12 + + +547759 +547760 +1 + + + + + + + + +svnaffectedfiles +1314068 + + +id +531628 + + +file +90924 + + +action +1 + + + + +id +file + + +12 + + +1 +2 +337698 + + +2 +3 +77525 + + +3 +4 +43024 + + +4 +7 +46689 + + +7 +16635 +26692 + + + + + + +id +action + + +12 + + +1 +2 +531628 + + + + + + +file +id + + +12 + + +1 +2 +11819 + + +2 +3 +18230 + + +3 +4 +9501 + + +4 +5 +6656 + + +5 +6 +5012 + + +6 +8 +7103 + + +8 +11 +6788 + + +11 +16 +6996 + + +16 +26 +7180 + + +26 +54 +6824 + + +54 +3572 +4815 + + + + + + +file +action + + +12 + + +1 +2 +90924 + + + + + + +action +id + + +12 + + +531628 +531629 +1 + + + + + + +action +file + + +12 + + +90924 +90925 +1 + + + + + + + + +svnentrymsg +575525 + + +id +575525 + + +message +568305 + + + + +id +message + + +12 + + +1 +2 +575525 + + + + + + +message +id + + +12 + + +1 +2 +565381 + + +2 +142 +2924 + + + + + + + + +svnchurn +46790 + + +commit +22361 + + +file +16124 + + +addedLines +910 + + +deletedLines +787 + + + + +commit +file + + +12 + + +1 +2 +15208 + + +2 +3 +3101 + + +3 +4 +1746 + + +4 +8 +1774 + + +8 +246 +532 + + + + + + +commit +addedLines + + +12 + + +1 +2 +16074 + + +2 +3 +3323 + + +3 +4 +1561 + + +4 +118 +1403 + + + + + + +commit +deletedLines + + +12 + + +1 +2 +16799 + + +2 +3 +3286 + + +3 +5 +1763 + + +5 +113 +513 + + + + + + +file +commit + + +12 + + +1 +2 +8618 + + +2 +3 +2956 + + +3 +4 +1426 + + +4 +6 +1364 + + +6 +12 +1210 + + +12 +448 +550 + + + + + + +file +addedLines + + +12 + + +1 +2 +9240 + + +2 +3 +3129 + + +3 +4 +1393 + + +4 +6 +1239 + + +6 +59 +1123 + + + + + + +file +deletedLines + + +12 + + +1 +2 +9525 + + +2 +3 +3192 + + +3 +4 +1401 + + +4 +7 +1387 + + +7 +70 +619 + + + + + + +addedLines +commit + + +12 + + +1 +2 +446 + + +2 +3 +133 + + +3 +4 +70 + + +4 +6 +68 + + +6 +12 +70 + + +12 +57 +69 + + +57 +6874 +54 + + + + + + +addedLines +file + + +12 + + +1 +2 +445 + + +2 +3 +132 + + +3 +4 +69 + + +4 +6 +68 + + +6 +12 +73 + + +12 +58 +69 + + +58 +6663 +54 + + + + + + +addedLines +deletedLines + + +12 + + +1 +2 +621 + + +2 +3 +96 + + +3 +7 +81 + + +7 +34 +70 + + +34 +727 +42 + + + + + + +deletedLines +commit + + +12 + + +1 +2 +439 + + +2 +3 +116 + + +3 +4 +48 + + +4 +8 +67 + + +8 +28 +60 + + +28 +6794 +57 + + + + + + +deletedLines +file + + +12 + + +1 +2 +437 + + +2 +3 +113 + + +3 +4 +49 + + +4 +7 +61 + + +7 +19 +60 + + +19 +770 +60 + + +985 +7318 +7 + + + + + + +deletedLines +addedLines + + +12 + + +1 +2 +545 + + +2 +3 +72 + + +3 +7 +69 + + +7 +30 +60 + + +30 +871 +41 + + + + + + + + +locations_default +8232279 + + +id +8232279 + + +container +65431 + + +startLine +133582 + + +startColumn +6069 + + +endLine +133471 + + +endColumn +8122 + + + + +id +container + + +12 + + +1 +2 +8232279 + + + + + + +id +startLine + + +12 + + +1 +2 +8232279 + + + + + + +id +startColumn + + +12 + + +1 +2 +8232279 + + + + + + +id +endLine + + +12 + + +1 +2 +8232279 + + + + + + +id +endColumn + + +12 + + +1 +2 +8232279 + + + + + + +container +id + + +12 + + +1 +2 +8621 + + +2 +19 +5603 + + +19 +25 +5525 + + +25 +31 +5270 + + +31 +40 +5004 + + +40 +51 +4970 + + +51 +68 +5137 + + +68 +90 +4970 + + +90 +122 +4926 + + +122 +181 +4915 + + +181 +315 +4937 + + +315 +1243 +4915 + + +1268 +18386 +632 + + + + + + +container +startLine + + +12 + + +1 +2 +8621 + + +2 +15 +5514 + + +15 +20 +5991 + + +20 +25 +5392 + + +25 +32 +5803 + + +32 +41 +5292 + + +41 +52 +5026 + + +52 +67 +4948 + + +67 +91 +5015 + + +91 +138 +4948 + + +138 +259 +4915 + + +259 +8116 +3961 + + + + + + +container +startColumn + + +12 + + +1 +2 +8621 + + +2 +4 +6002 + + +4 +8 +6002 + + +8 +11 +4859 + + +11 +14 +5581 + + +14 +18 +6013 + + +18 +23 +5547 + + +23 +29 +5736 + + +29 +36 +5015 + + +36 +48 +5237 + + +48 +70 +5059 + + +70 +187 +1753 + + + + + + +container +endLine + + +12 + + +1 +2 +8621 + + +2 +15 +5525 + + +15 +20 +5958 + + +20 +25 +5370 + + +25 +32 +5703 + + +32 +41 +5337 + + +41 +52 +5148 + + +52 +68 +5192 + + +68 +92 +4948 + + +92 +140 +4915 + + +140 +265 +4926 + + +265 +8116 +3783 + + + + + + +container +endColumn + + +12 + + +1 +2 +8621 + + +2 +14 +5081 + + +14 +19 +5902 + + +19 +23 +5736 + + +23 +27 +5481 + + +27 +32 +5325 + + +32 +38 +4959 + + +38 +45 +5581 + + +45 +54 +5203 + + +54 +65 +5226 + + +65 +80 +5092 + + +80 +215 +3217 + + + + + + +startLine +id + + +12 + + +1 +2 +20937 + + +2 +3 +15866 + + +3 +4 +14269 + + +4 +5 +10208 + + +5 +6 +7467 + + +6 +7 +6036 + + +7 +8 +6768 + + +8 +10 +10330 + + +10 +14 +10585 + + +14 +31 +10041 + + +31 +117 +10030 + + +117 +1729 +10019 + + +1734 +5898 +1020 + + + + + + +startLine +container + + +12 + + +1 +2 +38413 + + +2 +3 +33908 + + +3 +4 +9387 + + +4 +5 +8565 + + +5 +7 +11473 + + +7 +17 +10374 + + +17 +62 +10052 + + +62 +820 +10019 + + +821 +5898 +1386 + + + + + + +startLine +startColumn + + +12 + + +1 +2 +21891 + + +2 +3 +15556 + + +3 +4 +16721 + + +4 +5 +9398 + + +5 +6 +8011 + + +6 +7 +5891 + + +7 +8 +7489 + + +8 +10 +10052 + + +10 +14 +10518 + + +14 +29 +10219 + + +29 +63 +10197 + + +63 +164 +7633 + + + + + + +startLine +endLine + + +12 + + +1 +2 +97997 + + +2 +3 +15600 + + +3 +6 +10596 + + +6 +177 +9387 + + + + + + +startLine +endColumn + + +12 + + +1 +2 +21581 + + +2 +3 +15866 + + +3 +4 +15012 + + +4 +5 +10330 + + +5 +6 +7767 + + +6 +7 +5958 + + +7 +8 +6857 + + +8 +10 +10629 + + +10 +14 +10341 + + +14 +31 +10152 + + +31 +73 +10119 + + +73 +196 +8965 + + + + + + +startColumn +id + + +12 + + +1 +2 +1353 + + +2 +3 +1020 + + +3 +5 +488 + + +5 +9 +554 + + +9 +21 +466 + + +21 +49 +466 + + +50 +157 +466 + + +165 +1206 +477 + + +1337 +7569 +466 + + +7573 +184045 +310 + + + + + + +startColumn +container + + +12 + + +1 +2 +2907 + + +2 +3 +443 + + +3 +6 +477 + + +6 +14 +488 + + +14 +65 +466 + + +66 +358 +466 + + +393 +1478 +466 + + +1509 +5898 +355 + + + + + + +startColumn +startLine + + +12 + + +1 +2 +1386 + + +2 +3 +1009 + + +3 +5 +477 + + +5 +8 +466 + + +8 +17 +510 + + +17 +43 +466 + + +45 +119 +466 + + +120 +570 +466 + + +577 +1853 +466 + + +1863 +5945 +355 + + + + + + +startColumn +endLine + + +12 + + +1 +2 +1386 + + +2 +3 +1009 + + +3 +5 +477 + + +5 +8 +466 + + +8 +17 +510 + + +17 +43 +466 + + +45 +119 +466 + + +121 +570 +466 + + +577 +1855 +466 + + +1861 +5942 +355 + + + + + + +startColumn +endColumn + + +12 + + +1 +2 +3051 + + +2 +3 +454 + + +3 +7 +510 + + +7 +13 +477 + + +13 +28 +466 + + +28 +55 +477 + + +56 +108 +466 + + +114 +426 +166 + + + + + + +endLine +id + + +12 + + +1 +2 +20948 + + +2 +3 +15611 + + +3 +4 +14280 + + +4 +5 +10119 + + +5 +6 +7467 + + +6 +7 +6246 + + +7 +8 +7123 + + +8 +10 +9875 + + +10 +14 +10640 + + +14 +31 +10074 + + +31 +118 +10097 + + +118 +1791 +10019 + + +1807 +5898 +965 + + + + + + +endLine +container + + +12 + + +1 +2 +38335 + + +2 +3 +33853 + + +3 +4 +9320 + + +4 +5 +8454 + + +5 +7 +11661 + + +7 +17 +10418 + + +17 +63 +10130 + + +63 +897 +10019 + + +898 +5898 +1276 + + + + + + +endLine +startLine + + +12 + + +1 +2 +97354 + + +2 +3 +15445 + + +3 +6 +10374 + + +6 +37 +10019 + + +37 +44 +277 + + + + + + +endLine +startColumn + + +12 + + +1 +2 +21880 + + +2 +3 +15345 + + +3 +4 +16821 + + +4 +5 +9298 + + +5 +6 +7988 + + +6 +7 +6080 + + +7 +8 +7378 + + +8 +10 +10041 + + +10 +14 +10596 + + +14 +29 +10185 + + +29 +63 +10185 + + +63 +164 +7667 + + + + + + +endLine +endColumn + + +12 + + +1 +2 +21614 + + +2 +3 +15600 + + +3 +4 +15045 + + +4 +5 +10174 + + +5 +6 +8011 + + +6 +7 +5914 + + +7 +8 +7190 + + +8 +10 +10197 + + +10 +14 +10463 + + +14 +31 +10119 + + +31 +73 +10152 + + +73 +196 +8987 + + + + + + +endColumn +id + + +12 + + +1 +2 +2696 + + +2 +3 +1043 + + +3 +4 +554 + + +4 +7 +599 + + +7 +17 +665 + + +17 +58 +632 + + +60 +246 +610 + + +268 +5177 +610 + + +5177 +13550 +610 + + +13603 +22043 +99 + + + + + + +endColumn +container + + +12 + + +1 +2 +3561 + + +2 +3 +954 + + +3 +4 +532 + + +4 +9 +643 + + +9 +27 +621 + + +27 +197 +610 + + +200 +1849 +610 + + +1899 +5898 +588 + + + + + + +endColumn +startLine + + +12 + + +1 +2 +2707 + + +2 +3 +1065 + + +3 +4 +532 + + +4 +7 +599 + + +7 +17 +676 + + +17 +53 +610 + + +53 +215 +610 + + +217 +1337 +610 + + +1362 +2896 +610 + + +2914 +4469 +99 + + + + + + +endColumn +startColumn + + +12 + + +1 +2 +3705 + + +2 +3 +1054 + + +3 +5 +721 + + +5 +13 +610 + + +13 +27 +610 + + +27 +50 +632 + + +50 +76 +621 + + +76 +81 +166 + + + + + + +endColumn +endLine + + +12 + + +1 +2 +2707 + + +2 +3 +1065 + + +3 +4 +532 + + +4 +7 +599 + + +7 +15 +610 + + +15 +46 +610 + + +46 +176 +621 + + +182 +1211 +610 + + +1221 +2450 +610 + + +2609 +4469 +155 + + + + + + + + +locations_stmt +5631060 + + +id +5631060 + + +container +9396 + + +startLine +46320 + + +startColumn +299 + + +endLine +44708 + + +endColumn +412 + + + + +id +container + + +12 + + +1 +2 +5631060 + + + + + + +id +startLine + + +12 + + +1 +2 +5631060 + + + + + + +id +startColumn + + +12 + + +1 +2 +5631060 + + + + + + +id +endLine + + +12 + + +1 +2 +5631060 + + + + + + +id +endColumn + + +12 + + +1 +2 +5631060 + + + + + + +container +id + + +12 + + +1 +6 +709 + + +6 +14 +759 + + +14 +27 +737 + + +27 +48 +729 + + +48 +76 +729 + + +76 +115 +726 + + +115 +171 +709 + + +171 +237 +712 + + +237 +348 +707 + + +348 +528 +712 + + +528 +840 +709 + + +841 +1620 +707 + + +1620 +11419 +707 + + +11860 +84476 +38 + + + + + + +container +startLine + + +12 + + +1 +5 +819 + + +5 +11 +723 + + +11 +20 +745 + + +20 +33 +740 + + +33 +52 +715 + + +52 +73 +718 + + +73 +105 +718 + + +105 +153 +709 + + +153 +209 +707 + + +209 +307 +715 + + +307 +490 +707 + + +490 +903 +707 + + +903 +10013 +668 + + + + + + +container +startColumn + + +12 + + +1 +2 +126 + + +2 +3 +1350 + + +3 +4 +830 + + +4 +5 +520 + + +5 +7 +756 + + +7 +9 +605 + + +9 +12 +748 + + +12 +16 +797 + + +16 +21 +742 + + +21 +27 +726 + + +27 +35 +729 + + +35 +46 +723 + + +46 +71 +715 + + +71 +76 +22 + + + + + + +container +endLine + + +12 + + +1 +5 +869 + + +5 +11 +778 + + +11 +19 +742 + + +19 +31 +740 + + +31 +49 +712 + + +49 +69 +715 + + +69 +99 +718 + + +99 +142 +720 + + +142 +198 +715 + + +198 +292 +709 + + +292 +469 +707 + + +469 +894 +707 + + +895 +8668 +558 + + + + + + +container +endColumn + + +12 + + +1 +3 +594 + + +3 +6 +808 + + +6 +10 +756 + + +10 +15 +718 + + +15 +22 +751 + + +22 +29 +709 + + +29 +38 +740 + + +38 +46 +759 + + +46 +53 +759 + + +53 +60 +773 + + +60 +67 +817 + + +67 +73 +729 + + +73 +109 +478 + + + + + + +startLine +id + + +12 + + +1 +2 +8650 + + +2 +3 +7420 + + +3 +4 +3719 + + +4 +6 +3667 + + +6 +10 +3582 + + +10 +19 +3615 + + +19 +37 +3486 + + +37 +81 +3491 + + +81 +228 +3475 + + +228 +953 +3475 + + +953 +5507 +1736 + + + + + + +startLine +container + + +12 + + +1 +2 +9822 + + +2 +3 +8620 + + +3 +4 +3923 + + +4 +6 +3392 + + +6 +11 +3981 + + +11 +22 +3552 + + +22 +42 +3563 + + +42 +98 +3483 + + +98 +299 +3480 + + +299 +946 +2501 + + + + + + +startLine +startColumn + + +12 + + +1 +2 +11338 + + +2 +3 +9473 + + +3 +4 +5123 + + +4 +5 +3191 + + +5 +7 +3975 + + +7 +10 +3695 + + +10 +17 +3821 + + +17 +35 +3538 + + +35 +65 +2162 + + + + + + +startLine +endLine + + +12 + + +1 +2 +15862 + + +2 +3 +9261 + + +3 +4 +3937 + + +4 +6 +4149 + + +6 +9 +3626 + + +9 +15 +3629 + + +15 +27 +3549 + + +27 +62 +2305 + + + + + + +startLine +endColumn + + +12 + + +1 +2 +9544 + + +2 +3 +8276 + + +3 +4 +4212 + + +4 +6 +3830 + + +6 +10 +3656 + + +10 +18 +3684 + + +18 +29 +3560 + + +29 +46 +3488 + + +46 +64 +3477 + + +64 +84 +2589 + + + + + + +startColumn +id + + +12 + + +1 +2 +19 + + +2 +3 +13 + + +3 +7 +24 + + +7 +20 +24 + + +22 +177 +24 + + +217 +428 +24 + + +563 +874 +24 + + +964 +1575 +24 + + +1900 +2624 +24 + + +2880 +4018 +24 + + +4047 +5227 +24 + + +5723 +27012 +24 + + +32000 +631138 +19 + + + + + + +startColumn +container + + +12 + + +1 +2 +22 + + +2 +4 +27 + + +4 +12 +24 + + +14 +74 +24 + + +120 +246 +24 + + +280 +418 +24 + + +444 +562 +24 + + +577 +732 +27 + + +739 +778 +24 + + +791 +836 +24 + + +840 +973 +24 + + +977 +3376 +24 + + + + + + +startColumn +startLine + + +12 + + +1 +2 +19 + + +2 +3 +13 + + +3 +7 +24 + + +7 +19 +24 + + +20 +169 +24 + + +193 +375 +24 + + +446 +688 +24 + + +780 +1061 +24 + + +1091 +1251 +24 + + +1279 +1359 +24 + + +1364 +1475 +24 + + +1568 +2722 +24 + + +3168 +13523 +19 + + + + + + +startColumn +endLine + + +12 + + +1 +2 +19 + + +2 +3 +13 + + +3 +7 +24 + + +7 +19 +24 + + +20 +173 +24 + + +192 +384 +24 + + +453 +681 +24 + + +794 +1045 +24 + + +1088 +1247 +24 + + +1258 +1359 +24 + + +1366 +1484 +24 + + +1530 +2730 +24 + + +2911 +13557 +19 + + + + + + +startColumn +endColumn + + +12 + + +1 +2 +22 + + +2 +3 +24 + + +3 +5 +27 + + +5 +10 +19 + + +10 +12 +24 + + +12 +15 +19 + + +16 +23 +22 + + +23 +36 +24 + + +38 +59 +24 + + +59 +65 +27 + + +65 +69 +24 + + +69 +87 +24 + + +101 +133 +13 + + + + + + +endLine +id + + +12 + + +1 +2 +7461 + + +2 +3 +6193 + + +3 +4 +4055 + + +4 +6 +3912 + + +6 +10 +3752 + + +10 +18 +3442 + + +18 +36 +3527 + + +36 +74 +3370 + + +74 +202 +3362 + + +202 +744 +3354 + + +745 +4894 +2275 + + + + + + +endLine +container + + +12 + + +1 +2 +10568 + + +2 +3 +7792 + + +3 +4 +3607 + + +4 +6 +3106 + + +6 +11 +3791 + + +11 +22 +3516 + + +22 +42 +3356 + + +42 +94 +3365 + + +94 +288 +3356 + + +288 +838 +2247 + + + + + + +endLine +startLine + + +12 + + +1 +2 +13721 + + +2 +3 +9269 + + +3 +4 +4292 + + +4 +5 +2536 + + +5 +7 +3150 + + +7 +11 +3631 + + +11 +18 +3439 + + +18 +33 +3370 + + +33 +51 +1295 + + + + + + +endLine +startColumn + + +12 + + +1 +2 +9679 + + +2 +3 +9349 + + +3 +4 +5456 + + +4 +5 +3343 + + +5 +7 +3824 + + +7 +10 +3568 + + +10 +16 +3475 + + +16 +31 +3389 + + +31 +59 +2622 + + + + + + +endLine +endColumn + + +12 + + +1 +2 +10265 + + +2 +3 +7676 + + +3 +4 +3857 + + +4 +6 +3307 + + +6 +10 +3538 + + +10 +18 +3615 + + +18 +29 +3381 + + +29 +46 +3395 + + +46 +65 +3472 + + +65 +84 +2198 + + + + + + +endColumn +id + + +12 + + +1 +2 +44 + + +2 +4 +33 + + +4 +10 +33 + + +10 +39 +33 + + +41 +108 +33 + + +121 +2080 +33 + + +2761 +5372 +33 + + +6979 +16637 +33 + + +16827 +22261 +33 + + +22373 +26352 +33 + + +26608 +28939 +33 + + +29002 +39436 +33 + + +105141 +513216 +5 + + + + + + +endColumn +container + + +12 + + +1 +2 +57 + + +2 +3 +19 + + +3 +7 +35 + + +7 +19 +33 + + +20 +49 +33 + + +62 +263 +33 + + +513 +1036 +33 + + +1086 +1395 +33 + + +1398 +1737 +33 + + +1737 +1886 +33 + + +1893 +1950 +33 + + +1951 +2346 +33 + + +3360 +3361 +2 + + + + + + +endColumn +startLine + + +12 + + +1 +2 +49 + + +2 +3 +24 + + +3 +6 +30 + + +7 +21 +33 + + +21 +42 +33 + + +57 +271 +33 + + +290 +1569 +33 + + +1578 +2600 +33 + + +2675 +3394 +33 + + +3507 +4212 +33 + + +4223 +4454 +33 + + +4506 +5479 +33 + + +5487 +7385 +11 + + + + + + +endColumn +startColumn + + +12 + + +1 +2 +55 + + +2 +3 +30 + + +3 +4 +27 + + +4 +6 +33 + + +6 +13 +33 + + +14 +22 +33 + + +22 +31 +33 + + +32 +37 +35 + + +37 +44 +35 + + +44 +49 +33 + + +49 +53 +35 + + +53 +100 +27 + + + + + + +endColumn +endLine + + +12 + + +1 +2 +55 + + +2 +3 +22 + + +3 +7 +33 + + +7 +21 +35 + + +22 +59 +33 + + +77 +372 +33 + + +764 +1800 +33 + + +1820 +2849 +33 + + +2893 +3480 +33 + + +3651 +4070 +33 + + +4091 +4246 +33 + + +4256 +6581 +33 + + +7012 +7013 +2 + + + + + + + + +locations_expr +22023911 + + +id +22023911 + + +container +4089 + + +startLine +229350 + + +startColumn +504 + + +endLine +229860 + + +endColumn +600 + + + + +id +container + + +12 + + +1 +2 +22023911 + + + + + + +id +startLine + + +12 + + +1 +2 +22023911 + + + + + + +id +startColumn + + +12 + + +1 +2 +22023911 + + + + + + +id +endLine + + +12 + + +1 +2 +22023911 + + + + + + +id +endColumn + + +12 + + +1 +2 +22023911 + + + + + + +container +id + + +12 + + +1 +18 +307 + + +18 +35 +309 + + +35 +91 +309 + + +91 +231 +307 + + +231 +387 +311 + + +388 +590 +310 + + +590 +917 +307 + + +917 +1413 +307 + + +1415 +2130 +307 + + +2131 +3358 +307 + + +3360 +5751 +307 + + +5751 +11505 +307 + + +11533 +44558 +307 + + +44700 +908128 +77 + + + + + + +container +startLine + + +12 + + +1 +3 +302 + + +3 +7 +352 + + +7 +19 +318 + + +19 +40 +314 + + +40 +62 +314 + + +62 +90 +309 + + +90 +141 +309 + + +141 +207 +309 + + +207 +336 +307 + + +336 +522 +307 + + +522 +890 +307 + + +890 +1809 +307 + + +1813 +11869 +307 + + +12483 +139195 +19 + + + + + + +container +startColumn + + +12 + + +1 +3 +330 + + +3 +7 +313 + + +7 +20 +323 + + +20 +42 +321 + + +42 +54 +323 + + +54 +64 +335 + + +64 +74 +328 + + +74 +83 +313 + + +83 +92 +339 + + +92 +104 +322 + + +104 +119 +310 + + +119 +146 +310 + + +146 +330 +217 + + + + + + +container +endLine + + +12 + + +1 +3 +296 + + +3 +7 +356 + + +7 +19 +317 + + +19 +40 +313 + + +40 +61 +307 + + +61 +90 +313 + + +90 +141 +310 + + +141 +208 +310 + + +209 +339 +307 + + +339 +533 +309 + + +533 +902 +309 + + +905 +1894 +307 + + +1894 +11844 +307 + + +12485 +139999 +22 + + + + + + +container +endColumn + + +12 + + +1 +3 +283 + + +3 +7 +317 + + +7 +21 +326 + + +21 +47 +311 + + +47 +63 +334 + + +63 +74 +332 + + +74 +85 +330 + + +85 +95 +327 + + +95 +106 +338 + + +106 +120 +334 + + +120 +137 +319 + + +137 +165 +309 + + +165 +416 +225 + + + + + + +startLine +id + + +12 + + +1 +3 +11387 + + +3 +4 +10675 + + +4 +5 +23096 + + +5 +7 +7702 + + +7 +8 +19288 + + +8 +9 +2573 + + +9 +10 +25155 + + +10 +11 +18536 + + +11 +13 +18611 + + +13 +19 +19317 + + +19 +27 +18258 + + +27 +49 +17604 + + +49 +185 +17207 + + +185 +1650 +17205 + + +1650 +12866 +2730 + + + + + + +startLine +container + + +12 + + +1 +2 +124619 + + +2 +3 +26056 + + +3 +4 +18837 + + +4 +5 +16080 + + +5 +11 +17831 + + +11 +70 +17290 + + +70 +1186 +8633 + + + + + + +startLine +startColumn + + +12 + + +1 +2 +11418 + + +2 +3 +32844 + + +3 +4 +42846 + + +4 +5 +18227 + + +5 +6 +13390 + + +6 +7 +16116 + + +7 +9 +16527 + + +9 +12 +17531 + + +12 +17 +17493 + + +17 +30 +17810 + + +30 +95 +17257 + + +95 +196 +7887 + + + + + + +startLine +endLine + + +12 + + +1 +2 +116391 + + +2 +3 +74812 + + +3 +4 +14973 + + +4 +8 +19018 + + +8 +55 +4153 + + + + + + +startLine +endColumn + + +12 + + +1 +2 +22287 + + +2 +3 +39994 + + +3 +4 +26137 + + +4 +5 +17801 + + +5 +6 +23505 + + +6 +8 +11278 + + +8 +11 +20762 + + +11 +16 +18521 + + +16 +25 +17247 + + +25 +70 +17269 + + +70 +183 +14543 + + + + + + +startColumn +id + + +12 + + +1 +3 +39 + + +3 +6 +40 + + +6 +17 +39 + + +18 +63 +39 + + +67 +285 +38 + + +293 +961 +38 + + +1059 +2449 +38 + + +2583 +5443 +38 + + +5520 +11130 +38 + + +11557 +30611 +38 + + +31981 +74350 +38 + + +76021 +155966 +38 + + +161191 +675108 +38 + + +827342 +1129445 +3 + + + + + + +startColumn +container + + +12 + + +1 +2 +98 + + +2 +3 +39 + + +3 +8 +39 + + +8 +21 +39 + + +21 +55 +38 + + +56 +131 +38 + + +136 +336 +38 + + +341 +729 +38 + + +733 +1424 +38 + + +1471 +1972 +38 + + +2001 +2204 +38 + + +2210 +2528 +22 + + + + + + +startColumn +startLine + + +12 + + +1 +2 +55 + + +2 +4 +38 + + +4 +9 +38 + + +9 +30 +39 + + +31 +119 +38 + + +128 +344 +38 + + +368 +881 +39 + + +937 +2167 +38 + + +2203 +5695 +38 + + +5825 +11890 +38 + + +12106 +16720 +38 + + +16802 +25740 +38 + + +25952 +100812 +28 + + + + + + +startColumn +endLine + + +12 + + +1 +2 +55 + + +2 +4 +38 + + +4 +9 +38 + + +9 +30 +39 + + +31 +119 +38 + + +128 +344 +38 + + +368 +881 +39 + + +937 +2167 +38 + + +2204 +5695 +38 + + +5826 +11882 +38 + + +12096 +16722 +38 + + +16802 +25747 +38 + + +25952 +100803 +28 + + + + + + +startColumn +endColumn + + +12 + + +1 +2 +45 + + +2 +3 +43 + + +3 +4 +28 + + +4 +7 +38 + + +7 +13 +41 + + +13 +29 +38 + + +29 +48 +40 + + +48 +64 +38 + + +64 +86 +38 + + +86 +115 +38 + + +116 +154 +38 + + +154 +172 +39 + + +172 +292 +36 + + + + + + +endLine +id + + +12 + + +1 +3 +11894 + + +3 +4 +27646 + + +4 +6 +3709 + + +6 +7 +27277 + + +7 +9 +9505 + + +9 +10 +25862 + + +10 +12 +11199 + + +12 +13 +15813 + + +13 +17 +18651 + + +17 +24 +18713 + + +24 +40 +17830 + + +40 +111 +17254 + + +111 +682 +17248 + + +682 +12555 +7252 + + + + + + +endLine +container + + +12 + + +1 +2 +124920 + + +2 +3 +25883 + + +3 +4 +18722 + + +4 +5 +16270 + + +5 +11 +18123 + + +11 +73 +17298 + + +73 +1184 +8640 + + + + + + +endLine +startLine + + +12 + + +1 +2 +144556 + + +2 +3 +24454 + + +3 +4 +33678 + + +4 +7 +20357 + + +7 +23 +6812 + + + + + + +endLine +startColumn + + +12 + + +1 +2 +11894 + + +2 +3 +45878 + + +3 +4 +9882 + + +4 +5 +36610 + + +5 +6 +12359 + + +6 +7 +16776 + + +7 +9 +17358 + + +9 +12 +17962 + + +12 +17 +17368 + + +17 +29 +17835 + + +29 +92 +17288 + + +92 +196 +8644 + + + + + + +endLine +endColumn + + +12 + + +1 +2 +39524 + + +2 +3 +20467 + + +3 +4 +11696 + + +4 +5 +34241 + + +5 +6 +22970 + + +6 +8 +17793 + + +8 +12 +19985 + + +12 +18 +19862 + + +18 +34 +17734 + + +34 +107 +17342 + + +107 +185 +8240 + + + + + + +endColumn +id + + +12 + + +1 +3 +55 + + +3 +6 +49 + + +6 +11 +44 + + +11 +19 +45 + + +19 +76 +45 + + +82 +485 +45 + + +519 +1557 +45 + + +1577 +4435 +45 + + +4620 +13284 +45 + + +13504 +39571 +45 + + +44472 +101042 +45 + + +104467 +222423 +45 + + +222557 +374461 +38 + + + + + + +endColumn +container + + +12 + + +1 +2 +131 + + +2 +3 +58 + + +3 +9 +48 + + +9 +26 +45 + + +26 +66 +45 + + +69 +201 +47 + + +202 +538 +45 + + +544 +1085 +45 + + +1099 +1899 +45 + + +1909 +2234 +45 + + +2243 +2368 +39 + + + + + + +endColumn +startLine + + +12 + + +1 +2 +61 + + +2 +4 +47 + + +4 +10 +41 + + +10 +15 +45 + + +15 +37 +45 + + +43 +186 +45 + + +198 +606 +45 + + +635 +1783 +45 + + +1785 +5015 +45 + + +5017 +11631 +45 + + +11782 +17737 +45 + + +17876 +25461 +45 + + +25466 +57229 +36 + + + + + + +endColumn +startColumn + + +12 + + +1 +2 +61 + + +2 +3 +70 + + +3 +5 +49 + + +5 +12 +47 + + +12 +34 +47 + + +35 +54 +53 + + +54 +65 +45 + + +65 +82 +45 + + +82 +103 +45 + + +103 +123 +45 + + +123 +133 +48 + + +133 +152 +38 + + + + + + +endColumn +endLine + + +12 + + +1 +2 +61 + + +2 +4 +47 + + +4 +10 +41 + + +10 +15 +45 + + +15 +37 +45 + + +43 +186 +45 + + +198 +606 +45 + + +635 +1783 +45 + + +1787 +5010 +45 + + +5014 +11616 +45 + + +11748 +17725 +45 + + +17881 +25509 +45 + + +25511 +57229 +36 + + + + + + + + +numlines +464735 + + +element_id +464491 + + +num_lines +9098 + + +num_code +7234 + + +num_comment +3805 + + + + +element_id +num_lines + + +12 + + +1 +2 +464291 + + +2 +7 +199 + + + + + + +element_id +num_code + + +12 + + +1 +2 +464336 + + +2 +7 +155 + + + + + + +element_id +num_comment + + +12 + + +1 +2 +464480 + + +2 +3 +11 + + + + + + +num_lines +element_id + + +12 + + +1 +2 +4094 + + +2 +3 +1242 + + +3 +4 +621 + + +4 +6 +721 + + +6 +11 +754 + + +11 +24 +721 + + +24 +116 +687 + + +125 +7329 +255 + + + + + + +num_lines +num_code + + +12 + + +1 +2 +4138 + + +2 +3 +1298 + + +3 +4 +599 + + +4 +6 +754 + + +6 +10 +776 + + +10 +18 +787 + + +18 +29 +732 + + +29 +30 +11 + + + + + + +num_lines +num_comment + + +12 + + +1 +2 +4105 + + +2 +3 +1298 + + +3 +4 +643 + + +4 +6 +732 + + +6 +10 +787 + + +10 +16 +765 + + +16 +24 +699 + + +24 +28 +66 + + + + + + +num_code +element_id + + +12 + + +1 +2 +2951 + + +2 +3 +1165 + + +3 +4 +565 + + +4 +6 +621 + + +6 +11 +565 + + +11 +24 +554 + + +24 +133 +543 + + +143 +7334 +266 + + + + + + +num_code +num_lines + + +12 + + +1 +2 +2984 + + +2 +3 +1153 + + +3 +4 +599 + + +4 +6 +610 + + +6 +11 +565 + + +11 +20 +543 + + +20 +34 +543 + + +34 +40 +233 + + + + + + +num_code +num_comment + + +12 + + +1 +2 +2973 + + +2 +3 +1176 + + +3 +4 +565 + + +4 +6 +621 + + +6 +11 +599 + + +11 +18 +554 + + +18 +29 +610 + + +29 +33 +133 + + + + + + +num_comment +element_id + + +12 + + +1 +2 +1753 + + +2 +3 +477 + + +3 +4 +210 + + +4 +7 +343 + + +7 +12 +288 + + +12 +27 +299 + + +34 +200 +288 + + +228 +33793 +144 + + + + + + +num_comment +num_lines + + +12 + + +1 +2 +1753 + + +2 +3 +488 + + +3 +5 +343 + + +5 +8 +299 + + +8 +15 +299 + + +15 +47 +288 + + +48 +108 +299 + + +111 +121 +33 + + + + + + +num_comment +num_code + + +12 + + +1 +2 +1753 + + +2 +3 +488 + + +3 +5 +343 + + +5 +8 +299 + + +8 +15 +299 + + +15 +46 +310 + + +51 +102 +288 + + +106 +108 +22 + + + + + + + + +diagnostics +66951 + + +id +66951 + + +severity +22 + + +error_tag +133 + + +error_message +388 + + +full_error_message +63312 + + +location +16377 + + + + +id +severity + + +12 + + +1 +2 +66951 + + + + + + +id +error_tag + + +12 + + +1 +2 +66951 + + + + + + +id +error_message + + +12 + + +1 +2 +66951 + + + + + + +id +full_error_message + + +12 + + +1 +2 +66951 + + + + + + +id +location + + +12 + + +1 +2 +66951 + + + + + + +severity +id + + +12 + + +2 +3 +11 + + +6032 +6033 +11 + + + + + + +severity +error_tag + + +12 + + +1 +2 +11 + + +11 +12 +11 + + + + + + +severity +error_message + + +12 + + +2 +3 +11 + + +33 +34 +11 + + + + + + +severity +full_error_message + + +12 + + +2 +3 +11 + + +5704 +5705 +11 + + + + + + +severity +location + + +12 + + +2 +3 +11 + + +1474 +1475 +11 + + + + + + +error_tag +id + + +12 + + +1 +2 +22 + + +2 +3 +22 + + +36 +37 +11 + + +45 +46 +33 + + +92 +93 +11 + + +329 +330 +11 + + +437 +438 +11 + + +4999 +5000 +11 + + + + + + +error_tag +severity + + +12 + + +1 +2 +133 + + + + + + +error_tag +error_message + + +12 + + +1 +2 +88 + + +2 +3 +11 + + +3 +4 +11 + + +10 +11 +11 + + +12 +13 +11 + + + + + + +error_tag +full_error_message + + +12 + + +1 +2 +33 + + +2 +3 +22 + + +36 +37 +11 + + +45 +46 +33 + + +92 +93 +11 + + +437 +438 +11 + + +4999 +5000 +11 + + + + + + +error_tag +location + + +12 + + +1 +2 +77 + + +2 +3 +11 + + +6 +7 +11 + + +71 +72 +11 + + +92 +93 +11 + + +1302 +1303 +11 + + + + + + +error_message +id + + +12 + + +1 +2 +44 + + +2 +3 +11 + + +3 +4 +133 + + +4 +5 +22 + + +8 +9 +22 + + +10 +11 +22 + + +12 +13 +44 + + +45 +46 +33 + + +60 +303 +33 + + +329 +5000 +22 + + + + + + +error_message +severity + + +12 + + +1 +2 +388 + + + + + + +error_message +error_tag + + +12 + + +1 +2 +388 + + + + + + +error_message +full_error_message + + +12 + + +1 +2 +55 + + +2 +3 +11 + + +3 +4 +133 + + +4 +5 +22 + + +8 +9 +22 + + +10 +11 +22 + + +12 +13 +44 + + +45 +46 +33 + + +60 +303 +33 + + +4999 +5000 +11 + + + + + + +error_message +location + + +12 + + +1 +2 +233 + + +4 +5 +22 + + +8 +9 +22 + + +10 +11 +22 + + +12 +13 +44 + + +17 +35 +33 + + +1302 +1303 +11 + + + + + + +full_error_message +id + + +12 + + +1 +2 +63301 + + +329 +330 +11 + + + + + + +full_error_message +severity + + +12 + + +1 +2 +63312 + + + + + + +full_error_message +error_tag + + +12 + + +1 +2 +63312 + + + + + + +full_error_message +error_message + + +12 + + +1 +2 +63312 + + + + + + +full_error_message +location + + +12 + + +1 +2 +63312 + + + + + + +location +id + + +12 + + +1 +2 +3872 + + +2 +3 +2407 + + +3 +4 +6113 + + +4 +5 +1730 + + +6 +7 +1531 + + +15 +467 +721 + + + + + + +location +severity + + +12 + + +1 +2 +16377 + + + + + + +location +error_tag + + +12 + + +1 +2 +16366 + + +5 +6 +11 + + + + + + +location +error_message + + +12 + + +1 +2 +16299 + + +2 +6 +77 + + + + + + +location +full_error_message + + +12 + + +1 +2 +3872 + + +2 +3 +2407 + + +3 +4 +6113 + + +4 +5 +1730 + + +6 +7 +1531 + + +15 +139 +721 + + + + + + + + +files +57465 + + +id +57465 + + +name +57465 + + +simple +39201 + + +ext +99 + + +fromSource +11 + + + + +id +name + + +12 + + +1 +2 +57465 + + + + + + +id +simple + + +12 + + +1 +2 +57465 + + + + + + +id +ext + + +12 + + +1 +2 +57465 + + + + + + +id +fromSource + + +12 + + +1 +2 +57465 + + + + + + +name +id + + +12 + + +1 +2 +57465 + + + + + + +name +simple + + +12 + + +1 +2 +57465 + + + + + + +name +ext + + +12 + + +1 +2 +57465 + + + + + + +name +fromSource + + +12 + + +1 +2 +57465 + + + + + + +simple +id + + +12 + + +1 +2 +29703 + + +2 +3 +5914 + + +3 +7 +3117 + + +7 +36 +466 + + + + + + +simple +name + + +12 + + +1 +2 +29703 + + +2 +3 +5914 + + +3 +7 +3117 + + +7 +36 +466 + + + + + + +simple +ext + + +12 + + +1 +2 +34674 + + +2 +3 +3828 + + +3 +6 +699 + + + + + + +simple +fromSource + + +12 + + +1 +2 +39201 + + + + + + +ext +id + + +12 + + +1 +2 +11 + + +14 +15 +11 + + +35 +36 +11 + + +52 +53 +11 + + +88 +89 +11 + + +116 +117 +11 + + +421 +422 +11 + + +662 +663 +11 + + +3790 +3791 +11 + + + + + + +ext +name + + +12 + + +1 +2 +11 + + +14 +15 +11 + + +35 +36 +11 + + +52 +53 +11 + + +88 +89 +11 + + +116 +117 +11 + + +421 +422 +11 + + +662 +663 +11 + + +3790 +3791 +11 + + + + + + +ext +simple + + +12 + + +1 +2 +11 + + +14 +15 +11 + + +34 +35 +11 + + +52 +53 +11 + + +85 +86 +11 + + +114 +115 +11 + + +407 +408 +11 + + +585 +586 +11 + + +2721 +2722 +11 + + + + + + +ext +fromSource + + +12 + + +1 +2 +99 + + + + + + +fromSource +id + + +12 + + +5179 +5180 +11 + + + + + + +fromSource +name + + +12 + + +5179 +5180 +11 + + + + + + +fromSource +simple + + +12 + + +3533 +3534 +11 + + + + + + +fromSource +ext + + +12 + + +9 +10 +11 + + + + + + + + +folders +7966 + + +id +7966 + + +name +7966 + + +simple +3073 + + + + +id +name + + +12 + + +1 +2 +7966 + + + + + + +id +simple + + +12 + + +1 +2 +7966 + + + + + + +name +id + + +12 + + +1 +2 +7966 + + + + + + +name +simple + + +12 + + +1 +2 +7966 + + + + + + +simple +id + + +12 + + +1 +2 +1963 + + +2 +3 +488 + + +3 +4 +355 + + +4 +29 +233 + + +29 +107 +33 + + + + + + +simple +name + + +12 + + +1 +2 +1963 + + +2 +3 +488 + + +3 +4 +355 + + +4 +29 +233 + + +29 +107 +33 + + + + + + + + +containerparent +65409 + + +parent +7966 + + +child +65409 + + + + +parent +child + + +12 + + +1 +2 +2840 + + +2 +3 +987 + + +3 +4 +410 + + +4 +5 +599 + + +5 +7 +599 + + +7 +10 +499 + + +10 +13 +676 + + +13 +20 +654 + + +20 +77 +599 + + +77 +155 +99 + + + + + + +child +parent + + +12 + + +1 +2 +65409 + + + + + + + + +fileannotations +5449491 + + +id +5203 + + +kind +22 + + +name +51617 + + +value +45248 + + + + +id +kind + + +12 + + +1 +2 +122 + + +2 +3 +5081 + + + + + + +id +name + + +12 + + +1 +67 +421 + + +67 +85 +399 + + +89 +189 +399 + + +190 +296 +399 + + +337 +488 +399 + + +488 +593 +399 + + +593 +687 +399 + + +687 +933 +399 + + +935 +974 +233 + + +974 +975 +1398 + + +976 +2244 +355 + + + + + + +id +value + + +12 + + +1 +80 +410 + + +89 +109 +410 + + +109 +234 +410 + + +234 +533 +399 + + +536 +744 +399 + + +744 +934 +399 + + +934 +1154 +410 + + +1166 +1612 +366 + + +1616 +1617 +1398 + + +1635 +2819 +399 + + +2827 +4079 +199 + + + + + + +kind +id + + +12 + + +458 +459 +11 + + +469 +470 +11 + + + + + + +kind +name + + +12 + + +2 +3 +11 + + +4650 +4651 +11 + + + + + + +kind +value + + +12 + + +1 +2 +11 + + +4078 +4079 +11 + + + + + + +name +id + + +12 + + +1 +2 +7633 + + +2 +3 +5270 + + +3 +6 +4049 + + +6 +8 +4405 + + +8 +13 +4205 + + +13 +17 +4382 + + +17 +21 +4582 + + +21 +35 +3972 + + +35 +155 +4227 + + +155 +262 +3872 + + +262 +380 +3905 + + +380 +457 +1109 + + + + + + +name +kind + + +12 + + +1 +2 +51617 + + + + + + +name +value + + +12 + + +1 +2 +9586 + + +2 +3 +6124 + + +3 +4 +2086 + + +4 +6 +3861 + + +6 +9 +4449 + + +9 +14 +4227 + + +14 +17 +4149 + + +17 +21 +4127 + + +21 +38 +3916 + + +38 +101 +3894 + + +101 +149 +3872 + + +150 +2005 +1320 + + + + + + +value +id + + +12 + + +1 +2 +4904 + + +2 +3 +2418 + + +3 +6 +3916 + + +6 +21 +4127 + + +21 +24 +3317 + + +24 +26 +3528 + + +26 +41 +3395 + + +41 +196 +3306 + + +196 +213 +3395 + + +213 +267 +3639 + + +267 +323 +3395 + + +323 +366 +3428 + + +366 +470 +2474 + + + + + + +value +kind + + +12 + + +1 +2 +45237 + + +2 +3 +11 + + + + + + +value +name + + +12 + + +1 +2 +4959 + + +2 +3 +2807 + + +3 +6 +3484 + + +6 +15 +3927 + + +15 +18 +3417 + + +18 +20 +3362 + + +20 +24 +3805 + + +24 +40 +3517 + + +40 +50 +3439 + + +50 +77 +3495 + + +77 +95 +3395 + + +95 +109 +3617 + + +109 +167 +2019 + + + + + + + + +inmacroexpansion +63452833 + + +id +18693802 + + +inv +2624364 + + + + +id +inv + + +12 + + +1 +2 +5514134 + + +2 +3 +3596100 + + +3 +4 +2525028 + + +4 +5 +1762416 + + +5 +6 +1364780 + + +6 +7 +895889 + + +7 +8 +1716180 + + +8 +2023 +1319271 + + + + + + +inv +id + + +12 + + +1 +2 +619235 + + +2 +3 +363933 + + +3 +4 +191730 + + +4 +5 +222590 + + +5 +7 +198749 + + +7 +10 +222714 + + +10 +15 +213496 + + +15 +24 +198952 + + +24 +52 +198586 + + +52 +61799 +194374 + + + + + + + + +affectedbymacroexpansion +41757016 + + +id +4872284 + + +inv +3777162 + + + + +id +inv + + +12 + + +1 +2 +1547962 + + +2 +3 +940276 + + +3 +4 +790792 + + +4 +6 +395719 + + +6 +10 +382771 + + +10 +25 +394460 + + +25 +83 +365469 + + +83 +11028 +54832 + + + + + + +inv +id + + +12 + + +1 +2 +221152 + + +2 +3 +297570 + + +3 +4 +256423 + + +4 +5 +331756 + + +5 +6 +326665 + + +6 +7 +309023 + + +7 +8 +256875 + + +8 +9 +246492 + + +9 +10 +209406 + + +10 +12 +317632 + + +12 +15 +288105 + + +15 +22 +300866 + + +22 +50 +284976 + + +50 +526 +130214 + + + + + + + + +macroinvocations +37650204 + + +id +37650204 + + +macro_id +79368 + + +location +711283 + + +kind +22 + + + + +id +macro_id + + +12 + + +1 +2 +37650204 + + + + + + +id +location + + +12 + + +1 +2 +37650204 + + + + + + +id +kind + + +12 + + +1 +2 +37650204 + + + + + + +macro_id +id + + +12 + + +1 +2 +17575 + + +2 +3 +10796 + + +3 +4 +4382 + + +4 +5 +6479 + + +5 +8 +5448 + + +8 +13 +6557 + + +13 +27 +6147 + + +27 +52 +6058 + + +52 +145 +5980 + + +145 +907 +5958 + + +921 +207618 +3983 + + + + + + +macro_id +location + + +12 + + +1 +2 +40943 + + +2 +3 +11029 + + +3 +4 +5647 + + +4 +5 +4793 + + +5 +9 +6546 + + +9 +27 +6013 + + +27 +3101 +4393 + + + + + + +macro_id +kind + + +12 + + +1 +2 +73742 + + +2 +3 +5625 + + + + + + +location +id + + +12 + + +1 +2 +291841 + + +2 +3 +148994 + + +3 +4 +34574 + + +4 +6 +64388 + + +6 +11 +62879 + + +11 +33 +53892 + + +33 +2013 +53348 + + +2064 +268130 +1364 + + + + + + +location +macro_id + + +12 + + +1 +2 +665691 + + +2 +356 +45592 + + + + + + +location +kind + + +12 + + +1 +2 +711283 + + + + + + +kind +id + + +12 + + +40547 +40548 +11 + + +3352654 +3352655 +11 + + + + + + +kind +macro_id + + +12 + + +1745 +1746 +11 + + +5915 +5916 +11 + + + + + + +kind +location + + +12 + + +5415 +5416 +11 + + +58689 +58690 +11 + + + + + + + + +macroparent +33270445 + + +id +33270445 + + +parent_id +25964519 + + + + +id +parent_id + + +12 + + +1 +2 +33270445 + + + + + + +parent_id +id + + +12 + + +1 +2 +20030530 + + +2 +3 +5082698 + + +3 +88 +851290 + + + + + + + + +macrolocationbind +16645292 + + +id +3297751 + + +location +15489990 + + + + +id +location + + +12 + + +1 +2 +767809 + + +2 +3 +538486 + + +3 +4 +742858 + + +4 +5 +383751 + + +5 +6 +188308 + + +6 +8 +250282 + + +8 +14 +270108 + + +14 +984 +156145 + + + + + + +location +id + + +12 + + +1 +2 +15194777 + + +2 +272 +295212 + + + + + + + + +macro_argument_unexpanded +98490828 + + +invocation +28576010 + + +argument_index +732 + + +text +308551 + + + + +invocation +argument_index + + +12 + + +1 +2 +7849008 + + +2 +3 +12096905 + + +3 +4 +6528112 + + +4 +67 +2101984 + + + + + + +invocation +text + + +12 + + +1 +2 +7909436 + + +2 +3 +12273561 + + +3 +4 +6341703 + + +4 +67 +2051309 + + + + + + +argument_index +invocation + + +12 + + +55017 +55018 +643 + + +55186 +189441 +55 + + +777782 +2575396 +33 + + + + + + +argument_index +text + + +12 + + +2 +3 +643 + + +13 +921 +55 + + +6053 +19178 +33 + + + + + + +text +invocation + + +12 + + +1 +2 +42496 + + +2 +3 +48000 + + +3 +4 +13104 + + +4 +5 +56976 + + +5 +8 +20316 + + +8 +12 +17475 + + +12 +16 +20848 + + +16 +25 +26086 + + +25 +50 +23645 + + +50 +208 +23167 + + +208 +596657 +16432 + + + + + + +text +argument_index + + +12 + + +1 +2 +227030 + + +2 +3 +70990 + + +3 +9 +10529 + + + + + + + + +macro_argument_expanded +98490828 + + +invocation +28576010 + + +argument_index +732 + + +text +184955 + + + + +invocation +argument_index + + +12 + + +1 +2 +7849008 + + +2 +3 +12096905 + + +3 +4 +6528112 + + +4 +67 +2101984 + + + + + + +invocation +text + + +12 + + +1 +2 +11463891 + + +2 +3 +10533599 + + +3 +4 +5478950 + + +4 +9 +1099569 + + + + + + +argument_index +invocation + + +12 + + +55017 +55018 +643 + + +55186 +189441 +55 + + +777782 +2575396 +33 + + + + + + +argument_index +text + + +12 + + +1 +2 +632 + + +2 +80 +55 + + +767 +13318 +44 + + + + + + +text +invocation + + +12 + + +1 +2 +27428 + + +2 +3 +37969 + + +3 +4 +5669 + + +4 +5 +16632 + + +5 +6 +2407 + + +6 +7 +16832 + + +7 +11 +15756 + + +11 +17 +15112 + + +17 +37 +14602 + + +37 +109 +13947 + + +110 +641 +13880 + + +641 +1149670 +4715 + + + + + + +text +argument_index + + +12 + + +1 +2 +96921 + + +2 +3 +73454 + + +3 +5 +14102 + + +5 +66 +477 + + + + + + + + +functions +3698710 + + +id +3698710 + + +name +266409 + + +kind +77 + + + + +id +name + + +12 + + +1 +2 +3698710 + + + + + + +id +kind + + +12 + + +1 +2 +3698710 + + + + + + +name +id + + +12 + + +1 +2 +173737 + + +2 +3 +25930 + + +3 +4 +14846 + + +4 +7 +20283 + + +7 +24 +20194 + + +24 +63010 +11417 + + + + + + +name +kind + + +12 + + +1 +2 +264889 + + +2 +3 +1520 + + + + + + +kind +id + + +12 + + +30 +31 +11 + + +1563 +1564 +11 + + +2406 +2407 +11 + + +6934 +6935 +11 + + +66893 +66894 +11 + + +96418 +96419 +11 + + +159100 +159101 +11 + + + + + + +kind +name + + +12 + + +10 +11 +11 + + +42 +43 +11 + + +425 +426 +11 + + +1449 +1450 +11 + + +2406 +2407 +11 + + +2766 +2767 +11 + + +17049 +17050 +11 + + + + + + + + +function_entry_point +1222898 + + +id +1201683 + + +entry_point +1222887 + + + + +id +entry_point + + +12 + + +1 +2 +1194116 + + +2 +84 +7567 + + + + + + +entry_point +id + + +12 + + +1 +2 +1222876 + + +2 +3 +11 + + + + + + + + +function_return_type +3751671 + + +id +3698688 + + +return_type +884222 + + + + +id +return_type + + +12 + + +1 +2 +3649490 + + +2 +6 +49198 + + + + + + +return_type +id + + +12 + + +1 +2 +433201 + + +2 +3 +329400 + + +3 +5 +69925 + + +5 +118707 +51695 + + + + + + + + +purefunctions +33130 + + +id +33130 + + + + + +function_deleted +70191 + + +id +70191 + + + + + +function_defaulted +20560 + + +id +20560 + + + + + +fun_decls +3050240 + + +id +3050240 + + +function +2845512 + + +type_id +727206 + + +name +237715 + + +location +675633 + + + + +id +function + + +12 + + +1 +2 +3050240 + + + + + + +id +type_id + + +12 + + +1 +2 +3050240 + + + + + + +id +name + + +12 + + +1 +2 +3050240 + + + + + + +id +location + + +12 + + +1 +2 +3050240 + + + + + + +function +id + + +12 + + +1 +2 +2676301 + + +2 +29 +169210 + + + + + + +function +type_id + + +12 + + +1 +2 +2808374 + + +2 +6 +37137 + + + + + + +function +name + + +12 + + +1 +2 +2845512 + + + + + + +function +location + + +12 + + +1 +2 +2755547 + + +2 +29 +89964 + + + + + + +type_id +id + + +12 + + +1 +2 +313167 + + +2 +3 +308218 + + +3 +5 +65098 + + +5 +93469 +40721 + + + + + + +type_id +function + + +12 + + +1 +2 +328324 + + +2 +3 +303780 + + +3 +5 +57975 + + +5 +87492 +37126 + + + + + + +type_id +name + + +12 + + +1 +2 +620098 + + +2 +4 +64644 + + +4 +7436 +42463 + + + + + + +type_id +location + + +12 + + +1 +2 +576248 + + +2 +3 +69936 + + +3 +6 +59340 + + +6 +18812 +21681 + + + + + + +name +id + + +12 + + +1 +2 +136311 + + +2 +3 +32222 + + +3 +4 +17020 + + +4 +6 +17997 + + +6 +13 +19195 + + +13 +62669 +14968 + + + + + + +name +function + + +12 + + +1 +2 +149759 + + +2 +3 +29581 + + +3 +4 +15234 + + +4 +7 +18885 + + +7 +32 +17842 + + +32 +62314 +6413 + + + + + + +name +type_id + + +12 + + +1 +2 +208378 + + +2 +5 +19073 + + +5 +32496 +10263 + + + + + + +name +location + + +12 + + +1 +2 +154320 + + +2 +3 +46568 + + +3 +5 +18718 + + +5 +133 +17830 + + +134 +7703 +277 + + + + + + +location +id + + +12 + + +1 +2 +438793 + + +2 +3 +107762 + + +3 +5 +57953 + + +5 +18 +51084 + + +18 +1219 +20038 + + + + + + +location +function + + +12 + + +1 +2 +449123 + + +2 +3 +115884 + + +3 +6 +52826 + + +6 +71 +50774 + + +71 +1219 +7023 + + + + + + +location +type_id + + +12 + + +1 +2 +581596 + + +2 +4 +59883 + + +4 +611 +34152 + + + + + + +location +name + + +12 + + +1 +2 +651155 + + +2 +126 +24477 + + + + + + + + +fun_def +577324 + + +id +577324 + + + + + +fun_specialized +2585 + + +id +2585 + + + + + +fun_implicit +410 + + +id +410 + + + + + +fun_decl_specifiers +656424 + + +id +368087 + + +name +11 + + + + +id +name + + +12 + + +1 +2 +107397 + + +2 +3 +233043 + + +3 +4 +27646 + + + + + + +name +id + + +12 + + +13599 +13600 +2 + + +25854 +25855 +2 + + +65341 +65342 +2 + + +133779 +133780 +2 + + + + + + + + +fun_decl_throws +7 + + +fun_decl +7 + + +index +1 + + +type_id +2 + + + + +fun_decl +index + + +12 + + +1 +2 +7 + + + + + + +fun_decl +type_id + + +12 + + +1 +2 +7 + + + + + + +index +fun_decl + + +12 + + +7 +8 +1 + + + + + + +index +type_id + + +12 + + +2 +3 +1 + + + + + + +type_id +fun_decl + + +12 + + +1 +2 +1 + + +6 +7 +1 + + + + + + +type_id +index + + +12 + + +1 +2 +2 + + + + + + + + +fun_decl_empty_throws +730557 + + +fun_decl +730557 + + + + + +fun_decl_noexcept +7911 + + +fun_decl +7134 + + +constant +7889 + + + + +fun_decl +constant + + +12 + + +1 +2 +6357 + + +2 +3 +776 + + + + + + +constant +fun_decl + + +12 + + +1 +2 +7866 + + +2 +3 +22 + + + + + + + + +fun_decl_empty_noexcept +455104 + + +fun_decl +455104 + + + + + +fun_decl_typedef_type +177 + + +fun_decl +177 + + +typedeftype_id +88 + + + + +fun_decl +typedeftype_id + + +12 + + +1 +2 +177 + + + + + + +typedeftype_id +fun_decl + + +12 + + +2 +3 +88 + + + + + + + + +param_decl_bind +3782805 + + +id +3782805 + + +index +355 + + +fun_decl +2425903 + + + + +id +index + + +12 + + +1 +2 +3782805 + + + + + + +id +fun_decl + + +12 + + +1 +2 +3782805 + + + + + + +index +id + + +12 + + +2 +3 +144 + + +4 +10 +22 + + +15 +44 +22 + + +118 +197 +22 + + +272 +349 +22 + + +436 +641 +22 + + +915 +1254 +22 + + +1770 +2683 +22 + + +4837 +11858 +22 + + +29350 +67520 +22 + + +218633 +218634 +11 + + + + + + +index +fun_decl + + +12 + + +2 +3 +144 + + +4 +10 +22 + + +15 +44 +22 + + +118 +197 +22 + + +272 +349 +22 + + +436 +641 +22 + + +915 +1254 +22 + + +1770 +2683 +22 + + +4837 +11858 +22 + + +29350 +67520 +22 + + +218633 +218634 +11 + + + + + + +fun_decl +id + + +12 + + +1 +2 +1676727 + + +2 +3 +423514 + + +3 +4 +194098 + + +4 +33 +131562 + + + + + + +fun_decl +index + + +12 + + +1 +2 +1676727 + + +2 +3 +423514 + + +3 +4 +194098 + + +4 +33 +131562 + + + + + + + + +var_decls +4647089 + + +id +4647089 + + +variable +4346826 + + +type_id +1416642 + + +name +130197 + + +location +1190266 + + + + +id +variable + + +12 + + +1 +2 +4647089 + + + + + + +id +type_id + + +12 + + +1 +2 +4647089 + + + + + + +id +name + + +12 + + +1 +2 +4647089 + + + + + + +id +location + + +12 + + +1 +2 +4647089 + + + + + + +variable +id + + +12 + + +1 +2 +4099679 + + +2 +9 +247147 + + + + + + +variable +type_id + + +12 + + +1 +2 +4284057 + + +2 +9 +62768 + + + + + + +variable +name + + +12 + + +1 +2 +4329217 + + +2 +3 +17608 + + + + + + +variable +location + + +12 + + +1 +2 +4230032 + + +2 +9 +116794 + + + + + + +type_id +id + + +12 + + +1 +2 +927107 + + +2 +3 +270914 + + +3 +5 +106586 + + +5 +61 +106308 + + +61 +8408 +5725 + + + + + + +type_id +variable + + +12 + + +1 +2 +952417 + + +2 +3 +268429 + + +3 +6 +112988 + + +6 +7848 +82807 + + + + + + +type_id +name + + +12 + + +1 +2 +1238843 + + +2 +3 +119867 + + +3 +1005 +57931 + + + + + + +type_id +location + + +12 + + +1 +2 +1128640 + + +2 +3 +141204 + + +3 +8 +106885 + + +8 +3994 +39911 + + + + + + +name +id + + +12 + + +1 +2 +50208 + + +2 +3 +25087 + + +3 +4 +10729 + + +4 +6 +11717 + + +6 +9 +10030 + + +9 +21 +9952 + + +21 +168 +9775 + + +169 +101174 +2696 + + + + + + +name +variable + + +12 + + +1 +2 +54735 + + +2 +3 +23645 + + +3 +4 +11539 + + +4 +6 +10529 + + +6 +10 +9919 + + +10 +25 +9875 + + +25 +2795 +9775 + + +2917 +99535 +177 + + + + + + +name +type_id + + +12 + + +1 +2 +79789 + + +2 +3 +17786 + + +3 +4 +8022 + + +4 +7 +10984 + + +7 +33 +9864 + + +33 +60702 +3750 + + + + + + +name +location + + +12 + + +1 +2 +75484 + + +2 +3 +20305 + + +3 +4 +7023 + + +4 +7 +11506 + + +7 +22 +9930 + + +22 +9251 +5947 + + + + + + +location +id + + +12 + + +1 +2 +844488 + + +2 +3 +158270 + + +3 +5 +90508 + + +5 +65 +89365 + + +65 +64659 +7633 + + + + + + +location +variable + + +12 + + +1 +2 +890092 + + +2 +3 +140350 + + +3 +7 +95889 + + +7 +64264 +63933 + + + + + + +location +type_id + + +12 + + +1 +2 +1026936 + + +2 +4 +109881 + + +4 +52838 +53448 + + + + + + +location +name + + +12 + + +1 +2 +1181411 + + +2 +52 +8854 + + + + + + + + +var_def +1669548 + + +id +1669548 + + + + + +var_decl_specifiers +348206 + + +id +348206 + + +name +5 + + + + +id +name + + +12 + + +1 +2 +348206 + + + + + + +name +id + + +12 + + +29 +30 +1 + + +37 +38 +1 + + +2792 +2793 +1 + + +262875 +262876 +1 + + + + + + + + +type_decls +1388703 + + +id +1388703 + + +type_id +1360431 + + +location +1078842 + + + + +id +type_id + + +12 + + +1 +2 +1388703 + + + + + + +id +location + + +12 + + +1 +2 +1388703 + + + + + + +type_id +id + + +12 + + +1 +2 +1339571 + + +2 +24 +20860 + + + + + + +type_id +location + + +12 + + +1 +2 +1340658 + + +2 +24 +19772 + + + + + + +location +id + + +12 + + +1 +2 +1018448 + + +2 +585 +60394 + + + + + + +location +type_id + + +12 + + +1 +2 +1019535 + + +2 +585 +59306 + + + + + + + + +type_def +923545 + + +id +923545 + + + + + +type_decl_top +265444 + + +type_decl +265444 + + + + + +namespace_decls +130885 + + +id +130885 + + +namespace_id +7866 + + +location +116061 + + +bodylocation +116383 + + + + +id +namespace_id + + +12 + + +1 +2 +130885 + + + + + + +id +location + + +12 + + +1 +2 +130885 + + + + + + +id +bodylocation + + +12 + + +1 +2 +130885 + + + + + + +namespace_id +id + + +12 + + +1 +2 +4038 + + +2 +3 +920 + + +3 +5 +699 + + +5 +9 +599 + + +9 +18 +621 + + +18 +67 +621 + + +68 +3515 +366 + + + + + + +namespace_id +location + + +12 + + +1 +2 +4038 + + +2 +3 +920 + + +3 +5 +699 + + +5 +9 +599 + + +9 +18 +621 + + +18 +67 +621 + + +68 +3515 +366 + + + + + + +namespace_id +bodylocation + + +12 + + +1 +2 +4038 + + +2 +3 +920 + + +3 +5 +699 + + +5 +9 +599 + + +9 +18 +621 + + +18 +67 +621 + + +68 +3514 +366 + + + + + + +location +id + + +12 + + +1 +2 +107706 + + +2 +29 +8355 + + + + + + +location +namespace_id + + +12 + + +1 +2 +107706 + + +2 +29 +8355 + + + + + + +location +bodylocation + + +12 + + +1 +2 +115329 + + +2 +3 +732 + + + + + + +bodylocation +id + + +12 + + +1 +2 +108383 + + +2 +29 +8000 + + + + + + +bodylocation +namespace_id + + +12 + + +1 +2 +108383 + + +2 +29 +8000 + + + + + + +bodylocation +location + + +12 + + +1 +2 +115995 + + +2 +5 +388 + + + + + + + + +usings +201199 + + +id +201199 + + +element_id +19883 + + +location +20560 + + + + +id +element_id + + +12 + + +1 +2 +201199 + + + + + + +id +location + + +12 + + +1 +2 +201199 + + + + + + +element_id +id + + +12 + + +1 +2 +14757 + + +2 +3 +2152 + + +3 +96 +1464 + + +113 +116 +1509 + + + + + + +element_id +location + + +12 + + +1 +2 +14757 + + +2 +3 +2152 + + +3 +96 +1464 + + +113 +116 +1509 + + + + + + +location +id + + +12 + + +1 +2 +16898 + + +2 +3 +1697 + + +3 +138 +1675 + + +138 +139 +288 + + + + + + +location +element_id + + +12 + + +1 +2 +16898 + + +2 +3 +1697 + + +3 +138 +1675 + + +138 +139 +288 + + + + + + + + +using_container +587887 + + +parent +10984 + + +child +198536 + + + + +parent +child + + +12 + + +1 +2 +5115 + + +2 +6 +843 + + +6 +39 +832 + + +39 +134 +221 + + +137 +138 +3040 + + +138 +141 +832 + + +153 +433 +99 + + + + + + +child +parent + + +12 + + +1 +2 +22402 + + +2 +3 +43850 + + +3 +4 +98852 + + +4 +5 +18973 + + +5 +128 +14457 + + + + + + + + +static_asserts +18201 + + +id +18201 + + +condition +18201 + + +message +3770 + + +location +11603 + + + + +id +condition + + +12 + + +1 +2 +18201 + + + + + + +id +message + + +12 + + +1 +2 +18201 + + + + + + +id +location + + +12 + + +1 +2 +18201 + + + + + + +condition +id + + +12 + + +1 +2 +18201 + + + + + + +condition +message + + +12 + + +1 +2 +18201 + + + + + + +condition +location + + +12 + + +1 +2 +18201 + + + + + + +message +id + + +12 + + +1 +2 +3532 + + +2 +3227 +238 + + + + + + +message +condition + + +12 + + +1 +2 +3532 + + +2 +3227 +238 + + + + + + +message +location + + +12 + + +1 +2 +3622 + + +2 +2872 +148 + + + + + + +location +id + + +12 + + +1 +2 +9349 + + +2 +3 +1745 + + +3 +168 +509 + + + + + + +location +condition + + +12 + + +1 +2 +9349 + + +2 +3 +1745 + + +3 +168 +509 + + + + + + +location +message + + +12 + + +1 +2 +9947 + + +2 +3 +1358 + + +3 +47 +298 + + + + + + + + +params +4638656 + + +id +4568553 + + +function +2877789 + + +index +355 + + +type_id +1332824 + + + + +id +function + + +12 + + +1 +2 +4567898 + + +2 +65 +654 + + + + + + +id +index + + +12 + + +1 +2 +4568553 + + + + + + +id +type_id + + +12 + + +1 +2 +4504253 + + +2 +9 +64300 + + + + + + +function +id + + +12 + + +1 +2 +1906398 + + +2 +3 +559515 + + +3 +4 +258465 + + +4 +33 +153410 + + + + + + +function +index + + +12 + + +1 +2 +1906398 + + +2 +3 +559515 + + +3 +4 +258465 + + +4 +33 +153410 + + + + + + +function +type_id + + +12 + + +1 +2 +2004119 + + +2 +3 +555598 + + +3 +4 +213893 + + +4 +20 +104178 + + + + + + +index +id + + +12 + + +2 +3 +144 + + +4 +10 +22 + + +15 +44 +22 + + +119 +199 +22 + + +275 +353 +22 + + +441 +660 +22 + + +939 +1280 +22 + + +1788 +2750 +22 + + +5151 +13827 +22 + + +37120 +87547 +22 + + +259199 +259200 +11 + + + + + + +index +function + + +12 + + +2 +3 +144 + + +4 +10 +22 + + +15 +44 +22 + + +119 +199 +22 + + +275 +353 +22 + + +441 +660 +22 + + +939 +1280 +22 + + +1788 +2750 +22 + + +5151 +13827 +22 + + +37120 +87547 +22 + + +259359 +259360 +11 + + + + + + +index +type_id + + +12 + + +1 +2 +144 + + +3 +7 +22 + + +8 +20 +22 + + +38 +42 +22 + + +44 +47 +22 + + +62 +107 +22 + + +177 +259 +22 + + +420 +675 +22 + + +1210 +2885 +22 + + +8583 +23874 +22 + + +100628 +100629 +11 + + + + + + +type_id +id + + +12 + + +1 +2 +845575 + + +2 +3 +252961 + + +3 +5 +102946 + + +5 +16 +101004 + + +16 +6402 +30335 + + + + + + +type_id +function + + +12 + + +1 +2 +868411 + + +2 +3 +238736 + + +3 +5 +102979 + + +5 +19 +101681 + + +19 +6013 +21015 + + + + + + +type_id +index + + +12 + + +1 +2 +1169961 + + +2 +3 +130497 + + +3 +33 +32366 + + + + + + + + +overrides +81081 + + +new +78180 + + +old +25208 + + + + +new +old + + +12 + + +1 +2 +75401 + + +2 +14 +2779 + + + + + + +old +new + + +12 + + +1 +2 +16878 + + +2 +3 +3636 + + +3 +4 +1571 + + +4 +8 +1954 + + +8 +1923 +1169 + + + + + + + + +membervariables +352723 + + +id +352723 + + +type_id +165704 + + +name +53415 + + + + +id +type_id + + +12 + + +1 +2 +352723 + + + + + + +id +name + + +12 + + +1 +2 +352723 + + + + + + +type_id +id + + +12 + + +1 +2 +138808 + + +2 +3 +13448 + + +3 +23 +12471 + + +23 +2170 +976 + + + + + + +type_id +name + + +12 + + +1 +2 +148206 + + +2 +4 +13636 + + +4 +322 +3861 + + + + + + +name +id + + +12 + + +1 +2 +27528 + + +2 +3 +8710 + + +3 +4 +5137 + + +4 +6 +3794 + + +6 +13 +4294 + + +13 +2076 +3950 + + + + + + +name +type_id + + +12 + + +1 +2 +34485 + + +2 +3 +6857 + + +3 +4 +3095 + + +4 +7 +4704 + + +7 +128 +4016 + + +130 +899 +255 + + + + + + + + +globalvariables +336079 + + +id +336079 + + +type_id +1692 + + +name +329434 + + + + +id +type_id + + +12 + + +1 +2 +336079 + + + + + + +id +name + + +12 + + +1 +2 +336079 + + + + + + +type_id +id + + +12 + + +1 +2 +1176 + + +2 +3 +172 + + +3 +6 +127 + + +6 +34 +128 + + +34 +143737 +87 + + + + + + +type_id +name + + +12 + + +1 +2 +1226 + + +2 +3 +138 + + +3 +7 +142 + + +7 +83 +127 + + +89 +142875 +57 + + + + + + +name +id + + +12 + + +1 +2 +325314 + + +2 +30 +4119 + + + + + + +name +type_id + + +12 + + +1 +2 +328733 + + +2 +12 +701 + + + + + + + + +localvariables +664877 + + +id +664877 + + +type_id +39010 + + +name +56256 + + + + +id +type_id + + +12 + + +1 +2 +664877 + + + + + + +id +name + + +12 + + +1 +2 +664877 + + + + + + +type_id +id + + +12 + + +1 +2 +15185 + + +2 +3 +9792 + + +3 +4 +2044 + + +4 +5 +4407 + + +5 +9 +3004 + + +9 +28 +2960 + + +28 +57646 +1615 + + + + + + +type_id +name + + +12 + + +1 +2 +29435 + + +2 +3 +5175 + + +3 +7 +3059 + + +7 +4629 +1339 + + + + + + +name +id + + +12 + + +1 +2 +33617 + + +2 +3 +9002 + + +3 +4 +3450 + + +4 +7 +4308 + + +7 +33 +4251 + + +33 +14411 +1626 + + + + + + +name +type_id + + +12 + + +1 +2 +45228 + + +2 +3 +5841 + + +3 +11 +4308 + + +11 +3978 +877 + + + + + + + + +autoderivation +4993 + + +var +4993 + + +derivation_type +55 + + + + +var +derivation_type + + +12 + + +1 +2 +4993 + + + + + + +derivation_type +var + + +12 + + +8 +9 +11 + + +30 +31 +11 + + +74 +75 +11 + + +125 +126 +11 + + +213 +214 +11 + + + + + + + + +enumconstants +97072 + + +id +97072 + + +parent +13127 + + +index +6695 + + +type_id +12441 + + +name +62521 + + +location +76630 + + + + +id +parent + + +12 + + +1 +2 +97072 + + + + + + +id +index + + +12 + + +1 +2 +97072 + + + + + + +id +type_id + + +12 + + +1 +2 +97072 + + + + + + +id +name + + +12 + + +1 +2 +97072 + + + + + + +id +location + + +12 + + +1 +2 +97072 + + + + + + +parent +id + + +12 + + +1 +2 +4980 + + +2 +3 +1603 + + +3 +4 +1643 + + +4 +5 +1204 + + +5 +6 +713 + + +6 +8 +891 + + +8 +13 +1058 + + +13 +135 +985 + + +136 +6696 +50 + + + + + + +parent +index + + +12 + + +1 +2 +5033 + + +2 +3 +1751 + + +3 +4 +1875 + + +4 +5 +1173 + + +5 +6 +802 + + +6 +9 +1019 + + +9 +20 +994 + + +20 +6696 +480 + + + + + + +parent +type_id + + +12 + + +1 +2 +12519 + + +2 +3 +608 + + + + + + +parent +name + + +12 + + +1 +2 +5032 + + +2 +3 +1752 + + +3 +4 +1875 + + +4 +5 +1172 + + +5 +6 +802 + + +6 +9 +1018 + + +9 +20 +996 + + +20 +6696 +480 + + + + + + +parent +location + + +12 + + +1 +2 +5002 + + +2 +3 +1616 + + +3 +4 +1627 + + +4 +5 +1207 + + +5 +6 +714 + + +6 +8 +892 + + +8 +13 +1056 + + +13 +189 +985 + + +193 +2234 +28 + + + + + + +index +id + + +12 + + +1 +2 +5530 + + +2 +8 +470 + + +9 +31 +510 + + +32 +14225 +185 + + + + + + +index +parent + + +12 + + +1 +2 +5530 + + +2 +7 +470 + + +7 +26 +510 + + +27 +13128 +185 + + + + + + +index +type_id + + +12 + + +1 +2 +5530 + + +2 +7 +470 + + +7 +24 +510 + + +25 +12442 +185 + + + + + + +index +name + + +12 + + +1 +2 +5530 + + +2 +7 +470 + + +7 +24 +510 + + +25 +6691 +185 + + + + + + +index +location + + +12 + + +1 +2 +5530 + + +2 +8 +470 + + +9 +30 +510 + + +31 +11208 +185 + + + + + + +type_id +id + + +12 + + +1 +2 +4957 + + +2 +3 +1523 + + +3 +4 +1579 + + +4 +5 +1114 + + +5 +6 +671 + + +6 +9 +1131 + + +9 +19 +943 + + +19 +16086 +523 + + + + + + +type_id +parent + + +12 + + +1 +2 +12440 + + +1295 +1296 +1 + + + + + + +type_id +index + + +12 + + +1 +2 +5002 + + +2 +3 +1647 + + +3 +4 +1754 + + +4 +5 +1070 + + +5 +7 +1114 + + +7 +12 +984 + + +12 +6696 +870 + + + + + + +type_id +name + + +12 + + +1 +2 +5001 + + +2 +3 +1648 + + +3 +4 +1754 + + +4 +5 +1069 + + +5 +7 +1114 + + +7 +12 +985 + + +12 +12517 +870 + + + + + + +type_id +location + + +12 + + +1 +2 +4979 + + +2 +3 +1534 + + +3 +4 +1563 + + +4 +5 +1117 + + +5 +6 +672 + + +6 +9 +1130 + + +9 +19 +941 + + +19 +15911 +505 + + + + + + +name +id + + +12 + + +1 +2 +45879 + + +2 +3 +13216 + + +3 +1343 +3426 + + + + + + +name +parent + + +12 + + +1 +2 +55370 + + +2 +3 +4738 + + +3 +1343 +2413 + + + + + + +name +index + + +12 + + +1 +2 +60721 + + +2 +19 +1800 + + + + + + +name +type_id + + +12 + + +1 +2 +49401 + + +2 +3 +12189 + + +3 +1343 +931 + + + + + + +name +location + + +12 + + +1 +2 +46883 + + +2 +3 +14895 + + +3 +1343 +743 + + + + + + +location +id + + +12 + + +1 +2 +70131 + + +2 +4 +6328 + + +4 +504 +171 + + + + + + +location +parent + + +12 + + +1 +2 +72451 + + +2 +504 +4179 + + + + + + +location +index + + +12 + + +1 +2 +73906 + + +2 +347 +2724 + + + + + + +location +type_id + + +12 + + +1 +2 +66489 + + +2 +3 +9907 + + +3 +504 +234 + + + + + + +location +name + + +12 + + +1 +2 +74059 + + +2 +347 +2571 + + + + + + + + +builtintypes +510 + + +id +510 + + +name +510 + + +kind +510 + + +size +77 + + +sign +33 + + +alignment +66 + + + + +id +name + + +12 + + +1 +2 +510 + + + + + + +id +kind + + +12 + + +1 +2 +510 + + + + + + +id +size + + +12 + + +1 +2 +510 + + + + + + +id +sign + + +12 + + +1 +2 +510 + + + + + + +id +alignment + + +12 + + +1 +2 +510 + + + + + + +name +id + + +12 + + +1 +2 +510 + + + + + + +name +kind + + +12 + + +1 +2 +510 + + + + + + +name +size + + +12 + + +1 +2 +510 + + + + + + +name +sign + + +12 + + +1 +2 +510 + + + + + + +name +alignment + + +12 + + +1 +2 +510 + + + + + + +kind +id + + +12 + + +1 +2 +510 + + + + + + +kind +name + + +12 + + +1 +2 +510 + + + + + + +kind +size + + +12 + + +1 +2 +510 + + + + + + +kind +sign + + +12 + + +1 +2 +510 + + + + + + +kind +alignment + + +12 + + +1 +2 +510 + + + + + + +size +id + + +12 + + +2 +3 +11 + + +3 +4 +11 + + +4 +5 +22 + + +9 +10 +11 + + +11 +12 +11 + + +13 +14 +11 + + + + + + +size +name + + +12 + + +2 +3 +11 + + +3 +4 +11 + + +4 +5 +22 + + +9 +10 +11 + + +11 +12 +11 + + +13 +14 +11 + + + + + + +size +kind + + +12 + + +2 +3 +11 + + +3 +4 +11 + + +4 +5 +22 + + +9 +10 +11 + + +11 +12 +11 + + +13 +14 +11 + + + + + + +size +sign + + +12 + + +1 +2 +22 + + +3 +4 +55 + + + + + + +size +alignment + + +12 + + +1 +2 +44 + + +2 +3 +33 + + + + + + +sign +id + + +12 + + +6 +7 +11 + + +12 +13 +11 + + +28 +29 +11 + + + + + + +sign +name + + +12 + + +6 +7 +11 + + +12 +13 +11 + + +28 +29 +11 + + + + + + +sign +kind + + +12 + + +6 +7 +11 + + +12 +13 +11 + + +28 +29 +11 + + + + + + +sign +size + + +12 + + +5 +6 +22 + + +7 +8 +11 + + + + + + +sign +alignment + + +12 + + +5 +6 +22 + + +6 +7 +11 + + + + + + +alignment +id + + +12 + + +2 +3 +11 + + +4 +5 +11 + + +5 +6 +11 + + +10 +11 +11 + + +12 +13 +11 + + +13 +14 +11 + + + + + + +alignment +name + + +12 + + +2 +3 +11 + + +4 +5 +11 + + +5 +6 +11 + + +10 +11 +11 + + +12 +13 +11 + + +13 +14 +11 + + + + + + +alignment +kind + + +12 + + +2 +3 +11 + + +4 +5 +11 + + +5 +6 +11 + + +10 +11 +11 + + +12 +13 +11 + + +13 +14 +11 + + + + + + +alignment +size + + +12 + + +1 +2 +22 + + +2 +3 +44 + + + + + + +alignment +sign + + +12 + + +1 +2 +11 + + +3 +4 +55 + + + + + + + + +derivedtypes +3614061 + + +id +3614061 + + +name +1415033 + + +kind +88 + + +type_id +2300088 + + + + +id +name + + +12 + + +1 +2 +3614061 + + + + + + +id +kind + + +12 + + +1 +2 +3614061 + + + + + + +id +type_id + + +12 + + +1 +2 +3614061 + + + + + + +name +id + + +12 + + +1 +2 +985460 + + +2 +3 +348584 + + +3 +59432 +80988 + + + + + + +name +kind + + +12 + + +1 +2 +1415011 + + +2 +3 +22 + + + + + + +name +type_id + + +12 + + +1 +2 +985460 + + +2 +3 +348584 + + +3 +59414 +80988 + + + + + + +kind +id + + +12 + + +23 +24 +11 + + +37 +38 +11 + + +2154 +2155 +11 + + +30292 +30293 +11 + + +33879 +33880 +11 + + +59477 +59478 +11 + + +87607 +87608 +11 + + +112218 +112219 +11 + + + + + + +kind +name + + +12 + + +1 +2 +11 + + +13 +14 +11 + + +37 +38 +11 + + +1234 +1235 +11 + + +17157 +17158 +11 + + +20434 +20435 +11 + + +36775 +36776 +11 + + +51880 +51881 +11 + + + + + + +kind +type_id + + +12 + + +11 +12 +11 + + +23 +24 +11 + + +1015 +1016 +11 + + +30292 +30293 +11 + + +33879 +33880 +11 + + +59477 +59478 +11 + + +87290 +87291 +11 + + +112218 +112219 +11 + + + + + + +type_id +id + + +12 + + +1 +2 +1453901 + + +2 +3 +477318 + + +3 +4 +281411 + + +4 +210 +87456 + + + + + + +type_id +name + + +12 + + +1 +2 +1455078 + + +2 +3 +477173 + + +3 +4 +280334 + + +4 +210 +87501 + + + + + + +type_id +kind + + +12 + + +1 +2 +1455632 + + +2 +3 +478505 + + +3 +4 +279957 + + +4 +7 +85992 + + + + + + + + +pointerishsize +2617371 + + +id +2617371 + + +size +22 + + +alignment +11 + + + + +id +size + + +12 + + +1 +2 +2617371 + + + + + + +id +alignment + + +12 + + +1 +2 +2617371 + + + + + + +size +id + + +12 + + +23 +24 +11 + + +235866 +235867 +11 + + + + + + +size +alignment + + +12 + + +1 +2 +22 + + + + + + +alignment +id + + +12 + + +235889 +235890 +11 + + + + + + +alignment +size + + +12 + + +2 +3 +11 + + + + + + + + +arraysizes +18574 + + +id +18574 + + +num_elements +2352 + + +bytesize +2785 + + +alignment +77 + + + + +id +num_elements + + +12 + + +1 +2 +18574 + + + + + + +id +bytesize + + +12 + + +1 +2 +18574 + + + + + + +id +alignment + + +12 + + +1 +2 +18574 + + + + + + +num_elements +id + + +12 + + +1 +2 +255 + + +2 +3 +1331 + + +3 +4 +55 + + +4 +5 +166 + + +5 +9 +199 + + +9 +25 +177 + + +25 +129 +166 + + + + + + +num_elements +bytesize + + +12 + + +1 +2 +1742 + + +2 +3 +210 + + +3 +4 +122 + + +4 +6 +210 + + +6 +17 +66 + + + + + + +num_elements +alignment + + +12 + + +1 +2 +1753 + + +2 +3 +210 + + +3 +4 +199 + + +4 +8 +188 + + + + + + +bytesize +id + + +12 + + +1 +2 +244 + + +2 +3 +1542 + + +3 +4 +99 + + +4 +6 +244 + + +6 +9 +233 + + +9 +26 +210 + + +28 +81 +210 + + + + + + +bytesize +num_elements + + +12 + + +1 +2 +2163 + + +2 +3 +343 + + +3 +6 +233 + + +6 +8 +44 + + + + + + +bytesize +alignment + + +12 + + +1 +2 +2219 + + +2 +3 +277 + + +3 +5 +244 + + +5 +7 +44 + + + + + + +alignment +id + + +12 + + +11 +12 +11 + + +12 +13 +11 + + +42 +43 +11 + + +43 +44 +11 + + +212 +213 +11 + + +468 +469 +11 + + +886 +887 +11 + + + + + + +alignment +num_elements + + +12 + + +4 +5 +11 + + +5 +6 +11 + + +15 +16 +11 + + +17 +18 +11 + + +40 +41 +11 + + +44 +45 +11 + + +204 +205 +11 + + + + + + +alignment +bytesize + + +12 + + +1 +2 +11 + + +2 +3 +11 + + +16 +17 +11 + + +18 +19 +11 + + +43 +44 +11 + + +59 +60 +11 + + +205 +206 +11 + + + + + + + + +typedefbase +1603728 + + +id +1603728 + + +type_id +761891 + + + + +id +type_id + + +12 + + +1 +2 +1603728 + + + + + + +type_id +id + + +12 + + +1 +2 +588642 + + +2 +3 +75994 + + +3 +6 +65609 + + +6 +4247 +31645 + + + + + + + + +decltypes +79334 + + +id +79334 + + +expr +74896 + + +base_type +8055 + + +parentheses_would_change_meaning +22 + + + + +id +expr + + +12 + + +1 +2 +79334 + + + + + + +id +base_type + + +12 + + +1 +2 +79334 + + + + + + +id +parentheses_would_change_meaning + + +12 + + +1 +2 +79334 + + + + + + +expr +id + + +12 + + +1 +2 +70480 + + +2 +4 +4416 + + + + + + +expr +base_type + + +12 + + +1 +2 +70480 + + +2 +4 +4416 + + + + + + +expr +parentheses_would_change_meaning + + +12 + + +1 +2 +74896 + + + + + + +base_type +id + + +12 + + +1 +2 +4926 + + +2 +3 +2785 + + +3 +247 +343 + + + + + + +base_type +expr + + +12 + + +1 +2 +2363 + + +2 +3 +3561 + + +3 +4 +277 + + +4 +5 +965 + + +5 +15 +676 + + +15 +2687 +210 + + + + + + +base_type +parentheses_would_change_meaning + + +12 + + +1 +2 +8055 + + + + + + +parentheses_would_change_meaning +id + + +12 + + +6 +7 +11 + + +1304 +1305 +11 + + + + + + +parentheses_would_change_meaning +expr + + +12 + + +9 +10 +11 + + +6741 +6742 +11 + + + + + + +parentheses_would_change_meaning +base_type + + +12 + + +6 +7 +11 + + +720 +721 +11 + + + + + + + + +usertypes +3394431 + + +id +3394431 + + +name +608880 + + +kind +110 + + + + +id +name + + +12 + + +1 +2 +3394431 + + + + + + +id +kind + + +12 + + +1 +2 +3394431 + + + + + + +name +id + + +12 + + +1 +2 +414072 + + +2 +3 +119224 + + +3 +7 +46546 + + +7 +27586 +29037 + + + + + + +name +kind + + +12 + + +1 +2 +563732 + + +2 +9 +45148 + + + + + + +kind +id + + +12 + + +23 +24 +11 + + +161 +162 +11 + + +608 +609 +11 + + +805 +806 +11 + + +1211 +1212 +11 + + +15458 +15459 +11 + + +17960 +17961 +11 + + +50501 +50502 +11 + + +74656 +74657 +11 + + +144535 +144536 +11 + + + + + + +kind +name + + +12 + + +15 +16 +11 + + +18 +19 +11 + + +37 +38 +11 + + +83 +84 +11 + + +398 +399 +11 + + +2650 +2651 +11 + + +4695 +4696 +11 + + +10425 +10426 +11 + + +10826 +10827 +11 + + +30022 +30023 +11 + + + + + + + + +usertypesize +876666 + + +id +876666 + + +size +1564 + + +alignment +88 + + + + +id +size + + +12 + + +1 +2 +876666 + + + + + + +id +alignment + + +12 + + +1 +2 +876666 + + + + + + +size +id + + +12 + + +1 +2 +477 + + +2 +3 +210 + + +3 +5 +144 + + +5 +8 +133 + + +8 +12 +122 + + +12 +21 +144 + + +21 +63 +122 + + +72 +378 +122 + + +549 +53250 +88 + + + + + + +size +alignment + + +12 + + +1 +2 +1253 + + +2 +3 +221 + + +3 +6 +88 + + + + + + +alignment +id + + +12 + + +1 +2 +11 + + +3 +4 +11 + + +27 +28 +11 + + +53 +54 +11 + + +59 +60 +11 + + +381 +382 +11 + + +10892 +10893 +11 + + +67593 +67594 +11 + + + + + + +alignment +size + + +12 + + +1 +2 +22 + + +8 +9 +22 + + +12 +13 +11 + + +19 +20 +11 + + +26 +27 +11 + + +109 +110 +11 + + + + + + + + +usertype_final +4311 + + +id +4311 + + + + + +usertype_uuid +3076 + + +id +3076 + + +uuid +3076 + + + + +id +uuid + + +12 + + +1 +2 +3076 + + + + + + +uuid +id + + +12 + + +1 +2 +3076 + + + + + + + + +is_pod_class +588875 + + +id +588875 + + + + + +is_complete +876666 + + +id +876666 + + + + + +is_class_template +171518 + + +id +171518 + + + + + +class_instantiation +680992 + + +to +680426 + + +from +57409 + + + + +to +from + + +12 + + +1 +2 +679904 + + +2 +4 +521 + + + + + + +from +to + + +12 + + +1 +2 +18208 + + +2 +3 +10330 + + +3 +4 +6246 + + +4 +5 +3994 + + +5 +7 +4948 + + +7 +11 +5237 + + +11 +24 +4349 + + +24 +2199 +4094 + + + + + + + + +class_template_argument +1723884 + + +type_id +842335 + + +index +443 + + +arg_type +716720 + + + + +type_id +index + + +12 + + +1 +2 +379886 + + +2 +3 +291519 + + +3 +4 +105687 + + +4 +21 +64433 + + +21 +41 +809 + + + + + + +type_id +arg_type + + +12 + + +1 +2 +395575 + + +2 +3 +291552 + + +3 +4 +103412 + + +4 +41 +51795 + + + + + + +index +type_id + + +12 + + +1 +6 +33 + + +7 +12 +33 + + +15 +24 +33 + + +27 +36 +33 + + +39 +48 +33 + + +51 +62 +33 + + +67 +230 +33 + + +245 +299 +33 + + +317 +386 +33 + + +511 +678 +33 + + +993 +1396 +33 + + +1719 +4462 +33 + + +6599 +41796 +33 + + +72036 +72037 +11 + + + + + + +index +arg_type + + +12 + + +1 +4 +33 + + +4 +7 +33 + + +9 +11 +22 + + +11 +13 +33 + + +13 +16 +33 + + +16 +21 +33 + + +21 +28 +33 + + +41 +63 +33 + + +77 +106 +33 + + +117 +187 +33 + + +226 +454 +33 + + +573 +1114 +33 + + +1966 +7930 +33 + + +20642 +33985 +22 + + + + + + +arg_type +type_id + + +12 + + +1 +2 +474877 + + +2 +3 +151179 + + +3 +6 +59062 + + +6 +2551 +31600 + + + + + + +arg_type +index + + +12 + + +1 +2 +646107 + + +2 +3 +61548 + + +3 +22 +9065 + + + + + + + + +is_proxy_class_for +6746 + + +id +6746 + + +templ_param_id +6746 + + + + +id +templ_param_id + + +12 + + +1 +2 +6746 + + + + + + +templ_param_id +id + + +12 + + +1 +2 +6746 + + + + + + + + +type_mentions +7771692 + + +id +7771692 + + +type_id +90190 + + +location +1895039 + + +kind +2 + + + + +id +type_id + + +12 + + +1 +2 +7771692 + + + + + + +id +location + + +12 + + +1 +2 +7771692 + + + + + + +id +kind + + +12 + + +1 +2 +7771692 + + + + + + +type_id +id + + +12 + + +1 +2 +18983 + + +2 +3 +10246 + + +3 +4 +6520 + + +4 +5 +5136 + + +5 +7 +7307 + + +7 +11 +8149 + + +11 +17 +7152 + + +17 +27 +6850 + + +27 +49 +6932 + + +49 +124 +6794 + + +124 +275000 +6121 + + + + + + +type_id +location + + +12 + + +1 +2 +30113 + + +2 +3 +14640 + + +3 +4 +7010 + + +4 +6 +8217 + + +6 +8 +6533 + + +8 +13 +7435 + + +13 +24 +6942 + + +24 +85 +6792 + + +85 +60827 +2508 + + + + + + +type_id +kind + + +12 + + +1 +2 +88534 + + +2 +3 +1656 + + + + + + +location +id + + +12 + + +1 +2 +1273524 + + +2 +3 +176920 + + +3 +5 +161931 + + +5 +11 +143004 + + +11 +339 +139660 + + + + + + +location +type_id + + +12 + + +1 +2 +1800160 + + +2 +14 +94879 + + + + + + +location +kind + + +12 + + +1 +2 +1895037 + + +2 +3 +2 + + + + + + +kind +id + + +12 + + +33594 +33595 +1 + + +7673652 +7673653 +1 + + + + + + +kind +type_id + + +12 + + +1842 +1843 +1 + + +90004 +90005 +1 + + + + + + +kind +location + + +12 + + +6460 +6461 +1 + + +1888581 +1888582 +1 + + + + + + + + +is_function_template +945393 + + +id +945393 + + + + + +function_instantiation +1762697 + + +to +1762697 + + +from +229249 + + + + +to +from + + +12 + + +1 +2 +1762697 + + + + + + +from +to + + +12 + + +1 +2 +100916 + + +2 +3 +54679 + + +3 +4 +13070 + + +4 +6 +17964 + + +6 +11 +18430 + + +11 +44 +17287 + + +44 +962 +6901 + + + + + + + + +function_template_argument +1881078 + + +function_id +1082559 + + +index +221 + + +arg_type +348030 + + + + +function_id +index + + +12 + + +1 +2 +615671 + + +2 +3 +345677 + + +3 +5 +91739 + + +5 +21 +29470 + + + + + + +function_id +arg_type + + +12 + + +1 +2 +628242 + + +2 +3 +323586 + + +3 +4 +74552 + + +4 +21 +56177 + + + + + + +index +function_id + + +12 + + +4 +5 +11 + + +7 +8 +11 + + +17 +18 +11 + + +38 +39 +11 + + +64 +65 +11 + + +153 +154 +11 + + +241 +242 +11 + + +330 +331 +11 + + +427 +428 +11 + + +530 +531 +11 + + +767 +768 +11 + + +1024 +1025 +11 + + +1281 +1282 +11 + + +1543 +1544 +11 + + +2530 +2531 +11 + + +2886 +2887 +11 + + +4855 +4856 +11 + + +15383 +15384 +11 + + +43437 +43438 +11 + + +90216 +90217 +11 + + + + + + +index +arg_type + + +12 + + +4 +5 +11 + + +7 +8 +11 + + +14 +15 +11 + + +22 +23 +11 + + +32 +33 +11 + + +55 +56 +11 + + +58 +59 +11 + + +62 +63 +11 + + +74 +75 +11 + + +92 +93 +11 + + +136 +137 +11 + + +223 +224 +11 + + +239 +240 +11 + + +327 +328 +11 + + +528 +529 +11 + + +695 +696 +11 + + +1442 +1443 +11 + + +4101 +4102 +11 + + +9159 +9160 +11 + + +18077 +18078 +11 + + + + + + +arg_type +function_id + + +12 + + +1 +2 +233044 + + +2 +3 +42186 + + +3 +6 +27883 + + +6 +15 +26518 + + +15 +928 +18396 + + + + + + +arg_type +index + + +12 + + +1 +2 +318016 + + +2 +4 +28638 + + +4 +16 +1375 + + + + + + + + +is_variable_template +25897 + + +id +25897 + + + + + +variable_instantiation +25298 + + +to +25298 + + +from +5514 + + + + +to +from + + +12 + + +1 +2 +25298 + + + + + + +from +to + + +12 + + +1 +2 +1797 + + +2 +3 +1963 + + +3 +4 +510 + + +4 +7 +454 + + +7 +14 +466 + + +14 +207 +321 + + + + + + + + +variable_template_argument +28 + + +variable_id +22 + + +index +2 + + +arg_type +17 + + + + +variable_id +index + + +12 + + +1 +2 +16 + + +2 +3 +6 + + + + + + +variable_id +arg_type + + +12 + + +1 +2 +16 + + +2 +3 +6 + + + + + + +index +variable_id + + +12 + + +6 +7 +1 + + +22 +23 +1 + + + + + + +index +arg_type + + +12 + + +5 +6 +1 + + +13 +14 +1 + + + + + + +arg_type +variable_id + + +12 + + +1 +2 +11 + + +2 +3 +3 + + +3 +4 +1 + + +4 +5 +2 + + + + + + +arg_type +index + + +12 + + +1 +2 +16 + + +2 +3 +1 + + + + + + + + +routinetypes +532763 + + +id +532763 + + +return_type +257521 + + + + +id +return_type + + +12 + + +1 +2 +532763 + + + + + + +return_type +id + + +12 + + +1 +2 +214925 + + +2 +3 +25409 + + +3 +9092 +17187 + + + + + + + + +routinetypeargs +844211 + + +routine +406449 + + +index +355 + + +type_id +261982 + + + + +routine +index + + +12 + + +1 +2 +191901 + + +2 +3 +108161 + + +3 +4 +62258 + + +4 +6 +33686 + + +6 +33 +10441 + + + + + + +routine +type_id + + +12 + + +1 +2 +219108 + + +2 +3 +109670 + + +3 +4 +52383 + + +4 +27 +25287 + + + + + + +index +routine + + +12 + + +1 +2 +66 + + +6 +17 +22 + + +26 +37 +22 + + +46 +58 +22 + + +71 +86 +22 + + +100 +115 +22 + + +137 +170 +22 + + +202 +235 +22 + + +267 +305 +22 + + +376 +460 +22 + + +564 +691 +22 + + +941 +1647 +22 + + +3977 +9589 +22 + + +19336 +36632 +22 + + + + + + +index +type_id + + +12 + + +1 +2 +66 + + +2 +13 +22 + + +22 +33 +22 + + +42 +53 +22 + + +64 +77 +22 + + +89 +102 +22 + + +121 +146 +22 + + +158 +171 +22 + + +183 +197 +22 + + +245 +299 +22 + + +366 +444 +22 + + +561 +687 +22 + + +1297 +3121 +22 + + +7008 +15194 +22 + + + + + + +type_id +routine + + +12 + + +1 +2 +166447 + + +2 +3 +43140 + + +3 +4 +17664 + + +4 +7 +19861 + + +7 +1090 +14868 + + + + + + +type_id +index + + +12 + + +1 +2 +202253 + + +2 +3 +46668 + + +3 +33 +13059 + + + + + + + + +ptrtomembers +13636 + + +id +13636 + + +type_id +11772 + + +class_id +7556 + + + + +id +type_id + + +12 + + +1 +2 +13636 + + + + + + +id +class_id + + +12 + + +1 +2 +13636 + + + + + + +type_id +id + + +12 + + +1 +2 +11539 + + +2 +72 +233 + + + + + + +type_id +class_id + + +12 + + +1 +2 +11539 + + +2 +72 +233 + + + + + + +class_id +id + + +12 + + +1 +2 +6724 + + +2 +4 +332 + + +8 +65 +499 + + + + + + +class_id +type_id + + +12 + + +1 +2 +6724 + + +2 +4 +332 + + +8 +65 +499 + + + + + + + + +specifiers +466 + + +id +466 + + +str +466 + + + + +id +str + + +12 + + +1 +2 +466 + + + + + + +str +id + + +12 + + +1 +2 +466 + + + + + + + + +typespecifiers +1107391 + + +type_id +1102842 + + +spec_id +77 + + + + +type_id +spec_id + + +12 + + +1 +2 +1098293 + + +2 +3 +4549 + + + + + + +spec_id +type_id + + +12 + + +111 +112 +11 + + +214 +215 +11 + + +256 +257 +11 + + +754 +755 +11 + + +1731 +1732 +11 + + +9613 +9614 +11 + + +87124 +87125 +11 + + + + + + + + +funspecifiers +9771518 + + +func_id +3652064 + + +spec_id +155 + + + + +func_id +spec_id + + +12 + + +1 +2 +352756 + + +2 +3 +949876 + + +3 +4 +1882920 + + +4 +5 +462327 + + +5 +7 +4183 + + + + + + +spec_id +func_id + + +12 + + +2 +3 +11 + + +135 +136 +11 + + +652 +653 +11 + + +908 +909 +11 + + +1031 +1032 +11 + + +5939 +5940 +11 + + +11598 +11599 +11 + + +16932 +16933 +11 + + +18534 +18535 +11 + + +35244 +35245 +11 + + +60580 +60581 +11 + + +230299 +230300 +11 + + +239580 +239581 +11 + + +259218 +259219 +11 + + + + + + + + +varspecifiers +1125224 + + +var_id +1032943 + + +spec_id +19 + + + + +var_id +spec_id + + +12 + + +1 +2 +940661 + + +2 +3 +92281 + + + + + + +spec_id +var_id + + +12 + + +784 +785 +2 + + +3139 +3140 +2 + + +14786 +14787 +2 + + +18771 +18772 +2 + + +40646 +40647 +2 + + +93832 +93833 +2 + + +236997 +236998 +2 + + + + + + + + +attributes +425143 + + +id +425143 + + +kind +2 + + +name +38 + + +name_space +2 + + +location +415778 + + + + +id +kind + + +12 + + +1 +2 +425143 + + + + + + +id +name + + +12 + + +1 +2 +425143 + + + + + + +id +name_space + + +12 + + +1 +2 +425143 + + + + + + +id +location + + +12 + + +1 +2 +425143 + + + + + + +kind +id + + +12 + + +3 +4 +1 + + +324444 +324445 +1 + + + + + + +kind +name + + +12 + + +2 +3 +1 + + +29 +30 +1 + + + + + + +kind +name_space + + +12 + + +1 +2 +1 + + +2 +3 +1 + + + + + + +kind +location + + +12 + + +3 +4 +1 + + +317297 +317298 +1 + + + + + + +name +id + + +12 + + +1 +2 +2 + + +4 +6 +2 + + +7 +9 +2 + + +9 +11 +2 + + +12 +16 +2 + + +18 +20 +2 + + +28 +37 +2 + + +79 +88 +2 + + +129 +147 +2 + + +233 +269 +2 + + +450 +496 +2 + + +1162 +1173 +2 + + +1238 +1314 +2 + + +1369 +6317 +2 + + +309817 +309818 +1 + + + + + + +name +kind + + +12 + + +1 +2 +35 + + +2 +3 +2 + + + + + + +name +name_space + + +12 + + +1 +2 +36 + + +2 +3 +1 + + + + + + +name +location + + +12 + + +1 +2 +2 + + +4 +6 +2 + + +7 +9 +2 + + +9 +10 +2 + + +12 +16 +2 + + +18 +20 +2 + + +28 +37 +2 + + +78 +83 +2 + + +125 +128 +2 + + +160 +184 +2 + + +424 +465 +2 + + +707 +708 +1 + + +773 +774 +2 + + +777 +835 +2 + + +4273 +308275 +2 + + + + + + +name_space +id + + +12 + + +1 +2 +1 + + +324446 +324447 +1 + + + + + + +name_space +kind + + +12 + + +1 +2 +1 + + +2 +3 +1 + + + + + + +name_space +name + + +12 + + +1 +2 +1 + + +29 +30 +1 + + + + + + +name_space +location + + +12 + + +1 +2 +1 + + +317299 +317300 +1 + + + + + + +location +id + + +12 + + +1 +2 +408386 + + +2 +13 +7391 + + + + + + +location +kind + + +12 + + +1 +2 +415778 + + + + + + +location +name + + +12 + + +1 +2 +414667 + + +2 +4 +1111 + + + + + + +location +name_space + + +12 + + +1 +2 +415778 + + + + + + + + +attribute_args +136271 + + +id +136271 + + +kind +5 + + +attribute +134117 + + +index +19 + + +location +86164 + + + + +id +kind + + +12 + + +1 +2 +136271 + + + + + + +id +attribute + + +12 + + +1 +2 +136271 + + + + + + +id +index + + +12 + + +1 +2 +136271 + + + + + + +id +location + + +12 + + +1 +2 +136271 + + + + + + +kind +id + + +12 + + +414 +415 +2 + + +49098 +49099 +2 + + + + + + +kind +attribute + + +12 + + +330 +331 +2 + + +48712 +48713 +2 + + + + + + +kind +index + + +12 + + +3 +4 +2 + + +7 +8 +2 + + + + + + +kind +location + + +12 + + +211 +212 +2 + + +31108 +31109 +2 + + + + + + +attribute +id + + +12 + + +1 +2 +132994 + + +2 +8 +1122 + + + + + + +attribute +kind + + +12 + + +1 +2 +133297 + + +2 +3 +819 + + + + + + +attribute +index + + +12 + + +1 +2 +132994 + + +2 +8 +1122 + + + + + + +attribute +location + + +12 + + +1 +2 +133046 + + +2 +4 +1070 + + + + + + +index +id + + +12 + + +4 +5 +2 + + +16 +17 +8 + + +323 +324 +2 + + +408 +409 +2 + + +48729 +48730 +2 + + + + + + +index +kind + + +12 + + +1 +2 +11 + + +2 +3 +8 + + + + + + +index +attribute + + +12 + + +4 +5 +2 + + +16 +17 +8 + + +323 +324 +2 + + +408 +409 +2 + + +48744 +48745 +2 + + + + + + +index +location + + +12 + + +2 +3 +2 + + +8 +9 +8 + + +204 +205 +2 + + +289 +290 +2 + + +30845 +30846 +2 + + + + + + +location +id + + +12 + + +1 +2 +50742 + + +2 +3 +31325 + + +3 +25 +4096 + + + + + + +location +kind + + +12 + + +1 +2 +86156 + + +2 +3 +8 + + + + + + +location +attribute + + +12 + + +1 +2 +50717 + + +2 +3 +31374 + + +3 +25 +4072 + + + + + + +location +index + + +12 + + +1 +2 +86134 + + +3 +8 +30 + + + + + + + + +attribute_arg_value +136230 + + +arg +136230 + + +value +29825 + + + + +arg +value + + +12 + + +1 +2 +136230 + + + + + + +value +arg + + +12 + + +1 +2 +29242 + + +2 +10083 +583 + + + + + + + + +attribute_arg_type +99 + + +arg +99 + + +type_id +44 + + + + +arg +type_id + + +12 + + +1 +2 +99 + + + + + + +type_id +arg + + +12 + + +1 +2 +11 + + +2 +3 +22 + + +4 +5 +11 + + + + + + + + +attribute_arg_name +6 + + +arg +6 + + +name +5 + + + + +arg +name + + +12 + + +1 +2 +6 + + + + + + +name +arg + + +12 + + +1 +2 +4 + + +2 +3 +1 + + + + + + + + +typeattributes +8188 + + +type_id +7578 + + +spec_id +8188 + + + + +type_id +spec_id + + +12 + + +1 +2 +7312 + + +2 +22 +266 + + + + + + +spec_id +type_id + + +12 + + +1 +2 +8188 + + + + + + + + +funcattributes +258842 + + +func_id +135989 + + +spec_id +258842 + + + + +func_id +spec_id + + +12 + + +1 +2 +68305 + + +2 +3 +13880 + + +3 +4 +52926 + + +4 +7 +876 + + + + + + +spec_id +func_id + + +12 + + +1 +2 +258842 + + + + + + + + +varattributes +406582 + + +var_id +351026 + + +spec_id +406582 + + + + +var_id +spec_id + + +12 + + +1 +2 +295487 + + +2 +3 +55538 + + +14 +15 +1 + + + + + + +spec_id +var_id + + +12 + + +1 +2 +406582 + + + + + + + + +stmtattributes +6 + + +stmt_id +6 + + +spec_id +6 + + + + +stmt_id +spec_id + + +12 + + +1 +2 +6 + + + + + + +spec_id +stmt_id + + +12 + + +1 +2 +6 + + + + + + + + +unspecifiedtype +7569595 + + +type_id +7569595 + + +unspecified_type_id +3993891 + + + + +type_id +unspecified_type_id + + +12 + + +1 +2 +7569595 + + + + + + +unspecified_type_id +type_id + + +12 + + +1 +2 +2251444 + + +2 +3 +1376286 + + +3 +8 +309361 + + +8 +5869 +56799 + + + + + + + + +member +4607455 + + +parent +458333 + + +index +2640 + + +child +4564536 + + + + +parent +index + + +12 + + +1 +2 +47101 + + +2 +3 +76993 + + +3 +4 +58951 + + +4 +5 +39489 + + +5 +6 +29381 + + +6 +8 +41842 + + +8 +9 +20371 + + +9 +12 +35584 + + +12 +18 +37825 + + +18 +25 +35018 + + +25 +142 +35617 + + +142 +239 +155 + + + + + + +parent +child + + +12 + + +1 +2 +46580 + + +2 +3 +76971 + + +3 +4 +57842 + + +4 +5 +39678 + + +5 +6 +29204 + + +6 +8 +41609 + + +8 +9 +20183 + + +9 +12 +36249 + + +12 +18 +37736 + + +18 +25 +35173 + + +25 +110 +34541 + + +110 +303 +2563 + + + + + + +index +parent + + +12 + + +1 +2 +454 + + +2 +9 +210 + + +9 +11 +188 + + +11 +13 +210 + + +14 +178 +199 + + +186 +375 +199 + + +387 +492 +199 + + +492 +553 +199 + + +578 +924 +199 + + +928 +1716 +199 + + +2044 +6774 +199 + + +7261 +41308 +177 + + + + + + +index +child + + +12 + + +1 +2 +454 + + +2 +9 +144 + + +9 +11 +244 + + +11 +24 +210 + + +24 +171 +199 + + +185 +227 +199 + + +379 +499 +199 + + +502 +577 +199 + + +579 +717 +199 + + +949 +1613 +199 + + +1763 +6528 +199 + + +6950 +41816 +188 + + + + + + +child +parent + + +12 + + +1 +2 +4564536 + + + + + + +child +index + + +12 + + +1 +2 +4536708 + + +2 +14 +27828 + + + + + + + + +enclosingfunction +156494 + + +child +156494 + + +parent +86979 + + + + +child +parent + + +12 + + +1 +2 +156494 + + + + + + +parent +child + + +12 + + +1 +2 +42263 + + +2 +3 +31944 + + +3 +4 +6413 + + +4 +49 +6357 + + + + + + + + +derivations +220295 + + +derivation +220295 + + +sub +208101 + + +index +66 + + +super +157071 + + +location +80988 + + + + +derivation +sub + + +12 + + +1 +2 +220295 + + + + + + +derivation +index + + +12 + + +1 +2 +220295 + + + + + + +derivation +super + + +12 + + +1 +2 +220295 + + + + + + +derivation +location + + +12 + + +1 +2 +220295 + + + + + + +sub +derivation + + +12 + + +1 +2 +197615 + + +2 +7 +10485 + + + + + + +sub +index + + +12 + + +1 +2 +199113 + + +2 +7 +8987 + + + + + + +sub +super + + +12 + + +1 +2 +197615 + + +2 +7 +10485 + + + + + + +sub +location + + +12 + + +1 +2 +199147 + + +2 +7 +8954 + + + + + + +index +derivation + + +12 + + +1 +2 +11 + + +4 +5 +11 + + +35 +36 +11 + + +104 +105 +11 + + +812 +813 +11 + + +18898 +18899 +11 + + + + + + +index +sub + + +12 + + +1 +2 +11 + + +4 +5 +11 + + +35 +36 +11 + + +104 +105 +11 + + +810 +811 +11 + + +18755 +18756 +11 + + + + + + +index +super + + +12 + + +1 +2 +11 + + +3 +4 +11 + + +22 +23 +11 + + +84 +85 +11 + + +401 +402 +11 + + +13697 +13698 +11 + + + + + + +index +location + + +12 + + +1 +2 +11 + + +4 +5 +11 + + +18 +19 +11 + + +50 +51 +11 + + +248 +249 +11 + + +6984 +6985 +11 + + + + + + +super +derivation + + +12 + + +1 +2 +147518 + + +2 +454 +9553 + + + + + + +super +sub + + +12 + + +1 +2 +147518 + + +2 +454 +9553 + + + + + + +super +index + + +12 + + +1 +2 +156528 + + +2 +4 +543 + + + + + + +super +location + + +12 + + +1 +2 +151734 + + +2 +441 +5337 + + + + + + +location +derivation + + +12 + + +1 +2 +64277 + + +2 +3 +7178 + + +3 +8 +6135 + + +8 +430 +3395 + + + + + + +location +sub + + +12 + + +1 +2 +65087 + + +2 +3 +6479 + + +3 +9 +6402 + + +9 +422 +3018 + + + + + + +location +index + + +12 + + +1 +2 +80932 + + +2 +4 +55 + + + + + + +location +super + + +12 + + +1 +2 +66863 + + +2 +3 +6546 + + +3 +12 +6091 + + +12 +427 +1486 + + + + + + + + +derspecifiers +222803 + + +der_id +220284 + + +spec_id +44 + + + + +der_id +spec_id + + +12 + + +1 +2 +217765 + + +2 +3 +2518 + + + + + + +spec_id +der_id + + +12 + + +227 +228 +11 + + +238 +239 +11 + + +723 +724 +11 + + +18892 +18893 +11 + + + + + + + + +direct_base_offsets +147817 + + +der_id +147817 + + +offset +221 + + + + +der_id +offset + + +12 + + +1 +2 +147817 + + + + + + +offset +der_id + + +12 + + +1 +2 +66 + + +2 +3 +11 + + +4 +5 +22 + + +5 +6 +22 + + +6 +7 +11 + + +7 +8 +11 + + +8 +9 +11 + + +9 +10 +11 + + +16 +17 +11 + + +17 +18 +11 + + +79 +80 +11 + + +81 +82 +11 + + +13073 +13074 +11 + + + + + + + + +virtual_base_offsets +5470 + + +sub +2607 + + +super +499 + + +offset +244 + + + + +sub +super + + +12 + + +1 +2 +1852 + + +2 +3 +88 + + +3 +4 +221 + + +4 +6 +144 + + +6 +7 +110 + + +7 +11 +188 + + + + + + +sub +offset + + +12 + + +1 +2 +2052 + + +2 +3 +199 + + +3 +5 +221 + + +5 +8 +133 + + + + + + +super +sub + + +12 + + +1 +2 +88 + + +2 +3 +44 + + +3 +4 +55 + + +4 +5 +88 + + +5 +7 +33 + + +8 +13 +44 + + +13 +15 +44 + + +15 +23 +44 + + +24 +60 +44 + + +110 +111 +11 + + + + + + +super +offset + + +12 + + +1 +2 +288 + + +2 +3 +77 + + +4 +6 +33 + + +6 +8 +44 + + +8 +10 +44 + + +15 +16 +11 + + + + + + +offset +sub + + +12 + + +1 +2 +11 + + +2 +3 +33 + + +4 +5 +33 + + +5 +6 +33 + + +6 +7 +11 + + +7 +8 +22 + + +8 +11 +22 + + +18 +22 +22 + + +26 +31 +22 + + +36 +59 +22 + + +97 +98 +11 + + + + + + +offset +super + + +12 + + +1 +2 +77 + + +2 +3 +22 + + +3 +4 +55 + + +5 +7 +22 + + +7 +10 +22 + + +12 +14 +22 + + +21 +30 +22 + + + + + + + + +frienddecls +88113 + + +id +88113 + + +type_id +9165 + + +decl_id +11676 + + +location +4615 + + + + +id +type_id + + +12 + + +1 +2 +88113 + + + + + + +id +decl_id + + +12 + + +1 +2 +88113 + + + + + + +id +location + + +12 + + +1 +2 +88113 + + + + + + +type_id +id + + +12 + + +1 +2 +4993 + + +2 +3 +1713 + + +3 +4 +530 + + +4 +10 +721 + + +10 +58 +739 + + +58 +333 +469 + + + + + + +type_id +decl_id + + +12 + + +1 +2 +5120 + + +2 +3 +1671 + + +3 +5 +843 + + +5 +37 +719 + + +39 +118 +688 + + +118 +333 +124 + + + + + + +type_id +location + + +12 + + +1 +2 +8232 + + +2 +4 +691 + + +4 +31 +242 + + + + + + +decl_id +id + + +12 + + +1 +2 +9156 + + +2 +3 +900 + + +3 +14 +908 + + +14 +669 +712 + + + + + + +decl_id +type_id + + +12 + + +1 +2 +9561 + + +2 +4 +903 + + +4 +48 +876 + + +48 +669 +336 + + + + + + +decl_id +location + + +12 + + +1 +2 +10693 + + +2 +5 +896 + + +5 +115 +87 + + + + + + +location +id + + +12 + + +1 +2 +3240 + + +2 +3 +1314 + + +3 +82041 +61 + + + + + + +location +type_id + + +12 + + +1 +2 +4442 + + +2 +6627 +173 + + + + + + +location +decl_id + + +12 + + +1 +2 +3264 + + +2 +3 +1322 + + +3 +7681 +29 + + + + + + + + +comments +1490251 + + +id +1490251 + + +contents +757542 + + +location +1490251 + + + + +id +contents + + +12 + + +1 +2 +1490251 + + + + + + +id +location + + +12 + + +1 +2 +1490251 + + + + + + +contents +id + + +12 + + +1 +2 +649324 + + +2 +3 +67451 + + +3 +10112 +40765 + + + + + + +contents +location + + +12 + + +1 +2 +649324 + + +2 +3 +67451 + + +3 +10112 +40765 + + + + + + +location +id + + +12 + + +1 +2 +1490251 + + + + + + +location +contents + + +12 + + +1 +2 +1490251 + + + + + + + + +commentbinding +690900 + + +id +592680 + + +element +665458 + + + + +id +element + + +12 + + +1 +2 +527182 + + +2 +3 +51151 + + +3 +67 +14346 + + + + + + +element +id + + +12 + + +1 +2 +640015 + + +2 +3 +25442 + + + + + + + + +exprconv +8065841 + + +converted +8065488 + + +conversion +8065841 + + + + +converted +conversion + + +12 + + +1 +2 +8065136 + + +2 +3 +352 + + + + + + +conversion +converted + + +12 + + +1 +2 +8065841 + + + + + + + + +compgenerated +7351784 + + +id +7351784 + + + + + +namespaces +7878 + + +id +7878 + + +name +4449 + + + + +id +name + + +12 + + +1 +2 +7878 + + + + + + +name +id + + +12 + + +1 +2 +3816 + + +2 +3 +421 + + +3 +140 +210 + + + + + + + + +namespacembrs +1153750 + + +parentid +7400 + + +memberid +1153750 + + + + +parentid +memberid + + +12 + + +1 +2 +721 + + +2 +3 +765 + + +3 +4 +543 + + +4 +6 +665 + + +6 +10 +610 + + +10 +17 +632 + + +17 +26 +510 + + +26 +35 +565 + + +35 +53 +565 + + +53 +113 +576 + + +115 +261 +565 + + +261 +1041 +565 + + +1691 +24643 +110 + + + + + + +memberid +parentid + + +12 + + +1 +2 +1153750 + + + + + + + + +exprparents +16652035 + + +expr_id +16400802 + + +child_index +2214 + + +parent_id +11485996 + + + + +expr_id +child_index + + +12 + + +1 +2 +16235076 + + +2 +4 +165726 + + + + + + +expr_id +parent_id + + +12 + + +1 +2 +16341056 + + +2 +3 +59745 + + + + + + +child_index +expr_id + + +12 + + +1 +2 +214 + + +2 +3 +242 + + +3 +4 +566 + + +5 +6 +115 + + +6 +8 +184 + + +8 +9 +360 + + +9 +33 +200 + + +33 +113 +167 + + +115 +3470758 +162 + + + + + + +child_index +parent_id + + +12 + + +1 +2 +214 + + +2 +3 +242 + + +3 +4 +566 + + +5 +6 +115 + + +6 +8 +184 + + +8 +9 +360 + + +9 +33 +200 + + +33 +113 +167 + + +115 +3482076 +162 + + + + + + +parent_id +expr_id + + +12 + + +1 +2 +7456196 + + +2 +3 +3521605 + + +3 +403 +508195 + + + + + + +parent_id +child_index + + +12 + + +1 +2 +7422292 + + +2 +3 +3542901 + + +3 +805 +520802 + + + + + + + + +expr_isload +5379876 + + +expr_id +5379876 + + + + + +conversionkinds +5257931 + + +expr_id +5257931 + + +kind +7 + + + + +expr_id +kind + + +12 + + +1 +2 +5257931 + + + + + + +kind +expr_id + + +12 + + +1750 +1751 +1 + + +11952 +11953 +1 + + +12205 +12206 +1 + + +14480 +14481 +1 + + +40906 +40907 +1 + + +3931281 +3931282 +1 + + + + + + + + +iscall +2879054 + + +caller +2879054 + + +kind +33 + + + + +caller +kind + + +12 + + +1 +2 +2879054 + + + + + + +kind +caller + + +12 + + +1791 +1792 +11 + + +6435 +6436 +11 + + +251247 +251248 +11 + + + + + + + + +numtemplatearguments +171718 + + +expr_id +171718 + + +num +44 + + + + +expr_id +num + + +12 + + +1 +2 +171718 + + + + + + +num +expr_id + + +12 + + +3 +4 +11 + + +35 +36 +11 + + +1630 +1631 +11 + + +13808 +13809 +11 + + + + + + + + +specialnamequalifyingelements +11 + + +id +11 + + +name +11 + + + + +id +name + + +12 + + +1 +2 +11 + + + + + + +name +id + + +12 + + +1 +2 +11 + + + + + + + + +namequalifiers +942519 + + +id +942519 + + +qualifiableelement +942519 + + +qualifyingelement +78746 + + +location +198425 + + + + +id +qualifiableelement + + +12 + + +1 +2 +942519 + + + + + + +id +qualifyingelement + + +12 + + +1 +2 +942519 + + + + + + +id +location + + +12 + + +1 +2 +942519 + + + + + + +qualifiableelement +id + + +12 + + +1 +2 +942519 + + + + + + +qualifiableelement +qualifyingelement + + +12 + + +1 +2 +942519 + + + + + + +qualifiableelement +location + + +12 + + +1 +2 +942519 + + + + + + +qualifyingelement +id + + +12 + + +1 +2 +43517 + + +2 +3 +13270 + + +3 +4 +5126 + + +4 +6 +6701 + + +6 +13 +6091 + + +13 +20492 +4038 + + + + + + +qualifyingelement +qualifiableelement + + +12 + + +1 +2 +43517 + + +2 +3 +13270 + + +3 +4 +5126 + + +4 +6 +6701 + + +6 +13 +6091 + + +13 +20492 +4038 + + + + + + +qualifyingelement +location + + +12 + + +1 +2 +51273 + + +2 +3 +12371 + + +3 +5 +6380 + + +5 +10 +6091 + + +10 +3208 +2629 + + + + + + +location +id + + +12 + + +1 +2 +86402 + + +2 +3 +37126 + + +3 +4 +31445 + + +4 +6 +15622 + + +6 +11 +16288 + + +11 +1309 +11539 + + + + + + +location +qualifiableelement + + +12 + + +1 +2 +86402 + + +2 +3 +37126 + + +3 +4 +31445 + + +4 +6 +15622 + + +6 +11 +16288 + + +11 +1309 +11539 + + + + + + +location +qualifyingelement + + +12 + + +1 +2 +155762 + + +2 +3 +18496 + + +3 +4 +15866 + + +4 +312 +8299 + + + + + + + + +varbind +6694687 + + +expr +6694687 + + +var +1300121 + + + + +expr +var + + +12 + + +1 +2 +6694687 + + + + + + +var +expr + + +12 + + +1 +2 +421705 + + +2 +3 +255855 + + +3 +4 +162383 + + +4 +5 +98477 + + +5 +6 +124514 + + +6 +9 +115495 + + +9 +25 +98642 + + +25 +69786 +23046 + + + + + + + + +funbind +2933069 + + +expr +2863531 + + +fun +983030 + + + + +expr +fun + + +12 + + +1 +2 +2795525 + + +2 +4 +68006 + + + + + + +fun +expr + + +12 + + +1 +2 +637152 + + +2 +3 +162076 + + +3 +4 +57764 + + +4 +8 +75506 + + +8 +2144 +50530 + + + + + + + + +expr_allocator +34663 + + +expr +34663 + + +func +122 + + +form +11 + + + + +expr +func + + +12 + + +1 +2 +34663 + + + + + + +expr +form + + +12 + + +1 +2 +34663 + + + + + + +func +expr + + +12 + + +1 +2 +44 + + +3 +4 +11 + + +4 +5 +11 + + +9 +10 +22 + + +38 +39 +11 + + +1250 +1251 +11 + + +1807 +1808 +11 + + + + + + +func +form + + +12 + + +1 +2 +122 + + + + + + +form +expr + + +12 + + +3124 +3125 +11 + + + + + + +form +func + + +12 + + +11 +12 +11 + + + + + + + + +expr_deallocator +37814 + + +expr +37814 + + +func +122 + + +form +22 + + + + +expr +func + + +12 + + +1 +2 +37814 + + + + + + +expr +form + + +12 + + +1 +2 +37814 + + + + + + +func +expr + + +12 + + +1 +2 +33 + + +2 +3 +11 + + +3 +4 +11 + + +4 +5 +11 + + +9 +10 +22 + + +116 +117 +11 + + +1112 +1113 +11 + + +2150 +2151 +11 + + + + + + +func +form + + +12 + + +1 +2 +122 + + + + + + +form +expr + + +12 + + +1117 +1118 +11 + + +2291 +2292 +11 + + + + + + +form +func + + +12 + + +3 +4 +11 + + +8 +9 +11 + + + + + + + + +values +10189744 + + +id +10189744 + + +str +849227 + + +text +991801 + + + + +id +str + + +12 + + +1 +2 +10189744 + + + + + + +id +text + + +12 + + +1 +2 +10189744 + + + + + + +str +id + + +12 + + +1 +2 +713829 + + +2 +3 +73119 + + +3 +3378922 +62277 + + + + + + +str +text + + +12 + + +1 +2 +824811 + + +2 +23640 +24416 + + + + + + +text +id + + +12 + + +1 +2 +736526 + + +2 +3 +125247 + + +3 +6 +78409 + + +6 +1512711 +51617 + + + + + + +text +str + + +12 + + +1 +2 +963944 + + +2 +5856 +27857 + + + + + + + + +valuebind +11000680 + + +val +10183302 + + +expr +11000680 + + + + +val +expr + + +12 + + +1 +2 +9367015 + + +2 +3 +815204 + + +3 +6 +1082 + + + + + + +expr +val + + +12 + + +1 +2 +11000680 + + + + + + + + +fieldoffsets +281688 + + +id +281688 + + +byteoffset +3461 + + +bitoffset +77 + + + + +id +byteoffset + + +12 + + +1 +2 +281688 + + + + + + +id +bitoffset + + +12 + + +1 +2 +281688 + + + + + + +byteoffset +id + + +12 + + +1 +2 +1342 + + +2 +3 +466 + + +3 +4 +210 + + +4 +6 +244 + + +6 +9 +266 + + +9 +14 +266 + + +14 +34 +277 + + +36 +157 +266 + + +157 +14470 +122 + + + + + + +byteoffset +bitoffset + + +12 + + +1 +2 +3406 + + +2 +8 +55 + + + + + + +bitoffset +id + + +12 + + +1 +2 +22 + + +2 +3 +22 + + +3 +4 +11 + + +4 +5 +11 + + +25374 +25375 +11 + + + + + + +bitoffset +byteoffset + + +12 + + +1 +2 +22 + + +2 +3 +33 + + +3 +4 +11 + + +312 +313 +11 + + + + + + + + +bitfield +16189 + + +id +16189 + + +bits +104 + + +declared_bits +104 + + + + +id +bits + + +12 + + +1 +2 +16189 + + + + + + +id +declared_bits + + +12 + + +1 +2 +16189 + + + + + + +bits +id + + +12 + + +1 +2 +27 + + +2 +3 +11 + + +3 +4 +11 + + +4 +5 +8 + + +5 +6 +5 + + +6 +9 +8 + + +14 +25 +8 + + +29 +50 +8 + + +68 +118 +8 + + +132 +3562 +8 + + + + + + +bits +declared_bits + + +12 + + +1 +2 +104 + + + + + + +declared_bits +id + + +12 + + +1 +2 +27 + + +2 +3 +11 + + +3 +4 +11 + + +4 +5 +8 + + +5 +6 +5 + + +6 +9 +8 + + +14 +25 +8 + + +29 +50 +8 + + +68 +118 +8 + + +132 +3562 +8 + + + + + + +declared_bits +bits + + +12 + + +1 +2 +104 + + + + + + + + +initialisers +1576583 + + +init +1576583 + + +var +625276 + + +expr +1576583 + + +location +428767 + + + + +init +var + + +12 + + +1 +2 +1576583 + + + + + + +init +expr + + +12 + + +1 +2 +1576583 + + + + + + +init +location + + +12 + + +1 +2 +1576583 + + + + + + +var +init + + +12 + + +1 +2 +540943 + + +2 +6 +48511 + + +6 +7693 +35822 + + + + + + +var +expr + + +12 + + +1 +2 +540943 + + +2 +6 +48511 + + +6 +7693 +35822 + + + + + + +var +location + + +12 + + +1 +2 +624458 + + +2 +36 +818 + + + + + + +expr +init + + +12 + + +1 +2 +1576583 + + + + + + +expr +var + + +12 + + +1 +2 +1576583 + + + + + + +expr +location + + +12 + + +1 +2 +1576583 + + + + + + +location +init + + +12 + + +1 +2 +375378 + + +2 +6 +34656 + + +6 +545994 +18733 + + + + + + +location +var + + +12 + + +1 +2 +406693 + + +2 +59565 +22074 + + + + + + +location +expr + + +12 + + +1 +2 +375378 + + +2 +6 +34656 + + +6 +545994 +18733 + + + + + + + + +expr_ancestor +154467 + + +exp +154134 + + +ancestor +85940 + + + + +exp +ancestor + + +12 + + +1 +2 +153960 + + +2 +34 +174 + + + + + + +ancestor +exp + + +12 + + +1 +2 +52526 + + +2 +3 +19527 + + +3 +4 +5723 + + +4 +7 +6718 + + +7 +322 +1446 + + + + + + + + +exprs +22820542 + + +id +22820542 + + +kind +1065 + + +location +19069924 + + + + +id +kind + + +12 + + +1 +2 +22820542 + + + + + + +id +location + + +12 + + +1 +2 +22820542 + + + + + + +kind +id + + +12 + + +2 +15 +88 + + +22 +51 +77 + + +70 +122 +88 + + +129 +290 +88 + + +376 +642 +88 + + +658 +1453 +88 + + +1851 +2906 +88 + + +2995 +4796 +88 + + +4934 +6867 +88 + + +6874 +8573 +88 + + +9217 +24353 +88 + + +42229 +268985 +88 + + +524785 +524786 +11 + + + + + + +kind +location + + +12 + + +1 +12 +88 + + +14 +51 +88 + + +70 +122 +88 + + +129 +377 +88 + + +397 +659 +88 + + +663 +1453 +88 + + +1851 +2906 +88 + + +2972 +4066 +88 + + +4362 +6861 +88 + + +6874 +9218 +88 + + +10966 +29509 +88 + + +42203 +505591 +88 + + + + + + +location +id + + +12 + + +1 +2 +18631686 + + +2 +141281 +438238 + + + + + + +location +kind + + +12 + + +1 +2 +18973669 + + +2 +23 +96255 + + + + + + + + +expr_types +23060865 + + +id +22820542 + + +typeid +1681154 + + +value_category +33 + + + + +id +typeid + + +12 + + +1 +2 +22582115 + + +2 +4 +238426 + + + + + + +id +value_category + + +12 + + +1 +2 +22820542 + + + + + + +typeid +id + + +12 + + +1 +2 +681081 + + +2 +3 +311780 + + +3 +4 +129021 + + +4 +5 +113298 + + +5 +8 +149349 + + +8 +15 +135601 + + +15 +62 +126791 + + +62 +195406 +34230 + + + + + + +typeid +value_category + + +12 + + +1 +2 +1463200 + + +2 +3 +209577 + + +3 +4 +8377 + + + + + + +value_category +id + + +12 + + +6236 +6237 +11 + + +441248 +441249 +11 + + +1609203 +1609204 +11 + + + + + + +value_category +typeid + + +12 + + +1649 +1650 +11 + + +36307 +36308 +11 + + +133955 +133956 +11 + + + + + + + + +new_allocated_type +36793 + + +expr +36793 + + +type_id +22735 + + + + +expr +type_id + + +12 + + +1 +2 +36793 + + + + + + +type_id +expr + + +12 + + +1 +2 +15256 + + +2 +3 +5703 + + +3 +9 +1708 + + +13 +122 +66 + + + + + + + + +new_array_allocated_type +915 + + +expr +915 + + +type_id +248 + + + + +expr +type_id + + +12 + + +1 +2 +915 + + + + + + +type_id +expr + + +12 + + +1 +2 +167 + + +2 +3 +32 + + +3 +4 +12 + + +4 +7 +19 + + +7 +147 +18 + + + + + + + + +condition_decl_bind +12582 + + +expr +12582 + + +decl +12582 + + + + +expr +decl + + +12 + + +1 +2 +12582 + + + + + + +decl +expr + + +12 + + +1 +2 +12582 + + + + + + + + +typeid_bind +4482 + + +expr +4482 + + +type_id +2596 + + + + +expr +type_id + + +12 + + +1 +2 +4482 + + + + + + +type_id +expr + + +12 + + +1 +2 +1986 + + +2 +3 +366 + + +3 +7 +199 + + +7 +40 +44 + + + + + + + + +uuidof_bind +122 + + +expr +122 + + +type_id +62 + + + + +expr +type_id + + +12 + + +1 +2 +122 + + + + + + +type_id +expr + + +12 + + +1 +2 +38 + + +2 +3 +11 + + +3 +4 +7 + + +4 +9 +4 + + +10 +11 +2 + + + + + + + + +sizeof_bind +208210 + + +expr +208210 + + +type_id +4095 + + + + +expr +type_id + + +12 + + +1 +2 +208210 + + + + + + +type_id +expr + + +12 + + +1 +2 +1627 + + +2 +3 +757 + + +3 +4 +299 + + +4 +7 +361 + + +7 +34 +229 + + +34 +37 +312 + + +37 +77 +312 + + +77 +9386 +194 + + + + + + + + +code_block +15 + + +block +15 + + +routine +15 + + + + +block +routine + + +12 + + +1 +2 +15 + + + + + + +routine +block + + +12 + + +1 +2 +15 + + + + + + + + +lambdas +2392 + + +expr +2392 + + +default_capture +1 + + +has_explicit_return_type +1 + + + + +expr +default_capture + + +12 + + +1 +2 +2392 + + + + + + +expr +has_explicit_return_type + + +12 + + +1 +2 +2392 + + + + + + +default_capture +expr + + +12 + + +1826 +1827 +1 + + + + + + +default_capture +has_explicit_return_type + + +12 + + +1 +2 +1 + + + + + + +has_explicit_return_type +expr + + +12 + + +1826 +1827 +1 + + + + + + +has_explicit_return_type +default_capture + + +12 + + +1 +2 +1 + + + + + + + + +lambda_capture +266 + + +id +266 + + +lambda +155 + + +index +44 + + +captured_by_reference +22 + + +is_implicit +22 + + +location +133 + + + + +id +lambda + + +12 + + +1 +2 +266 + + + + + + +id +index + + +12 + + +1 +2 +266 + + + + + + +id +captured_by_reference + + +12 + + +1 +2 +266 + + + + + + +id +is_implicit + + +12 + + +1 +2 +266 + + + + + + +id +location + + +12 + + +1 +2 +266 + + + + + + +lambda +id + + +12 + + +1 +2 +66 + + +2 +3 +77 + + +4 +5 +11 + + + + + + +lambda +index + + +12 + + +1 +2 +66 + + +2 +3 +77 + + +4 +5 +11 + + + + + + +lambda +captured_by_reference + + +12 + + +1 +2 +144 + + +2 +3 +11 + + + + + + +lambda +is_implicit + + +12 + + +1 +2 +155 + + + + + + +lambda +location + + +12 + + +1 +2 +66 + + +2 +3 +88 + + + + + + +index +id + + +12 + + +1 +2 +22 + + +8 +9 +11 + + +14 +15 +11 + + + + + + +index +lambda + + +12 + + +1 +2 +22 + + +8 +9 +11 + + +14 +15 +11 + + + + + + +index +captured_by_reference + + +12 + + +1 +2 +33 + + +2 +3 +11 + + + + + + +index +is_implicit + + +12 + + +1 +2 +22 + + +2 +3 +22 + + + + + + +index +location + + +12 + + +1 +2 +22 + + +3 +4 +11 + + +9 +10 +11 + + + + + + +captured_by_reference +id + + +12 + + +1 +2 +11 + + +23 +24 +11 + + + + + + +captured_by_reference +lambda + + +12 + + +1 +2 +11 + + +14 +15 +11 + + + + + + +captured_by_reference +index + + +12 + + +1 +2 +11 + + +4 +5 +11 + + + + + + +captured_by_reference +is_implicit + + +12 + + +1 +2 +11 + + +2 +3 +11 + + + + + + +captured_by_reference +location + + +12 + + +1 +2 +11 + + +11 +12 +11 + + + + + + +is_implicit +id + + +12 + + +6 +7 +11 + + +18 +19 +11 + + + + + + +is_implicit +lambda + + +12 + + +5 +6 +11 + + +9 +10 +11 + + + + + + +is_implicit +index + + +12 + + +2 +3 +11 + + +4 +5 +11 + + + + + + +is_implicit +captured_by_reference + + +12 + + +1 +2 +11 + + +2 +3 +11 + + + + + + +is_implicit +location + + +12 + + +6 +7 +22 + + + + + + +location +id + + +12 + + +1 +2 +110 + + +6 +7 +11 + + +8 +9 +11 + + + + + + +location +lambda + + +12 + + +1 +2 +110 + + +6 +7 +22 + + + + + + +location +index + + +12 + + +1 +2 +122 + + +3 +4 +11 + + + + + + +location +captured_by_reference + + +12 + + +1 +2 +133 + + + + + + +location +is_implicit + + +12 + + +1 +2 +133 + + + + + + + + +stmts +5687673 + + +id +5687673 + + +kind +210 + + +location +5604699 + + + + +id +kind + + +12 + + +1 +2 +5687673 + + + + + + +id +location + + +12 + + +1 +2 +5687673 + + + + + + +kind +id + + +12 + + +2 +3 +11 + + +25 +26 +11 + + +452 +453 +11 + + +619 +620 +11 + + +858 +859 +11 + + +1755 +1756 +11 + + +1939 +1940 +11 + + +2505 +2506 +11 + + +2603 +2604 +11 + + +2767 +2768 +11 + + +3377 +3378 +11 + + +3768 +3769 +11 + + +4427 +4428 +11 + + +6231 +6232 +11 + + +37830 +37831 +11 + + +62553 +62554 +11 + + +111982 +111983 +11 + + +123254 +123255 +11 + + +145651 +145652 +11 + + + + + + +kind +location + + +12 + + +2 +3 +11 + + +25 +26 +11 + + +452 +453 +11 + + +619 +620 +11 + + +858 +859 +11 + + +1755 +1756 +11 + + +1939 +1940 +11 + + +2505 +2506 +11 + + +2603 +2604 +11 + + +2767 +2768 +11 + + +3377 +3378 +11 + + +3768 +3769 +11 + + +4427 +4428 +11 + + +6231 +6232 +11 + + +37830 +37831 +11 + + +62549 +62550 +11 + + +111896 +111897 +11 + + +120613 +120614 +11 + + +142847 +142848 +11 + + + + + + +location +id + + +12 + + +1 +2 +5583151 + + +2 +5535 +21548 + + + + + + +location +kind + + +12 + + +1 +2 +5583151 + + +2 +4 +21548 + + + + + + + + +type_vla +1 + + +type_id +1 + + +decl +1 + + + + +type_id +decl + + +12 + + +1 +2 +1 + + + + + + +decl +type_id + + +12 + + +1 +2 +1 + + + + + + + + +variable_vla +104 + + +var +104 + + +decl +104 + + + + +var +decl + + +12 + + +1 +2 +104 + + + + + + +decl +var + + +12 + + +1 +2 +104 + + + + + + + + +if_then +658624 + + +if_stmt +658624 + + +then_id +658624 + + + + +if_stmt +then_id + + +12 + + +1 +2 +658624 + + + + + + +then_id +if_stmt + + +12 + + +1 +2 +658624 + + + + + + + + +if_else +192230 + + +if_stmt +192230 + + +else_id +192230 + + + + +if_stmt +else_id + + +12 + + +1 +2 +192230 + + + + + + +else_id +if_stmt + + +12 + + +1 +2 +192230 + + + + + + + + +while_body +41808 + + +while_stmt +41808 + + +body_id +41808 + + + + +while_stmt +body_id + + +12 + + +1 +2 +41808 + + + + + + +body_id +while_stmt + + +12 + + +1 +2 +41808 + + + + + + + + +do_body +220170 + + +do_stmt +220170 + + +body_id +220170 + + + + +do_stmt +body_id + + +12 + + +1 +2 +220170 + + + + + + +body_id +do_stmt + + +12 + + +1 +2 +220170 + + + + + + + + +switch_case +388126 + + +switch_stmt +76066 + + +index +426 + + +case_id +388126 + + + + +switch_stmt +index + + +12 + + +1 +5 +5877 + + +5 +6 +67190 + + +6 +156 +2999 + + + + + + +switch_stmt +case_id + + +12 + + +1 +5 +5877 + + +5 +6 +67190 + + +6 +156 +2999 + + + + + + +index +switch_stmt + + +12 + + +1 +2 +167 + + +2 +3 +46 + + +3 +5 +38 + + +5 +11 +33 + + +11 +28 +33 + + +28 +56 +33 + + +59 +158 +33 + + +177 +26285 +33 + + +27166 +27647 +8 + + + + + + +index +case_id + + +12 + + +1 +2 +167 + + +2 +3 +46 + + +3 +5 +38 + + +5 +11 +33 + + +11 +28 +33 + + +28 +56 +33 + + +59 +158 +33 + + +177 +26285 +33 + + +27166 +27647 +8 + + + + + + +case_id +switch_stmt + + +12 + + +1 +2 +388126 + + + + + + +case_id +index + + +12 + + +1 +2 +388126 + + + + + + + + +switch_body +76075 + + +switch_stmt +76075 + + +body_id +76075 + + + + +switch_stmt +body_id + + +12 + + +1 +2 +76075 + + + + + + +body_id +switch_stmt + + +12 + + +1 +2 +76075 + + + + + + + + +for_initialization +35089 + + +for_stmt +35089 + + +init_id +35089 + + + + +for_stmt +init_id + + +12 + + +1 +2 +35089 + + + + + + +init_id +for_stmt + + +12 + + +1 +2 +35089 + + + + + + + + +for_condition +40832 + + +for_stmt +40832 + + +condition_id +40832 + + + + +for_stmt +condition_id + + +12 + + +1 +2 +40832 + + + + + + +condition_id +for_stmt + + +12 + + +1 +2 +40832 + + + + + + + + +for_update +39995 + + +for_stmt +39995 + + +update_id +39995 + + + + +for_stmt +update_id + + +12 + + +1 +2 +39995 + + + + + + +update_id +for_stmt + + +12 + + +1 +2 +39995 + + + + + + + + +for_body +42104 + + +for_stmt +42104 + + +body_id +42104 + + + + +for_stmt +body_id + + +12 + + +1 +2 +42104 + + + + + + +body_id +for_stmt + + +12 + + +1 +2 +42104 + + + + + + + + +stmtparents +5409488 + + +id +5409488 + + +index +1273 + + +parent +1953109 + + + + +id +index + + +12 + + +1 +2 +5409488 + + + + + + +id +parent + + +12 + + +1 +2 +5409488 + + + + + + +index +id + + +12 + + +1 +2 +448 + + +2 +3 +55 + + +3 +4 +90 + + +4 +6 +104 + + +6 +10 +90 + + +10 +19 +115 + + +22 +46 +96 + + +47 +149 +96 + + +161 +1629 +96 + + +1801 +579863 +79 + + + + + + +index +parent + + +12 + + +1 +2 +448 + + +2 +3 +55 + + +3 +4 +90 + + +4 +6 +104 + + +6 +10 +90 + + +10 +19 +115 + + +22 +46 +96 + + +47 +149 +96 + + +161 +1629 +96 + + +1801 +579863 +79 + + + + + + +parent +id + + +12 + + +1 +2 +984434 + + +2 +3 +483283 + + +3 +4 +163888 + + +4 +7 +153217 + + +7 +17 +147973 + + +17 +464 +20311 + + + + + + +parent +index + + +12 + + +1 +2 +984434 + + +2 +3 +483283 + + +3 +4 +163888 + + +4 +7 +153217 + + +7 +17 +147973 + + +17 +464 +20311 + + + + + + + + +ishandler +21514 + + +block +21514 + + + + + +successors +21584873 + + +from +20200932 + + +to +20198819 + + + + +from +to + + +12 + + +1 +2 +19055919 + + +2 +156 +1145012 + + + + + + +to +from + + +12 + + +1 +2 +19381610 + + +2 +349 +817209 + + + + + + + + +truecond +1246775 + + +from +1246775 + + +to +1206657 + + + + +from +to + + +12 + + +1 +2 +1246775 + + + + + + +to +from + + +12 + + +1 +2 +1173793 + + +2 +23 +32864 + + + + + + + + +falsecond +1246775 + + +from +1246775 + + +to +1026937 + + + + +from +to + + +12 + + +1 +2 +1246775 + + + + + + +to +from + + +12 + + +1 +2 +878392 + + +2 +3 +109699 + + +3 +22 +38845 + + + + + + + + +stmt_decl_bind +702192 + + +stmt +640540 + + +num +740 + + +decl +676381 + + + + +stmt +num + + +12 + + +1 +2 +596376 + + +2 +270 +44163 + + + + + + +stmt +decl + + +12 + + +1 +2 +596376 + + +2 +15 +44163 + + + + + + +num +stmt + + +12 + + +8 +9 +365 + + +10 +11 +2 + + +15 +16 +332 + + +16 +232801 +38 + + + + + + +num +decl + + +12 + + +8 +9 +365 + + +9 +10 +330 + + +10 +226538 +44 + + + + + + +decl +stmt + + +12 + + +1 +2 +668555 + + +2 +81 +7825 + + + + + + +decl +num + + +12 + + +1 +2 +676320 + + +2 +269 +60 + + + + + + + + +stmt_decl_entry_bind +702192 + + +stmt +640540 + + +num +740 + + +decl_entry +679737 + + + + +stmt +num + + +12 + + +1 +2 +596376 + + +2 +270 +44163 + + + + + + +stmt +decl_entry + + +12 + + +1 +2 +596376 + + +2 +15 +44163 + + + + + + +num +stmt + + +12 + + +8 +9 +365 + + +10 +11 +2 + + +15 +16 +332 + + +16 +232801 +38 + + + + + + +num +decl_entry + + +12 + + +8 +9 +365 + + +9 +10 +330 + + +10 +227748 +44 + + + + + + +decl_entry +stmt + + +12 + + +1 +2 +672245 + + +2 +17 +7492 + + + + + + +decl_entry +num + + +12 + + +1 +2 +679663 + + +2 +269 +74 + + + + + + + + +blockscope +1614668 + + +block +1614657 + + +enclosing +1429080 + + + + +block +enclosing + + +12 + + +1 +2 +1614646 + + +2 +3 +11 + + + + + + +enclosing +block + + +12 + + +1 +2 +1329806 + + +2 +692 +99273 + + + + + + + + +jumpinfo +508905 + + +id +508905 + + +str +8898 + + +target +118849 + + + + +id +str + + +12 + + +1 +2 +508905 + + + + + + +id +target + + +12 + + +1 +2 +508905 + + + + + + +str +id + + +12 + + +1 +2 +5 + + +2 +3 +4850 + + +3 +4 +1172 + + +4 +5 +1059 + + +5 +7 +726 + + +7 +16 +693 + + +16 +154441 +390 + + + + + + +str +target + + +12 + + +1 +2 +7076 + + +2 +3 +1023 + + +3 +12 +676 + + +12 +33252 +121 + + + + + + +target +id + + +12 + + +1 +2 +16 + + +2 +3 +30010 + + +3 +4 +10557 + + +4 +5 +5524 + + +5 +6 +54916 + + +6 +7 +15207 + + +7 +155 +2616 + + + + + + +target +str + + +12 + + +1 +2 +118849 + + + + + + + + +preprocdirects +1209495 + + +id +1209495 + + +kind +155 + + +location +1203037 + + + + +id +kind + + +12 + + +1 +2 +1209495 + + + + + + +id +location + + +12 + + +1 +2 +1209495 + + + + + + +kind +id + + +12 + + +2 +3 +11 + + +4 +5 +11 + + +7 +8 +11 + + +89 +90 +11 + + +640 +641 +11 + + +1510 +1511 +11 + + +1541 +1542 +11 + + +4500 +4501 +11 + + +4602 +4603 +11 + + +6920 +6921 +11 + + +12060 +12061 +11 + + +23482 +23483 +11 + + +25393 +25394 +11 + + +28255 +28256 +11 + + + + + + +kind +location + + +12 + + +2 +3 +11 + + +4 +5 +11 + + +6 +7 +11 + + +89 +90 +11 + + +640 +641 +11 + + +1510 +1511 +11 + + +1541 +1542 +11 + + +4500 +4501 +11 + + +4602 +4603 +11 + + +6920 +6921 +11 + + +12060 +12061 +11 + + +23482 +23483 +11 + + +25034 +25035 +11 + + +28033 +28034 +11 + + + + + + +location +id + + +12 + + +1 +2 +1202704 + + +2 +224 +332 + + + + + + +location +kind + + +12 + + +1 +2 +1203037 + + + + + + + + +preprocpair +328368 + + +begin +260551 + + +elseelifend +328368 + + + + +begin +elseelifend + + +12 + + +1 +2 +206348 + + +2 +3 +48355 + + +3 +53 +5847 + + + + + + +elseelifend +begin + + +12 + + +1 +2 +328368 + + + + + + + + +preproctrue +146475 + + +branch +146475 + + + + + +preprocfalse +106186 + + +branch +106186 + + + + + +preproctext +897870 + + +id +897870 + + +head +445096 + + +body +175257 + + + + +id +head + + +12 + + +1 +2 +897870 + + + + + + +id +body + + +12 + + +1 +2 +897870 + + + + + + +head +id + + +12 + + +1 +2 +341306 + + +2 +3 +68993 + + +3 +45 +33398 + + +45 +671 +1398 + + + + + + +head +body + + +12 + + +1 +2 +428907 + + +2 +40 +16188 + + + + + + +body +id + + +12 + + +1 +2 +164927 + + +2 +58048 +10330 + + + + + + +body +head + + +12 + + +1 +2 +165759 + + +2 +19352 +9497 + + + + + + + + +includes +281832 + + +id +281832 + + +included +51595 + + + + +id +included + + +12 + + +1 +2 +281832 + + + + + + +included +id + + +12 + + +1 +2 +24976 + + +2 +3 +8565 + + +3 +4 +4083 + + +4 +6 +4726 + + +6 +11 +4016 + + +11 +32 +3916 + + +32 +684 +1309 + + + + + + + + +link_targets +1295 + + +id +1295 + + +binary +1295 + + + + +id +binary + + +12 + + +1 +2 +1295 + + + + + + +binary +id + + +12 + + +1 +2 +1295 + + + + + + + + +link_parent +12935047 + + +element +4650728 + + +link_target +654 + + + + +element +link_target + + +12 + + +1 +2 +2823653 + + +2 +3 +1009804 + + +3 +5 +416424 + + +5 +36 +355131 + + +36 +60 +45714 + + + + + + +link_target +element + + +12 + + +5 +7 +55 + + +7 +9 +44 + + +9 +25 +55 + + +50 +977 +55 + + +4264 +6130 +55 + + +6207 +8492 +55 + + +8548 +14604 +55 + + +16365 +19508 +55 + + +19529 +21610 +55 + + +22168 +27227 +55 + + +27483 +38406 +55 + + +39263 +327210 +55 + + + + + + + + +xmlEncoding +39724 + + +id +39724 + + +encoding +1 + + + + +id +encoding + + +12 + + +1 +2 +39724 + + + + + + +encoding +id + + +12 + + +39724 +39725 +1 + + + + + + + + +xmlDTDs +1 + + +id +1 + + +root +1 + + +publicId +1 + + +systemId +1 + + +fileid +1 + + + + +id +root + + +12 + + +1 +2 +1 + + + + + + +id +publicId + + +12 + + +1 +2 +1 + + + + + + +id +systemId + + +12 + + +1 +2 +1 + + + + + + +id +fileid + + +12 + + +1 +2 +1 + + + + + + +root +id + + +12 + + +1 +2 +1 + + + + + + +root +publicId + + +12 + + +1 +2 +1 + + + + + + +root +systemId + + +12 + + +1 +2 +1 + + + + + + +root +fileid + + +12 + + +1 +2 +1 + + + + + + +publicId +id + + +12 + + +1 +2 +1 + + + + + + +publicId +root + + +12 + + +1 +2 +1 + + + + + + +publicId +systemId + + +12 + + +1 +2 +1 + + + + + + +publicId +fileid + + +12 + + +1 +2 +1 + + + + + + +systemId +id + + +12 + + +1 +2 +1 + + + + + + +systemId +root + + +12 + + +1 +2 +1 + + + + + + +systemId +publicId + + +12 + + +1 +2 +1 + + + + + + +systemId +fileid + + +12 + + +1 +2 +1 + + + + + + +fileid +id + + +12 + + +1 +2 +1 + + + + + + +fileid +root + + +12 + + +1 +2 +1 + + + + + + +fileid +publicId + + +12 + + +1 +2 +1 + + + + + + +fileid +systemId + + +12 + + +1 +2 +1 + + + + + + + + +xmlElements +1270313 + + +id +1270313 + + +name +4655 + + +parentid +578021 + + +idx +35122 + + +fileid +39721 + + + + +id +name + + +12 + + +1 +2 +1270313 + + + + + + +id +parentid + + +12 + + +1 +2 +1270313 + + + + + + +id +idx + + +12 + + +1 +2 +1270313 + + + + + + +id +fileid + + +12 + + +1 +2 +1270313 + + + + + + +name +id + + +12 + + +1 +2 +420 + + +2 +5 +156 + + +5 +6 +3832 + + +6 +310317 +247 + + + + + + +name +parentid + + +12 + + +1 +2 +456 + + +2 +5 +150 + + +5 +6 +3829 + + +6 +161565 +220 + + + + + + +name +idx + + +12 + + +1 +2 +4358 + + +2 +35123 +297 + + + + + + +name +fileid + + +12 + + +1 +2 +486 + + +2 +5 +133 + + +5 +6 +3831 + + +6 +14503 +205 + + + + + + +parentid +id + + +12 + + +1 +2 +371969 + + +2 +3 +62095 + + +3 +4 +104113 + + +4 +35123 +39844 + + + + + + +parentid +name + + +12 + + +1 +2 +500482 + + +2 +3 +17866 + + +3 +4 +49117 + + +4 +45 +10556 + + + + + + +parentid +idx + + +12 + + +1 +2 +371969 + + +2 +3 +62095 + + +3 +4 +104113 + + +4 +35123 +39844 + + + + + + +parentid +fileid + + +12 + + +1 +2 +578021 + + + + + + +idx +id + + +12 + + +2 +3 +606 + + +4 +5 +17851 + + +5 +6 +6533 + + +6 +7 +859 + + +7 +8 +4471 + + +9 +16 +2719 + + +16 +578022 +2083 + + + + + + +idx +name + + +12 + + +1 +2 +18457 + + +2 +3 +6533 + + +3 +4 +6178 + + +4 +8 +2624 + + +8 +4397 +1330 + + + + + + +idx +parentid + + +12 + + +2 +3 +606 + + +4 +5 +17851 + + +5 +6 +6533 + + +6 +7 +859 + + +7 +8 +4471 + + +9 +16 +2719 + + +16 +578022 +2083 + + + + + + +idx +fileid + + +12 + + +2 +3 +606 + + +4 +5 +17851 + + +5 +6 +6533 + + +6 +7 +859 + + +7 +8 +4471 + + +9 +16 +2719 + + +16 +39722 +2083 + + + + + + +fileid +id + + +12 + + +1 +2 +20457 + + +2 +3 +3115 + + +3 +7 +3026 + + +7 +8 +3588 + + +8 +9 +2220 + + +9 +11 +3099 + + +11 +19 +3087 + + +19 +114506 +1129 + + + + + + +fileid +name + + +12 + + +1 +2 +20459 + + +2 +3 +3458 + + +3 +5 +2569 + + +5 +7 +2172 + + +7 +8 +6158 + + +8 +9 +3501 + + +9 +46 +1404 + + + + + + +fileid +parentid + + +12 + + +1 +2 +20457 + + +2 +3 +3870 + + +3 +5 +2152 + + +5 +6 +2876 + + +6 +7 +2720 + + +7 +8 +4132 + + +8 +14 +3096 + + +14 +31079 +418 + + + + + + +fileid +idx + + +12 + + +1 +2 +25894 + + +2 +3 +5301 + + +3 +4 +3787 + + +4 +6 +3268 + + +6 +35123 +1471 + + + + + + + + +xmlAttrs +1202020 + + +id +1202020 + + +elementid +760198 + + +name +3649 + + +value +121803 + + +idx +2000 + + +fileid +39448 + + + + +id +elementid + + +12 + + +1 +2 +1202020 + + + + + + +id +name + + +12 + + +1 +2 +1202020 + + + + + + +id +value + + +12 + + +1 +2 +1202020 + + + + + + +id +idx + + +12 + + +1 +2 +1202020 + + + + + + +id +fileid + + +12 + + +1 +2 +1202020 + + + + + + +elementid +id + + +12 + + +1 +2 +425697 + + +2 +3 +249659 + + +3 +4 +66474 + + +4 +2001 +18368 + + + + + + +elementid +name + + +12 + + +1 +2 +425778 + + +2 +3 +249579 + + +3 +4 +66475 + + +4 +2001 +18366 + + + + + + +elementid +value + + +12 + + +1 +2 +466237 + + +2 +3 +266291 + + +3 +46 +27670 + + + + + + +elementid +idx + + +12 + + +1 +2 +425697 + + +2 +3 +249659 + + +3 +4 +66474 + + +4 +2001 +18368 + + + + + + +elementid +fileid + + +12 + + +1 +2 +760198 + + + + + + +name +id + + +12 + + +1 +2 +3467 + + +2 +262475 +182 + + + + + + +name +elementid + + +12 + + +1 +2 +3467 + + +2 +262475 +182 + + + + + + +name +value + + +12 + + +1 +2 +3501 + + +2 +54146 +148 + + + + + + +name +idx + + +12 + + +1 +2 +3531 + + +2 +11 +118 + + + + + + +name +fileid + + +12 + + +1 +2 +3491 + + +2 +21768 +158 + + + + + + +value +id + + +12 + + +1 +2 +72032 + + +2 +3 +42366 + + +3 +199269 +7405 + + + + + + +value +elementid + + +12 + + +1 +2 +72036 + + +2 +3 +42374 + + +3 +199269 +7393 + + + + + + +value +name + + +12 + + +1 +2 +116722 + + +2 +2041 +5081 + + + + + + +value +idx + + +12 + + +1 +2 +117957 + + +2 +2001 +3846 + + + + + + +value +fileid + + +12 + + +1 +2 +86306 + + +2 +3 +28570 + + +3 +4175 +6927 + + + + + + +idx +id + + +12 + + +1 +2 +1955 + + +2 +760199 +45 + + + + + + +idx +elementid + + +12 + + +1 +2 +1955 + + +2 +760199 +45 + + + + + + +idx +name + + +12 + + +1 +2 +1955 + + +2 +189 +45 + + + + + + +idx +value + + +12 + + +1 +2 +1955 + + +2 +116643 +45 + + + + + + +idx +fileid + + +12 + + +1 +2 +1955 + + +2 +39449 +45 + + + + + + +fileid +id + + +12 + + +1 +2 +22884 + + +2 +4 +2565 + + +4 +6 +2294 + + +6 +7 +3299 + + +7 +9 +3272 + + +9 +16 +3143 + + +16 +129952 +1991 + + + + + + +fileid +elementid + + +12 + + +1 +2 +23890 + + +2 +4 +2131 + + +4 +5 +1971 + + +5 +6 +4096 + + +6 +8 +3519 + + +8 +16 +3137 + + +16 +106600 +704 + + + + + + +fileid +name + + +12 + + +1 +2 +22946 + + +2 +3 +2338 + + +3 +4 +2726 + + +4 +5 +2824 + + +5 +6 +2994 + + +6 +7 +3876 + + +7 +2002 +1744 + + + + + + +fileid +value + + +12 + + +1 +2 +22916 + + +2 +4 +2772 + + +4 +5 +2112 + + +5 +6 +3510 + + +6 +8 +1993 + + +8 +11 +3365 + + +11 +50357 +2780 + + + + + + +fileid +idx + + +12 + + +1 +2 +26133 + + +2 +3 +9699 + + +3 +5 +3511 + + +5 +2001 +105 + + + + + + + + +xmlNs +71201 + + +id +4185 + + +prefixName +958 + + +URI +4185 + + +fileid +39544 + + + + +id +prefixName + + +12 + + +1 +2 +2602 + + +2 +3 +1553 + + +3 +872 +30 + + + + + + +id +URI + + +12 + + +1 +2 +4185 + + + + + + +id +fileid + + +12 + + +1 +6 +274 + + +6 +7 +3825 + + +7 +24905 +86 + + + + + + +prefixName +id + + +12 + + +1 +2 +915 + + +2 +4054 +43 + + + + + + +prefixName +URI + + +12 + + +1 +2 +915 + + +2 +4054 +43 + + + + + + +prefixName +fileid + + +12 + + +1 +2 +828 + + +2 +5 +73 + + +5 +24903 +57 + + + + + + +URI +id + + +12 + + +1 +2 +4185 + + + + + + +URI +prefixName + + +12 + + +1 +2 +2602 + + +2 +3 +1553 + + +3 +872 +30 + + + + + + +URI +fileid + + +12 + + +1 +6 +274 + + +6 +7 +3825 + + +7 +24905 +86 + + + + + + +fileid +id + + +12 + + +1 +2 +11655 + + +2 +3 +26146 + + +3 +8 +1743 + + + + + + +fileid +prefixName + + +12 + + +1 +2 +11653 + + +2 +3 +25982 + + +3 +31 +1909 + + + + + + +fileid +URI + + +12 + + +1 +2 +11655 + + +2 +3 +26146 + + +3 +8 +1743 + + + + + + + + +xmlHasNs +1139730 + + +elementId +1139730 + + +nsId +4136 + + +fileid +39537 + + + + +elementId +nsId + + +12 + + +1 +2 +1139730 + + + + + + +elementId +fileid + + +12 + + +1 +2 +1139730 + + + + + + +nsId +elementId + + +12 + + +1 +5 +234 + + +5 +6 +3824 + + +6 +643289 +78 + + + + + + +nsId +fileid + + +12 + + +1 +5 +257 + + +5 +6 +3823 + + +6 +24759 +56 + + + + + + +fileid +elementId + + +12 + + +1 +2 +3669 + + +2 +3 +20429 + + +3 +7 +2536 + + +7 +8 +3473 + + +8 +9 +2258 + + +9 +11 +3036 + + +11 +18 +2966 + + +18 +147552 +1170 + + + + + + +fileid +nsId + + +12 + + +1 +2 +18261 + + +2 +3 +21032 + + +3 +8 +244 + + + + + + + + +xmlComments +26812 + + +id +26812 + + +text +22933 + + +parentid +26546 + + +fileid +26368 + + + + +id +text + + +12 + + +1 +2 +26812 + + + + + + +id +parentid + + +12 + + +1 +2 +26812 + + + + + + +id +fileid + + +12 + + +1 +2 +26812 + + + + + + +text +id + + +12 + + +1 +2 +21517 + + +2 +62 +1416 + + + + + + +text +parentid + + +12 + + +1 +2 +21519 + + +2 +62 +1414 + + + + + + +text +fileid + + +12 + + +1 +2 +21522 + + +2 +62 +1411 + + + + + + +parentid +id + + +12 + + +1 +2 +26379 + + +2 +17 +167 + + + + + + +parentid +text + + +12 + + +1 +2 +26379 + + +2 +17 +167 + + + + + + +parentid +fileid + + +12 + + +1 +2 +26546 + + + + + + +fileid +id + + +12 + + +1 +2 +26161 + + +2 +17 +207 + + + + + + +fileid +text + + +12 + + +1 +2 +26165 + + +2 +17 +203 + + + + + + +fileid +parentid + + +12 + + +1 +2 +26223 + + +2 +10 +145 + + + + + + + + +xmlChars +439958 + + +id +439958 + + +text +100518 + + +parentid +433851 + + +idx +4 + + +isCDATA +1 + + +fileid +26494 + + + + +id +text + + +12 + + +1 +2 +439958 + + + + + + +id +parentid + + +12 + + +1 +2 +439958 + + + + + + +id +idx + + +12 + + +1 +2 +439958 + + + + + + +id +isCDATA + + +12 + + +1 +2 +439958 + + + + + + +id +fileid + + +12 + + +1 +2 +439958 + + + + + + +text +id + + +12 + + +1 +2 +60389 + + +2 +4 +3811 + + +4 +5 +29257 + + +5 +23171 +7061 + + + + + + +text +parentid + + +12 + + +1 +2 +60389 + + +2 +4 +3811 + + +4 +5 +29257 + + +5 +23171 +7061 + + + + + + +text +idx + + +12 + + +1 +2 +100517 + + +2 +3 +1 + + + + + + +text +isCDATA + + +12 + + +1 +2 +100518 + + + + + + +text +fileid + + +12 + + +1 +2 +61284 + + +2 +4 +4205 + + +4 +5 +28328 + + +5 +351 +6701 + + + + + + +parentid +id + + +12 + + +1 +2 +429716 + + +2 +5 +4135 + + + + + + +parentid +text + + +12 + + +1 +2 +429716 + + +2 +5 +4135 + + + + + + +parentid +idx + + +12 + + +1 +2 +429716 + + +2 +5 +4135 + + + + + + +parentid +isCDATA + + +12 + + +1 +2 +433851 + + + + + + +parentid +fileid + + +12 + + +1 +2 +433851 + + + + + + +idx +id + + +12 + + +80 +81 +1 + + +1892 +1893 +1 + + +4135 +4136 +1 + + +433851 +433852 +1 + + + + + + +idx +text + + +12 + + +1 +2 +1 + + +3 +4 +1 + + +16 +17 +1 + + +100499 +100500 +1 + + + + + + +idx +parentid + + +12 + + +80 +81 +1 + + +1892 +1893 +1 + + +4135 +4136 +1 + + +433851 +433852 +1 + + + + + + +idx +isCDATA + + +12 + + +1 +2 +4 + + + + + + +idx +fileid + + +12 + + +4 +5 +1 + + +46 +47 +1 + + +97 +98 +1 + + +26494 +26495 +1 + + + + + + +isCDATA +id + + +12 + + +439958 +439959 +1 + + + + + + +isCDATA +text + + +12 + + +100518 +100519 +1 + + + + + + +isCDATA +parentid + + +12 + + +433851 +433852 +1 + + + + + + +isCDATA +idx + + +12 + + +4 +5 +1 + + + + + + +isCDATA +fileid + + +12 + + +26494 +26495 +1 + + + + + + +fileid +id + + +12 + + +1 +2 +25303 + + +2 +35123 +1191 + + + + + + +fileid +text + + +12 + + +1 +2 +25765 + + +2 +35123 +729 + + + + + + +fileid +parentid + + +12 + + +1 +2 +25312 + + +2 +35123 +1182 + + + + + + +fileid +idx + + +12 + + +1 +2 +26397 + + +2 +5 +97 + + + + + + +fileid +isCDATA + + +12 + + +1 +2 +26494 + + + + + + + + +xmllocations +3051056 + + +xmlElement +2982460 + + +location +3051056 + + + + +xmlElement +location + + +12 + + +1 +2 +2978326 + + +2 +24903 +4134 + + + + + + +location +xmlElement + + +12 + + +1 +2 +3051056 + + + + + + + + + diff --git a/cpp/ql/src/tools/instantiate_templates.py b/cpp/ql/src/tools/instantiate_templates.py new file mode 100644 index 000000000000..0beeeed20d8e --- /dev/null +++ b/cpp/ql/src/tools/instantiate_templates.py @@ -0,0 +1,137 @@ +import sys +import os.path +import glob +import re +import json + +BEGIN_TEMPLATE = re.compile(r"^/\*template\s*$") +END_TEMPLATE = re.compile(r"^\*/\s*$") + +def expand_template_params(args, param_arg_map): + '''Given a list of template arguments that may reference template parameters + of the current template, return a new list of template arguments with each + parameter use replaced with the appropriate fully-qualified argument for + that parameter.''' + result = [] + for arg in args: + if arg in param_arg_map: + result.append(param_arg_map[arg]) + else: + result.append(arg) + + return result + +def find_instantiation(module, args, templates): + '''Given a template module and a set of template arguments, find the module + name of the instantiation of that module with those arguments.''' + template = templates[module] + for instantiation in template["template_def"]["instantiations"]: + if instantiation["args"] == args: + return instantiation["name"] + return None + +def instantiate_template(template, instantiation, root, templates): + '''Create a single instantiation of a template.''' + template_def = template["template_def"] + output_components = instantiation["name"].split(".") + output_path = root + for component in output_components: + output_path = os.path.join(output_path, component) + output_path = output_path + ".qll" + with open(output_path, "w") as output: + output.write( +""" +/* + * THIS FILE IS AUTOMATICALLY GENERATED FROM '%s'. + * DO NOT EDIT MANUALLY. + */ + +""" % (template["name"].replace(".", "/") + ".qllt") + ) + param_arg_map = {} + for param_index in range(len(template_def["params"])): + param = template_def["params"][param_index] + arg = instantiation["args"][param_index] + output.write("private import %s as %s // Template parameter\n" % (arg, param)) + param_arg_map[param] = arg + for import_record in template_def["imports"]: + if "access" in import_record: + output.write(import_record["access"] + " ") + imported_module = find_instantiation(import_record["module"], + expand_template_params(import_record["args"], param_arg_map), templates) + output.write("import %s // %s<%s>\n" % + ( + imported_module, + import_record["module"], + ", ".join(import_record["args"]) + ) + ) + + output.writelines(template_def["body_lines"]) + +def generate_instantiations(template, root, templates): + '''Create a .qll source file for each instantiation of the specified template.''' + template_def = template["template_def"] + if "instantiations" in template_def: + for instantiation in template_def["instantiations"]: + instantiate_template(template, instantiation, root, templates) + +def read_template(template_path, module_name): + '''Read a .qllt template file from template_path, using module_name as the + fully qualified name of the module.''' + with open(template_path) as input: + in_template = False + template_text = "" + template_def = None + body_lines = [] + for line in iter(input): + if in_template: + if END_TEMPLATE.match(line): + template_def = json.loads(template_text) + in_template = False + else: + template_text += line + else: + if BEGIN_TEMPLATE.match(line) and not template_def: + in_template = True + else: + body_lines.append(line) + + if template_def: + template_def["body_lines"] = body_lines + + result = { "name": module_name } + if template_def: + result["template_def"] = template_def + return result + +def module_name_from_path_impl(path): + (head, tail) = os.path.split(path) + if head == "": + return tail + else: + return module_name_from_path(head) + "." + tail + +def module_name_from_path(path): + '''Compute the fully qualified name of a module from the path of its .qll[t] + file. The path should be relative to the library root.''' + (module_root, ext) = os.path.splitext(path) + return module_name_from_path_impl(module_root) + +def main(): + templates = {} + + root = sys.argv[1] + for template_path in glob.glob(os.path.join(root, "**\\*.qllt"), recursive = True): + print(template_path) + module_name = module_name_from_path(os.path.relpath(template_path, root)) + print(module_name) + template = read_template(template_path, module_name) + templates[template["name"]] = template + + for name, template in templates.items(): + if "template_def" in template: + generate_instantiations(template, root, templates) + +if __name__ == "__main__": + main() diff --git a/cpp/ql/test/duplication-tests/duplicate_functions/FLinesOfDuplicatedCode.expected b/cpp/ql/test/duplication-tests/duplicate_functions/FLinesOfDuplicatedCode.expected new file mode 100644 index 000000000000..2a106a36d6b9 --- /dev/null +++ b/cpp/ql/test/duplication-tests/duplicate_functions/FLinesOfDuplicatedCode.expected @@ -0,0 +1,2 @@ +| duplicate_functions.cpp:0:0:0:0 | duplicate_functions.cpp | 42 | +| file://:0:0:0:0 | | 0 | diff --git a/cpp/ql/test/duplication-tests/duplicate_functions/FLinesOfDuplicatedCode.qlref b/cpp/ql/test/duplication-tests/duplicate_functions/FLinesOfDuplicatedCode.qlref new file mode 100644 index 000000000000..7e9bdfa57f7c --- /dev/null +++ b/cpp/ql/test/duplication-tests/duplicate_functions/FLinesOfDuplicatedCode.qlref @@ -0,0 +1 @@ +Metrics/Files/FLinesOfDuplicatedCode.ql diff --git a/cpp/ql/test/duplication-tests/duplicate_functions/duplicate_functions.cpp b/cpp/ql/test/duplication-tests/duplicate_functions/duplicate_functions.cpp new file mode 100644 index 000000000000..d31bfa2d90ff --- /dev/null +++ b/cpp/ql/test/duplication-tests/duplicate_functions/duplicate_functions.cpp @@ -0,0 +1,47 @@ +int x; + +void Void1() +{ + x = 0; + x = 1; + x = 2; + x = 3; + x = 4; + x = 5; + x = 6; +} + +void Void2() +{ + x = 0; + x = 1; + x = 2; + x = 3; + x = 4; + x = 5; + x = 6; +} + +int Int1() +{ + x = 0; + ++x; + ++x; + --x; + --x; + ++x; + --x; + return x; +} + +int Int2() +{ + x = 0; + ++x; + ++x; + --x; + --x; + ++x; + --x; + return x; +} diff --git a/cpp/ql/test/duplication-tests/duplicate_functions/duplicate_functions.expected b/cpp/ql/test/duplication-tests/duplicate_functions/duplicate_functions.expected new file mode 100644 index 000000000000..d31c0b9d72d6 --- /dev/null +++ b/cpp/ql/test/duplication-tests/duplicate_functions/duplicate_functions.expected @@ -0,0 +1,4 @@ +| duplicate_functions.cpp:3:6:3:10 | definition of Void1 | Function Void1 is duplicated at $@. | duplicate_functions.cpp:14:6:14:10 | definition of Void2 | duplicate_functions.cpp:14 | +| duplicate_functions.cpp:14:6:14:10 | definition of Void2 | Function Void2 is duplicated at $@. | duplicate_functions.cpp:3:6:3:10 | definition of Void1 | duplicate_functions.cpp:3 | +| duplicate_functions.cpp:25:5:25:8 | definition of Int1 | Function Int1 is duplicated at $@. | duplicate_functions.cpp:37:5:37:8 | definition of Int2 | duplicate_functions.cpp:37 | +| duplicate_functions.cpp:37:5:37:8 | definition of Int2 | Function Int2 is duplicated at $@. | duplicate_functions.cpp:25:5:25:8 | definition of Int1 | duplicate_functions.cpp:25 | diff --git a/cpp/ql/test/duplication-tests/duplicate_functions/duplicate_functions.qlref b/cpp/ql/test/duplication-tests/duplicate_functions/duplicate_functions.qlref new file mode 100644 index 000000000000..bb7e3a454178 --- /dev/null +++ b/cpp/ql/test/duplication-tests/duplicate_functions/duplicate_functions.qlref @@ -0,0 +1 @@ +external/DuplicateFunction.ql diff --git a/cpp/ql/test/duplication-tests/tokenising/FLinesOfDuplicatedCode.expected b/cpp/ql/test/duplication-tests/tokenising/FLinesOfDuplicatedCode.expected new file mode 100644 index 000000000000..d79f7cfd0864 --- /dev/null +++ b/cpp/ql/test/duplication-tests/tokenising/FLinesOfDuplicatedCode.expected @@ -0,0 +1,2 @@ +| test.c:0:0:0:0 | test.c | 10 | +| file://:0:0:0:0 | | 0 | diff --git a/cpp/ql/test/duplication-tests/tokenising/FLinesOfDuplicatedCode.qlref b/cpp/ql/test/duplication-tests/tokenising/FLinesOfDuplicatedCode.qlref new file mode 100644 index 000000000000..7e9bdfa57f7c --- /dev/null +++ b/cpp/ql/test/duplication-tests/tokenising/FLinesOfDuplicatedCode.qlref @@ -0,0 +1 @@ +Metrics/Files/FLinesOfDuplicatedCode.ql diff --git a/cpp/ql/test/duplication-tests/tokenising/duplications.expected b/cpp/ql/test/duplication-tests/tokenising/duplications.expected new file mode 100644 index 000000000000..a882f18620ba --- /dev/null +++ b/cpp/ql/test/duplication-tests/tokenising/duplications.expected @@ -0,0 +1 @@ +| 2 | diff --git a/cpp/ql/test/duplication-tests/tokenising/duplications.ql b/cpp/ql/test/duplication-tests/tokenising/duplications.ql new file mode 100644 index 000000000000..fd142954d483 --- /dev/null +++ b/cpp/ql/test/duplication-tests/tokenising/duplications.ql @@ -0,0 +1,6 @@ +import cpp + +// Provided tokenisation succeeded, we ought to have some +// @duplication rows. +select count(@duplication d) + diff --git a/cpp/ql/test/duplication-tests/tokenising/test.c b/cpp/ql/test/duplication-tests/tokenising/test.c new file mode 100644 index 000000000000..c7c0b991a66a --- /dev/null +++ b/cpp/ql/test/duplication-tests/tokenising/test.c @@ -0,0 +1,18 @@ + +#define foo "/*bar" +#define bar aaa \ bbb + +int i; + +void f(void) { + i = 1; + i = 2; + i = 3; +} + +void g(void) { + i = 1; + i = 2; + i = 3; +} + diff --git a/cpp/ql/test/examples/expressions/AddressOf.c b/cpp/ql/test/examples/expressions/AddressOf.c new file mode 100644 index 000000000000..40d89d7a1feb --- /dev/null +++ b/cpp/ql/test/examples/expressions/AddressOf.c @@ -0,0 +1,3 @@ +void v(int i) { + int *j = &i; +} diff --git a/cpp/ql/test/examples/expressions/ArrayToPointer.c b/cpp/ql/test/examples/expressions/ArrayToPointer.c new file mode 100644 index 000000000000..1bf2f9afae40 --- /dev/null +++ b/cpp/ql/test/examples/expressions/ArrayToPointer.c @@ -0,0 +1,10 @@ +struct S { + char* name; +}; + +void v() +{ + char c[] = "hello"; + struct S s; + s.name = c; +} diff --git a/cpp/ql/test/examples/expressions/Cast.c b/cpp/ql/test/examples/expressions/Cast.c new file mode 100644 index 000000000000..8c4c174939d2 --- /dev/null +++ b/cpp/ql/test/examples/expressions/Cast.c @@ -0,0 +1,3 @@ +void v(char *c, void *v) { + c = (char *)v; +} \ No newline at end of file diff --git a/cpp/ql/test/examples/expressions/ConditionDecl.cpp b/cpp/ql/test/examples/expressions/ConditionDecl.cpp new file mode 100644 index 000000000000..ffcbf3482165 --- /dev/null +++ b/cpp/ql/test/examples/expressions/ConditionDecl.cpp @@ -0,0 +1,5 @@ +void v() { + int j = 0; + while(int k = j < 5) { + } +} diff --git a/cpp/ql/test/examples/expressions/ConstructorCall.cpp b/cpp/ql/test/examples/expressions/ConstructorCall.cpp new file mode 100644 index 000000000000..e600548fc524 --- /dev/null +++ b/cpp/ql/test/examples/expressions/ConstructorCall.cpp @@ -0,0 +1,21 @@ +class C { +public: + C(int i) { + } +}; + +class D { +public: + D() { + } +}; + +class E { +public: +}; + +void v(C *c, D *d, E *e) { + c = new C(5); + d = new D(); + e = new E(); +} \ No newline at end of file diff --git a/cpp/ql/test/examples/expressions/Conversion1.c b/cpp/ql/test/examples/expressions/Conversion1.c new file mode 100644 index 000000000000..f512dfab80cd --- /dev/null +++ b/cpp/ql/test/examples/expressions/Conversion1.c @@ -0,0 +1,4 @@ +void v() { + int i = (int)1; +} + \ No newline at end of file diff --git a/cpp/ql/test/examples/expressions/Conversion2.c b/cpp/ql/test/examples/expressions/Conversion2.c new file mode 100644 index 000000000000..831797db4dc8 --- /dev/null +++ b/cpp/ql/test/examples/expressions/Conversion2.c @@ -0,0 +1,3 @@ +void v(int x) { + x = (int)5 + (int)7; +} \ No newline at end of file diff --git a/cpp/ql/test/examples/expressions/Conversion3.cpp b/cpp/ql/test/examples/expressions/Conversion3.cpp new file mode 100644 index 000000000000..185b4b8be00b --- /dev/null +++ b/cpp/ql/test/examples/expressions/Conversion3.cpp @@ -0,0 +1,3 @@ +void v(int x) { + x = (bool)(int)5 + ((int)7); +} \ No newline at end of file diff --git a/cpp/ql/test/examples/expressions/Conversion4.c b/cpp/ql/test/examples/expressions/Conversion4.c new file mode 100644 index 000000000000..5afc646a00b4 --- /dev/null +++ b/cpp/ql/test/examples/expressions/Conversion4.c @@ -0,0 +1,3 @@ +void v(int x) { + x = ((int)7); +} \ No newline at end of file diff --git a/cpp/ql/test/examples/expressions/ConvertVirtualFunctionPointer.cpp b/cpp/ql/test/examples/expressions/ConvertVirtualFunctionPointer.cpp new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/cpp/ql/test/examples/expressions/DestructorCall.cpp b/cpp/ql/test/examples/expressions/DestructorCall.cpp new file mode 100644 index 000000000000..303d3d0ce667 --- /dev/null +++ b/cpp/ql/test/examples/expressions/DestructorCall.cpp @@ -0,0 +1,14 @@ +class C { +public: + ~C() { + } +}; + +class D { +public: +}; + +void v(C *c, D *d) { + delete c; + delete d; +} \ No newline at end of file diff --git a/cpp/ql/test/examples/expressions/DynamicCast.cpp b/cpp/ql/test/examples/expressions/DynamicCast.cpp new file mode 100644 index 000000000000..63085edecadc --- /dev/null +++ b/cpp/ql/test/examples/expressions/DynamicCast.cpp @@ -0,0 +1,14 @@ +class Base { + virtual void f() { } +}; +class Derived : public Base { + void f() { } +}; + +void v(Base *bp, Derived *d) { + d = dynamic_cast(bp); +} + +void v_ref(Base &bp, Derived &d) { + d = dynamic_cast(bp); +} \ No newline at end of file diff --git a/cpp/ql/test/examples/expressions/Parenthesis.c b/cpp/ql/test/examples/expressions/Parenthesis.c new file mode 100644 index 000000000000..dee54291df97 --- /dev/null +++ b/cpp/ql/test/examples/expressions/Parenthesis.c @@ -0,0 +1,3 @@ +void v(int i) { + i = (i + 1) * 2; +} \ No newline at end of file diff --git a/cpp/ql/test/examples/expressions/PointerDereference.c b/cpp/ql/test/examples/expressions/PointerDereference.c new file mode 100644 index 000000000000..f88490ff1fe0 --- /dev/null +++ b/cpp/ql/test/examples/expressions/PointerDereference.c @@ -0,0 +1,3 @@ +void v(int *i, int j) { + j = *i; +} \ No newline at end of file diff --git a/cpp/ql/test/examples/expressions/PrintAST.expected b/cpp/ql/test/examples/expressions/PrintAST.expected new file mode 100644 index 000000000000..ffc83484efee --- /dev/null +++ b/cpp/ql/test/examples/expressions/PrintAST.expected @@ -0,0 +1,1843 @@ +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | AddressOf.c:1:6:1:6 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | ArrayToPointer.c:5:6:5:6 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Cast.c:1:6:1:6 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Conversion1.c:1:6:1:6 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Conversion2.c:1:6:1:6 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Conversion4.c:1:6:1:6 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Parenthesis.c:1:6:1:6 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | PointerDereference.c:1:6:1:6 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Sizeof.c:1:6:1:6 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | StatementExpr.c:1:6:1:6 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Subscript.c:1:6:1:6 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | AddressOf.c:1:15:3:1 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | ArrayToPointer.c:6:1:10:1 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Cast.c:1:26:3:1 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Conversion1.c:1:10:3:1 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Conversion2.c:1:15:3:1 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Conversion4.c:1:15:3:1 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Parenthesis.c:1:15:3:1 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | PointerDereference.c:1:23:3:1 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Sizeof.c:1:21:4:1 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | StatementExpr.c:1:10:3:1 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Subscript.c:1:24:3:1 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Cast.c:2:3:2:16 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Conversion2.c:2:3:2:22 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Conversion4.c:2:3:2:15 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Parenthesis.c:2:3:2:18 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | PointerDereference.c:2:3:2:9 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Subscript.c:2:3:2:11 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | AddressOf.c:2:3:2:14 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | ArrayToPointer.c:7:3:7:21 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | Conversion1.c:2:3:2:17 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | Sizeof.c:2:3:2:22 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | StatementExpr.c:2:3:2:30 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: char * | Cast.c:2:3:2:15 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Conversion2.c:2:3:2:21 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Conversion4.c:2:3:2:14 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Parenthesis.c:2:3:2:17 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | PointerDereference.c:2:3:2:8 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Subscript.c:2:3:2:10 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of c | | | char[] | ArrayToPointer.c:7:8:7:8 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of i | | | int | Conversion1.c:2:7:2:7 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of i | | | int | Sizeof.c:2:7:2:7 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of j | | | int | StatementExpr.c:2:7:2:7 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of j | | | int * | AddressOf.c:2:8:2:8 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: char * | Cast.c:2:3:2:3 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: int | Conversion2.c:2:3:2:3 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: int | Conversion4.c:2:3:2:3 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: char * | Cast.c:2:3:2:3 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: int | Conversion2.c:2:3:2:3 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: int | Conversion4.c:2:3:2:3 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: char * | Cast.c:2:3:2:3 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: int | Conversion2.c:2:3:2:3 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: int | Conversion4.c:2:3:2:3 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for c | | | | ArrayToPointer.c:7:14:7:20 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for i | | | | Conversion1.c:2:10:2:16 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for i | | | | Sizeof.c:2:10:2:21 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for j | | | | AddressOf.c:2:11:2:13 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for j | | | | StatementExpr.c:2:10:2:29 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | j | | | lvalue: int | PointerDereference.c:2:3:2:3 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | j | | | lvalue: int | Subscript.c:2:3:2:3 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | v | | | lvalue: int | PointerDereference.c:2:3:2:3 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | v | | | lvalue: int | Subscript.c:2:3:2:3 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: char * | Cast.c:2:3:2:3 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: int | Conversion2.c:2:3:2:3 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: int | Conversion4.c:2:3:2:3 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | (...) | | =7 | prvalue: int | Conversion4.c:2:7:2:14 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | (char *)... | pointer conversion | | prvalue: char * | Cast.c:2:7:2:15 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | * ... | | | prvalue(load): int | PointerDereference.c:2:7:2:8 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | ... * ... | | | prvalue: int | Parenthesis.c:2:7:2:17 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | ... + ... | | =12 | prvalue: int | Conversion2.c:2:7:2:21 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | access to array | | | prvalue(load): int | Subscript.c:2:7:2:10 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | & ... | | | prvalue: int * | AddressOf.c:2:12:2:13 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | (int)... | integral conversion | =1 | prvalue: int | Conversion1.c:2:11:2:16 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | (int)... | integral conversion | =4 | prvalue: int | Sizeof.c:2:11:2:21 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | (statement expression) | | | prvalue: int | StatementExpr.c:2:11:2:29 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | hello | | =hello | lvalue: char[6] | ArrayToPointer.c:7:14:7:20 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 1 | 1 | declaration | | | | ArrayToPointer.c:8:3:8:13 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 1 | 1 | return ... | | | | StatementExpr.c:3:1:3:1 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | 1 | | =1 | prvalue: int | Conversion1.c:2:16:2:16 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | (...) | | | prvalue: int | Parenthesis.c:2:7:2:13 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | (int)... | integral conversion | =5 | prvalue: int | Conversion2.c:2:7:2:12 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | (int)... | integral conversion | =7 | prvalue: int | Conversion4.c:2:8:2:13 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | array | | | lvalue: int | AddressOf.c:2:13:2:13 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | array | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | array | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | c | | | lvalue: int | AddressOf.c:2:13:2:13 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | c | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | c | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | i | | | lvalue: int | AddressOf.c:2:13:2:13 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | i | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | i | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | j | | | prvalue(load): void * | Cast.c:2:15:2:15 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | sizeof(int) | | =4 | prvalue: unsigned long | Sizeof.c:2:11:2:21 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | v | | | prvalue(load): void * | Cast.c:2:15:2:15 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | x | | | lvalue: int | AddressOf.c:2:13:2:13 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | x | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | x | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | declaration | | | | Sizeof.c:3:3:3:24 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | AddressOf.c:3:1:3:1 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | Cast.c:3:1:3:1 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | Conversion1.c:3:1:3:1 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | PointerDereference.c:3:1:3:1 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 5 | 1 | 5 | | =5 | prvalue: int | Subscript.c:2:9:2:9 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | 5 | | =5 | prvalue: int | Conversion2.c:2:12:2:12 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | 7 | | =7 | prvalue: int | Conversion4.c:2:13:2:13 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | ... + ... | | | prvalue: int | Parenthesis.c:2:8:2:12 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | definition of s | | | S | ArrayToPointer.c:8:12:8:12 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 1 | 1 | return ... | | | | Conversion4.c:3:1:3:1 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 1 | 1 | return ... | | | | Subscript.c:3:1:3:1 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 1 | 2 | ExprStmt | | | | ArrayToPointer.c:9:3:9:13 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 5 | 1 | (int)... | integral conversion | =7 | prvalue: int | Conversion2.c:2:16:2:21 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | array | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | c | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | definition of j | | | int | Sizeof.c:3:7:3:7 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | i | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | x | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 7 | 1 | 1 | | =1 | prvalue: int | Parenthesis.c:2:12:2:12 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 8 | 0 | 7 | | =7 | prvalue: int | Conversion2.c:2:21:2:21 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 8 | 0 | ... = ... | | | prvalue: char * | ArrayToPointer.c:9:3:9:12 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 8 | 0 | initializer for j | | | | Sizeof.c:3:10:3:23 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 1 | 1 | return ... | | | | Conversion2.c:3:1:3:1 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 5 | 1 | 2 | | =2 | prvalue: int | Parenthesis.c:2:17:2:17 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 9 | 0 | (int)... | integral conversion | =8 | prvalue: int | Sizeof.c:3:11:3:23 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 9 | 0 | name | | | lvalue: char * | ArrayToPointer.c:9:5:9:8 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 11 | 1 | 1 | return ... | | | | Parenthesis.c:3:1:3:1 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 11 | 10 | 0 | sizeof() | | =8 | prvalue: unsigned long | Sizeof.c:3:11:3:23 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 11 | 10 | -1 | s | | | lvalue: S | ArrayToPointer.c:9:3:9:3 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 12 | 9 | 1 | array to pointer conversion | | | prvalue: char * | ArrayToPointer.c:9:12:9:12 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 12 | 11 | 0 | (...) | | | lvalue: int * | Sizeof.c:3:18:3:23 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | array | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | c | | | lvalue: char[6] | ArrayToPointer.c:9:12:9:12 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | c | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | i | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | x | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 14 | 1 | 2 | return ... | | | | Sizeof.c:4:1:4:1 | +| AddressOf.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 14 | 1 | 3 | return ... | | | | ArrayToPointer.c:10:1:10:1 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | AddressOf.c:1:6:1:6 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | ArrayToPointer.c:5:6:5:6 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Cast.c:1:6:1:6 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Conversion1.c:1:6:1:6 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Conversion2.c:1:6:1:6 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Conversion4.c:1:6:1:6 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Parenthesis.c:1:6:1:6 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | PointerDereference.c:1:6:1:6 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Sizeof.c:1:6:1:6 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | StatementExpr.c:1:6:1:6 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Subscript.c:1:6:1:6 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | AddressOf.c:1:15:3:1 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | ArrayToPointer.c:6:1:10:1 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Cast.c:1:26:3:1 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Conversion1.c:1:10:3:1 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Conversion2.c:1:15:3:1 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Conversion4.c:1:15:3:1 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Parenthesis.c:1:15:3:1 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | PointerDereference.c:1:23:3:1 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Sizeof.c:1:21:4:1 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | StatementExpr.c:1:10:3:1 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Subscript.c:1:24:3:1 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Cast.c:2:3:2:16 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Conversion2.c:2:3:2:22 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Conversion4.c:2:3:2:15 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Parenthesis.c:2:3:2:18 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | PointerDereference.c:2:3:2:9 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Subscript.c:2:3:2:11 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | AddressOf.c:2:3:2:14 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | ArrayToPointer.c:7:3:7:21 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | Conversion1.c:2:3:2:17 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | Sizeof.c:2:3:2:22 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | StatementExpr.c:2:3:2:30 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: char * | Cast.c:2:3:2:15 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Conversion2.c:2:3:2:21 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Conversion4.c:2:3:2:14 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Parenthesis.c:2:3:2:17 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | PointerDereference.c:2:3:2:8 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Subscript.c:2:3:2:10 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of c | | | char[] | ArrayToPointer.c:7:8:7:8 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of i | | | int | Conversion1.c:2:7:2:7 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of i | | | int | Sizeof.c:2:7:2:7 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of j | | | int | StatementExpr.c:2:7:2:7 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of j | | | int * | AddressOf.c:2:8:2:8 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: char * | Cast.c:2:3:2:3 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: int | Conversion2.c:2:3:2:3 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: int | Conversion4.c:2:3:2:3 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: char * | Cast.c:2:3:2:3 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: int | Conversion2.c:2:3:2:3 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: int | Conversion4.c:2:3:2:3 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: char * | Cast.c:2:3:2:3 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: int | Conversion2.c:2:3:2:3 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: int | Conversion4.c:2:3:2:3 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for c | | | | ArrayToPointer.c:7:14:7:20 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for i | | | | Conversion1.c:2:10:2:16 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for i | | | | Sizeof.c:2:10:2:21 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for j | | | | AddressOf.c:2:11:2:13 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for j | | | | StatementExpr.c:2:10:2:29 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | j | | | lvalue: int | PointerDereference.c:2:3:2:3 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | j | | | lvalue: int | Subscript.c:2:3:2:3 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | v | | | lvalue: int | PointerDereference.c:2:3:2:3 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | v | | | lvalue: int | Subscript.c:2:3:2:3 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: char * | Cast.c:2:3:2:3 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: int | Conversion2.c:2:3:2:3 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: int | Conversion4.c:2:3:2:3 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | (...) | | =7 | prvalue: int | Conversion4.c:2:7:2:14 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | (char *)... | pointer conversion | | prvalue: char * | Cast.c:2:7:2:15 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | * ... | | | prvalue(load): int | PointerDereference.c:2:7:2:8 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | ... * ... | | | prvalue: int | Parenthesis.c:2:7:2:17 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | ... + ... | | =12 | prvalue: int | Conversion2.c:2:7:2:21 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | access to array | | | prvalue(load): int | Subscript.c:2:7:2:10 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | & ... | | | prvalue: int * | AddressOf.c:2:12:2:13 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | (int)... | integral conversion | =1 | prvalue: int | Conversion1.c:2:11:2:16 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | (int)... | integral conversion | =4 | prvalue: int | Sizeof.c:2:11:2:21 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | (statement expression) | | | prvalue: int | StatementExpr.c:2:11:2:29 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | hello | | =hello | lvalue: char[6] | ArrayToPointer.c:7:14:7:20 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 1 | 1 | declaration | | | | ArrayToPointer.c:8:3:8:13 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 1 | 1 | return ... | | | | StatementExpr.c:3:1:3:1 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | 1 | | =1 | prvalue: int | Conversion1.c:2:16:2:16 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | (...) | | | prvalue: int | Parenthesis.c:2:7:2:13 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | (int)... | integral conversion | =5 | prvalue: int | Conversion2.c:2:7:2:12 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | (int)... | integral conversion | =7 | prvalue: int | Conversion4.c:2:8:2:13 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | array | | | lvalue: int | AddressOf.c:2:13:2:13 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | array | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | array | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | c | | | lvalue: int | AddressOf.c:2:13:2:13 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | c | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | c | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | i | | | lvalue: int | AddressOf.c:2:13:2:13 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | i | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | i | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | j | | | prvalue(load): void * | Cast.c:2:15:2:15 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | sizeof(int) | | =4 | prvalue: unsigned long | Sizeof.c:2:11:2:21 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | v | | | prvalue(load): void * | Cast.c:2:15:2:15 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | x | | | lvalue: int | AddressOf.c:2:13:2:13 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | x | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | x | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | declaration | | | | Sizeof.c:3:3:3:24 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | AddressOf.c:3:1:3:1 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | Cast.c:3:1:3:1 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | Conversion1.c:3:1:3:1 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | PointerDereference.c:3:1:3:1 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 5 | 1 | 5 | | =5 | prvalue: int | Subscript.c:2:9:2:9 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | 5 | | =5 | prvalue: int | Conversion2.c:2:12:2:12 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | 7 | | =7 | prvalue: int | Conversion4.c:2:13:2:13 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | ... + ... | | | prvalue: int | Parenthesis.c:2:8:2:12 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | definition of s | | | S | ArrayToPointer.c:8:12:8:12 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 1 | 1 | return ... | | | | Conversion4.c:3:1:3:1 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 1 | 1 | return ... | | | | Subscript.c:3:1:3:1 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 1 | 2 | ExprStmt | | | | ArrayToPointer.c:9:3:9:13 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 5 | 1 | (int)... | integral conversion | =7 | prvalue: int | Conversion2.c:2:16:2:21 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | array | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | c | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | definition of j | | | int | Sizeof.c:3:7:3:7 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | i | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | x | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 7 | 1 | 1 | | =1 | prvalue: int | Parenthesis.c:2:12:2:12 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 8 | 0 | 7 | | =7 | prvalue: int | Conversion2.c:2:21:2:21 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 8 | 0 | ... = ... | | | prvalue: char * | ArrayToPointer.c:9:3:9:12 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 8 | 0 | initializer for j | | | | Sizeof.c:3:10:3:23 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 1 | 1 | return ... | | | | Conversion2.c:3:1:3:1 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 5 | 1 | 2 | | =2 | prvalue: int | Parenthesis.c:2:17:2:17 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 9 | 0 | (int)... | integral conversion | =8 | prvalue: int | Sizeof.c:3:11:3:23 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 9 | 0 | name | | | lvalue: char * | ArrayToPointer.c:9:5:9:8 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 11 | 1 | 1 | return ... | | | | Parenthesis.c:3:1:3:1 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 11 | 10 | 0 | sizeof() | | =8 | prvalue: unsigned long | Sizeof.c:3:11:3:23 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 11 | 10 | -1 | s | | | lvalue: S | ArrayToPointer.c:9:3:9:3 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 12 | 9 | 1 | array to pointer conversion | | | prvalue: char * | ArrayToPointer.c:9:12:9:12 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 12 | 11 | 0 | (...) | | | lvalue: int * | Sizeof.c:3:18:3:23 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | array | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | c | | | lvalue: char[6] | ArrayToPointer.c:9:12:9:12 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | c | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | i | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | x | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 14 | 1 | 2 | return ... | | | | Sizeof.c:4:1:4:1 | +| ArrayToPointer.c:5:6:5:6 | v(int[], int *, int, char *, void *, int) -> void | 14 | 1 | 3 | return ... | | | | ArrayToPointer.c:10:1:10:1 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | AddressOf.c:1:6:1:6 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | ArrayToPointer.c:5:6:5:6 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Cast.c:1:6:1:6 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Conversion1.c:1:6:1:6 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Conversion2.c:1:6:1:6 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Conversion4.c:1:6:1:6 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Parenthesis.c:1:6:1:6 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | PointerDereference.c:1:6:1:6 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Sizeof.c:1:6:1:6 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | StatementExpr.c:1:6:1:6 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Subscript.c:1:6:1:6 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | AddressOf.c:1:15:3:1 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | ArrayToPointer.c:6:1:10:1 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Cast.c:1:26:3:1 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Conversion1.c:1:10:3:1 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Conversion2.c:1:15:3:1 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Conversion4.c:1:15:3:1 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Parenthesis.c:1:15:3:1 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | PointerDereference.c:1:23:3:1 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Sizeof.c:1:21:4:1 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | StatementExpr.c:1:10:3:1 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Subscript.c:1:24:3:1 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Cast.c:2:3:2:16 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Conversion2.c:2:3:2:22 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Conversion4.c:2:3:2:15 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Parenthesis.c:2:3:2:18 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | PointerDereference.c:2:3:2:9 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Subscript.c:2:3:2:11 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | AddressOf.c:2:3:2:14 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | ArrayToPointer.c:7:3:7:21 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | Conversion1.c:2:3:2:17 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | Sizeof.c:2:3:2:22 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | StatementExpr.c:2:3:2:30 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: char * | Cast.c:2:3:2:15 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Conversion2.c:2:3:2:21 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Conversion4.c:2:3:2:14 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Parenthesis.c:2:3:2:17 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | PointerDereference.c:2:3:2:8 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Subscript.c:2:3:2:10 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of c | | | char[] | ArrayToPointer.c:7:8:7:8 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of i | | | int | Conversion1.c:2:7:2:7 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of i | | | int | Sizeof.c:2:7:2:7 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of j | | | int | StatementExpr.c:2:7:2:7 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of j | | | int * | AddressOf.c:2:8:2:8 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: char * | Cast.c:2:3:2:3 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: int | Conversion2.c:2:3:2:3 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: int | Conversion4.c:2:3:2:3 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: char * | Cast.c:2:3:2:3 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: int | Conversion2.c:2:3:2:3 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: int | Conversion4.c:2:3:2:3 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: char * | Cast.c:2:3:2:3 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: int | Conversion2.c:2:3:2:3 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: int | Conversion4.c:2:3:2:3 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for c | | | | ArrayToPointer.c:7:14:7:20 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for i | | | | Conversion1.c:2:10:2:16 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for i | | | | Sizeof.c:2:10:2:21 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for j | | | | AddressOf.c:2:11:2:13 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for j | | | | StatementExpr.c:2:10:2:29 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | j | | | lvalue: int | PointerDereference.c:2:3:2:3 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | j | | | lvalue: int | Subscript.c:2:3:2:3 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | v | | | lvalue: int | PointerDereference.c:2:3:2:3 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | v | | | lvalue: int | Subscript.c:2:3:2:3 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: char * | Cast.c:2:3:2:3 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: int | Conversion2.c:2:3:2:3 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: int | Conversion4.c:2:3:2:3 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | (...) | | =7 | prvalue: int | Conversion4.c:2:7:2:14 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | (char *)... | pointer conversion | | prvalue: char * | Cast.c:2:7:2:15 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | * ... | | | prvalue(load): int | PointerDereference.c:2:7:2:8 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | ... * ... | | | prvalue: int | Parenthesis.c:2:7:2:17 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | ... + ... | | =12 | prvalue: int | Conversion2.c:2:7:2:21 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | access to array | | | prvalue(load): int | Subscript.c:2:7:2:10 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | & ... | | | prvalue: int * | AddressOf.c:2:12:2:13 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | (int)... | integral conversion | =1 | prvalue: int | Conversion1.c:2:11:2:16 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | (int)... | integral conversion | =4 | prvalue: int | Sizeof.c:2:11:2:21 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | (statement expression) | | | prvalue: int | StatementExpr.c:2:11:2:29 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | hello | | =hello | lvalue: char[6] | ArrayToPointer.c:7:14:7:20 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 1 | 1 | declaration | | | | ArrayToPointer.c:8:3:8:13 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 1 | 1 | return ... | | | | StatementExpr.c:3:1:3:1 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | 1 | | =1 | prvalue: int | Conversion1.c:2:16:2:16 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | (...) | | | prvalue: int | Parenthesis.c:2:7:2:13 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | (int)... | integral conversion | =5 | prvalue: int | Conversion2.c:2:7:2:12 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | (int)... | integral conversion | =7 | prvalue: int | Conversion4.c:2:8:2:13 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | array | | | lvalue: int | AddressOf.c:2:13:2:13 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | array | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | array | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | c | | | lvalue: int | AddressOf.c:2:13:2:13 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | c | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | c | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | i | | | lvalue: int | AddressOf.c:2:13:2:13 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | i | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | i | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | j | | | prvalue(load): void * | Cast.c:2:15:2:15 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | sizeof(int) | | =4 | prvalue: unsigned long | Sizeof.c:2:11:2:21 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | v | | | prvalue(load): void * | Cast.c:2:15:2:15 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | x | | | lvalue: int | AddressOf.c:2:13:2:13 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | x | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | x | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | declaration | | | | Sizeof.c:3:3:3:24 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | AddressOf.c:3:1:3:1 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | Cast.c:3:1:3:1 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | Conversion1.c:3:1:3:1 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | PointerDereference.c:3:1:3:1 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 5 | 1 | 5 | | =5 | prvalue: int | Subscript.c:2:9:2:9 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | 5 | | =5 | prvalue: int | Conversion2.c:2:12:2:12 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | 7 | | =7 | prvalue: int | Conversion4.c:2:13:2:13 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | ... + ... | | | prvalue: int | Parenthesis.c:2:8:2:12 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | definition of s | | | S | ArrayToPointer.c:8:12:8:12 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 1 | 1 | return ... | | | | Conversion4.c:3:1:3:1 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 1 | 1 | return ... | | | | Subscript.c:3:1:3:1 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 1 | 2 | ExprStmt | | | | ArrayToPointer.c:9:3:9:13 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 5 | 1 | (int)... | integral conversion | =7 | prvalue: int | Conversion2.c:2:16:2:21 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | array | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | c | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | definition of j | | | int | Sizeof.c:3:7:3:7 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | i | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | x | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 7 | 1 | 1 | | =1 | prvalue: int | Parenthesis.c:2:12:2:12 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 8 | 0 | 7 | | =7 | prvalue: int | Conversion2.c:2:21:2:21 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 8 | 0 | ... = ... | | | prvalue: char * | ArrayToPointer.c:9:3:9:12 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 8 | 0 | initializer for j | | | | Sizeof.c:3:10:3:23 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 1 | 1 | return ... | | | | Conversion2.c:3:1:3:1 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 5 | 1 | 2 | | =2 | prvalue: int | Parenthesis.c:2:17:2:17 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 9 | 0 | (int)... | integral conversion | =8 | prvalue: int | Sizeof.c:3:11:3:23 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 9 | 0 | name | | | lvalue: char * | ArrayToPointer.c:9:5:9:8 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 11 | 1 | 1 | return ... | | | | Parenthesis.c:3:1:3:1 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 11 | 10 | 0 | sizeof() | | =8 | prvalue: unsigned long | Sizeof.c:3:11:3:23 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 11 | 10 | -1 | s | | | lvalue: S | ArrayToPointer.c:9:3:9:3 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 12 | 9 | 1 | array to pointer conversion | | | prvalue: char * | ArrayToPointer.c:9:12:9:12 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 12 | 11 | 0 | (...) | | | lvalue: int * | Sizeof.c:3:18:3:23 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | array | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | c | | | lvalue: char[6] | ArrayToPointer.c:9:12:9:12 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | c | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | i | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | x | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 14 | 1 | 2 | return ... | | | | Sizeof.c:4:1:4:1 | +| Cast.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 14 | 1 | 3 | return ... | | | | ArrayToPointer.c:10:1:10:1 | +| ConditionDecl.cpp:1:6:1:6 | v() -> void | 0 | -1 | 0 | v | | | | ConditionDecl.cpp:1:6:1:6 | +| ConditionDecl.cpp:1:6:1:6 | v() -> void | 1 | 0 | 0 | { ... } | | | | ConditionDecl.cpp:1:10:5:1 | +| ConditionDecl.cpp:1:6:1:6 | v() -> void | 2 | 1 | 0 | declaration | | | | ConditionDecl.cpp:2:3:2:12 | +| ConditionDecl.cpp:1:6:1:6 | v() -> void | 3 | 2 | 0 | definition of j | | | int | ConditionDecl.cpp:2:7:2:7 | +| ConditionDecl.cpp:1:6:1:6 | v() -> void | 4 | 3 | 0 | initializer for j | | | | ConditionDecl.cpp:2:10:2:11 | +| ConditionDecl.cpp:1:6:1:6 | v() -> void | 5 | 4 | 0 | 0 | | =0 | prvalue: int | ConditionDecl.cpp:2:10:2:11 | +| ConditionDecl.cpp:1:6:1:6 | v() -> void | 6 | 1 | 1 | while (...) ... | | | | ConditionDecl.cpp:3:3:4:3 | +| ConditionDecl.cpp:1:6:1:6 | v() -> void | 7 | 6 | 0 | (condition decl) | | | prvalue: bool | ConditionDecl.cpp:3:9:3:21 | +| ConditionDecl.cpp:1:6:1:6 | v() -> void | 8 | 7 | 0 | (bool)... | conversion to bool | | prvalue: bool | ConditionDecl.cpp:3:13:3:13 | +| ConditionDecl.cpp:1:6:1:6 | v() -> void | 9 | 8 | 0 | k | | | prvalue(load): int | ConditionDecl.cpp:3:13:3:13 | +| ConditionDecl.cpp:1:6:1:6 | v() -> void | 10 | 6 | 1 | { ... } | | | | ConditionDecl.cpp:3:24:4:3 | +| ConditionDecl.cpp:1:6:1:6 | v() -> void | 11 | 1 | 2 | return ... | | | | ConditionDecl.cpp:5:1:5:1 | +| ConstructorCall.cpp:1:7:1:7 | C::C(C &&) -> void | 0 | -1 | 0 | C | | | | ConstructorCall.cpp:1:7:1:7 | +| ConstructorCall.cpp:1:7:1:7 | C::C(const C &) -> void | 0 | -1 | 0 | C | | | | ConstructorCall.cpp:1:7:1:7 | +| ConstructorCall.cpp:1:7:1:7 | C::operator=(C &&) -> C & | 0 | -1 | 0 | operator= | | | | ConstructorCall.cpp:1:7:1:7 | +| ConstructorCall.cpp:1:7:1:7 | C::operator=(const C &) -> C & | 0 | -1 | 0 | operator= | | | | ConstructorCall.cpp:1:7:1:7 | +| ConstructorCall.cpp:1:7:1:7 | C::operator=(const C &) -> C & | 0 | -1 | 0 | operator= | | | | DestructorCall.cpp:1:7:1:7 | +| ConstructorCall.cpp:3:3:3:3 | C::C(int) -> void | 0 | -1 | 0 | C | | | | ConstructorCall.cpp:3:3:3:3 | +| ConstructorCall.cpp:3:3:3:3 | C::C(int) -> void | 1 | 0 | 0 | { ... } | | | | ConstructorCall.cpp:3:12:4:3 | +| ConstructorCall.cpp:3:3:3:3 | C::C(int) -> void | 2 | 1 | 0 | return ... | | | | ConstructorCall.cpp:4:3:4:3 | +| ConstructorCall.cpp:7:7:7:7 | D::D(D &&) -> void | 0 | -1 | 0 | D | | | | ConstructorCall.cpp:7:7:7:7 | +| ConstructorCall.cpp:7:7:7:7 | D::D(const D &) -> void | 0 | -1 | 0 | D | | | | ConstructorCall.cpp:7:7:7:7 | +| ConstructorCall.cpp:7:7:7:7 | D::operator=(D &&) -> D & | 0 | -1 | 0 | operator= | | | | ConstructorCall.cpp:7:7:7:7 | +| ConstructorCall.cpp:7:7:7:7 | D::operator=(D &&) -> D & | 0 | -1 | 0 | operator= | | | | DestructorCall.cpp:7:7:7:7 | +| ConstructorCall.cpp:7:7:7:7 | D::operator=(const D &) -> D & | 0 | -1 | 0 | operator= | | | | ConstructorCall.cpp:7:7:7:7 | +| ConstructorCall.cpp:7:7:7:7 | D::operator=(const D &) -> D & | 0 | -1 | 0 | operator= | | | | DestructorCall.cpp:7:7:7:7 | +| ConstructorCall.cpp:9:3:9:3 | D::D() -> void | 0 | -1 | 0 | D | | | | ConstructorCall.cpp:9:3:9:3 | +| ConstructorCall.cpp:9:3:9:3 | D::D() -> void | 1 | 0 | 0 | { ... } | | | | ConstructorCall.cpp:9:7:10:3 | +| ConstructorCall.cpp:9:3:9:3 | D::D() -> void | 2 | 1 | 0 | return ... | | | | ConstructorCall.cpp:10:3:10:3 | +| ConstructorCall.cpp:13:7:13:7 | E::operator=(E &&) -> E & | 0 | -1 | 0 | operator= | | | | ConstructorCall.cpp:13:7:13:7 | +| ConstructorCall.cpp:13:7:13:7 | E::operator=(E &&) -> E & | 0 | -1 | 0 | operator= | | | | Throw.cpp:1:7:1:7 | +| ConstructorCall.cpp:13:7:13:7 | E::operator=(const E &) -> E & | 0 | -1 | 0 | operator= | | | | ConstructorCall.cpp:13:7:13:7 | +| ConstructorCall.cpp:13:7:13:7 | E::operator=(const E &) -> E & | 0 | -1 | 0 | operator= | | | | Throw.cpp:1:7:1:7 | +| ConstructorCall.cpp:17:6:17:6 | v(C *, D *, E *) -> void | 0 | -1 | 0 | v | | | | ConstructorCall.cpp:17:6:17:6 | +| ConstructorCall.cpp:17:6:17:6 | v(C *, D *, E *) -> void | 1 | 0 | 0 | { ... } | | | | ConstructorCall.cpp:17:26:21:1 | +| ConstructorCall.cpp:17:6:17:6 | v(C *, D *, E *) -> void | 2 | 1 | 0 | ExprStmt | | | | ConstructorCall.cpp:18:3:18:15 | +| ConstructorCall.cpp:17:6:17:6 | v(C *, D *, E *) -> void | 3 | 2 | 0 | ... = ... | | | lvalue: C * | ConstructorCall.cpp:18:3:18:14 | +| ConstructorCall.cpp:17:6:17:6 | v(C *, D *, E *) -> void | 4 | 3 | 0 | c | | | lvalue: C * | ConstructorCall.cpp:18:3:18:3 | +| ConstructorCall.cpp:17:6:17:6 | v(C *, D *, E *) -> void | 5 | 3 | 1 | new | | | prvalue: C * | ConstructorCall.cpp:18:7:18:14 | +| ConstructorCall.cpp:17:6:17:6 | v(C *, D *, E *) -> void | 6 | 5 | 1 | call to C | | | prvalue: void | ConstructorCall.cpp:18:7:18:14 | +| ConstructorCall.cpp:17:6:17:6 | v(C *, D *, E *) -> void | 7 | 6 | 0 | 5 | | =5 | prvalue: int | ConstructorCall.cpp:18:13:18:13 | +| ConstructorCall.cpp:17:6:17:6 | v(C *, D *, E *) -> void | 8 | 1 | 1 | ExprStmt | | | | ConstructorCall.cpp:19:3:19:14 | +| ConstructorCall.cpp:17:6:17:6 | v(C *, D *, E *) -> void | 9 | 8 | 0 | ... = ... | | | lvalue: D * | ConstructorCall.cpp:19:3:19:13 | +| ConstructorCall.cpp:17:6:17:6 | v(C *, D *, E *) -> void | 10 | 9 | 0 | d | | | lvalue: D * | ConstructorCall.cpp:19:3:19:3 | +| ConstructorCall.cpp:17:6:17:6 | v(C *, D *, E *) -> void | 11 | 9 | 1 | new | | | prvalue: D * | ConstructorCall.cpp:19:7:19:13 | +| ConstructorCall.cpp:17:6:17:6 | v(C *, D *, E *) -> void | 12 | 11 | 1 | call to D | | | prvalue: void | ConstructorCall.cpp:19:7:19:13 | +| ConstructorCall.cpp:17:6:17:6 | v(C *, D *, E *) -> void | 13 | 1 | 2 | ExprStmt | | | | ConstructorCall.cpp:20:3:20:14 | +| ConstructorCall.cpp:17:6:17:6 | v(C *, D *, E *) -> void | 14 | 13 | 0 | ... = ... | | | lvalue: E * | ConstructorCall.cpp:20:3:20:13 | +| ConstructorCall.cpp:17:6:17:6 | v(C *, D *, E *) -> void | 15 | 14 | 0 | e | | | lvalue: E * | ConstructorCall.cpp:20:3:20:3 | +| ConstructorCall.cpp:17:6:17:6 | v(C *, D *, E *) -> void | 16 | 14 | 1 | new | | | prvalue: E * | ConstructorCall.cpp:20:7:20:13 | +| ConstructorCall.cpp:17:6:17:6 | v(C *, D *, E *) -> void | 17 | 16 | 1 | 0 | | =0 | prvalue: E | ConstructorCall.cpp:20:7:20:13 | +| ConstructorCall.cpp:17:6:17:6 | v(C *, D *, E *) -> void | 18 | 1 | 3 | return ... | | | | ConstructorCall.cpp:21:1:21:1 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | AddressOf.c:1:6:1:6 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | ArrayToPointer.c:5:6:5:6 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Cast.c:1:6:1:6 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Conversion1.c:1:6:1:6 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Conversion2.c:1:6:1:6 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Conversion4.c:1:6:1:6 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Parenthesis.c:1:6:1:6 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | PointerDereference.c:1:6:1:6 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Sizeof.c:1:6:1:6 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | StatementExpr.c:1:6:1:6 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Subscript.c:1:6:1:6 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | AddressOf.c:1:15:3:1 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | ArrayToPointer.c:6:1:10:1 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Cast.c:1:26:3:1 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Conversion1.c:1:10:3:1 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Conversion2.c:1:15:3:1 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Conversion4.c:1:15:3:1 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Parenthesis.c:1:15:3:1 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | PointerDereference.c:1:23:3:1 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Sizeof.c:1:21:4:1 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | StatementExpr.c:1:10:3:1 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Subscript.c:1:24:3:1 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Cast.c:2:3:2:16 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Conversion2.c:2:3:2:22 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Conversion4.c:2:3:2:15 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Parenthesis.c:2:3:2:18 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | PointerDereference.c:2:3:2:9 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Subscript.c:2:3:2:11 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | AddressOf.c:2:3:2:14 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | ArrayToPointer.c:7:3:7:21 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | Conversion1.c:2:3:2:17 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | Sizeof.c:2:3:2:22 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | StatementExpr.c:2:3:2:30 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: char * | Cast.c:2:3:2:15 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Conversion2.c:2:3:2:21 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Conversion4.c:2:3:2:14 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Parenthesis.c:2:3:2:17 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | PointerDereference.c:2:3:2:8 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Subscript.c:2:3:2:10 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of c | | | char[] | ArrayToPointer.c:7:8:7:8 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of i | | | int | Conversion1.c:2:7:2:7 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of i | | | int | Sizeof.c:2:7:2:7 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of j | | | int | StatementExpr.c:2:7:2:7 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of j | | | int * | AddressOf.c:2:8:2:8 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: char * | Cast.c:2:3:2:3 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: int | Conversion2.c:2:3:2:3 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: int | Conversion4.c:2:3:2:3 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: char * | Cast.c:2:3:2:3 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: int | Conversion2.c:2:3:2:3 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: int | Conversion4.c:2:3:2:3 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: char * | Cast.c:2:3:2:3 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: int | Conversion2.c:2:3:2:3 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: int | Conversion4.c:2:3:2:3 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for c | | | | ArrayToPointer.c:7:14:7:20 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for i | | | | Conversion1.c:2:10:2:16 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for i | | | | Sizeof.c:2:10:2:21 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for j | | | | AddressOf.c:2:11:2:13 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for j | | | | StatementExpr.c:2:10:2:29 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | j | | | lvalue: int | PointerDereference.c:2:3:2:3 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | j | | | lvalue: int | Subscript.c:2:3:2:3 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | v | | | lvalue: int | PointerDereference.c:2:3:2:3 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | v | | | lvalue: int | Subscript.c:2:3:2:3 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: char * | Cast.c:2:3:2:3 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: int | Conversion2.c:2:3:2:3 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: int | Conversion4.c:2:3:2:3 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | (...) | | =7 | prvalue: int | Conversion4.c:2:7:2:14 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | (char *)... | pointer conversion | | prvalue: char * | Cast.c:2:7:2:15 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | * ... | | | prvalue(load): int | PointerDereference.c:2:7:2:8 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | ... * ... | | | prvalue: int | Parenthesis.c:2:7:2:17 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | ... + ... | | =12 | prvalue: int | Conversion2.c:2:7:2:21 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | access to array | | | prvalue(load): int | Subscript.c:2:7:2:10 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | & ... | | | prvalue: int * | AddressOf.c:2:12:2:13 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | (int)... | integral conversion | =1 | prvalue: int | Conversion1.c:2:11:2:16 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | (int)... | integral conversion | =4 | prvalue: int | Sizeof.c:2:11:2:21 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | (statement expression) | | | prvalue: int | StatementExpr.c:2:11:2:29 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | hello | | =hello | lvalue: char[6] | ArrayToPointer.c:7:14:7:20 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 1 | 1 | declaration | | | | ArrayToPointer.c:8:3:8:13 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 1 | 1 | return ... | | | | StatementExpr.c:3:1:3:1 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | 1 | | =1 | prvalue: int | Conversion1.c:2:16:2:16 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | (...) | | | prvalue: int | Parenthesis.c:2:7:2:13 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | (int)... | integral conversion | =5 | prvalue: int | Conversion2.c:2:7:2:12 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | (int)... | integral conversion | =7 | prvalue: int | Conversion4.c:2:8:2:13 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | array | | | lvalue: int | AddressOf.c:2:13:2:13 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | array | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | array | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | c | | | lvalue: int | AddressOf.c:2:13:2:13 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | c | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | c | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | i | | | lvalue: int | AddressOf.c:2:13:2:13 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | i | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | i | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | j | | | prvalue(load): void * | Cast.c:2:15:2:15 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | sizeof(int) | | =4 | prvalue: unsigned long | Sizeof.c:2:11:2:21 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | v | | | prvalue(load): void * | Cast.c:2:15:2:15 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | x | | | lvalue: int | AddressOf.c:2:13:2:13 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | x | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | x | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | declaration | | | | Sizeof.c:3:3:3:24 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | AddressOf.c:3:1:3:1 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | Cast.c:3:1:3:1 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | Conversion1.c:3:1:3:1 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | PointerDereference.c:3:1:3:1 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 5 | 1 | 5 | | =5 | prvalue: int | Subscript.c:2:9:2:9 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | 5 | | =5 | prvalue: int | Conversion2.c:2:12:2:12 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | 7 | | =7 | prvalue: int | Conversion4.c:2:13:2:13 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | ... + ... | | | prvalue: int | Parenthesis.c:2:8:2:12 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | definition of s | | | S | ArrayToPointer.c:8:12:8:12 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 1 | 1 | return ... | | | | Conversion4.c:3:1:3:1 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 1 | 1 | return ... | | | | Subscript.c:3:1:3:1 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 1 | 2 | ExprStmt | | | | ArrayToPointer.c:9:3:9:13 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 5 | 1 | (int)... | integral conversion | =7 | prvalue: int | Conversion2.c:2:16:2:21 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | array | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | c | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | definition of j | | | int | Sizeof.c:3:7:3:7 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | i | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | x | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 7 | 1 | 1 | | =1 | prvalue: int | Parenthesis.c:2:12:2:12 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 8 | 0 | 7 | | =7 | prvalue: int | Conversion2.c:2:21:2:21 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 8 | 0 | ... = ... | | | prvalue: char * | ArrayToPointer.c:9:3:9:12 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 8 | 0 | initializer for j | | | | Sizeof.c:3:10:3:23 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 1 | 1 | return ... | | | | Conversion2.c:3:1:3:1 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 5 | 1 | 2 | | =2 | prvalue: int | Parenthesis.c:2:17:2:17 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 9 | 0 | (int)... | integral conversion | =8 | prvalue: int | Sizeof.c:3:11:3:23 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 9 | 0 | name | | | lvalue: char * | ArrayToPointer.c:9:5:9:8 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 11 | 1 | 1 | return ... | | | | Parenthesis.c:3:1:3:1 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 11 | 10 | 0 | sizeof() | | =8 | prvalue: unsigned long | Sizeof.c:3:11:3:23 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 11 | 10 | -1 | s | | | lvalue: S | ArrayToPointer.c:9:3:9:3 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 12 | 9 | 1 | array to pointer conversion | | | prvalue: char * | ArrayToPointer.c:9:12:9:12 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 12 | 11 | 0 | (...) | | | lvalue: int * | Sizeof.c:3:18:3:23 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | array | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | c | | | lvalue: char[6] | ArrayToPointer.c:9:12:9:12 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | c | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | i | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | x | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 14 | 1 | 2 | return ... | | | | Sizeof.c:4:1:4:1 | +| Conversion1.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 14 | 1 | 3 | return ... | | | | ArrayToPointer.c:10:1:10:1 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | AddressOf.c:1:6:1:6 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | ArrayToPointer.c:5:6:5:6 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Cast.c:1:6:1:6 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Conversion1.c:1:6:1:6 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Conversion2.c:1:6:1:6 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Conversion4.c:1:6:1:6 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Parenthesis.c:1:6:1:6 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | PointerDereference.c:1:6:1:6 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Sizeof.c:1:6:1:6 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | StatementExpr.c:1:6:1:6 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Subscript.c:1:6:1:6 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | AddressOf.c:1:15:3:1 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | ArrayToPointer.c:6:1:10:1 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Cast.c:1:26:3:1 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Conversion1.c:1:10:3:1 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Conversion2.c:1:15:3:1 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Conversion4.c:1:15:3:1 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Parenthesis.c:1:15:3:1 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | PointerDereference.c:1:23:3:1 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Sizeof.c:1:21:4:1 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | StatementExpr.c:1:10:3:1 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Subscript.c:1:24:3:1 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Cast.c:2:3:2:16 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Conversion2.c:2:3:2:22 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Conversion4.c:2:3:2:15 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Parenthesis.c:2:3:2:18 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | PointerDereference.c:2:3:2:9 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Subscript.c:2:3:2:11 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | AddressOf.c:2:3:2:14 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | ArrayToPointer.c:7:3:7:21 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | Conversion1.c:2:3:2:17 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | Sizeof.c:2:3:2:22 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | StatementExpr.c:2:3:2:30 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: char * | Cast.c:2:3:2:15 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Conversion2.c:2:3:2:21 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Conversion4.c:2:3:2:14 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Parenthesis.c:2:3:2:17 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | PointerDereference.c:2:3:2:8 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Subscript.c:2:3:2:10 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of c | | | char[] | ArrayToPointer.c:7:8:7:8 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of i | | | int | Conversion1.c:2:7:2:7 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of i | | | int | Sizeof.c:2:7:2:7 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of j | | | int | StatementExpr.c:2:7:2:7 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of j | | | int * | AddressOf.c:2:8:2:8 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: char * | Cast.c:2:3:2:3 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: int | Conversion2.c:2:3:2:3 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: int | Conversion4.c:2:3:2:3 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: char * | Cast.c:2:3:2:3 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: int | Conversion2.c:2:3:2:3 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: int | Conversion4.c:2:3:2:3 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: char * | Cast.c:2:3:2:3 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: int | Conversion2.c:2:3:2:3 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: int | Conversion4.c:2:3:2:3 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for c | | | | ArrayToPointer.c:7:14:7:20 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for i | | | | Conversion1.c:2:10:2:16 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for i | | | | Sizeof.c:2:10:2:21 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for j | | | | AddressOf.c:2:11:2:13 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for j | | | | StatementExpr.c:2:10:2:29 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | j | | | lvalue: int | PointerDereference.c:2:3:2:3 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | j | | | lvalue: int | Subscript.c:2:3:2:3 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | v | | | lvalue: int | PointerDereference.c:2:3:2:3 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | v | | | lvalue: int | Subscript.c:2:3:2:3 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: char * | Cast.c:2:3:2:3 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: int | Conversion2.c:2:3:2:3 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: int | Conversion4.c:2:3:2:3 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | (...) | | =7 | prvalue: int | Conversion4.c:2:7:2:14 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | (char *)... | pointer conversion | | prvalue: char * | Cast.c:2:7:2:15 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | * ... | | | prvalue(load): int | PointerDereference.c:2:7:2:8 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | ... * ... | | | prvalue: int | Parenthesis.c:2:7:2:17 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | ... + ... | | =12 | prvalue: int | Conversion2.c:2:7:2:21 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | access to array | | | prvalue(load): int | Subscript.c:2:7:2:10 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | & ... | | | prvalue: int * | AddressOf.c:2:12:2:13 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | (int)... | integral conversion | =1 | prvalue: int | Conversion1.c:2:11:2:16 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | (int)... | integral conversion | =4 | prvalue: int | Sizeof.c:2:11:2:21 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | (statement expression) | | | prvalue: int | StatementExpr.c:2:11:2:29 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | hello | | =hello | lvalue: char[6] | ArrayToPointer.c:7:14:7:20 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 1 | 1 | declaration | | | | ArrayToPointer.c:8:3:8:13 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 1 | 1 | return ... | | | | StatementExpr.c:3:1:3:1 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | 1 | | =1 | prvalue: int | Conversion1.c:2:16:2:16 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | (...) | | | prvalue: int | Parenthesis.c:2:7:2:13 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | (int)... | integral conversion | =5 | prvalue: int | Conversion2.c:2:7:2:12 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | (int)... | integral conversion | =7 | prvalue: int | Conversion4.c:2:8:2:13 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | array | | | lvalue: int | AddressOf.c:2:13:2:13 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | array | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | array | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | c | | | lvalue: int | AddressOf.c:2:13:2:13 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | c | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | c | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | i | | | lvalue: int | AddressOf.c:2:13:2:13 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | i | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | i | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | j | | | prvalue(load): void * | Cast.c:2:15:2:15 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | sizeof(int) | | =4 | prvalue: unsigned long | Sizeof.c:2:11:2:21 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | v | | | prvalue(load): void * | Cast.c:2:15:2:15 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | x | | | lvalue: int | AddressOf.c:2:13:2:13 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | x | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | x | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | declaration | | | | Sizeof.c:3:3:3:24 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | AddressOf.c:3:1:3:1 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | Cast.c:3:1:3:1 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | Conversion1.c:3:1:3:1 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | PointerDereference.c:3:1:3:1 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 5 | 1 | 5 | | =5 | prvalue: int | Subscript.c:2:9:2:9 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | 5 | | =5 | prvalue: int | Conversion2.c:2:12:2:12 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | 7 | | =7 | prvalue: int | Conversion4.c:2:13:2:13 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | ... + ... | | | prvalue: int | Parenthesis.c:2:8:2:12 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | definition of s | | | S | ArrayToPointer.c:8:12:8:12 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 1 | 1 | return ... | | | | Conversion4.c:3:1:3:1 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 1 | 1 | return ... | | | | Subscript.c:3:1:3:1 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 1 | 2 | ExprStmt | | | | ArrayToPointer.c:9:3:9:13 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 5 | 1 | (int)... | integral conversion | =7 | prvalue: int | Conversion2.c:2:16:2:21 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | array | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | c | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | definition of j | | | int | Sizeof.c:3:7:3:7 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | i | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | x | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 7 | 1 | 1 | | =1 | prvalue: int | Parenthesis.c:2:12:2:12 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 8 | 0 | 7 | | =7 | prvalue: int | Conversion2.c:2:21:2:21 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 8 | 0 | ... = ... | | | prvalue: char * | ArrayToPointer.c:9:3:9:12 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 8 | 0 | initializer for j | | | | Sizeof.c:3:10:3:23 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 1 | 1 | return ... | | | | Conversion2.c:3:1:3:1 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 5 | 1 | 2 | | =2 | prvalue: int | Parenthesis.c:2:17:2:17 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 9 | 0 | (int)... | integral conversion | =8 | prvalue: int | Sizeof.c:3:11:3:23 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 9 | 0 | name | | | lvalue: char * | ArrayToPointer.c:9:5:9:8 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 11 | 1 | 1 | return ... | | | | Parenthesis.c:3:1:3:1 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 11 | 10 | 0 | sizeof() | | =8 | prvalue: unsigned long | Sizeof.c:3:11:3:23 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 11 | 10 | -1 | s | | | lvalue: S | ArrayToPointer.c:9:3:9:3 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 12 | 9 | 1 | array to pointer conversion | | | prvalue: char * | ArrayToPointer.c:9:12:9:12 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 12 | 11 | 0 | (...) | | | lvalue: int * | Sizeof.c:3:18:3:23 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | array | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | c | | | lvalue: char[6] | ArrayToPointer.c:9:12:9:12 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | c | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | i | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | x | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 14 | 1 | 2 | return ... | | | | Sizeof.c:4:1:4:1 | +| Conversion2.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 14 | 1 | 3 | return ... | | | | ArrayToPointer.c:10:1:10:1 | +| Conversion3.cpp:1:6:1:6 | v(int) -> void | 0 | -1 | 0 | v | | | | Conversion3.cpp:1:6:1:6 | +| Conversion3.cpp:1:6:1:6 | v(int) -> void | 1 | 0 | 0 | { ... } | | | | Conversion3.cpp:1:15:3:1 | +| Conversion3.cpp:1:6:1:6 | v(int) -> void | 2 | 1 | 0 | ExprStmt | | | | Conversion3.cpp:2:3:2:30 | +| Conversion3.cpp:1:6:1:6 | v(int) -> void | 3 | 2 | 0 | ... = ... | | | lvalue: int | Conversion3.cpp:2:3:2:29 | +| Conversion3.cpp:1:6:1:6 | v(int) -> void | 4 | 3 | 0 | x | | | lvalue: int | Conversion3.cpp:2:3:2:3 | +| Conversion3.cpp:1:6:1:6 | v(int) -> void | 5 | 3 | 1 | ... + ... | | =8 | prvalue: int | Conversion3.cpp:2:7:2:29 | +| Conversion3.cpp:1:6:1:6 | v(int) -> void | 6 | 5 | 0 | (int)... | integral conversion | =1 | prvalue: int | Conversion3.cpp:2:7:2:18 | +| Conversion3.cpp:1:6:1:6 | v(int) -> void | 7 | 6 | 0 | (bool)... | conversion to bool | =1 | prvalue: bool | Conversion3.cpp:2:7:2:18 | +| Conversion3.cpp:1:6:1:6 | v(int) -> void | 8 | 7 | 0 | (int)... | integral conversion | =1 | prvalue: int | Conversion3.cpp:2:13:2:18 | +| Conversion3.cpp:1:6:1:6 | v(int) -> void | 9 | 8 | 0 | 5 | | =5 | prvalue: int | Conversion3.cpp:2:18:2:18 | +| Conversion3.cpp:1:6:1:6 | v(int) -> void | 10 | 5 | 1 | (...) | | =7 | prvalue: int | Conversion3.cpp:2:22:2:29 | +| Conversion3.cpp:1:6:1:6 | v(int) -> void | 11 | 10 | 0 | (int)... | integral conversion | =7 | prvalue: int | Conversion3.cpp:2:23:2:28 | +| Conversion3.cpp:1:6:1:6 | v(int) -> void | 12 | 11 | 0 | 7 | | =7 | prvalue: int | Conversion3.cpp:2:28:2:28 | +| Conversion3.cpp:1:6:1:6 | v(int) -> void | 13 | 1 | 1 | return ... | | | | Conversion3.cpp:3:1:3:1 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | AddressOf.c:1:6:1:6 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | ArrayToPointer.c:5:6:5:6 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Cast.c:1:6:1:6 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Conversion1.c:1:6:1:6 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Conversion2.c:1:6:1:6 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Conversion4.c:1:6:1:6 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Parenthesis.c:1:6:1:6 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | PointerDereference.c:1:6:1:6 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Sizeof.c:1:6:1:6 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | StatementExpr.c:1:6:1:6 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Subscript.c:1:6:1:6 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | AddressOf.c:1:15:3:1 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | ArrayToPointer.c:6:1:10:1 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Cast.c:1:26:3:1 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Conversion1.c:1:10:3:1 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Conversion2.c:1:15:3:1 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Conversion4.c:1:15:3:1 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Parenthesis.c:1:15:3:1 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | PointerDereference.c:1:23:3:1 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Sizeof.c:1:21:4:1 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | StatementExpr.c:1:10:3:1 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Subscript.c:1:24:3:1 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Cast.c:2:3:2:16 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Conversion2.c:2:3:2:22 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Conversion4.c:2:3:2:15 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Parenthesis.c:2:3:2:18 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | PointerDereference.c:2:3:2:9 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Subscript.c:2:3:2:11 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | AddressOf.c:2:3:2:14 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | ArrayToPointer.c:7:3:7:21 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | Conversion1.c:2:3:2:17 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | Sizeof.c:2:3:2:22 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | StatementExpr.c:2:3:2:30 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: char * | Cast.c:2:3:2:15 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Conversion2.c:2:3:2:21 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Conversion4.c:2:3:2:14 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Parenthesis.c:2:3:2:17 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | PointerDereference.c:2:3:2:8 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Subscript.c:2:3:2:10 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of c | | | char[] | ArrayToPointer.c:7:8:7:8 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of i | | | int | Conversion1.c:2:7:2:7 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of i | | | int | Sizeof.c:2:7:2:7 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of j | | | int | StatementExpr.c:2:7:2:7 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of j | | | int * | AddressOf.c:2:8:2:8 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: char * | Cast.c:2:3:2:3 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: int | Conversion2.c:2:3:2:3 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: int | Conversion4.c:2:3:2:3 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: char * | Cast.c:2:3:2:3 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: int | Conversion2.c:2:3:2:3 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: int | Conversion4.c:2:3:2:3 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: char * | Cast.c:2:3:2:3 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: int | Conversion2.c:2:3:2:3 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: int | Conversion4.c:2:3:2:3 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for c | | | | ArrayToPointer.c:7:14:7:20 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for i | | | | Conversion1.c:2:10:2:16 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for i | | | | Sizeof.c:2:10:2:21 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for j | | | | AddressOf.c:2:11:2:13 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for j | | | | StatementExpr.c:2:10:2:29 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | j | | | lvalue: int | PointerDereference.c:2:3:2:3 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | j | | | lvalue: int | Subscript.c:2:3:2:3 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | v | | | lvalue: int | PointerDereference.c:2:3:2:3 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | v | | | lvalue: int | Subscript.c:2:3:2:3 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: char * | Cast.c:2:3:2:3 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: int | Conversion2.c:2:3:2:3 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: int | Conversion4.c:2:3:2:3 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | (...) | | =7 | prvalue: int | Conversion4.c:2:7:2:14 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | (char *)... | pointer conversion | | prvalue: char * | Cast.c:2:7:2:15 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | * ... | | | prvalue(load): int | PointerDereference.c:2:7:2:8 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | ... * ... | | | prvalue: int | Parenthesis.c:2:7:2:17 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | ... + ... | | =12 | prvalue: int | Conversion2.c:2:7:2:21 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | access to array | | | prvalue(load): int | Subscript.c:2:7:2:10 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | & ... | | | prvalue: int * | AddressOf.c:2:12:2:13 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | (int)... | integral conversion | =1 | prvalue: int | Conversion1.c:2:11:2:16 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | (int)... | integral conversion | =4 | prvalue: int | Sizeof.c:2:11:2:21 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | (statement expression) | | | prvalue: int | StatementExpr.c:2:11:2:29 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | hello | | =hello | lvalue: char[6] | ArrayToPointer.c:7:14:7:20 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 1 | 1 | declaration | | | | ArrayToPointer.c:8:3:8:13 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 1 | 1 | return ... | | | | StatementExpr.c:3:1:3:1 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | 1 | | =1 | prvalue: int | Conversion1.c:2:16:2:16 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | (...) | | | prvalue: int | Parenthesis.c:2:7:2:13 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | (int)... | integral conversion | =5 | prvalue: int | Conversion2.c:2:7:2:12 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | (int)... | integral conversion | =7 | prvalue: int | Conversion4.c:2:8:2:13 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | array | | | lvalue: int | AddressOf.c:2:13:2:13 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | array | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | array | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | c | | | lvalue: int | AddressOf.c:2:13:2:13 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | c | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | c | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | i | | | lvalue: int | AddressOf.c:2:13:2:13 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | i | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | i | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | j | | | prvalue(load): void * | Cast.c:2:15:2:15 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | sizeof(int) | | =4 | prvalue: unsigned long | Sizeof.c:2:11:2:21 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | v | | | prvalue(load): void * | Cast.c:2:15:2:15 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | x | | | lvalue: int | AddressOf.c:2:13:2:13 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | x | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | x | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | declaration | | | | Sizeof.c:3:3:3:24 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | AddressOf.c:3:1:3:1 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | Cast.c:3:1:3:1 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | Conversion1.c:3:1:3:1 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | PointerDereference.c:3:1:3:1 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 5 | 1 | 5 | | =5 | prvalue: int | Subscript.c:2:9:2:9 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | 5 | | =5 | prvalue: int | Conversion2.c:2:12:2:12 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | 7 | | =7 | prvalue: int | Conversion4.c:2:13:2:13 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | ... + ... | | | prvalue: int | Parenthesis.c:2:8:2:12 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | definition of s | | | S | ArrayToPointer.c:8:12:8:12 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 1 | 1 | return ... | | | | Conversion4.c:3:1:3:1 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 1 | 1 | return ... | | | | Subscript.c:3:1:3:1 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 1 | 2 | ExprStmt | | | | ArrayToPointer.c:9:3:9:13 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 5 | 1 | (int)... | integral conversion | =7 | prvalue: int | Conversion2.c:2:16:2:21 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | array | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | c | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | definition of j | | | int | Sizeof.c:3:7:3:7 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | i | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | x | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 7 | 1 | 1 | | =1 | prvalue: int | Parenthesis.c:2:12:2:12 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 8 | 0 | 7 | | =7 | prvalue: int | Conversion2.c:2:21:2:21 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 8 | 0 | ... = ... | | | prvalue: char * | ArrayToPointer.c:9:3:9:12 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 8 | 0 | initializer for j | | | | Sizeof.c:3:10:3:23 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 1 | 1 | return ... | | | | Conversion2.c:3:1:3:1 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 5 | 1 | 2 | | =2 | prvalue: int | Parenthesis.c:2:17:2:17 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 9 | 0 | (int)... | integral conversion | =8 | prvalue: int | Sizeof.c:3:11:3:23 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 9 | 0 | name | | | lvalue: char * | ArrayToPointer.c:9:5:9:8 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 11 | 1 | 1 | return ... | | | | Parenthesis.c:3:1:3:1 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 11 | 10 | 0 | sizeof() | | =8 | prvalue: unsigned long | Sizeof.c:3:11:3:23 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 11 | 10 | -1 | s | | | lvalue: S | ArrayToPointer.c:9:3:9:3 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 12 | 9 | 1 | array to pointer conversion | | | prvalue: char * | ArrayToPointer.c:9:12:9:12 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 12 | 11 | 0 | (...) | | | lvalue: int * | Sizeof.c:3:18:3:23 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | array | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | c | | | lvalue: char[6] | ArrayToPointer.c:9:12:9:12 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | c | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | i | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | x | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 14 | 1 | 2 | return ... | | | | Sizeof.c:4:1:4:1 | +| Conversion4.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 14 | 1 | 3 | return ... | | | | ArrayToPointer.c:10:1:10:1 | +| DestructorCall.cpp:1:7:1:7 | C::operator=(const C &) -> C & | 0 | -1 | 0 | operator= | | | | ConstructorCall.cpp:1:7:1:7 | +| DestructorCall.cpp:1:7:1:7 | C::operator=(const C &) -> C & | 0 | -1 | 0 | operator= | | | | DestructorCall.cpp:1:7:1:7 | +| DestructorCall.cpp:3:3:3:4 | C::~C() -> void | 0 | -1 | 0 | ~C | | | | DestructorCall.cpp:3:3:3:4 | +| DestructorCall.cpp:3:3:3:4 | C::~C() -> void | 1 | 0 | 0 | { ... } | | | | DestructorCall.cpp:3:8:4:3 | +| DestructorCall.cpp:3:3:3:4 | C::~C() -> void | 2 | 1 | 0 | return ... | | | | DestructorCall.cpp:4:3:4:3 | +| DestructorCall.cpp:7:7:7:7 | D::operator=(D &&) -> D & | 0 | -1 | 0 | operator= | | | | ConstructorCall.cpp:7:7:7:7 | +| DestructorCall.cpp:7:7:7:7 | D::operator=(D &&) -> D & | 0 | -1 | 0 | operator= | | | | DestructorCall.cpp:7:7:7:7 | +| DestructorCall.cpp:7:7:7:7 | D::operator=(const D &) -> D & | 0 | -1 | 0 | operator= | | | | ConstructorCall.cpp:7:7:7:7 | +| DestructorCall.cpp:7:7:7:7 | D::operator=(const D &) -> D & | 0 | -1 | 0 | operator= | | | | DestructorCall.cpp:7:7:7:7 | +| DestructorCall.cpp:11:6:11:6 | v(C *, D *) -> void | 0 | -1 | 0 | v | | | | DestructorCall.cpp:11:6:11:6 | +| DestructorCall.cpp:11:6:11:6 | v(C *, D *) -> void | 1 | 0 | 0 | { ... } | | | | DestructorCall.cpp:11:20:14:1 | +| DestructorCall.cpp:11:6:11:6 | v(C *, D *) -> void | 2 | 1 | 0 | ExprStmt | | | | DestructorCall.cpp:12:3:12:11 | +| DestructorCall.cpp:11:6:11:6 | v(C *, D *) -> void | 3 | 2 | 0 | delete | | | prvalue: void | DestructorCall.cpp:12:3:12:10 | +| DestructorCall.cpp:11:6:11:6 | v(C *, D *) -> void | 4 | 3 | 1 | call to ~C | | | prvalue: void | DestructorCall.cpp:12:3:12:10 | +| DestructorCall.cpp:11:6:11:6 | v(C *, D *) -> void | 5 | 4 | -1 | c | | | prvalue(load): C * | DestructorCall.cpp:12:10:12:10 | +| DestructorCall.cpp:11:6:11:6 | v(C *, D *) -> void | 6 | 1 | 1 | ExprStmt | | | | DestructorCall.cpp:13:3:13:11 | +| DestructorCall.cpp:11:6:11:6 | v(C *, D *) -> void | 7 | 6 | 0 | delete | | | prvalue: void | DestructorCall.cpp:13:3:13:10 | +| DestructorCall.cpp:11:6:11:6 | v(C *, D *) -> void | 8 | 7 | 3 | d | | | prvalue(load): D * | DestructorCall.cpp:13:10:13:10 | +| DestructorCall.cpp:11:6:11:6 | v(C *, D *) -> void | 9 | 1 | 2 | return ... | | | | DestructorCall.cpp:14:1:14:1 | +| DynamicCast.cpp:1:7:1:7 | Base::Base() -> void | 0 | -1 | 0 | Base | | | | DynamicCast.cpp:1:7:1:7 | +| DynamicCast.cpp:1:7:1:7 | Base::Base() -> void | 0 | -1 | 0 | Base | | | | Typeid.cpp:11:7:11:7 | +| DynamicCast.cpp:1:7:1:7 | Base::Base(Base &&) -> void | 0 | -1 | 0 | Base | | | | DynamicCast.cpp:1:7:1:7 | +| DynamicCast.cpp:1:7:1:7 | Base::Base(Base &&) -> void | 0 | -1 | 0 | Base | | | | Typeid.cpp:11:7:11:7 | +| DynamicCast.cpp:1:7:1:7 | Base::Base(const Base &) -> void | 0 | -1 | 0 | Base | | | | DynamicCast.cpp:1:7:1:7 | +| DynamicCast.cpp:1:7:1:7 | Base::Base(const Base &) -> void | 0 | -1 | 0 | Base | | | | Typeid.cpp:11:7:11:7 | +| DynamicCast.cpp:1:7:1:7 | Base::operator=(Base &&) -> Base & | 0 | -1 | 0 | operator= | | | | DynamicCast.cpp:1:7:1:7 | +| DynamicCast.cpp:1:7:1:7 | Base::operator=(Base &&) -> Base & | 0 | -1 | 0 | operator= | | | | Typeid.cpp:11:7:11:7 | +| DynamicCast.cpp:1:7:1:7 | Base::operator=(const Base &) -> Base & | 0 | -1 | 0 | operator= | | | | DynamicCast.cpp:1:7:1:7 | +| DynamicCast.cpp:1:7:1:7 | Base::operator=(const Base &) -> Base & | 1 | 0 | 0 | { ... } | | | | file://:0:0:0:0 | +| DynamicCast.cpp:1:7:1:7 | Base::operator=(const Base &) -> Base & | 2 | 1 | 0 | return ... | | | | file://:0:0:0:0 | +| DynamicCast.cpp:1:7:1:7 | Base::operator=(const Base &) -> Base & | 3 | 2 | 0 | (reference to) | | | prvalue: Base & | file://:0:0:0:0 | +| DynamicCast.cpp:1:7:1:7 | Base::operator=(const Base &) -> Base & | 4 | 3 | 0 | * ... | | | lvalue: Base | file://:0:0:0:0 | +| DynamicCast.cpp:1:7:1:7 | Base::operator=(const Base &) -> Base & | 5 | 4 | 0 | this | | | prvalue(load): Base * | file://:0:0:0:0 | +| DynamicCast.cpp:2:16:2:16 | Base::f() -> void | 0 | -1 | 0 | f | | | | DynamicCast.cpp:2:16:2:16 | +| DynamicCast.cpp:2:16:2:16 | Base::f() -> void | 1 | 0 | 0 | { ... } | | | | DynamicCast.cpp:2:20:2:22 | +| DynamicCast.cpp:2:16:2:16 | Base::f() -> void | 2 | 1 | 0 | return ... | | | | DynamicCast.cpp:2:22:2:22 | +| DynamicCast.cpp:4:7:4:7 | Derived::Derived() -> void | 0 | -1 | 0 | Derived | | | | DynamicCast.cpp:4:7:4:7 | +| DynamicCast.cpp:4:7:4:7 | Derived::Derived() -> void | 0 | -1 | 0 | Derived | | | | Typeid.cpp:15:7:15:7 | +| DynamicCast.cpp:4:7:4:7 | Derived::Derived(Derived &&) -> void | 0 | -1 | 0 | Derived | | | | DynamicCast.cpp:4:7:4:7 | +| DynamicCast.cpp:4:7:4:7 | Derived::Derived(Derived &&) -> void | 0 | -1 | 0 | Derived | | | | Typeid.cpp:15:7:15:7 | +| DynamicCast.cpp:4:7:4:7 | Derived::Derived(const Derived &) -> void | 0 | -1 | 0 | Derived | | | | DynamicCast.cpp:4:7:4:7 | +| DynamicCast.cpp:4:7:4:7 | Derived::Derived(const Derived &) -> void | 0 | -1 | 0 | Derived | | | | Typeid.cpp:15:7:15:7 | +| DynamicCast.cpp:4:7:4:7 | Derived::operator=(Derived &&) -> Derived & | 0 | -1 | 0 | operator= | | | | DynamicCast.cpp:4:7:4:7 | +| DynamicCast.cpp:4:7:4:7 | Derived::operator=(Derived &&) -> Derived & | 0 | -1 | 0 | operator= | | | | Typeid.cpp:15:7:15:7 | +| DynamicCast.cpp:4:7:4:7 | Derived::operator=(const Derived &) -> Derived & | 0 | -1 | 0 | operator= | | | | DynamicCast.cpp:4:7:4:7 | +| DynamicCast.cpp:4:7:4:7 | Derived::operator=(const Derived &) -> Derived & | 1 | 0 | 0 | { ... } | | | | file://:0:0:0:0 | +| DynamicCast.cpp:4:7:4:7 | Derived::operator=(const Derived &) -> Derived & | 2 | 1 | 0 | ExprStmt | | | | file://:0:0:0:0 | +| DynamicCast.cpp:4:7:4:7 | Derived::operator=(const Derived &) -> Derived & | 3 | 2 | 0 | (reference dereference) | | | lvalue: Base | file://:0:0:0:0 | +| DynamicCast.cpp:4:7:4:7 | Derived::operator=(const Derived &) -> Derived & | 4 | 3 | 0 | call to operator= | | | prvalue: Base & | DynamicCast.cpp:4:7:4:7 | +| DynamicCast.cpp:4:7:4:7 | Derived::operator=(const Derived &) -> Derived & | 5 | 4 | -1 | (Base *)... | base class conversion | | prvalue: Base * | file://:0:0:0:0 | +| DynamicCast.cpp:4:7:4:7 | Derived::operator=(const Derived &) -> Derived & | 6 | 5 | 0 | this | | | prvalue(load): Derived * | file://:0:0:0:0 | +| DynamicCast.cpp:4:7:4:7 | Derived::operator=(const Derived &) -> Derived & | 7 | 4 | 0 | (reference to) | | | prvalue: const Base & | file://:0:0:0:0 | +| DynamicCast.cpp:4:7:4:7 | Derived::operator=(const Derived &) -> Derived & | 8 | 7 | 0 | * ... | | | lvalue: const Base | file://:0:0:0:0 | +| DynamicCast.cpp:4:7:4:7 | Derived::operator=(const Derived &) -> Derived & | 9 | 8 | 0 | (const Base *)... | base class conversion | | prvalue: const Base * | file://:0:0:0:0 | +| DynamicCast.cpp:4:7:4:7 | Derived::operator=(const Derived &) -> Derived & | 10 | 9 | 0 | & ... | | | prvalue: const Derived * | file://:0:0:0:0 | +| DynamicCast.cpp:4:7:4:7 | Derived::operator=(const Derived &) -> Derived & | 11 | 10 | 0 | (reference dereference) | | | lvalue: const Derived | file://:0:0:0:0 | +| DynamicCast.cpp:4:7:4:7 | Derived::operator=(const Derived &) -> Derived & | 12 | 11 | 0 | p#0 | | | prvalue(load): const Derived & | file://:0:0:0:0 | +| DynamicCast.cpp:4:7:4:7 | Derived::operator=(const Derived &) -> Derived & | 13 | 1 | 1 | return ... | | | | file://:0:0:0:0 | +| DynamicCast.cpp:4:7:4:7 | Derived::operator=(const Derived &) -> Derived & | 14 | 13 | 0 | (reference to) | | | prvalue: Derived & | file://:0:0:0:0 | +| DynamicCast.cpp:4:7:4:7 | Derived::operator=(const Derived &) -> Derived & | 15 | 14 | 0 | * ... | | | lvalue: Derived | file://:0:0:0:0 | +| DynamicCast.cpp:4:7:4:7 | Derived::operator=(const Derived &) -> Derived & | 16 | 15 | 0 | this | | | prvalue(load): Derived * | file://:0:0:0:0 | +| DynamicCast.cpp:5:8:5:8 | Derived::f() -> void | 0 | -1 | 0 | f | | | | DynamicCast.cpp:5:8:5:8 | +| DynamicCast.cpp:5:8:5:8 | Derived::f() -> void | 1 | 0 | 0 | { ... } | | | | DynamicCast.cpp:5:12:5:14 | +| DynamicCast.cpp:5:8:5:8 | Derived::f() -> void | 2 | 1 | 0 | return ... | | | | DynamicCast.cpp:5:14:5:14 | +| DynamicCast.cpp:8:6:8:6 | v(Base *, Derived *) -> void | 0 | -1 | 0 | v | | | | DynamicCast.cpp:8:6:8:6 | +| DynamicCast.cpp:8:6:8:6 | v(Base *, Derived *) -> void | 1 | 0 | 0 | { ... } | | | | DynamicCast.cpp:8:30:10:1 | +| DynamicCast.cpp:8:6:8:6 | v(Base *, Derived *) -> void | 2 | 1 | 0 | ExprStmt | | | | DynamicCast.cpp:9:3:9:34 | +| DynamicCast.cpp:8:6:8:6 | v(Base *, Derived *) -> void | 3 | 2 | 0 | ... = ... | | | lvalue: Derived * | DynamicCast.cpp:9:3:9:33 | +| DynamicCast.cpp:8:6:8:6 | v(Base *, Derived *) -> void | 4 | 3 | 0 | d | | | lvalue: Derived * | DynamicCast.cpp:9:3:9:3 | +| DynamicCast.cpp:8:6:8:6 | v(Base *, Derived *) -> void | 5 | 3 | 1 | dynamic_cast... | dynamic_cast | | prvalue: Derived * | DynamicCast.cpp:9:7:9:33 | +| DynamicCast.cpp:8:6:8:6 | v(Base *, Derived *) -> void | 6 | 5 | 0 | bp | | | prvalue(load): Base * | DynamicCast.cpp:9:31:9:32 | +| DynamicCast.cpp:8:6:8:6 | v(Base *, Derived *) -> void | 7 | 1 | 1 | return ... | | | | DynamicCast.cpp:10:1:10:1 | +| DynamicCast.cpp:12:6:12:10 | v_ref(Base &, Derived &) -> void | 0 | -1 | 0 | v_ref | | | | DynamicCast.cpp:12:6:12:10 | +| DynamicCast.cpp:12:6:12:10 | v_ref(Base &, Derived &) -> void | 1 | 0 | 0 | { ... } | | | | DynamicCast.cpp:12:34:14:1 | +| DynamicCast.cpp:12:6:12:10 | v_ref(Base &, Derived &) -> void | 2 | 1 | 0 | ExprStmt | | | | DynamicCast.cpp:13:3:13:34 | +| DynamicCast.cpp:12:6:12:10 | v_ref(Base &, Derived &) -> void | 3 | 2 | 0 | (reference dereference) | | | lvalue: Derived | DynamicCast.cpp:13:5:13:34 | +| DynamicCast.cpp:12:6:12:10 | v_ref(Base &, Derived &) -> void | 4 | 3 | 0 | call to operator= | | | prvalue: Derived & | DynamicCast.cpp:13:5:13:5 | +| DynamicCast.cpp:12:6:12:10 | v_ref(Base &, Derived &) -> void | 5 | 4 | -1 | (reference dereference) | | | lvalue: Derived | DynamicCast.cpp:13:3:13:3 | +| DynamicCast.cpp:12:6:12:10 | v_ref(Base &, Derived &) -> void | 6 | 5 | 0 | d | | | prvalue(load): Derived & | DynamicCast.cpp:13:3:13:3 | +| DynamicCast.cpp:12:6:12:10 | v_ref(Base &, Derived &) -> void | 7 | 4 | 0 | (reference to) | | | prvalue: const Derived & | DynamicCast.cpp:13:7:13:33 | +| DynamicCast.cpp:12:6:12:10 | v_ref(Base &, Derived &) -> void | 8 | 7 | 0 | (const Derived)... | glvalue conversion | | lvalue: const Derived | DynamicCast.cpp:13:7:13:33 | +| DynamicCast.cpp:12:6:12:10 | v_ref(Base &, Derived &) -> void | 9 | 8 | 0 | dynamic_cast... | dynamic_cast | | lvalue: Derived | DynamicCast.cpp:13:7:13:33 | +| DynamicCast.cpp:12:6:12:10 | v_ref(Base &, Derived &) -> void | 10 | 9 | 0 | (reference dereference) | | | lvalue: Base | DynamicCast.cpp:13:31:13:32 | +| DynamicCast.cpp:12:6:12:10 | v_ref(Base &, Derived &) -> void | 11 | 10 | 0 | bp | | | prvalue(load): Base & | DynamicCast.cpp:13:31:13:32 | +| DynamicCast.cpp:12:6:12:10 | v_ref(Base &, Derived &) -> void | 12 | 1 | 1 | return ... | | | | DynamicCast.cpp:14:1:14:1 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | AddressOf.c:1:6:1:6 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | ArrayToPointer.c:5:6:5:6 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Cast.c:1:6:1:6 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Conversion1.c:1:6:1:6 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Conversion2.c:1:6:1:6 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Conversion4.c:1:6:1:6 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Parenthesis.c:1:6:1:6 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | PointerDereference.c:1:6:1:6 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Sizeof.c:1:6:1:6 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | StatementExpr.c:1:6:1:6 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Subscript.c:1:6:1:6 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | AddressOf.c:1:15:3:1 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | ArrayToPointer.c:6:1:10:1 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Cast.c:1:26:3:1 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Conversion1.c:1:10:3:1 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Conversion2.c:1:15:3:1 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Conversion4.c:1:15:3:1 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Parenthesis.c:1:15:3:1 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | PointerDereference.c:1:23:3:1 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Sizeof.c:1:21:4:1 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | StatementExpr.c:1:10:3:1 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Subscript.c:1:24:3:1 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Cast.c:2:3:2:16 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Conversion2.c:2:3:2:22 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Conversion4.c:2:3:2:15 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Parenthesis.c:2:3:2:18 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | PointerDereference.c:2:3:2:9 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Subscript.c:2:3:2:11 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | AddressOf.c:2:3:2:14 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | ArrayToPointer.c:7:3:7:21 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | Conversion1.c:2:3:2:17 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | Sizeof.c:2:3:2:22 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | StatementExpr.c:2:3:2:30 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: char * | Cast.c:2:3:2:15 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Conversion2.c:2:3:2:21 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Conversion4.c:2:3:2:14 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Parenthesis.c:2:3:2:17 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | PointerDereference.c:2:3:2:8 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Subscript.c:2:3:2:10 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of c | | | char[] | ArrayToPointer.c:7:8:7:8 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of i | | | int | Conversion1.c:2:7:2:7 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of i | | | int | Sizeof.c:2:7:2:7 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of j | | | int | StatementExpr.c:2:7:2:7 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of j | | | int * | AddressOf.c:2:8:2:8 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: char * | Cast.c:2:3:2:3 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: int | Conversion2.c:2:3:2:3 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: int | Conversion4.c:2:3:2:3 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: char * | Cast.c:2:3:2:3 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: int | Conversion2.c:2:3:2:3 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: int | Conversion4.c:2:3:2:3 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: char * | Cast.c:2:3:2:3 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: int | Conversion2.c:2:3:2:3 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: int | Conversion4.c:2:3:2:3 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for c | | | | ArrayToPointer.c:7:14:7:20 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for i | | | | Conversion1.c:2:10:2:16 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for i | | | | Sizeof.c:2:10:2:21 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for j | | | | AddressOf.c:2:11:2:13 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for j | | | | StatementExpr.c:2:10:2:29 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | j | | | lvalue: int | PointerDereference.c:2:3:2:3 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | j | | | lvalue: int | Subscript.c:2:3:2:3 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | v | | | lvalue: int | PointerDereference.c:2:3:2:3 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | v | | | lvalue: int | Subscript.c:2:3:2:3 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: char * | Cast.c:2:3:2:3 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: int | Conversion2.c:2:3:2:3 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: int | Conversion4.c:2:3:2:3 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | (...) | | =7 | prvalue: int | Conversion4.c:2:7:2:14 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | (char *)... | pointer conversion | | prvalue: char * | Cast.c:2:7:2:15 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | * ... | | | prvalue(load): int | PointerDereference.c:2:7:2:8 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | ... * ... | | | prvalue: int | Parenthesis.c:2:7:2:17 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | ... + ... | | =12 | prvalue: int | Conversion2.c:2:7:2:21 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | access to array | | | prvalue(load): int | Subscript.c:2:7:2:10 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | & ... | | | prvalue: int * | AddressOf.c:2:12:2:13 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | (int)... | integral conversion | =1 | prvalue: int | Conversion1.c:2:11:2:16 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | (int)... | integral conversion | =4 | prvalue: int | Sizeof.c:2:11:2:21 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | (statement expression) | | | prvalue: int | StatementExpr.c:2:11:2:29 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | hello | | =hello | lvalue: char[6] | ArrayToPointer.c:7:14:7:20 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 1 | 1 | declaration | | | | ArrayToPointer.c:8:3:8:13 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 1 | 1 | return ... | | | | StatementExpr.c:3:1:3:1 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | 1 | | =1 | prvalue: int | Conversion1.c:2:16:2:16 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | (...) | | | prvalue: int | Parenthesis.c:2:7:2:13 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | (int)... | integral conversion | =5 | prvalue: int | Conversion2.c:2:7:2:12 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | (int)... | integral conversion | =7 | prvalue: int | Conversion4.c:2:8:2:13 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | array | | | lvalue: int | AddressOf.c:2:13:2:13 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | array | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | array | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | c | | | lvalue: int | AddressOf.c:2:13:2:13 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | c | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | c | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | i | | | lvalue: int | AddressOf.c:2:13:2:13 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | i | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | i | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | j | | | prvalue(load): void * | Cast.c:2:15:2:15 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | sizeof(int) | | =4 | prvalue: unsigned long | Sizeof.c:2:11:2:21 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | v | | | prvalue(load): void * | Cast.c:2:15:2:15 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | x | | | lvalue: int | AddressOf.c:2:13:2:13 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | x | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | x | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | declaration | | | | Sizeof.c:3:3:3:24 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | AddressOf.c:3:1:3:1 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | Cast.c:3:1:3:1 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | Conversion1.c:3:1:3:1 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | PointerDereference.c:3:1:3:1 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 5 | 1 | 5 | | =5 | prvalue: int | Subscript.c:2:9:2:9 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | 5 | | =5 | prvalue: int | Conversion2.c:2:12:2:12 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | 7 | | =7 | prvalue: int | Conversion4.c:2:13:2:13 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | ... + ... | | | prvalue: int | Parenthesis.c:2:8:2:12 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | definition of s | | | S | ArrayToPointer.c:8:12:8:12 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 1 | 1 | return ... | | | | Conversion4.c:3:1:3:1 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 1 | 1 | return ... | | | | Subscript.c:3:1:3:1 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 1 | 2 | ExprStmt | | | | ArrayToPointer.c:9:3:9:13 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 5 | 1 | (int)... | integral conversion | =7 | prvalue: int | Conversion2.c:2:16:2:21 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | array | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | c | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | definition of j | | | int | Sizeof.c:3:7:3:7 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | i | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | x | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 7 | 1 | 1 | | =1 | prvalue: int | Parenthesis.c:2:12:2:12 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 8 | 0 | 7 | | =7 | prvalue: int | Conversion2.c:2:21:2:21 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 8 | 0 | ... = ... | | | prvalue: char * | ArrayToPointer.c:9:3:9:12 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 8 | 0 | initializer for j | | | | Sizeof.c:3:10:3:23 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 1 | 1 | return ... | | | | Conversion2.c:3:1:3:1 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 5 | 1 | 2 | | =2 | prvalue: int | Parenthesis.c:2:17:2:17 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 9 | 0 | (int)... | integral conversion | =8 | prvalue: int | Sizeof.c:3:11:3:23 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 9 | 0 | name | | | lvalue: char * | ArrayToPointer.c:9:5:9:8 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 11 | 1 | 1 | return ... | | | | Parenthesis.c:3:1:3:1 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 11 | 10 | 0 | sizeof() | | =8 | prvalue: unsigned long | Sizeof.c:3:11:3:23 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 11 | 10 | -1 | s | | | lvalue: S | ArrayToPointer.c:9:3:9:3 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 12 | 9 | 1 | array to pointer conversion | | | prvalue: char * | ArrayToPointer.c:9:12:9:12 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 12 | 11 | 0 | (...) | | | lvalue: int * | Sizeof.c:3:18:3:23 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | array | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | c | | | lvalue: char[6] | ArrayToPointer.c:9:12:9:12 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | c | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | i | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | x | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 14 | 1 | 2 | return ... | | | | Sizeof.c:4:1:4:1 | +| Parenthesis.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 14 | 1 | 3 | return ... | | | | ArrayToPointer.c:10:1:10:1 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | AddressOf.c:1:6:1:6 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | ArrayToPointer.c:5:6:5:6 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Cast.c:1:6:1:6 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Conversion1.c:1:6:1:6 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Conversion2.c:1:6:1:6 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Conversion4.c:1:6:1:6 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Parenthesis.c:1:6:1:6 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | PointerDereference.c:1:6:1:6 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Sizeof.c:1:6:1:6 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | StatementExpr.c:1:6:1:6 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Subscript.c:1:6:1:6 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | AddressOf.c:1:15:3:1 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | ArrayToPointer.c:6:1:10:1 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Cast.c:1:26:3:1 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Conversion1.c:1:10:3:1 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Conversion2.c:1:15:3:1 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Conversion4.c:1:15:3:1 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Parenthesis.c:1:15:3:1 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | PointerDereference.c:1:23:3:1 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Sizeof.c:1:21:4:1 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | StatementExpr.c:1:10:3:1 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Subscript.c:1:24:3:1 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Cast.c:2:3:2:16 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Conversion2.c:2:3:2:22 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Conversion4.c:2:3:2:15 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Parenthesis.c:2:3:2:18 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | PointerDereference.c:2:3:2:9 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Subscript.c:2:3:2:11 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | AddressOf.c:2:3:2:14 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | ArrayToPointer.c:7:3:7:21 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | Conversion1.c:2:3:2:17 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | Sizeof.c:2:3:2:22 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | StatementExpr.c:2:3:2:30 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: char * | Cast.c:2:3:2:15 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Conversion2.c:2:3:2:21 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Conversion4.c:2:3:2:14 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Parenthesis.c:2:3:2:17 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | PointerDereference.c:2:3:2:8 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Subscript.c:2:3:2:10 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of c | | | char[] | ArrayToPointer.c:7:8:7:8 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of i | | | int | Conversion1.c:2:7:2:7 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of i | | | int | Sizeof.c:2:7:2:7 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of j | | | int | StatementExpr.c:2:7:2:7 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of j | | | int * | AddressOf.c:2:8:2:8 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: char * | Cast.c:2:3:2:3 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: int | Conversion2.c:2:3:2:3 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: int | Conversion4.c:2:3:2:3 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: char * | Cast.c:2:3:2:3 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: int | Conversion2.c:2:3:2:3 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: int | Conversion4.c:2:3:2:3 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: char * | Cast.c:2:3:2:3 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: int | Conversion2.c:2:3:2:3 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: int | Conversion4.c:2:3:2:3 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for c | | | | ArrayToPointer.c:7:14:7:20 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for i | | | | Conversion1.c:2:10:2:16 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for i | | | | Sizeof.c:2:10:2:21 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for j | | | | AddressOf.c:2:11:2:13 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for j | | | | StatementExpr.c:2:10:2:29 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | j | | | lvalue: int | PointerDereference.c:2:3:2:3 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | j | | | lvalue: int | Subscript.c:2:3:2:3 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | v | | | lvalue: int | PointerDereference.c:2:3:2:3 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | v | | | lvalue: int | Subscript.c:2:3:2:3 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: char * | Cast.c:2:3:2:3 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: int | Conversion2.c:2:3:2:3 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: int | Conversion4.c:2:3:2:3 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | (...) | | =7 | prvalue: int | Conversion4.c:2:7:2:14 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | (char *)... | pointer conversion | | prvalue: char * | Cast.c:2:7:2:15 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | * ... | | | prvalue(load): int | PointerDereference.c:2:7:2:8 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | ... * ... | | | prvalue: int | Parenthesis.c:2:7:2:17 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | ... + ... | | =12 | prvalue: int | Conversion2.c:2:7:2:21 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | access to array | | | prvalue(load): int | Subscript.c:2:7:2:10 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | & ... | | | prvalue: int * | AddressOf.c:2:12:2:13 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | (int)... | integral conversion | =1 | prvalue: int | Conversion1.c:2:11:2:16 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | (int)... | integral conversion | =4 | prvalue: int | Sizeof.c:2:11:2:21 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | (statement expression) | | | prvalue: int | StatementExpr.c:2:11:2:29 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | hello | | =hello | lvalue: char[6] | ArrayToPointer.c:7:14:7:20 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 1 | 1 | declaration | | | | ArrayToPointer.c:8:3:8:13 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 1 | 1 | return ... | | | | StatementExpr.c:3:1:3:1 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | 1 | | =1 | prvalue: int | Conversion1.c:2:16:2:16 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | (...) | | | prvalue: int | Parenthesis.c:2:7:2:13 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | (int)... | integral conversion | =5 | prvalue: int | Conversion2.c:2:7:2:12 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | (int)... | integral conversion | =7 | prvalue: int | Conversion4.c:2:8:2:13 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | array | | | lvalue: int | AddressOf.c:2:13:2:13 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | array | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | array | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | c | | | lvalue: int | AddressOf.c:2:13:2:13 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | c | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | c | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | i | | | lvalue: int | AddressOf.c:2:13:2:13 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | i | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | i | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | j | | | prvalue(load): void * | Cast.c:2:15:2:15 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | sizeof(int) | | =4 | prvalue: unsigned long | Sizeof.c:2:11:2:21 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | v | | | prvalue(load): void * | Cast.c:2:15:2:15 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | x | | | lvalue: int | AddressOf.c:2:13:2:13 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | x | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | x | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | declaration | | | | Sizeof.c:3:3:3:24 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | AddressOf.c:3:1:3:1 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | Cast.c:3:1:3:1 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | Conversion1.c:3:1:3:1 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | PointerDereference.c:3:1:3:1 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 5 | 1 | 5 | | =5 | prvalue: int | Subscript.c:2:9:2:9 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | 5 | | =5 | prvalue: int | Conversion2.c:2:12:2:12 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | 7 | | =7 | prvalue: int | Conversion4.c:2:13:2:13 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | ... + ... | | | prvalue: int | Parenthesis.c:2:8:2:12 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | definition of s | | | S | ArrayToPointer.c:8:12:8:12 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 1 | 1 | return ... | | | | Conversion4.c:3:1:3:1 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 1 | 1 | return ... | | | | Subscript.c:3:1:3:1 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 1 | 2 | ExprStmt | | | | ArrayToPointer.c:9:3:9:13 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 5 | 1 | (int)... | integral conversion | =7 | prvalue: int | Conversion2.c:2:16:2:21 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | array | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | c | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | definition of j | | | int | Sizeof.c:3:7:3:7 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | i | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | x | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 7 | 1 | 1 | | =1 | prvalue: int | Parenthesis.c:2:12:2:12 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 8 | 0 | 7 | | =7 | prvalue: int | Conversion2.c:2:21:2:21 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 8 | 0 | ... = ... | | | prvalue: char * | ArrayToPointer.c:9:3:9:12 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 8 | 0 | initializer for j | | | | Sizeof.c:3:10:3:23 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 1 | 1 | return ... | | | | Conversion2.c:3:1:3:1 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 5 | 1 | 2 | | =2 | prvalue: int | Parenthesis.c:2:17:2:17 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 9 | 0 | (int)... | integral conversion | =8 | prvalue: int | Sizeof.c:3:11:3:23 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 9 | 0 | name | | | lvalue: char * | ArrayToPointer.c:9:5:9:8 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 11 | 1 | 1 | return ... | | | | Parenthesis.c:3:1:3:1 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 11 | 10 | 0 | sizeof() | | =8 | prvalue: unsigned long | Sizeof.c:3:11:3:23 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 11 | 10 | -1 | s | | | lvalue: S | ArrayToPointer.c:9:3:9:3 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 12 | 9 | 1 | array to pointer conversion | | | prvalue: char * | ArrayToPointer.c:9:12:9:12 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 12 | 11 | 0 | (...) | | | lvalue: int * | Sizeof.c:3:18:3:23 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | array | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | c | | | lvalue: char[6] | ArrayToPointer.c:9:12:9:12 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | c | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | i | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | x | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 14 | 1 | 2 | return ... | | | | Sizeof.c:4:1:4:1 | +| PointerDereference.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 14 | 1 | 3 | return ... | | | | ArrayToPointer.c:10:1:10:1 | +| ReferenceDereference.cpp:4:6:4:6 | v(int &, int) -> void | 0 | -1 | 0 | v | | | | ReferenceDereference.cpp:4:6:4:6 | +| ReferenceDereference.cpp:4:6:4:6 | v(int &, int) -> void | 1 | 0 | 0 | { ... } | | | | ReferenceDereference.cpp:4:23:6:1 | +| ReferenceDereference.cpp:4:6:4:6 | v(int &, int) -> void | 2 | 1 | 0 | ExprStmt | | | | ReferenceDereference.cpp:5:3:5:8 | +| ReferenceDereference.cpp:4:6:4:6 | v(int &, int) -> void | 3 | 2 | 0 | ... = ... | | | lvalue: int | ReferenceDereference.cpp:5:3:5:7 | +| ReferenceDereference.cpp:4:6:4:6 | v(int &, int) -> void | 4 | 3 | 0 | j | | | lvalue: int | ReferenceDereference.cpp:5:3:5:3 | +| ReferenceDereference.cpp:4:6:4:6 | v(int &, int) -> void | 5 | 3 | 1 | (reference dereference) | | | prvalue(load): int | ReferenceDereference.cpp:5:7:5:7 | +| ReferenceDereference.cpp:4:6:4:6 | v(int &, int) -> void | 6 | 5 | 0 | i | | | prvalue(load): int & | ReferenceDereference.cpp:5:7:5:7 | +| ReferenceDereference.cpp:4:6:4:6 | v(int &, int) -> void | 7 | 1 | 1 | return ... | | | | ReferenceDereference.cpp:6:1:6:1 | +| ReferenceTo.cpp:1:6:1:6 | v(int *) -> int & | 0 | -1 | 0 | v | | | | ReferenceTo.cpp:1:6:1:6 | +| ReferenceTo.cpp:1:6:1:6 | v(int *) -> int & | 1 | 0 | 0 | { ... } | | | | ReferenceTo.cpp:1:16:3:1 | +| ReferenceTo.cpp:1:6:1:6 | v(int *) -> int & | 2 | 1 | 0 | return ... | | | | ReferenceTo.cpp:2:3:2:12 | +| ReferenceTo.cpp:1:6:1:6 | v(int *) -> int & | 3 | 2 | 0 | (reference to) | | | prvalue: int & | ReferenceTo.cpp:2:10:2:11 | +| ReferenceTo.cpp:1:6:1:6 | v(int *) -> int & | 4 | 3 | 0 | * ... | | | lvalue: int | ReferenceTo.cpp:2:10:2:11 | +| ReferenceTo.cpp:1:6:1:6 | v(int *) -> int & | 5 | 4 | 0 | i | | | prvalue(load): int * | ReferenceTo.cpp:2:11:2:11 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | AddressOf.c:1:6:1:6 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | ArrayToPointer.c:5:6:5:6 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Cast.c:1:6:1:6 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Conversion1.c:1:6:1:6 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Conversion2.c:1:6:1:6 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Conversion4.c:1:6:1:6 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Parenthesis.c:1:6:1:6 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | PointerDereference.c:1:6:1:6 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Sizeof.c:1:6:1:6 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | StatementExpr.c:1:6:1:6 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Subscript.c:1:6:1:6 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | AddressOf.c:1:15:3:1 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | ArrayToPointer.c:6:1:10:1 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Cast.c:1:26:3:1 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Conversion1.c:1:10:3:1 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Conversion2.c:1:15:3:1 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Conversion4.c:1:15:3:1 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Parenthesis.c:1:15:3:1 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | PointerDereference.c:1:23:3:1 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Sizeof.c:1:21:4:1 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | StatementExpr.c:1:10:3:1 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Subscript.c:1:24:3:1 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Cast.c:2:3:2:16 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Conversion2.c:2:3:2:22 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Conversion4.c:2:3:2:15 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Parenthesis.c:2:3:2:18 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | PointerDereference.c:2:3:2:9 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Subscript.c:2:3:2:11 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | AddressOf.c:2:3:2:14 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | ArrayToPointer.c:7:3:7:21 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | Conversion1.c:2:3:2:17 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | Sizeof.c:2:3:2:22 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | StatementExpr.c:2:3:2:30 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: char * | Cast.c:2:3:2:15 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Conversion2.c:2:3:2:21 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Conversion4.c:2:3:2:14 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Parenthesis.c:2:3:2:17 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | PointerDereference.c:2:3:2:8 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Subscript.c:2:3:2:10 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of c | | | char[] | ArrayToPointer.c:7:8:7:8 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of i | | | int | Conversion1.c:2:7:2:7 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of i | | | int | Sizeof.c:2:7:2:7 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of j | | | int | StatementExpr.c:2:7:2:7 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of j | | | int * | AddressOf.c:2:8:2:8 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: char * | Cast.c:2:3:2:3 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: int | Conversion2.c:2:3:2:3 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: int | Conversion4.c:2:3:2:3 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: char * | Cast.c:2:3:2:3 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: int | Conversion2.c:2:3:2:3 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: int | Conversion4.c:2:3:2:3 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: char * | Cast.c:2:3:2:3 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: int | Conversion2.c:2:3:2:3 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: int | Conversion4.c:2:3:2:3 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for c | | | | ArrayToPointer.c:7:14:7:20 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for i | | | | Conversion1.c:2:10:2:16 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for i | | | | Sizeof.c:2:10:2:21 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for j | | | | AddressOf.c:2:11:2:13 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for j | | | | StatementExpr.c:2:10:2:29 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | j | | | lvalue: int | PointerDereference.c:2:3:2:3 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | j | | | lvalue: int | Subscript.c:2:3:2:3 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | v | | | lvalue: int | PointerDereference.c:2:3:2:3 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | v | | | lvalue: int | Subscript.c:2:3:2:3 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: char * | Cast.c:2:3:2:3 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: int | Conversion2.c:2:3:2:3 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: int | Conversion4.c:2:3:2:3 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | (...) | | =7 | prvalue: int | Conversion4.c:2:7:2:14 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | (char *)... | pointer conversion | | prvalue: char * | Cast.c:2:7:2:15 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | * ... | | | prvalue(load): int | PointerDereference.c:2:7:2:8 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | ... * ... | | | prvalue: int | Parenthesis.c:2:7:2:17 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | ... + ... | | =12 | prvalue: int | Conversion2.c:2:7:2:21 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | access to array | | | prvalue(load): int | Subscript.c:2:7:2:10 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | & ... | | | prvalue: int * | AddressOf.c:2:12:2:13 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | (int)... | integral conversion | =1 | prvalue: int | Conversion1.c:2:11:2:16 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | (int)... | integral conversion | =4 | prvalue: int | Sizeof.c:2:11:2:21 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | (statement expression) | | | prvalue: int | StatementExpr.c:2:11:2:29 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | hello | | =hello | lvalue: char[6] | ArrayToPointer.c:7:14:7:20 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 1 | 1 | declaration | | | | ArrayToPointer.c:8:3:8:13 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 1 | 1 | return ... | | | | StatementExpr.c:3:1:3:1 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | 1 | | =1 | prvalue: int | Conversion1.c:2:16:2:16 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | (...) | | | prvalue: int | Parenthesis.c:2:7:2:13 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | (int)... | integral conversion | =5 | prvalue: int | Conversion2.c:2:7:2:12 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | (int)... | integral conversion | =7 | prvalue: int | Conversion4.c:2:8:2:13 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | array | | | lvalue: int | AddressOf.c:2:13:2:13 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | array | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | array | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | c | | | lvalue: int | AddressOf.c:2:13:2:13 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | c | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | c | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | i | | | lvalue: int | AddressOf.c:2:13:2:13 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | i | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | i | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | j | | | prvalue(load): void * | Cast.c:2:15:2:15 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | sizeof(int) | | =4 | prvalue: unsigned long | Sizeof.c:2:11:2:21 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | v | | | prvalue(load): void * | Cast.c:2:15:2:15 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | x | | | lvalue: int | AddressOf.c:2:13:2:13 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | x | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | x | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | declaration | | | | Sizeof.c:3:3:3:24 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | AddressOf.c:3:1:3:1 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | Cast.c:3:1:3:1 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | Conversion1.c:3:1:3:1 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | PointerDereference.c:3:1:3:1 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 5 | 1 | 5 | | =5 | prvalue: int | Subscript.c:2:9:2:9 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | 5 | | =5 | prvalue: int | Conversion2.c:2:12:2:12 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | 7 | | =7 | prvalue: int | Conversion4.c:2:13:2:13 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | ... + ... | | | prvalue: int | Parenthesis.c:2:8:2:12 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | definition of s | | | S | ArrayToPointer.c:8:12:8:12 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 1 | 1 | return ... | | | | Conversion4.c:3:1:3:1 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 1 | 1 | return ... | | | | Subscript.c:3:1:3:1 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 1 | 2 | ExprStmt | | | | ArrayToPointer.c:9:3:9:13 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 5 | 1 | (int)... | integral conversion | =7 | prvalue: int | Conversion2.c:2:16:2:21 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | array | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | c | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | definition of j | | | int | Sizeof.c:3:7:3:7 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | i | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | x | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 7 | 1 | 1 | | =1 | prvalue: int | Parenthesis.c:2:12:2:12 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 8 | 0 | 7 | | =7 | prvalue: int | Conversion2.c:2:21:2:21 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 8 | 0 | ... = ... | | | prvalue: char * | ArrayToPointer.c:9:3:9:12 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 8 | 0 | initializer for j | | | | Sizeof.c:3:10:3:23 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 1 | 1 | return ... | | | | Conversion2.c:3:1:3:1 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 5 | 1 | 2 | | =2 | prvalue: int | Parenthesis.c:2:17:2:17 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 9 | 0 | (int)... | integral conversion | =8 | prvalue: int | Sizeof.c:3:11:3:23 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 9 | 0 | name | | | lvalue: char * | ArrayToPointer.c:9:5:9:8 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 11 | 1 | 1 | return ... | | | | Parenthesis.c:3:1:3:1 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 11 | 10 | 0 | sizeof() | | =8 | prvalue: unsigned long | Sizeof.c:3:11:3:23 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 11 | 10 | -1 | s | | | lvalue: S | ArrayToPointer.c:9:3:9:3 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 12 | 9 | 1 | array to pointer conversion | | | prvalue: char * | ArrayToPointer.c:9:12:9:12 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 12 | 11 | 0 | (...) | | | lvalue: int * | Sizeof.c:3:18:3:23 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | array | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | c | | | lvalue: char[6] | ArrayToPointer.c:9:12:9:12 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | c | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | i | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | x | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 14 | 1 | 2 | return ... | | | | Sizeof.c:4:1:4:1 | +| Sizeof.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 14 | 1 | 3 | return ... | | | | ArrayToPointer.c:10:1:10:1 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | AddressOf.c:1:6:1:6 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | ArrayToPointer.c:5:6:5:6 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Cast.c:1:6:1:6 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Conversion1.c:1:6:1:6 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Conversion2.c:1:6:1:6 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Conversion4.c:1:6:1:6 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Parenthesis.c:1:6:1:6 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | PointerDereference.c:1:6:1:6 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Sizeof.c:1:6:1:6 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | StatementExpr.c:1:6:1:6 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Subscript.c:1:6:1:6 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | AddressOf.c:1:15:3:1 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | ArrayToPointer.c:6:1:10:1 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Cast.c:1:26:3:1 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Conversion1.c:1:10:3:1 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Conversion2.c:1:15:3:1 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Conversion4.c:1:15:3:1 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Parenthesis.c:1:15:3:1 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | PointerDereference.c:1:23:3:1 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Sizeof.c:1:21:4:1 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | StatementExpr.c:1:10:3:1 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Subscript.c:1:24:3:1 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Cast.c:2:3:2:16 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Conversion2.c:2:3:2:22 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Conversion4.c:2:3:2:15 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Parenthesis.c:2:3:2:18 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | PointerDereference.c:2:3:2:9 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Subscript.c:2:3:2:11 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | AddressOf.c:2:3:2:14 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | ArrayToPointer.c:7:3:7:21 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | Conversion1.c:2:3:2:17 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | Sizeof.c:2:3:2:22 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | StatementExpr.c:2:3:2:30 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: char * | Cast.c:2:3:2:15 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Conversion2.c:2:3:2:21 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Conversion4.c:2:3:2:14 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Parenthesis.c:2:3:2:17 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | PointerDereference.c:2:3:2:8 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Subscript.c:2:3:2:10 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of c | | | char[] | ArrayToPointer.c:7:8:7:8 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of i | | | int | Conversion1.c:2:7:2:7 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of i | | | int | Sizeof.c:2:7:2:7 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of j | | | int | StatementExpr.c:2:7:2:7 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of j | | | int * | AddressOf.c:2:8:2:8 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: char * | Cast.c:2:3:2:3 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: int | Conversion2.c:2:3:2:3 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: int | Conversion4.c:2:3:2:3 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: char * | Cast.c:2:3:2:3 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: int | Conversion2.c:2:3:2:3 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: int | Conversion4.c:2:3:2:3 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: char * | Cast.c:2:3:2:3 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: int | Conversion2.c:2:3:2:3 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: int | Conversion4.c:2:3:2:3 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for c | | | | ArrayToPointer.c:7:14:7:20 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for i | | | | Conversion1.c:2:10:2:16 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for i | | | | Sizeof.c:2:10:2:21 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for j | | | | AddressOf.c:2:11:2:13 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for j | | | | StatementExpr.c:2:10:2:29 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | j | | | lvalue: int | PointerDereference.c:2:3:2:3 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | j | | | lvalue: int | Subscript.c:2:3:2:3 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | v | | | lvalue: int | PointerDereference.c:2:3:2:3 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | v | | | lvalue: int | Subscript.c:2:3:2:3 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: char * | Cast.c:2:3:2:3 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: int | Conversion2.c:2:3:2:3 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: int | Conversion4.c:2:3:2:3 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | (...) | | =7 | prvalue: int | Conversion4.c:2:7:2:14 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | (char *)... | pointer conversion | | prvalue: char * | Cast.c:2:7:2:15 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | * ... | | | prvalue(load): int | PointerDereference.c:2:7:2:8 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | ... * ... | | | prvalue: int | Parenthesis.c:2:7:2:17 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | ... + ... | | =12 | prvalue: int | Conversion2.c:2:7:2:21 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | access to array | | | prvalue(load): int | Subscript.c:2:7:2:10 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | & ... | | | prvalue: int * | AddressOf.c:2:12:2:13 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | (int)... | integral conversion | =1 | prvalue: int | Conversion1.c:2:11:2:16 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | (int)... | integral conversion | =4 | prvalue: int | Sizeof.c:2:11:2:21 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | (statement expression) | | | prvalue: int | StatementExpr.c:2:11:2:29 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | hello | | =hello | lvalue: char[6] | ArrayToPointer.c:7:14:7:20 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 1 | 1 | declaration | | | | ArrayToPointer.c:8:3:8:13 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 1 | 1 | return ... | | | | StatementExpr.c:3:1:3:1 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | 1 | | =1 | prvalue: int | Conversion1.c:2:16:2:16 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | (...) | | | prvalue: int | Parenthesis.c:2:7:2:13 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | (int)... | integral conversion | =5 | prvalue: int | Conversion2.c:2:7:2:12 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | (int)... | integral conversion | =7 | prvalue: int | Conversion4.c:2:8:2:13 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | array | | | lvalue: int | AddressOf.c:2:13:2:13 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | array | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | array | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | c | | | lvalue: int | AddressOf.c:2:13:2:13 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | c | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | c | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | i | | | lvalue: int | AddressOf.c:2:13:2:13 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | i | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | i | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | j | | | prvalue(load): void * | Cast.c:2:15:2:15 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | sizeof(int) | | =4 | prvalue: unsigned long | Sizeof.c:2:11:2:21 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | v | | | prvalue(load): void * | Cast.c:2:15:2:15 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | x | | | lvalue: int | AddressOf.c:2:13:2:13 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | x | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | x | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | declaration | | | | Sizeof.c:3:3:3:24 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | AddressOf.c:3:1:3:1 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | Cast.c:3:1:3:1 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | Conversion1.c:3:1:3:1 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | PointerDereference.c:3:1:3:1 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 5 | 1 | 5 | | =5 | prvalue: int | Subscript.c:2:9:2:9 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | 5 | | =5 | prvalue: int | Conversion2.c:2:12:2:12 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | 7 | | =7 | prvalue: int | Conversion4.c:2:13:2:13 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | ... + ... | | | prvalue: int | Parenthesis.c:2:8:2:12 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | definition of s | | | S | ArrayToPointer.c:8:12:8:12 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 1 | 1 | return ... | | | | Conversion4.c:3:1:3:1 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 1 | 1 | return ... | | | | Subscript.c:3:1:3:1 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 1 | 2 | ExprStmt | | | | ArrayToPointer.c:9:3:9:13 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 5 | 1 | (int)... | integral conversion | =7 | prvalue: int | Conversion2.c:2:16:2:21 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | array | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | c | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | definition of j | | | int | Sizeof.c:3:7:3:7 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | i | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | x | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 7 | 1 | 1 | | =1 | prvalue: int | Parenthesis.c:2:12:2:12 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 8 | 0 | 7 | | =7 | prvalue: int | Conversion2.c:2:21:2:21 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 8 | 0 | ... = ... | | | prvalue: char * | ArrayToPointer.c:9:3:9:12 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 8 | 0 | initializer for j | | | | Sizeof.c:3:10:3:23 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 1 | 1 | return ... | | | | Conversion2.c:3:1:3:1 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 5 | 1 | 2 | | =2 | prvalue: int | Parenthesis.c:2:17:2:17 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 9 | 0 | (int)... | integral conversion | =8 | prvalue: int | Sizeof.c:3:11:3:23 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 9 | 0 | name | | | lvalue: char * | ArrayToPointer.c:9:5:9:8 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 11 | 1 | 1 | return ... | | | | Parenthesis.c:3:1:3:1 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 11 | 10 | 0 | sizeof() | | =8 | prvalue: unsigned long | Sizeof.c:3:11:3:23 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 11 | 10 | -1 | s | | | lvalue: S | ArrayToPointer.c:9:3:9:3 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 12 | 9 | 1 | array to pointer conversion | | | prvalue: char * | ArrayToPointer.c:9:12:9:12 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 12 | 11 | 0 | (...) | | | lvalue: int * | Sizeof.c:3:18:3:23 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | array | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | c | | | lvalue: char[6] | ArrayToPointer.c:9:12:9:12 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | c | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | i | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | x | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 14 | 1 | 2 | return ... | | | | Sizeof.c:4:1:4:1 | +| StatementExpr.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 14 | 1 | 3 | return ... | | | | ArrayToPointer.c:10:1:10:1 | +| StaticMemberAccess.cpp:1:8:1:8 | X::operator=(X &&) -> X & | 0 | -1 | 0 | operator= | | | | StaticMemberAccess.cpp:1:8:1:8 | +| StaticMemberAccess.cpp:1:8:1:8 | X::operator=(const X &) -> X & | 0 | -1 | 0 | operator= | | | | StaticMemberAccess.cpp:1:8:1:8 | +| StaticMemberAccess.cpp:5:6:5:6 | v(int, X &) -> void | 0 | -1 | 0 | v | | | | StaticMemberAccess.cpp:5:6:5:6 | +| StaticMemberAccess.cpp:5:6:5:6 | v(int, X &) -> void | 1 | 0 | 0 | { ... } | | | | StaticMemberAccess.cpp:5:24:9:1 | +| StaticMemberAccess.cpp:5:6:5:6 | v(int, X &) -> void | 2 | 1 | 0 | ExprStmt | | | | StaticMemberAccess.cpp:7:3:7:13 | +| StaticMemberAccess.cpp:5:6:5:6 | v(int, X &) -> void | 3 | 2 | 0 | ... = ... | | | lvalue: int | StaticMemberAccess.cpp:7:3:7:12 | +| StaticMemberAccess.cpp:5:6:5:6 | v(int, X &) -> void | 4 | 3 | 0 | i | | | lvalue: int | StaticMemberAccess.cpp:7:3:7:3 | +| StaticMemberAccess.cpp:5:6:5:6 | v(int, X &) -> void | 5 | 3 | 1 | i | | | prvalue: int | StaticMemberAccess.cpp:7:12:7:12 | +| StaticMemberAccess.cpp:5:6:5:6 | v(int, X &) -> void | 6 | 5 | -1 | (reference dereference) | | | lvalue: X | StaticMemberAccess.cpp:7:7:7:10 | +| StaticMemberAccess.cpp:5:6:5:6 | v(int, X &) -> void | 7 | 6 | 0 | xref | | | prvalue(load): X & | StaticMemberAccess.cpp:7:7:7:10 | +| StaticMemberAccess.cpp:5:6:5:6 | v(int, X &) -> void | 8 | 1 | 1 | return ... | | | | StaticMemberAccess.cpp:9:1:9:1 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | AddressOf.c:1:6:1:6 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | ArrayToPointer.c:5:6:5:6 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Cast.c:1:6:1:6 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Conversion1.c:1:6:1:6 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Conversion2.c:1:6:1:6 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Conversion4.c:1:6:1:6 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Parenthesis.c:1:6:1:6 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | PointerDereference.c:1:6:1:6 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Sizeof.c:1:6:1:6 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | StatementExpr.c:1:6:1:6 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 0 | -1 | 0 | v | | | | Subscript.c:1:6:1:6 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | AddressOf.c:1:15:3:1 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | ArrayToPointer.c:6:1:10:1 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Cast.c:1:26:3:1 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Conversion1.c:1:10:3:1 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Conversion2.c:1:15:3:1 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Conversion4.c:1:15:3:1 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Parenthesis.c:1:15:3:1 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | PointerDereference.c:1:23:3:1 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Sizeof.c:1:21:4:1 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | StatementExpr.c:1:10:3:1 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 1 | 0 | 0 | { ... } | | | | Subscript.c:1:24:3:1 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Cast.c:2:3:2:16 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Conversion2.c:2:3:2:22 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Conversion4.c:2:3:2:15 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Parenthesis.c:2:3:2:18 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | PointerDereference.c:2:3:2:9 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | ExprStmt | | | | Subscript.c:2:3:2:11 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | AddressOf.c:2:3:2:14 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | ArrayToPointer.c:7:3:7:21 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | Conversion1.c:2:3:2:17 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | Sizeof.c:2:3:2:22 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 2 | 1 | 0 | declaration | | | | StatementExpr.c:2:3:2:30 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: char * | Cast.c:2:3:2:15 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Conversion2.c:2:3:2:21 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Conversion4.c:2:3:2:14 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Parenthesis.c:2:3:2:17 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | PointerDereference.c:2:3:2:8 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | ... = ... | | | prvalue: int | Subscript.c:2:3:2:10 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of c | | | char[] | ArrayToPointer.c:7:8:7:8 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of i | | | int | Conversion1.c:2:7:2:7 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of i | | | int | Sizeof.c:2:7:2:7 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of j | | | int | StatementExpr.c:2:7:2:7 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 3 | 2 | 0 | definition of j | | | int * | AddressOf.c:2:8:2:8 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: char * | Cast.c:2:3:2:3 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: int | Conversion2.c:2:3:2:3 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: int | Conversion4.c:2:3:2:3 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | array | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: char * | Cast.c:2:3:2:3 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: int | Conversion2.c:2:3:2:3 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: int | Conversion4.c:2:3:2:3 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | c | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: char * | Cast.c:2:3:2:3 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: int | Conversion2.c:2:3:2:3 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: int | Conversion4.c:2:3:2:3 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | i | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for c | | | | ArrayToPointer.c:7:14:7:20 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for i | | | | Conversion1.c:2:10:2:16 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for i | | | | Sizeof.c:2:10:2:21 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for j | | | | AddressOf.c:2:11:2:13 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | initializer for j | | | | StatementExpr.c:2:10:2:29 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | j | | | lvalue: int | PointerDereference.c:2:3:2:3 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | j | | | lvalue: int | Subscript.c:2:3:2:3 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | v | | | lvalue: int | PointerDereference.c:2:3:2:3 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | v | | | lvalue: int | Subscript.c:2:3:2:3 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: char * | Cast.c:2:3:2:3 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: int | Conversion2.c:2:3:2:3 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: int | Conversion4.c:2:3:2:3 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 4 | 3 | 0 | x | | | lvalue: int | Parenthesis.c:2:3:2:3 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | (...) | | =7 | prvalue: int | Conversion4.c:2:7:2:14 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | (char *)... | pointer conversion | | prvalue: char * | Cast.c:2:7:2:15 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | * ... | | | prvalue(load): int | PointerDereference.c:2:7:2:8 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | ... * ... | | | prvalue: int | Parenthesis.c:2:7:2:17 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | ... + ... | | =12 | prvalue: int | Conversion2.c:2:7:2:21 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 3 | 1 | access to array | | | prvalue(load): int | Subscript.c:2:7:2:10 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | & ... | | | prvalue: int * | AddressOf.c:2:12:2:13 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | (int)... | integral conversion | =1 | prvalue: int | Conversion1.c:2:11:2:16 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | (int)... | integral conversion | =4 | prvalue: int | Sizeof.c:2:11:2:21 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | (statement expression) | | | prvalue: int | StatementExpr.c:2:11:2:29 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 5 | 4 | 0 | hello | | =hello | lvalue: char[6] | ArrayToPointer.c:7:14:7:20 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 1 | 1 | declaration | | | | ArrayToPointer.c:8:3:8:13 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 1 | 1 | return ... | | | | StatementExpr.c:3:1:3:1 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | 1 | | =1 | prvalue: int | Conversion1.c:2:16:2:16 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | (...) | | | prvalue: int | Parenthesis.c:2:7:2:13 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | (int)... | integral conversion | =5 | prvalue: int | Conversion2.c:2:7:2:12 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | (int)... | integral conversion | =7 | prvalue: int | Conversion4.c:2:8:2:13 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | array | | | lvalue: int | AddressOf.c:2:13:2:13 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | array | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | array | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | c | | | lvalue: int | AddressOf.c:2:13:2:13 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | c | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | c | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | i | | | lvalue: int | AddressOf.c:2:13:2:13 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | i | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | i | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | j | | | prvalue(load): void * | Cast.c:2:15:2:15 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | sizeof(int) | | =4 | prvalue: unsigned long | Sizeof.c:2:11:2:21 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | v | | | prvalue(load): void * | Cast.c:2:15:2:15 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | x | | | lvalue: int | AddressOf.c:2:13:2:13 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | x | | | prvalue(load): int * | PointerDereference.c:2:8:2:8 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 6 | 5 | 0 | x | | | prvalue(load): int * | Subscript.c:2:7:2:7 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | declaration | | | | Sizeof.c:3:3:3:24 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | AddressOf.c:3:1:3:1 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | Cast.c:3:1:3:1 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | Conversion1.c:3:1:3:1 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 1 | 1 | return ... | | | | PointerDereference.c:3:1:3:1 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 5 | 1 | 5 | | =5 | prvalue: int | Subscript.c:2:9:2:9 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | 5 | | =5 | prvalue: int | Conversion2.c:2:12:2:12 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | 7 | | =7 | prvalue: int | Conversion4.c:2:13:2:13 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | ... + ... | | | prvalue: int | Parenthesis.c:2:8:2:12 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 7 | 6 | 0 | definition of s | | | S | ArrayToPointer.c:8:12:8:12 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 1 | 1 | return ... | | | | Conversion4.c:3:1:3:1 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 1 | 1 | return ... | | | | Subscript.c:3:1:3:1 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 1 | 2 | ExprStmt | | | | ArrayToPointer.c:9:3:9:13 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 5 | 1 | (int)... | integral conversion | =7 | prvalue: int | Conversion2.c:2:16:2:21 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | array | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | c | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | definition of j | | | int | Sizeof.c:3:7:3:7 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | i | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 8 | 7 | 0 | x | | | prvalue(load): int | Parenthesis.c:2:8:2:8 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 7 | 1 | 1 | | =1 | prvalue: int | Parenthesis.c:2:12:2:12 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 8 | 0 | 7 | | =7 | prvalue: int | Conversion2.c:2:21:2:21 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 8 | 0 | ... = ... | | | prvalue: char * | ArrayToPointer.c:9:3:9:12 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 9 | 8 | 0 | initializer for j | | | | Sizeof.c:3:10:3:23 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 1 | 1 | return ... | | | | Conversion2.c:3:1:3:1 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 5 | 1 | 2 | | =2 | prvalue: int | Parenthesis.c:2:17:2:17 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 9 | 0 | (int)... | integral conversion | =8 | prvalue: int | Sizeof.c:3:11:3:23 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 10 | 9 | 0 | name | | | lvalue: char * | ArrayToPointer.c:9:5:9:8 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 11 | 1 | 1 | return ... | | | | Parenthesis.c:3:1:3:1 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 11 | 10 | 0 | sizeof() | | =8 | prvalue: unsigned long | Sizeof.c:3:11:3:23 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 11 | 10 | -1 | s | | | lvalue: S | ArrayToPointer.c:9:3:9:3 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 12 | 9 | 1 | array to pointer conversion | | | prvalue: char * | ArrayToPointer.c:9:12:9:12 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 12 | 11 | 0 | (...) | | | lvalue: int * | Sizeof.c:3:18:3:23 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | array | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | c | | | lvalue: char[6] | ArrayToPointer.c:9:12:9:12 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | c | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | i | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 13 | 12 | 0 | x | | | lvalue: int * | Sizeof.c:3:18:3:22 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 14 | 1 | 2 | return ... | | | | Sizeof.c:4:1:4:1 | +| Subscript.c:1:6:1:6 | v(int[], int *, int, char *, void *, int) -> void | 14 | 1 | 3 | return ... | | | | ArrayToPointer.c:10:1:10:1 | +| Throw.cpp:1:7:1:7 | E::operator=(E &&) -> E & | 0 | -1 | 0 | operator= | | | | ConstructorCall.cpp:13:7:13:7 | +| Throw.cpp:1:7:1:7 | E::operator=(E &&) -> E & | 0 | -1 | 0 | operator= | | | | Throw.cpp:1:7:1:7 | +| Throw.cpp:1:7:1:7 | E::operator=(const E &) -> E & | 0 | -1 | 0 | operator= | | | | ConstructorCall.cpp:13:7:13:7 | +| Throw.cpp:1:7:1:7 | E::operator=(const E &) -> E & | 0 | -1 | 0 | operator= | | | | Throw.cpp:1:7:1:7 | +| Throw.cpp:2:7:2:7 | F::F(F &&) -> void | 0 | -1 | 0 | F | | | | Throw.cpp:2:7:2:7 | +| Throw.cpp:2:7:2:7 | F::F(F &&) -> void | 1 | 0 | 0 | { ... } | | | | Throw.cpp:2:7:2:7 | +| Throw.cpp:2:7:2:7 | F::F(F &&) -> void | 2 | 1 | 0 | return ... | | | | Throw.cpp:2:7:2:7 | +| Throw.cpp:2:7:2:7 | F::F(const F &) -> void | 0 | -1 | 0 | F | | | | Throw.cpp:2:7:2:7 | +| Throw.cpp:2:7:2:7 | F::operator=(F &&) -> F & | 0 | -1 | 0 | operator= | | | | Throw.cpp:2:7:2:7 | +| Throw.cpp:2:7:2:7 | F::operator=(const F &) -> F & | 0 | -1 | 0 | operator= | | | | Throw.cpp:2:7:2:7 | +| Throw.cpp:4:3:4:3 | F::F() -> void | 0 | -1 | 0 | F | | | | Throw.cpp:4:3:4:3 | +| Throw.cpp:4:3:4:3 | F::F() -> void | 1 | 0 | 0 | { ... } | | | | Throw.cpp:4:7:4:9 | +| Throw.cpp:4:3:4:3 | F::F() -> void | 2 | 1 | 0 | return ... | | | | Throw.cpp:4:9:4:9 | +| Throw.cpp:6:6:6:6 | f(int) -> void | 0 | -1 | 0 | f | | | | Throw.cpp:6:6:6:6 | +| Throw.cpp:6:6:6:6 | f(int) -> void | 0 | -1 | 0 | f | | | | VacuousDestructorCall.cpp:7:6:7:6 | +| Throw.cpp:6:6:6:6 | f(int) -> void | 1 | 0 | 0 | { ... } | | | | Throw.cpp:6:15:16:1 | +| Throw.cpp:6:6:6:6 | f(int) -> void | 1 | 0 | 0 | { ... } | | | | VacuousDestructorCall.cpp:7:15:11:1 | +| Throw.cpp:6:6:6:6 | f(int) -> void | 2 | 1 | 0 | ExprStmt | | | | VacuousDestructorCall.cpp:10:3:10:11 | +| Throw.cpp:6:6:6:6 | f(int) -> void | 2 | 1 | 0 | try { ... } | | | | Throw.cpp:7:3:12:3 | +| Throw.cpp:6:6:6:6 | f(int) -> void | 3 | 2 | 0 | call to v | | | prvalue: void | VacuousDestructorCall.cpp:10:3:10:3 | +| Throw.cpp:6:6:6:6 | f(int) -> void | 3 | 2 | 0 | { ... } | | | | Throw.cpp:7:7:12:3 | +| Throw.cpp:6:6:6:6 | f(int) -> void | 4 | 3 | 0 | i | | | prvalue(load): int | VacuousDestructorCall.cpp:10:5:10:5 | +| Throw.cpp:6:6:6:6 | f(int) -> void | 4 | 3 | 0 | if (...) ... | | | | Throw.cpp:8:5:11:16 | +| Throw.cpp:6:6:6:6 | f(int) -> void | 5 | 3 | 1 | & ... | | | prvalue: int * | VacuousDestructorCall.cpp:10:8:10:9 | +| Throw.cpp:6:6:6:6 | f(int) -> void | 5 | 4 | 0 | (bool)... | conversion to bool | | prvalue: bool | Throw.cpp:8:8:8:8 | +| Throw.cpp:6:6:6:6 | f(int) -> void | 6 | 5 | 0 | i | | | lvalue: int | VacuousDestructorCall.cpp:10:9:10:9 | +| Throw.cpp:6:6:6:6 | f(int) -> void | 6 | 5 | 0 | i | | | prvalue(load): int | Throw.cpp:8:8:8:8 | +| Throw.cpp:6:6:6:6 | f(int) -> void | 7 | 1 | 1 | return ... | | | | VacuousDestructorCall.cpp:11:1:11:1 | +| Throw.cpp:6:6:6:6 | f(int) -> void | 7 | 4 | 1 | ExprStmt | | | | Throw.cpp:9:7:9:16 | +| Throw.cpp:6:6:6:6 | f(int) -> void | 8 | 7 | 0 | throw ... | | | prvalue: E | Throw.cpp:9:7:9:15 | +| Throw.cpp:6:6:6:6 | f(int) -> void | 9 | 8 | 0 | 0 | | =0 | prvalue: E | Throw.cpp:9:7:9:15 | +| Throw.cpp:6:6:6:6 | f(int) -> void | 10 | 4 | 2 | ExprStmt | | | | Throw.cpp:11:7:11:16 | +| Throw.cpp:6:6:6:6 | f(int) -> void | 11 | 10 | 0 | throw ... | | | prvalue: F | Throw.cpp:11:7:11:15 | +| Throw.cpp:6:6:6:6 | f(int) -> void | 12 | 11 | 0 | call to F | | | prvalue: void | Throw.cpp:11:7:11:15 | +| Throw.cpp:6:6:6:6 | f(int) -> void | 13 | 2 | 1 | | | | | Throw.cpp:12:17:14:3 | +| Throw.cpp:6:6:6:6 | f(int) -> void | 14 | 13 | 0 | { ... } | | | | Throw.cpp:12:17:14:3 | +| Throw.cpp:6:6:6:6 | f(int) -> void | 15 | 14 | 0 | ExprStmt | | | | Throw.cpp:13:5:13:10 | +| Throw.cpp:6:6:6:6 | f(int) -> void | 16 | 15 | 0 | re-throw exception | | | prvalue: void | Throw.cpp:13:5:13:9 | +| Typeid.cpp:4:9:4:9 | std::type_info::operator=(const type_info &) -> type_info & | 0 | -1 | 0 | operator= | | | | Typeid.cpp:4:9:4:9 | +| Typeid.cpp:4:9:4:9 | std::type_info::operator=(type_info &&) -> type_info & | 0 | -1 | 0 | operator= | | | | Typeid.cpp:4:9:4:9 | +| Typeid.cpp:7:17:7:20 | std::type_info::name() -> const char * | 0 | -1 | 0 | name | | | | Typeid.cpp:7:17:7:20 | +| Typeid.cpp:11:7:11:7 | Base::Base() -> void | 0 | -1 | 0 | Base | | | | DynamicCast.cpp:1:7:1:7 | +| Typeid.cpp:11:7:11:7 | Base::Base() -> void | 0 | -1 | 0 | Base | | | | Typeid.cpp:11:7:11:7 | +| Typeid.cpp:11:7:11:7 | Base::Base(Base &&) -> void | 0 | -1 | 0 | Base | | | | DynamicCast.cpp:1:7:1:7 | +| Typeid.cpp:11:7:11:7 | Base::Base(Base &&) -> void | 0 | -1 | 0 | Base | | | | Typeid.cpp:11:7:11:7 | +| Typeid.cpp:11:7:11:7 | Base::Base(const Base &) -> void | 0 | -1 | 0 | Base | | | | DynamicCast.cpp:1:7:1:7 | +| Typeid.cpp:11:7:11:7 | Base::Base(const Base &) -> void | 0 | -1 | 0 | Base | | | | Typeid.cpp:11:7:11:7 | +| Typeid.cpp:11:7:11:7 | Base::operator=(Base &&) -> Base & | 0 | -1 | 0 | operator= | | | | DynamicCast.cpp:1:7:1:7 | +| Typeid.cpp:11:7:11:7 | Base::operator=(Base &&) -> Base & | 0 | -1 | 0 | operator= | | | | Typeid.cpp:11:7:11:7 | +| Typeid.cpp:13:18:13:18 | Base::v() -> void | 0 | -1 | 0 | v | | | | Typeid.cpp:13:18:13:18 | +| Typeid.cpp:13:18:13:18 | Base::v() -> void | 1 | 0 | 0 | { ... } | | | | Typeid.cpp:13:22:13:24 | +| Typeid.cpp:13:18:13:18 | Base::v() -> void | 2 | 1 | 0 | return ... | | | | Typeid.cpp:13:24:13:24 | +| Typeid.cpp:15:7:15:7 | Derived::Derived() -> void | 0 | -1 | 0 | Derived | | | | DynamicCast.cpp:4:7:4:7 | +| Typeid.cpp:15:7:15:7 | Derived::Derived() -> void | 0 | -1 | 0 | Derived | | | | Typeid.cpp:15:7:15:7 | +| Typeid.cpp:15:7:15:7 | Derived::Derived(Derived &&) -> void | 0 | -1 | 0 | Derived | | | | DynamicCast.cpp:4:7:4:7 | +| Typeid.cpp:15:7:15:7 | Derived::Derived(Derived &&) -> void | 0 | -1 | 0 | Derived | | | | Typeid.cpp:15:7:15:7 | +| Typeid.cpp:15:7:15:7 | Derived::Derived(const Derived &) -> void | 0 | -1 | 0 | Derived | | | | DynamicCast.cpp:4:7:4:7 | +| Typeid.cpp:15:7:15:7 | Derived::Derived(const Derived &) -> void | 0 | -1 | 0 | Derived | | | | Typeid.cpp:15:7:15:7 | +| Typeid.cpp:15:7:15:7 | Derived::operator=(Derived &&) -> Derived & | 0 | -1 | 0 | operator= | | | | DynamicCast.cpp:4:7:4:7 | +| Typeid.cpp:15:7:15:7 | Derived::operator=(Derived &&) -> Derived & | 0 | -1 | 0 | operator= | | | | Typeid.cpp:15:7:15:7 | +| Typeid.cpp:18:6:18:6 | v(Base *) -> void | 0 | -1 | 0 | v | | | | Typeid.cpp:18:6:18:6 | +| Typeid.cpp:18:6:18:6 | v(Base *) -> void | 1 | 0 | 0 | { ... } | | | | Typeid.cpp:18:18:20:1 | +| Typeid.cpp:18:6:18:6 | v(Base *) -> void | 2 | 1 | 0 | declaration | | | | Typeid.cpp:19:3:19:39 | +| Typeid.cpp:18:6:18:6 | v(Base *) -> void | 3 | 2 | 0 | definition of name | | | const char * | Typeid.cpp:19:15:19:18 | +| Typeid.cpp:18:6:18:6 | v(Base *) -> void | 4 | 3 | 0 | initializer for name | | | | Typeid.cpp:19:21:19:38 | +| Typeid.cpp:18:6:18:6 | v(Base *) -> void | 5 | 4 | 0 | call to name | | | prvalue: const char * | Typeid.cpp:19:33:19:36 | +| Typeid.cpp:18:6:18:6 | v(Base *) -> void | 6 | 5 | -1 | typeid ... | | | lvalue: const type_info | Typeid.cpp:19:22:19:31 | +| Typeid.cpp:18:6:18:6 | v(Base *) -> void | 7 | 6 | 0 | bp | | | lvalue: Base * | Typeid.cpp:19:29:19:30 | +| Typeid.cpp:18:6:18:6 | v(Base *) -> void | 8 | 1 | 1 | return ... | | | | Typeid.cpp:20:1:20:1 | +| VacuousDestructorCall.cpp:2:6:2:6 | v(T, T *) -> void | 0 | -1 | 0 | v | | | | VacuousDestructorCall.cpp:2:6:2:6 | +| VacuousDestructorCall.cpp:2:6:2:6 | v(T, T *) -> void | 1 | 0 | 0 | { ... } | | | | VacuousDestructorCall.cpp:2:19:5:1 | +| VacuousDestructorCall.cpp:2:6:2:6 | v(T, T *) -> void | 2 | 1 | 0 | ExprStmt | | | | VacuousDestructorCall.cpp:3:3:3:12 | +| VacuousDestructorCall.cpp:2:6:2:6 | v(T, T *) -> void | 3 | 2 | 0 | call to expression | | | prvalue: unknown | VacuousDestructorCall.cpp:3:3:3:11 | +| VacuousDestructorCall.cpp:2:6:2:6 | v(T, T *) -> void | 4 | 3 | 0 | Unknown literal | | | prvalue: unknown | VacuousDestructorCall.cpp:3:8:3:9 | +| VacuousDestructorCall.cpp:2:6:2:6 | v(T, T *) -> void | 5 | 4 | -1 | x | | | lvalue: T | VacuousDestructorCall.cpp:3:3:3:3 | +| VacuousDestructorCall.cpp:2:6:2:6 | v(T, T *) -> void | 6 | 1 | 1 | ExprStmt | | | | VacuousDestructorCall.cpp:4:3:4:13 | +| VacuousDestructorCall.cpp:2:6:2:6 | v(T, T *) -> void | 7 | 6 | 0 | call to expression | | | prvalue: unknown | VacuousDestructorCall.cpp:4:3:4:12 | +| VacuousDestructorCall.cpp:2:6:2:6 | v(T, T *) -> void | 8 | 7 | 0 | Unknown literal | | | prvalue: unknown | VacuousDestructorCall.cpp:4:9:4:10 | +| VacuousDestructorCall.cpp:2:6:2:6 | v(T, T *) -> void | 9 | 8 | -1 | y | | | prvalue(load): T * | VacuousDestructorCall.cpp:4:3:4:3 | +| VacuousDestructorCall.cpp:2:6:2:6 | v(T, T *) -> void | 10 | 1 | 2 | return ... | | | | VacuousDestructorCall.cpp:5:1:5:1 | +| VacuousDestructorCall.cpp:2:6:2:6 | v(int, int *) -> void | 0 | -1 | 0 | v | | | | VacuousDestructorCall.cpp:2:6:2:6 | +| VacuousDestructorCall.cpp:2:6:2:6 | v(int, int *) -> void | 1 | 0 | 0 | { ... } | | | | VacuousDestructorCall.cpp:2:19:5:1 | +| VacuousDestructorCall.cpp:2:6:2:6 | v(int, int *) -> void | 2 | 1 | 0 | ExprStmt | | | | VacuousDestructorCall.cpp:3:3:3:12 | +| VacuousDestructorCall.cpp:2:6:2:6 | v(int, int *) -> void | 3 | 2 | 0 | (vacuous destructor call) | | | prvalue: void | VacuousDestructorCall.cpp:3:3:3:11 | +| VacuousDestructorCall.cpp:2:6:2:6 | v(int, int *) -> void | 4 | 3 | 0 | x | | | lvalue: int | VacuousDestructorCall.cpp:3:3:3:3 | +| VacuousDestructorCall.cpp:2:6:2:6 | v(int, int *) -> void | 5 | 1 | 1 | ExprStmt | | | | VacuousDestructorCall.cpp:4:3:4:13 | +| VacuousDestructorCall.cpp:2:6:2:6 | v(int, int *) -> void | 6 | 5 | 0 | (vacuous destructor call) | | | prvalue: void | VacuousDestructorCall.cpp:4:3:4:12 | +| VacuousDestructorCall.cpp:2:6:2:6 | v(int, int *) -> void | 7 | 1 | 2 | return ... | | | | VacuousDestructorCall.cpp:5:1:5:1 | +| VacuousDestructorCall.cpp:7:6:7:6 | f(int) -> void | 0 | -1 | 0 | f | | | | Throw.cpp:6:6:6:6 | +| VacuousDestructorCall.cpp:7:6:7:6 | f(int) -> void | 0 | -1 | 0 | f | | | | VacuousDestructorCall.cpp:7:6:7:6 | +| VacuousDestructorCall.cpp:7:6:7:6 | f(int) -> void | 1 | 0 | 0 | { ... } | | | | Throw.cpp:6:15:16:1 | +| VacuousDestructorCall.cpp:7:6:7:6 | f(int) -> void | 1 | 0 | 0 | { ... } | | | | VacuousDestructorCall.cpp:7:15:11:1 | +| VacuousDestructorCall.cpp:7:6:7:6 | f(int) -> void | 2 | 1 | 0 | ExprStmt | | | | VacuousDestructorCall.cpp:10:3:10:11 | +| VacuousDestructorCall.cpp:7:6:7:6 | f(int) -> void | 2 | 1 | 0 | try { ... } | | | | Throw.cpp:7:3:12:3 | +| VacuousDestructorCall.cpp:7:6:7:6 | f(int) -> void | 3 | 2 | 0 | call to v | | | prvalue: void | VacuousDestructorCall.cpp:10:3:10:3 | +| VacuousDestructorCall.cpp:7:6:7:6 | f(int) -> void | 3 | 2 | 0 | { ... } | | | | Throw.cpp:7:7:12:3 | +| VacuousDestructorCall.cpp:7:6:7:6 | f(int) -> void | 4 | 3 | 0 | i | | | prvalue(load): int | VacuousDestructorCall.cpp:10:5:10:5 | +| VacuousDestructorCall.cpp:7:6:7:6 | f(int) -> void | 4 | 3 | 0 | if (...) ... | | | | Throw.cpp:8:5:11:16 | +| VacuousDestructorCall.cpp:7:6:7:6 | f(int) -> void | 5 | 3 | 1 | & ... | | | prvalue: int * | VacuousDestructorCall.cpp:10:8:10:9 | +| VacuousDestructorCall.cpp:7:6:7:6 | f(int) -> void | 5 | 4 | 0 | (bool)... | conversion to bool | | prvalue: bool | Throw.cpp:8:8:8:8 | +| VacuousDestructorCall.cpp:7:6:7:6 | f(int) -> void | 6 | 5 | 0 | i | | | lvalue: int | VacuousDestructorCall.cpp:10:9:10:9 | +| VacuousDestructorCall.cpp:7:6:7:6 | f(int) -> void | 6 | 5 | 0 | i | | | prvalue(load): int | Throw.cpp:8:8:8:8 | +| VacuousDestructorCall.cpp:7:6:7:6 | f(int) -> void | 7 | 1 | 1 | return ... | | | | VacuousDestructorCall.cpp:11:1:11:1 | +| VacuousDestructorCall.cpp:7:6:7:6 | f(int) -> void | 7 | 4 | 1 | ExprStmt | | | | Throw.cpp:9:7:9:16 | +| VacuousDestructorCall.cpp:7:6:7:6 | f(int) -> void | 8 | 7 | 0 | throw ... | | | prvalue: E | Throw.cpp:9:7:9:15 | +| VacuousDestructorCall.cpp:7:6:7:6 | f(int) -> void | 9 | 8 | 0 | 0 | | =0 | prvalue: E | Throw.cpp:9:7:9:15 | +| VacuousDestructorCall.cpp:7:6:7:6 | f(int) -> void | 10 | 4 | 2 | ExprStmt | | | | Throw.cpp:11:7:11:16 | +| VacuousDestructorCall.cpp:7:6:7:6 | f(int) -> void | 11 | 10 | 0 | throw ... | | | prvalue: F | Throw.cpp:11:7:11:15 | +| VacuousDestructorCall.cpp:7:6:7:6 | f(int) -> void | 12 | 11 | 0 | call to F | | | prvalue: void | Throw.cpp:11:7:11:15 | +| VacuousDestructorCall.cpp:7:6:7:6 | f(int) -> void | 13 | 2 | 1 | | | | | Throw.cpp:12:17:14:3 | +| VacuousDestructorCall.cpp:7:6:7:6 | f(int) -> void | 14 | 13 | 0 | { ... } | | | | Throw.cpp:12:17:14:3 | +| VacuousDestructorCall.cpp:7:6:7:6 | f(int) -> void | 15 | 14 | 0 | ExprStmt | | | | Throw.cpp:13:5:13:10 | +| VacuousDestructorCall.cpp:7:6:7:6 | f(int) -> void | 16 | 15 | 0 | re-throw exception | | | prvalue: void | Throw.cpp:13:5:13:9 | +| Varargs.c:8:6:8:11 | output(const char *) -> void | 0 | -1 | 0 | output | | | | Varargs.c:8:6:8:11 | +| Varargs.c:8:6:8:11 | output(const char *) -> void | 1 | 0 | 0 | { ... } | | | | Varargs.c:8:36:12:1 | +| Varargs.c:8:6:8:11 | output(const char *) -> void | 2 | 1 | 0 | declaration | | | | Varargs.c:9:3:9:15 | +| Varargs.c:8:6:8:11 | output(const char *) -> void | 3 | 2 | 0 | definition of args | | | va_list | Varargs.c:9:11:9:14 | +| Varargs.c:8:6:8:11 | output(const char *) -> void | 4 | 1 | 1 | ExprStmt | | | | Varargs.c:10:3:10:23 | +| Varargs.c:8:6:8:11 | output(const char *) -> void | 5 | 4 | 0 | __builtin_va_start | | | prvalue: void | Varargs.c:10:3:10:22 | +| Varargs.c:8:6:8:11 | output(const char *) -> void | 6 | 5 | 0 | array to pointer conversion | | | prvalue: __va_list_tag * | Varargs.c:10:12:10:15 | +| Varargs.c:8:6:8:11 | output(const char *) -> void | 7 | 6 | 0 | args | | | lvalue: va_list | Varargs.c:10:12:10:15 | +| Varargs.c:8:6:8:11 | output(const char *) -> void | 8 | 5 | 1 | text | | | lvalue: const char * | Varargs.c:10:18:10:21 | +| Varargs.c:8:6:8:11 | output(const char *) -> void | 9 | 1 | 2 | ExprStmt | | | | Varargs.c:11:3:11:16 | +| Varargs.c:8:6:8:11 | output(const char *) -> void | 10 | 9 | 0 | __builtin_va_end | | | prvalue: void | Varargs.c:11:3:11:15 | +| Varargs.c:8:6:8:11 | output(const char *) -> void | 11 | 10 | 0 | array to pointer conversion | | | prvalue: __va_list_tag * | Varargs.c:11:11:11:14 | +| Varargs.c:8:6:8:11 | output(const char *) -> void | 12 | 11 | 0 | args | | | lvalue: va_list | Varargs.c:11:11:11:14 | +| Varargs.c:8:6:8:11 | output(const char *) -> void | 13 | 1 | 3 | return ... | | | | Varargs.c:12:1:12:1 | diff --git a/cpp/ql/test/examples/expressions/PrintAST.qlref b/cpp/ql/test/examples/expressions/PrintAST.qlref new file mode 100644 index 000000000000..6fcb30ac7a6e --- /dev/null +++ b/cpp/ql/test/examples/expressions/PrintAST.qlref @@ -0,0 +1 @@ +semmle/code/cpp/PrintAST.ql \ No newline at end of file diff --git a/cpp/ql/test/examples/expressions/ReferenceDereference.cpp b/cpp/ql/test/examples/expressions/ReferenceDereference.cpp new file mode 100644 index 000000000000..347e962c4c2d --- /dev/null +++ b/cpp/ql/test/examples/expressions/ReferenceDereference.cpp @@ -0,0 +1,6 @@ +/* + there is an implicit compiler-generated dereference node before the access to the variable 'i' +*/ +void v(int &i, int j) { + j = i; +} \ No newline at end of file diff --git a/cpp/ql/test/examples/expressions/ReferenceTo.cpp b/cpp/ql/test/examples/expressions/ReferenceTo.cpp new file mode 100644 index 000000000000..83ddffca6ca7 --- /dev/null +++ b/cpp/ql/test/examples/expressions/ReferenceTo.cpp @@ -0,0 +1,3 @@ +int& v(int *i) { + return *i; +} \ No newline at end of file diff --git a/cpp/ql/test/examples/expressions/Sizeof.c b/cpp/ql/test/examples/expressions/Sizeof.c new file mode 100644 index 000000000000..0fbccfe618d5 --- /dev/null +++ b/cpp/ql/test/examples/expressions/Sizeof.c @@ -0,0 +1,4 @@ +void v(int array[]) { + int i = sizeof(int); + int j = sizeof(array); +} \ No newline at end of file diff --git a/cpp/ql/test/examples/expressions/StatementExpr.c b/cpp/ql/test/examples/expressions/StatementExpr.c new file mode 100644 index 000000000000..89910e714ac1 --- /dev/null +++ b/cpp/ql/test/examples/expressions/StatementExpr.c @@ -0,0 +1,3 @@ +void v() { + int j = ({ int i = 5; i; }); +} \ No newline at end of file diff --git a/cpp/ql/test/examples/expressions/StaticMemberAccess.cpp b/cpp/ql/test/examples/expressions/StaticMemberAccess.cpp new file mode 100644 index 000000000000..b67c02277057 --- /dev/null +++ b/cpp/ql/test/examples/expressions/StaticMemberAccess.cpp @@ -0,0 +1,9 @@ +struct X { + static int i; +}; + +void v(int i, X &xref) { +// i = X::i; + i = xref.i; + +} \ No newline at end of file diff --git a/cpp/ql/test/examples/expressions/Subscript.c b/cpp/ql/test/examples/expressions/Subscript.c new file mode 100644 index 000000000000..fc8d48580c10 --- /dev/null +++ b/cpp/ql/test/examples/expressions/Subscript.c @@ -0,0 +1,3 @@ +void v(int i[], int j) { + j = i[5]; +} \ No newline at end of file diff --git a/cpp/ql/test/examples/expressions/Throw.cpp b/cpp/ql/test/examples/expressions/Throw.cpp new file mode 100644 index 000000000000..85cb7dc65974 --- /dev/null +++ b/cpp/ql/test/examples/expressions/Throw.cpp @@ -0,0 +1,16 @@ +class E { }; +class F { + public: + F() { } +}; +void f(int i) { + try { + if(i) + throw E(); + else + throw F(); + } catch(E *e) { + throw; + } + +} \ No newline at end of file diff --git a/cpp/ql/test/examples/expressions/Typeid.cpp b/cpp/ql/test/examples/expressions/Typeid.cpp new file mode 100644 index 000000000000..b517d300373d --- /dev/null +++ b/cpp/ql/test/examples/expressions/Typeid.cpp @@ -0,0 +1,20 @@ + +namespace std +{ + class type_info + { + public: + const char *name() const; + }; +} + +class Base { + public: + virtual void v() { } +}; +class Derived : public Base { +}; + +void v(Base *bp) { + const char *name = typeid(bp).name(); +} \ No newline at end of file diff --git a/cpp/ql/test/examples/expressions/VacuousDestructorCall.cpp b/cpp/ql/test/examples/expressions/VacuousDestructorCall.cpp new file mode 100644 index 000000000000..6c0260f382d8 --- /dev/null +++ b/cpp/ql/test/examples/expressions/VacuousDestructorCall.cpp @@ -0,0 +1,11 @@ +template +void v(T x, T *y) { + x.T::~T(); + y->T::~T(); +} + +void f(int i) { + // An int doesn't have a destructor, but we get to call it anyway through a + // template. + v(i, &i); +} diff --git a/cpp/ql/test/examples/expressions/Varargs.c b/cpp/ql/test/examples/expressions/Varargs.c new file mode 100644 index 000000000000..4a81b8cb5f6b --- /dev/null +++ b/cpp/ql/test/examples/expressions/Varargs.c @@ -0,0 +1,12 @@ +typedef __builtin_va_list __gnuc_va_list; +#define va_start(v,l) __builtin_va_start(v,l) +#define va_end(v) __builtin_va_end(v) +#define va_arg(v,l) __builtin_va_arg(v,l) +#define va_copy(d,s) __builtin_va_copy(d,s) +typedef __gnuc_va_list va_list; + +void output(const char *text, ...) { + va_list args; + va_start(args, text); + va_end (args); +} diff --git a/cpp/ql/test/examples/lgtm-query-examples/README.md b/cpp/ql/test/examples/lgtm-query-examples/README.md new file mode 100644 index 000000000000..4d5bba173c6d --- /dev/null +++ b/cpp/ql/test/examples/lgtm-query-examples/README.md @@ -0,0 +1,3 @@ +This directory contains the C++ demo queries used on LGTM that are not also +standard queries. Maintaining this copy should ensure that they continue to +work. diff --git a/cpp/ql/test/examples/lgtm-query-examples/assignment_to_parameter.expected b/cpp/ql/test/examples/lgtm-query-examples/assignment_to_parameter.expected new file mode 100644 index 000000000000..2a862f3fdea9 --- /dev/null +++ b/cpp/ql/test/examples/lgtm-query-examples/assignment_to_parameter.expected @@ -0,0 +1 @@ +| test.cpp:10:5:10:17 | ... = ... | Assignment of '0' to parameter '$@' has no effect. | test.cpp:3:42:3:44 | buf | buf | diff --git a/cpp/ql/test/examples/lgtm-query-examples/assignment_to_parameter.ql b/cpp/ql/test/examples/lgtm-query-examples/assignment_to_parameter.ql new file mode 100644 index 000000000000..0fea65969487 --- /dev/null +++ b/cpp/ql/test/examples/lgtm-query-examples/assignment_to_parameter.ql @@ -0,0 +1,17 @@ +/** + * @name Assignment to parameter has no effect + * @description An assignment to a parameter that is not subsequently read may + * indicate a logic error. + * @kind problem + * @problem.severity warning + */ + +import cpp + +from Parameter p, Assignment assign +where assign = p.getAnAssignment() + and not assign.getASuccessor+() = p.getAnAccess() + and not p.getType() instanceof ReferenceType +select assign, "Assignment of '"+ assign.getRValue() + + "' to parameter '$@' has no effect.", + p, p.getName() diff --git a/cpp/ql/test/examples/lgtm-query-examples/equality_test_on_boolean.expected b/cpp/ql/test/examples/lgtm-query-examples/equality_test_on_boolean.expected new file mode 100644 index 000000000000..f08cbd1fbd0f --- /dev/null +++ b/cpp/ql/test/examples/lgtm-query-examples/equality_test_on_boolean.expected @@ -0,0 +1 @@ +| test.cpp:4:9:4:17 | ... == ... | Expression tested for equality with 'true' | diff --git a/cpp/ql/test/examples/lgtm-query-examples/equality_test_on_boolean.ql b/cpp/ql/test/examples/lgtm-query-examples/equality_test_on_boolean.ql new file mode 100644 index 000000000000..083c4bc1c683 --- /dev/null +++ b/cpp/ql/test/examples/lgtm-query-examples/equality_test_on_boolean.ql @@ -0,0 +1,15 @@ +/** + * @name Equality test on Boolean + * @description Testing equality with `true` is redundant + * and can make the code harder to read. + * @kind problem + * @problem.severity warning + */ +import cpp + +from EqualityOperation eq, Expr trueExpr +where + trueExpr = eq.getAnOperand() and + trueExpr.getType() instanceof BoolType and + trueExpr.getValue().toInt() = 1 +select eq, "Expression tested for equality with 'true'" diff --git a/cpp/ql/test/examples/lgtm-query-examples/non_literal_format_string.expected b/cpp/ql/test/examples/lgtm-query-examples/non_literal_format_string.expected new file mode 100644 index 000000000000..264d14536209 --- /dev/null +++ b/cpp/ql/test/examples/lgtm-query-examples/non_literal_format_string.expected @@ -0,0 +1 @@ +| test.cpp:5:9:5:15 | call to sprintf | sprintf called with variable format string. | diff --git a/cpp/ql/test/examples/lgtm-query-examples/non_literal_format_string.ql b/cpp/ql/test/examples/lgtm-query-examples/non_literal_format_string.ql new file mode 100644 index 000000000000..9c1eb974d68e --- /dev/null +++ b/cpp/ql/test/examples/lgtm-query-examples/non_literal_format_string.ql @@ -0,0 +1,16 @@ +/** + * @name Call to sprintf with non-literal format string + * @description Passing a non-constant 'format' string to a printf-like + * function can lead to a mismatch between the number of arguments + * defined by the 'format' and the number of arguments actually + * passed to the function. If the format string ultimately stems + * from an untrusted source, this can be used for exploits. + * @kind problem + * @problem.severity warning + */ +import cpp + +from FunctionCall fc +where fc.getTarget().getQualifiedName() = "sprintf" +and not fc.getArgument(1) instanceof StringLiteral +select fc, "sprintf called with variable format string." diff --git a/cpp/ql/test/examples/lgtm-query-examples/test.cpp b/cpp/ql/test/examples/lgtm-query-examples/test.cpp new file mode 100644 index 000000000000..17731af04d29 --- /dev/null +++ b/cpp/ql/test/examples/lgtm-query-examples/test.cpp @@ -0,0 +1,11 @@ +long sprintf(char *buf, const char *format, ...); + +void f(bool b, const char *format, char *buf) { + if (b == true) { // BAD + sprintf(buf, format, 5); // BAD + } else if (!b) { // GOOD + buf = buf + 1; // GOOD + sprintf(buf, "%d", 5); // GOOD + } + buf = nullptr; // BAD +} diff --git a/cpp/ql/test/header-variant-tests/clang-pch/_.c b/cpp/ql/test/header-variant-tests/clang-pch/_.c new file mode 100644 index 000000000000..6186edd586f7 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/clang-pch/_.c @@ -0,0 +1,3 @@ +// This file exists to ensure that the output subdirectory exists prior to +// a.c being indexed, as said directory needs to exist for the PCH file to +// be created, and will be created by running the extractor. diff --git a/cpp/ql/test/header-variant-tests/clang-pch/a.c b/cpp/ql/test/header-variant-tests/clang-pch/a.c new file mode 100644 index 000000000000..9164cdc19a58 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/clang-pch/a.c @@ -0,0 +1,3 @@ +#include "a.h" +#define FOUR 4 +// semmle-extractor-options: --clang -emit-pch -o ${testdir}/clang-pch.testproj/a.pch diff --git a/cpp/ql/test/header-variant-tests/clang-pch/a.h b/cpp/ql/test/header-variant-tests/clang-pch/a.h new file mode 100644 index 000000000000..f01809a81bd3 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/clang-pch/a.h @@ -0,0 +1 @@ +#define ONE 1 diff --git a/cpp/ql/test/header-variant-tests/clang-pch/b.c b/cpp/ql/test/header-variant-tests/clang-pch/b.c new file mode 100644 index 000000000000..76369e11f221 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/clang-pch/b.c @@ -0,0 +1,7 @@ +#define TWO 2 +#include "b.h" + +int main() { + return ONE + TWO + THREE + FOUR; +} +// semmle-extractor-options: --clang -include-pch ${testdir}/clang-pch.testproj/a.pch -Iextra_dummy_path diff --git a/cpp/ql/test/header-variant-tests/clang-pch/b.h b/cpp/ql/test/header-variant-tests/clang-pch/b.h new file mode 100644 index 000000000000..77685b4fb1d1 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/clang-pch/b.h @@ -0,0 +1 @@ +#define THREE 3 diff --git a/cpp/ql/test/header-variant-tests/clang-pch/c.c b/cpp/ql/test/header-variant-tests/clang-pch/c.c new file mode 100644 index 000000000000..0a8b97667137 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/clang-pch/c.c @@ -0,0 +1,4 @@ +int main() { + return ONE + FOUR; +} +// semmle-extractor-options: --clang -include ${testdir}/clang-pch.testproj/a -Iextra_dummy_path diff --git a/cpp/ql/test/header-variant-tests/clang-pch/clang-pch.expected b/cpp/ql/test/header-variant-tests/clang-pch/clang-pch.expected new file mode 100644 index 000000000000..78c63f748e4d --- /dev/null +++ b/cpp/ql/test/header-variant-tests/clang-pch/clang-pch.expected @@ -0,0 +1,6 @@ +| b.c:5:3:5:34 | return ... | 10 | +| c.c:2:3:2:20 | return ... | 5 | +| e.c:2:3:2:19 | return ... | 17 | +| i.c:3:3:3:12 | return ... | 30 | +| i.c:8:3:8:12 | return ... | 31 | +| i.c:13:3:13:12 | return ... | 32 | diff --git a/cpp/ql/test/header-variant-tests/clang-pch/clang-pch.ql b/cpp/ql/test/header-variant-tests/clang-pch/clang-pch.ql new file mode 100644 index 000000000000..143880a2b248 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/clang-pch/clang-pch.ql @@ -0,0 +1,4 @@ +import cpp + +from ReturnStmt rs +select rs, rs.getExpr().getValue() diff --git a/cpp/ql/test/header-variant-tests/clang-pch/d.c b/cpp/ql/test/header-variant-tests/clang-pch/d.c new file mode 100644 index 000000000000..f81af3d1b63c --- /dev/null +++ b/cpp/ql/test/header-variant-tests/clang-pch/d.c @@ -0,0 +1,2 @@ +#import "d.h" +// semmle-extractor-options: --clang -emit-pch -o ${testdir}/clang-pch.testproj/d.pch diff --git a/cpp/ql/test/header-variant-tests/clang-pch/d.h b/cpp/ql/test/header-variant-tests/clang-pch/d.h new file mode 100644 index 000000000000..56c76ce4dc13 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/clang-pch/d.h @@ -0,0 +1 @@ +enum { SEVENTEEN = 17 }; diff --git a/cpp/ql/test/header-variant-tests/clang-pch/e.c b/cpp/ql/test/header-variant-tests/clang-pch/e.c new file mode 100644 index 000000000000..abdb59002009 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/clang-pch/e.c @@ -0,0 +1,4 @@ +int main() { + return SEVENTEEN; +} +// semmle-extractor-options: --clang -include-pch ${testdir}/clang-pch.testproj/d.pch diff --git a/cpp/ql/test/header-variant-tests/clang-pch/f.c b/cpp/ql/test/header-variant-tests/clang-pch/f.c new file mode 100644 index 000000000000..cca56931acd5 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/clang-pch/f.c @@ -0,0 +1,6 @@ +#if 1 +#pragma hdrstop +extern int x; +#define SEEN_F +#endif +// semmle-extractor-options: --clang -emit-pch -o ${testdir}/clang-pch.testproj/f.pch diff --git a/cpp/ql/test/header-variant-tests/clang-pch/g.c b/cpp/ql/test/header-variant-tests/clang-pch/g.c new file mode 100644 index 000000000000..feabf6be4a7c --- /dev/null +++ b/cpp/ql/test/header-variant-tests/clang-pch/g.c @@ -0,0 +1,6 @@ +#ifdef SEEN_F +static int g() { + return 20; +} +#endif +// semmle-extractor-options: --clang -include-pch ${testdir}/clang-pch.testproj/f.pch --expect_errors diff --git a/cpp/ql/test/header-variant-tests/clang-pch/h.c b/cpp/ql/test/header-variant-tests/clang-pch/h.c new file mode 100644 index 000000000000..7ffc9b6133c1 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/clang-pch/h.c @@ -0,0 +1,5 @@ +#include "h1.h" +#pragma hdrstop +#include "h2.h" +#define SEEN_H +// semmle-extractor-options: --clang -emit-pch -o ${testdir}/clang-pch.testproj/h.pch diff --git a/cpp/ql/test/header-variant-tests/clang-pch/h1.h b/cpp/ql/test/header-variant-tests/clang-pch/h1.h new file mode 100644 index 000000000000..aab8ad06bafe --- /dev/null +++ b/cpp/ql/test/header-variant-tests/clang-pch/h1.h @@ -0,0 +1,3 @@ + +#define H1 + diff --git a/cpp/ql/test/header-variant-tests/clang-pch/h2.h b/cpp/ql/test/header-variant-tests/clang-pch/h2.h new file mode 100644 index 000000000000..a235a9f17f45 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/clang-pch/h2.h @@ -0,0 +1,3 @@ + +#define H2 + diff --git a/cpp/ql/test/header-variant-tests/clang-pch/i.c b/cpp/ql/test/header-variant-tests/clang-pch/i.c new file mode 100644 index 000000000000..f162aa818d94 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/clang-pch/i.c @@ -0,0 +1,16 @@ +#ifdef SEEN_H +static int h() { + return 30; // [FALSE POSITIVE] (#pragma hdrstop bug, SEEN_H should not be defined in the precompiled header) +} +#endif +#ifdef H1 +static int h1() { + return 31; +} +#endif +#ifdef H2 +static int h2() { + return 32; // [FALSE POSITIVE] (#pragma hdrstop bug, H2 should not be defined in the precompiled header) +} +#endif +// semmle-extractor-options: --clang -include-pch ${testdir}/clang-pch.testproj/h.pch diff --git a/cpp/ql/test/header-variant-tests/deduplication/bar.h b/cpp/ql/test/header-variant-tests/deduplication/bar.h new file mode 100644 index 000000000000..dc712a5029e7 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/deduplication/bar.h @@ -0,0 +1,8 @@ +const char* bar() +{ +#ifndef BAR + return "bar"; +#else + return "baz"; +#endif +} diff --git a/cpp/ql/test/header-variant-tests/deduplication/classes.expected b/cpp/ql/test/header-variant-tests/deduplication/classes.expected new file mode 100644 index 000000000000..5f909e716631 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/deduplication/classes.expected @@ -0,0 +1,4 @@ +| file://:0:0:0:0 | __va_list_tag | 1 distinct class(es) called __va_list_tag | +| main2.cpp:7:7:7:7 | C | 2 distinct class(es) called C | +| main2.cpp:7:7:7:7 | C | 2 distinct class(es) called C | +| main2.cpp:15:7:15:7 | D | 1 distinct class(es) called D | diff --git a/cpp/ql/test/header-variant-tests/deduplication/classes.ql b/cpp/ql/test/header-variant-tests/deduplication/classes.ql new file mode 100644 index 000000000000..2fe0098c9cf7 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/deduplication/classes.ql @@ -0,0 +1,5 @@ +import default + +from Class c, string n +where n = count(Class x | x.getName() = c.getName()) + " distinct class(es) called " + c.getName() +select c, n diff --git a/cpp/ql/test/header-variant-tests/deduplication/foo.h b/cpp/ql/test/header-variant-tests/deduplication/foo.h new file mode 100644 index 000000000000..5d74392fb01f --- /dev/null +++ b/cpp/ql/test/header-variant-tests/deduplication/foo.h @@ -0,0 +1,10 @@ +const char* foo() +{ + return "foo"; +} + +#ifdef FOO +int foo_defined; +#else +int foo_not_defined; +#endif diff --git a/cpp/ql/test/header-variant-tests/deduplication/functions.expected b/cpp/ql/test/header-variant-tests/deduplication/functions.expected new file mode 100644 index 000000000000..a5ffe6852907 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/deduplication/functions.expected @@ -0,0 +1,15 @@ +| bar.h:1:13:1:15 | bar | Function | 2 | | +| bar.h:1:13:1:15 | bar | MemberFunction | 1 | C | +| bar.h:1:13:1:15 | bar | MemberFunction | 1 | C | +| bar.h:1:13:1:15 | bar | MemberFunction | 2 | D | +| file://:0:0:0:0 | operator= | MemberFunction | 0 | __va_list_tag | +| file://:0:0:0:0 | operator= | MemberFunction | 0 | __va_list_tag | +| 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 | operator= | 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 | +| main2.cpp:15:7:15:7 | operator= | MemberFunction | 0 | D | +| main2.cpp:15:7:15:7 | operator= | MemberFunction | 0 | D | diff --git a/cpp/ql/test/header-variant-tests/deduplication/functions.ql b/cpp/ql/test/header-variant-tests/deduplication/functions.ql new file mode 100644 index 000000000000..8265e5dfab5c --- /dev/null +++ b/cpp/ql/test/header-variant-tests/deduplication/functions.ql @@ -0,0 +1,9 @@ +import cpp + +from Function f, string d, string c +where if f instanceof MemberFunction then ( + d = "MemberFunction" and c = ((MemberFunction)f).getDeclaringType().getName() +) else ( + d = "Function" and c = "" +) +select f, d, count(f.getBlock()), c diff --git a/cpp/ql/test/header-variant-tests/deduplication/main1.cpp b/cpp/ql/test/header-variant-tests/deduplication/main1.cpp new file mode 100644 index 000000000000..f4f5ac2bb1ec --- /dev/null +++ b/cpp/ql/test/header-variant-tests/deduplication/main1.cpp @@ -0,0 +1,3 @@ +#define FOO +#define BAR +#include "main2.cpp" diff --git a/cpp/ql/test/header-variant-tests/deduplication/main2.cpp b/cpp/ql/test/header-variant-tests/deduplication/main2.cpp new file mode 100644 index 000000000000..1ea7730d3411 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/deduplication/main2.cpp @@ -0,0 +1,18 @@ +#include "foo.h" +#include "bar.h" + +// This will cause multiple, incompatible definitions of C since the member +// variable in foo.h has different names depending on whether or not FOO is +// defined. +class C +{ +#include "foo.h" +#include "bar.h" +}; + +// All definitions of D should be merged, since all members have the same name +// and type. +class D +{ +#include "bar.h" +}; diff --git a/cpp/ql/test/header-variant-tests/deduplication/variables.expected b/cpp/ql/test/header-variant-tests/deduplication/variables.expected new file mode 100644 index 000000000000..05233432cdbd --- /dev/null +++ b/cpp/ql/test/header-variant-tests/deduplication/variables.expected @@ -0,0 +1,14 @@ +| file://:0:0:0:0 | fp_offset | file://:0:0:0:0 | unsigned int | MemberVariable | __va_list_tag | +| file://:0:0:0:0 | gp_offset | file://:0:0:0:0 | unsigned int | MemberVariable | __va_list_tag | +| file://:0:0:0:0 | overflow_arg_area | file://:0:0:0:0 | void * | MemberVariable | __va_list_tag | +| file://:0:0:0:0 | p#0 | file://:0:0:0:0 | C && | Variable | | +| file://:0:0:0:0 | p#0 | file://:0:0:0:0 | C && | Variable | | +| file://:0:0:0:0 | p#0 | file://:0:0:0:0 | D && | Variable | | +| file://:0:0:0:0 | p#0 | file://:0:0:0:0 | const C & | Variable | | +| file://:0:0:0:0 | p#0 | file://:0:0:0:0 | const C & | Variable | | +| file://:0:0:0:0 | p#0 | file://:0:0:0:0 | const D & | Variable | | +| file://:0:0:0:0 | reg_save_area | file://:0:0:0:0 | void * | MemberVariable | __va_list_tag | +| foo.h:7:5:7:15 | foo_defined | file://:0:0:0:0 | int | MemberVariable | C | +| foo.h:7:5:7:15 | foo_defined | file://:0:0:0:0 | int | Variable | | +| foo.h:9:5:9:19 | foo_not_defined | file://:0:0:0:0 | int | MemberVariable | C | +| foo.h:9:5:9:19 | foo_not_defined | file://:0:0:0:0 | int | Variable | | diff --git a/cpp/ql/test/header-variant-tests/deduplication/variables.ql b/cpp/ql/test/header-variant-tests/deduplication/variables.ql new file mode 100644 index 000000000000..1dc60d846156 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/deduplication/variables.ql @@ -0,0 +1,9 @@ +import cpp + +from Variable v, string d, string c +where if v instanceof MemberVariable then ( + d = "MemberVariable" and c = ((MemberVariable)v).getDeclaringType().getName() +) else ( + d = "Variable" and c = "" +) +select v, v.getType(), d, c diff --git a/cpp/ql/test/header-variant-tests/functions-in-headers/a.h b/cpp/ql/test/header-variant-tests/functions-in-headers/a.h new file mode 100644 index 000000000000..7d359607cf20 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/functions-in-headers/a.h @@ -0,0 +1,10 @@ +int complex() +{ + auto x = 4; + return x * x; +} + +static int simple() +{ + return SIMPLE; +} diff --git a/cpp/ql/test/header-variant-tests/functions-in-headers/both_variants.expected b/cpp/ql/test/header-variant-tests/functions-in-headers/both_variants.expected new file mode 100644 index 000000000000..f270a9d622ef --- /dev/null +++ b/cpp/ql/test/header-variant-tests/functions-in-headers/both_variants.expected @@ -0,0 +1,2 @@ +| a.h:9:3:9:16 | return ... | a.h:7:12:7:17 | simple | 1 | +| a.h:9:3:9:16 | return ... | a.h:7:12:7:17 | simple | 2 | diff --git a/cpp/ql/test/header-variant-tests/functions-in-headers/both_variants.ql b/cpp/ql/test/header-variant-tests/functions-in-headers/both_variants.ql new file mode 100644 index 000000000000..4887df40e8db --- /dev/null +++ b/cpp/ql/test/header-variant-tests/functions-in-headers/both_variants.ql @@ -0,0 +1,4 @@ +import cpp + +from ReturnStmt r +select r, r.getEnclosingFunction(), r.getExpr().getValue() diff --git a/cpp/ql/test/header-variant-tests/functions-in-headers/one.cpp b/cpp/ql/test/header-variant-tests/functions-in-headers/one.cpp new file mode 100644 index 000000000000..e1907a0e097d --- /dev/null +++ b/cpp/ql/test/header-variant-tests/functions-in-headers/one.cpp @@ -0,0 +1,4 @@ +#define SIMPLE 1 +#include "a.h" + +int one() { return simple(); } \ No newline at end of file diff --git a/cpp/ql/test/header-variant-tests/functions-in-headers/one_x.expected b/cpp/ql/test/header-variant-tests/functions-in-headers/one_x.expected new file mode 100644 index 000000000000..990c69a3946d --- /dev/null +++ b/cpp/ql/test/header-variant-tests/functions-in-headers/one_x.expected @@ -0,0 +1,5 @@ +| a.h:3:8:3:8 | x | x | true | +| file://:0:0:0:0 | fp_offset | fp_offset | false | +| file://:0:0:0:0 | gp_offset | gp_offset | false | +| file://:0:0:0:0 | overflow_arg_area | overflow_arg_area | false | +| file://:0:0:0:0 | reg_save_area | reg_save_area | false | diff --git a/cpp/ql/test/header-variant-tests/functions-in-headers/one_x.ql b/cpp/ql/test/header-variant-tests/functions-in-headers/one_x.ql new file mode 100644 index 000000000000..c48216313642 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/functions-in-headers/one_x.ql @@ -0,0 +1,5 @@ +import cpp + +from Variable x, boolean auto +where if x.declaredUsingAutoType() then auto = true else auto = false +select x, x.getName(), auto diff --git a/cpp/ql/test/header-variant-tests/functions-in-headers/two.cpp b/cpp/ql/test/header-variant-tests/functions-in-headers/two.cpp new file mode 100644 index 000000000000..3e9a5131c5d1 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/functions-in-headers/two.cpp @@ -0,0 +1,4 @@ +#define SIMPLE 2 +#include "a.h" + +int two() { return simple(); } \ No newline at end of file diff --git a/cpp/ql/test/header-variant-tests/headermaps/1.h b/cpp/ql/test/header-variant-tests/headermaps/1.h new file mode 100644 index 000000000000..5aa756c6f219 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/headermaps/1.h @@ -0,0 +1 @@ +static int one = __COUNTER__; diff --git a/cpp/ql/test/header-variant-tests/headermaps/2.h b/cpp/ql/test/header-variant-tests/headermaps/2.h new file mode 100644 index 000000000000..80c0489bafe4 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/headermaps/2.h @@ -0,0 +1 @@ +static int two = __COUNTER__; \ No newline at end of file diff --git a/cpp/ql/test/header-variant-tests/headermaps/3.h b/cpp/ql/test/header-variant-tests/headermaps/3.h new file mode 100644 index 000000000000..21c3bb1e48e6 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/headermaps/3.h @@ -0,0 +1 @@ +static int three = __COUNTER__; \ No newline at end of file diff --git a/cpp/ql/test/header-variant-tests/headermaps/4.h b/cpp/ql/test/header-variant-tests/headermaps/4.h new file mode 100644 index 000000000000..bbe3dc057051 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/headermaps/4.h @@ -0,0 +1 @@ +static int four = __COUNTER__; diff --git a/cpp/ql/test/header-variant-tests/headermaps/5.h b/cpp/ql/test/header-variant-tests/headermaps/5.h new file mode 100644 index 000000000000..c621c57ab285 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/headermaps/5.h @@ -0,0 +1 @@ +static int five = __COUNTER__; diff --git a/cpp/ql/test/header-variant-tests/headermaps/6.h b/cpp/ql/test/header-variant-tests/headermaps/6.h new file mode 100644 index 000000000000..858b0eb9a537 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/headermaps/6.h @@ -0,0 +1 @@ +static int six = __COUNTER__; diff --git a/cpp/ql/test/header-variant-tests/headermaps/7.h b/cpp/ql/test/header-variant-tests/headermaps/7.h new file mode 100644 index 000000000000..7cc3a5329d53 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/headermaps/7.h @@ -0,0 +1 @@ +static int seven = __COUNTER__; diff --git a/cpp/ql/test/header-variant-tests/headermaps/8.h b/cpp/ql/test/header-variant-tests/headermaps/8.h new file mode 100644 index 000000000000..7e8614b22f17 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/headermaps/8.h @@ -0,0 +1 @@ +static int eight = __COUNTER__; diff --git a/cpp/ql/test/header-variant-tests/headermaps/big_langtests.hmap b/cpp/ql/test/header-variant-tests/headermaps/big_langtests.hmap new file mode 100644 index 000000000000..b0bf0dc0d68b Binary files /dev/null and b/cpp/ql/test/header-variant-tests/headermaps/big_langtests.hmap differ diff --git a/cpp/ql/test/header-variant-tests/headermaps/big_qltest.hmap b/cpp/ql/test/header-variant-tests/headermaps/big_qltest.hmap new file mode 100644 index 000000000000..d8c8271b8b1a Binary files /dev/null and b/cpp/ql/test/header-variant-tests/headermaps/big_qltest.hmap differ diff --git a/cpp/ql/test/header-variant-tests/headermaps/headermaps.c b/cpp/ql/test/header-variant-tests/headermaps/headermaps.c new file mode 100644 index 000000000000..9d340da7042d --- /dev/null +++ b/cpp/ql/test/header-variant-tests/headermaps/headermaps.c @@ -0,0 +1,15 @@ +static int zero = __COUNTER__; + +// these map to 1.h through 4.h via little.hmap +#include "a.h" +#include "b.h" +#include "C.H" +#include "D.H" + +// these map to 5.h through 8.h via big.hmap +#include "e.h" +#include "f.h" +#include "G.H" +#include "H.H" + +// semmle-extractor-options: -I${testdir}/little_qltest.hmap -I${testdir}/big_qltest.hmap diff --git a/cpp/ql/test/header-variant-tests/headermaps/headermaps.expected b/cpp/ql/test/header-variant-tests/headermaps/headermaps.expected new file mode 100644 index 000000000000..3f0afe13eb05 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/headermaps/headermaps.expected @@ -0,0 +1,9 @@ +| 1.h:1:12:1:14 | one | 1 | +| 2.h:1:12:1:14 | two | 2 | +| 3.h:1:12:1:16 | three | 3 | +| 4.h:1:12:1:15 | four | 4 | +| 5.h:1:12:1:15 | five | 5 | +| 6.h:1:12:1:14 | six | 6 | +| 7.h:1:12:1:16 | seven | 7 | +| 8.h:1:12:1:16 | eight | 8 | +| headermaps.c:1:12:1:15 | zero | 0 | diff --git a/cpp/ql/test/header-variant-tests/headermaps/headermaps.ql b/cpp/ql/test/header-variant-tests/headermaps/headermaps.ql new file mode 100644 index 000000000000..19157bc9ff2d --- /dev/null +++ b/cpp/ql/test/header-variant-tests/headermaps/headermaps.ql @@ -0,0 +1,4 @@ +import cpp + +from Variable v +select v, v.getInitializer().getExpr().getValue() diff --git a/cpp/ql/test/header-variant-tests/headermaps/little_langtests.hmap b/cpp/ql/test/header-variant-tests/headermaps/little_langtests.hmap new file mode 100644 index 000000000000..56ed71108e5d Binary files /dev/null and b/cpp/ql/test/header-variant-tests/headermaps/little_langtests.hmap differ diff --git a/cpp/ql/test/header-variant-tests/headermaps/little_qltest.hmap b/cpp/ql/test/header-variant-tests/headermaps/little_qltest.hmap new file mode 100644 index 000000000000..5b259d62e12b Binary files /dev/null and b/cpp/ql/test/header-variant-tests/headermaps/little_qltest.hmap differ diff --git a/cpp/ql/test/header-variant-tests/headermaps/make_maps.lua b/cpp/ql/test/header-variant-tests/headermaps/make_maps.lua new file mode 100644 index 000000000000..abaf7a1169f9 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/headermaps/make_maps.lua @@ -0,0 +1,90 @@ +-- This file generates the header map files used by this test. +-- (it is provided in case the maps need to be changed) + +local function hash(str) + local n = 0 + for c in str:lower():gmatch"." do + n = n + c:byte()*13 + end + return n +end + +local function write_map(filename, endian, mapping) + local num_entries = 0 + local max_value_length = 0 + for k, v in pairs(mapping) do + num_entries = num_entries + 1 + max_value_length = math.max(max_value_length, #v) + end + local num_buckets = 2^math.ceil(math.log(num_entries + 1)/math.log(2)) + local f = io.open(filename, "wb") + local function write_uint32(val) + local b0 = (val % 256); val = (val - b0) / 256 + local b1 = (val % 256); val = (val - b1) / 256 + local b2 = (val % 256); val = (val - b2) / 256 + local b3 = (val % 256); + if endian == "little" then + f:write(string.char(b0, b1, b2, b3)) + elseif endian == "big" then + f:write(string.char(b3, b2, b1, b0)) + else + error(("Expected endian of %q or %q, got %q"):format("little", "big", endian)) + end + end + if endian == "little" then + f:write("pamh\1\0") + else + f:write("hmap\0\1") + end + f:write("\0\0") + write_uint32(6*4 + num_buckets*12) + write_uint32(num_entries) + write_uint32(num_buckets) + write_uint32(max_value_length) + + local string_pieces = {"\0"} + local string_piece_length = 1 + local encode = setmetatable({}, {__index = function(cache, str) + local result = string_piece_length + cache[str] = result + str = str .."\0" + string_pieces[#string_pieces + 1] = str + string_piece_length = string_piece_length + #str + return result + end}) + local buckets = {} + for k, v in pairs(mapping) do + local i = hash(k) % num_buckets + while buckets[i] do + i = (i + 1) % num_buckets + end + local v1, v2 = v:match"(.*/)([^/]*)$" + buckets[i] = {encode[k], encode[v1], encode[v2]} + end + + for i = 0, num_buckets-1 do + for _, v in ipairs(buckets[i] or {0, 0, 0}) do + write_uint32(v) + end + end + f:write(table.unpack(string_pieces)) + + f:close() +end + +-- for langtests: +--local root = "semmlecode-cpp-tests/header-variant-tests/headermaps/" +-- for qltest: +local root = "./" +write_map("little.hmap", "little", { + ["a.h"] = root .."1.h", + ["B.h"] = root .."2.h", + ["c.H"] = root .."3.h", + ["D.H"] = root .."4.h", +}) +write_map("big.hmap", "big", { + ["e.h"] = root .."5.h", + ["F.h"] = root .."6.h", + ["g.H"] = root .."7.h", + ["H.H"] = root .."8.h", +}) diff --git a/cpp/ql/test/header-variant-tests/iquote/dir1/a.h b/cpp/ql/test/header-variant-tests/iquote/dir1/a.h new file mode 100644 index 000000000000..a14c8e050e97 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/iquote/dir1/a.h @@ -0,0 +1 @@ +static int dir1_a = __COUNTER__; diff --git a/cpp/ql/test/header-variant-tests/iquote/dir1/b.h b/cpp/ql/test/header-variant-tests/iquote/dir1/b.h new file mode 100644 index 000000000000..5056fdd94842 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/iquote/dir1/b.h @@ -0,0 +1 @@ +static int dir1_b = __COUNTER__; diff --git a/cpp/ql/test/header-variant-tests/iquote/dir2/a.h b/cpp/ql/test/header-variant-tests/iquote/dir2/a.h new file mode 100644 index 000000000000..5c6391b7e0c1 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/iquote/dir2/a.h @@ -0,0 +1 @@ +static int dir2_a = __COUNTER__; diff --git a/cpp/ql/test/header-variant-tests/iquote/iquote.c b/cpp/ql/test/header-variant-tests/iquote/iquote.c new file mode 100644 index 000000000000..a2b6e279a54b --- /dev/null +++ b/cpp/ql/test/header-variant-tests/iquote/iquote.c @@ -0,0 +1,6 @@ +#include "a.h" +#include +#include "b.h" +static int has_angle_b = __has_include(); + +// semmle-extractor-options: -I${testdir}/dir2 -iquote ${testdir}/dir1 --edg --clang diff --git a/cpp/ql/test/header-variant-tests/iquote/iquote.expected b/cpp/ql/test/header-variant-tests/iquote/iquote.expected new file mode 100644 index 000000000000..a12c6197c476 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/iquote/iquote.expected @@ -0,0 +1,4 @@ +| dir1/a.h:1:12:1:17 | dir1_a | 0 | +| dir1/b.h:1:12:1:17 | dir1_b | 2 | +| dir2/a.h:1:12:1:17 | dir2_a | 1 | +| iquote.c:4:12:4:22 | has_angle_b | 0 | diff --git a/cpp/ql/test/header-variant-tests/iquote/iquote.ql b/cpp/ql/test/header-variant-tests/iquote/iquote.ql new file mode 100644 index 000000000000..19157bc9ff2d --- /dev/null +++ b/cpp/ql/test/header-variant-tests/iquote/iquote.ql @@ -0,0 +1,4 @@ +import cpp + +from Variable v +select v, v.getInitializer().getExpr().getValue() diff --git a/cpp/ql/test/header-variant-tests/isysroot/isysroot.c b/cpp/ql/test/header-variant-tests/isysroot/isysroot.c new file mode 100644 index 000000000000..727f4bc0f603 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/isysroot/isysroot.c @@ -0,0 +1,3 @@ +#include "a.h" +#include "b.h" +// semmle-extractor-options: -I${testdir}/other -I/usr/include -isysroot ${testdir} diff --git a/cpp/ql/test/header-variant-tests/isysroot/isysroot.expected b/cpp/ql/test/header-variant-tests/isysroot/isysroot.expected new file mode 100644 index 000000000000..feee7901e690 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/isysroot/isysroot.expected @@ -0,0 +1,2 @@ +| other/b.h:0:0:0:0 | other/b.h | +| usr/include/a.h:0:0:0:0 | usr/include/a.h | diff --git a/cpp/ql/test/header-variant-tests/isysroot/isysroot.ql b/cpp/ql/test/header-variant-tests/isysroot/isysroot.ql new file mode 100644 index 000000000000..e5dfbd00e37e --- /dev/null +++ b/cpp/ql/test/header-variant-tests/isysroot/isysroot.ql @@ -0,0 +1,4 @@ +import cpp + +from HeaderFile hf +select hf diff --git a/cpp/ql/test/header-variant-tests/isysroot/other/b.h b/cpp/ql/test/header-variant-tests/isysroot/other/b.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/cpp/ql/test/header-variant-tests/isysroot/usr/include/a.h b/cpp/ql/test/header-variant-tests/isysroot/usr/include/a.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/cpp/ql/test/header-variant-tests/line-directives-in-functions/main.c b/cpp/ql/test/header-variant-tests/line-directives-in-functions/main.c new file mode 100644 index 000000000000..551f76e2a7bd --- /dev/null +++ b/cpp/ql/test/header-variant-tests/line-directives-in-functions/main.c @@ -0,0 +1,4 @@ +int main() { +#line 152 "TokenLookup.gperf" + return 0; +} diff --git a/cpp/ql/test/header-variant-tests/line-directives-in-functions/main.expected b/cpp/ql/test/header-variant-tests/line-directives-in-functions/main.expected new file mode 100644 index 000000000000..b5e11c3cb8a1 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/line-directives-in-functions/main.expected @@ -0,0 +1 @@ +| main.c:3:3:3:11 | return ... | diff --git a/cpp/ql/test/header-variant-tests/line-directives-in-functions/main.ql b/cpp/ql/test/header-variant-tests/line-directives-in-functions/main.ql new file mode 100644 index 000000000000..75b5c5ad279c --- /dev/null +++ b/cpp/ql/test/header-variant-tests/line-directives-in-functions/main.ql @@ -0,0 +1,4 @@ +import cpp + +from ReturnStmt rs +select rs diff --git a/cpp/ql/test/header-variant-tests/microsoft-pch/_.c b/cpp/ql/test/header-variant-tests/microsoft-pch/_.c new file mode 100644 index 000000000000..6186edd586f7 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/microsoft-pch/_.c @@ -0,0 +1,3 @@ +// This file exists to ensure that the output subdirectory exists prior to +// a.c being indexed, as said directory needs to exist for the PCH file to +// be created, and will be created by running the extractor. diff --git a/cpp/ql/test/header-variant-tests/microsoft-pch/a.c b/cpp/ql/test/header-variant-tests/microsoft-pch/a.c new file mode 100644 index 000000000000..ba41bafcaade --- /dev/null +++ b/cpp/ql/test/header-variant-tests/microsoft-pch/a.c @@ -0,0 +1,2 @@ +#include "a.h" +// semmle-extractor-options: --microsoft /Yca.h /Fp${testdir}/microsoft-pch.testproj/a.pch diff --git a/cpp/ql/test/header-variant-tests/microsoft-pch/a.h b/cpp/ql/test/header-variant-tests/microsoft-pch/a.h new file mode 100644 index 000000000000..ab2a05dbbf84 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/microsoft-pch/a.h @@ -0,0 +1 @@ +#define A 1 diff --git a/cpp/ql/test/header-variant-tests/microsoft-pch/b.c b/cpp/ql/test/header-variant-tests/microsoft-pch/b.c new file mode 100644 index 000000000000..b6ea5085fd1f --- /dev/null +++ b/cpp/ql/test/header-variant-tests/microsoft-pch/b.c @@ -0,0 +1,7 @@ +#pragma hdrstop +#include "b.h" + +int b() { + return A; +} +// semmle-extractor-options: --microsoft /Yub.h /Fp${testdir}/microsoft-pch.testproj/a.pch diff --git a/cpp/ql/test/header-variant-tests/microsoft-pch/c.c b/cpp/ql/test/header-variant-tests/microsoft-pch/c.c new file mode 100644 index 000000000000..aaddbae8688d --- /dev/null +++ b/cpp/ql/test/header-variant-tests/microsoft-pch/c.c @@ -0,0 +1,7 @@ +#include "d.h" +#include "c.h" + +int c() { + return A; +} +// semmle-extractor-options: --microsoft /Yuc.h /Fp${testdir}/microsoft-pch.testproj/a.pch diff --git a/cpp/ql/test/header-variant-tests/microsoft-pch/microsoft-pch.expected b/cpp/ql/test/header-variant-tests/microsoft-pch/microsoft-pch.expected new file mode 100644 index 000000000000..0e61937f0a0f --- /dev/null +++ b/cpp/ql/test/header-variant-tests/microsoft-pch/microsoft-pch.expected @@ -0,0 +1,2 @@ +| b | 1 | +| c | 1 | diff --git a/cpp/ql/test/header-variant-tests/microsoft-pch/microsoft-pch.ql b/cpp/ql/test/header-variant-tests/microsoft-pch/microsoft-pch.ql new file mode 100644 index 000000000000..d29ab57cf7ba --- /dev/null +++ b/cpp/ql/test/header-variant-tests/microsoft-pch/microsoft-pch.ql @@ -0,0 +1,4 @@ +import cpp + +from ReturnStmt rs +select rs.getEnclosingFunction().getName(), ((Literal)rs.getExpr()).getValue() diff --git a/cpp/ql/test/header-variant-tests/multi-target-includes/arrays.expected b/cpp/ql/test/header-variant-tests/multi-target-includes/arrays.expected new file mode 100644 index 000000000000..a206a2be3dbb --- /dev/null +++ b/cpp/ql/test/header-variant-tests/multi-target-includes/arrays.expected @@ -0,0 +1 @@ +| common.h:5:12:5:18 | myArray | int[1] | diff --git a/cpp/ql/test/header-variant-tests/multi-target-includes/arrays.ql b/cpp/ql/test/header-variant-tests/multi-target-includes/arrays.ql new file mode 100644 index 000000000000..b70e83993ad5 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/multi-target-includes/arrays.ql @@ -0,0 +1,5 @@ +import cpp + +from Variable v, ArrayType t +where v.getType() = t +select v, t.toString() \ No newline at end of file diff --git a/cpp/ql/test/header-variant-tests/multi-target-includes/common.h b/cpp/ql/test/header-variant-tests/multi-target-includes/common.h new file mode 100644 index 000000000000..8c20e36220b3 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/multi-target-includes/common.h @@ -0,0 +1,5 @@ +// common.h + +#include "nameclash.h" + +static int myArray[sizeof(MYTYPE)]; \ No newline at end of file diff --git a/cpp/ql/test/header-variant-tests/multi-target-includes/define1.h b/cpp/ql/test/header-variant-tests/multi-target-includes/define1.h new file mode 100644 index 000000000000..85d137900cef --- /dev/null +++ b/cpp/ql/test/header-variant-tests/multi-target-includes/define1.h @@ -0,0 +1,4 @@ +// define1.h + +#if 1 +#endif \ No newline at end of file diff --git a/cpp/ql/test/header-variant-tests/multi-target-includes/define2.h b/cpp/ql/test/header-variant-tests/multi-target-includes/define2.h new file mode 100644 index 000000000000..58d077e9275f --- /dev/null +++ b/cpp/ql/test/header-variant-tests/multi-target-includes/define2.h @@ -0,0 +1,4 @@ +// define2.h + +#if 1 +#endif \ No newline at end of file diff --git a/cpp/ql/test/header-variant-tests/multi-target-includes/defines_issue.h b/cpp/ql/test/header-variant-tests/multi-target-includes/defines_issue.h new file mode 100644 index 000000000000..f80f739121e9 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/multi-target-includes/defines_issue.h @@ -0,0 +1,3 @@ +// defines_issue.h + +#include DEFINED_HEADER \ No newline at end of file diff --git a/cpp/ql/test/header-variant-tests/multi-target-includes/includes.expected b/cpp/ql/test/header-variant-tests/multi-target-includes/includes.expected new file mode 100644 index 000000000000..209af4b8ec49 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/multi-target-includes/includes.expected @@ -0,0 +1,10 @@ +| common.h:3:1:3:22 | #include "nameclash.h" | Include | nameclash.h | 1 | +| defines_issue.h:3:1:3:23 | #include DEFINED_HEADER | Include | define1.h | 1 | +| defines_issue.h:3:1:3:23 | #include DEFINED_HEADER | Include | define2.h | 1 | +| main1.cpp:3:1:3:19 | #include "common.h" | Include | common.h | 1 | +| main1.cpp:7:1:7:26 | #include "defines_issue.h" | Include | defines_issue.h | 1 | +| main2.cpp:3:1:3:19 | #include "common.h" | Include | common.h | 1 | +| main2.cpp:7:1:7:26 | #include "defines_issue.h" | Include | defines_issue.h | 1 | +| nameclash.h:3:1:3:27 | #include_next "nameclash.h" | IncludeNext | nameclash.h | 1 | +| nameclash.h:3:1:3:27 | #include_next "nameclash.h" | IncludeNext | subdir1/nameclash.h | 1 | +| nameclash.h:3:1:3:27 | #include_next "nameclash.h" | IncludeNext | subdir2/nameclash.h | 1 | diff --git a/cpp/ql/test/header-variant-tests/multi-target-includes/includes.ql b/cpp/ql/test/header-variant-tests/multi-target-includes/includes.ql new file mode 100644 index 000000000000..ada7782f8748 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/multi-target-includes/includes.ql @@ -0,0 +1,5 @@ +import cpp + +from Include i, string d +where if i instanceof IncludeNext then d = "IncludeNext" else d = "Include" +select i, d, i.getIncludedFile().getAbsolutePath(), count(i.getIncludedFile()) diff --git a/cpp/ql/test/header-variant-tests/multi-target-includes/main1.cpp b/cpp/ql/test/header-variant-tests/multi-target-includes/main1.cpp new file mode 100644 index 000000000000..5dcd1483cc0a --- /dev/null +++ b/cpp/ql/test/header-variant-tests/multi-target-includes/main1.cpp @@ -0,0 +1,7 @@ +// semmle-extractor-options: -I${testdir}/subdir1 +// main1.cpp +#include "common.h" + +#define DEFINED_HEADER "define1.h" + +#include "defines_issue.h" diff --git a/cpp/ql/test/header-variant-tests/multi-target-includes/main2.cpp b/cpp/ql/test/header-variant-tests/multi-target-includes/main2.cpp new file mode 100644 index 000000000000..49ec6536942e --- /dev/null +++ b/cpp/ql/test/header-variant-tests/multi-target-includes/main2.cpp @@ -0,0 +1,7 @@ +// semmle-extractor-options: -I${testdir}/subdir2 +// main2.cpp +#include "common.h" + +#define DEFINED_HEADER "define2.h" + +#include "defines_issue.h" diff --git a/cpp/ql/test/header-variant-tests/multi-target-includes/nameclash.h b/cpp/ql/test/header-variant-tests/multi-target-includes/nameclash.h new file mode 100644 index 000000000000..9ab85dc2b12e --- /dev/null +++ b/cpp/ql/test/header-variant-tests/multi-target-includes/nameclash.h @@ -0,0 +1,3 @@ +// nameclash.h + +#include_next "nameclash.h" \ No newline at end of file diff --git a/cpp/ql/test/header-variant-tests/multi-target-includes/subdir1/nameclash.h b/cpp/ql/test/header-variant-tests/multi-target-includes/subdir1/nameclash.h new file mode 100644 index 000000000000..d0361b16bdd9 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/multi-target-includes/subdir1/nameclash.h @@ -0,0 +1,3 @@ +// subdir1/nameclash.h + +typedef long long int MYTYPE; \ No newline at end of file diff --git a/cpp/ql/test/header-variant-tests/multi-target-includes/subdir2/nameclash.h b/cpp/ql/test/header-variant-tests/multi-target-includes/subdir2/nameclash.h new file mode 100644 index 000000000000..469a0c9b3039 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/multi-target-includes/subdir2/nameclash.h @@ -0,0 +1,3 @@ +// subdir2/nameclash.h + +typedef char MYTYPE; \ No newline at end of file diff --git a/cpp/ql/test/header-variant-tests/type_variants/common.h b/cpp/ql/test/header-variant-tests/type_variants/common.h new file mode 100644 index 000000000000..8b7c9fd90027 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/type_variants/common.h @@ -0,0 +1,11 @@ +int printf(...); + +void print_int(int x) { + printf("%d\n", x); +} + +void common_function() { + for(IteratorT itr = first(); itr != last(); ++itr) { + print_int(*itr); + } +} diff --git a/cpp/ql/test/header-variant-tests/type_variants/debug.cpp b/cpp/ql/test/header-variant-tests/type_variants/debug.cpp new file mode 100644 index 000000000000..9a23aa1db0cb --- /dev/null +++ b/cpp/ql/test/header-variant-tests/type_variants/debug.cpp @@ -0,0 +1,21 @@ +struct IteratorT { + IteratorT(int value) : m_value(value) {} + + int operator*() const { return m_value; } + IteratorT& operator++() { ++m_value; return *this; } + +private: + int m_value; +}; + +bool operator!=(const IteratorT& lhs, const IteratorT& rhs) { return *lhs != *rhs; } + +IteratorT first() { + return IteratorT(0); +} + +IteratorT last() { + return IteratorT(10); +} + +#include "common.h" diff --git a/cpp/ql/test/header-variant-tests/type_variants/release.cpp b/cpp/ql/test/header-variant-tests/type_variants/release.cpp new file mode 100644 index 000000000000..3e449b6f6912 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/type_variants/release.cpp @@ -0,0 +1,15 @@ +typedef int* IteratorT; + +static int g_values[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 +}; + +IteratorT first() { + return g_values; +} + +IteratorT last() { + return g_values + (sizeof(g_values) / sizeof(*g_values)); +} + +#include "common.h" diff --git a/cpp/ql/test/header-variant-tests/type_variants/variants.expected b/cpp/ql/test/header-variant-tests/type_variants/variants.expected new file mode 100644 index 000000000000..e608a4d10b1f --- /dev/null +++ b/cpp/ql/test/header-variant-tests/type_variants/variants.expected @@ -0,0 +1,8 @@ +| IteratorT | 1 | +| common_function | 2 | +| first | 2 | +| last | 2 | +| operator!= | 1 | +| operator* | 1 | +| operator++ | 1 | +| print_int | 1 | diff --git a/cpp/ql/test/header-variant-tests/type_variants/variants.ql b/cpp/ql/test/header-variant-tests/type_variants/variants.ql new file mode 100644 index 000000000000..23a5f30d2ed1 --- /dev/null +++ b/cpp/ql/test/header-variant-tests/type_variants/variants.ql @@ -0,0 +1,4 @@ +import cpp + +from Function f +select f.getName(), strictcount(f.getBlock()) diff --git a/cpp/ql/test/library-tests/CPP-205/CPP-205.cpp b/cpp/ql/test/library-tests/CPP-205/CPP-205.cpp new file mode 100644 index 000000000000..4a48e68fd424 --- /dev/null +++ b/cpp/ql/test/library-tests/CPP-205/CPP-205.cpp @@ -0,0 +1,9 @@ +template +int fn(T out) { + typedef int y[sizeof(out) + 1]; + return 0; +} + +int main() { + return fn(0); +} diff --git a/cpp/ql/test/library-tests/CPP-205/diags.expected b/cpp/ql/test/library-tests/CPP-205/diags.expected new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/cpp/ql/test/library-tests/CPP-205/diags.ql b/cpp/ql/test/library-tests/CPP-205/diags.ql new file mode 100644 index 000000000000..8c255348ea7a --- /dev/null +++ b/cpp/ql/test/library-tests/CPP-205/diags.ql @@ -0,0 +1,4 @@ +import cpp + +from Diagnostic d +select d, d.getSeverity(), d.getTag(), d.getFullMessage() diff --git a/cpp/ql/test/library-tests/CPP-205/elements.expected b/cpp/ql/test/library-tests/CPP-205/elements.expected new file mode 100644 index 000000000000..5988280f9a91 --- /dev/null +++ b/cpp/ql/test/library-tests/CPP-205/elements.expected @@ -0,0 +1,30 @@ +| CPP-205.cpp:0:0:0:0 | CPP-205.cpp | +| CPP-205.cpp:1:20:1:20 | T | +| CPP-205.cpp:1:20:1:20 | definition of T | +| CPP-205.cpp:2:5:2:6 | definition of fn | +| CPP-205.cpp:2:5:2:6 | fn | +| CPP-205.cpp:2:5:2:6 | fn | +| CPP-205.cpp:2:10:2:12 | definition of out | +| CPP-205.cpp:2:10:2:12 | out | +| CPP-205.cpp:2:10:2:12 | out | +| CPP-205.cpp:2:15:5:1 | { ... } | +| CPP-205.cpp:2:15:5:1 | { ... } | +| CPP-205.cpp:3:3:3:33 | declaration | +| CPP-205.cpp:3:3:3:33 | declaration | +| CPP-205.cpp:3:15:3:15 | declaration of y | +| CPP-205.cpp:3:15:3:15 | declaration of y | +| CPP-205.cpp:3:15:3:15 | y | +| CPP-205.cpp:3:15:3:15 | y | +| CPP-205.cpp:3:17:3:31 | 5 | +| CPP-205.cpp:4:3:4:11 | return ... | +| CPP-205.cpp:4:3:4:11 | return ... | +| CPP-205.cpp:4:10:4:10 | 0 | +| CPP-205.cpp:4:10:4:10 | 0 | +| CPP-205.cpp:7:5:7:8 | definition of main | +| CPP-205.cpp:7:5:7:8 | main | +| CPP-205.cpp:7:12:9:1 | { ... } | +| CPP-205.cpp:8:3:8:15 | return ... | +| CPP-205.cpp:8:10:8:11 | call to fn | +| CPP-205.cpp:8:13:8:13 | 0 | +| file://:0:0:0:0 | operator= | +| file://:0:0:0:0 | operator= | diff --git a/cpp/ql/test/library-tests/CPP-205/elements.ql b/cpp/ql/test/library-tests/CPP-205/elements.ql new file mode 100644 index 000000000000..36659f08c985 --- /dev/null +++ b/cpp/ql/test/library-tests/CPP-205/elements.ql @@ -0,0 +1,6 @@ +import cpp + +from Element e +where not e.getLocation() instanceof UnknownLocation + and not e instanceof Folder +select e diff --git a/cpp/ql/test/library-tests/CPP-206/CPP-206.cpp b/cpp/ql/test/library-tests/CPP-206/CPP-206.cpp new file mode 100644 index 000000000000..bdc74b36de7d --- /dev/null +++ b/cpp/ql/test/library-tests/CPP-206/CPP-206.cpp @@ -0,0 +1,16 @@ +template struct Int {}; + +template +constexpr bool operator==(Int lhs, Int rhs) { return Ia == Ja; } + +template +struct AsArraySize +{ + char arr[Int() == Int()]; +}; + +template +constexpr int operator+(Int lhs, Int rhs) { return Ic + Jc; } + +template +struct Sum : Int() + Int()> { }; diff --git a/cpp/ql/test/library-tests/CPP-206/CPP-206.expected b/cpp/ql/test/library-tests/CPP-206/CPP-206.expected new file mode 100644 index 000000000000..c44d5fb5f0a4 --- /dev/null +++ b/cpp/ql/test/library-tests/CPP-206/CPP-206.expected @@ -0,0 +1 @@ +| 0 | \ No newline at end of file diff --git a/cpp/ql/test/library-tests/CPP-206/CPP-206.ql b/cpp/ql/test/library-tests/CPP-206/CPP-206.ql new file mode 100644 index 000000000000..2bd8cad16b5f --- /dev/null +++ b/cpp/ql/test/library-tests/CPP-206/CPP-206.ql @@ -0,0 +1,3 @@ +import cpp + +select count(Diagnostic d) diff --git a/cpp/ql/test/library-tests/CPP-207/CPP-207.cpp b/cpp/ql/test/library-tests/CPP-207/CPP-207.cpp new file mode 100644 index 000000000000..554675022a14 --- /dev/null +++ b/cpp/ql/test/library-tests/CPP-207/CPP-207.cpp @@ -0,0 +1,19 @@ + + + + + + + + + + + + + +typedef void *t; + +class C +{ + const struct t *v; +}; diff --git a/cpp/ql/test/library-tests/CPP-207/CPP-207.expected b/cpp/ql/test/library-tests/CPP-207/CPP-207.expected new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/cpp/ql/test/library-tests/CPP-207/CPP-207.ql b/cpp/ql/test/library-tests/CPP-207/CPP-207.ql new file mode 100644 index 000000000000..3fa864748e16 --- /dev/null +++ b/cpp/ql/test/library-tests/CPP-207/CPP-207.ql @@ -0,0 +1,4 @@ +import cpp + +from Diagnostic d +select d diff --git a/cpp/ql/test/library-tests/CPP-207/options b/cpp/ql/test/library-tests/CPP-207/options new file mode 100644 index 000000000000..45e0279b139f --- /dev/null +++ b/cpp/ql/test/library-tests/CPP-207/options @@ -0,0 +1 @@ +extractor_flags: --edg --microsoft diff --git a/cpp/ql/test/library-tests/__builtin_constant_p/__builtin_constant_p.c b/cpp/ql/test/library-tests/__builtin_constant_p/__builtin_constant_p.c new file mode 100644 index 000000000000..7aa567898e05 --- /dev/null +++ b/cpp/ql/test/library-tests/__builtin_constant_p/__builtin_constant_p.c @@ -0,0 +1,13 @@ + +extern int g(void); + +void f(int i) { + static const int i1 = 1; + static const int i2 = g(); + static const int i3 = i; + static const int i4 = __builtin_constant_p(i); + static const int i5 = 5; + + char str[0 - !__builtin_constant_p(i)]; +} + diff --git a/cpp/ql/test/library-tests/__builtin_constant_p/diag.expected b/cpp/ql/test/library-tests/__builtin_constant_p/diag.expected new file mode 100644 index 000000000000..707a4bfe3f06 --- /dev/null +++ b/cpp/ql/test/library-tests/__builtin_constant_p/diag.expected @@ -0,0 +1,3 @@ +| __builtin_constant_p.c:6:27:6:27 | function call is not allowed in a constant expression | 4 | bad_constant_function_call | +| __builtin_constant_p.c:7:27:7:27 | expression must have a constant value | 4 | expr_not_constant | +| file://:0:0:0:0 | There was an error during this compilation | 4 | Error occurred | diff --git a/cpp/ql/test/library-tests/__builtin_constant_p/diag.ql b/cpp/ql/test/library-tests/__builtin_constant_p/diag.ql new file mode 100644 index 000000000000..53e8ea5de9da --- /dev/null +++ b/cpp/ql/test/library-tests/__builtin_constant_p/diag.ql @@ -0,0 +1,4 @@ +import cpp + +from Diagnostic d +select d, d.getSeverity(), d.getTag() diff --git a/cpp/ql/test/library-tests/__builtin_constant_p/options b/cpp/ql/test/library-tests/__builtin_constant_p/options new file mode 100644 index 000000000000..4181beff3c28 --- /dev/null +++ b/cpp/ql/test/library-tests/__builtin_constant_p/options @@ -0,0 +1 @@ +extractor_flags: --expect_errors diff --git a/cpp/ql/test/library-tests/abi/diag.expected b/cpp/ql/test/library-tests/abi/diag.expected new file mode 100644 index 000000000000..cad0aeb2762c --- /dev/null +++ b/cpp/ql/test/library-tests/abi/diag.expected @@ -0,0 +1,3 @@ +| file://:0:0:0:0 | There was an error during this compilation | 4 | Error occurred | +| packed.c:11:16:11:16 | the size of an array must be greater than zero | 4 | array_size_must_be_positive | +| packed.c:14:16:14:16 | the size of an array must be greater than zero | 4 | array_size_must_be_positive | diff --git a/cpp/ql/test/library-tests/abi/diag.ql b/cpp/ql/test/library-tests/abi/diag.ql new file mode 100644 index 000000000000..53e8ea5de9da --- /dev/null +++ b/cpp/ql/test/library-tests/abi/diag.ql @@ -0,0 +1,4 @@ +import cpp + +from Diagnostic d +select d, d.getSeverity(), d.getTag() diff --git a/cpp/ql/test/library-tests/abi/options b/cpp/ql/test/library-tests/abi/options new file mode 100644 index 000000000000..4181beff3c28 --- /dev/null +++ b/cpp/ql/test/library-tests/abi/options @@ -0,0 +1 @@ +extractor_flags: --expect_errors diff --git a/cpp/ql/test/library-tests/abi/packed.c b/cpp/ql/test/library-tests/abi/packed.c new file mode 100644 index 000000000000..f35ff5564b17 --- /dev/null +++ b/cpp/ql/test/library-tests/abi/packed.c @@ -0,0 +1,21 @@ + +struct stru { + union { + int i; + char s[6]; + } __attribute__((packed)); +}; + +void f(void) +{ + char test1[((int)sizeof(int)) - 5]; // 4 - 5 = -1 error + char test2[((int)sizeof(int)) - 4]; // 4 - 4 = 0 + char test3[((int)sizeof(int)) - 3]; // 4 - 3 = 1 + char test4[((int)sizeof(struct stru)) - 7]; // 6 - 7 = -1 error + char test5[((int)sizeof(struct stru)) - 6]; // 6 - 6 = 0 + char test6[((int)sizeof(struct stru)) - 5]; // 6 - 5 = 1 + char test7[9 - ((int)sizeof(struct stru))]; // 9 - 6 = 3 + char test8[8 - ((int)sizeof(struct stru))]; // 8 - 6 = 2 + char test9[7 - ((int)sizeof(struct stru))]; // 7 - 6 = 1 +} + diff --git a/cpp/ql/test/library-tests/access/DerivationAccess/DerivationAccess.cpp b/cpp/ql/test/library-tests/access/DerivationAccess/DerivationAccess.cpp new file mode 100644 index 000000000000..bedb38746bc5 --- /dev/null +++ b/cpp/ql/test/library-tests/access/DerivationAccess/DerivationAccess.cpp @@ -0,0 +1,68 @@ +namespace ambiguous { + class Duplicated {}; + + class Intermediate: public Duplicated {}; + + class C: protected Duplicated, public Intermediate {}; +} + +namespace chain { + class C { + public: static void pubC(); + protected: static void protC(); + private: static void priC(); + }; + + class D: public C { + private: static void priD(); + }; + + class E: protected D {}; + + class F: public E {}; +} + +namespace diamond { + class Top { + public: static void pub(); + protected: static void prot(); + }; + + class Left: private Top {}; + + class Right: public Top {}; + + class Bottom: public Left, protected Right {}; + + class Alone { + Alone(); + }; +} + +namespace friend_class { + class A { + }; + + class B : private A { + friend class D1; + friend class D2; + + public: + void fun() {}; + }; + + class C : private B { + public: + void fun() {}; + }; + + class D1 : private C { + public: + void fun() {}; + }; + + class D2 : private B { + protected: + void fun() {}; + }; +} diff --git a/cpp/ql/test/library-tests/access/DerivationAccess/actual_member.expected b/cpp/ql/test/library-tests/access/DerivationAccess/actual_member.expected new file mode 100644 index 000000000000..35628f38c53d --- /dev/null +++ b/cpp/ql/test/library-tests/access/DerivationAccess/actual_member.expected @@ -0,0 +1,26 @@ +| chain::C | chain::C::priC | private | +| chain::C | chain::C::protC | protected | +| chain::C | chain::C::pubC | public | +| chain::D | chain::C::protC | protected | +| chain::D | chain::C::pubC | public | +| chain::D | chain::D::priD | private | +| chain::E | chain::C::protC | protected | +| chain::E | chain::C::pubC | protected | +| chain::F | chain::C::protC | protected | +| chain::F | chain::C::pubC | protected | +| diamond::Alone | diamond::Alone::Alone | private | +| diamond::Bottom | diamond::Top::prot | protected | +| diamond::Bottom | diamond::Top::pub | protected | +| diamond::Left | diamond::Top::prot | private | +| diamond::Left | diamond::Top::pub | private | +| diamond::Right | diamond::Top::prot | protected | +| diamond::Right | diamond::Top::pub | public | +| diamond::Top | diamond::Top::prot | protected | +| diamond::Top | diamond::Top::pub | public | +| friend_class::B | friend_class::B::fun | public | +| friend_class::C | friend_class::B::fun | private | +| friend_class::C | friend_class::C::fun | public | +| friend_class::D1 | friend_class::C::fun | private | +| friend_class::D1 | friend_class::D1::fun | public | +| friend_class::D2 | friend_class::B::fun | private | +| friend_class::D2 | friend_class::D2::fun | protected | diff --git a/cpp/ql/test/library-tests/access/DerivationAccess/actual_member.ql b/cpp/ql/test/library-tests/access/DerivationAccess/actual_member.ql new file mode 100644 index 000000000000..ef4d021671fd --- /dev/null +++ b/cpp/ql/test/library-tests/access/DerivationAccess/actual_member.ql @@ -0,0 +1,7 @@ +import cpp + +from Class c, Declaration member +where not member.(Function).isCompilerGenerated() +select c.getQualifiedName(), + member.getQualifiedName(), + c.accessOfBaseMember(member).getName() diff --git a/cpp/ql/test/library-tests/access/DerivationAccess/hypothetical_member.expected b/cpp/ql/test/library-tests/access/DerivationAccess/hypothetical_member.expected new file mode 100644 index 000000000000..49c48f64d4a0 --- /dev/null +++ b/cpp/ql/test/library-tests/access/DerivationAccess/hypothetical_member.expected @@ -0,0 +1,35 @@ +| ambiguous::C | ambiguous::C | public | +| ambiguous::C | ambiguous::Duplicated | public | +| ambiguous::C | ambiguous::Intermediate | public | +| ambiguous::Duplicated | ambiguous::Duplicated | public | +| ambiguous::Intermediate | ambiguous::Duplicated | public | +| ambiguous::Intermediate | ambiguous::Intermediate | public | +| chain::C | chain::C | public | +| chain::D | chain::C | public | +| chain::D | chain::D | public | +| chain::E | chain::C | protected | +| chain::E | chain::D | protected | +| chain::E | chain::E | public | +| chain::F | chain::C | protected | +| chain::F | chain::D | protected | +| chain::F | chain::E | public | +| chain::F | chain::F | public | +| diamond::Alone | diamond::Alone | public | +| diamond::Bottom | diamond::Bottom | public | +| diamond::Bottom | diamond::Left | public | +| diamond::Bottom | diamond::Right | protected | +| diamond::Bottom | diamond::Top | protected | +| diamond::Left | diamond::Left | public | +| diamond::Left | diamond::Top | private | +| diamond::Right | diamond::Right | public | +| diamond::Right | diamond::Top | public | +| diamond::Top | diamond::Top | public | +| friend_class::A | friend_class::A | public | +| friend_class::B | friend_class::A | private | +| friend_class::B | friend_class::B | public | +| friend_class::C | friend_class::B | private | +| friend_class::C | friend_class::C | public | +| friend_class::D1 | friend_class::C | private | +| friend_class::D1 | friend_class::D1 | public | +| friend_class::D2 | friend_class::B | private | +| friend_class::D2 | friend_class::D2 | public | diff --git a/cpp/ql/test/library-tests/access/DerivationAccess/hypothetical_member.ql b/cpp/ql/test/library-tests/access/DerivationAccess/hypothetical_member.ql new file mode 100644 index 000000000000..20bc19bd85a6 --- /dev/null +++ b/cpp/ql/test/library-tests/access/DerivationAccess/hypothetical_member.ql @@ -0,0 +1,9 @@ +import cpp + +from Class base, Class derived, AccessSpecifier public +where public.hasName("public") + and // filter out compiler-generated junk + not derived.getNamespace() instanceof GlobalNamespace +select derived.getQualifiedName(), + base.getQualifiedName(), + derived.accessOfBaseMember(base, public).getName() diff --git a/cpp/ql/test/library-tests/access/FieldAccess/FieldAccess.cpp b/cpp/ql/test/library-tests/access/FieldAccess/FieldAccess.cpp new file mode 100644 index 000000000000..50c02ab889e0 --- /dev/null +++ b/cpp/ql/test/library-tests/access/FieldAccess/FieldAccess.cpp @@ -0,0 +1,86 @@ +typedef int I; +typedef I* P; + +struct D { + int *p1; + P p2; + + D() : p1(new int), p2(new int) {} + + virtual ~D() { + delete p1; + delete p2; + } +}; + +struct S { + D d; + int x1; + I x2; + + int get1() { + // This looks like an implicit access of `this->x1`, but it is + // apparently automatically converted to `this->x1` by the frontend, so it + // comes out as a `PointerFieldAccess`. + return x1; + } + + int get2() { + return this->x2; + } + + virtual ~S() { + // Implicit access of `d`, to call its destructor. + } +}; + +typedef S ST; + +union U { + int x; + double d; +}; + +int test_ptr00(S *s) { + return s->x1; +} + +int test_ptr01(S *s) { + return *(s->d.p1); +} + +int test_ptr02(ST *s) { + return s->x2; +} + +int test_ptr03(ST *s) { + return *(s->d.p2); +} + +int test_ptr04(U *u) { + return u->x; +} + +int test_ref00(S &s) { + return s.x1; +} + +int test_ref01(S &s) { + return s.x2; +} + +int test_ref02(U &u) { + return u.x; +} + +int test_ref03(S &&s) { + return s.x1; +} + +int test_val00(S s) { + return s.x1; +} + +int test_val01(U u) { + return u.x; +} diff --git a/cpp/ql/test/library-tests/access/FieldAccess/FieldAccess.expected b/cpp/ql/test/library-tests/access/FieldAccess/FieldAccess.expected new file mode 100644 index 000000000000..659bb032e43c --- /dev/null +++ b/cpp/ql/test/library-tests/access/FieldAccess/FieldAccess.expected @@ -0,0 +1,18 @@ +| FieldAccess.cpp:11:12:11:13 | p1 | ptr | +| FieldAccess.cpp:12:12:12:13 | p2 | ptr | +| FieldAccess.cpp:25:12:25:13 | x1 | ptr | +| FieldAccess.cpp:29:18:29:19 | x2 | ptr | +| FieldAccess.cpp:34:3:34:3 | d | this | +| FieldAccess.cpp:45:13:45:14 | x1 | ptr | +| FieldAccess.cpp:49:15:49:15 | d | ptr | +| FieldAccess.cpp:49:17:49:18 | p1 | val | +| FieldAccess.cpp:53:13:53:14 | x2 | ptr | +| FieldAccess.cpp:57:15:57:15 | d | ptr | +| FieldAccess.cpp:57:17:57:18 | p2 | val | +| FieldAccess.cpp:61:13:61:13 | x | ptr | +| FieldAccess.cpp:65:12:65:13 | x1 | ref | +| FieldAccess.cpp:69:12:69:13 | x2 | ref | +| FieldAccess.cpp:73:12:73:12 | x | ref | +| FieldAccess.cpp:77:12:77:13 | x1 | ref | +| FieldAccess.cpp:81:12:81:13 | x1 | val | +| FieldAccess.cpp:85:12:85:12 | x | val | diff --git a/cpp/ql/test/library-tests/access/FieldAccess/FieldAccess.ql b/cpp/ql/test/library-tests/access/FieldAccess/FieldAccess.ql new file mode 100644 index 000000000000..32af8bd6bde7 --- /dev/null +++ b/cpp/ql/test/library-tests/access/FieldAccess/FieldAccess.ql @@ -0,0 +1,14 @@ +import cpp + +string describeAccess(FieldAccess access) { + (access instanceof PointerFieldAccess and result = "ptr") + or + (access instanceof ReferenceFieldAccess and result = "ref") + or + (access instanceof ValueFieldAccess and result = "val") + or + (access instanceof ImplicitThisFieldAccess and result = "this") +} + +from FieldAccess access +select access, concat(describeAccess(access), ", ") diff --git a/cpp/ql/test/library-tests/access/MemberOrFriend/MemberOrFriend.cpp b/cpp/ql/test/library-tests/access/MemberOrFriend/MemberOrFriend.cpp new file mode 100644 index 000000000000..30e1e6cbf7b1 --- /dev/null +++ b/cpp/ql/test/library-tests/access/MemberOrFriend/MemberOrFriend.cpp @@ -0,0 +1,59 @@ +int topLevelFriend(); + +class HasFriendFunction { +public: + static int friendFunction(); +}; + +class C { + friend class Friend; + friend int f() { return 1; } + friend int topLevelFriend(); + friend int HasFriendFunction::friendFunction(); + + C(); + C(const C& c) {} + static int return2() { return 2; } + + class Nested { + Nested(); + int nested1() { + struct Local { + int localOfNested() { return return2(); } + } l; + return l.localOfNested(); + } + struct Nested2 { + void nested2(); + }; + }; +}; + +class Friend { + int f() { + auto lambdaFriend = [](int a, int b) { return a + b + C::return2(); }; + return lambdaFriend(1, 2); + } +}; + +class NotFriend { + int f() { + auto lambdaNotFriend = [](int a, int b) { return a + b; }; + return lambdaNotFriend(1, 2); + } +}; + +int topLevelFriend() { + struct LocalOfFriend { + int localOfFriend() { return C::return2(); } + } l; + return l.localOfFriend(); +} + +class DerivesFromC : public C { + int f(); +}; + +C::C() { + (void)return2(); +} diff --git a/cpp/ql/test/library-tests/access/MemberOrFriend/MemberOrFriend.expected b/cpp/ql/test/library-tests/access/MemberOrFriend/MemberOrFriend.expected new file mode 100644 index 000000000000..e25239967e1e --- /dev/null +++ b/cpp/ql/test/library-tests/access/MemberOrFriend/MemberOrFriend.expected @@ -0,0 +1,12 @@ +| MemberOrFriend.cpp:8:7:8:7 | C | MemberOrFriend.cpp:5:14:5:27 | friendFunction | HasFriendFunction::friendFunction() | +| MemberOrFriend.cpp:8:7:8:7 | C | MemberOrFriend.cpp:15:3:15:3 | C | C::C(const C & c) | +| MemberOrFriend.cpp:8:7:8:7 | C | MemberOrFriend.cpp:16:14:16:20 | return2 | C::return2() | +| MemberOrFriend.cpp:8:7:8:7 | C | MemberOrFriend.cpp:19:5:19:10 | Nested | C::Nested::Nested() | +| MemberOrFriend.cpp:8:7:8:7 | C | MemberOrFriend.cpp:20:9:20:15 | nested1 | C::Nested::nested1() | +| MemberOrFriend.cpp:8:7:8:7 | C | MemberOrFriend.cpp:22:13:22:25 | localOfNested | localOfNested() | +| MemberOrFriend.cpp:8:7:8:7 | C | MemberOrFriend.cpp:27:12:27:18 | nested2 | C::Nested::Nested2::nested2() | +| MemberOrFriend.cpp:8:7:8:7 | C | MemberOrFriend.cpp:33:7:33:7 | f | Friend::f() | +| MemberOrFriend.cpp:8:7:8:7 | C | MemberOrFriend.cpp:34:27:34:27 | operator() | operator()(int a, int b) | +| MemberOrFriend.cpp:8:7:8:7 | C | MemberOrFriend.cpp:46:5:46:18 | topLevelFriend | topLevelFriend() | +| MemberOrFriend.cpp:8:7:8:7 | C | MemberOrFriend.cpp:48:9:48:21 | localOfFriend | localOfFriend() | +| MemberOrFriend.cpp:8:7:8:7 | C | MemberOrFriend.cpp:57:1:57:4 | C | C::C() | diff --git a/cpp/ql/test/library-tests/access/MemberOrFriend/MemberOrFriend.ql b/cpp/ql/test/library-tests/access/MemberOrFriend/MemberOrFriend.ql new file mode 100644 index 000000000000..670030e24c64 --- /dev/null +++ b/cpp/ql/test/library-tests/access/MemberOrFriend/MemberOrFriend.ql @@ -0,0 +1,12 @@ +import cpp + +string functionName(Function f) { + result = f.getQualifiedName() +"("+ f.getParameterString() +")" or + not exists(f.getQualifiedName()) and + result = f.getName() +"("+ f.getParameterString() +")" +} + +from Class c, Function f +where c.getName() = "C" and f.inMemberOrFriendOf(c) + and not f.isCompilerGenerated() +select c, f, functionName(f) diff --git a/cpp/ql/test/library-tests/access/canAccessClass/canAccessClass.cpp b/cpp/ql/test/library-tests/access/canAccessClass/canAccessClass.cpp new file mode 100644 index 000000000000..9190300e0fe6 --- /dev/null +++ b/cpp/ql/test/library-tests/access/canAccessClass/canAccessClass.cpp @@ -0,0 +1,100 @@ + +namespace simple { + class Base {}; + class Derived : private Base { + public: Base *castme() { return this; } + }; + + Base *top(Derived *p) { + return (Base*)p; // C-style cast needed + } +} + +namespace side_tree { + class B {}; + class S: protected B {}; + class N: public S {}; + class P: private S { + static B* f(N* n) { + // Allowed by C++14 11.2/4 since N can be converted to S by (4.1) and S + // can be converted to B by (4.3). + return n; + } + }; +} + +namespace gcc44 { + struct B { }; + struct D1: protected B { }; + struct D2: B { + B* f(D1* d) { + return nullptr; + // This conversion was allowed by GCC < 4.4, and the frontend defaults to + // emulating GCC 4.3, so it will actually accept the following illegal + // conversion. + //return d; + } + }; +} + +namespace mixed { + class A { + }; + + class B : private A { + public: + void fun() {}; // can convert B -> A; D -> C (public) + }; + + class C : protected B { + public: + void fun() {}; // can convert C -> B; D -> B, C (public) + }; + + class D : public C { + public: + void fun() {}; // can convert C-> B; D -> C, B (public) + }; + + void fun() {}; // can convert D -> C (public) +} + +namespace friend_class { + class A { + }; + + class B : private A { + friend class D1; + friend class D2; + + public: + void fun() {}; // can convert B -> A + }; + + class C : private B { + public: + void fun() {}; // can convert C -> B + }; + + class D1 : private C { + public: + void fun() {}; // can convert D1 -> B, C -> A (due to friend decl) + }; + + class D2 : private B { + protected: + void fun() {}; // can convert D2 -> A, B, B -> A (due to friend decl) + }; +} + +namespace friend_fun { + class A { + }; + + class B : private A { + friend void fun2(); + }; + + void fun1() {}; + void fun2() {}; // can convert B -> A (due to friend decl) +} diff --git a/cpp/ql/test/library-tests/access/canAccessClass/canAccessClass.expected b/cpp/ql/test/library-tests/access/canAccessClass/canAccessClass.expected new file mode 100644 index 000000000000..6e5472574930 --- /dev/null +++ b/cpp/ql/test/library-tests/access/canAccessClass/canAccessClass.expected @@ -0,0 +1,75 @@ +| friend_class::B::fun | Can convert A -> A | +| friend_class::B::fun | Can convert B -> A | +| friend_class::B::fun | Can convert B -> B | +| friend_class::B::fun | Can convert C -> C | +| friend_class::B::fun | Can convert D1 -> D1 | +| friend_class::B::fun | Can convert D2 -> D2 | +| friend_class::C::fun | Can convert A -> A | +| friend_class::C::fun | Can convert B -> B | +| friend_class::C::fun | Can convert C -> B | +| friend_class::C::fun | Can convert C -> C | +| friend_class::C::fun | Can convert D1 -> D1 | +| friend_class::C::fun | Can convert D2 -> D2 | +| friend_class::D1::fun | Can convert A -> A | +| friend_class::D1::fun | Can convert B -> A | +| friend_class::D1::fun | Can convert B -> B | +| friend_class::D1::fun | Can convert C -> C | +| friend_class::D1::fun | Can convert D1 -> C | +| friend_class::D1::fun | Can convert D1 -> D1 | +| friend_class::D1::fun | Can convert D2 -> D2 | +| friend_class::D2::fun | Can convert A -> A | +| friend_class::D2::fun | Can convert B -> A | +| friend_class::D2::fun | Can convert B -> B | +| friend_class::D2::fun | Can convert C -> C | +| friend_class::D2::fun | Can convert D1 -> D1 | +| friend_class::D2::fun | Can convert D2 -> A | +| friend_class::D2::fun | Can convert D2 -> B | +| friend_class::D2::fun | Can convert D2 -> D2 | +| friend_fun::fun1 | Can convert A -> A | +| friend_fun::fun1 | Can convert B -> B | +| friend_fun::fun2 | Can convert A -> A | +| friend_fun::fun2 | Can convert B -> A | +| friend_fun::fun2 | Can convert B -> B | +| gcc44::D2::f | Can convert B -> B | +| gcc44::D2::f | Can convert D1 -> D1 | +| gcc44::D2::f | Can convert D2 -> B | +| gcc44::D2::f | Can convert D2 -> D2 | +| mixed::B::fun | Can convert A -> A | +| mixed::B::fun | Can convert B -> A | +| mixed::B::fun | Can convert B -> B | +| mixed::B::fun | Can convert C -> C | +| mixed::B::fun | Can convert D -> C | +| mixed::B::fun | Can convert D -> D | +| mixed::C::fun | Can convert A -> A | +| mixed::C::fun | Can convert B -> B | +| mixed::C::fun | Can convert C -> B | +| mixed::C::fun | Can convert C -> C | +| mixed::C::fun | Can convert D -> B | +| mixed::C::fun | Can convert D -> C | +| mixed::C::fun | Can convert D -> D | +| mixed::D::fun | Can convert A -> A | +| mixed::D::fun | Can convert B -> B | +| mixed::D::fun | Can convert C -> B | +| mixed::D::fun | Can convert C -> C | +| mixed::D::fun | Can convert D -> B | +| mixed::D::fun | Can convert D -> C | +| mixed::D::fun | Can convert D -> D | +| mixed::fun | Can convert A -> A | +| mixed::fun | Can convert B -> B | +| mixed::fun | Can convert C -> C | +| mixed::fun | Can convert D -> C | +| mixed::fun | Can convert D -> D | +| side_tree::P::f | Can convert B -> B | +| side_tree::P::f | Can convert N -> B | +| side_tree::P::f | Can convert N -> N | +| side_tree::P::f | Can convert N -> S | +| side_tree::P::f | Can convert P -> B | +| side_tree::P::f | Can convert P -> P | +| side_tree::P::f | Can convert P -> S | +| side_tree::P::f | Can convert S -> B | +| side_tree::P::f | Can convert S -> S | +| simple::Derived::castme | Can convert Base -> Base | +| simple::Derived::castme | Can convert Derived -> Base | +| simple::Derived::castme | Can convert Derived -> Derived | +| simple::top | Can convert Base -> Base | +| simple::top | Can convert Derived -> Derived | diff --git a/cpp/ql/test/library-tests/access/canAccessClass/canAccessClass.ql b/cpp/ql/test/library-tests/access/canAccessClass/canAccessClass.ql new file mode 100644 index 000000000000..58a91c597512 --- /dev/null +++ b/cpp/ql/test/library-tests/access/canAccessClass/canAccessClass.ql @@ -0,0 +1,11 @@ +import cpp + +from Function f, Class namingClass, Class base +where f.canAccessClass(base, namingClass) + and // filter out compiler-generated junk + not namingClass.getNamespace() instanceof GlobalNamespace + and // only in same namespace + f.getNamespace() = namingClass.getNamespace() + and not f.isCompilerGenerated() +select f.getQualifiedName(), + "Can convert "+ namingClass.getName() +" -> "+ base.getName() diff --git a/cpp/ql/test/library-tests/access/canAccessMember/canAccessMember.cpp b/cpp/ql/test/library-tests/access/canAccessMember/canAccessMember.cpp new file mode 100644 index 000000000000..6afd846741f8 --- /dev/null +++ b/cpp/ql/test/library-tests/access/canAccessMember/canAccessMember.cpp @@ -0,0 +1,160 @@ + + +namespace direct_friend { + class C { + friend class D; + private: + C(C &) {} + }; + + class D { + C x; + void f() {} + }; +} + +namespace field_and_base { + class B { + // An access expression `x.m`, where `m` as a member of `x` is protected + // and not static, may only occur in a member or friend of a (reflexive, + // transitive) base class of `x`'s class (N4140 11.4). + // To keep the implementation simple and fast, we apply this restriction to + // static members as well even though it's wrong. If that should ever be + // fixed, the expected results of this test should be updated to show that + // `P::f` can indeed access `P::m_static`. + protected: + int m; + static int m_static; + }; + class P: public B { + B fieldB; + int f() { + // In the fully general syntax `x.B::m`, the _naming class_ `B` may be + // different from the static type of `x`. Our encoding of access rules + // currently pretends that they are always the same. + return this->m + this->B::m + this->m_static + B::m_static; + } + }; +} + +namespace protected_derived { + class B { + public: + int m; + }; + class BN : protected B { }; + class BPNprot; + class BPNpub; + class BP : protected B { + int f(BN*, BPNprot*, BPNpub*); + }; + class BPNprot : protected BP { }; + class BPNpub : public BP { }; + int BP::f(BN* bn, BPNprot* bpnProt, BPNpub* bpnPub) { + return bpnPub->m; + } +} + +namespace protected_virtual { + class B { + protected: + int m; + }; + + class Npub; + class Nprot; + class P : virtual private B { + int f(Npub*, Nprot*); + }; + + // f can access Npub::m since because it can convert an Npub* to a B* and + // then access B::m. + class Npub : virtual public B, private P { }; + + class Nprot : virtual protected B, private P { }; + + int P::f(Npub* pub, Nprot* prot) { + return pub->m; + } +} + +namespace simple { + class Base {}; + class Derived : private Base { + public: Base *castme() { return this; } + }; + + Base *top(Derived *p) { + return (Base*)p; // C-style cast needed + } +} + +namespace mixed { + class A { + private: + int x; + static int y; + }; + + class B : private A { + private: + void fun() {}; + }; + + class C : protected B { + private: + void fun() {}; + }; + + class D : public C { + private: + void fun() {}; + }; +} + +namespace friend_class { + class B { + friend class D1; + friend class D2; + + private: + int a; + static int b; + + void fun() {}; + }; + + class C : private B { + private: + int x; + static int y; + + void fun() {}; + }; + + class D1 : private C { + private: + void fun() {}; + }; + + class D2 : private B { + protected: + void fun() {}; + }; +} + +namespace friend_fun { + class A { + }; + + class B : private A { + private: + int x; + static int y; + + friend void fun2(); + }; + + void fun1() {}; + void fun2() {}; +} diff --git a/cpp/ql/test/library-tests/access/canAccessMember/canAccessMember.expected b/cpp/ql/test/library-tests/access/canAccessMember/canAccessMember.expected new file mode 100644 index 000000000000..9238c6182984 --- /dev/null +++ b/cpp/ql/test/library-tests/access/canAccessMember/canAccessMember.expected @@ -0,0 +1,96 @@ +| direct_friend::C::C | Can access C::C | +| direct_friend::C::C | Can access C::operator= | +| direct_friend::C::C | Can access D::D | +| direct_friend::C::C | Can access D::operator= | +| direct_friend::D::f | Can access C::C | +| direct_friend::D::f | Can access C::operator= | +| direct_friend::D::f | Can access D::D | +| 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::operator= | +| 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::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::operator= | +| friend_class::B::fun | Can access D1::operator= | +| friend_class::B::fun | Can access D2::operator= | +| friend_class::C::fun | Can access B::operator= | +| 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::operator= | +| friend_class::C::fun | Can access D2::operator= | +| 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::operator= | +| friend_class::D1::fun | Can access D1::fun | +| friend_class::D1::fun | Can access D1::operator= | +| friend_class::D1::fun | Can access D2::operator= | +| 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::operator= | +| friend_class::D2::fun | Can access D1::operator= | +| 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::operator= | +| friend_fun::fun2 | Can access A::operator= | +| 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::operator= | +| mixed::B::fun | Can access B::fun | +| mixed::B::fun | Can access B::operator= | +| mixed::B::fun | Can access C::operator= | +| mixed::B::fun | Can access D::operator= | +| mixed::C::fun | Can access A::operator= | +| mixed::C::fun | Can access B::operator= | +| mixed::C::fun | Can access C::fun | +| mixed::C::fun | Can access C::operator= | +| mixed::C::fun | Can access D::fun | +| mixed::C::fun | Can access D::operator= | +| mixed::D::fun | Can access A::operator= | +| mixed::D::fun | Can access B::operator= | +| mixed::D::fun | Can access C::operator= | +| 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::operator= | +| 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::operator= | +| 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::operator= | +| protected_virtual::P::f | Can access Nprot::Nprot | +| protected_virtual::P::f | Can access Nprot::operator= | +| 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::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::castme | +| simple::Derived::castme | Can access Derived::operator= | +| simple::top | Can access Base::operator= | +| simple::top | Can access Derived::castme | +| simple::top | Can access Derived::operator= | diff --git a/cpp/ql/test/library-tests/access/canAccessMember/canAccessMember.ql b/cpp/ql/test/library-tests/access/canAccessMember/canAccessMember.ql new file mode 100644 index 000000000000..ecd0f803158e --- /dev/null +++ b/cpp/ql/test/library-tests/access/canAccessMember/canAccessMember.ql @@ -0,0 +1,9 @@ +import cpp + +from Function f, Class namingClass, Declaration member +where f.canAccessMember(member, namingClass) + and // only in same namespace + f.getNamespace() = namingClass.getNamespace() + and not f.isCompilerGenerated() +select f.getQualifiedName(), + "Can access "+ namingClass.getName() +"::"+ member.getName() diff --git a/cpp/ql/test/library-tests/access/noPublic/noPublic.cpp b/cpp/ql/test/library-tests/access/noPublic/noPublic.cpp new file mode 100644 index 000000000000..f59a4f4f8fef --- /dev/null +++ b/cpp/ql/test/library-tests/access/noPublic/noPublic.cpp @@ -0,0 +1,5 @@ +// Some of our libraries depend on the resulting access of a hypothetical +// public member. This test verifies that the database contains QL +// `AccessSpecifier` instances for both `private` and `public` even when +// extracting a program that does not contain those keywords. Indeed, this +// program contains nothing at all except this comment. diff --git a/cpp/ql/test/library-tests/access/noPublic/noPublic.expected b/cpp/ql/test/library-tests/access/noPublic/noPublic.expected new file mode 100644 index 000000000000..dd09d291f323 --- /dev/null +++ b/cpp/ql/test/library-tests/access/noPublic/noPublic.expected @@ -0,0 +1,2 @@ +| file://:0:0:0:0 | private | +| file://:0:0:0:0 | public | diff --git a/cpp/ql/test/library-tests/access/noPublic/noPublic.ql b/cpp/ql/test/library-tests/access/noPublic/noPublic.ql new file mode 100644 index 000000000000..e6d78dcfd5ab --- /dev/null +++ b/cpp/ql/test/library-tests/access/noPublic/noPublic.ql @@ -0,0 +1,7 @@ +import cpp + +from AccessSpecifier spec +// There is no way to create "protected" access without writing the keyword +// `protected` in the source, so we don't need to test for that. +where spec.hasName("private") or spec.hasName("public") +select spec diff --git a/cpp/ql/test/library-tests/alias_templates/alias_templates.cpp b/cpp/ql/test/library-tests/alias_templates/alias_templates.cpp new file mode 100644 index 000000000000..b25861663333 --- /dev/null +++ b/cpp/ql/test/library-tests/alias_templates/alias_templates.cpp @@ -0,0 +1,18 @@ +template struct pair { + T first; + U second; +}; + +struct coords { + int x; + template using a_wild_alias_template_appeared = pair; + int y; + int z; + enum { + after_alias_template = 1 + }; +}; + +enum { + global_constant = 0 +}; diff --git a/cpp/ql/test/library-tests/alias_templates/enum_const_parents.expected b/cpp/ql/test/library-tests/alias_templates/enum_const_parents.expected new file mode 100644 index 000000000000..02b0654dae4d --- /dev/null +++ b/cpp/ql/test/library-tests/alias_templates/enum_const_parents.expected @@ -0,0 +1 @@ +| alias_templates.cpp:12:5:12:24 | after_alias_template | alias_templates.cpp:6:8:6:13 | coords | diff --git a/cpp/ql/test/library-tests/alias_templates/enum_const_parents.ql b/cpp/ql/test/library-tests/alias_templates/enum_const_parents.ql new file mode 100644 index 000000000000..0ef60fadfd84 --- /dev/null +++ b/cpp/ql/test/library-tests/alias_templates/enum_const_parents.ql @@ -0,0 +1,4 @@ +import cpp + +from EnumConstant ec +select ec, ec.getDeclaringType() diff --git a/cpp/ql/test/library-tests/aliased_ssa/constants/constants.cpp b/cpp/ql/test/library-tests/aliased_ssa/constants/constants.cpp new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/cpp/ql/test/library-tests/aliased_ssa/constants/constants.expected b/cpp/ql/test/library-tests/aliased_ssa/constants/constants.expected new file mode 100644 index 000000000000..6c555a03b210 --- /dev/null +++ b/cpp/ql/test/library-tests/aliased_ssa/constants/constants.expected @@ -0,0 +1,35 @@ +| 0 * 0 | 0 | +| 0 * INT_MAX | 0 | +| 0 * unknown | 0 | +| 0 + 0 | 0 | +| 0 + -INT_MAX | -2147483647 | +| 0 + INT_MAX | 2147483647 | +| 0 - -INT_MAX | 2147483647 | +| 0 - INT_MAX | -2147483647 | +| 0 / -INT_MAX | 0 | +| 0 / unknown | unknown | +| 1 + -INT_MAX | -2147483646 | +| 1 + INT_MAX | unknown | +| 2 * INT_MAX | unknown | +| 5 * 7 | 35 | +| 5 + unknown | unknown | +| 5 - unknown | unknown | +| 35 / 7 | 5 | +| 35 / 8 | 4 | +| 35 / -7 | -5 | +| 35 / -8 | -4 | +| -1 * -INT_MAX | 2147483647 | +| -1 - -INT_MAX | 2147483646 | +| -1 - INT_MAX | unknown | +| -35 / 7 | -5 | +| -35 / 8 | -4 | +| -35 / -7 | 5 | +| -35 / -8 | 4 | +| INT_MAX * INT_MAX | unknown | +| INT_MAX / 0 | unknown | +| unknown + 5 | unknown | +| unknown + unknown | unknown | +| unknown - 5 | unknown | +| unknown - unknown | unknown | +| unknown / 3 | unknown | +| unknown / unknown | unknown | diff --git a/cpp/ql/test/library-tests/aliased_ssa/constants/constants.ql b/cpp/ql/test/library-tests/aliased_ssa/constants/constants.ql new file mode 100644 index 000000000000..eb11adc22862 --- /dev/null +++ b/cpp/ql/test/library-tests/aliased_ssa/constants/constants.ql @@ -0,0 +1,49 @@ +import cpp +import semmle.code.cpp.ssa.internal.IntegerConstant as Ints + +bindingset[n] +string resultString(int n) { + if Ints::hasValue(n) then + result = n.toString() + else + result = "unknown" +} + +from string expr, int res +where + expr = "0 + 0" and res = Ints::add(0, 0) or + expr = "0 + INT_MAX" and res = Ints::add(0, Ints::maxValue()) or + expr = "0 + -INT_MAX" and res = Ints::add(0, Ints::minValue()) or + expr = "1 + INT_MAX" and res = Ints::add(1, Ints::maxValue()) or + expr = "1 + -INT_MAX" and res = Ints::add(1, Ints::minValue()) or + expr = "unknown + unknown" and res = Ints::add(Ints::unknown(), Ints::unknown()) or + expr = "5 + unknown" and res = Ints::add(5, Ints::unknown()) or + expr = "unknown + 5" and res = Ints::add(Ints::unknown(), 5) or + expr = "0 - INT_MAX" and res = Ints::sub(0, Ints::maxValue()) or + expr = "0 - -INT_MAX" and res = Ints::sub(0, Ints::minValue()) or + expr = "-1 - INT_MAX" and res = Ints::sub(-1, Ints::maxValue()) or + expr = "-1 - -INT_MAX" and res = Ints::sub(-1, Ints::minValue()) or + expr = "unknown - unknown" and res = Ints::sub(Ints::unknown(), Ints::unknown()) or + expr = "5 - unknown" and res = Ints::sub(5, Ints::unknown()) or + expr = "unknown - 5" and res = Ints::sub(Ints::unknown(), 5) or + expr = "0 * 0" and res = Ints::mul(0, 0) or + expr = "5 * 7" and res = Ints::mul(5, 7) or + expr = "0 * INT_MAX" and res = Ints::mul(0, Ints::maxValue()) or + expr = "2 * INT_MAX" and res = Ints::mul(2, Ints::maxValue()) or + expr = "-1 * -INT_MAX" and res = Ints::mul(-1, Ints::minValue()) or + expr = "INT_MAX * INT_MAX" and res = Ints::mul(Ints::maxValue(), Ints::maxValue()) or + expr = "0 * unknown" and res = Ints::mul(0, Ints::unknown()) or + expr = "35 / 7" and res = Ints::div(35, 7) or + expr = "35 / 8" and res = Ints::div(35, 8) or + expr = "35 / -7" and res = Ints::div(35, -7) or + expr = "35 / -8" and res = Ints::div(35, -8) or + expr = "-35 / 7" and res = Ints::div(-35, 7) or + expr = "-35 / 8" and res = Ints::div(-35, 8) or + expr = "-35 / -7" and res = Ints::div(-35, -7) or + expr = "-35 / -8" and res = Ints::div(-35, -8) or + expr = "0 / -INT_MAX" and res = Ints::div(0, Ints::minValue()) or + expr = "INT_MAX / 0" and res = Ints::div(Ints::maxValue(), 0) or + expr = "0 / unknown" and res = Ints::div(0, Ints::unknown()) or + expr = "unknown / 3" and res = Ints::div(Ints::unknown(), 3) or + expr = "unknown / unknown" and res = Ints::div(Ints::unknown(), Ints::unknown()) +select expr, resultString(res) diff --git a/cpp/ql/test/library-tests/aliased_ssa/escape/escape.cpp b/cpp/ql/test/library-tests/aliased_ssa/escape/escape.cpp new file mode 100644 index 000000000000..81a23ac4fb21 --- /dev/null +++ b/cpp/ql/test/library-tests/aliased_ssa/escape/escape.cpp @@ -0,0 +1,98 @@ +void CallByPointer(int* p); +void CallByReference(int& r); + +struct Point { + float x; + float y; + float z; +}; + +struct Base { + float b; +}; + +struct ReusedBase { + float rb; +}; + +struct Intermediate1 : Base, ReusedBase { + float i1; +}; + +struct Intermediate2 : ReusedBase { + float i2; +}; + +struct Derived : Intermediate1, Intermediate2 { + float d; +}; + +void Escape() +{ + int no_result; + int no_; + + no_ = 1; + no_ = no_; + no_result = no_; + no_result = *&no_; +// no_result = (int&)no_; Restore when we have correct IR types for glvalues + no_; + &no_; + no_result = *((&no_) + 0); + no_result = *((&no_) - 0); + no_result = *(0 + &no_); + if (&no_) { + } + while (&no_) { + } + do { + } while (&no_); + for(&no_; &no_; &no_) { + } + + if (&no_ == nullptr) { + } + while (&no_ != nullptr) { + } + + int no_Array[10]; + no_Array; + (int*)no_Array; + no_Array[5]; + 5[no_Array]; + no_result = no_Array[5]; + no_result = 5[no_Array]; + + Point no_Point = { 1, 2, 3 }; + float no_x = no_Point.x; + no_Point.y = no_x; + float no_y = (&no_Point)->y; + (&no_Point)->y = no_y; + float no_z = *(&no_Point.z); + *(&no_Point.z) = no_z; + + Derived no_Derived; + no_Derived.b = 0; + float no_b = no_Derived.b; + no_Derived.i2 = 1; + float no_i2 = no_Derived.i2; + + int no_ssa_addrOf; + int* no_p = &no_ssa_addrOf; + + int no_ssa_refTo; + int& no_r = no_ssa_refTo; + + int no_ssa_refToArrayElement[10]; + int& no_rae = no_ssa_refToArrayElement[5]; + + int no_ssa_refToArray[10]; + int (&no_ra)[10] = no_ssa_refToArray; + + int passByPtr; + CallByPointer(&passByPtr); + + int passByRef; + CallByReference(passByRef); +} diff --git a/cpp/ql/test/library-tests/aliased_ssa/escape/escape.expected b/cpp/ql/test/library-tests/aliased_ssa/escape/escape.expected new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/cpp/ql/test/library-tests/aliased_ssa/escape/escape.ql b/cpp/ql/test/library-tests/aliased_ssa/escape/escape.ql new file mode 100644 index 000000000000..6dcedf72db76 --- /dev/null +++ b/cpp/ql/test/library-tests/aliased_ssa/escape/escape.ql @@ -0,0 +1,22 @@ +import default +import semmle.code.cpp.ssa.internal.ssa.AliasAnalysis +import semmle.code.cpp.ir.IR + +predicate shouldEscape(IRAutomaticUserVariable var) { + exists(string name | + name = var.getVariable().getName() and + name.matches("no_%") and + not name.matches("no_ssa_%") + ) +} + +from IRAutomaticUserVariable var +where + exists(FunctionIR funcIR | + funcIR = var.getFunctionIR() and + ( + (shouldEscape(var) and variableAddressEscapes(var)) or + (not shouldEscape(var) and not variableAddressEscapes(var)) + ) + ) +select var diff --git a/cpp/ql/test/library-tests/aliased_ssa/escape/points_to.expected b/cpp/ql/test/library-tests/aliased_ssa/escape/points_to.expected new file mode 100644 index 000000000000..5d387a4b267d --- /dev/null +++ b/cpp/ql/test/library-tests/aliased_ssa/escape/points_to.expected @@ -0,0 +1,108 @@ +| escape.cpp:32:9:32:17 | VariableAddress[no_result] | no_result+0:0 | +| escape.cpp:33:9:33:11 | VariableAddress[no_] | no_+0:0 | +| escape.cpp:35:5:35:7 | VariableAddress[no_] | no_+0:0 | +| escape.cpp:36:5:36:7 | VariableAddress[no_] | no_+0:0 | +| escape.cpp:36:11:36:13 | VariableAddress[no_] | no_+0:0 | +| escape.cpp:37:5:37:13 | VariableAddress[no_result] | no_result+0:0 | +| escape.cpp:37:17:37:19 | VariableAddress[no_] | no_+0:0 | +| escape.cpp:38:5:38:13 | VariableAddress[no_result] | no_result+0:0 | +| escape.cpp:38:19:38:21 | VariableAddress[no_] | no_+0:0 | +| escape.cpp:40:5:40:7 | VariableAddress[no_] | no_+0:0 | +| escape.cpp:41:6:41:8 | VariableAddress[no_] | no_+0:0 | +| escape.cpp:42:5:42:13 | VariableAddress[no_result] | no_result+0:0 | +| escape.cpp:42:19:42:28 | PointerAdd[4] | no_+0:0 | +| escape.cpp:42:21:42:23 | VariableAddress[no_] | no_+0:0 | +| escape.cpp:43:5:43:13 | VariableAddress[no_result] | no_result+0:0 | +| escape.cpp:43:19:43:28 | PointerSub[4] | no_+0:0 | +| escape.cpp:43:21:43:23 | VariableAddress[no_] | no_+0:0 | +| escape.cpp:44:5:44:13 | VariableAddress[no_result] | no_result+0:0 | +| escape.cpp:44:19:44:26 | PointerAdd[4] | no_+0:0 | +| escape.cpp:44:24:44:26 | VariableAddress[no_] | no_+0:0 | +| escape.cpp:45:10:45:12 | VariableAddress[no_] | no_+0:0 | +| escape.cpp:47:13:47:15 | VariableAddress[no_] | no_+0:0 | +| escape.cpp:50:15:50:17 | VariableAddress[no_] | no_+0:0 | +| escape.cpp:51:10:51:12 | VariableAddress[no_] | no_+0:0 | +| escape.cpp:51:16:51:18 | VariableAddress[no_] | no_+0:0 | +| escape.cpp:51:22:51:24 | VariableAddress[no_] | no_+0:0 | +| escape.cpp:54:10:54:12 | VariableAddress[no_] | no_+0:0 | +| escape.cpp:56:13:56:15 | VariableAddress[no_] | no_+0:0 | +| escape.cpp:59:9:59:16 | VariableAddress[no_Array] | no_Array+0:0 | +| escape.cpp:60:5:60:12 | VariableAddress[no_Array] | no_Array+0:0 | +| escape.cpp:61:5:61:18 | Convert | no_Array+0:0 | +| escape.cpp:61:11:61:18 | Convert | no_Array+0:0 | +| escape.cpp:61:11:61:18 | VariableAddress[no_Array] | no_Array+0:0 | +| escape.cpp:62:5:62:12 | Convert | no_Array+0:0 | +| escape.cpp:62:5:62:12 | VariableAddress[no_Array] | no_Array+0:0 | +| escape.cpp:62:5:62:15 | PointerAdd[4] | no_Array+20:0 | +| escape.cpp:63:5:63:15 | PointerAdd[4] | no_Array+20:0 | +| escape.cpp:63:7:63:14 | Convert | no_Array+0:0 | +| escape.cpp:63:7:63:14 | VariableAddress[no_Array] | no_Array+0:0 | +| escape.cpp:64:5:64:13 | VariableAddress[no_result] | no_result+0:0 | +| escape.cpp:64:17:64:24 | Convert | no_Array+0:0 | +| escape.cpp:64:17:64:24 | VariableAddress[no_Array] | no_Array+0:0 | +| escape.cpp:64:17:64:27 | PointerAdd[4] | no_Array+20:0 | +| escape.cpp:65:5:65:13 | VariableAddress[no_result] | no_result+0:0 | +| escape.cpp:65:17:65:27 | PointerAdd[4] | no_Array+20:0 | +| escape.cpp:65:19:65:26 | Convert | no_Array+0:0 | +| escape.cpp:65:19:65:26 | VariableAddress[no_Array] | no_Array+0:0 | +| escape.cpp:67:11:67:18 | VariableAddress[no_Point] | no_Point+0:0 | +| escape.cpp:67:21:67:32 | FieldAddress[x] | no_Point+0:0 | +| escape.cpp:67:21:67:32 | FieldAddress[y] | no_Point+4:0 | +| escape.cpp:67:21:67:32 | FieldAddress[z] | no_Point+8:0 | +| escape.cpp:68:11:68:14 | VariableAddress[no_x] | no_x+0:0 | +| escape.cpp:68:18:68:25 | VariableAddress[no_Point] | no_Point+0:0 | +| escape.cpp:68:27:68:27 | FieldAddress[x] | no_Point+0:0 | +| escape.cpp:69:5:69:12 | VariableAddress[no_Point] | no_Point+0:0 | +| escape.cpp:69:14:69:14 | FieldAddress[y] | no_Point+4:0 | +| escape.cpp:69:18:69:21 | VariableAddress[no_x] | no_x+0:0 | +| escape.cpp:70:11:70:14 | VariableAddress[no_y] | no_y+0:0 | +| escape.cpp:70:20:70:27 | VariableAddress[no_Point] | no_Point+0:0 | +| escape.cpp:70:31:70:31 | FieldAddress[y] | no_Point+4:0 | +| escape.cpp:71:7:71:14 | VariableAddress[no_Point] | no_Point+0:0 | +| escape.cpp:71:18:71:18 | FieldAddress[y] | no_Point+4:0 | +| escape.cpp:71:22:71:25 | VariableAddress[no_y] | no_y+0:0 | +| escape.cpp:72:11:72:14 | VariableAddress[no_z] | no_z+0:0 | +| escape.cpp:72:21:72:28 | VariableAddress[no_Point] | no_Point+0:0 | +| escape.cpp:72:30:72:30 | FieldAddress[z] | no_Point+8:0 | +| escape.cpp:73:8:73:15 | VariableAddress[no_Point] | no_Point+0:0 | +| escape.cpp:73:17:73:17 | FieldAddress[z] | no_Point+8:0 | +| escape.cpp:73:22:73:25 | VariableAddress[no_z] | no_z+0:0 | +| escape.cpp:75:13:75:22 | VariableAddress[no_Derived] | no_Derived+0:0 | +| escape.cpp:76:5:76:14 | ConvertToBase[Derived : Intermediate1] | no_Derived+0:0 | +| escape.cpp:76:5:76:14 | ConvertToBase[Intermediate1 : Base] | no_Derived+0:0 | +| escape.cpp:76:5:76:14 | VariableAddress[no_Derived] | no_Derived+0:0 | +| escape.cpp:76:16:76:16 | FieldAddress[b] | no_Derived+0:0 | +| escape.cpp:77:11:77:14 | VariableAddress[no_b] | no_b+0:0 | +| escape.cpp:77:18:77:27 | ConvertToBase[Derived : Intermediate1] | no_Derived+0:0 | +| escape.cpp:77:18:77:27 | ConvertToBase[Intermediate1 : Base] | no_Derived+0:0 | +| escape.cpp:77:18:77:27 | VariableAddress[no_Derived] | no_Derived+0:0 | +| escape.cpp:77:29:77:29 | FieldAddress[b] | no_Derived+0:0 | +| escape.cpp:78:5:78:14 | ConvertToBase[Derived : Intermediate2] | no_Derived+12:0 | +| escape.cpp:78:5:78:14 | VariableAddress[no_Derived] | no_Derived+0:0 | +| escape.cpp:78:16:78:17 | FieldAddress[i2] | no_Derived+16:0 | +| escape.cpp:79:11:79:15 | VariableAddress[no_i2] | no_i2+0:0 | +| escape.cpp:79:19:79:28 | ConvertToBase[Derived : Intermediate2] | no_Derived+12:0 | +| escape.cpp:79:19:79:28 | VariableAddress[no_Derived] | no_Derived+0:0 | +| escape.cpp:79:30:79:31 | FieldAddress[i2] | no_Derived+16:0 | +| escape.cpp:81:9:81:21 | VariableAddress[no_ssa_addrOf] | no_ssa_addrOf+0:0 | +| escape.cpp:82:10:82:13 | VariableAddress[no_p] | no_p+0:0 | +| escape.cpp:82:17:82:30 | Store | no_ssa_addrOf+0:0 | +| escape.cpp:82:18:82:30 | VariableAddress[no_ssa_addrOf] | no_ssa_addrOf+0:0 | +| escape.cpp:84:9:84:20 | VariableAddress[no_ssa_refTo] | no_ssa_refTo+0:0 | +| escape.cpp:85:10:85:13 | VariableAddress[no_r] | no_r+0:0 | +| escape.cpp:85:17:85:28 | Store | no_ssa_refTo+0:0 | +| escape.cpp:85:17:85:28 | VariableAddress[no_ssa_refTo] | no_ssa_refTo+0:0 | +| escape.cpp:87:9:87:32 | VariableAddress[no_ssa_refToArrayElement] | no_ssa_refToArrayElement+0:0 | +| escape.cpp:88:10:88:15 | VariableAddress[no_rae] | no_rae+0:0 | +| escape.cpp:88:19:88:42 | Convert | no_ssa_refToArrayElement+0:0 | +| escape.cpp:88:19:88:42 | VariableAddress[no_ssa_refToArrayElement] | no_ssa_refToArrayElement+0:0 | +| escape.cpp:88:19:88:45 | PointerAdd[4] | no_ssa_refToArrayElement+20:0 | +| escape.cpp:88:19:88:45 | Store | no_ssa_refToArrayElement+20:0 | +| escape.cpp:90:9:90:25 | VariableAddress[no_ssa_refToArray] | no_ssa_refToArray+0:0 | +| escape.cpp:91:11:91:15 | VariableAddress[no_ra] | no_ra+0:0 | +| escape.cpp:91:24:91:40 | Store | no_ssa_refToArray+0:0 | +| escape.cpp:91:24:91:40 | VariableAddress[no_ssa_refToArray] | no_ssa_refToArray+0:0 | +| escape.cpp:93:9:93:17 | VariableAddress[passByPtr] | passByPtr+0:0 | +| escape.cpp:94:20:94:28 | VariableAddress[passByPtr] | passByPtr+0:0 | +| escape.cpp:96:9:96:17 | VariableAddress[passByRef] | passByRef+0:0 | +| escape.cpp:97:21:97:29 | VariableAddress[passByRef] | passByRef+0:0 | diff --git a/cpp/ql/test/library-tests/aliased_ssa/escape/points_to.ql b/cpp/ql/test/library-tests/aliased_ssa/escape/points_to.ql new file mode 100644 index 000000000000..95746737a148 --- /dev/null +++ b/cpp/ql/test/library-tests/aliased_ssa/escape/points_to.ql @@ -0,0 +1,11 @@ +import default +import semmle.code.cpp.ssa.internal.ssa.AliasAnalysis +import semmle.code.cpp.ir.IR + +from Instruction instr, string pointsTo +where + exists(IRVariable var, int bitOffset | + resultPointsTo(instr, var, bitOffset) and + pointsTo = var.toString() + getBitOffsetString(bitOffset) + ) +select instr, pointsTo diff --git a/cpp/ql/test/library-tests/aliased_ssa/escape/ssa_escape.expected b/cpp/ql/test/library-tests/aliased_ssa/escape/ssa_escape.expected new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/cpp/ql/test/library-tests/aliased_ssa/escape/ssa_escape.ql b/cpp/ql/test/library-tests/aliased_ssa/escape/ssa_escape.ql new file mode 100644 index 000000000000..f38fab307401 --- /dev/null +++ b/cpp/ql/test/library-tests/aliased_ssa/escape/ssa_escape.ql @@ -0,0 +1,21 @@ +import default +import semmle.code.cpp.ssa.internal.aliased_ssa.AliasAnalysis +import semmle.code.cpp.ssa.SSAIR + +predicate shouldEscape(IRAutomaticUserVariable var) { + exists(string name | + name = var.getVariable().getName() and + name.matches("no_%") + ) +} + +from IRAutomaticUserVariable var +where + exists(FunctionIR funcIR | + funcIR = var.getFunctionIR() and + ( + (shouldEscape(var) and variableAddressEscapes(var)) or + (not shouldEscape(var) and not variableAddressEscapes(var)) + ) + ) +select var diff --git a/cpp/ql/test/library-tests/allocators/allocators.cpp b/cpp/ql/test/library-tests/allocators/allocators.cpp new file mode 100644 index 000000000000..a15056b0f19c --- /dev/null +++ b/cpp/ql/test/library-tests/allocators/allocators.cpp @@ -0,0 +1,111 @@ +// extractor_flags: -std=c++17 +typedef unsigned long size_t; +namespace std { + enum class align_val_t : size_t {}; +} + +void* operator new(size_t, float); +void* operator new[](size_t, float); +void* operator new(size_t, std::align_val_t, float); +void* operator new[](size_t, std::align_val_t, float); +void operator delete(void*, float); +void operator delete[](void*, float); +void operator delete(void*, std::align_val_t, float); +void operator delete[](void*, std::align_val_t, float); + +struct String { + String(); + String(const String&); + String(String&&); + String(const char*); + ~String(); + + String& operator=(const String&); + String& operator=(String&&); + + const char* c_str() const; + +private: + const char* p; +}; + +struct SizedDealloc { + char a[32]; + void* operator new(size_t); + void* operator new[](size_t); + void operator delete(void*, size_t); + void operator delete[](void*, size_t); +}; + +struct alignas(128) Overaligned { + char a[256]; +}; + +struct PolymorphicBase { + virtual ~PolymorphicBase(); +}; + +void OperatorNew() { + new int; // No constructor + new(1.0f) int; // Placement new, no constructor + new int(); // Zero-init + new String(); // Constructor + new(1.0f) String("hello"); // Placement new, constructor with args + new Overaligned; // Aligned new + new(1.0f) Overaligned(); // Placement aligned new +} + +void OperatorDelete() { + delete static_cast(nullptr); // No destructor + delete static_cast(nullptr); // Non-virtual destructor, with size. + delete static_cast(nullptr); // No destructor, with size. + delete static_cast(nullptr); // No destructor, with size and alignment. + delete static_cast(nullptr); // Virtual destructor + delete static_cast(nullptr); // Pointer to const +} + +void OperatorNewArray(int n) { + new int[n]; // No constructor + new(1.0f) int[n]; // Placement new, no constructor + new String[n]; // Constructor + new Overaligned[n]; // Aligned new + new String[10]; // Constant size +} + +int* const GetPointer(); + +void OperatorDeleteArray() { + delete[] static_cast(nullptr); // No destructor + delete[] static_cast(nullptr); // Non-virtual destructor, with size. + delete[] static_cast(nullptr); // No destructor, with size. + delete[] static_cast(nullptr); // No destructor, with size and alignment. + delete[] static_cast(nullptr); // Virtual destructor + delete[] GetPointer(); +} + +struct FailedInit { + FailedInit(); + ~FailedInit(); + + void* operator new(size_t); // Non-placement + void* operator new[](size_t); // Non-placement + void operator delete(void*, size_t); // Sized deallocation + void operator delete[](void*, size_t); // Sized deallocation +}; + +struct alignas(128) FailedInitOveraligned { + FailedInitOveraligned(); + ~FailedInitOveraligned(); + + void* operator new(size_t, std::align_val_t, float); // Aligned placement + void* operator new[](size_t, std::align_val_t, float); // Aligned placement + void operator delete(void*, std::align_val_t, float); // Aligned placement + void operator delete[](void*, std::align_val_t, float); // Aligned placement +}; + +void TestFailedInit(int n) { + new FailedInit(); + new FailedInit[n]; + new(1.0f) FailedInitOveraligned(); + new(1.0f) FailedInitOveraligned[10]; +} diff --git a/cpp/ql/test/library-tests/allocators/allocators.expected b/cpp/ql/test/library-tests/allocators/allocators.expected new file mode 100644 index 000000000000..586b82c8664f --- /dev/null +++ b/cpp/ql/test/library-tests/allocators/allocators.expected @@ -0,0 +1,41 @@ +newExprs +| allocators.cpp:49:3:49:9 | new | int | operator new(unsigned long) -> void * | 4 | 4 | | +| allocators.cpp:50:3:50:15 | new | int | operator new(size_t, float) -> void * | 4 | 4 | | +| allocators.cpp:51:3:51:11 | new | int | operator new(unsigned long) -> void * | 4 | 4 | | +| allocators.cpp:52:3:52:14 | new | String | operator new(unsigned long) -> void * | 8 | 8 | | +| allocators.cpp:53:3:53:27 | new | String | operator new(size_t, float) -> void * | 8 | 8 | | +| allocators.cpp:54:3:54:17 | new | Overaligned | operator new(unsigned long, align_val_t) -> void * | 256 | 128 | aligned | +| allocators.cpp:55:3:55:25 | new | Overaligned | operator new(size_t, align_val_t, float) -> void * | 256 | 128 | aligned | +| allocators.cpp:107:3:107:18 | new | FailedInit | FailedInit::operator new(size_t) -> void * | 1 | 1 | | +| allocators.cpp:109:3:109:35 | new | FailedInitOveraligned | FailedInitOveraligned::operator new(size_t, align_val_t, float) -> void * | 128 | 128 | aligned | +newArrayExprs +| allocators.cpp:68:3:68:12 | new[] | int | operator new[](unsigned long) -> void * | 4 | 4 | | +| allocators.cpp:69:3:69:18 | new[] | int | operator new[](size_t, float) -> void * | 4 | 4 | | +| allocators.cpp:70:3:70:15 | new[] | String | operator new[](unsigned long) -> void * | 8 | 8 | | +| allocators.cpp:71:3:71:20 | new[] | Overaligned | operator new[](unsigned long, align_val_t) -> void * | 256 | 128 | aligned | +| allocators.cpp:72:3:72:16 | new[] | String | operator new[](unsigned long) -> void * | 8 | 8 | | +| allocators.cpp:108:3:108:19 | new[] | FailedInit | FailedInit::operator new[](size_t) -> void * | 1 | 1 | | +| allocators.cpp:110:3:110:37 | new[] | FailedInitOveraligned | FailedInitOveraligned::operator new[](size_t, align_val_t, float) -> void * | 128 | 128 | aligned | +newExprDeallocators +| allocators.cpp:52:3:52:14 | new | String | operator delete(void *, unsigned long) -> void | 8 | 8 | sized | +| allocators.cpp:53:3:53:27 | new | String | operator delete(void *, float) -> void | 8 | 8 | | +| allocators.cpp:107:3:107:18 | new | FailedInit | FailedInit::operator delete(void *, size_t) -> void | 1 | 1 | sized | +| allocators.cpp:109:3:109:35 | new | FailedInitOveraligned | FailedInitOveraligned::operator delete(void *, align_val_t, float) -> void | 128 | 128 | aligned | +newArrayExprDeallocators +| allocators.cpp:70:3:70:15 | new[] | String | operator delete[](void *, unsigned long) -> void | 8 | 8 | sized | +| allocators.cpp:72:3:72:16 | new[] | String | operator delete[](void *, unsigned long) -> void | 8 | 8 | sized | +| allocators.cpp:108:3:108:19 | new[] | FailedInit | FailedInit::operator delete[](void *, size_t) -> void | 1 | 1 | sized | +| allocators.cpp:110:3:110:37 | new[] | FailedInitOveraligned | FailedInitOveraligned::operator delete[](void *, align_val_t, float) -> void | 128 | 128 | aligned | +deleteExprs +| allocators.cpp:59:3:59:35 | delete | int | operator delete(void *, unsigned long) -> void | 4 | 4 | sized | +| allocators.cpp:60:3:60:38 | delete | String | operator delete(void *, unsigned long) -> void | 8 | 8 | sized | +| allocators.cpp:61:3:61:44 | delete | SizedDealloc | SizedDealloc::operator delete(void *, size_t) -> void | 32 | 1 | sized | +| allocators.cpp:62:3:62:43 | delete | Overaligned | operator delete(void *, unsigned long, align_val_t) -> void | 256 | 128 | sized aligned | +| allocators.cpp:64:3:64:44 | delete | const String | operator delete(void *, unsigned long) -> void | 8 | 8 | sized | +deleteArrayExprs +| allocators.cpp:78:3:78:37 | delete[] | int | operator delete[](void *, unsigned long) -> void | 4 | 4 | sized | +| allocators.cpp:79:3:79:40 | delete[] | String | operator delete[](void *, unsigned long) -> void | 8 | 8 | sized | +| allocators.cpp:80:3:80:46 | delete[] | SizedDealloc | SizedDealloc::operator delete[](void *, size_t) -> void | 32 | 1 | sized | +| allocators.cpp:81:3:81:45 | delete[] | Overaligned | operator delete[](void *, unsigned long, align_val_t) -> void | 256 | 128 | sized aligned | +| allocators.cpp:82:3:82:49 | delete[] | PolymorphicBase | operator delete[](void *, unsigned long) -> void | 8 | 8 | sized | +| allocators.cpp:83:3:83:23 | delete[] | int | operator delete[](void *, unsigned long) -> void | 4 | 4 | sized | diff --git a/cpp/ql/test/library-tests/allocators/allocators.ql b/cpp/ql/test/library-tests/allocators/allocators.ql new file mode 100644 index 000000000000..66c8d20b3f40 --- /dev/null +++ b/cpp/ql/test/library-tests/allocators/allocators.ql @@ -0,0 +1,89 @@ +import default + +query predicate newExprs(NewExpr expr, string type, string sig, int size, int alignment, string form) { + exists(Function allocator, Type allocatedType | + expr.getAllocator() = allocator and + sig = allocator.getFullSignature() and + allocatedType = expr.getAllocatedType() and + type = allocatedType.toString() and + size = allocatedType.getSize() and + alignment = allocatedType.getAlignment() and + if expr.hasAlignedAllocation() then form = "aligned" else form = "" + ) +} + +query predicate newArrayExprs(NewArrayExpr expr, string type, string sig, int size, int alignment, string form) { + exists(Function allocator, Type elementType | + expr.getAllocator() = allocator and + sig = allocator.getFullSignature() and + elementType = expr.getAllocatedElementType() and + type = elementType.toString() and + size = elementType.getSize() and + alignment = elementType.getAlignment() and + if expr.hasAlignedAllocation() then form = "aligned" else form = "" + ) +} + +query predicate newExprDeallocators(NewExpr expr, string type, string sig, int size, int alignment, string form) { + exists(Function deallocator, Type allocatedType | + expr.getDeallocator() = deallocator and + sig = deallocator.getFullSignature() and + allocatedType = expr.getAllocatedType() and + type = allocatedType.toString() and + size = allocatedType.getSize() and + alignment = allocatedType.getAlignment() and + exists(string sized, string aligned | + (if expr.hasAlignedDeallocation() then aligned = "aligned" else aligned = "") and + (if expr.hasSizedDeallocation() then sized = "sized" else sized = "") and + form = sized + " " + aligned + ) + ) +} + +query predicate newArrayExprDeallocators(NewArrayExpr expr, string type, string sig, int size, int alignment, string form) { + exists(Function deallocator, Type elementType | + expr.getDeallocator() = deallocator and + sig = deallocator.getFullSignature() and + elementType = expr.getAllocatedElementType() and + type = elementType.toString() and + size = elementType.getSize() and + alignment = elementType.getAlignment() and + exists(string sized, string aligned | + (if expr.hasAlignedDeallocation() then aligned = "aligned" else aligned = "") and + (if expr.hasSizedDeallocation() then sized = "sized" else sized = "") and + form = sized + " " + aligned + ) + ) +} + +query predicate deleteExprs(DeleteExpr expr, string type, string sig, int size, int alignment, string form) { + exists(Function deallocator, Type deletedType | + expr.getDeallocator() = deallocator and + sig = deallocator.getFullSignature() and + deletedType = expr.getDeletedObjectType() and + type = deletedType.toString() and + size = deletedType.getSize() and + alignment = deletedType.getAlignment() and + exists(string sized, string aligned | + (if expr.hasAlignedDeallocation() then aligned = "aligned" else aligned = "") and + (if expr.hasSizedDeallocation() then sized = "sized" else sized = "") and + form = sized + " " + aligned + ) + ) +} + +query predicate deleteArrayExprs(DeleteArrayExpr expr, string type, string sig, int size, int alignment, string form) { + exists(Function deallocator, Type elementType | + expr.getDeallocator() = deallocator and + sig = deallocator.getFullSignature() and + elementType = expr.getDeletedElementType() and + type = elementType.toString() and + size = elementType.getSize() and + alignment = elementType.getAlignment() and + exists(string sized, string aligned | + (if expr.hasAlignedDeallocation() then aligned = "aligned" else aligned = "") and + (if expr.hasSizedDeallocation() then sized = "sized" else sized = "") and + form = sized + " " + aligned + ) + ) +} diff --git a/cpp/ql/test/library-tests/anachronisms/options b/cpp/ql/test/library-tests/anachronisms/options new file mode 100644 index 000000000000..a01498a45e5d --- /dev/null +++ b/cpp/ql/test/library-tests/anachronisms/options @@ -0,0 +1,2 @@ +extractor_flags: --edg --anachronisms + diff --git a/cpp/ql/test/library-tests/anachronisms/test.cpp b/cpp/ql/test/library-tests/anachronisms/test.cpp new file mode 100644 index 000000000000..146e5e6c2153 --- /dev/null +++ b/cpp/ql/test/library-tests/anachronisms/test.cpp @@ -0,0 +1,15 @@ + +class A { +public: + virtual int f(); +}; + +class B: public A { +public: + int f() { return 1; } +}; + +A *p = new A; +int (*pf)() = (int (*)())p->f; + + diff --git a/cpp/ql/test/library-tests/anachronisms/virtfunptrexpr.expected b/cpp/ql/test/library-tests/anachronisms/virtfunptrexpr.expected new file mode 100644 index 000000000000..2a4f078a25fc --- /dev/null +++ b/cpp/ql/test/library-tests/anachronisms/virtfunptrexpr.expected @@ -0,0 +1 @@ +| 1 | diff --git a/cpp/ql/test/library-tests/anachronisms/virtfunptrexpr.ql b/cpp/ql/test/library-tests/anachronisms/virtfunptrexpr.ql new file mode 100644 index 000000000000..a10e4bb03426 --- /dev/null +++ b/cpp/ql/test/library-tests/anachronisms/virtfunptrexpr.ql @@ -0,0 +1,3 @@ +import cpp + +select count(@virtfunptrexpr e) diff --git a/cpp/ql/test/library-tests/arguments/arguments.c b/cpp/ql/test/library-tests/arguments/arguments.c new file mode 100644 index 000000000000..fe7ca426b11e --- /dev/null +++ b/cpp/ql/test/library-tests/arguments/arguments.c @@ -0,0 +1 @@ +// semmle-extractor-options: -Werror diff --git a/cpp/ql/test/library-tests/arguments/arguments.expected b/cpp/ql/test/library-tests/arguments/arguments.expected new file mode 100644 index 000000000000..7422d01133b0 --- /dev/null +++ b/cpp/ql/test/library-tests/arguments/arguments.expected @@ -0,0 +1,14 @@ +| arguments.c | 1 | --preprocessArgs | +| arguments.c | 2 | --force-recompute | +| arguments.c | 3 | --edg | +| arguments.c | 4 | --disable_system_macros | +| arguments.c | 5 | --edg | +| arguments.c | 6 | --target | +| arguments.c | 7 | --edg | +| arguments.c | 8 | linux_x86_64 | +| arguments.c | 9 | --gcc | +| arguments.c | 10 | --predefined_macros | +| arguments.c | 11 | /tools/qltest/predefined_macros | +| arguments.c | 12 | -w | +| arguments.c | 13 | -Werror | +| arguments.c | 14 | arguments.c | diff --git a/cpp/ql/test/library-tests/arguments/arguments.ql b/cpp/ql/test/library-tests/arguments/arguments.ql new file mode 100644 index 000000000000..2680fd90d18b --- /dev/null +++ b/cpp/ql/test/library-tests/arguments/arguments.ql @@ -0,0 +1,10 @@ +import cpp + +from Compilation c, int i, string s +// Skip the extractor name; it'll vary depending on platform +where i > 0 + and s = c.getArgument(i).replaceAll("\\", "/") + .regexpReplaceAll(".*(tools/qltest/predefined_macros)", "/$1") +select c.getAFileCompiled().toString(), + i, s + diff --git a/cpp/ql/test/library-tests/array_expr/array_expr.c b/cpp/ql/test/library-tests/array_expr/array_expr.c new file mode 100644 index 000000000000..21cf54358dcd --- /dev/null +++ b/cpp/ql/test/library-tests/array_expr/array_expr.c @@ -0,0 +1,4 @@ +int main() { + int arr[] = {1}; + return arr[0] + 0[arr]; +} diff --git a/cpp/ql/test/library-tests/array_expr/parts.expected b/cpp/ql/test/library-tests/array_expr/parts.expected new file mode 100644 index 000000000000..f9edd46e934f --- /dev/null +++ b/cpp/ql/test/library-tests/array_expr/parts.expected @@ -0,0 +1,4 @@ +| getArrayBase | array_expr.c:3:10:3:12 | arr | +| getArrayBase | array_expr.c:3:21:3:23 | arr | +| getArrayOffset | array_expr.c:3:14:3:14 | 0 | +| getArrayOffset | array_expr.c:3:19:3:19 | 0 | diff --git a/cpp/ql/test/library-tests/array_expr/parts.ql b/cpp/ql/test/library-tests/array_expr/parts.ql new file mode 100644 index 000000000000..ed627e12210d --- /dev/null +++ b/cpp/ql/test/library-tests/array_expr/parts.ql @@ -0,0 +1,6 @@ +import cpp + +from ArrayExpr ae, string meth, Expr expr +where (meth = "getArrayBase" and expr = ae.getArrayBase()) + or (meth = "getArrayOffset" and expr = ae.getArrayOffset()) +select meth, expr diff --git a/cpp/ql/test/library-tests/array_sizes/arr1.expected b/cpp/ql/test/library-tests/array_sizes/arr1.expected new file mode 100644 index 000000000000..02bdc937204a --- /dev/null +++ b/cpp/ql/test/library-tests/array_sizes/arr1.expected @@ -0,0 +1,6 @@ +| array_sizes.c:10:10:10:20 | sizeof_arr5 | +| array_sizes.c:11:11:11:21 | sizeof_arr6 | +| array_sizes.c:14:14:14:24 | sizeof_arr7 | +| array_sizes.cpp:7:10:7:20 | sizeof_arr1 | +| array_sizes.cpp:8:11:8:21 | sizeof_arr2 | +| array_sizes.cpp:11:14:11:24 | sizeof_arr3 | diff --git a/cpp/ql/test/library-tests/array_sizes/arr1.ql b/cpp/ql/test/library-tests/array_sizes/arr1.ql new file mode 100644 index 000000000000..66bfbaeb9495 --- /dev/null +++ b/cpp/ql/test/library-tests/array_sizes/arr1.ql @@ -0,0 +1,6 @@ +import cpp + +from EnumConstant ec, Access ac +where ec.getName().matches("sizeof\\_arr%") +and ac = ec.getAnAccess() +select ac diff --git a/cpp/ql/test/library-tests/array_sizes/arr2.expected b/cpp/ql/test/library-tests/array_sizes/arr2.expected new file mode 100644 index 000000000000..959f04358288 --- /dev/null +++ b/cpp/ql/test/library-tests/array_sizes/arr2.expected @@ -0,0 +1,7 @@ +| file://:0:0:0:0 | char[6] | 6 | +| file://:0:0:0:0 | char[26] | 26 | +| file://:0:0:0:0 | int[1] | 1 | +| file://:0:0:0:0 | int[11] | 11 | +| file://:0:0:0:0 | long[] | | +| file://:0:0:0:0 | short[5] | 5 | +| file://:0:0:0:0 | short[15] | 15 | diff --git a/cpp/ql/test/library-tests/array_sizes/arr2.ql b/cpp/ql/test/library-tests/array_sizes/arr2.ql new file mode 100644 index 000000000000..f7345ca93353 --- /dev/null +++ b/cpp/ql/test/library-tests/array_sizes/arr2.ql @@ -0,0 +1,5 @@ +import cpp + +from ArrayType at, string sz +where if exists(at.getArraySize()) then sz = at.getArraySize().toString() else sz = "" +select at, sz diff --git a/cpp/ql/test/library-tests/array_sizes/array_sizes.c b/cpp/ql/test/library-tests/array_sizes/array_sizes.c new file mode 100644 index 000000000000..d8796a8ac14f --- /dev/null +++ b/cpp/ql/test/library-tests/array_sizes/array_sizes.c @@ -0,0 +1,16 @@ +// semmle-extractor-options: --g++ --gnu_version 40602 +// this test is in a .c file with compiler flags set to be like g++. This is a workaround to +// build as C++ without C++11, a configuration where we've had problems in the past. +enum { + sizeof_arr5 = 11, + sizeof_arr6 = 13, + sizeof_arr7 = 15 +}; + +int arr5[sizeof_arr5] = {0}; +char arr6[sizeof_arr6 * 2]; + +int main(int argc) { + short arr7[sizeof_arr7]; + long arr8[argc]; +} diff --git a/cpp/ql/test/library-tests/array_sizes/array_sizes.cpp b/cpp/ql/test/library-tests/array_sizes/array_sizes.cpp new file mode 100644 index 000000000000..1586bbd55eaf --- /dev/null +++ b/cpp/ql/test/library-tests/array_sizes/array_sizes.cpp @@ -0,0 +1,13 @@ +enum { + sizeof_arr1 = 1, + sizeof_arr2 = 3, + sizeof_arr3 = 5 +}; + +int arr1[sizeof_arr1] = {0}; +char arr2[sizeof_arr2 * 2]; + +int main(int argc) { + short arr3[sizeof_arr3]; + long arr4[argc]; +} diff --git a/cpp/ql/test/library-tests/atomic/atomic.cpp b/cpp/ql/test/library-tests/atomic/atomic.cpp new file mode 100644 index 000000000000..96153c2118ca --- /dev/null +++ b/cpp/ql/test/library-tests/atomic/atomic.cpp @@ -0,0 +1,21 @@ +template +struct atomic_box +{ + mutable _Atomic(T) value; +}; + +int main() { + _Atomic int a; + _Atomic(int) b; + _Atomic int *c, d; + _Atomic(int*) e, f; + atomic_box g; + _Atomic const int h = 0; + const _Atomic(int) i = 0; + _Atomic(_Atomic(int)*) j; + _Atomic int* _Atomic k; + int m = 0; + + a = m; + m = a; +} diff --git a/cpp/ql/test/library-tests/atomic/options b/cpp/ql/test/library-tests/atomic/options new file mode 100644 index 000000000000..5e8fada6b21f --- /dev/null +++ b/cpp/ql/test/library-tests/atomic/options @@ -0,0 +1 @@ +extractor_flags: --clang diff --git a/cpp/ql/test/library-tests/atomic/variables.expected b/cpp/ql/test/library-tests/atomic/variables.expected new file mode 100644 index 000000000000..e5303709924c --- /dev/null +++ b/cpp/ql/test/library-tests/atomic/variables.expected @@ -0,0 +1,20 @@ +| a | _Atomic(int) | atomic {int} | +| b | _Atomic(int) | atomic {int} | +| c | _Atomic(int) * | pointer to {atomic {int}} | +| d | _Atomic(int) | atomic {int} | +| e | int *_Atomic | atomic {pointer to {int}} | +| f | int *_Atomic | atomic {pointer to {int}} | +| fp_offset | unsigned int | unsigned int | +| g | atomic_box | struct atomic_box | +| gp_offset | unsigned int | unsigned int | +| h | const _Atomic(int) | atomic const {int} | +| i | const _Atomic(int) | atomic const {int} | +| j | _Atomic(int) *_Atomic | atomic {pointer to {atomic {int}}} | +| k | _Atomic(int) *_Atomic | atomic {pointer to {atomic {int}}} | +| m | int | int | +| overflow_arg_area | void * | pointer to {void} | +| p#0 | atomic_box && | rvalue reference to {struct atomic_box} | +| p#0 | const atomic_box & | reference to {const {struct atomic_box}} | +| reg_save_area | void * | pointer to {void} | +| value | _Atomic(T) | atomic {T} | +| value | _Atomic(int) | atomic {int} | diff --git a/cpp/ql/test/library-tests/atomic/variables.ql b/cpp/ql/test/library-tests/atomic/variables.ql new file mode 100644 index 000000000000..c1643ac75f7a --- /dev/null +++ b/cpp/ql/test/library-tests/atomic/variables.ql @@ -0,0 +1,4 @@ +import cpp + +from Variable v +select v.getName(), v.getType().toString(), v.getType().explain() diff --git a/cpp/ql/test/library-tests/attributes/alignment/alignment.cpp b/cpp/ql/test/library-tests/attributes/alignment/alignment.cpp new file mode 100644 index 000000000000..a6ca72526ae5 --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/alignment/alignment.cpp @@ -0,0 +1,35 @@ +namespace AlignAs { +struct alignas(16) sse_t +{ + float sse_data[4]; +}; + +struct alignas(sse_t) avx_t +{ + float avx_data[8]; +}; + +} +namespace AttributeAlign { + +struct [[align(16)]] sse_t +{ + float sse_data[4]; +}; + +struct [[align(sse_t)]] avx_t +{ + float avx_data[8]; +}; + +struct [[align(1024 * 8)]] align_to_8_kilobytes +{ + int x; +}; + +struct [[align(1024 * 1024 * 256)]] align_to_256_megabytes +{ + int x; +}; + +} diff --git a/cpp/ql/test/library-tests/attributes/alignment/alignment.expected b/cpp/ql/test/library-tests/attributes/alignment/alignment.expected new file mode 100644 index 000000000000..168ff2abe63c --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/alignment/alignment.expected @@ -0,0 +1,6 @@ +| alignment.cpp:2:8:2:14 | AlignAs::sse_t | 16 | +| alignment.cpp:7:8:7:14 | AlignAs::avx_t | AlignAs::sse_t | +| alignment.cpp:15:10:15:14 | AttributeAlign::sse_t | 16 | +| alignment.cpp:20:10:20:14 | AttributeAlign::avx_t | AttributeAlign::sse_t | +| alignment.cpp:25:10:25:14 | AttributeAlign::align_to_8_kilobytes | 8192 | +| alignment.cpp:30:10:30:14 | AttributeAlign::align_to_256_megabytes | 268435456 | diff --git a/cpp/ql/test/library-tests/attributes/alignment/alignment.ql b/cpp/ql/test/library-tests/attributes/alignment/alignment.ql new file mode 100644 index 000000000000..2f81e786b193 --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/alignment/alignment.ql @@ -0,0 +1,6 @@ +import cpp + +from Struct s, Attribute a, string alignment +where a = s.getAnAttribute() + and (alignment = a.getAnArgument().getValueInt().toString() or alignment = ((Struct)a.getAnArgument().getValueType()).getQualifiedName()) +select a.getLocation().toString(), s.getQualifiedName(), alignment diff --git a/cpp/ql/test/library-tests/attributes/availability/availability.c b/cpp/ql/test/library-tests/attributes/availability/availability.c new file mode 100644 index 000000000000..8e7892aa1082 --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/availability/availability.c @@ -0,0 +1,11 @@ +#if __has_feature(attribute_availability) +void f(void) __attribute__((availability(macosx,introduced=10.4,deprecated=10.6,obsoleted=10.7))); +#endif + +#if __has_feature(attribute_availability_with_message) +void g(void) __attribute__((availability(ios,unavailable,message="Woah, don't use this."))); +#endif + +void h(void); + +void tententhree(void) __attribute__((availability(macosx,introduced=10.10.3,message = "Ten" "Ten" "Three"))); diff --git a/cpp/ql/test/library-tests/attributes/availability/availability.expected b/cpp/ql/test/library-tests/attributes/availability/availability.expected new file mode 100644 index 000000000000..270c386bfb06 --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/availability/availability.expected @@ -0,0 +1,16 @@ +| f | 0 | availability.c:2:42:2:47 | macosx | +| f | 1 | availability.c:2:49:2:58 | introduced | +| f | 2 | availability.c:2:60:2:63 | 10.4 | +| f | 3 | availability.c:2:65:2:74 | deprecated | +| f | 4 | availability.c:2:76:2:79 | 10.6 | +| f | 5 | availability.c:2:81:2:89 | obsoleted | +| f | 6 | availability.c:2:91:2:94 | 10.7 | +| g | 0 | availability.c:6:42:6:44 | ios | +| g | 1 | availability.c:6:46:6:56 | unavailable | +| g | 2 | availability.c:6:58:6:64 | message | +| g | 3 | availability.c:6:66:6:88 | "Woah, don\\'t use this." | +| tententhree | 0 | availability.c:11:52:11:57 | macosx | +| tententhree | 1 | availability.c:11:59:11:68 | introduced | +| tententhree | 2 | availability.c:11:70:11:76 | 10.10.3 | +| tententhree | 3 | availability.c:11:78:11:84 | message | +| tententhree | 4 | availability.c:11:88:11:106 | "TenTenThree" | diff --git a/cpp/ql/test/library-tests/attributes/availability/availability.ql b/cpp/ql/test/library-tests/attributes/availability/availability.ql new file mode 100644 index 000000000000..2894df522c7a --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/availability/availability.ql @@ -0,0 +1,6 @@ +import cpp + +from Function f, Attribute a, int i +where a = f.getAnAttribute() + and a.hasName("availability") +select f.getName(), i, a.getArgument(i) diff --git a/cpp/ql/test/library-tests/attributes/availability/options b/cpp/ql/test/library-tests/attributes/availability/options new file mode 100644 index 000000000000..93878bfd66cb --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/availability/options @@ -0,0 +1 @@ +extractor_flags: --edg --clang diff --git a/cpp/ql/test/library-tests/attributes/deprecated_with_msg/clang421.c b/cpp/ql/test/library-tests/attributes/deprecated_with_msg/clang421.c new file mode 100644 index 000000000000..43e489fdffb8 --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/deprecated_with_msg/clang421.c @@ -0,0 +1,2 @@ +static int clang421 = __has_feature(attribute_deprecated_with_message); +// semmle-extractor-options: --gnu_version 40201 --edg --clang diff --git a/cpp/ql/test/library-tests/attributes/deprecated_with_msg/clang450.c b/cpp/ql/test/library-tests/attributes/deprecated_with_msg/clang450.c new file mode 100644 index 000000000000..80b0a5a8711c --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/deprecated_with_msg/clang450.c @@ -0,0 +1,2 @@ +static int clang450 = __has_feature(attribute_deprecated_with_message); +// semmle-extractor-options: --gnu_version 40500 --edg --clang diff --git a/cpp/ql/test/library-tests/attributes/deprecated_with_msg/deprecated_with_msg.expected b/cpp/ql/test/library-tests/attributes/deprecated_with_msg/deprecated_with_msg.expected new file mode 100644 index 000000000000..fcc90fac7abd --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/deprecated_with_msg/deprecated_with_msg.expected @@ -0,0 +1,4 @@ +| clang421.c:1:12:1:19 | clang421 | 0 | +| clang450.c:1:12:1:19 | clang450 | 1 | +| gcc421.c:1:12:1:17 | gcc421 | 0 | +| gcc450.c:1:12:1:17 | gcc450 | 1 | diff --git a/cpp/ql/test/library-tests/attributes/deprecated_with_msg/deprecated_with_msg.ql b/cpp/ql/test/library-tests/attributes/deprecated_with_msg/deprecated_with_msg.ql new file mode 100644 index 000000000000..19157bc9ff2d --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/deprecated_with_msg/deprecated_with_msg.ql @@ -0,0 +1,4 @@ +import cpp + +from Variable v +select v, v.getInitializer().getExpr().getValue() diff --git a/cpp/ql/test/library-tests/attributes/deprecated_with_msg/gcc421.c b/cpp/ql/test/library-tests/attributes/deprecated_with_msg/gcc421.c new file mode 100644 index 000000000000..25ca6a4f694c --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/deprecated_with_msg/gcc421.c @@ -0,0 +1,2 @@ +static int gcc421 = __has_feature(attribute_deprecated_with_message); +// semmle-extractor-options: --gnu_version 40201 --edg --clang diff --git a/cpp/ql/test/library-tests/attributes/deprecated_with_msg/gcc450.c b/cpp/ql/test/library-tests/attributes/deprecated_with_msg/gcc450.c new file mode 100644 index 000000000000..e10dd05dfc85 --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/deprecated_with_msg/gcc450.c @@ -0,0 +1,2 @@ +static int gcc450 = __has_feature(attribute_deprecated_with_message); +// semmle-extractor-options: --gnu_version 40500 --edg --clang diff --git a/cpp/ql/test/library-tests/attributes/enumerators/enumerators.c b/cpp/ql/test/library-tests/attributes/enumerators/enumerators.c new file mode 100644 index 000000000000..deccab31ee51 --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/enumerators/enumerators.c @@ -0,0 +1,23 @@ +// Some source code checks for enumerator_attributes prior to using enumerator attributes: +enum OperationMode { + OM_Invalid, + OM_Normal, + OM_Terrified +#if __has_feature(enumerator_attributes) + __attribute__((deprecated)) +#endif + , + OM_AbortOnError +#if __has_feature(enumerator_attributes) + __attribute__((deprecated)) +#endif + = 4 +}; + +// Other source code just goes ahead and uses them: +enum NSUserNotificationActivationType { + NSUserNotificationActivationTypeNone = 0, + NSUserNotificationActivationTypeContentsClicked = 1, + NSUserNotificationActivationTypeActionButtonClicked = 2, + NSUserNotificationActivationTypeReplied __attribute__((availability(macosx,introduced=10.9))) = 3 +}; diff --git a/cpp/ql/test/library-tests/attributes/enumerators/enumerators.expected b/cpp/ql/test/library-tests/attributes/enumerators/enumerators.expected new file mode 100644 index 000000000000..c4799c9bed2c --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/enumerators/enumerators.expected @@ -0,0 +1,3 @@ +| enumerators.c:5:3:5:14 | OM_Terrified | enumerators.c:7:18:7:27 | deprecated | +| enumerators.c:10:3:10:17 | OM_AbortOnError | enumerators.c:12:18:12:27 | deprecated | +| enumerators.c:22:5:22:43 | NSUserNotificationActivationTypeReplied | enumerators.c:22:60:22:71 | availability | diff --git a/cpp/ql/test/library-tests/attributes/enumerators/enumerators.ql b/cpp/ql/test/library-tests/attributes/enumerators/enumerators.ql new file mode 100644 index 000000000000..9fe445d76056 --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/enumerators/enumerators.ql @@ -0,0 +1,4 @@ +import cpp + +from EnumConstant ec +select ec, ec.getAnAttribute() diff --git a/cpp/ql/test/library-tests/attributes/enumerators/options b/cpp/ql/test/library-tests/attributes/enumerators/options new file mode 100644 index 000000000000..5e8fada6b21f --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/enumerators/options @@ -0,0 +1 @@ +extractor_flags: --clang diff --git a/cpp/ql/test/library-tests/attributes/exclusive_locks_required/attributes.expected b/cpp/ql/test/library-tests/attributes/exclusive_locks_required/attributes.expected new file mode 100644 index 000000000000..5d29f0487eef --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/exclusive_locks_required/attributes.expected @@ -0,0 +1,5 @@ +| file://:0:0:0:0 | operator= | | +| file://:0:0:0:0 | operator= | | +| test.cpp:2:33:2:33 | operator= | | +| test.cpp:2:33:2:33 | operator= | | +| test.cpp:7:6:7:6 | f | exclusive_locks_required | diff --git a/cpp/ql/test/library-tests/attributes/exclusive_locks_required/attributes.ql b/cpp/ql/test/library-tests/attributes/exclusive_locks_required/attributes.ql new file mode 100644 index 000000000000..628006311be9 --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/exclusive_locks_required/attributes.ql @@ -0,0 +1,11 @@ +import cpp + +string attribute(Function f) { + if exists(f.getAnAttribute()) + then result = f.getAnAttribute().toString() + else result = "" +} + +from Function f +select f, attribute(f) + diff --git a/cpp/ql/test/library-tests/attributes/exclusive_locks_required/test.cpp b/cpp/ql/test/library-tests/attributes/exclusive_locks_required/test.cpp new file mode 100644 index 000000000000..346a1a5ca58c --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/exclusive_locks_required/test.cpp @@ -0,0 +1,9 @@ + +class __attribute__((lockable)) CSW { +}; + +CSW* csw; + +void f(void) __attribute__((exclusive_locks_required(csw))) { +} + diff --git a/cpp/ql/test/library-tests/attributes/gnu_visibility/visibility.c b/cpp/ql/test/library-tests/attributes/gnu_visibility/visibility.c new file mode 100644 index 000000000000..2eb8e4d9adb8 --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/gnu_visibility/visibility.c @@ -0,0 +1,14 @@ +int h_var __attribute__((visibility("hidden"))); +int p_var __attribute__((visibility("protected"))); +int i_var __attribute__((visibility("internal"))); +int d_var __attribute__((visibility("default"))); + +__attribute__((visibility("hidden"))) void h_rout(); +__attribute__((visibility("protected"))) void p_rout(); +__attribute__((visibility("internal"))) void i_rout(); +__attribute__((visibility("default"))) void d_rout(); + +struct __attribute__((visibility("hidden"))) h_type {}; +struct __attribute__((visibility("protected"))) p_type {}; +struct __attribute__((visibility("internal"))) i_type {}; +struct __attribute__((visibility("default"))) d_type {}; diff --git a/cpp/ql/test/library-tests/attributes/gnu_visibility/visibility.expected b/cpp/ql/test/library-tests/attributes/gnu_visibility/visibility.expected new file mode 100644 index 000000000000..e48cd7bb4970 --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/gnu_visibility/visibility.expected @@ -0,0 +1,12 @@ +| visibility.c:1:5:1:9 | h_var | visibility.c:1:26:1:35 | visibility | visibility.c:1:37:1:44 | hidden | +| visibility.c:2:5:2:9 | p_var | visibility.c:2:26:2:35 | visibility | visibility.c:2:37:2:47 | protected | +| visibility.c:3:5:3:9 | i_var | visibility.c:3:26:3:35 | visibility | visibility.c:3:37:3:46 | internal | +| visibility.c:4:5:4:9 | d_var | visibility.c:4:26:4:35 | visibility | visibility.c:4:37:4:45 | default | +| visibility.c:6:47:6:52 | h_rout | visibility.c:6:16:6:25 | visibility | visibility.c:6:27:6:34 | hidden | +| visibility.c:7:47:7:52 | p_rout | visibility.c:7:16:7:25 | visibility | visibility.c:7:27:7:37 | protected | +| visibility.c:8:47:8:52 | i_rout | visibility.c:8:16:8:25 | visibility | visibility.c:8:27:8:36 | internal | +| visibility.c:9:47:9:52 | d_rout | visibility.c:9:16:9:25 | visibility | visibility.c:9:27:9:35 | default | +| visibility.c:11:49:11:54 | h_type | visibility.c:11:23:11:32 | visibility | visibility.c:11:34:11:41 | hidden | +| visibility.c:12:49:12:54 | p_type | visibility.c:12:23:12:32 | visibility | visibility.c:12:34:12:44 | protected | +| visibility.c:13:49:13:54 | i_type | visibility.c:13:23:13:32 | visibility | visibility.c:13:34:13:43 | internal | +| visibility.c:14:49:14:54 | d_type | visibility.c:14:23:14:32 | visibility | visibility.c:14:34:14:42 | default | diff --git a/cpp/ql/test/library-tests/attributes/gnu_visibility/visibility.ql b/cpp/ql/test/library-tests/attributes/gnu_visibility/visibility.ql new file mode 100644 index 000000000000..566d9992726d --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/gnu_visibility/visibility.ql @@ -0,0 +1,7 @@ +import cpp + +from Element e, Attribute a +where a = ((Variable)e).getAnAttribute() + or a = ((Function)e).getAnAttribute() + or a = ((Struct )e).getAnAttribute() +select e, a, a.getAnArgument() diff --git a/cpp/ql/test/library-tests/attributes/ms_repeated/ms_repeated.c b/cpp/ql/test/library-tests/attributes/ms_repeated/ms_repeated.c new file mode 100644 index 000000000000..5528fc6cf4e4 --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/ms_repeated/ms_repeated.c @@ -0,0 +1,2 @@ +#define FN __declspec(deprecated("aaa")) __declspec(deprecated("bbb")) int x() +FN; diff --git a/cpp/ql/test/library-tests/attributes/ms_repeated/ms_repeated.expected b/cpp/ql/test/library-tests/attributes/ms_repeated/ms_repeated.expected new file mode 100644 index 000000000000..9474169d8bd2 --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/ms_repeated/ms_repeated.expected @@ -0,0 +1,2 @@ +| ms_repeated.c:2:1:2:2 | deprecated | ms_repeated.c:2:1:2:2 | aaa | +| ms_repeated.c:2:1:2:2 | deprecated | ms_repeated.c:2:1:2:2 | bbb | diff --git a/cpp/ql/test/library-tests/attributes/ms_repeated/ms_repeated.ql b/cpp/ql/test/library-tests/attributes/ms_repeated/ms_repeated.ql new file mode 100644 index 000000000000..ba1916e263db --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/ms_repeated/ms_repeated.ql @@ -0,0 +1,4 @@ +import cpp + +from Attribute a +select a, a.getAnArgument() diff --git a/cpp/ql/test/library-tests/attributes/ms_repeated/options b/cpp/ql/test/library-tests/attributes/ms_repeated/options new file mode 100644 index 000000000000..d759bfdab2ee --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/ms_repeated/options @@ -0,0 +1 @@ +extractor_flags: --microsoft --microsoft_version 1700 diff --git a/cpp/ql/test/library-tests/attributes/nonnull/attrs.expected b/cpp/ql/test/library-tests/attributes/nonnull/attrs.expected new file mode 100644 index 000000000000..74b5b83c4d8c --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/nonnull/attrs.expected @@ -0,0 +1,4 @@ +| nonnull.c:3:28:3:34 | nonnull | 1 | +| nonnull.c:3:28:3:34 | nonnull | 2 | +| nonnull.c:6:28:6:34 | nonnull | empty argument | +| nonnull.c:9:28:9:34 | nonnull | | diff --git a/cpp/ql/test/library-tests/attributes/nonnull/attrs.ql b/cpp/ql/test/library-tests/attributes/nonnull/attrs.ql new file mode 100644 index 000000000000..847264af9e93 --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/nonnull/attrs.ql @@ -0,0 +1,10 @@ +import cpp + +string attributeArg(Attribute a) { + if exists(a.getAnArgument()) + then result = a.getAnArgument().toString() + else result = "" +} + +from Attribute a +select a, attributeArg(a) diff --git a/cpp/ql/test/library-tests/attributes/nonnull/nonnull.c b/cpp/ql/test/library-tests/attributes/nonnull/nonnull.c new file mode 100644 index 000000000000..8ed4dcfc31df --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/nonnull/nonnull.c @@ -0,0 +1,10 @@ + +extern void *f1 (void *px, const void *py, int ii) + __attribute__((nonnull (1, 2))); + +extern void *f2 (void *px, const void *py, int ii) + __attribute__((nonnull ())); + +extern void *f3 (void *px, const void *py, int ii) + __attribute__((nonnull)); + diff --git a/cpp/ql/test/library-tests/attributes/routine_attributes/arguments.expected b/cpp/ql/test/library-tests/attributes/routine_attributes/arguments.expected new file mode 100644 index 000000000000..7be35948ed88 --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/routine_attributes/arguments.expected @@ -0,0 +1,7 @@ +| declspec.cpp:4:23:4:43 | Use fatal() instead | declspec.cpp:4:59:4:62 | exit | declspec.cpp:4:12:4:21 | deprecated | Use fatal() instead | +| routine_attributes.c:3:53:3:59 | dummy | routine_attributes.c:3:12:3:24 | named_weakref | routine_attributes.c:3:44:3:50 | weakref | dummy | +| routine_attributes.c:4:62:4:68 | dummy | routine_attributes.c:4:12:4:26 | aliased_weakref | routine_attributes.c:4:55:4:59 | alias | dummy | +| routine_attributes.c:6:49:6:55 | dummy | routine_attributes.c:6:12:6:22 | plain_alias | routine_attributes.c:6:42:6:46 | alias | dummy | +| routine_attributes.c:8:49:8:51 | 100 | routine_attributes.c:8:12:8:18 | init_fn | routine_attributes.c:8:37:8:47 | constructor | 100 | +| routine_attributes.c:9:50:9:52 | 200 | routine_attributes.c:9:12:9:20 | uninit_fn | routine_attributes.c:9:39:9:48 | destructor | 200 | +| routine_attributes.c:20:67:20:79 | "No, really!" | routine_attributes.c:20:12:20:21 | impossible | routine_attributes.c:20:40:20:65 | solves_the_halting_problem | "No, really!" | diff --git a/cpp/ql/test/library-tests/attributes/routine_attributes/arguments.ql b/cpp/ql/test/library-tests/attributes/routine_attributes/arguments.ql new file mode 100644 index 000000000000..f9bac5b82be7 --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/routine_attributes/arguments.ql @@ -0,0 +1,6 @@ +import cpp + +from Function f, Attribute a, AttributeArgument arg +where a = f.getAnAttribute() + and arg = a.getAnArgument() +select arg, f, a, arg.getValueText() diff --git a/cpp/ql/test/library-tests/attributes/routine_attributes/declspec.cpp b/cpp/ql/test/library-tests/attributes/routine_attributes/declspec.cpp new file mode 100644 index 000000000000..5beeed241a1c --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/routine_attributes/declspec.cpp @@ -0,0 +1,14 @@ +__declspec(noreturn) extern void fatal(); +__declspec(dllimport) int imported(); +__declspec(dllexport) int exported() { return 4; } +__declspec(deprecated("Use fatal() instead")) extern void exit(); +__declspec(naked) int no_clothes() {} +__declspec(restrict) float* ma(int size); +__declspec(noalias) void multiply(float* a, float* b, float* c); +class X { + __declspec(noinline) int mbrfunc() { + return 0; + } // will not inline +}; +static __declspec(nothrow, safebuffers) int noCheckBuffers(); +// semmle-extractor-options: --microsoft diff --git a/cpp/ql/test/library-tests/attributes/routine_attributes/header.h b/cpp/ql/test/library-tests/attributes/routine_attributes/header.h new file mode 100644 index 000000000000..69ba55ad316b --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/routine_attributes/header.h @@ -0,0 +1,13 @@ +// header.h + +#ifndef HEADER_H +#define HEADER_H + + DLLEXPORT void myFunction1(int a, int b, int c); + DLLEXPORT void myFunction2(int a, int b, int c); + DLLEXPORT void myFunction3(int a, int b, int c); + + DLLEXPORT void myFunction5(int a, int b, int c); + void myFunction5(int a, int b, int c); + +#endif // HEADER_H diff --git a/cpp/ql/test/library-tests/attributes/routine_attributes/header_export.cpp b/cpp/ql/test/library-tests/attributes/routine_attributes/header_export.cpp new file mode 100644 index 000000000000..e8ee94b00668 --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/routine_attributes/header_export.cpp @@ -0,0 +1,20 @@ +// semmle-extractor-options: --microsoft +// header_export.cpp +#define DLLEXPORT __declspec(dllexport) +#include "header.h" + +DLLEXPORT void myFunction1(int a, int b, int c) +{ +} + +void myFunction2(int a, int b, int c) +{ +} + +DLLEXPORT void myFunction4(int a, int b, int c) +{ +} + +void myFunction5(int a, int b, int c) +{ +} diff --git a/cpp/ql/test/library-tests/attributes/routine_attributes/header_import.cpp b/cpp/ql/test/library-tests/attributes/routine_attributes/header_import.cpp new file mode 100644 index 000000000000..8886070392dd --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/routine_attributes/header_import.cpp @@ -0,0 +1,4 @@ +// semmle-extractor-options: --microsoft +// header_import.cpp +#define DLLEXPORT __declspec(dllimport) +#include "header.h" diff --git a/cpp/ql/test/library-tests/attributes/routine_attributes/routine_attributes.c b/cpp/ql/test/library-tests/attributes/routine_attributes/routine_attributes.c new file mode 100644 index 000000000000..cc10556f2740 --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/routine_attributes/routine_attributes.c @@ -0,0 +1,21 @@ +static int dummy() {} + +static int named_weakref() __attribute__ ((weakref ("dummy"))); +static int aliased_weakref() __attribute__ ((weakref, alias ("dummy"))); +static int plain_weakref() __attribute__ ((weakref)); +static int plain_alias() __attribute__ ((alias ("dummy"))); + +static int init_fn() __attribute__((constructor(100))); +static int uninit_fn() __attribute__((destructor(200))); + +static int pure_fn() __attribute__((pure)); + +static int used_fn() __attribute__((used)); +static int unused_fn() __attribute__((unused)); + +static void* my_alloc() __attribute__((malloc)); + +static int cocktail1() __attribute__((naked, no_instrument_function, no_check_memory_usage, noinline)); +static int cocktail2() __attribute__((always_inline, nothrow)); +static int impossible() __attribute__((solves_the_halting_problem("No, really!"), cures_cancer, is_batman)); +static int empty_attr() __attribute__(()); diff --git a/cpp/ql/test/library-tests/attributes/routine_attributes/routine_attributes.expected b/cpp/ql/test/library-tests/attributes/routine_attributes/routine_attributes.expected new file mode 100644 index 000000000000..1d2d5f1d395d --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/routine_attributes/routine_attributes.expected @@ -0,0 +1,41 @@ +| declspec.cpp:1:34:1:38 | fatal | declspec.cpp:1:12:1:19 | noreturn | +| declspec.cpp:2:27:2:34 | imported | declspec.cpp:2:12:2:20 | dllimport | +| declspec.cpp:3:27:3:34 | exported | declspec.cpp:3:12:3:20 | dllexport | +| declspec.cpp:4:59:4:62 | exit | declspec.cpp:4:12:4:21 | deprecated | +| declspec.cpp:5:23:5:32 | no_clothes | declspec.cpp:5:12:5:16 | naked | +| declspec.cpp:6:29:6:30 | ma | declspec.cpp:6:12:6:19 | restrict | +| declspec.cpp:7:26:7:33 | multiply | declspec.cpp:7:12:7:18 | noalias | +| declspec.cpp:9:29:9:35 | mbrfunc | declspec.cpp:9:15:9:22 | noinline | +| declspec.cpp:13:45:13:58 | noCheckBuffers | declspec.cpp:13:19:13:25 | nothrow | +| declspec.cpp:13:45:13:58 | noCheckBuffers | declspec.cpp:13:28:13:38 | safebuffers | +| header.h:8:17:8:27 | myFunction3 | header.h:8:2:8:10 | dllexport | +| header.h:8:17:8:27 | myFunction3 | header.h:8:2:8:10 | dllimport | +| header_export.cpp:6:16:6:26 | myFunction1 | header.h:6:2:6:10 | dllexport | +| header_export.cpp:6:16:6:26 | myFunction1 | header.h:6:2:6:10 | dllimport | +| header_export.cpp:6:16:6:26 | myFunction1 | header_export.cpp:6:1:6:9 | dllexport | +| header_export.cpp:10:6:10:16 | myFunction2 | header.h:7:2:7:10 | dllexport | +| header_export.cpp:10:6:10:16 | myFunction2 | header.h:7:2:7:10 | dllimport | +| header_export.cpp:14:16:14:26 | myFunction4 | header_export.cpp:14:1:14:9 | dllexport | +| header_export.cpp:18:6:18:16 | myFunction5 | header.h:10:2:10:10 | dllexport | +| header_export.cpp:18:6:18:16 | myFunction5 | header.h:10:2:10:10 | dllimport | +| routine_attributes.c:3:12:3:24 | named_weakref | routine_attributes.c:3:44:3:50 | weakref | +| routine_attributes.c:4:12:4:26 | aliased_weakref | routine_attributes.c:4:46:4:52 | weakref | +| routine_attributes.c:4:12:4:26 | aliased_weakref | routine_attributes.c:4:55:4:59 | alias | +| routine_attributes.c:5:12:5:24 | plain_weakref | routine_attributes.c:5:44:5:50 | weakref | +| routine_attributes.c:6:12:6:22 | plain_alias | routine_attributes.c:6:42:6:46 | alias | +| routine_attributes.c:8:12:8:18 | init_fn | routine_attributes.c:8:37:8:47 | constructor | +| routine_attributes.c:9:12:9:20 | uninit_fn | routine_attributes.c:9:39:9:48 | destructor | +| routine_attributes.c:11:12:11:18 | pure_fn | routine_attributes.c:11:37:11:40 | pure | +| routine_attributes.c:13:12:13:18 | used_fn | routine_attributes.c:13:37:13:40 | used | +| routine_attributes.c:14:12:14:20 | unused_fn | routine_attributes.c:14:39:14:44 | unused | +| routine_attributes.c:16:14:16:21 | my_alloc | routine_attributes.c:16:40:16:45 | malloc | +| routine_attributes.c:18:12:18:20 | cocktail1 | routine_attributes.c:18:39:18:43 | naked | +| routine_attributes.c:18:12:18:20 | cocktail1 | routine_attributes.c:18:46:18:67 | no_instrument_function | +| routine_attributes.c:18:12:18:20 | cocktail1 | routine_attributes.c:18:70:18:90 | no_check_memory_usage | +| routine_attributes.c:18:12:18:20 | cocktail1 | routine_attributes.c:18:93:18:100 | noinline | +| routine_attributes.c:19:12:19:20 | cocktail2 | routine_attributes.c:19:39:19:51 | always_inline | +| routine_attributes.c:19:12:19:20 | cocktail2 | routine_attributes.c:19:54:19:60 | nothrow | +| routine_attributes.c:20:12:20:21 | impossible | routine_attributes.c:20:40:20:65 | solves_the_halting_problem | +| routine_attributes.c:20:12:20:21 | impossible | routine_attributes.c:20:83:20:94 | cures_cancer | +| routine_attributes.c:20:12:20:21 | impossible | routine_attributes.c:20:97:20:105 | is_batman | +| routine_attributes.c:21:12:21:21 | empty_attr | routine_attributes.c:21:40:21:40 | | diff --git a/cpp/ql/test/library-tests/attributes/routine_attributes/routine_attributes.ql b/cpp/ql/test/library-tests/attributes/routine_attributes/routine_attributes.ql new file mode 100644 index 000000000000..6df8ce4d68a8 --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/routine_attributes/routine_attributes.ql @@ -0,0 +1,5 @@ +import cpp + +from Function f, Attribute a +where a = f.getAnAttribute() +select f, a diff --git a/cpp/ql/test/library-tests/attributes/sal/options b/cpp/ql/test/library-tests/attributes/sal/options new file mode 100644 index 000000000000..d759bfdab2ee --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/sal/options @@ -0,0 +1 @@ +extractor_flags: --microsoft --microsoft_version 1700 diff --git a/cpp/ql/test/library-tests/attributes/sal/sal.cpp b/cpp/ql/test/library-tests/attributes/sal/sal.cpp new file mode 100644 index 000000000000..0b58ba7b44e4 --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/sal/sal.cpp @@ -0,0 +1,15 @@ +void Bar( // Attribute style: +[SA_Pre(Null=SA_No,ValidBytes="cb")] +[SA_Pre(Deref=1,Valid=SA_Yes)] +[SA_Pre(Deref=1,Access=SA_Read)] char* pBuf, size_t cb); + +void Foo( // Declspec style: +__declspec("SAL_pre") +__declspec("SAL_valid") +__declspec("SAL_pre") +__declspec("SAL_deref") +__declspec("SAL_readonly") +__declspec("SAL_pre") +__declspec("SAL_readableTo(byteCount(cb))") char* pBuf, size_t cb); + +int Exotic(__declspec("{#foo}") int thisIsWhyAttributeNamesAreHashedInFullIds); diff --git a/cpp/ql/test/library-tests/attributes/sal/sal.expected b/cpp/ql/test/library-tests/attributes/sal/sal.expected new file mode 100644 index 000000000000..00edf9c83b89 --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/sal/sal.expected @@ -0,0 +1,14 @@ +| 2 | sal.cpp:4:40:4:43 | pBuf | sal.cpp:2:2:2:2 | SA_Pre | 0 | Null | SA_No | +| 2 | sal.cpp:4:40:4:43 | pBuf | sal.cpp:2:2:2:2 | SA_Pre | 1 | ValidBytes | "cb" | +| 3 | sal.cpp:4:40:4:43 | pBuf | sal.cpp:3:2:3:2 | SA_Pre | 0 | Deref | 1 | +| 3 | sal.cpp:4:40:4:43 | pBuf | sal.cpp:3:2:3:2 | SA_Pre | 1 | Valid | SA_Yes | +| 4 | sal.cpp:4:40:4:43 | pBuf | sal.cpp:4:2:4:2 | SA_Pre | 0 | Deref | 1 | +| 4 | sal.cpp:4:40:4:43 | pBuf | sal.cpp:4:2:4:2 | SA_Pre | 1 | Access | SA_Read | +| 7 | sal.cpp:13:51:13:54 | pBuf | sal.cpp:7:12:7:12 | SAL_pre | -1 | | | +| 8 | sal.cpp:13:51:13:54 | pBuf | sal.cpp:8:12:8:12 | SAL_valid | -1 | | | +| 9 | sal.cpp:13:51:13:54 | pBuf | sal.cpp:9:12:9:12 | SAL_pre | -1 | | | +| 10 | sal.cpp:13:51:13:54 | pBuf | sal.cpp:10:12:10:12 | SAL_deref | -1 | | | +| 11 | sal.cpp:13:51:13:54 | pBuf | sal.cpp:11:12:11:12 | SAL_readonly | -1 | | | +| 12 | sal.cpp:13:51:13:54 | pBuf | sal.cpp:12:12:12:12 | SAL_pre | -1 | | | +| 13 | sal.cpp:13:51:13:54 | pBuf | sal.cpp:13:12:13:12 | SAL_readableTo(byteCount(cb)) | -1 | | | +| 15 | sal.cpp:15:37:15:77 | thisIsWhyAttributeNamesAreHashedInFullIds | sal.cpp:15:23:15:23 | {#foo} | -1 | | | diff --git a/cpp/ql/test/library-tests/attributes/sal/sal.ql b/cpp/ql/test/library-tests/attributes/sal/sal.ql new file mode 100644 index 000000000000..e4e41a796dc5 --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/sal/sal.ql @@ -0,0 +1,23 @@ +import cpp + +predicate argsOrPlaceholder(Attribute a, int index, int start_line, string name, string value) { + if exists(a.getAnArgument()) then + exists(AttributeArgument arg | arg = a.getAnArgument() | + index = arg.getIndex() and + start_line = arg.getLocation().getStartLine() and + name = arg.getName() and + value = arg.getValueText() + ) + else + ( + index = -1 and + start_line = a.getLocation().getStartLine() and + name = "" and + value = "" + ) +} + +from Parameter p, Attribute a, int arg_index, int arg_line, string arg_name, string arg_value +where a = p.getAnAttribute() + and argsOrPlaceholder(a, arg_index, arg_line, arg_name, arg_value) +select arg_line, p, a, arg_index, arg_name, arg_value diff --git a/cpp/ql/test/library-tests/attributes/stmt_attributes/all_attributes.expected b/cpp/ql/test/library-tests/attributes/stmt_attributes/all_attributes.expected new file mode 100644 index 000000000000..83a8a2f600e5 --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/stmt_attributes/all_attributes.expected @@ -0,0 +1,6 @@ +| clang_fallthrough.cpp:11:3:11:25 | ; | clang_fallthrough.cpp:11:5:11:22 | fallthrough | +| clang_fallthrough.cpp:20:3:20:25 | ; | clang_fallthrough.cpp:20:5:20:22 | fallthrough | +| clang_fallthrough.cpp:22:3:22:25 | ; | clang_fallthrough.cpp:22:5:22:22 | fallthrough | +| clang_fallthrough.cpp:36:4:36:26 | ; | clang_fallthrough.cpp:36:6:36:23 | fallthrough | +| clang_fallthrough.cpp:41:5:41:27 | ; | clang_fallthrough.cpp:41:7:41:24 | fallthrough | +| clang_fallthrough.cpp:43:3:43:25 | ; | clang_fallthrough.cpp:43:5:43:22 | fallthrough | diff --git a/cpp/ql/test/library-tests/attributes/stmt_attributes/all_attributes.ql b/cpp/ql/test/library-tests/attributes/stmt_attributes/all_attributes.ql new file mode 100644 index 000000000000..d7b70aa9a683 --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/stmt_attributes/all_attributes.ql @@ -0,0 +1,5 @@ +import cpp + +from Stmt s, Attribute a +where a = s.getAnAttribute() +select s, a diff --git a/cpp/ql/test/library-tests/attributes/stmt_attributes/clang_fallthrough.cpp b/cpp/ql/test/library-tests/attributes/stmt_attributes/clang_fallthrough.cpp new file mode 100644 index 000000000000..9d92b937d671 --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/stmt_attributes/clang_fallthrough.cpp @@ -0,0 +1,45 @@ + +void test(int x) { + int y = 0; + + switch (x) + { + case 1: + break; + case 2: + case 3: + [[clang::fallthrough]]; + + case 4: + y++; + break; + case 5: + y++; + case 6: + y++; + [[clang::fallthrough]]; + case 7: + [[clang::fallthrough]]; + y++; + + case 8: + { + y++; + } break; + case 9: + { + y++; + } + case 10: + { + y++; + [[clang::fallthrough]]; + } + case 11: + { + y++; + } [[clang::fallthrough]]; + default: + [[clang::fallthrough]]; + } +} diff --git a/cpp/ql/test/library-tests/attributes/stmt_attributes/clang_fallthrough.expected b/cpp/ql/test/library-tests/attributes/stmt_attributes/clang_fallthrough.expected new file mode 100644 index 000000000000..c59d73cfdad8 --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/stmt_attributes/clang_fallthrough.expected @@ -0,0 +1,4 @@ +| clang_fallthrough.cpp:11:3:11:25 | ; | clang_fallthrough.cpp:11:5:11:22 | +| clang_fallthrough.cpp:20:3:20:25 | ; | clang_fallthrough.cpp:20:5:20:22 | +| clang_fallthrough.cpp:36:4:36:26 | ; | clang_fallthrough.cpp:36:6:36:23 | +| clang_fallthrough.cpp:41:5:41:27 | ; | clang_fallthrough.cpp:41:7:41:24 | diff --git a/cpp/ql/test/library-tests/attributes/stmt_attributes/clang_fallthrough.ql b/cpp/ql/test/library-tests/attributes/stmt_attributes/clang_fallthrough.ql new file mode 100644 index 000000000000..cb4dbdb7bf75 --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/stmt_attributes/clang_fallthrough.ql @@ -0,0 +1,7 @@ +import cpp + +from Stmt s, StdAttribute a +where s.getASuccessor() instanceof SwitchCase + and a = s.getAnAttribute() + and a.hasQualifiedName("clang", "fallthrough") +select s, a.getLocation().toString() diff --git a/cpp/ql/test/library-tests/attributes/type_attributes/arguments.expected b/cpp/ql/test/library-tests/attributes/type_attributes/arguments.expected new file mode 100644 index 000000000000..b668523ad4e6 --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/type_attributes/arguments.expected @@ -0,0 +1,3 @@ +| type_attributes_ms.cpp:4:67:4:75 | IDispatch | type_attributes_ms.cpp:4:19:4:22 | uuid | type_attributes_ms.cpp:4:24:4:63 | {00020400-0000-0000-c000-000000000046} | +| type_attributes_ms.cpp:5:30:5:33 | Str1 | type_attributes_ms.cpp:5:12:5:16 | align | type_attributes_ms.cpp:5:18:5:19 | 32 | +| type_attributes_ms.cpp:6:55:6:62 | IUnknown | type_attributes_ms.cpp:6:2:6:2 | uuid | type_attributes_ms.cpp:6:2:6:2 | 00000000-0000-0000-c000-000000000046 | diff --git a/cpp/ql/test/library-tests/attributes/type_attributes/arguments.ql b/cpp/ql/test/library-tests/attributes/type_attributes/arguments.ql new file mode 100644 index 000000000000..c17576c97475 --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/type_attributes/arguments.ql @@ -0,0 +1,6 @@ +import cpp + +from Type t, Attribute a, AttributeArgument arg +where a = t.getAnAttribute() + and arg = a.getAnArgument() +select t, a, arg diff --git a/cpp/ql/test/library-tests/attributes/type_attributes/type_attributes.c b/cpp/ql/test/library-tests/attributes/type_attributes/type_attributes.c new file mode 100644 index 000000000000..1111602430a5 --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/type_attributes/type_attributes.c @@ -0,0 +1,26 @@ +// Compilable with +// gcc -c type_attributes.c +// clang -c type_attributes.c + +struct __attribute__((__packed__)) my_packed_struct { + char c; + int i; +}; + +typedef union __attribute__((__transparent_union__)) { + int i; + int j; +} tu; + +// This union can't be made transparent, as the types aren't the same size +typedef union __attribute__((__transparent_union__)) { + char c; + long long int j; +} notTransparent; + +typedef __attribute__((unused)) int unusedInt; + +typedef int depInt __attribute__ ((deprecated)); + +typedef short __attribute__((__may_alias__)) short_a; + diff --git a/cpp/ql/test/library-tests/attributes/type_attributes/type_attributes.expected b/cpp/ql/test/library-tests/attributes/type_attributes/type_attributes.expected new file mode 100644 index 000000000000..7002f036019b --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/type_attributes/type_attributes.expected @@ -0,0 +1,10 @@ +| file://:0:0:0:0 | short __attribute((__may_alias__)) | type_attributes.c:25:30:25:42 | may_alias | +| type_attributes.c:5:36:5:51 | my_packed_struct | type_attributes.c:5:23:5:32 | packed | +| type_attributes.c:10:54:10:54 | union | type_attributes.c:10:30:10:50 | transparent_union | +| type_attributes.c:16:54:16:54 | union | type_attributes.c:16:30:16:50 | transparent_union | +| type_attributes.c:21:37:21:45 | unusedInt | type_attributes.c:21:24:21:29 | unused | +| type_attributes.c:23:13:23:18 | depInt | type_attributes.c:23:36:23:45 | deprecated | +| type_attributes_ms.cpp:1:29:1:29 | X | type_attributes_ms.cpp:1:19:1:26 | novtable | +| type_attributes_ms.cpp:4:67:4:75 | IDispatch | type_attributes_ms.cpp:4:19:4:22 | uuid | +| type_attributes_ms.cpp:5:30:5:33 | Str1 | type_attributes_ms.cpp:5:12:5:16 | align | +| type_attributes_ms.cpp:6:55:6:62 | IUnknown | type_attributes_ms.cpp:6:2:6:2 | uuid | diff --git a/cpp/ql/test/library-tests/attributes/type_attributes/type_attributes.ql b/cpp/ql/test/library-tests/attributes/type_attributes/type_attributes.ql new file mode 100644 index 000000000000..90e7b5bd26d2 --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/type_attributes/type_attributes.ql @@ -0,0 +1,5 @@ +import cpp + +from Type t, Attribute a +where a = t.getAnAttribute() +select t, a diff --git a/cpp/ql/test/library-tests/attributes/type_attributes/type_attributes_ms.cpp b/cpp/ql/test/library-tests/attributes/type_attributes/type_attributes_ms.cpp new file mode 100644 index 000000000000..8f3658e38d9a --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/type_attributes/type_attributes_ms.cpp @@ -0,0 +1,8 @@ +struct __declspec(novtable) X { + virtual void mf(); +}; +struct __declspec(uuid("{00020400-0000-0000-c000-000000000046}")) IDispatch; +__declspec(align(32)) struct Str1{ int a; }; +[uuid("00000000-0000-0000-C000-000000000046")] struct IUnknown; + +// semmle-extractor-options: --microsoft diff --git a/cpp/ql/test/library-tests/attributes/var_attributes/ms_var_attributes.cpp b/cpp/ql/test/library-tests/attributes/var_attributes/ms_var_attributes.cpp new file mode 100644 index 000000000000..5952529e93eb --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/var_attributes/ms_var_attributes.cpp @@ -0,0 +1,18 @@ +// semmle-extractor-options: --microsoft +// ms_var_attributes.cpp +#define DLLEXPORT __declspec(dllexport) +#include "ms_var_attributes.h" + +DLLEXPORT int myInt1 = 100; +int myInt2 = 100; +DLLEXPORT int myInt4 = 100; +int myInt5 = 100; + +class AddressOfGetter { + __declspec(property(get = getter)) int field; + int& getter() throw(); + void f() throw() + { + &field; + } +}; diff --git a/cpp/ql/test/library-tests/attributes/var_attributes/ms_var_attributes.h b/cpp/ql/test/library-tests/attributes/var_attributes/ms_var_attributes.h new file mode 100644 index 000000000000..527f6e892f1c --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/var_attributes/ms_var_attributes.h @@ -0,0 +1,8 @@ +// ms_var_attributes.h + +DLLEXPORT extern int myInt1; +DLLEXPORT extern int myInt2; +DLLEXPORT extern int myInt3; + +DLLEXPORT extern int myInt5; +extern int myInt5; diff --git a/cpp/ql/test/library-tests/attributes/var_attributes/var_attributes.c b/cpp/ql/test/library-tests/attributes/var_attributes/var_attributes.c new file mode 100644 index 000000000000..336b6971bdfb --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/var_attributes/var_attributes.c @@ -0,0 +1,6 @@ + int weak_var __attribute__((weak)); +static int weakref_var __attribute__((weakref)); +static int used_var __attribute__((used)); +static int unused_var __attribute__((unused)); + +static void f1(unsigned unused_param __attribute__((unused))) {} diff --git a/cpp/ql/test/library-tests/attributes/var_attributes/var_attributes.expected b/cpp/ql/test/library-tests/attributes/var_attributes/var_attributes.expected new file mode 100644 index 000000000000..2ef69dc6c441 --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/var_attributes/var_attributes.expected @@ -0,0 +1,11 @@ +| ms_var_attributes.cpp:6:15:6:20 | myInt1 | ms_var_attributes.cpp:6:1:6:9 | dllexport | +| ms_var_attributes.cpp:6:15:6:20 | myInt1 | ms_var_attributes.h:3:1:3:9 | dllexport | +| ms_var_attributes.cpp:7:5:7:10 | myInt2 | ms_var_attributes.h:4:1:4:9 | dllexport | +| ms_var_attributes.cpp:8:15:8:20 | myInt4 | ms_var_attributes.cpp:8:1:8:9 | dllexport | +| ms_var_attributes.cpp:9:5:9:10 | myInt5 | ms_var_attributes.h:7:1:7:9 | dllexport | +| ms_var_attributes.h:5:22:5:27 | myInt3 | ms_var_attributes.h:5:1:5:9 | dllexport | +| var_attributes.c:1:12:1:19 | weak_var | var_attributes.c:1:36:1:39 | weak | +| var_attributes.c:2:12:2:22 | weakref_var | var_attributes.c:2:39:2:45 | weakref | +| var_attributes.c:3:12:3:19 | used_var | var_attributes.c:3:36:3:39 | used | +| var_attributes.c:4:12:4:21 | unused_var | var_attributes.c:4:38:4:43 | unused | +| var_attributes.c:6:25:6:36 | unused_param | var_attributes.c:6:53:6:58 | unused | diff --git a/cpp/ql/test/library-tests/attributes/var_attributes/var_attributes.ql b/cpp/ql/test/library-tests/attributes/var_attributes/var_attributes.ql new file mode 100644 index 000000000000..4c63354fa9c7 --- /dev/null +++ b/cpp/ql/test/library-tests/attributes/var_attributes/var_attributes.ql @@ -0,0 +1,5 @@ +import cpp + +from Variable v, Attribute a +where a = v.getAnAttribute() +select v, a diff --git a/cpp/ql/test/library-tests/basic_blocks/bb_cfg.expected b/cpp/ql/test/library-tests/basic_blocks/bb_cfg.expected new file mode 100644 index 000000000000..a081680ad466 --- /dev/null +++ b/cpp/ql/test/library-tests/basic_blocks/bb_cfg.expected @@ -0,0 +1,235 @@ +| f_cond_1 | false | 421 | 421 | 3 [test.c:93:17:93:17] | +| f_cond_1 | false | 424 | 424 | 4 [test.c:93:21:93:21] | +| f_cond_1 | false | 433 | 433 | return ... [test.c:94:5:94:11] | +| f_cond_1 | false | 435 | 435 | { ... } [test.c:92:22:95:1] | +| f_cond_1 | true | 421 | 433 | | +| f_cond_1 | true | 424 | 433 | | +| f_cond_1 | true | 435 | 421 | T | +| f_cond_1 | true | 435 | 424 | F | +| f_cond_2 | false | 447 | 447 | 3 [test.c:98:17:98:17] | +| f_cond_2 | false | 450 | 450 | 4 [test.c:98:21:98:21] | +| f_cond_2 | false | 461 | 461 | return ... [test.c:99:5:99:11] | +| f_cond_2 | false | 463 | 463 | { ... } [test.c:97:21:100:1] | +| f_cond_2 | true | 447 | 461 | | +| f_cond_2 | true | 450 | 461 | | +| f_cond_2 | true | 463 | 447 | T | +| f_cond_3 | false | 475 | 475 | 3 [test.c:103:17:103:17] | +| f_cond_3 | false | 478 | 478 | 4 [test.c:103:21:103:21] | +| f_cond_3 | false | 489 | 489 | return ... [test.c:104:5:104:11] | +| f_cond_3 | false | 491 | 491 | { ... } [test.c:102:21:105:1] | +| f_cond_3 | true | 475 | 489 | | +| f_cond_3 | true | 478 | 489 | | +| f_cond_3 | true | 491 | 478 | F | +| f_do_1 | false | 368 | 368 | { ... } [test.c:72:8:74:5] | +| f_do_1 | false | 372 | 372 | return ... [test.c:75:5:75:11] | +| f_do_1 | false | 374 | 374 | { ... } [test.c:71:20:76:1] | +| f_do_1 | true | 368 | 368 | T | +| f_do_1 | true | 368 | 372 | F | +| f_do_1 | true | 374 | 368 | | +| f_do_2 | false | 385 | 385 | { ... } [test.c:79:8:81:5] | +| f_do_2 | false | 389 | 389 | return ... [test.c:82:5:82:11] | +| f_do_2 | false | 391 | 391 | { ... } [test.c:78:19:83:1] | +| f_do_2 | true | 385 | 385 | T | +| f_do_2 | true | 391 | 385 | | +| f_do_3 | false | 402 | 402 | { ... } [test.c:86:8:88:5] | +| f_do_3 | false | 406 | 406 | return ... [test.c:89:5:89:11] | +| f_do_3 | false | 408 | 408 | { ... } [test.c:85:19:90:1] | +| f_do_3 | true | 402 | 406 | F | +| f_do_3 | true | 408 | 402 | | +| f_for_1 | false | 198 | 198 | i [test.c:28:16:28:16] | +| f_for_1 | false | 207 | 207 | { ... } [test.c:28:29:30:5] | +| f_for_1 | false | 224 | 224 | return ... [test.c:31:5:31:11] | +| f_for_1 | false | 226 | 226 | { ... } [test.c:26:20:32:1] | +| f_for_1 | true | 198 | 207 | T | +| f_for_1 | true | 198 | 224 | F | +| f_for_1 | true | 207 | 198 | | +| f_for_1 | true | 226 | 198 | | +| f_for_2 | false | 239 | 239 | 1 [test.c:36:16:36:16] | +| f_for_2 | false | 242 | 242 | { ... } [test.c:36:24:38:5] | +| f_for_2 | false | 259 | 259 | return ... [test.c:39:5:39:11] | +| f_for_2 | false | 261 | 261 | { ... } [test.c:34:20:40:1] | +| f_for_2 | true | 239 | 242 | T | +| f_for_2 | true | 242 | 239 | | +| f_for_2 | true | 261 | 239 | | +| f_for_3 | false | 274 | 274 | 0 [test.c:44:16:44:16] | +| f_for_3 | false | 277 | 277 | { ... } [test.c:44:24:46:5] | +| f_for_3 | false | 294 | 294 | return ... [test.c:47:5:47:11] | +| f_for_3 | false | 296 | 296 | { ... } [test.c:42:20:48:1] | +| f_for_3 | true | 274 | 294 | F | +| f_for_3 | true | 277 | 274 | | +| f_for_3 | true | 296 | 274 | | +| f_if_1 | false | 135 | 135 | { ... } [test.c:3:12:5:5] | +| f_if_1 | false | 139 | 139 | { ... } [test.c:5:12:7:5] | +| f_if_1 | false | 143 | 143 | return ... [test.c:8:1:8:1] | +| f_if_1 | false | 145 | 145 | { ... } [test.c:2:20:8:1] | +| f_if_1 | true | 135 | 143 | | +| f_if_1 | true | 139 | 143 | | +| f_if_1 | true | 145 | 135 | T | +| f_if_1 | true | 145 | 139 | F | +| f_if_2 | false | 156 | 156 | { ... } [test.c:11:12:13:5] | +| f_if_2 | false | 160 | 160 | { ... } [test.c:13:12:15:5] | +| f_if_2 | false | 164 | 164 | return ... [test.c:16:1:16:1] | +| f_if_2 | false | 166 | 166 | { ... } [test.c:10:19:16:1] | +| f_if_2 | true | 156 | 164 | | +| f_if_2 | true | 160 | 164 | | +| f_if_2 | true | 166 | 156 | T | +| f_if_3 | false | 177 | 177 | { ... } [test.c:19:12:21:5] | +| f_if_3 | false | 181 | 181 | { ... } [test.c:21:12:23:5] | +| f_if_3 | false | 185 | 185 | return ... [test.c:24:1:24:1] | +| f_if_3 | false | 187 | 187 | { ... } [test.c:18:19:24:1] | +| f_if_3 | true | 177 | 185 | | +| f_if_3 | true | 181 | 185 | | +| f_if_3 | true | 187 | 181 | F | +| f_switch_1 | false | 495 | 495 | f_switch_1 [test.c:107:6:107:15] | +| f_switch_1 | false | 504 | 504 | case ...: [test.c:109:9:109:15] | +| f_switch_1 | false | 511 | 511 | case ...: [test.c:111:9:111:15] | +| f_switch_1 | false | 518 | 518 | case ...: [test.c:113:9:113:15] | +| f_switch_1 | false | 523 | 523 | case ...: [test.c:114:9:114:15] | +| f_switch_1 | false | 530 | 530 | case ...: [test.c:116:9:116:15] | +| f_switch_1 | false | 534 | 534 | default: [test.c:118:9:118:16] | +| f_switch_1 | false | 542 | 542 | return ... [test.c:121:5:121:11] | +| f_switch_1 | false | 544 | 544 | { ... } [test.c:107:24:122:1] | +| f_switch_1 | true | 504 | 495 | | +| f_switch_1 | true | 511 | 495 | | +| f_switch_1 | true | 518 | 523 | | +| f_switch_1 | true | 523 | 530 | | +| f_switch_1 | true | 530 | 495 | | +| f_switch_1 | true | 534 | 495 | | +| f_switch_1 | true | 542 | 495 | | +| f_switch_1 | true | 544 | 504 | | +| f_switch_1 | true | 544 | 511 | | +| f_switch_1 | true | 544 | 518 | | +| f_switch_1 | true | 544 | 523 | | +| f_switch_1 | true | 544 | 530 | | +| f_switch_1 | true | 544 | 534 | | +| f_switch_2 | false | 548 | 548 | f_switch_2 [test.c:124:6:124:15] | +| f_switch_2 | false | 557 | 557 | case ...: [test.c:126:9:126:15] | +| f_switch_2 | false | 564 | 564 | case ...: [test.c:128:9:128:15] | +| f_switch_2 | false | 571 | 571 | case ...: [test.c:130:9:130:15] | +| f_switch_2 | false | 576 | 576 | case ...: [test.c:131:9:131:15] | +| f_switch_2 | false | 583 | 583 | case ...: [test.c:133:9:133:15] | +| f_switch_2 | false | 591 | 591 | return ... [test.c:136:5:136:11] | +| f_switch_2 | false | 593 | 593 | { ... } [test.c:124:24:137:1] | +| f_switch_2 | true | 557 | 548 | | +| f_switch_2 | true | 564 | 548 | | +| f_switch_2 | true | 571 | 576 | | +| f_switch_2 | true | 576 | 583 | | +| f_switch_2 | true | 583 | 548 | | +| f_switch_2 | true | 591 | 548 | | +| f_switch_2 | true | 593 | 557 | | +| f_switch_2 | true | 593 | 564 | | +| f_switch_2 | true | 593 | 571 | | +| f_switch_2 | true | 593 | 576 | | +| f_switch_2 | true | 593 | 583 | | +| f_switch_2 | true | 593 | 591 | | +| f_switch_3 | false | 596 | 596 | f_switch_3 [test.c:139:6:139:15] | +| f_switch_3 | false | 605 | 605 | case ...: [test.c:141:9:141:15] | +| f_switch_3 | false | 612 | 612 | case ...: [test.c:143:9:143:15] | +| f_switch_3 | false | 619 | 619 | case ...: [test.c:145:9:145:15] | +| f_switch_3 | false | 624 | 624 | case ...: [test.c:146:9:146:15] | +| f_switch_3 | false | 631 | 631 | case ...: [test.c:148:9:148:15] | +| f_switch_3 | false | 635 | 635 | default: [test.c:150:9:150:16] | +| f_switch_3 | false | 643 | 643 | return ... [test.c:153:5:153:11] | +| f_switch_3 | false | 645 | 645 | { ... } [test.c:139:23:154:1] | +| f_switch_3 | true | 605 | 596 | | +| f_switch_3 | true | 612 | 596 | | +| f_switch_3 | true | 619 | 624 | | +| f_switch_3 | true | 624 | 631 | | +| f_switch_3 | true | 631 | 596 | | +| f_switch_3 | true | 635 | 596 | | +| f_switch_3 | true | 643 | 596 | | +| f_switch_3 | true | 645 | 612 | | +| f_switch_4 | false | 648 | 648 | f_switch_4 [test.c:156:6:156:15] | +| f_switch_4 | false | 657 | 657 | case ...: [test.c:158:9:158:15] | +| f_switch_4 | false | 664 | 664 | case ...: [test.c:160:9:160:15] | +| f_switch_4 | false | 671 | 671 | case ...: [test.c:162:9:162:15] | +| f_switch_4 | false | 676 | 676 | case ...: [test.c:163:9:163:15] | +| f_switch_4 | false | 683 | 683 | case ...: [test.c:165:9:165:15] | +| f_switch_4 | false | 691 | 691 | return ... [test.c:168:5:168:11] | +| f_switch_4 | false | 693 | 693 | { ... } [test.c:156:23:169:1] | +| f_switch_4 | true | 657 | 648 | | +| f_switch_4 | true | 664 | 648 | | +| f_switch_4 | true | 671 | 676 | | +| f_switch_4 | true | 676 | 683 | | +| f_switch_4 | true | 683 | 648 | | +| f_switch_4 | true | 691 | 648 | | +| f_switch_4 | true | 693 | 664 | | +| f_switch_4 | true | 693 | 691 | | +| f_switch_5 | false | 696 | 696 | f_switch_5 [test.c:171:6:171:15] | +| f_switch_5 | false | 705 | 705 | case ...: [test.c:173:9:173:15] | +| f_switch_5 | false | 712 | 712 | case ...: [test.c:175:9:175:15] | +| f_switch_5 | false | 719 | 719 | case ...: [test.c:177:9:177:15] | +| f_switch_5 | false | 724 | 724 | case ...: [test.c:178:9:178:15] | +| f_switch_5 | false | 731 | 731 | case ...: [test.c:180:9:180:15] | +| f_switch_5 | false | 735 | 735 | default: [test.c:182:9:182:16] | +| f_switch_5 | false | 743 | 743 | return ... [test.c:185:5:185:11] | +| f_switch_5 | false | 745 | 745 | { ... } [test.c:171:23:186:1] | +| f_switch_5 | true | 705 | 696 | | +| f_switch_5 | true | 712 | 696 | | +| f_switch_5 | true | 719 | 724 | | +| f_switch_5 | true | 724 | 731 | | +| f_switch_5 | true | 731 | 696 | | +| f_switch_5 | true | 735 | 696 | | +| f_switch_5 | true | 743 | 696 | | +| f_switch_5 | true | 745 | 735 | | +| f_switch_6 | false | 748 | 748 | f_switch_6 [test.c:188:6:188:15] | +| f_switch_6 | false | 757 | 757 | case ...: [test.c:190:9:190:15] | +| f_switch_6 | false | 764 | 764 | case ...: [test.c:192:9:192:15] | +| f_switch_6 | false | 771 | 771 | case ...: [test.c:194:9:194:15] | +| f_switch_6 | false | 776 | 776 | case ...: [test.c:195:9:195:15] | +| f_switch_6 | false | 783 | 783 | case ...: [test.c:197:9:197:15] | +| f_switch_6 | false | 791 | 791 | return ... [test.c:200:5:200:11] | +| f_switch_6 | false | 793 | 793 | { ... } [test.c:188:23:201:1] | +| f_switch_6 | true | 757 | 748 | | +| f_switch_6 | true | 764 | 748 | | +| f_switch_6 | true | 771 | 776 | | +| f_switch_6 | true | 776 | 783 | | +| f_switch_6 | true | 783 | 748 | | +| f_switch_6 | true | 791 | 748 | | +| f_switch_6 | true | 793 | 791 | | +| f_switch_7 | false | 797 | 797 | f_switch_7 [test.c:203:6:203:15] | +| f_switch_7 | false | 805 | 805 | 1 [test.c:204:16:204:16] | +| f_switch_7 | false | 808 | 808 | 3 [test.c:204:20:204:20] | +| f_switch_7 | false | 814 | 814 | case ...: [test.c:205:9:205:15] | +| f_switch_7 | false | 821 | 821 | case ...: [test.c:207:9:207:15] | +| f_switch_7 | false | 828 | 828 | case ...: [test.c:209:9:209:15] | +| f_switch_7 | false | 833 | 833 | case ...: [test.c:210:9:210:15] | +| f_switch_7 | false | 840 | 840 | case ...: [test.c:212:9:212:15] | +| f_switch_7 | false | 844 | 844 | { ... } [test.c:204:23:214:5] | +| f_switch_7 | false | 848 | 848 | return ... [test.c:215:5:215:11] | +| f_switch_7 | false | 850 | 850 | { ... } [test.c:203:24:216:1] | +| f_switch_7 | true | 805 | 844 | | +| f_switch_7 | true | 808 | 844 | | +| f_switch_7 | true | 814 | 797 | | +| f_switch_7 | true | 821 | 797 | | +| f_switch_7 | true | 828 | 833 | | +| f_switch_7 | true | 833 | 840 | | +| f_switch_7 | true | 840 | 797 | | +| f_switch_7 | true | 844 | 821 | | +| f_switch_7 | true | 844 | 833 | | +| f_switch_7 | true | 844 | 848 | | +| f_switch_7 | true | 848 | 797 | | +| f_switch_7 | true | 850 | 805 | T | +| f_switch_7 | true | 850 | 808 | F | +| f_while_1 | false | 304 | 304 | i [test.c:51:11:51:11] | +| f_while_1 | false | 312 | 312 | { ... } [test.c:51:14:53:5] | +| f_while_1 | false | 316 | 316 | return ... [test.c:54:5:54:11] | +| f_while_1 | false | 318 | 318 | { ... } [test.c:50:23:55:1] | +| f_while_1 | true | 304 | 312 | T | +| f_while_1 | true | 304 | 316 | F | +| f_while_1 | true | 312 | 304 | | +| f_while_1 | true | 318 | 304 | | +| f_while_2 | false | 326 | 326 | 1 [test.c:58:11:58:11] | +| f_while_2 | false | 329 | 329 | { ... } [test.c:58:14:60:5] | +| f_while_2 | false | 333 | 333 | return ... [test.c:61:5:61:11] | +| f_while_2 | false | 335 | 335 | { ... } [test.c:57:22:62:1] | +| f_while_2 | true | 326 | 329 | T | +| f_while_2 | true | 329 | 326 | | +| f_while_2 | true | 335 | 326 | | +| f_while_3 | false | 343 | 343 | 0 [test.c:65:11:65:11] | +| f_while_3 | false | 346 | 346 | { ... } [test.c:65:14:67:5] | +| f_while_3 | false | 350 | 350 | return ... [test.c:68:5:68:11] | +| f_while_3 | false | 352 | 352 | { ... } [test.c:64:22:69:1] | +| f_while_3 | true | 343 | 350 | F | +| f_while_3 | true | 346 | 343 | | +| f_while_3 | true | 352 | 343 | | diff --git a/cpp/ql/test/library-tests/basic_blocks/bb_cfg.ql b/cpp/ql/test/library-tests/basic_blocks/bb_cfg.ql new file mode 100644 index 000000000000..96bcd3987109 --- /dev/null +++ b/cpp/ql/test/library-tests/basic_blocks/bb_cfg.ql @@ -0,0 +1,34 @@ +// query-type: graph +import cpp + +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() + } +} + +string scope(ControlFlowNode x) { + if exists(x.getControlFlowScope().getQualifiedName()) + then result = x.getControlFlowScope().getQualifiedName() + else result = "" +} + +predicate isNode(boolean isEdge, BasicBlock x, BasicBlock y, string label) { + isEdge = false and x = y and label = x.getStart().toString() + " [" + x.getStart().getLocation().toString() + "]" +} + +predicate isSuccessor(boolean isEdge, BasicBlock x, BasicBlock y, string label) { + exists(string truelabel, string falselabel | + isEdge = true + and x.getASuccessor() = y + and if x.getATrueSuccessor() = y then truelabel = "T" else truelabel = "" + and if x.getAFalseSuccessor() = y then falselabel = "F" else falselabel = "" + and label = truelabel + falselabel) +} + +from boolean isEdge, BasicBlock x, BasicBlock y, string label +where isNode(isEdge, x, y, label) or isSuccessor(isEdge, x, y, label) +select scope(x), isEdge, x, y, label + diff --git a/cpp/ql/test/library-tests/basic_blocks/cfg.expected b/cpp/ql/test/library-tests/basic_blocks/cfg.expected new file mode 100644 index 000000000000..11bdb1e3e58b --- /dev/null +++ b/cpp/ql/test/library-tests/basic_blocks/cfg.expected @@ -0,0 +1,548 @@ +| f_cond_1 | false | 354 | 354 | f_cond_1 | +| f_cond_1 | false | 363 | 363 | ... ? ... : ... | +| f_cond_1 | false | 365 | 365 | i | +| f_cond_1 | false | 369 | 369 | 3 | +| f_cond_1 | false | 372 | 372 | 4 | +| f_cond_1 | false | 373 | 373 | initializer for j | +| f_cond_1 | false | 377 | 377 | { ... } | +| f_cond_1 | false | 379 | 379 | declaration | +| f_cond_1 | false | 381 | 381 | return ... | +| f_cond_1 | true | 363 | 365 | | +| f_cond_1 | true | 365 | 369 | T | +| f_cond_1 | true | 365 | 372 | F | +| f_cond_1 | true | 369 | 381 | | +| f_cond_1 | true | 372 | 381 | | +| f_cond_1 | true | 373 | 363 | | +| f_cond_1 | true | 377 | 379 | | +| f_cond_1 | true | 379 | 373 | | +| f_cond_1 | true | 381 | 354 | | +| f_cond_2 | false | 383 | 383 | f_cond_2 | +| f_cond_2 | false | 389 | 389 | ... ? ... : ... | +| f_cond_2 | false | 393 | 393 | 1 | +| f_cond_2 | false | 396 | 396 | 3 | +| f_cond_2 | false | 399 | 399 | 4 | +| f_cond_2 | false | 402 | 402 | initializer for j | +| f_cond_2 | false | 406 | 406 | { ... } | +| f_cond_2 | false | 408 | 408 | declaration | +| f_cond_2 | false | 410 | 410 | return ... | +| f_cond_2 | true | 389 | 393 | | +| f_cond_2 | true | 393 | 396 | T | +| f_cond_2 | true | 396 | 410 | | +| f_cond_2 | true | 399 | 410 | | +| f_cond_2 | true | 402 | 389 | | +| f_cond_2 | true | 406 | 408 | | +| f_cond_2 | true | 408 | 402 | | +| f_cond_2 | true | 410 | 383 | | +| f_cond_3 | false | 412 | 412 | f_cond_3 | +| f_cond_3 | false | 418 | 418 | ... ? ... : ... | +| f_cond_3 | false | 422 | 422 | 0 | +| f_cond_3 | false | 425 | 425 | 3 | +| f_cond_3 | false | 428 | 428 | 4 | +| f_cond_3 | false | 431 | 431 | initializer for j | +| f_cond_3 | false | 435 | 435 | { ... } | +| f_cond_3 | false | 437 | 437 | declaration | +| f_cond_3 | false | 439 | 439 | return ... | +| f_cond_3 | true | 418 | 422 | | +| f_cond_3 | true | 422 | 428 | F | +| f_cond_3 | true | 425 | 439 | | +| f_cond_3 | true | 428 | 439 | | +| f_cond_3 | true | 431 | 418 | | +| f_cond_3 | true | 435 | 437 | | +| f_cond_3 | true | 437 | 431 | | +| f_cond_3 | true | 439 | 412 | | +| f_do_1 | false | 294 | 294 | f_do_1 | +| f_do_1 | false | 302 | 302 | { ... } | +| f_do_1 | false | 304 | 304 | do (...) ... | +| f_do_1 | false | 306 | 306 | { ... } | +| f_do_1 | false | 308 | 308 | ExprStmt | +| f_do_1 | false | 310 | 310 | ... -- | +| f_do_1 | false | 312 | 312 | i | +| f_do_1 | false | 314 | 314 | i | +| f_do_1 | false | 316 | 316 | return ... | +| f_do_1 | true | 302 | 304 | | +| f_do_1 | true | 304 | 306 | | +| f_do_1 | true | 306 | 308 | | +| f_do_1 | true | 308 | 312 | | +| f_do_1 | true | 310 | 314 | | +| f_do_1 | true | 312 | 310 | | +| f_do_1 | true | 314 | 306 | T | +| f_do_1 | true | 314 | 316 | F | +| f_do_1 | true | 316 | 294 | | +| f_do_2 | false | 318 | 318 | f_do_2 | +| f_do_2 | false | 323 | 323 | { ... } | +| f_do_2 | false | 325 | 325 | do (...) ... | +| f_do_2 | false | 327 | 327 | { ... } | +| f_do_2 | false | 329 | 329 | ; | +| f_do_2 | false | 333 | 333 | 1 | +| f_do_2 | false | 334 | 334 | return ... | +| f_do_2 | true | 323 | 325 | | +| f_do_2 | true | 325 | 327 | | +| f_do_2 | true | 327 | 329 | | +| f_do_2 | true | 329 | 333 | | +| f_do_2 | true | 333 | 327 | T | +| f_do_2 | true | 334 | 318 | | +| f_do_3 | false | 336 | 336 | f_do_3 | +| f_do_3 | false | 341 | 341 | { ... } | +| f_do_3 | false | 343 | 343 | do (...) ... | +| f_do_3 | false | 345 | 345 | { ... } | +| f_do_3 | false | 347 | 347 | ; | +| f_do_3 | false | 351 | 351 | 0 | +| f_do_3 | false | 352 | 352 | return ... | +| f_do_3 | true | 341 | 343 | | +| f_do_3 | true | 343 | 345 | | +| f_do_3 | true | 345 | 347 | | +| f_do_3 | true | 347 | 351 | | +| f_do_3 | true | 351 | 352 | F | +| f_do_3 | true | 352 | 336 | | +| f_for_1 | false | 122 | 122 | f_for_1 | +| f_for_1 | false | 130 | 130 | { ... } | +| f_for_1 | false | 132 | 132 | declaration | +| f_for_1 | false | 134 | 134 | for(...;...;...) ... | +| f_for_1 | false | 136 | 136 | ExprStmt | +| f_for_1 | false | 138 | 138 | ... = ... | +| f_for_1 | false | 140 | 140 | i | +| f_for_1 | false | 144 | 144 | 0 | +| f_for_1 | false | 145 | 145 | ... < ... | +| f_for_1 | false | 147 | 147 | i | +| f_for_1 | false | 151 | 151 | 10 | +| f_for_1 | false | 152 | 152 | ... ++ | +| f_for_1 | false | 154 | 154 | i | +| f_for_1 | false | 156 | 156 | { ... } | +| f_for_1 | false | 158 | 158 | ; | +| f_for_1 | false | 160 | 160 | return ... | +| f_for_1 | true | 130 | 132 | | +| f_for_1 | true | 132 | 134 | | +| f_for_1 | true | 134 | 136 | | +| f_for_1 | true | 136 | 144 | | +| f_for_1 | true | 138 | 147 | | +| f_for_1 | true | 140 | 138 | | +| f_for_1 | true | 144 | 140 | | +| f_for_1 | true | 145 | 156 | T | +| f_for_1 | true | 145 | 160 | F | +| f_for_1 | true | 147 | 151 | | +| f_for_1 | true | 151 | 145 | | +| f_for_1 | true | 152 | 147 | | +| f_for_1 | true | 154 | 152 | | +| f_for_1 | true | 156 | 158 | | +| f_for_1 | true | 158 | 154 | | +| f_for_1 | true | 160 | 122 | | +| f_for_2 | false | 162 | 162 | f_for_2 | +| f_for_2 | false | 170 | 170 | { ... } | +| f_for_2 | false | 172 | 172 | declaration | +| f_for_2 | false | 174 | 174 | for(...;...;...) ... | +| f_for_2 | false | 176 | 176 | ExprStmt | +| f_for_2 | false | 178 | 178 | ... = ... | +| f_for_2 | false | 180 | 180 | i | +| f_for_2 | false | 184 | 184 | 0 | +| f_for_2 | false | 187 | 187 | 1 | +| f_for_2 | false | 188 | 188 | ... ++ | +| f_for_2 | false | 190 | 190 | i | +| f_for_2 | false | 192 | 192 | { ... } | +| f_for_2 | false | 194 | 194 | ; | +| f_for_2 | false | 196 | 196 | return ... | +| f_for_2 | true | 170 | 172 | | +| f_for_2 | true | 172 | 174 | | +| f_for_2 | true | 174 | 176 | | +| f_for_2 | true | 176 | 184 | | +| f_for_2 | true | 178 | 187 | | +| f_for_2 | true | 180 | 178 | | +| f_for_2 | true | 184 | 180 | | +| f_for_2 | true | 187 | 192 | T | +| f_for_2 | true | 188 | 187 | | +| f_for_2 | true | 190 | 188 | | +| f_for_2 | true | 192 | 194 | | +| f_for_2 | true | 194 | 190 | | +| f_for_2 | true | 196 | 162 | | +| f_for_3 | false | 198 | 198 | f_for_3 | +| f_for_3 | false | 206 | 206 | { ... } | +| f_for_3 | false | 208 | 208 | declaration | +| f_for_3 | false | 210 | 210 | for(...;...;...) ... | +| f_for_3 | false | 212 | 212 | ExprStmt | +| f_for_3 | false | 214 | 214 | ... = ... | +| f_for_3 | false | 216 | 216 | i | +| f_for_3 | false | 220 | 220 | 0 | +| f_for_3 | false | 223 | 223 | 0 | +| f_for_3 | false | 224 | 224 | ... ++ | +| f_for_3 | false | 226 | 226 | i | +| f_for_3 | false | 228 | 228 | { ... } | +| f_for_3 | false | 230 | 230 | ; | +| f_for_3 | false | 232 | 232 | return ... | +| f_for_3 | true | 206 | 208 | | +| f_for_3 | true | 208 | 210 | | +| f_for_3 | true | 210 | 212 | | +| f_for_3 | true | 212 | 220 | | +| f_for_3 | true | 214 | 223 | | +| f_for_3 | true | 216 | 214 | | +| f_for_3 | true | 220 | 216 | | +| f_for_3 | true | 223 | 232 | F | +| f_for_3 | true | 224 | 223 | | +| f_for_3 | true | 226 | 224 | | +| f_for_3 | true | 228 | 230 | | +| f_for_3 | true | 230 | 226 | | +| f_for_3 | true | 232 | 198 | | +| f_if_1 | false | 54 | 54 | f_if_1 | +| f_if_1 | false | 62 | 62 | { ... } | +| f_if_1 | false | 64 | 64 | if (...) ... | +| f_if_1 | false | 66 | 66 | i | +| f_if_1 | false | 68 | 68 | { ... } | +| f_if_1 | false | 70 | 70 | ; | +| f_if_1 | false | 72 | 72 | { ... } | +| f_if_1 | false | 74 | 74 | ; | +| f_if_1 | false | 76 | 76 | return ... | +| f_if_1 | true | 62 | 64 | | +| f_if_1 | true | 64 | 66 | | +| f_if_1 | true | 66 | 68 | T | +| f_if_1 | true | 66 | 72 | F | +| f_if_1 | true | 68 | 70 | | +| f_if_1 | true | 70 | 76 | | +| f_if_1 | true | 72 | 74 | | +| f_if_1 | true | 74 | 76 | | +| f_if_1 | true | 76 | 54 | | +| f_if_2 | false | 78 | 78 | f_if_2 | +| f_if_2 | false | 83 | 83 | { ... } | +| f_if_2 | false | 85 | 85 | if (...) ... | +| f_if_2 | false | 89 | 89 | 1 | +| f_if_2 | false | 90 | 90 | { ... } | +| f_if_2 | false | 92 | 92 | ; | +| f_if_2 | false | 94 | 94 | { ... } | +| f_if_2 | false | 96 | 96 | ; | +| f_if_2 | false | 98 | 98 | return ... | +| f_if_2 | true | 83 | 85 | | +| f_if_2 | true | 85 | 89 | | +| f_if_2 | true | 89 | 90 | T | +| f_if_2 | true | 90 | 92 | | +| f_if_2 | true | 92 | 98 | | +| f_if_2 | true | 94 | 96 | | +| f_if_2 | true | 96 | 98 | | +| f_if_2 | true | 98 | 78 | | +| f_if_3 | false | 100 | 100 | f_if_3 | +| f_if_3 | false | 105 | 105 | { ... } | +| f_if_3 | false | 107 | 107 | if (...) ... | +| f_if_3 | false | 111 | 111 | 0 | +| f_if_3 | false | 112 | 112 | { ... } | +| f_if_3 | false | 114 | 114 | ; | +| f_if_3 | false | 116 | 116 | { ... } | +| f_if_3 | false | 118 | 118 | ; | +| f_if_3 | false | 120 | 120 | return ... | +| f_if_3 | true | 105 | 107 | | +| f_if_3 | true | 107 | 111 | | +| f_if_3 | true | 111 | 116 | F | +| f_if_3 | true | 112 | 114 | | +| f_if_3 | true | 114 | 120 | | +| f_if_3 | true | 116 | 118 | | +| f_if_3 | true | 118 | 120 | | +| f_if_3 | true | 120 | 100 | | +| f_switch_1 | false | 441 | 441 | f_switch_1 | +| f_switch_1 | false | 449 | 449 | { ... } | +| f_switch_1 | false | 451 | 451 | switch (...) ... | +| f_switch_1 | false | 453 | 453 | i | +| f_switch_1 | false | 455 | 455 | { ... } | +| f_switch_1 | false | 459 | 459 | 0 | +| f_switch_1 | false | 460 | 460 | case ...: | +| f_switch_1 | false | 462 | 462 | return ... | +| f_switch_1 | false | 466 | 466 | 1 | +| f_switch_1 | false | 467 | 467 | case ...: | +| f_switch_1 | false | 469 | 469 | return ... | +| f_switch_1 | false | 473 | 473 | 2 | +| f_switch_1 | false | 474 | 474 | case ...: | +| f_switch_1 | false | 478 | 478 | 3 | +| f_switch_1 | false | 479 | 479 | case ...: | +| f_switch_1 | false | 481 | 481 | ; | +| f_switch_1 | false | 485 | 485 | 4 | +| f_switch_1 | false | 486 | 486 | case ...: | +| f_switch_1 | false | 488 | 488 | return ... | +| f_switch_1 | false | 490 | 490 | default: | +| f_switch_1 | false | 492 | 492 | return ... | +| f_switch_1 | false | 494 | 494 | return ... | +| f_switch_1 | true | 449 | 451 | | +| f_switch_1 | true | 451 | 453 | | +| f_switch_1 | true | 453 | 455 | | +| f_switch_1 | true | 455 | 460 | | +| f_switch_1 | true | 455 | 467 | | +| f_switch_1 | true | 455 | 474 | | +| f_switch_1 | true | 455 | 479 | | +| f_switch_1 | true | 455 | 486 | | +| f_switch_1 | true | 455 | 490 | | +| f_switch_1 | true | 460 | 462 | | +| f_switch_1 | true | 462 | 441 | | +| f_switch_1 | true | 467 | 469 | | +| f_switch_1 | true | 469 | 441 | | +| f_switch_1 | true | 474 | 479 | | +| f_switch_1 | true | 479 | 481 | | +| f_switch_1 | true | 481 | 486 | | +| f_switch_1 | true | 486 | 488 | | +| f_switch_1 | true | 488 | 441 | | +| f_switch_1 | true | 490 | 492 | | +| f_switch_1 | true | 492 | 441 | | +| f_switch_1 | true | 494 | 441 | | +| f_switch_2 | false | 496 | 496 | f_switch_2 | +| f_switch_2 | false | 504 | 504 | { ... } | +| f_switch_2 | false | 506 | 506 | switch (...) ... | +| f_switch_2 | false | 508 | 508 | i | +| f_switch_2 | false | 510 | 510 | { ... } | +| f_switch_2 | false | 514 | 514 | 0 | +| f_switch_2 | false | 515 | 515 | case ...: | +| f_switch_2 | false | 517 | 517 | return ... | +| f_switch_2 | false | 521 | 521 | 1 | +| f_switch_2 | false | 522 | 522 | case ...: | +| f_switch_2 | false | 524 | 524 | return ... | +| f_switch_2 | false | 528 | 528 | 2 | +| f_switch_2 | false | 529 | 529 | case ...: | +| f_switch_2 | false | 533 | 533 | 3 | +| f_switch_2 | false | 534 | 534 | case ...: | +| f_switch_2 | false | 536 | 536 | ; | +| f_switch_2 | false | 540 | 540 | 4 | +| f_switch_2 | false | 541 | 541 | case ...: | +| f_switch_2 | false | 543 | 543 | return ... | +| f_switch_2 | false | 545 | 545 | return ... | +| f_switch_2 | true | 504 | 506 | | +| f_switch_2 | true | 506 | 508 | | +| f_switch_2 | true | 508 | 510 | | +| f_switch_2 | true | 510 | 515 | | +| f_switch_2 | true | 510 | 522 | | +| f_switch_2 | true | 510 | 529 | | +| f_switch_2 | true | 510 | 534 | | +| f_switch_2 | true | 510 | 541 | | +| f_switch_2 | true | 510 | 545 | | +| f_switch_2 | true | 515 | 517 | | +| f_switch_2 | true | 517 | 496 | | +| f_switch_2 | true | 522 | 524 | | +| f_switch_2 | true | 524 | 496 | | +| f_switch_2 | true | 529 | 534 | | +| f_switch_2 | true | 534 | 536 | | +| f_switch_2 | true | 536 | 541 | | +| f_switch_2 | true | 541 | 543 | | +| f_switch_2 | true | 543 | 496 | | +| f_switch_2 | true | 545 | 496 | | +| f_switch_3 | false | 547 | 547 | f_switch_3 | +| f_switch_3 | false | 552 | 552 | { ... } | +| f_switch_3 | false | 554 | 554 | switch (...) ... | +| f_switch_3 | false | 558 | 558 | 1 | +| f_switch_3 | false | 559 | 559 | { ... } | +| f_switch_3 | false | 563 | 563 | 0 | +| f_switch_3 | false | 564 | 564 | case ...: | +| f_switch_3 | false | 566 | 566 | return ... | +| f_switch_3 | false | 570 | 570 | 1 | +| f_switch_3 | false | 571 | 571 | case ...: | +| f_switch_3 | false | 573 | 573 | return ... | +| f_switch_3 | false | 577 | 577 | 2 | +| f_switch_3 | false | 578 | 578 | case ...: | +| f_switch_3 | false | 582 | 582 | 3 | +| f_switch_3 | false | 583 | 583 | case ...: | +| f_switch_3 | false | 585 | 585 | ; | +| f_switch_3 | false | 589 | 589 | 4 | +| f_switch_3 | false | 590 | 590 | case ...: | +| f_switch_3 | false | 592 | 592 | return ... | +| f_switch_3 | false | 594 | 594 | default: | +| f_switch_3 | false | 596 | 596 | return ... | +| f_switch_3 | false | 598 | 598 | return ... | +| f_switch_3 | true | 552 | 554 | | +| f_switch_3 | true | 554 | 558 | | +| f_switch_3 | true | 558 | 559 | | +| f_switch_3 | true | 559 | 571 | | +| f_switch_3 | true | 564 | 566 | | +| f_switch_3 | true | 566 | 547 | | +| f_switch_3 | true | 571 | 573 | | +| f_switch_3 | true | 573 | 547 | | +| f_switch_3 | true | 578 | 583 | | +| f_switch_3 | true | 583 | 585 | | +| f_switch_3 | true | 585 | 590 | | +| f_switch_3 | true | 590 | 592 | | +| f_switch_3 | true | 592 | 547 | | +| f_switch_3 | true | 594 | 596 | | +| f_switch_3 | true | 596 | 547 | | +| f_switch_3 | true | 598 | 547 | | +| f_switch_4 | false | 600 | 600 | f_switch_4 | +| f_switch_4 | false | 605 | 605 | { ... } | +| f_switch_4 | false | 607 | 607 | switch (...) ... | +| f_switch_4 | false | 611 | 611 | 1 | +| f_switch_4 | false | 612 | 612 | { ... } | +| f_switch_4 | false | 616 | 616 | 0 | +| f_switch_4 | false | 617 | 617 | case ...: | +| f_switch_4 | false | 619 | 619 | return ... | +| f_switch_4 | false | 623 | 623 | 1 | +| f_switch_4 | false | 624 | 624 | case ...: | +| f_switch_4 | false | 626 | 626 | return ... | +| f_switch_4 | false | 630 | 630 | 2 | +| f_switch_4 | false | 631 | 631 | case ...: | +| f_switch_4 | false | 635 | 635 | 3 | +| f_switch_4 | false | 636 | 636 | case ...: | +| f_switch_4 | false | 638 | 638 | ; | +| f_switch_4 | false | 642 | 642 | 4 | +| f_switch_4 | false | 643 | 643 | case ...: | +| f_switch_4 | false | 645 | 645 | return ... | +| f_switch_4 | false | 647 | 647 | return ... | +| f_switch_4 | true | 605 | 607 | | +| f_switch_4 | true | 607 | 611 | | +| f_switch_4 | true | 611 | 612 | | +| f_switch_4 | true | 612 | 624 | | +| f_switch_4 | true | 612 | 647 | | +| f_switch_4 | true | 617 | 619 | | +| f_switch_4 | true | 619 | 600 | | +| f_switch_4 | true | 624 | 626 | | +| f_switch_4 | true | 626 | 600 | | +| f_switch_4 | true | 631 | 636 | | +| f_switch_4 | true | 636 | 638 | | +| f_switch_4 | true | 638 | 643 | | +| f_switch_4 | true | 643 | 645 | | +| f_switch_4 | true | 645 | 600 | | +| f_switch_4 | true | 647 | 600 | | +| f_switch_5 | false | 649 | 649 | f_switch_5 | +| f_switch_5 | false | 654 | 654 | { ... } | +| f_switch_5 | false | 656 | 656 | switch (...) ... | +| f_switch_5 | false | 660 | 660 | 9 | +| f_switch_5 | false | 661 | 661 | { ... } | +| f_switch_5 | false | 665 | 665 | 0 | +| f_switch_5 | false | 666 | 666 | case ...: | +| f_switch_5 | false | 668 | 668 | return ... | +| f_switch_5 | false | 672 | 672 | 1 | +| f_switch_5 | false | 673 | 673 | case ...: | +| f_switch_5 | false | 675 | 675 | return ... | +| f_switch_5 | false | 679 | 679 | 2 | +| f_switch_5 | false | 680 | 680 | case ...: | +| f_switch_5 | false | 684 | 684 | 3 | +| f_switch_5 | false | 685 | 685 | case ...: | +| f_switch_5 | false | 687 | 687 | ; | +| f_switch_5 | false | 691 | 691 | 4 | +| f_switch_5 | false | 692 | 692 | case ...: | +| f_switch_5 | false | 694 | 694 | return ... | +| f_switch_5 | false | 696 | 696 | default: | +| f_switch_5 | false | 698 | 698 | return ... | +| f_switch_5 | false | 700 | 700 | return ... | +| f_switch_5 | true | 654 | 656 | | +| f_switch_5 | true | 656 | 660 | | +| f_switch_5 | true | 660 | 661 | | +| f_switch_5 | true | 661 | 696 | | +| f_switch_5 | true | 666 | 668 | | +| f_switch_5 | true | 668 | 649 | | +| f_switch_5 | true | 673 | 675 | | +| f_switch_5 | true | 675 | 649 | | +| f_switch_5 | true | 680 | 685 | | +| f_switch_5 | true | 685 | 687 | | +| f_switch_5 | true | 687 | 692 | | +| f_switch_5 | true | 692 | 694 | | +| f_switch_5 | true | 694 | 649 | | +| f_switch_5 | true | 696 | 698 | | +| f_switch_5 | true | 698 | 649 | | +| f_switch_5 | true | 700 | 649 | | +| f_switch_6 | false | 702 | 702 | f_switch_6 | +| f_switch_6 | false | 707 | 707 | { ... } | +| f_switch_6 | false | 709 | 709 | switch (...) ... | +| f_switch_6 | false | 713 | 713 | 9 | +| f_switch_6 | false | 714 | 714 | { ... } | +| f_switch_6 | false | 718 | 718 | 0 | +| f_switch_6 | false | 719 | 719 | case ...: | +| f_switch_6 | false | 721 | 721 | return ... | +| f_switch_6 | false | 725 | 725 | 1 | +| f_switch_6 | false | 726 | 726 | case ...: | +| f_switch_6 | false | 728 | 728 | return ... | +| f_switch_6 | false | 732 | 732 | 2 | +| f_switch_6 | false | 733 | 733 | case ...: | +| f_switch_6 | false | 737 | 737 | 3 | +| f_switch_6 | false | 738 | 738 | case ...: | +| f_switch_6 | false | 740 | 740 | ; | +| f_switch_6 | false | 744 | 744 | 4 | +| f_switch_6 | false | 745 | 745 | case ...: | +| f_switch_6 | false | 747 | 747 | return ... | +| f_switch_6 | false | 749 | 749 | return ... | +| f_switch_6 | true | 707 | 709 | | +| f_switch_6 | true | 709 | 713 | | +| f_switch_6 | true | 713 | 714 | | +| f_switch_6 | true | 714 | 749 | | +| f_switch_6 | true | 719 | 721 | | +| f_switch_6 | true | 721 | 702 | | +| f_switch_6 | true | 726 | 728 | | +| f_switch_6 | true | 728 | 702 | | +| f_switch_6 | true | 733 | 738 | | +| f_switch_6 | true | 738 | 740 | | +| f_switch_6 | true | 740 | 745 | | +| f_switch_6 | true | 745 | 747 | | +| f_switch_6 | true | 747 | 702 | | +| f_switch_6 | true | 749 | 702 | | +| f_switch_7 | false | 751 | 751 | f_switch_7 | +| f_switch_7 | false | 759 | 759 | { ... } | +| f_switch_7 | false | 761 | 761 | switch (...) ... | +| f_switch_7 | false | 763 | 763 | ... ? ... : ... | +| f_switch_7 | false | 765 | 765 | i | +| f_switch_7 | false | 769 | 769 | 1 | +| f_switch_7 | false | 772 | 772 | 3 | +| f_switch_7 | false | 773 | 773 | { ... } | +| f_switch_7 | false | 777 | 777 | 0 | +| f_switch_7 | false | 778 | 778 | case ...: | +| f_switch_7 | false | 780 | 780 | return ... | +| f_switch_7 | false | 784 | 784 | 1 | +| f_switch_7 | false | 785 | 785 | case ...: | +| f_switch_7 | false | 787 | 787 | return ... | +| f_switch_7 | false | 791 | 791 | 2 | +| f_switch_7 | false | 792 | 792 | case ...: | +| f_switch_7 | false | 796 | 796 | 3 | +| f_switch_7 | false | 797 | 797 | case ...: | +| f_switch_7 | false | 799 | 799 | ; | +| f_switch_7 | false | 803 | 803 | 4 | +| f_switch_7 | false | 804 | 804 | case ...: | +| f_switch_7 | false | 806 | 806 | return ... | +| f_switch_7 | false | 808 | 808 | return ... | +| f_switch_7 | true | 759 | 761 | | +| f_switch_7 | true | 761 | 763 | | +| f_switch_7 | true | 763 | 765 | | +| f_switch_7 | true | 765 | 769 | T | +| f_switch_7 | true | 765 | 772 | F | +| f_switch_7 | true | 769 | 773 | | +| f_switch_7 | true | 772 | 773 | | +| f_switch_7 | true | 773 | 785 | | +| f_switch_7 | true | 773 | 797 | | +| f_switch_7 | true | 773 | 808 | | +| f_switch_7 | true | 778 | 780 | | +| f_switch_7 | true | 780 | 751 | | +| f_switch_7 | true | 785 | 787 | | +| f_switch_7 | true | 787 | 751 | | +| f_switch_7 | true | 792 | 797 | | +| f_switch_7 | true | 797 | 799 | | +| f_switch_7 | true | 799 | 804 | | +| f_switch_7 | true | 804 | 806 | | +| f_switch_7 | true | 806 | 751 | | +| f_switch_7 | true | 808 | 751 | | +| f_while_1 | false | 234 | 234 | f_while_1 | +| f_while_1 | false | 242 | 242 | { ... } | +| f_while_1 | false | 244 | 244 | while (...) ... | +| f_while_1 | false | 246 | 246 | i | +| f_while_1 | false | 248 | 248 | { ... } | +| f_while_1 | false | 250 | 250 | ExprStmt | +| f_while_1 | false | 252 | 252 | ... -- | +| f_while_1 | false | 254 | 254 | i | +| f_while_1 | false | 256 | 256 | return ... | +| f_while_1 | true | 242 | 244 | | +| f_while_1 | true | 244 | 246 | | +| f_while_1 | true | 246 | 248 | T | +| f_while_1 | true | 246 | 256 | F | +| f_while_1 | true | 248 | 250 | | +| f_while_1 | true | 250 | 254 | | +| f_while_1 | true | 252 | 246 | | +| f_while_1 | true | 254 | 252 | | +| f_while_1 | true | 256 | 234 | | +| f_while_2 | false | 258 | 258 | f_while_2 | +| f_while_2 | false | 263 | 263 | { ... } | +| f_while_2 | false | 265 | 265 | while (...) ... | +| f_while_2 | false | 269 | 269 | 1 | +| f_while_2 | false | 270 | 270 | { ... } | +| f_while_2 | false | 272 | 272 | ; | +| f_while_2 | false | 274 | 274 | return ... | +| f_while_2 | true | 263 | 265 | | +| f_while_2 | true | 265 | 269 | | +| f_while_2 | true | 269 | 270 | T | +| f_while_2 | true | 270 | 272 | | +| f_while_2 | true | 272 | 269 | | +| f_while_2 | true | 274 | 258 | | +| f_while_3 | false | 276 | 276 | f_while_3 | +| f_while_3 | false | 281 | 281 | { ... } | +| f_while_3 | false | 283 | 283 | while (...) ... | +| f_while_3 | false | 287 | 287 | 0 | +| f_while_3 | false | 288 | 288 | { ... } | +| f_while_3 | false | 290 | 290 | ; | +| f_while_3 | false | 292 | 292 | return ... | +| f_while_3 | true | 281 | 283 | | +| f_while_3 | true | 283 | 287 | | +| f_while_3 | true | 287 | 292 | F | +| f_while_3 | true | 288 | 290 | | +| f_while_3 | true | 290 | 287 | | +| f_while_3 | true | 292 | 276 | | diff --git a/cpp/ql/test/library-tests/basic_blocks/cfg.ql b/cpp/ql/test/library-tests/basic_blocks/cfg.ql new file mode 100644 index 000000000000..4c3d73d46eea --- /dev/null +++ b/cpp/ql/test/library-tests/basic_blocks/cfg.ql @@ -0,0 +1,34 @@ +// query-type: graph +import cpp + +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() + } +} + +string scope(ControlFlowNode x) { + if exists(x.getControlFlowScope().getQualifiedName()) + then result = x.getControlFlowScope().getQualifiedName() + else result = "" +} + +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 x.getASuccessor() = y + and if x.getATrueSuccessor() = y then truelabel = "T" else truelabel = "" + and if x.getAFalseSuccessor() = y then falselabel = "F" else falselabel = "" + and label = truelabel + falselabel) +} + +from boolean isEdge, ControlFlowNode x, ControlFlowNode y, string label +where isNode(isEdge, x, y, label) or isSuccessor(isEdge, x, y, label) +select scope(x), isEdge, x, y, label + diff --git a/cpp/ql/test/library-tests/basic_blocks/test.c b/cpp/ql/test/library-tests/basic_blocks/test.c new file mode 100644 index 000000000000..9b1d30762905 --- /dev/null +++ b/cpp/ql/test/library-tests/basic_blocks/test.c @@ -0,0 +1,217 @@ + +void f_if_1(int i) { + if (i) { + ; + } else { + ; + } +} + +void f_if_2(void) { + if (1) { + ; + } else { + ; + } +} + +void f_if_3(void) { + if (0) { + ; + } else { + ; + } +} + +void f_for_1(void) { + int i; + for(i = 0; i < 10; i++) { + ; + } + return; +} + +void f_for_2(void) { + int i; + for(i = 0; 1; i++) { + ; + } + return; +} + +void f_for_3(void) { + int i; + for(i = 0; 0; i++) { + ; + } + return; +} + +void f_while_1(int i) { + while(i) { + i--; + } + return; +} + +void f_while_2(void) { + while(1) { + ; + } + return; +} + +void f_while_3(void) { + while(0) { + ; + } + return; +} + +void f_do_1(int i) { + do { + i--; + } while (i); + return; +} + +void f_do_2(void) { + do { + ; + } while (1); + return; +} + +void f_do_3(void) { + do { + ; + } while (0); + return; +} + +void f_cond_1(int i) { + int j = i ? 3 : 4; + return; +} + +void f_cond_2(void) { + int j = 1 ? 3 : 4; + return; +} + +void f_cond_3(void) { + int j = 0 ? 3 : 4; + return; +} + +void f_switch_1(int i) { + switch(i) { + case 0: + return; + case 1: + return; + case 2: + case 3: + ; + case 4: + return; + default: + return; + } + return; +} + +void f_switch_2(int i) { + switch(i) { + case 0: + return; + case 1: + return; + case 2: + case 3: + ; + case 4: + return; + } + return; +} + +void f_switch_3(void) { + switch(1) { + case 0: + return; + case 1: + return; + case 2: + case 3: + ; + case 4: + return; + default: + return; + } + return; +} + +void f_switch_4(void) { + switch(1) { + case 0: + return; + case 1: + return; + case 2: + case 3: + ; + case 4: + return; + } + return; +} + +void f_switch_5(void) { + switch(9) { + case 0: + return; + case 1: + return; + case 2: + case 3: + ; + case 4: + return; + default: + return; + } + return; +} + +void f_switch_6(void) { + switch(9) { + case 0: + return; + case 1: + return; + case 2: + case 3: + ; + case 4: + return; + } + return; +} + +void f_switch_7(int i) { + switch(i ? 1 : 3) { + case 0: + return; + case 1: + return; + case 2: + case 3: + ; + case 4: + return; + } + return; +} + diff --git a/cpp/ql/test/library-tests/blocks/c/blocks.c b/cpp/ql/test/library-tests/blocks/c/blocks.c new file mode 100644 index 000000000000..61cdf068b3ff --- /dev/null +++ b/cpp/ql/test/library-tests/blocks/c/blocks.c @@ -0,0 +1,81 @@ +// Compilable with: +// clang -fblocks -c blocks.c + +void (*functionPtrReturningVoidWithVoidArgument)(void); +int (*functionPtrReturningIntWithIntAndCharArguments)(int, char); +void (*arrayOfTenFunctionPtrsReturningVoidWithIntArgument[10])(int); + +void (^blockReturningVoidWithVoidArgument)(void); +int (^blockReturningIntWithIntAndCharArguments)(int, char); +void (^arrayOfTenBlocksReturningVoidWithIntArgument[10])(int); + +int x; + +void f(void) { + + blockReturningVoidWithVoidArgument + = ^ void (void) { x = 1; }; + + blockReturningVoidWithVoidArgument + = ^ (void) { x = 1; }; + + blockReturningVoidWithVoidArgument + = ^ { x = 1; }; + + // Has an implicit "return;" so result type is void + void (^b1)(int, char) + = ^ (int y, char z) { x = 1; }; + + // Has no implicit "return;" but result type still void + void (^b2)(int, char) + = ^ (int y, char z) { x = 1; while (1) { } }; + + // Explicit return so result type double + double (^b3)(int, char) + = ^ (int y, char z) { x = 1; return 0.5; }; + + // Explicit return so result type double + double (^b4)(int, char) + = ^ (int y, char z) { x = 1; if (1) { return 0.5; }; while (1) { } }; + + // Also has an explicit return type + char (^b5)(int, char) + = ^ char (int y, char z) { x = 1; return 'c'; }; + + // Also has an explicit return type + const char * (^b6)(int, char) + = ^ const char * (int y, char z) { x = 1; return "str"; }; + + // Also has an explicit return type + const char * const * (^b7)(int, char) + = ^ const char * const * (int y, char z) { x = 1; while (1) { }; }; + + char c; + double d; + + b1(5, 'a'); + b2(5, 'a'); + d = b3(5, 'a'); + d = b4(5, 'a'); + c = b5(5, 'a'); +} + +int (^fn)() = ^{ + return 4; +}; + +void g(void) { + // Implicit parameter list + int(^b1)(void) = ^ int { return 4; }; + + // Exotic function pointer case + typedef int (*pointerToFunctionThatReturnsIntWithCharArg)(char); + pointerToFunctionThatReturnsIntWithCharArg functionPointer = 0; + pointerToFunctionThatReturnsIntWithCharArg (^b2)(float) = ^int(*(float x))(char) { return functionPointer; }; +} + +void h(void) { + typedef void(^B)(void); + (B)^{}; + (B)^{}; +} diff --git a/cpp/ql/test/library-tests/blocks/c/blocks.expected b/cpp/ql/test/library-tests/blocks/c/blocks.expected new file mode 100644 index 000000000000..91517b67eb79 --- /dev/null +++ b/cpp/ql/test/library-tests/blocks/c/blocks.expected @@ -0,0 +1,39 @@ +| blocks.c:4:8:4:47 | functionPtrReturningVoidWithVoidArgument | ..(*)(..) | pointer to {function returning {void} with arguments ()} | +| blocks.c:5:7:5:52 | functionPtrReturningIntWithIntAndCharArguments | ..(*)(..) | pointer to {function returning {int} with arguments (int,char)} | +| blocks.c:6:8:6:57 | arrayOfTenFunctionPtrsReturningVoidWithIntArgument | ..(*[10])(..) | array of 10 {pointer to {function returning {void} with arguments (int)}} | +| blocks.c:8:8:8:41 | blockReturningVoidWithVoidArgument | ..(^)(..) | block of {function returning {void} with arguments ()} | +| blocks.c:9:7:9:46 | blockReturningIntWithIntAndCharArguments | ..(^)(..) | block of {function returning {int} with arguments (int,char)} | +| blocks.c:10:8:10:51 | arrayOfTenBlocksReturningVoidWithIntArgument | ..(^[10])(..) | array of 10 {block of {function returning {void} with arguments (int)}} | +| blocks.c:12:5:12:5 | x | int | int | +| blocks.c:26:12:26:13 | b1 | ..(^)(..) | block of {function returning {void} with arguments (int,char)} | +| blocks.c:27:16:27:16 | y | int | int | +| blocks.c:27:24:27:24 | z | char | char | +| blocks.c:30:12:30:13 | b2 | ..(^)(..) | block of {function returning {void} with arguments (int,char)} | +| blocks.c:31:16:31:16 | y | int | int | +| blocks.c:31:24:31:24 | z | char | char | +| blocks.c:34:14:34:15 | b3 | ..(^)(..) | block of {function returning {double} with arguments (int,char)} | +| blocks.c:35:16:35:16 | y | int | int | +| blocks.c:35:24:35:24 | z | char | char | +| blocks.c:38:14:38:15 | b4 | ..(^)(..) | block of {function returning {double} with arguments (int,char)} | +| blocks.c:39:16:39:16 | y | int | int | +| blocks.c:39:24:39:24 | z | char | char | +| blocks.c:42:12:42:13 | b5 | ..(^)(..) | block of {function returning {char} with arguments (int,char)} | +| blocks.c:43:21:43:21 | y | int | int | +| blocks.c:43:29:43:29 | z | char | char | +| blocks.c:46:20:46:21 | b6 | ..(^)(..) | block of {function returning {pointer to {const {char}}} with arguments (int,char)} | +| blocks.c:47:29:47:29 | y | int | int | +| blocks.c:47:37:47:37 | z | char | char | +| blocks.c:50:28:50:29 | b7 | ..(^)(..) | block of {function returning {pointer to {const {pointer to {const {char}}}}} with arguments (int,char)} | +| blocks.c:51:37:51:37 | y | int | int | +| blocks.c:51:45:51:45 | z | char | char | +| blocks.c:53:10:53:10 | c | char | char | +| blocks.c:54:12:54:12 | d | double | double | +| blocks.c:63:7:63:8 | fn | ..(^)(..) | block of {function returning {int} with arguments ()} | +| blocks.c:69:8:69:9 | b1 | ..(^)(..) | block of {function returning {int} with arguments ()} | +| blocks.c:73:46:73:60 | functionPointer | pointerToFunctionThatReturnsIntWithCharArg | typedef {pointer to {function returning {int} with arguments (char)}} as "pointerToFunctionThatReturnsIntWithCharArg" | +| blocks.c:74:48:74:49 | b2 | ..(^)(..) | block of {function returning {typedef {pointer to {function returning {int} with arguments (char)}} as "pointerToFunctionThatReturnsIntWithCharArg"} with arguments (float)} | +| blocks.c:74:74:74:74 | x | float | float | +| file://:0:0:0:0 | fp_offset | unsigned int | unsigned int | +| file://:0:0:0:0 | gp_offset | unsigned int | unsigned int | +| file://:0:0:0:0 | overflow_arg_area | void * | pointer to {void} | +| file://:0:0:0:0 | reg_save_area | void * | pointer to {void} | diff --git a/cpp/ql/test/library-tests/blocks/c/blocks.ql b/cpp/ql/test/library-tests/blocks/c/blocks.ql new file mode 100644 index 000000000000..683201375063 --- /dev/null +++ b/cpp/ql/test/library-tests/blocks/c/blocks.ql @@ -0,0 +1,7 @@ +import cpp + +from Variable v +select v, + v.getType().toString(), + v.getType().explain() + diff --git a/cpp/ql/test/library-tests/blocks/c/code_blocks.expected b/cpp/ql/test/library-tests/blocks/c/code_blocks.expected new file mode 100644 index 000000000000..82f6eaa03e02 --- /dev/null +++ b/cpp/ql/test/library-tests/blocks/c/code_blocks.expected @@ -0,0 +1,15 @@ +| blocks.c:17:11:17:33 | ^ { ... } | file://:0:0:0:0 | ..(^)(..) | block of {function returning {void} with arguments ()} | blocks.c:17:11:17:11 | | file://:0:0:0:0 | void | void | | +| blocks.c:20:11:20:28 | ^ { ... } | file://:0:0:0:0 | ..(^)(..) | block of {function returning {void} with arguments ()} | blocks.c:20:11:20:11 | | file://:0:0:0:0 | void | void | | +| blocks.c:23:11:23:21 | ^ { ... } | file://:0:0:0:0 | ..(^)(..) | block of {function returning {void} with arguments ()} | blocks.c:23:11:23:11 | | file://:0:0:0:0 | void | void | | +| blocks.c:27:8:27:37 | ^ { ... } | file://:0:0:0:0 | ..(^)(..) | block of {function returning {void} with arguments (int,char)} | blocks.c:27:11:27:11 | | file://:0:0:0:0 | void | void | y(int), z(char) | +| blocks.c:31:8:31:51 | ^ { ... } | file://:0:0:0:0 | ..(^)(..) | block of {function returning {void} with arguments (int,char)} | blocks.c:31:11:31:11 | | file://:0:0:0:0 | void | void | y(int), z(char) | +| blocks.c:35:8:35:49 | ^ { ... } | file://:0:0:0:0 | ..(^)(..) | block of {function returning {double} with arguments (int,char)} | blocks.c:35:11:35:11 | | file://:0:0:0:0 | double | double | y(int), z(char) | +| blocks.c:39:8:39:75 | ^ { ... } | file://:0:0:0:0 | ..(^)(..) | block of {function returning {double} with arguments (int,char)} | blocks.c:39:11:39:11 | | file://:0:0:0:0 | double | double | y(int), z(char) | +| blocks.c:43:8:43:54 | ^ { ... } | file://:0:0:0:0 | ..(^)(..) | block of {function returning {char} with arguments (int,char)} | blocks.c:43:11:43:11 | | file://:0:0:0:0 | char | char | y(int), z(char) | +| blocks.c:47:8:47:64 | ^ { ... } | file://:0:0:0:0 | ..(^)(..) | block of {function returning {pointer to {const {char}}} with arguments (int,char)} | blocks.c:47:11:47:11 | | file://:0:0:0:0 | const char * | pointer to {const {char}} | y(int), z(char) | +| blocks.c:51:8:51:73 | ^ { ... } | file://:0:0:0:0 | ..(^)(..) | block of {function returning {pointer to {const {pointer to {const {char}}}}} with arguments (int,char)} | blocks.c:51:11:51:11 | | file://:0:0:0:0 | const char *const * | pointer to {const {pointer to {const {char}}}} | y(int), z(char) | +| blocks.c:63:16:65:2 | ^ { ... } | file://:0:0:0:0 | ..(^)(..) | block of {function returning {int} with arguments ()} | blocks.c:63:16:63:16 | | file://:0:0:0:0 | int | int | | +| blocks.c:69:19:69:39 | ^ { ... } | file://:0:0:0:0 | ..(^)(..) | block of {function returning {int} with arguments ()} | blocks.c:69:22:69:22 | | file://:0:0:0:0 | int | int | | +| blocks.c:74:60:74:111 | ^ { ... } | file://:0:0:0:0 | ..(^)(..) | block of {function returning {typedef {pointer to {function returning {int} with arguments (char)}} as "pointerToFunctionThatReturnsIntWithCharArg"} with arguments (float)} | blocks.c:74:62:74:62 | | file://:0:0:0:0 | ..(*)(..) | pointer to {function returning {int} with arguments (char)} | x(float) | +| blocks.c:79:7:79:9 | ^ { ... } | file://:0:0:0:0 | ..(^)(..) | block of {function returning {void} with arguments ()} | blocks.c:79:7:79:7 | | file://:0:0:0:0 | void | void | | +| blocks.c:80:7:80:9 | ^ { ... } | file://:0:0:0:0 | ..(^)(..) | block of {function returning {void} with arguments ()} | blocks.c:80:7:80:7 | | file://:0:0:0:0 | void | void | | diff --git a/cpp/ql/test/library-tests/blocks/c/code_blocks.ql b/cpp/ql/test/library-tests/blocks/c/code_blocks.ql new file mode 100644 index 000000000000..842832fadb63 --- /dev/null +++ b/cpp/ql/test/library-tests/blocks/c/code_blocks.ql @@ -0,0 +1,25 @@ +import cpp + +predicate paramsString(Function f, int n, string res) { + if n = -1 then res = "" + else exists(string sep | + if n = 0 then sep = "" else sep = ", " + and + exists(string prefixRes | + paramsString(f, n - 1, prefixRes) + and + res = prefixRes + sep + f.getParameter(n) + + "(" + f.getParameter(n).getType() + ")")) +} + +from BlockExpr e, Function f, string params +where f = e.getFunction() + and paramsString(f, f.getNumberOfParameters() - 1, params) +select e, + e.getType() as te, + te.explain(), + e.getFunction(), + f.getType() as tf, + tf.explain(), + params + diff --git a/cpp/ql/test/library-tests/blocks/c/exprs.expected b/cpp/ql/test/library-tests/blocks/c/exprs.expected new file mode 100644 index 000000000000..73e8a84e0ce5 --- /dev/null +++ b/cpp/ql/test/library-tests/blocks/c/exprs.expected @@ -0,0 +1,103 @@ +| blocks.c:6:59:6:60 | 10 | +| blocks.c:10:53:10:54 | 10 | +| blocks.c:16:5:16:38 | blockReturningVoidWithVoidArgument | +| blocks.c:16:5:17:33 | ... = ... | +| blocks.c:17:11:17:33 | ^ { ... } | +| blocks.c:17:25:17:25 | x | +| blocks.c:17:25:17:29 | ... = ... | +| blocks.c:17:29:17:29 | 1 | +| blocks.c:19:5:19:38 | blockReturningVoidWithVoidArgument | +| blocks.c:19:5:20:28 | ... = ... | +| blocks.c:20:11:20:28 | ^ { ... } | +| blocks.c:20:20:20:20 | x | +| blocks.c:20:20:20:24 | ... = ... | +| blocks.c:20:24:20:24 | 1 | +| blocks.c:22:5:22:38 | blockReturningVoidWithVoidArgument | +| blocks.c:22:5:23:21 | ... = ... | +| blocks.c:23:11:23:21 | ^ { ... } | +| blocks.c:23:13:23:13 | x | +| blocks.c:23:13:23:17 | ... = ... | +| blocks.c:23:17:23:17 | 1 | +| blocks.c:27:8:27:37 | ^ { ... } | +| blocks.c:27:29:27:29 | x | +| blocks.c:27:29:27:33 | ... = ... | +| blocks.c:27:33:27:33 | 1 | +| blocks.c:31:8:31:51 | ^ { ... } | +| blocks.c:31:29:31:29 | x | +| blocks.c:31:29:31:33 | ... = ... | +| blocks.c:31:33:31:33 | 1 | +| blocks.c:31:43:31:43 | 1 | +| blocks.c:35:8:35:49 | ^ { ... } | +| blocks.c:35:29:35:29 | x | +| blocks.c:35:29:35:33 | ... = ... | +| blocks.c:35:33:35:33 | 1 | +| blocks.c:35:43:35:45 | 0.5 | +| blocks.c:39:8:39:75 | ^ { ... } | +| blocks.c:39:29:39:29 | x | +| blocks.c:39:29:39:33 | ... = ... | +| blocks.c:39:33:39:33 | 1 | +| blocks.c:39:40:39:40 | 1 | +| blocks.c:39:52:39:54 | 0.5 | +| blocks.c:39:67:39:67 | 1 | +| blocks.c:43:8:43:54 | ^ { ... } | +| blocks.c:43:34:43:34 | x | +| blocks.c:43:34:43:38 | ... = ... | +| blocks.c:43:38:43:38 | 1 | +| blocks.c:43:48:43:50 | 99 | +| blocks.c:43:48:43:50 | (char)... | +| blocks.c:47:8:47:64 | ^ { ... } | +| blocks.c:47:42:47:42 | x | +| blocks.c:47:42:47:46 | ... = ... | +| blocks.c:47:46:47:46 | 1 | +| blocks.c:47:56:47:60 | (const char *)... | +| blocks.c:47:56:47:60 | array to pointer conversion | +| blocks.c:47:56:47:60 | str | +| blocks.c:51:8:51:73 | ^ { ... } | +| blocks.c:51:50:51:50 | x | +| blocks.c:51:50:51:54 | ... = ... | +| blocks.c:51:54:51:54 | 1 | +| blocks.c:51:64:51:64 | 1 | +| blocks.c:56:5:56:6 | b1 | +| blocks.c:56:5:56:14 | call to expression | +| blocks.c:56:8:56:8 | 5 | +| blocks.c:56:11:56:13 | 97 | +| blocks.c:56:11:56:13 | (char)... | +| blocks.c:57:5:57:6 | b2 | +| blocks.c:57:5:57:14 | call to expression | +| blocks.c:57:8:57:8 | 5 | +| blocks.c:57:11:57:13 | 97 | +| blocks.c:57:11:57:13 | (char)... | +| blocks.c:58:5:58:5 | d | +| blocks.c:58:5:58:18 | ... = ... | +| blocks.c:58:9:58:10 | b3 | +| blocks.c:58:9:58:18 | call to expression | +| blocks.c:58:12:58:12 | 5 | +| blocks.c:58:15:58:17 | 97 | +| blocks.c:58:15:58:17 | (char)... | +| blocks.c:59:5:59:5 | d | +| blocks.c:59:5:59:18 | ... = ... | +| blocks.c:59:9:59:10 | b4 | +| blocks.c:59:9:59:18 | call to expression | +| blocks.c:59:12:59:12 | 5 | +| blocks.c:59:15:59:17 | 97 | +| blocks.c:59:15:59:17 | (char)... | +| blocks.c:60:5:60:5 | c | +| blocks.c:60:5:60:18 | ... = ... | +| blocks.c:60:9:60:10 | b5 | +| blocks.c:60:9:60:18 | call to expression | +| blocks.c:60:12:60:12 | 5 | +| blocks.c:60:15:60:17 | 97 | +| blocks.c:60:15:60:17 | (char)... | +| blocks.c:63:16:65:2 | (..(^)(..))... | +| blocks.c:63:16:65:2 | ^ { ... } | +| blocks.c:64:10:64:10 | 4 | +| blocks.c:69:19:69:39 | ^ { ... } | +| blocks.c:69:35:69:35 | 4 | +| blocks.c:73:64:73:64 | 0 | +| blocks.c:73:64:73:64 | (pointerToFunctionThatReturnsIntWithCharArg)... | +| blocks.c:74:60:74:111 | ^ { ... } | +| blocks.c:74:93:74:107 | functionPointer | +| blocks.c:79:3:79:9 | (B)... | +| blocks.c:79:7:79:9 | ^ { ... } | +| blocks.c:80:3:80:9 | (B)... | +| blocks.c:80:7:80:9 | ^ { ... } | diff --git a/cpp/ql/test/library-tests/blocks/c/exprs.ql b/cpp/ql/test/library-tests/blocks/c/exprs.ql new file mode 100644 index 000000000000..3c2a3269e72e --- /dev/null +++ b/cpp/ql/test/library-tests/blocks/c/exprs.ql @@ -0,0 +1,5 @@ +import cpp + +from Expr e +select e + diff --git a/cpp/ql/test/library-tests/blocks/c/options b/cpp/ql/test/library-tests/blocks/c/options new file mode 100644 index 000000000000..81574938c6ae --- /dev/null +++ b/cpp/ql/test/library-tests/blocks/c/options @@ -0,0 +1 @@ +extractor_flags: -fblocks diff --git a/cpp/ql/test/library-tests/blocks/capture/capture.c b/cpp/ql/test/library-tests/blocks/capture/capture.c new file mode 100644 index 000000000000..69c761df4abb --- /dev/null +++ b/cpp/ql/test/library-tests/blocks/capture/capture.c @@ -0,0 +1,41 @@ + +void printf(char *str, int a, int b); +typedef void (^voidBlock)(void); +voidBlock Block_copy(voidBlock); + +int x; + +void (^b1)(void); +void (^b2)(void); +void (^b3)(void); + +void f(void) { + __block int y; + + x = 1; + y = 2; + + b1 = Block_copy(^ void (void) { + printf("%d %d\n", x, y); + }); + + b2 = Block_copy(^ void (void) { + x = 3; + y = 4; + }); +} + +int main(void) { + f(); + b1(); // 1 2 + b2(); + b1(); // 3 4 + b3 = b1; + f(); + b3(); // 1 4 (global x is reset, still using the old y) + b1(); // 1 2 (using the new y) + b2(); + b1(); // 3 4 + return 0; +} + diff --git a/cpp/ql/test/library-tests/blocks/capture/capture.expected b/cpp/ql/test/library-tests/blocks/capture/capture.expected new file mode 100644 index 000000000000..3fa9dc0baddd --- /dev/null +++ b/cpp/ql/test/library-tests/blocks/capture/capture.expected @@ -0,0 +1,4 @@ +| capture.c:18:23:20:6 | ^ { ... } | capture.c:19:27:19:27 | x | capture.c:6:5:6:5 | x | capture.c:18:23:18:23 | | +| capture.c:18:23:20:6 | ^ { ... } | capture.c:19:30:19:30 | y | capture.c:13:17:13:17 | y | capture.c:18:23:18:23 | | +| capture.c:22:23:25:6 | ^ { ... } | capture.c:23:9:23:9 | x | capture.c:6:5:6:5 | x | capture.c:22:23:22:23 | | +| capture.c:22:23:25:6 | ^ { ... } | capture.c:24:9:24:9 | y | capture.c:13:17:13:17 | y | capture.c:22:23:22:23 | | diff --git a/cpp/ql/test/library-tests/blocks/capture/capture.ql b/cpp/ql/test/library-tests/blocks/capture/capture.ql new file mode 100644 index 000000000000..7b40e7377576 --- /dev/null +++ b/cpp/ql/test/library-tests/blocks/capture/capture.ql @@ -0,0 +1,9 @@ +import cpp + +from VariableAccess a, BlockExpr b +where a.getEnclosingFunction() = b.getFunction() +select b, + a, + a.getTarget(), + a.getEnclosingFunction() + diff --git a/cpp/ql/test/library-tests/blocks/capture/options b/cpp/ql/test/library-tests/blocks/capture/options new file mode 100644 index 000000000000..81574938c6ae --- /dev/null +++ b/cpp/ql/test/library-tests/blocks/capture/options @@ -0,0 +1 @@ +extractor_flags: -fblocks diff --git a/cpp/ql/test/library-tests/blocks/capture/quals.expected b/cpp/ql/test/library-tests/blocks/capture/quals.expected new file mode 100644 index 000000000000..375ac59046a2 --- /dev/null +++ b/cpp/ql/test/library-tests/blocks/capture/quals.expected @@ -0,0 +1 @@ +| capture.c:13:17:13:17 | y | file://:0:0:0:0 | __block | diff --git a/cpp/ql/test/library-tests/blocks/capture/quals.ql b/cpp/ql/test/library-tests/blocks/capture/quals.ql new file mode 100644 index 000000000000..61004e4d61de --- /dev/null +++ b/cpp/ql/test/library-tests/blocks/capture/quals.ql @@ -0,0 +1,6 @@ +import cpp + +from Variable v +select v, + v.getASpecifier() + diff --git a/cpp/ql/test/library-tests/blocks/cpp/blocks.cpp b/cpp/ql/test/library-tests/blocks/cpp/blocks.cpp new file mode 100644 index 000000000000..d944bf771736 --- /dev/null +++ b/cpp/ql/test/library-tests/blocks/cpp/blocks.cpp @@ -0,0 +1,59 @@ +// Compilable with: +// clang -std=c++11 -fblocks -c blocks.cpp + +void (*functionPtrReturningVoidWithVoidArgument)(void); +int (*functionPtrReturningIntWithIntAndCharArguments)(int, char); +void (*arrayOfTenFunctionPtrsReturningVoidWithIntArgument[10])(int); + +void (^blockReturningVoidWithVoidArgument)(void); +int (^blockReturningIntWithIntAndCharArguments)(int, char); +void (^arrayOfTenBlocksReturningVoidWithIntArgument[10])(int); + +int x; + +void f(void) { + + blockReturningVoidWithVoidArgument + = ^ void (void) { x = 1; }; + + blockReturningVoidWithVoidArgument + = ^ (void) { x = 1; }; + + blockReturningVoidWithVoidArgument + = ^ { x = 1; }; + + // Has an implicit "return;" so result type is void + auto b1 + = ^ (int y, char z) { x = 1; }; + + // Has no implicit "return;" but result type still void + auto b2 + = ^ (int y, char z) { x = 1; while (1) { } }; + + // Explicit return so result type char + auto b3 + = ^ (int y, char z) { x = 1; return 'c'; }; + + // Explicit return so result type char + auto b4 + = ^ (int y, char z) { x = 1; if (1) { return 'c'; }; while (1) { } }; + + // Also has an explicit return type + auto b5 + = ^ char (int y, char z) { x = 1; return 'c'; }; + + char c; + + b1(5, 'a'); + b2(5, 'a'); + c = b3(5, 'a'); + c = b4(5, 'a'); + c = b5(5, 'a'); +} + +struct Example { + int four; + int getFour() { + return (^{ return four; })(); + } +}; diff --git a/cpp/ql/test/library-tests/blocks/cpp/blocks.expected b/cpp/ql/test/library-tests/blocks/cpp/blocks.expected new file mode 100644 index 000000000000..5a4830a470f1 --- /dev/null +++ b/cpp/ql/test/library-tests/blocks/cpp/blocks.expected @@ -0,0 +1,30 @@ +| blocks.cpp:4:8:4:47 | functionPtrReturningVoidWithVoidArgument | file://:0:0:0:0 | ..(*)(..) | pointer to {function returning {void} with arguments ()} | +| blocks.cpp:5:7:5:52 | functionPtrReturningIntWithIntAndCharArguments | file://:0:0:0:0 | ..(*)(..) | pointer to {function returning {int} with arguments (int,char)} | +| blocks.cpp:6:8:6:57 | arrayOfTenFunctionPtrsReturningVoidWithIntArgument | file://:0:0:0:0 | ..(*[10])(..) | array of 10 {pointer to {function returning {void} with arguments (int)}} | +| blocks.cpp:8:8:8:41 | blockReturningVoidWithVoidArgument | file://:0:0:0:0 | ..(^)(..) | block of {function returning {void} with arguments ()} | +| blocks.cpp:9:7:9:46 | blockReturningIntWithIntAndCharArguments | file://:0:0:0:0 | ..(^)(..) | block of {function returning {int} with arguments (int,char)} | +| blocks.cpp:10:8:10:51 | arrayOfTenBlocksReturningVoidWithIntArgument | file://:0:0:0:0 | ..(^[10])(..) | array of 10 {block of {function returning {void} with arguments (int)}} | +| blocks.cpp:12:5:12:5 | x | file://:0:0:0:0 | int | int | +| blocks.cpp:26:10:26:11 | b1 | file://:0:0:0:0 | ..(^)(..) | block of {function returning {void} with arguments (int,char)} | +| blocks.cpp:27:16:27:16 | y | file://:0:0:0:0 | int | int | +| blocks.cpp:27:24:27:24 | z | file://:0:0:0:0 | char | char | +| blocks.cpp:30:10:30:11 | b2 | file://:0:0:0:0 | ..(^)(..) | block of {function returning {void} with arguments (int,char)} | +| blocks.cpp:31:16:31:16 | y | file://:0:0:0:0 | int | int | +| blocks.cpp:31:24:31:24 | z | file://:0:0:0:0 | char | char | +| blocks.cpp:34:10:34:11 | b3 | file://:0:0:0:0 | ..(^)(..) | block of {function returning {char} with arguments (int,char)} | +| blocks.cpp:35:16:35:16 | y | file://:0:0:0:0 | int | int | +| blocks.cpp:35:24:35:24 | z | file://:0:0:0:0 | char | char | +| blocks.cpp:38:10:38:11 | b4 | file://:0:0:0:0 | ..(^)(..) | block of {function returning {char} with arguments (int,char)} | +| blocks.cpp:39:16:39:16 | y | file://:0:0:0:0 | int | int | +| blocks.cpp:39:24:39:24 | z | file://:0:0:0:0 | char | char | +| blocks.cpp:42:10:42:11 | b5 | file://:0:0:0:0 | ..(^)(..) | block of {function returning {char} with arguments (int,char)} | +| blocks.cpp:43:21:43:21 | y | file://:0:0:0:0 | int | int | +| blocks.cpp:43:29:43:29 | z | file://:0:0:0:0 | char | char | +| blocks.cpp:45:10:45:10 | c | file://:0:0:0:0 | char | char | +| blocks.cpp:55:7:55:10 | four | file://:0:0:0:0 | int | int | +| file://:0:0:0:0 | fp_offset | file://:0:0:0:0 | unsigned int | unsigned int | +| file://:0:0:0:0 | gp_offset | file://:0:0:0:0 | unsigned int | unsigned int | +| file://:0:0:0:0 | overflow_arg_area | file://:0:0:0:0 | void * | pointer to {void} | +| file://:0:0:0:0 | p#0 | file://:0:0:0:0 | Example && | rvalue reference to {struct Example} | +| file://:0:0:0:0 | p#0 | file://:0:0:0:0 | const Example & | reference to {const {struct Example}} | +| file://:0:0:0:0 | reg_save_area | file://:0:0:0:0 | void * | pointer to {void} | diff --git a/cpp/ql/test/library-tests/blocks/cpp/blocks.ql b/cpp/ql/test/library-tests/blocks/cpp/blocks.ql new file mode 100644 index 000000000000..d437dd74f97e --- /dev/null +++ b/cpp/ql/test/library-tests/blocks/cpp/blocks.ql @@ -0,0 +1,7 @@ +import cpp + +from Variable v +select v, + v.getType(), + v.getType().explain() + diff --git a/cpp/ql/test/library-tests/blocks/cpp/code_blocks.expected b/cpp/ql/test/library-tests/blocks/cpp/code_blocks.expected new file mode 100644 index 000000000000..aadbe3ffe65d --- /dev/null +++ b/cpp/ql/test/library-tests/blocks/cpp/code_blocks.expected @@ -0,0 +1,9 @@ +| blocks.cpp:17:11:17:33 | ^ { ... } | file://:0:0:0:0 | ..(^)(..) | block of {function returning {void} with arguments ()} | blocks.cpp:17:11:17:11 | | file://:0:0:0:0 | void | void | | +| blocks.cpp:20:11:20:28 | ^ { ... } | file://:0:0:0:0 | ..(^)(..) | block of {function returning {void} with arguments ()} | blocks.cpp:20:11:20:11 | | file://:0:0:0:0 | void | void | | +| blocks.cpp:23:11:23:21 | ^ { ... } | file://:0:0:0:0 | ..(^)(..) | block of {function returning {void} with arguments ()} | blocks.cpp:23:11:23:11 | | file://:0:0:0:0 | void | void | | +| blocks.cpp:27:8:27:37 | ^ { ... } | file://:0:0:0:0 | ..(^)(..) | block of {function returning {void} with arguments (int,char)} | blocks.cpp:27:11:27:11 | | file://:0:0:0:0 | void | void | y(int), z(char) | +| blocks.cpp:31:8:31:51 | ^ { ... } | file://:0:0:0:0 | ..(^)(..) | block of {function returning {void} with arguments (int,char)} | blocks.cpp:31:11:31:11 | | file://:0:0:0:0 | void | void | y(int), z(char) | +| blocks.cpp:35:8:35:49 | ^ { ... } | file://:0:0:0:0 | ..(^)(..) | block of {function returning {char} with arguments (int,char)} | blocks.cpp:35:11:35:11 | | file://:0:0:0:0 | char | char | y(int), z(char) | +| blocks.cpp:39:8:39:75 | ^ { ... } | file://:0:0:0:0 | ..(^)(..) | block of {function returning {char} with arguments (int,char)} | blocks.cpp:39:11:39:11 | | file://:0:0:0:0 | char | char | y(int), z(char) | +| blocks.cpp:43:8:43:54 | ^ { ... } | file://:0:0:0:0 | ..(^)(..) | block of {function returning {char} with arguments (int,char)} | blocks.cpp:43:11:43:11 | | file://:0:0:0:0 | char | char | y(int), z(char) | +| blocks.cpp:57:14:57:30 | ^ { ... } | file://:0:0:0:0 | ..(^)(..) | block of {function returning {int} with arguments ()} | blocks.cpp:57:14:57:14 | | file://:0:0:0:0 | int | int | | diff --git a/cpp/ql/test/library-tests/blocks/cpp/code_blocks.ql b/cpp/ql/test/library-tests/blocks/cpp/code_blocks.ql new file mode 100644 index 000000000000..842832fadb63 --- /dev/null +++ b/cpp/ql/test/library-tests/blocks/cpp/code_blocks.ql @@ -0,0 +1,25 @@ +import cpp + +predicate paramsString(Function f, int n, string res) { + if n = -1 then res = "" + else exists(string sep | + if n = 0 then sep = "" else sep = ", " + and + exists(string prefixRes | + paramsString(f, n - 1, prefixRes) + and + res = prefixRes + sep + f.getParameter(n) + + "(" + f.getParameter(n).getType() + ")")) +} + +from BlockExpr e, Function f, string params +where f = e.getFunction() + and paramsString(f, f.getNumberOfParameters() - 1, params) +select e, + e.getType() as te, + te.explain(), + e.getFunction(), + f.getType() as tf, + tf.explain(), + params + diff --git a/cpp/ql/test/library-tests/blocks/cpp/exprs.expected b/cpp/ql/test/library-tests/blocks/cpp/exprs.expected new file mode 100644 index 000000000000..f81990f2c4b5 --- /dev/null +++ b/cpp/ql/test/library-tests/blocks/cpp/exprs.expected @@ -0,0 +1,82 @@ +| blocks.cpp:6:59:6:60 | 10 | +| blocks.cpp:6:59:6:60 | Conversion of 10 | +| blocks.cpp:10:53:10:54 | 10 | +| blocks.cpp:10:53:10:54 | Conversion of 10 | +| blocks.cpp:16:5:16:38 | blockReturningVoidWithVoidArgument | +| blocks.cpp:16:5:17:33 | ... = ... | +| blocks.cpp:17:11:17:33 | ^ { ... } | +| blocks.cpp:17:25:17:25 | x | +| blocks.cpp:17:25:17:29 | ... = ... | +| blocks.cpp:17:29:17:29 | 1 | +| blocks.cpp:19:5:19:38 | blockReturningVoidWithVoidArgument | +| blocks.cpp:19:5:20:28 | ... = ... | +| blocks.cpp:20:11:20:28 | ^ { ... } | +| blocks.cpp:20:20:20:20 | x | +| blocks.cpp:20:20:20:24 | ... = ... | +| blocks.cpp:20:24:20:24 | 1 | +| blocks.cpp:22:5:22:38 | blockReturningVoidWithVoidArgument | +| blocks.cpp:22:5:23:21 | ... = ... | +| blocks.cpp:23:11:23:21 | ^ { ... } | +| blocks.cpp:23:13:23:13 | x | +| blocks.cpp:23:13:23:17 | ... = ... | +| blocks.cpp:23:17:23:17 | 1 | +| blocks.cpp:27:8:27:37 | ^ { ... } | +| blocks.cpp:27:29:27:29 | x | +| blocks.cpp:27:29:27:33 | ... = ... | +| blocks.cpp:27:33:27:33 | 1 | +| blocks.cpp:31:8:31:51 | ^ { ... } | +| blocks.cpp:31:29:31:29 | x | +| blocks.cpp:31:29:31:33 | ... = ... | +| blocks.cpp:31:33:31:33 | 1 | +| blocks.cpp:31:43:31:43 | 1 | +| blocks.cpp:31:43:31:43 | Conversion of 1 | +| blocks.cpp:35:8:35:49 | ^ { ... } | +| blocks.cpp:35:29:35:29 | x | +| blocks.cpp:35:29:35:33 | ... = ... | +| blocks.cpp:35:33:35:33 | 1 | +| blocks.cpp:35:43:35:45 | 99 | +| blocks.cpp:39:8:39:75 | ^ { ... } | +| blocks.cpp:39:29:39:29 | x | +| blocks.cpp:39:29:39:33 | ... = ... | +| blocks.cpp:39:33:39:33 | 1 | +| blocks.cpp:39:40:39:40 | 1 | +| blocks.cpp:39:40:39:40 | Conversion of 1 | +| blocks.cpp:39:52:39:54 | 99 | +| blocks.cpp:39:67:39:67 | 1 | +| blocks.cpp:39:67:39:67 | Conversion of 1 | +| blocks.cpp:43:8:43:54 | ^ { ... } | +| blocks.cpp:43:34:43:34 | x | +| blocks.cpp:43:34:43:38 | ... = ... | +| blocks.cpp:43:38:43:38 | 1 | +| blocks.cpp:43:48:43:50 | 99 | +| blocks.cpp:47:5:47:6 | b1 | +| blocks.cpp:47:5:47:14 | call to expression | +| blocks.cpp:47:8:47:8 | 5 | +| blocks.cpp:47:11:47:13 | 97 | +| blocks.cpp:48:5:48:6 | b2 | +| blocks.cpp:48:5:48:14 | call to expression | +| blocks.cpp:48:8:48:8 | 5 | +| blocks.cpp:48:11:48:13 | 97 | +| blocks.cpp:49:5:49:5 | c | +| blocks.cpp:49:5:49:18 | ... = ... | +| blocks.cpp:49:9:49:10 | b3 | +| blocks.cpp:49:9:49:18 | call to expression | +| blocks.cpp:49:12:49:12 | 5 | +| blocks.cpp:49:15:49:17 | 97 | +| blocks.cpp:50:5:50:5 | c | +| blocks.cpp:50:5:50:18 | ... = ... | +| blocks.cpp:50:9:50:10 | b4 | +| blocks.cpp:50:9:50:18 | call to expression | +| blocks.cpp:50:12:50:12 | 5 | +| blocks.cpp:50:15:50:17 | 97 | +| blocks.cpp:51:5:51:5 | c | +| blocks.cpp:51:5:51:18 | ... = ... | +| blocks.cpp:51:9:51:10 | b5 | +| blocks.cpp:51:9:51:18 | call to expression | +| blocks.cpp:51:12:51:12 | 5 | +| blocks.cpp:51:15:51:17 | 97 | +| blocks.cpp:57:12:57:30 | (...) | +| blocks.cpp:57:12:57:32 | call to expression | +| blocks.cpp:57:14:57:30 | ^ { ... } | +| blocks.cpp:57:23:57:26 | four | +| file://:0:0:0:0 | this | diff --git a/cpp/ql/test/library-tests/blocks/cpp/exprs.ql b/cpp/ql/test/library-tests/blocks/cpp/exprs.ql new file mode 100644 index 000000000000..1403942f5a1d --- /dev/null +++ b/cpp/ql/test/library-tests/blocks/cpp/exprs.ql @@ -0,0 +1,13 @@ +import cpp + +/* + * Plainer output for CStyleCast, that does not reference the (potentially platform + * dependent) type of the cast. + */ +class CStyleCastPlain extends CStyleCast { + override string toString() { result = "Conversion of " + getExpr().toString() } +} + +from Expr e +select e + diff --git a/cpp/ql/test/library-tests/blocks/cpp/options b/cpp/ql/test/library-tests/blocks/cpp/options new file mode 100644 index 000000000000..81574938c6ae --- /dev/null +++ b/cpp/ql/test/library-tests/blocks/cpp/options @@ -0,0 +1 @@ +extractor_flags: -fblocks diff --git a/cpp/ql/test/library-tests/blocks/deduplication/deduplication.expected b/cpp/ql/test/library-tests/blocks/deduplication/deduplication.expected new file mode 100644 index 000000000000..d782ab52217b --- /dev/null +++ b/cpp/ql/test/library-tests/blocks/deduplication/deduplication.expected @@ -0,0 +1 @@ +| 1 | 1 | diff --git a/cpp/ql/test/library-tests/blocks/deduplication/deduplication.ql b/cpp/ql/test/library-tests/blocks/deduplication/deduplication.ql new file mode 100644 index 000000000000..4bf5f2d5ce1e --- /dev/null +++ b/cpp/ql/test/library-tests/blocks/deduplication/deduplication.ql @@ -0,0 +1,5 @@ +import cpp + +select count(BlockExpr be), + count(ReturnStmt rs | rs.getExpr().getValue() = "0") + diff --git a/cpp/ql/test/library-tests/blocks/deduplication/functions.expected b/cpp/ql/test/library-tests/blocks/deduplication/functions.expected new file mode 100644 index 000000000000..9f4d90dd1f1f --- /dev/null +++ b/cpp/ql/test/library-tests/blocks/deduplication/functions.expected @@ -0,0 +1,3 @@ +| 1 | 1 | +| 2 | 1 | +| 9 | 2 | diff --git a/cpp/ql/test/library-tests/blocks/deduplication/functions.ql b/cpp/ql/test/library-tests/blocks/deduplication/functions.ql new file mode 100644 index 000000000000..f6bf2f1b2d3d --- /dev/null +++ b/cpp/ql/test/library-tests/blocks/deduplication/functions.ql @@ -0,0 +1,4 @@ +import cpp + +from int line +select line, strictcount(Function f | f.getLocation().getStartLine() = line) diff --git a/cpp/ql/test/library-tests/blocks/deduplication/options b/cpp/ql/test/library-tests/blocks/deduplication/options new file mode 100644 index 000000000000..81574938c6ae --- /dev/null +++ b/cpp/ql/test/library-tests/blocks/deduplication/options @@ -0,0 +1 @@ +extractor_flags: -fblocks diff --git a/cpp/ql/test/library-tests/blocks/deduplication/primary.c b/cpp/ql/test/library-tests/blocks/deduplication/primary.c new file mode 100644 index 000000000000..6cbe3f038e3e --- /dev/null +++ b/cpp/ql/test/library-tests/blocks/deduplication/primary.c @@ -0,0 +1,11 @@ +static int common() { + return ^{return 0;}(); +} + +#ifndef FN +#define FN primary +#endif + +int FN() { + return common(); +} diff --git a/cpp/ql/test/library-tests/blocks/deduplication/secondary.c b/cpp/ql/test/library-tests/blocks/deduplication/secondary.c new file mode 100644 index 000000000000..93a63dac7fd6 --- /dev/null +++ b/cpp/ql/test/library-tests/blocks/deduplication/secondary.c @@ -0,0 +1,2 @@ +#define FN secondary +#include "primary.c" diff --git a/cpp/ql/test/library-tests/bugs/odasa-2709/one.expected b/cpp/ql/test/library-tests/bugs/odasa-2709/one.expected new file mode 100644 index 000000000000..2a4f078a25fc --- /dev/null +++ b/cpp/ql/test/library-tests/bugs/odasa-2709/one.expected @@ -0,0 +1 @@ +| 1 | diff --git a/cpp/ql/test/library-tests/bugs/odasa-2709/one.ql b/cpp/ql/test/library-tests/bugs/odasa-2709/one.ql new file mode 100644 index 000000000000..55cd6933f857 --- /dev/null +++ b/cpp/ql/test/library-tests/bugs/odasa-2709/one.ql @@ -0,0 +1,2 @@ +// This test is for the dbcheck only +select 1 diff --git a/cpp/ql/test/library-tests/bugs/odasa-2709/test1.c b/cpp/ql/test/library-tests/bugs/odasa-2709/test1.c new file mode 100644 index 000000000000..d37f600da802 --- /dev/null +++ b/cpp/ql/test/library-tests/bugs/odasa-2709/test1.c @@ -0,0 +1,2 @@ +void foo(int x) { +} diff --git a/cpp/ql/test/library-tests/bugs/odasa-2709/test2.c b/cpp/ql/test/library-tests/bugs/odasa-2709/test2.c new file mode 100644 index 000000000000..61a08bddd298 --- /dev/null +++ b/cpp/ql/test/library-tests/bugs/odasa-2709/test2.c @@ -0,0 +1,3 @@ +int foo(float x) { + return (int) (x - 1); +} diff --git a/cpp/ql/test/library-tests/builtins/builtins/atomic.cpp b/cpp/ql/test/library-tests/builtins/builtins/atomic.cpp new file mode 100644 index 000000000000..9c0cae9da712 --- /dev/null +++ b/cpp/ql/test/library-tests/builtins/builtins/atomic.cpp @@ -0,0 +1,20 @@ +static int atomic_increment_i(int* ptr) +{ + return __atomic_fetch_add(ptr, 1, 0); +} + +static double atomic_increment_c(char* ptr) +{ + return __atomic_fetch_add(ptr, 1, 0); +} + +static int atomic_increment_i4(int* ptr) +{ + return __atomic_fetch_add_4(ptr, 1, 0); +} + +static char atomic_increment_c1(char* ptr) +{ + return __atomic_fetch_add_1(ptr, 1, 0); +} +// semmle-extractor-options: --gnu_version 40700 diff --git a/cpp/ql/test/library-tests/builtins/builtins/builtins.expected b/cpp/ql/test/library-tests/builtins/builtins/builtins.expected new file mode 100644 index 000000000000..046944ee078f --- /dev/null +++ b/cpp/ql/test/library-tests/builtins/builtins/builtins.expected @@ -0,0 +1,4 @@ +| choose_expr.c:3:13:3:42 | __builtin_choose_expr | +| varargs.c:7:5:7:32 | __builtin_va_start | +| varargs.c:9:13:9:37 | __builtin_va_arg | +| varargs.c:11:5:11:24 | __builtin_va_end | diff --git a/cpp/ql/test/library-tests/builtins/builtins/builtins.ql b/cpp/ql/test/library-tests/builtins/builtins/builtins.ql new file mode 100644 index 000000000000..6406da42c24e --- /dev/null +++ b/cpp/ql/test/library-tests/builtins/builtins/builtins.ql @@ -0,0 +1,5 @@ +import cpp + +from BuiltInOperation op +select op + diff --git a/cpp/ql/test/library-tests/builtins/builtins/calls.expected b/cpp/ql/test/library-tests/builtins/builtins/calls.expected new file mode 100644 index 000000000000..9aeae327fc95 --- /dev/null +++ b/cpp/ql/test/library-tests/builtins/builtins/calls.expected @@ -0,0 +1,4 @@ +| atomic.cpp:3:10:3:27 | call to __atomic_fetch_add_4 | +| atomic.cpp:8:10:8:27 | call to __atomic_fetch_add_1 | +| atomic.cpp:13:10:13:29 | call to __atomic_fetch_add_4 | +| atomic.cpp:18:10:18:29 | call to __atomic_fetch_add_1 | diff --git a/cpp/ql/test/library-tests/builtins/builtins/calls.ql b/cpp/ql/test/library-tests/builtins/builtins/calls.ql new file mode 100644 index 000000000000..13cf5c516288 --- /dev/null +++ b/cpp/ql/test/library-tests/builtins/builtins/calls.ql @@ -0,0 +1,5 @@ +import cpp + +from FunctionCall fc +where fc.getTarget() instanceof BuiltInFunction +select fc diff --git a/cpp/ql/test/library-tests/builtins/builtins/choose_expr.c b/cpp/ql/test/library-tests/builtins/builtins/choose_expr.c new file mode 100644 index 000000000000..6dd25eaa17ea --- /dev/null +++ b/cpp/ql/test/library-tests/builtins/builtins/choose_expr.c @@ -0,0 +1,5 @@ + +static void test_choose_expr(void) { + int i = __builtin_choose_expr(1, 5, 6); +} + diff --git a/cpp/ql/test/library-tests/builtins/builtins/varargs.c b/cpp/ql/test/library-tests/builtins/builtins/varargs.c new file mode 100644 index 000000000000..3c374459c675 --- /dev/null +++ b/cpp/ql/test/library-tests/builtins/builtins/varargs.c @@ -0,0 +1,13 @@ + +void f(int args, ...) +{ + __builtin_va_list ap; + int i, j; + + __builtin_va_start(ap, args); + for (i = 0; i < args; i++) { + j = __builtin_va_arg(ap, int); + } + __builtin_va_end(ap); +} + diff --git a/cpp/ql/test/library-tests/builtins/edg/edg.c b/cpp/ql/test/library-tests/builtins/edg/edg.c new file mode 100644 index 000000000000..d1a78435317c --- /dev/null +++ b/cpp/ql/test/library-tests/builtins/edg/edg.c @@ -0,0 +1,15 @@ + +struct mystruct { + int f1; + int f2; +}; + +typedef int size_t; + +#define edg_offsetof(t, memb) ((size_t)__INTADDR__(&(((t *)0)->memb))) + +void f(void) { + int i1 = __builtin_offsetof(struct mystruct,f2); + int i2 = edg_offsetof(struct mystruct,f2); +} + diff --git a/cpp/ql/test/library-tests/builtins/edg/expr.expected b/cpp/ql/test/library-tests/builtins/edg/expr.expected new file mode 100644 index 000000000000..ed5cffd449f2 --- /dev/null +++ b/cpp/ql/test/library-tests/builtins/edg/expr.expected @@ -0,0 +1,14 @@ +| edg.c:12:14:12:51 | (int)... | 0 | 0 | +| edg.c:12:14:12:51 | __offsetof | 1 | 1 | +| edg.c:12:49:12:50 | f2 | 0 | 0 | +| edg.c:13:14:13:45 | 0 | 0 | 0 | +| edg.c:13:14:13:45 | & ... | 0 | 0 | +| edg.c:13:14:13:45 | (...) | 0 | 0 | +| edg.c:13:14:13:45 | (...) | 0 | 0 | +| edg.c:13:14:13:45 | (...) | 0 | 0 | +| edg.c:13:14:13:45 | (mystruct *)... | 0 | 0 | +| edg.c:13:14:13:45 | (size_t)... | 0 | 0 | +| edg.c:13:43:13:44 | f2 | 0 | 0 | +| file://:0:0:0:0 | 0 | 0 | 0 | +| file://:0:0:0:0 | * ... | 0 | 0 | +| file://:0:0:0:0 | mystruct | 0 | 0 | diff --git a/cpp/ql/test/library-tests/builtins/edg/expr.ql b/cpp/ql/test/library-tests/builtins/edg/expr.ql new file mode 100644 index 000000000000..e11a9b9f39a8 --- /dev/null +++ b/cpp/ql/test/library-tests/builtins/edg/expr.ql @@ -0,0 +1,5 @@ +import cpp + +from Expr e +select e, count(e.getAPredecessor()), count(e.getASuccessor()) + diff --git a/cpp/ql/test/library-tests/builtins/edg/intaddr.expected b/cpp/ql/test/library-tests/builtins/edg/intaddr.expected new file mode 100644 index 000000000000..2a4f078a25fc --- /dev/null +++ b/cpp/ql/test/library-tests/builtins/edg/intaddr.expected @@ -0,0 +1 @@ +| 1 | diff --git a/cpp/ql/test/library-tests/builtins/edg/intaddr.ql b/cpp/ql/test/library-tests/builtins/edg/intaddr.ql new file mode 100644 index 000000000000..64effcd1ba6b --- /dev/null +++ b/cpp/ql/test/library-tests/builtins/edg/intaddr.ql @@ -0,0 +1,4 @@ +import cpp + +select count(@intaddrexpr e) + diff --git a/cpp/ql/test/library-tests/builtins/functions_file/builtins.txt b/cpp/ql/test/library-tests/builtins/functions_file/builtins.txt new file mode 100644 index 000000000000..8ba175f507f7 --- /dev/null +++ b/cpp/ql/test/library-tests/builtins/functions_file/builtins.txt @@ -0,0 +1,2 @@ +__builtin_foobar(i)i +__builtin_malloc(i,i,i,f*)f diff --git a/cpp/ql/test/library-tests/builtins/functions_file/isbuiltin.expected b/cpp/ql/test/library-tests/builtins/functions_file/isbuiltin.expected new file mode 100644 index 000000000000..00a331f6f44e --- /dev/null +++ b/cpp/ql/test/library-tests/builtins/functions_file/isbuiltin.expected @@ -0,0 +1,4 @@ +| file://:0:0:0:0 | __builtin_add_overflow | true | 0 | file://:0:0:0:0 | bool | +| file://:0:0:0:0 | __builtin_foobar | true | 0 | file://:0:0:0:0 | int | +| file://:0:0:0:0 | __builtin_malloc | true | 0 | file://:0:0:0:0 | float | +| test.c:1:6:1:6 | f | false | 3 | file://:0:0:0:0 | long | diff --git a/cpp/ql/test/library-tests/builtins/functions_file/isbuiltin.ql b/cpp/ql/test/library-tests/builtins/functions_file/isbuiltin.ql new file mode 100644 index 000000000000..f450abb84bd5 --- /dev/null +++ b/cpp/ql/test/library-tests/builtins/functions_file/isbuiltin.ql @@ -0,0 +1,5 @@ +import cpp + +from Function f, boolean isBuiltin +where if f instanceof BuiltInFunction then isBuiltin = true else isBuiltin = false +select f, isBuiltin, f.getNumberOfParameters(), f.getType() diff --git a/cpp/ql/test/library-tests/builtins/functions_file/test.c b/cpp/ql/test/library-tests/builtins/functions_file/test.c new file mode 100644 index 000000000000..a73061e4eaba --- /dev/null +++ b/cpp/ql/test/library-tests/builtins/functions_file/test.c @@ -0,0 +1,20 @@ +long f(int a, int b, int c) { + // A builtin from the builtin_functions_file. + int i1 = __builtin_foobar(a); + + // A builtin that's not in the file, but the extractor should handle, given the + // --gnu_version flag we pass in. + int i2; + __builtin_add_overflow(a, b, &i2); + + // A builtin that would normally be defined by the extractor with a type + // expecting it to be called like this: + //void* x = __builtin_malloc(a); + // But we override the type in the builtin_functions_file so it's called like + // this: + float f1, f2; + f1 = __builtin_malloc(a, b, c, &f2); + + return 42; +} +// semmle-extractor-options: --gnu_version 50100 --edg --builtin_functions_file --edg ${testdir}/builtins.txt diff --git a/cpp/ql/test/library-tests/builtins/type_traits/expr.expected b/cpp/ql/test/library-tests/builtins/type_traits/expr.expected new file mode 100644 index 000000000000..6166de5a80d7 --- /dev/null +++ b/cpp/ql/test/library-tests/builtins/type_traits/expr.expected @@ -0,0 +1,298 @@ +| file://:0:0:0:0 | 0 | | 0 | +| file://:0:0:0:0 | 1 | | 1 | +| file://:0:0:0:0 | 2 | | 2 | +| file://:0:0:0:0 | C1 | | | +| file://:0:0:0:0 | C1 | | | +| file://:0:0:0:0 | C1 | | | +| file://:0:0:0:0 | C1 | | | +| file://:0:0:0:0 | C1 | | | +| file://:0:0:0:0 | C1 | | | +| file://:0:0:0:0 | C1 | | | +| file://:0:0:0:0 | C1 | | | +| file://:0:0:0:0 | C1 | | | +| file://:0:0:0:0 | C1 | | | +| file://:0:0:0:0 | C1 | | | +| file://:0:0:0:0 | C1 | | | +| file://:0:0:0:0 | C2 | | | +| file://:0:0:0:0 | C2 | | | +| file://:0:0:0:0 | C2 | | | +| file://:0:0:0:0 | C2 | | | +| file://:0:0:0:0 | C2 | | | +| file://:0:0:0:0 | C2 | | | +| file://:0:0:0:0 | C3 | | | +| file://:0:0:0:0 | C3 | | | +| file://:0:0:0:0 | C3 | | | +| file://:0:0:0:0 | C3 | | | +| file://:0:0:0:0 | C3 | | | +| file://:0:0:0:0 | C3 | | | +| file://:0:0:0:0 | C4 | | | +| file://:0:0:0:0 | C4 | | | +| file://:0:0:0:0 | C5 | | | +| file://:0:0:0:0 | C5 | | | +| file://:0:0:0:0 | a_final_struct | | | +| file://:0:0:0:0 | a_struct | | | +| file://:0:0:0:0 | a_struct | | | +| file://:0:0:0:0 | a_struct | | | +| file://:0:0:0:0 | a_struct | | | +| file://:0:0:0:0 | a_struct | | | +| file://:0:0:0:0 | a_struct | | | +| file://:0:0:0:0 | a_struct | | | +| file://:0:0:0:0 | a_struct | | | +| file://:0:0:0:0 | a_struct | | | +| file://:0:0:0:0 | a_struct | | | +| file://:0:0:0:0 | a_struct | | | +| file://:0:0:0:0 | a_struct | | | +| file://:0:0:0:0 | a_struct | | | +| file://:0:0:0:0 | a_struct | | | +| file://:0:0:0:0 | a_struct | | | +| file://:0:0:0:0 | a_struct | | | +| file://:0:0:0:0 | a_struct_plus | | | +| file://:0:0:0:0 | a_union | | | +| file://:0:0:0:0 | a_union | | | +| file://:0:0:0:0 | a_union | | | +| file://:0:0:0:0 | abstract | | | +| file://:0:0:0:0 | abstract | | | +| file://:0:0:0:0 | abstract | | | +| file://:0:0:0:0 | an_enum | | | +| file://:0:0:0:0 | an_enum | | | +| file://:0:0:0:0 | an_enum | | | +| file://:0:0:0:0 | data | | | +| file://:0:0:0:0 | double | | | +| file://:0:0:0:0 | double | | | +| file://:0:0:0:0 | double | | | +| file://:0:0:0:0 | double | | | +| file://:0:0:0:0 | empty | | | +| file://:0:0:0:0 | empty | | | +| file://:0:0:0:0 | empty | | | +| file://:0:0:0:0 | empty | | | +| file://:0:0:0:0 | empty | | | +| file://:0:0:0:0 | empty | | | +| file://:0:0:0:0 | empty | | | +| file://:0:0:0:0 | empty | | | +| file://:0:0:0:0 | empty | | | +| file://:0:0:0:0 | empty | | | +| file://:0:0:0:0 | empty | | | +| file://:0:0:0:0 | empty | | | +| file://:0:0:0:0 | empty | | | +| file://:0:0:0:0 | empty | | | +| file://:0:0:0:0 | empty | | | +| file://:0:0:0:0 | empty | | | +| file://:0:0:0:0 | empty | | | +| file://:0:0:0:0 | empty | | | +| file://:0:0:0:0 | empty | | | +| file://:0:0:0:0 | empty | | | +| file://:0:0:0:0 | empty | | | +| file://:0:0:0:0 | empty | | | +| file://:0:0:0:0 | empty | | | +| file://:0:0:0:0 | empty | | | +| file://:0:0:0:0 | empty | | | +| file://:0:0:0:0 | empty | | | +| file://:0:0:0:0 | empty | | | +| file://:0:0:0:0 | empty | | | +| file://:0:0:0:0 | empty | | | +| file://:0:0:0:0 | empty | | | +| file://:0:0:0:0 | empty | | | +| file://:0:0:0:0 | empty | | | +| file://:0:0:0:0 | empty | | | +| file://:0:0:0:0 | empty | | | +| file://:0:0:0:0 | empty | | | +| file://:0:0:0:0 | float | | | +| file://:0:0:0:0 | float | | | +| file://:0:0:0:0 | float | | | +| file://:0:0:0:0 | float | | | +| file://:0:0:0:0 | float | | | +| file://:0:0:0:0 | float | | | +| file://:0:0:0:0 | has_assign | | | +| file://:0:0:0:0 | has_assign | | | +| file://:0:0:0:0 | has_assign | | | +| file://:0:0:0:0 | has_assign | | | +| file://:0:0:0:0 | has_assign | | | +| file://:0:0:0:0 | has_copy | | | +| file://:0:0:0:0 | has_copy | | | +| file://:0:0:0:0 | has_copy | | | +| file://:0:0:0:0 | has_copy | | | +| file://:0:0:0:0 | has_copy | | | +| file://:0:0:0:0 | has_copy | | | +| file://:0:0:0:0 | has_copy | | | +| file://:0:0:0:0 | has_copy | | | +| file://:0:0:0:0 | has_copy | | | +| file://:0:0:0:0 | has_copy | | | +| file://:0:0:0:0 | has_copy | | | +| file://:0:0:0:0 | has_noexcept_destructor | | | +| file://:0:0:0:0 | has_noexcept_destructor | | | +| file://:0:0:0:0 | has_nothrow_assign | | | +| file://:0:0:0:0 | has_nothrow_assign | | | +| file://:0:0:0:0 | has_nothrow_constructor | | | +| file://:0:0:0:0 | has_nothrow_constructor | | | +| file://:0:0:0:0 | has_nothrow_copy | | | +| file://:0:0:0:0 | has_user_destructor | | | +| file://:0:0:0:0 | has_user_destructor | | | +| file://:0:0:0:0 | has_user_destructor | | | +| file://:0:0:0:0 | has_user_destructor | | | +| file://:0:0:0:0 | has_user_destructor | | | +| file://:0:0:0:0 | has_user_destructor | | | +| file://:0:0:0:0 | has_user_destructor | | | +| file://:0:0:0:0 | has_virtual_destructor | | | +| file://:0:0:0:0 | has_virtual_destructor | | | +| file://:0:0:0:0 | int | | | +| file://:0:0:0:0 | int | | | +| file://:0:0:0:0 | int | | | +| file://:0:0:0:0 | int | | | +| file://:0:0:0:0 | int | | | +| file://:0:0:0:0 | int | | | +| file://:0:0:0:0 | int | | | +| file://:0:0:0:0 | int | | | +| file://:0:0:0:0 | int | | | +| file://:0:0:0:0 | int | | | +| file://:0:0:0:0 | int | | | +| file://:0:0:0:0 | int | | | +| file://:0:0:0:0 | int | | | +| file://:0:0:0:0 | int | | | +| file://:0:0:0:0 | int | | | +| file://:0:0:0:0 | int | | | +| file://:0:0:0:0 | int | | | +| file://:0:0:0:0 | int | | | +| file://:0:0:0:0 | int | | | +| file://:0:0:0:0 | int | | | +| file://:0:0:0:0 | int | | | +| file://:0:0:0:0 | int | | | +| file://:0:0:0:0 | long | | | +| file://:0:0:0:0 | long | | | +| file://:0:0:0:0 | long | | | +| file://:0:0:0:0 | long | | | +| file://:0:0:0:0 | method | | | +| file://:0:0:0:0 | method | | | +| file://:0:0:0:0 | method | | | +| file://:0:0:0:0 | method_data | | | +| file://:0:0:0:0 | no_has_nothrow_constructor | | | +| file://:0:0:0:0 | no_has_nothrow_constructor | | | +| ms.cpp:38:41:38:45 | 0 | | 0 | +| ms.cpp:88:27:88:45 | __has_assign | empty | 0 | +| ms.cpp:89:27:89:50 | __has_assign | has_assign | 1 | +| ms.cpp:91:25:91:41 | __has_copy | empty | 0 | +| ms.cpp:92:25:92:44 | __has_copy | has_copy | 1 | +| ms.cpp:94:35:94:61 | __has_nothrow_assign | empty | 1 | +| ms.cpp:95:35:95:66 | __has_nothrow_assign | has_assign | 0 | +| ms.cpp:96:35:96:74 | __has_nothrow_assign | has_nothrow_assign | 1 | +| ms.cpp:98:40:98:71 | __has_nothrow_constructor | empty | 1 | +| ms.cpp:99:40:99:92 | __has_nothrow_constructor | no_has_nothrow_constructor | 0 | +| ms.cpp:100:40:100:89 | __has_nothrow_constructor | has_nothrow_constructor | 1 | +| ms.cpp:102:33:102:57 | __has_nothrow_copy | empty | 1 | +| ms.cpp:103:33:103:60 | __has_nothrow_copy | has_copy | 0 | +| ms.cpp:104:33:104:68 | __has_nothrow_copy | has_nothrow_copy | 1 | +| ms.cpp:106:35:106:61 | __has_trivial_assign | empty | 1 | +| ms.cpp:107:35:107:66 | __has_trivial_assign | has_assign | 0 | +| ms.cpp:109:40:109:71 | __has_trivial_constructor | empty | 1 | +| ms.cpp:110:40:110:92 | __has_trivial_constructor | no_has_nothrow_constructor | 0 | +| ms.cpp:111:40:111:89 | __has_trivial_constructor | has_nothrow_constructor | 0 | +| ms.cpp:113:33:113:57 | __has_trivial_copy | empty | 1 | +| ms.cpp:114:33:114:60 | __has_trivial_copy | has_copy | 0 | +| ms.cpp:116:36:116:63 | __has_user_destructor | empty | 0 | +| ms.cpp:117:36:117:77 | __has_user_destructor | has_user_destructor | 1 | +| ms.cpp:118:36:118:80 | __has_user_destructor | has_virtual_destructor | 1 | +| ms.cpp:120:39:120:69 | __has_virtual_destructor | empty | 0 | +| ms.cpp:121:39:121:83 | __has_virtual_destructor | has_user_destructor | 0 | +| ms.cpp:122:39:122:86 | __has_virtual_destructor | has_virtual_destructor | 1 | +| ms.cpp:124:28:124:47 | __is_abstract | empty | 0 | +| ms.cpp:125:28:125:50 | __is_abstract | abstract | 1 | +| ms.cpp:126:28:126:48 | __is_abstract | method | 0 | +| ms.cpp:128:27:128:45 | __is_base_of | C1,C1 | 1 | +| ms.cpp:129:27:129:45 | __is_base_of | C1,C2 | 1 | +| ms.cpp:130:27:130:45 | __is_base_of | C1,C3 | 1 | +| ms.cpp:131:27:131:45 | __is_base_of | C1,C5 | 0 | +| ms.cpp:132:27:132:45 | __is_base_of | C3,C2 | 0 | +| ms.cpp:133:27:133:45 | __is_base_of | C3,C1 | 0 | +| ms.cpp:134:27:134:45 | __is_base_of | C2,C4 | 0 | +| ms.cpp:135:27:135:47 | __is_base_of | int,int | 0 | +| ms.cpp:136:27:136:48 | __is_base_of | int,long | 0 | +| ms.cpp:137:28:137:49 | __is_base_of | long,int | 0 | +| ms.cpp:138:28:138:51 | __is_base_of | int,double | 0 | +| ms.cpp:139:28:139:51 | __is_base_of | double,int | 0 | +| ms.cpp:141:25:141:41 | __is_class | empty | 1 | +| ms.cpp:142:25:142:43 | __is_class | an_enum | 0 | +| ms.cpp:143:25:143:43 | __is_class | a_union | 0 | +| ms.cpp:144:25:144:44 | __is_class | a_struct | 1 | +| ms.cpp:145:25:145:39 | __is_class | int | 0 | +| ms.cpp:147:34:147:59 | __is_convertible_to | C1,C1 | 1 | +| ms.cpp:148:34:148:59 | __is_convertible_to | C1,C2 | 0 | +| ms.cpp:149:34:149:59 | __is_convertible_to | C1,C3 | 0 | +| ms.cpp:150:34:150:59 | __is_convertible_to | C1,C5 | 0 | +| ms.cpp:151:34:151:59 | __is_convertible_to | C3,C2 | 0 | +| ms.cpp:152:34:152:59 | __is_convertible_to | C3,C1 | 0 | +| ms.cpp:153:34:153:59 | __is_convertible_to | C2,C4 | 0 | +| ms.cpp:154:34:154:61 | __is_convertible_to | int,int | 1 | +| ms.cpp:155:34:155:62 | __is_convertible_to | int,long | 1 | +| ms.cpp:156:35:156:63 | __is_convertible_to | long,int | 1 | +| ms.cpp:157:35:157:65 | __is_convertible_to | int,double | 1 | +| ms.cpp:158:35:158:65 | __is_convertible_to | double,int | 1 | +| ms.cpp:160:25:160:41 | __is_empty | empty | 1 | +| ms.cpp:161:25:161:42 | __is_empty | method | 1 | +| ms.cpp:162:25:162:40 | __is_empty | data | 0 | +| ms.cpp:163:25:163:47 | __is_empty | method_data | 0 | +| ms.cpp:164:25:164:44 | __is_empty | abstract | 0 | +| ms.cpp:166:24:166:39 | __is_enum | empty | 0 | +| ms.cpp:167:24:167:41 | __is_enum | an_enum | 1 | +| ms.cpp:168:24:168:41 | __is_enum | a_union | 0 | +| ms.cpp:170:31:170:53 | __is_polymorphic | empty | 0 | +| ms.cpp:171:31:171:56 | __is_polymorphic | abstract | 1 | +| ms.cpp:172:31:172:54 | __is_polymorphic | method | 0 | +| ms.cpp:174:25:174:41 | __is_union | empty | 0 | +| ms.cpp:175:25:175:43 | __is_union | an_enum | 0 | +| ms.cpp:176:25:176:43 | __is_union | a_union | 1 | +| ms.cpp:177:25:177:44 | __is_union | a_struct | 0 | +| ms.cpp:178:25:178:39 | __is_union | int | 0 | +| ms.cpp:180:42:180:79 | __is_trivially_constructible | a_struct | 1 | +| ms.cpp:181:42:181:76 | __is_trivially_constructible | empty | 1 | +| ms.cpp:182:42:182:79 | __is_trivially_constructible | has_copy | 0 | +| ms.cpp:184:31:184:57 | __is_destructible | a_struct | 1 | +| ms.cpp:185:31:185:54 | __is_destructible | empty | 1 | +| ms.cpp:186:31:186:57 | __is_destructible | has_copy | 1 | +| ms.cpp:187:31:187:68 | __is_destructible | has_user_destructor | 0 | +| ms.cpp:189:39:189:73 | __is_nothrow_destructible | a_struct | 1 | +| ms.cpp:190:39:190:70 | __is_nothrow_destructible | empty | 1 | +| ms.cpp:191:39:191:73 | __is_nothrow_destructible | has_copy | 1 | +| ms.cpp:192:39:192:84 | __is_nothrow_destructible | has_user_destructor | 0 | +| ms.cpp:193:39:193:88 | __is_nothrow_destructible | has_noexcept_destructor | 0 | +| ms.cpp:195:41:195:77 | __is_trivially_destructible | a_struct | 1 | +| ms.cpp:196:41:196:74 | __is_trivially_destructible | empty | 1 | +| ms.cpp:197:41:197:77 | __is_trivially_destructible | has_copy | 1 | +| ms.cpp:198:41:198:88 | __is_trivially_destructible | has_user_destructor | 0 | +| ms.cpp:199:41:199:92 | __is_trivially_destructible | has_noexcept_destructor | 0 | +| ms.cpp:201:39:201:82 | __is_trivially_assignable | a_struct,a_struct | 1 | +| ms.cpp:202:39:202:79 | __is_trivially_assignable | a_struct,empty | 0 | +| ms.cpp:203:39:203:77 | __is_trivially_assignable | a_struct,int | 0 | +| ms.cpp:205:37:205:78 | __is_nothrow_assignable | a_struct,a_struct | 1 | +| ms.cpp:206:37:206:75 | __is_nothrow_assignable | a_struct,empty | 0 | +| ms.cpp:207:37:207:73 | __is_nothrow_assignable | a_struct,int | 0 | +| ms.cpp:209:34:209:63 | __is_standard_layout | a_struct | 1 | +| ms.cpp:210:34:210:68 | __is_standard_layout | a_struct_plus | 0 | +| ms.cpp:212:37:212:66 | __is_trivially_copyable | empty | 1 | +| ms.cpp:213:37:213:69 | __is_trivially_copyable | has_copy | 0 | +| ms.cpp:215:31:215:54 | __is_literal_type | empty | 1 | +| ms.cpp:216:31:216:68 | __is_literal_type | has_user_destructor | 0 | +| ms.cpp:218:44:218:80 | __has_trivial_move_constructor | empty | 1 | +| ms.cpp:219:44:219:83 | __has_trivial_move_constructor | has_copy | 0 | +| ms.cpp:220:44:220:94 | __has_trivial_move_constructor | has_user_destructor | 1 | +| ms.cpp:222:39:222:70 | __has_trivial_move_assign | empty | 1 | +| ms.cpp:223:39:223:73 | __has_trivial_move_assign | has_copy | 1 | +| ms.cpp:224:39:224:75 | __has_trivial_move_assign | has_assign | 0 | +| ms.cpp:226:39:226:70 | __has_nothrow_move_assign | empty | 1 | +| ms.cpp:227:39:227:73 | __has_nothrow_move_assign | has_copy | 1 | +| ms.cpp:228:39:228:75 | __has_nothrow_move_assign | has_assign | 0 | +| ms.cpp:229:39:229:83 | __has_nothrow_move_assign | has_nothrow_assign | 1 | +| ms.cpp:231:32:231:54 | __is_constructible | int | 1 | +| ms.cpp:232:32:232:60 | __is_constructible | int,float | 1 | +| ms.cpp:233:32:233:66 | __is_constructible | int,float,float | 0 | +| ms.cpp:235:40:235:70 | __is_nothrow_constructible | int | 1 | +| ms.cpp:236:40:236:76 | __is_nothrow_constructible | int,float | 1 | +| ms.cpp:237:40:237:82 | __is_nothrow_constructible | int,float,float | 0 | +| ms.cpp:239:29:239:50 | __has_finalizer | empty | 0 | +| ms.cpp:241:27:241:46 | __is_delegate | empty | 0 | +| ms.cpp:243:34:243:60 | __is_interface_class | empty | 0 | +| ms.cpp:245:28:245:48 | __is_ref_array | empty | 0 | +| ms.cpp:247:28:247:48 | __is_ref_class | empty | 0 | +| ms.cpp:249:25:249:42 | __is_sealed | empty | 0 | +| ms.cpp:251:37:251:66 | __is_simple_value_class | empty | 0 | +| ms.cpp:253:30:253:52 | __is_value_class | empty | 0 | +| ms.cpp:255:24:255:43 | __is_final | a_struct | 0 | +| ms.cpp:256:24:256:49 | __is_final | a_final_struct | 1 | diff --git a/cpp/ql/test/library-tests/builtins/type_traits/expr.ql b/cpp/ql/test/library-tests/builtins/type_traits/expr.ql new file mode 100644 index 000000000000..6a84fd6b619b --- /dev/null +++ b/cpp/ql/test/library-tests/builtins/type_traits/expr.ql @@ -0,0 +1,15 @@ +import cpp + +string childrenFrom(Expr e, int i) { + (if i = 0 + then result = e.getChild(i).toString() + childrenFrom(e, i + 1) + else result = "," + e.getChild(i).toString() + childrenFrom(e, i + 1)) + or + (i = e.getNumChild() and result = "") +} + +from Expr e, string value +where if exists(e.getValue()) then value = e.getValue().toString() + else value = "" +select e, childrenFrom(e, 0), value + diff --git a/cpp/ql/test/library-tests/builtins/type_traits/ms.cpp b/cpp/ql/test/library-tests/builtins/type_traits/ms.cpp new file mode 100644 index 000000000000..742f95faf07a --- /dev/null +++ b/cpp/ql/test/library-tests/builtins/type_traits/ms.cpp @@ -0,0 +1,258 @@ + +class empty { +}; + +class has_assign { + has_assign & operator=(has_assign x); +}; + +class has_copy { + has_copy(has_copy &x); +}; + +class has_nothrow_assign { + has_nothrow_assign & operator=(has_assign x) throw(); +}; + +class no_has_nothrow_constructor { + no_has_nothrow_constructor(); +}; + +class has_nothrow_constructor { + has_nothrow_constructor() throw(); +}; + +class has_nothrow_copy { + has_nothrow_copy(has_nothrow_copy &x) throw(); +}; + +class has_user_destructor { + ~has_user_destructor(); +}; + +class has_virtual_destructor { + virtual ~has_virtual_destructor(); +}; + +class has_noexcept_destructor { + ~has_noexcept_destructor() noexcept(false); +}; + +class abstract { + virtual void someFun() = 0; +}; + +class C1 {}; +class C2 : C1 {}; +class C3 : C2 {}; +class C4 : C1 {}; +class C5 {}; + +class method { + void someFun(); +}; + +class data { + int i; +}; + +class method_data { + void someFun(); + int i; +}; + +enum an_enum { + a, b, c +}; + +union a_union { + int i; + double d; +}; + +struct a_struct { + int i; + double d; +}; + +struct a_struct_plus : a_struct { + int j; +}; + +struct a_final_struct final { + int i; + double d; +}; + +void f(void) { + bool b_has_assign_1 = __has_assign(empty); + bool b_has_assign_2 = __has_assign(has_assign); + + bool b_has_copy_1 = __has_copy(empty); + bool b_has_copy_2 = __has_copy(has_copy); + + bool b_has_nothrow_assign_1 = __has_nothrow_assign(empty); + bool b_has_nothrow_assign_2 = __has_nothrow_assign(has_assign); + bool b_has_nothrow_assign_3 = __has_nothrow_assign(has_nothrow_assign); + + bool b_has_nothrow_constructor_1 = __has_nothrow_constructor(empty); + bool b_has_nothrow_constructor_2 = __has_nothrow_constructor(no_has_nothrow_constructor); + bool b_has_nothrow_constructor_3 = __has_nothrow_constructor(has_nothrow_constructor); + + bool b_has_nothrow_copy_1 = __has_nothrow_copy(empty); + bool b_has_nothrow_copy_2 = __has_nothrow_copy(has_copy); + bool b_has_nothrow_copy_3 = __has_nothrow_copy(has_nothrow_copy); + + bool b_has_trivial_assign_1 = __has_trivial_assign(empty); + bool b_has_trivial_assign_2 = __has_trivial_assign(has_assign); + + bool b_has_trivial_constructor_1 = __has_trivial_constructor(empty); + bool b_has_trivial_constructor_2 = __has_trivial_constructor(no_has_nothrow_constructor); + bool b_has_trivial_constructor_3 = __has_trivial_constructor(has_nothrow_constructor); + + bool b_has_trivial_copy_1 = __has_trivial_copy(empty); + bool b_has_trivial_copy_2 = __has_trivial_copy(has_copy); + + bool b_has_user_destructor_1 = __has_user_destructor(empty); + bool b_has_user_destructor_2 = __has_user_destructor(has_user_destructor); + bool b_has_user_destructor_3 = __has_user_destructor(has_virtual_destructor); + + bool b_has_virtual_destructor_1 = __has_virtual_destructor(empty); + bool b_has_virtual_destructor_2 = __has_virtual_destructor(has_user_destructor); + bool b_has_virtual_destructor_3 = __has_virtual_destructor(has_virtual_destructor); + + bool b_is_abstract_1 = __is_abstract(empty); + bool b_is_abstract_2 = __is_abstract(abstract); + bool b_is_abstract_3 = __is_abstract(method); + + bool b_is_base_of_1 = __is_base_of(C1,C1); + bool b_is_base_of_2 = __is_base_of(C1,C2); + bool b_is_base_of_3 = __is_base_of(C1,C3); + bool b_is_base_of_4 = __is_base_of(C1,C5); + bool b_is_base_of_5 = __is_base_of(C3,C2); + bool b_is_base_of_6 = __is_base_of(C3,C1); + bool b_is_base_of_7 = __is_base_of(C2,C4); + bool b_is_base_of_8 = __is_base_of(int,int); + bool b_is_base_of_9 = __is_base_of(int,long); + bool b_is_base_of_10 = __is_base_of(long,int); + bool b_is_base_of_11 = __is_base_of(int,double); + bool b_is_base_of_12 = __is_base_of(double,int); + + bool b_is_class_1 = __is_class(empty); + bool b_is_class_2 = __is_class(an_enum); + bool b_is_class_3 = __is_class(a_union); + bool b_is_class_4 = __is_class(a_struct); + bool b_is_class_5 = __is_class(int); + + bool b_is_convertible_to_1 = __is_convertible_to(C1,C1); + bool b_is_convertible_to_2 = __is_convertible_to(C1,C2); + bool b_is_convertible_to_3 = __is_convertible_to(C1,C3); + bool b_is_convertible_to_4 = __is_convertible_to(C1,C5); + bool b_is_convertible_to_5 = __is_convertible_to(C3,C2); + bool b_is_convertible_to_6 = __is_convertible_to(C3,C1); + bool b_is_convertible_to_7 = __is_convertible_to(C2,C4); + bool b_is_convertible_to_8 = __is_convertible_to(int,int); + bool b_is_convertible_to_9 = __is_convertible_to(int,long); + bool b_is_convertible_to_10 = __is_convertible_to(long,int); + bool b_is_convertible_to_11 = __is_convertible_to(int,double); + bool b_is_convertible_to_12 = __is_convertible_to(double,int); + + bool b_is_empty_1 = __is_empty(empty); + bool b_is_empty_2 = __is_empty(method); + bool b_is_empty_3 = __is_empty(data); + bool b_is_empty_4 = __is_empty(method_data); + bool b_is_empty_5 = __is_empty(abstract); + + bool b_is_enum_1 = __is_enum(empty); + bool b_is_enum_2 = __is_enum(an_enum); + bool b_is_enum_3 = __is_enum(a_union); + + bool b_is_polymorphic_1 = __is_polymorphic(empty); + bool b_is_polymorphic_2 = __is_polymorphic(abstract); + bool b_is_polymorphic_3 = __is_polymorphic(method); + + bool b_is_union_1 = __is_union(empty); + bool b_is_union_2 = __is_union(an_enum); + bool b_is_union_3 = __is_union(a_union); + bool b_is_union_4 = __is_union(a_struct); + bool b_is_union_5 = __is_union(int); + + bool b_is_trivially_constructible1 = __is_trivially_constructible(a_struct); + bool b_is_trivially_constructible2 = __is_trivially_constructible(empty); + bool b_is_trivially_constructible3 = __is_trivially_constructible(has_copy); + + bool b_is_destructible1 = __is_destructible(a_struct); + bool b_is_destructible2 = __is_destructible(empty); + bool b_is_destructible3 = __is_destructible(has_copy); + bool b_is_destructible4 = __is_destructible(has_user_destructor); + + bool b_is_nothrow_destructible1 = __is_nothrow_destructible(a_struct); + bool b_is_nothrow_destructible2 = __is_nothrow_destructible(empty); + bool b_is_nothrow_destructible3 = __is_nothrow_destructible(has_copy); + bool b_is_nothrow_destructible4 = __is_nothrow_destructible(has_user_destructor); + bool b_is_nothrow_destructible5 = __is_nothrow_destructible(has_noexcept_destructor); + + bool b_is_trivially_destructible1 = __is_trivially_destructible(a_struct); + bool b_is_trivially_destructible2 = __is_trivially_destructible(empty); + bool b_is_trivially_destructible3 = __is_trivially_destructible(has_copy); + bool b_is_trivially_destructible4 = __is_trivially_destructible(has_user_destructor); + bool b_is_trivially_destructible5 = __is_trivially_destructible(has_noexcept_destructor); + + bool b_is_trivially_assignable1 = __is_trivially_assignable(a_struct,a_struct); + bool b_is_trivially_assignable2 = __is_trivially_assignable(a_struct,empty); + bool b_is_trivially_assignable3 = __is_trivially_assignable(a_struct,int); + + bool b_is_nothrow_assignable1 = __is_nothrow_assignable(a_struct,a_struct); + bool b_is_nothrow_assignable2 = __is_nothrow_assignable(a_struct,empty); + bool b_is_nothrow_assignable3 = __is_nothrow_assignable(a_struct,int); + + bool b_is_standard_layout1 = __is_standard_layout(a_struct); + bool b_is_standard_layout2 = __is_standard_layout(a_struct_plus); + + bool b_is_trivially_copyable1 = __is_trivially_copyable(empty); + bool b_is_trivially_copyable2 = __is_trivially_copyable(has_copy); + + bool b_is_literal_type1 = __is_literal_type(empty); + bool b_is_literal_type2 = __is_literal_type(has_user_destructor); + + bool b_has_trivial_move_constructor1 = __has_trivial_move_constructor(empty); + bool b_has_trivial_move_constructor2 = __has_trivial_move_constructor(has_copy); + bool b_has_trivial_move_constructor3 = __has_trivial_move_constructor(has_user_destructor); + + bool b_has_trivial_move_assign1 = __has_trivial_move_assign(empty); + bool b_has_trivial_move_assign2 = __has_trivial_move_assign(has_copy); + bool b_has_trivial_move_assign3 = __has_trivial_move_assign(has_assign); + + bool b_has_nothrow_move_assign1 = __has_nothrow_move_assign(empty); + bool b_has_nothrow_move_assign2 = __has_nothrow_move_assign(has_copy); + bool b_has_nothrow_move_assign3 = __has_nothrow_move_assign(has_assign); + bool b_has_nothrow_move_assign4 = __has_nothrow_move_assign(has_nothrow_assign); + + bool b_is_constructible1 = __is_constructible(int); + bool b_is_constructible2 = __is_constructible(int,float); + bool b_is_constructible3 = __is_constructible(int,float,float); + + bool b_is_nothrow_constructible1 = __is_nothrow_constructible(int); + bool b_is_nothrow_constructible2 = __is_nothrow_constructible(int,float); + bool b_is_nothrow_constructible3 = __is_nothrow_constructible(int,float,float); + + bool b_has_finalizer1 = __has_finalizer(empty); + + bool b_is_delegate1 = __is_delegate(empty); + + bool b_is_interface_class1 = __is_interface_class(empty); + + bool b_is_ref_array1 = __is_ref_array(empty); + + bool b_is_ref_class1 = __is_ref_class(empty); + + bool b_is_sealed1 = __is_sealed(empty); + + bool b_is_simple_value_class1 = __is_simple_value_class(empty); + + bool b_is_value_class1 = __is_value_class(empty); + + bool b_is_final1 = __is_final(a_struct); + bool b_is_final2 = __is_final(a_final_struct); +} + diff --git a/cpp/ql/test/library-tests/builtins/type_traits/options b/cpp/ql/test/library-tests/builtins/type_traits/options new file mode 100644 index 000000000000..5b4a99fbe137 --- /dev/null +++ b/cpp/ql/test/library-tests/builtins/type_traits/options @@ -0,0 +1 @@ +extractor_flags: --microsoft --microsoft_version 1600 diff --git a/cpp/ql/test/library-tests/builtins/types/types.c b/cpp/ql/test/library-tests/builtins/types/types.c new file mode 100644 index 000000000000..c4246f2273d6 --- /dev/null +++ b/cpp/ql/test/library-tests/builtins/types/types.c @@ -0,0 +1,5 @@ + +_Decimal32 d32; +_Decimal64 d64; +_Decimal128 d128; + diff --git a/cpp/ql/test/library-tests/builtins/types/types.expected b/cpp/ql/test/library-tests/builtins/types/types.expected new file mode 100644 index 000000000000..deaca8e4c280 --- /dev/null +++ b/cpp/ql/test/library-tests/builtins/types/types.expected @@ -0,0 +1,7 @@ +| file://:0:0:0:0 | fp_offset | file://:0:0:0:0 | unsigned int | +| file://:0:0:0:0 | gp_offset | file://:0:0:0:0 | unsigned int | +| file://:0:0:0:0 | overflow_arg_area | file://:0:0:0:0 | void * | +| file://:0:0:0:0 | reg_save_area | file://:0:0:0:0 | void * | +| types.c:2:12:2:14 | d32 | file://:0:0:0:0 | _Decimal32 | +| types.c:3:12:3:14 | d64 | file://:0:0:0:0 | _Decimal64 | +| types.c:4:13:4:16 | d128 | file://:0:0:0:0 | _Decimal128 | diff --git a/cpp/ql/test/library-tests/builtins/types/types.ql b/cpp/ql/test/library-tests/builtins/types/types.ql new file mode 100644 index 000000000000..ff3df9a56535 --- /dev/null +++ b/cpp/ql/test/library-tests/builtins/types/types.ql @@ -0,0 +1,6 @@ +import cpp + +from Variable v, Type t +where t = v.getType() +select v, t + diff --git a/cpp/ql/test/library-tests/c++_exceptions/ODASA-5692.cpp b/cpp/ql/test/library-tests/c++_exceptions/ODASA-5692.cpp new file mode 100644 index 000000000000..82b5fe2af7be --- /dev/null +++ b/cpp/ql/test/library-tests/c++_exceptions/ODASA-5692.cpp @@ -0,0 +1,23 @@ +class Error { +public: + Error () {} + virtual ~Error() {} +}; + +template +int fun2() { + try { + } + catch (Error&) { + throw; + } + catch (...) { + return 1; + } + return 0; +} + +void run_fun2(void) { + fun2(); +} + diff --git a/cpp/ql/test/library-tests/c++_exceptions/anycatch.expected b/cpp/ql/test/library-tests/c++_exceptions/anycatch.expected new file mode 100644 index 000000000000..063d8298c5e7 --- /dev/null +++ b/cpp/ql/test/library-tests/c++_exceptions/anycatch.expected @@ -0,0 +1,3 @@ +| ODASA-5692.cpp:11:18:13:3 | { ... } | +| ODASA-5692.cpp:14:15:16:3 | { ... } | +| ODASA-5692.cpp:14:15:16:3 | { ... } | diff --git a/cpp/ql/test/library-tests/c++_exceptions/anycatch.ql b/cpp/ql/test/library-tests/c++_exceptions/anycatch.ql new file mode 100644 index 000000000000..428a2ef645f1 --- /dev/null +++ b/cpp/ql/test/library-tests/c++_exceptions/anycatch.ql @@ -0,0 +1,4 @@ +import cpp + +from CatchAnyBlock b +select b diff --git a/cpp/ql/test/library-tests/c++_exceptions/catchparams.expected b/cpp/ql/test/library-tests/c++_exceptions/catchparams.expected new file mode 100644 index 000000000000..f71c0a53dddc --- /dev/null +++ b/cpp/ql/test/library-tests/c++_exceptions/catchparams.expected @@ -0,0 +1,4 @@ +| ODASA-5692.cpp:11:10:11:14 | p#0 | ODASA-5692.cpp:11:18:13:3 | { ... } | +| exceptions.cpp:28:20:28:20 | e | exceptions.cpp:28:23:30:9 | { ... } | +| exceptions.cpp:32:16:32:16 | e | exceptions.cpp:32:19:34:5 | { ... } | +| exceptions.cpp:35:16:35:16 | e | exceptions.cpp:35:19:37:5 | { ... } | diff --git a/cpp/ql/test/library-tests/c++_exceptions/catchparams.ql b/cpp/ql/test/library-tests/c++_exceptions/catchparams.ql new file mode 100644 index 000000000000..66a0c146c9cf --- /dev/null +++ b/cpp/ql/test/library-tests/c++_exceptions/catchparams.ql @@ -0,0 +1,4 @@ +import cpp + +from Parameter p +select p, p.getCatchBlock() diff --git a/cpp/ql/test/library-tests/c++_exceptions/exceptions.cpp b/cpp/ql/test/library-tests/c++_exceptions/exceptions.cpp new file mode 100644 index 000000000000..120ae2a86c1c --- /dev/null +++ b/cpp/ql/test/library-tests/c++_exceptions/exceptions.cpp @@ -0,0 +1,40 @@ + +void f1(void) throw (int); +void f2(void) throw (); +void f3(void); +void f4(void) { + return; +} +void f5(void) { + throw 3; +} +void g(void); +void h(void); +void i(void); +void j(void); +void k(void); + +void fun(void) { + try { + try { + f1(); + f2(); + f3(); + f4(); + f5(); + throw 5; + g(); + } + catch (int e) { + h(); + } + } + catch (int e) { + i(); + } + catch (int e) { + j(); + } + k(); + return; +} diff --git a/cpp/ql/test/library-tests/c++_exceptions/graphable.expected b/cpp/ql/test/library-tests/c++_exceptions/graphable.expected new file mode 100644 index 000000000000..a8d075b33827 --- /dev/null +++ b/cpp/ql/test/library-tests/c++_exceptions/graphable.expected @@ -0,0 +1,351 @@ +| C::C | false | 390 | 390 | C | +| C::C | false | 393 | 393 | C | +| C::operator= | false | 388 | 388 | operator= | +| C::~C | false | 392 | 392 | ~C | +| Error::Error | false | 131 | 131 | Error | +| Error::Error | false | 135 | 135 | return ... | +| Error::Error | false | 137 | 137 | { ... } | +| Error::Error | false | 152 | 152 | Error | +| Error::Error | true | 135 | 131 | | +| Error::Error | true | 137 | 135 | | +| Error::operator= | false | 150 | 150 | operator= | +| Error::~Error | false | 140 | 140 | ~Error | +| Error::~Error | false | 144 | 144 | return ... | +| Error::~Error | false | 146 | 146 | { ... } | +| Error::~Error | true | 144 | 140 | | +| Error::~Error | true | 146 | 144 | | +| __va_list_tag::operator= | false | 123 | 123 | operator= | +| __va_list_tag::operator= | false | 127 | 127 | operator= | +| f | false | 407 | 407 | f | +| f | false | 413 | 413 | call to C | +| f | false | 417 | 417 | 101 | +| f | false | 418 | 418 | initializer for c101 | +| f | false | 423 | 423 | call to C | +| f | false | 427 | 427 | 105 | +| f | false | 428 | 428 | initializer for c105 | +| f | false | 433 | 433 | call to C | +| f | false | 437 | 437 | 109 | +| f | false | 438 | 438 | initializer for c109 | +| f | false | 443 | 443 | call to C | +| f | false | 447 | 447 | 102 | +| f | false | 448 | 448 | initializer for c102 | +| f | false | 453 | 453 | call to C | +| f | false | 457 | 457 | 103 | +| f | false | 458 | 458 | initializer for c103 | +| f | false | 463 | 463 | call to C | +| f | false | 467 | 467 | 104 | +| f | false | 468 | 468 | initializer for c104 | +| f | false | 473 | 473 | call to C | +| f | false | 477 | 477 | 106 | +| f | false | 478 | 478 | initializer for c106 | +| f | false | 483 | 483 | call to C | +| f | false | 487 | 487 | 107 | +| f | false | 488 | 488 | initializer for c107 | +| f | false | 493 | 493 | call to C | +| f | false | 497 | 497 | 108 | +| f | false | 498 | 498 | initializer for c108 | +| f | false | 502 | 502 | declaration | +| f | false | 504 | 504 | declaration | +| f | false | 506 | 506 | b1 | +| f | false | 508 | 508 | (bool)... | +| f | false | 512 | 512 | 1 | +| f | false | 513 | 513 | throw ... | +| f | false | 515 | 515 | ExprStmt | +| f | false | 517 | 517 | { ... } | +| f | false | 519 | 519 | if (...) ... | +| f | false | 521 | 521 | declaration | +| f | false | 523 | 523 | { ... } | +| f | false | 529 | 529 | 1 | +| f | false | 530 | 530 | declaration | +| f | false | 532 | 532 | { ... } | +| f | false | 534 | 534 | __try { ... } __except( ... ) { ... } | +| f | false | 536 | 536 | declaration | +| f | false | 538 | 538 | declaration | +| f | false | 540 | 540 | b2 | +| f | false | 542 | 542 | (bool)... | +| f | false | 546 | 546 | 2 | +| f | false | 547 | 547 | throw ... | +| f | false | 549 | 549 | ExprStmt | +| f | false | 551 | 551 | { ... } | +| f | false | 553 | 553 | if (...) ... | +| f | false | 555 | 555 | declaration | +| f | false | 557 | 557 | { ... } | +| f | false | 559 | 559 | declaration | +| f | false | 561 | 561 | { ... } | +| f | false | 563 | 563 | __try { ... } __finally { ... } | +| f | false | 565 | 565 | declaration | +| f | false | 567 | 567 | return ... | +| f | false | 569 | 569 | { ... } | +| f | false | 571 | 571 | c101 | +| f | false | 573 | 573 | call to c101.~C | +| f | false | 574 | 574 | c105 | +| f | false | 575 | 575 | call to c105.~C | +| f | false | 576 | 576 | c109 | +| f | false | 577 | 577 | call to c109.~C | +| f | false | 578 | 578 | c101 | +| f | false | 579 | 579 | call to c101.~C | +| f | false | 580 | 580 | c105 | +| f | false | 581 | 581 | call to c105.~C | +| f | false | 582 | 582 | c108 | +| f | false | 584 | 584 | call to c108.~C | +| f | false | 585 | 585 | c106 | +| f | false | 587 | 587 | call to c106.~C | +| f | false | 588 | 588 | c107 | +| f | false | 589 | 589 | call to c107.~C | +| f | false | 590 | 590 | c106 | +| f | false | 591 | 591 | call to c106.~C | +| f | false | 592 | 592 | c104 | +| f | false | 594 | 594 | call to c104.~C | +| f | false | 595 | 595 | c102 | +| f | false | 597 | 597 | call to c102.~C | +| f | false | 598 | 598 | c103 | +| f | false | 599 | 599 | call to c103.~C | +| f | false | 600 | 600 | c102 | +| f | false | 601 | 601 | call to c102.~C | +| f | true | 413 | 534 | | +| f | true | 417 | 413 | | +| f | true | 418 | 417 | | +| f | true | 423 | 563 | | +| f | true | 427 | 423 | | +| f | true | 428 | 427 | | +| f | true | 433 | 567 | | +| f | true | 437 | 433 | | +| f | true | 438 | 437 | | +| f | true | 443 | 519 | | +| f | true | 447 | 443 | | +| f | true | 448 | 447 | | +| f | true | 453 | 598 | | +| f | true | 457 | 453 | | +| f | true | 458 | 457 | | +| f | true | 463 | 592 | | +| f | true | 467 | 463 | | +| f | true | 468 | 467 | | +| f | true | 473 | 553 | | +| f | true | 477 | 473 | | +| f | true | 478 | 477 | | +| f | true | 483 | 588 | | +| f | true | 487 | 483 | | +| f | true | 488 | 487 | | +| f | true | 493 | 582 | | +| f | true | 497 | 493 | | +| f | true | 498 | 497 | | +| f | true | 502 | 418 | | +| f | true | 504 | 448 | | +| f | true | 506 | 517 | T | +| f | true | 506 | 521 | F | +| f | true | 512 | 513 | | +| f | true | 513 | 600 | | +| f | true | 515 | 512 | | +| f | true | 517 | 515 | | +| f | true | 519 | 506 | | +| f | true | 521 | 458 | | +| f | true | 523 | 504 | | +| f | true | 529 | 532 | T | +| f | true | 530 | 468 | | +| f | true | 532 | 530 | | +| f | true | 534 | 523 | | +| f | true | 536 | 428 | | +| f | true | 538 | 478 | | +| f | true | 540 | 551 | T | +| f | true | 540 | 555 | F | +| f | true | 546 | 547 | | +| f | true | 547 | 590 | | +| f | true | 549 | 546 | | +| f | true | 551 | 549 | | +| f | true | 553 | 540 | | +| f | true | 555 | 488 | | +| f | true | 557 | 538 | | +| f | true | 559 | 498 | | +| f | true | 561 | 559 | | +| f | true | 563 | 557 | | +| f | true | 565 | 438 | | +| f | true | 567 | 576 | | +| f | true | 569 | 502 | | +| f | true | 571 | 573 | | +| f | true | 573 | 407 | | +| f | true | 574 | 575 | | +| f | true | 575 | 571 | | +| f | true | 576 | 577 | | +| f | true | 577 | 574 | | +| f | true | 578 | 579 | | +| f | true | 579 | 407 | | +| f | true | 580 | 581 | | +| f | true | 581 | 578 | | +| f | true | 582 | 584 | | +| f | true | 584 | 565 | | +| f | true | 584 | 580 | | +| f | true | 585 | 587 | | +| f | true | 587 | 561 | | +| f | true | 588 | 589 | | +| f | true | 589 | 585 | | +| f | true | 590 | 591 | | +| f | true | 591 | 561 | | +| f | true | 592 | 594 | | +| f | true | 594 | 536 | | +| f | true | 595 | 597 | | +| f | true | 597 | 536 | | +| f | true | 598 | 599 | | +| f | true | 599 | 595 | | +| f | true | 600 | 601 | | +| f | true | 601 | 529 | | +| f1 | false | 251 | 251 | f1 | +| f2 | false | 254 | 254 | f2 | +| f3 | false | 257 | 257 | f3 | +| f4 | false | 261 | 261 | f4 | +| f4 | false | 264 | 264 | return ... | +| f4 | false | 266 | 266 | { ... } | +| f4 | true | 264 | 261 | | +| f4 | true | 266 | 264 | | +| f5 | false | 269 | 269 | f5 | +| f5 | false | 274 | 274 | 3 | +| f5 | false | 275 | 275 | throw ... | +| f5 | false | 277 | 277 | ExprStmt | +| f5 | false | 279 | 279 | { ... } | +| f5 | true | 274 | 275 | | +| f5 | true | 275 | 269 | | +| f5 | true | 277 | 274 | | +| f5 | true | 279 | 277 | | +| fun | false | 297 | 297 | fun | +| fun | false | 311 | 311 | call to f1 | +| fun | false | 313 | 313 | ExprStmt | +| fun | false | 315 | 315 | call to f2 | +| fun | false | 317 | 317 | ExprStmt | +| fun | false | 319 | 319 | call to f3 | +| fun | false | 321 | 321 | ExprStmt | +| fun | false | 323 | 323 | call to f4 | +| fun | false | 325 | 325 | ExprStmt | +| fun | false | 327 | 327 | call to f5 | +| fun | false | 329 | 329 | ExprStmt | +| fun | false | 333 | 333 | 5 | +| fun | false | 334 | 334 | throw ... | +| fun | false | 336 | 336 | ExprStmt | +| fun | false | 338 | 338 | call to g | +| fun | false | 340 | 340 | ExprStmt | +| fun | false | 342 | 342 | { ... } | +| fun | false | 344 | 344 | call to h | +| fun | false | 346 | 346 | ExprStmt | +| fun | false | 348 | 348 | { ... } | +| fun | false | 350 | 350 | | +| fun | false | 351 | 351 | try { ... } | +| fun | false | 353 | 353 | { ... } | +| fun | false | 355 | 355 | call to i | +| fun | false | 357 | 357 | ExprStmt | +| fun | false | 359 | 359 | { ... } | +| fun | false | 361 | 361 | call to j | +| fun | false | 363 | 363 | ExprStmt | +| fun | false | 365 | 365 | { ... } | +| fun | false | 367 | 367 | | +| fun | false | 368 | 368 | | +| fun | false | 369 | 369 | try { ... } | +| fun | false | 371 | 371 | call to k | +| fun | false | 373 | 373 | ExprStmt | +| fun | false | 375 | 375 | return ... | +| fun | false | 377 | 377 | { ... } | +| fun | true | 311 | 317 | | +| fun | true | 313 | 311 | | +| fun | true | 315 | 321 | | +| fun | true | 317 | 315 | | +| fun | true | 319 | 325 | | +| fun | true | 321 | 319 | | +| fun | true | 323 | 329 | | +| fun | true | 325 | 323 | | +| fun | true | 327 | 336 | | +| fun | true | 329 | 327 | | +| fun | true | 333 | 334 | | +| fun | true | 334 | 350 | | +| fun | true | 336 | 333 | | +| fun | true | 338 | 373 | | +| fun | true | 340 | 338 | | +| fun | true | 342 | 313 | | +| fun | true | 344 | 373 | | +| fun | true | 346 | 344 | | +| fun | true | 348 | 346 | | +| fun | true | 350 | 348 | | +| fun | true | 350 | 367 | | +| fun | true | 351 | 342 | | +| fun | true | 353 | 351 | | +| fun | true | 355 | 373 | | +| fun | true | 357 | 355 | | +| fun | true | 359 | 357 | | +| fun | true | 361 | 373 | | +| fun | true | 363 | 361 | | +| fun | true | 365 | 363 | | +| fun | true | 367 | 359 | | +| fun | true | 367 | 368 | | +| fun | true | 368 | 297 | | +| fun | true | 368 | 365 | | +| fun | true | 369 | 353 | | +| fun | true | 371 | 375 | | +| fun | true | 373 | 371 | | +| fun | true | 375 | 297 | | +| fun | true | 377 | 369 | | +| fun2 | false | 160 | 160 | fun2 | +| fun2 | false | 165 | 165 | { ... } | +| fun2 | false | 167 | 167 | re-throw exception | +| fun2 | false | 169 | 169 | ExprStmt | +| fun2 | false | 171 | 171 | { ... } | +| fun2 | false | 175 | 175 | 1 | +| fun2 | false | 176 | 176 | return ... | +| fun2 | false | 178 | 178 | { ... } | +| fun2 | false | 180 | 180 | | +| fun2 | false | 181 | 181 | | +| fun2 | false | 182 | 182 | try { ... } | +| fun2 | false | 186 | 186 | 0 | +| fun2 | false | 187 | 187 | return ... | +| fun2 | false | 189 | 189 | { ... } | +| fun2 | false | 200 | 200 | fun2 | +| fun2 | false | 207 | 207 | { ... } | +| fun2 | false | 209 | 209 | re-throw exception | +| fun2 | false | 211 | 211 | ExprStmt | +| fun2 | false | 213 | 213 | { ... } | +| fun2 | false | 216 | 216 | 1 | +| fun2 | false | 217 | 217 | return ... | +| fun2 | false | 219 | 219 | { ... } | +| fun2 | false | 221 | 221 | | +| fun2 | false | 222 | 222 | | +| fun2 | false | 223 | 223 | try { ... } | +| fun2 | false | 226 | 226 | 0 | +| fun2 | false | 227 | 227 | return ... | +| fun2 | false | 229 | 229 | { ... } | +| fun2 | true | 165 | 187 | | +| fun2 | true | 167 | 160 | | +| fun2 | true | 169 | 167 | | +| fun2 | true | 171 | 169 | | +| fun2 | true | 175 | 160 | | +| fun2 | true | 176 | 175 | | +| fun2 | true | 178 | 176 | | +| fun2 | true | 180 | 171 | | +| fun2 | true | 180 | 181 | | +| fun2 | true | 181 | 178 | | +| fun2 | true | 182 | 165 | | +| fun2 | true | 186 | 160 | | +| fun2 | true | 187 | 186 | | +| fun2 | true | 189 | 182 | | +| fun2 | true | 207 | 227 | | +| fun2 | true | 209 | 200 | | +| fun2 | true | 211 | 209 | | +| fun2 | true | 213 | 211 | | +| fun2 | true | 216 | 200 | | +| fun2 | true | 217 | 216 | | +| fun2 | true | 219 | 217 | | +| fun2 | true | 221 | 213 | | +| fun2 | true | 221 | 222 | | +| fun2 | true | 222 | 219 | | +| fun2 | true | 223 | 207 | | +| fun2 | true | 226 | 200 | | +| fun2 | true | 227 | 226 | | +| fun2 | true | 229 | 223 | | +| g | false | 281 | 281 | g | +| h | false | 284 | 284 | h | +| i | false | 287 | 287 | i | +| j | false | 290 | 290 | j | +| k | false | 293 | 293 | k | +| run_fun2 | false | 195 | 195 | run_fun2 | +| run_fun2 | false | 231 | 231 | call to fun2 | +| run_fun2 | false | 233 | 233 | ExprStmt | +| run_fun2 | false | 235 | 235 | return ... | +| run_fun2 | false | 237 | 237 | { ... } | +| run_fun2 | true | 231 | 235 | | +| run_fun2 | true | 233 | 231 | | +| run_fun2 | true | 235 | 195 | | +| run_fun2 | true | 237 | 233 | | diff --git a/cpp/ql/test/library-tests/c++_exceptions/graphable.ql b/cpp/ql/test/library-tests/c++_exceptions/graphable.ql new file mode 100644 index 000000000000..4c3d73d46eea --- /dev/null +++ b/cpp/ql/test/library-tests/c++_exceptions/graphable.ql @@ -0,0 +1,34 @@ +// query-type: graph +import cpp + +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() + } +} + +string scope(ControlFlowNode x) { + if exists(x.getControlFlowScope().getQualifiedName()) + then result = x.getControlFlowScope().getQualifiedName() + else result = "" +} + +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 x.getASuccessor() = y + and if x.getATrueSuccessor() = y then truelabel = "T" else truelabel = "" + and if x.getAFalseSuccessor() = y then falselabel = "F" else falselabel = "" + and label = truelabel + falselabel) +} + +from boolean isEdge, ControlFlowNode x, ControlFlowNode y, string label +where isNode(isEdge, x, y, label) or isSuccessor(isEdge, x, y, label) +select scope(x), isEdge, x, y, label + diff --git a/cpp/ql/test/library-tests/c++_exceptions/handler.expected b/cpp/ql/test/library-tests/c++_exceptions/handler.expected new file mode 100644 index 000000000000..e629e9b260e4 --- /dev/null +++ b/cpp/ql/test/library-tests/c++_exceptions/handler.expected @@ -0,0 +1,31 @@ +| ODASA-5692.cpp:11:18:13:3 | | getBlock | ODASA-5692.cpp:11:18:13:3 | { ... } | +| ODASA-5692.cpp:11:18:13:3 | | getBlock | ODASA-5692.cpp:11:18:13:3 | { ... } | +| ODASA-5692.cpp:11:18:13:3 | | getParameter | ODASA-5692.cpp:11:10:11:14 | p#0 | +| ODASA-5692.cpp:11:18:13:3 | | getTryStmt | ODASA-5692.cpp:9:3:10:3 | try { ... } | +| ODASA-5692.cpp:11:18:13:3 | | getTryStmt | ODASA-5692.cpp:9:3:10:3 | try { ... } | +| ODASA-5692.cpp:11:18:13:3 | | getTryStmt.getACatchClause() | ODASA-5692.cpp:11:18:13:3 | { ... } | +| ODASA-5692.cpp:11:18:13:3 | | getTryStmt.getACatchClause() | ODASA-5692.cpp:11:18:13:3 | { ... } | +| ODASA-5692.cpp:11:18:13:3 | | getTryStmt.getACatchClause() | ODASA-5692.cpp:14:15:16:3 | { ... } | +| ODASA-5692.cpp:11:18:13:3 | | getTryStmt.getACatchClause() | ODASA-5692.cpp:14:15:16:3 | { ... } | +| ODASA-5692.cpp:14:15:16:3 | | getBlock | ODASA-5692.cpp:14:15:16:3 | { ... } | +| ODASA-5692.cpp:14:15:16:3 | | getBlock | ODASA-5692.cpp:14:15:16:3 | { ... } | +| ODASA-5692.cpp:14:15:16:3 | | getTryStmt | ODASA-5692.cpp:9:3:10:3 | try { ... } | +| ODASA-5692.cpp:14:15:16:3 | | getTryStmt | ODASA-5692.cpp:9:3:10:3 | try { ... } | +| ODASA-5692.cpp:14:15:16:3 | | getTryStmt.getACatchClause() | ODASA-5692.cpp:11:18:13:3 | { ... } | +| ODASA-5692.cpp:14:15:16:3 | | getTryStmt.getACatchClause() | ODASA-5692.cpp:11:18:13:3 | { ... } | +| ODASA-5692.cpp:14:15:16:3 | | getTryStmt.getACatchClause() | ODASA-5692.cpp:14:15:16:3 | { ... } | +| ODASA-5692.cpp:14:15:16:3 | | getTryStmt.getACatchClause() | ODASA-5692.cpp:14:15:16:3 | { ... } | +| exceptions.cpp:28:23:30:9 | | getBlock | exceptions.cpp:28:23:30:9 | { ... } | +| exceptions.cpp:28:23:30:9 | | getParameter | exceptions.cpp:28:20:28:20 | e | +| exceptions.cpp:28:23:30:9 | | getTryStmt | exceptions.cpp:19:9:27:9 | try { ... } | +| exceptions.cpp:28:23:30:9 | | getTryStmt.getACatchClause() | exceptions.cpp:28:23:30:9 | { ... } | +| exceptions.cpp:32:19:34:5 | | getBlock | exceptions.cpp:32:19:34:5 | { ... } | +| exceptions.cpp:32:19:34:5 | | getParameter | exceptions.cpp:32:16:32:16 | e | +| exceptions.cpp:32:19:34:5 | | getTryStmt | exceptions.cpp:18:5:31:5 | try { ... } | +| exceptions.cpp:32:19:34:5 | | getTryStmt.getACatchClause() | exceptions.cpp:32:19:34:5 | { ... } | +| exceptions.cpp:32:19:34:5 | | getTryStmt.getACatchClause() | exceptions.cpp:35:19:37:5 | { ... } | +| exceptions.cpp:35:19:37:5 | | getBlock | exceptions.cpp:35:19:37:5 | { ... } | +| exceptions.cpp:35:19:37:5 | | getParameter | exceptions.cpp:35:16:35:16 | e | +| exceptions.cpp:35:19:37:5 | | getTryStmt | exceptions.cpp:18:5:31:5 | try { ... } | +| exceptions.cpp:35:19:37:5 | | getTryStmt.getACatchClause() | exceptions.cpp:32:19:34:5 | { ... } | +| exceptions.cpp:35:19:37:5 | | getTryStmt.getACatchClause() | exceptions.cpp:35:19:37:5 | { ... } | diff --git a/cpp/ql/test/library-tests/c++_exceptions/handler.ql b/cpp/ql/test/library-tests/c++_exceptions/handler.ql new file mode 100644 index 000000000000..e7a534da2a9e --- /dev/null +++ b/cpp/ql/test/library-tests/c++_exceptions/handler.ql @@ -0,0 +1,9 @@ +import cpp + +from Handler h, string property, Element value +where + (property = "getParameter" and value = h.getParameter()) or + (property = "getBlock" and value = h.getBlock()) or + (property = "getTryStmt" and value = h.getTryStmt()) or + (property = "getTryStmt.getACatchClause()" and value = h.getTryStmt().getACatchClause()) +select h, property, value diff --git a/cpp/ql/test/library-tests/c++_exceptions/ms.cpp b/cpp/ql/test/library-tests/c++_exceptions/ms.cpp new file mode 100644 index 000000000000..41551db93010 --- /dev/null +++ b/cpp/ql/test/library-tests/c++_exceptions/ms.cpp @@ -0,0 +1,39 @@ +// semmle-extractor-options: --microsoft +#define EXCEPTION_EXECUTE_HANDLER 1 + +class C { + public: + C(int x); + ~C(); +}; + +void f(int b1, int b2) { + C c101(101); + + __try { + C c102(102); + if (b1) { + throw 1; + } + C c103(103); + } + __except (EXCEPTION_EXECUTE_HANDLER) { + C c104(104); + } + + C c105(105); + + __try { + C c106(106); + if (b2) { + throw 2; + } + C c107(107); + } + __finally { + C c108(108); + } + + C c109(109); +} + diff --git a/cpp/ql/test/library-tests/c++_exceptions/unreachable.expected b/cpp/ql/test/library-tests/c++_exceptions/unreachable.expected new file mode 100644 index 000000000000..33c1f250aa6e --- /dev/null +++ b/cpp/ql/test/library-tests/c++_exceptions/unreachable.expected @@ -0,0 +1,5 @@ +| ODASA-5692.cpp:11:18:13:3 | BasicBlock | +| ODASA-5692.cpp:11:18:13:3 | BasicBlock | +| ODASA-5692.cpp:14:15:15:12 | BasicBlock | +| ODASA-5692.cpp:14:15:15:12 | BasicBlock | +| exceptions.cpp:26:13:26:13 | BasicBlock | diff --git a/cpp/ql/test/library-tests/c++_exceptions/unreachable.ql b/cpp/ql/test/library-tests/c++_exceptions/unreachable.ql new file mode 100644 index 000000000000..891b4d062ec5 --- /dev/null +++ b/cpp/ql/test/library-tests/c++_exceptions/unreachable.ql @@ -0,0 +1,6 @@ +import cpp + +from BasicBlock b +where not b.isReachable() +select b + diff --git a/cpp/ql/test/library-tests/c11_generic/generic.c b/cpp/ql/test/library-tests/c11_generic/generic.c new file mode 100644 index 000000000000..222d14efa148 --- /dev/null +++ b/cpp/ql/test/library-tests/c11_generic/generic.c @@ -0,0 +1,25 @@ +// semmle-extractor-options: -std=c11 + +int printf(const char *format, ...); + +#define describe(val) \ + _Generic((val), \ + int: "int", \ + const char *: "string", \ + default: "unknown" \ + ) + +typedef int MYINT; + +int main() +{ + int i; + MYINT m; + const char *s; + float ***f; + + printf("i is %s\n", describe(i)); // int + printf("c is %s\n", describe(m)); // int + printf("s is %s\n", describe(s)); // string + printf("f is %s\n", describe(f)); // unknown +} diff --git a/cpp/ql/test/library-tests/c11_generic/generic.cpp b/cpp/ql/test/library-tests/c11_generic/generic.cpp new file mode 100644 index 000000000000..9df82080f0f4 --- /dev/null +++ b/cpp/ql/test/library-tests/c11_generic/generic.cpp @@ -0,0 +1,26 @@ +// semmle-extractor-options: --clang -std=c++11 +// The C11 _Generic keyword is supported in C++ mode in clang >= 4.0.0. + +int printf(const char *format, ...); + +#define describe(val) \ + _Generic((val), \ + int: "int", \ + const char *: "string", \ + default: "unknown" \ + ) + +typedef int MYINT; + +int main() +{ + int i; + MYINT m; + const char *s; + float ***f; + + printf("i is %s\n", describe(i)); // int + printf("c is %s\n", describe(m)); // int + printf("s is %s\n", describe(s)); // string + printf("f is %s\n", describe(f)); // unknown +} diff --git a/cpp/ql/test/library-tests/c11_generic/test.expected b/cpp/ql/test/library-tests/c11_generic/test.expected new file mode 100644 index 000000000000..13b7ca0f9d71 --- /dev/null +++ b/cpp/ql/test/library-tests/c11_generic/test.expected @@ -0,0 +1,8 @@ +| generic.c:21:22:21:32 | int | +| generic.c:22:22:22:32 | int | +| generic.c:23:22:23:32 | string | +| generic.c:24:22:24:32 | unknown | +| generic.cpp:22:22:22:32 | int | +| generic.cpp:23:22:23:32 | int | +| generic.cpp:24:22:24:32 | string | +| generic.cpp:25:22:25:32 | unknown | diff --git a/cpp/ql/test/library-tests/c11_generic/test.ql b/cpp/ql/test/library-tests/c11_generic/test.ql new file mode 100644 index 000000000000..65fc28181dc6 --- /dev/null +++ b/cpp/ql/test/library-tests/c11_generic/test.ql @@ -0,0 +1,6 @@ +import cpp + +from FunctionCall fc +where + fc.getTarget().getName() = "printf" +select fc.getArgument(1) diff --git a/cpp/ql/test/library-tests/c_overload/c_overload.c b/cpp/ql/test/library-tests/c_overload/c_overload.c new file mode 100644 index 000000000000..2c65eb0064f1 --- /dev/null +++ b/cpp/ql/test/library-tests/c_overload/c_overload.c @@ -0,0 +1,37 @@ +/* static f */ + +static void f(...) __attribute__((overloadable)); + +static int __attribute((overloadable)) f(int x) { + return x; +} + +static double __attribute((overloadable)) f(double x) { + return x; +} + +/* non-static g */ + +int __attribute__((overloadable)) g(int x) { + return x; +} + +double __attribute__((overloadable)) g(double x) { + return x; +} + +/* usages */ + +typedef struct foo { int x; } foo_t; + +int main() { + foo_t foo; + f(foo); + f(3); + f(3.0); + + g(3); + g(3.0); + + return 0; +} diff --git a/cpp/ql/test/library-tests/c_overload/c_overload.expected b/cpp/ql/test/library-tests/c_overload/c_overload.expected new file mode 100644 index 000000000000..4b2ab1e8ead9 --- /dev/null +++ b/cpp/ql/test/library-tests/c_overload/c_overload.expected @@ -0,0 +1,6 @@ +| c_overload.c:3:13:3:13 | f | f | 0 | +| c_overload.c:5:40:5:40 | f | f | 1 | +| c_overload.c:9:43:9:43 | f | f | 1 | +| c_overload.c:15:35:15:35 | g | g | 1 | +| c_overload.c:19:38:19:38 | g | g | 1 | +| c_overload.c:27:5:27:8 | main | main | 0 | diff --git a/cpp/ql/test/library-tests/c_overload/c_overload.ql b/cpp/ql/test/library-tests/c_overload/c_overload.ql new file mode 100644 index 000000000000..e6d999d2f559 --- /dev/null +++ b/cpp/ql/test/library-tests/c_overload/c_overload.ql @@ -0,0 +1,4 @@ +import cpp + +from Function f +select f, f.getName(), count(f.getAParameter()) diff --git a/cpp/ql/test/library-tests/c_overload/calls.expected b/cpp/ql/test/library-tests/c_overload/calls.expected new file mode 100644 index 000000000000..bd7e2ed6efde --- /dev/null +++ b/cpp/ql/test/library-tests/c_overload/calls.expected @@ -0,0 +1,5 @@ +| c_overload.c:29:3:29:3 | call to f | c_overload.c:3:13:3:13 | f | +| c_overload.c:30:3:30:3 | call to f | c_overload.c:5:40:5:40 | f | +| c_overload.c:31:3:31:3 | call to f | c_overload.c:9:43:9:43 | f | +| c_overload.c:33:3:33:3 | call to g | c_overload.c:15:35:15:35 | g | +| c_overload.c:34:3:34:3 | call to g | c_overload.c:19:38:19:38 | g | diff --git a/cpp/ql/test/library-tests/c_overload/calls.ql b/cpp/ql/test/library-tests/c_overload/calls.ql new file mode 100644 index 000000000000..f52a0677281b --- /dev/null +++ b/cpp/ql/test/library-tests/c_overload/calls.ql @@ -0,0 +1,4 @@ +import cpp + +from FunctionCall fc +select fc, fc.getTarget() diff --git a/cpp/ql/test/library-tests/c_overload/options b/cpp/ql/test/library-tests/c_overload/options new file mode 100644 index 000000000000..5e8fada6b21f --- /dev/null +++ b/cpp/ql/test/library-tests/c_overload/options @@ -0,0 +1 @@ +extractor_flags: --clang diff --git a/cpp/ql/test/library-tests/calls/Calls1.expected b/cpp/ql/test/library-tests/calls/Calls1.expected new file mode 100644 index 000000000000..24e0156dccae --- /dev/null +++ b/cpp/ql/test/library-tests/calls/Calls1.expected @@ -0,0 +1,5 @@ +| f | accesses | negate | +| f | calls | compute | +| fun3 | calls | f | +| fun3 | calls | g | +| g | calls | swap | diff --git a/cpp/ql/test/library-tests/calls/Calls1.ql b/cpp/ql/test/library-tests/calls/Calls1.ql new file mode 100644 index 000000000000..145022dd9409 --- /dev/null +++ b/cpp/ql/test/library-tests/calls/Calls1.ql @@ -0,0 +1,10 @@ +/** + * @name Calls1 + * @kind table + */ +import cpp + +from Function a, string rel, Function b +where (a.accesses(b) and rel = "accesses") or + (a.calls(b) and rel = "calls") +select a.getName(), rel, b.getName() diff --git a/cpp/ql/test/library-tests/calls/Calls3.expected b/cpp/ql/test/library-tests/calls/Calls3.expected new file mode 100644 index 000000000000..5d381065d35f --- /dev/null +++ b/cpp/ql/test/library-tests/calls/Calls3.expected @@ -0,0 +1,4 @@ +| calls2.cpp:6:2:6:5 | call to swap | swap | calls1.cpp | +| calls2.cpp:18:2:18:8 | call to compute | compute | calls2.cpp | +| calls3.cpp:10:9:10:9 | call to f | f | calls3.cpp | +| calls3.cpp:11:13:11:13 | call to g | g | calls3.cpp | diff --git a/cpp/ql/test/library-tests/calls/Calls3.ql b/cpp/ql/test/library-tests/calls/Calls3.ql new file mode 100644 index 000000000000..6204ac03a89b --- /dev/null +++ b/cpp/ql/test/library-tests/calls/Calls3.ql @@ -0,0 +1,9 @@ +/** + * @name Calls3 + * @kind table + */ +import cpp + +from FunctionCall c, CppFile f +where c.getTarget().getFile() = f +select c, c.getTarget().getName(), f.getAbsolutePath() diff --git a/cpp/ql/test/library-tests/calls/args.expected b/cpp/ql/test/library-tests/calls/args.expected new file mode 100644 index 000000000000..31bc6aa26da9 --- /dev/null +++ b/cpp/ql/test/library-tests/calls/args.expected @@ -0,0 +1,8 @@ +| calls2.cpp:6:2:6:5 | call to swap | 0 | calls2.cpp:6:7:6:8 | & ... | +| calls2.cpp:6:2:6:5 | call to swap | 1 | calls2.cpp:6:10:6:11 | & ... | +| calls2.cpp:13:2:13:5 | call to expression | 0 | calls2.cpp:13:4:13:4 | b | +| calls2.cpp:13:2:13:5 | call to expression | 0 | calls2.cpp:13:4:13:4 | b | +| calls2.cpp:18:2:18:8 | call to compute | 0 | calls2.cpp:18:10:18:11 | aa | +| calls2.cpp:18:2:18:8 | call to compute | 1 | calls2.cpp:18:14:18:19 | negate | +| calls3.cpp:11:13:11:13 | call to g | 0 | calls3.cpp:11:15:11:15 | 1 | +| calls3.cpp:11:13:11:13 | call to g | 1 | calls3.cpp:11:18:11:18 | 2 | diff --git a/cpp/ql/test/library-tests/calls/args.ql b/cpp/ql/test/library-tests/calls/args.ql new file mode 100644 index 000000000000..1112dc5a7e39 --- /dev/null +++ b/cpp/ql/test/library-tests/calls/args.ql @@ -0,0 +1,5 @@ +import cpp + +from Call c, int i +select c, i, c.getArgument(i) + diff --git a/cpp/ql/test/library-tests/calls/calls.expected b/cpp/ql/test/library-tests/calls/calls.expected new file mode 100644 index 000000000000..4c33917a8f14 --- /dev/null +++ b/cpp/ql/test/library-tests/calls/calls.expected @@ -0,0 +1,7 @@ +| calls2.cpp:6:2:6:5 | call to swap | 2 | file://:0:0:0:0 | void | +| calls2.cpp:13:2:13:5 | call to expression | 1 | file://:0:0:0:0 | unknown | +| calls2.cpp:13:2:13:5 | call to expression | 1 | file://:0:0:0:0 | void | +| calls2.cpp:18:2:18:8 | call to compute | 2 | file://:0:0:0:0 | void | +| calls3.cpp:10:9:10:9 | call to f | 0 | file://:0:0:0:0 | void | +| calls3.cpp:11:13:11:13 | call to g | 2 | file://:0:0:0:0 | int | +| calls4.cpp:8:9:8:23 | call to expression | 0 | file://:0:0:0:0 | bool | diff --git a/cpp/ql/test/library-tests/calls/calls.ql b/cpp/ql/test/library-tests/calls/calls.ql new file mode 100644 index 000000000000..db68bd9c30f0 --- /dev/null +++ b/cpp/ql/test/library-tests/calls/calls.ql @@ -0,0 +1,4 @@ +import cpp + +from Call c +select c, c.getNumberOfArguments(), c.getType() diff --git a/cpp/ql/test/library-tests/calls/calls1.cpp b/cpp/ql/test/library-tests/calls/calls1.cpp new file mode 100644 index 000000000000..0c3f1231227b --- /dev/null +++ b/cpp/ql/test/library-tests/calls/calls1.cpp @@ -0,0 +1,8 @@ +#include "calls1.h" + +void swap(int* p, int* q) +{ + int t = *p; + *p = *q; + *q = t; +} diff --git a/cpp/ql/test/library-tests/calls/calls1.h b/cpp/ql/test/library-tests/calls/calls1.h new file mode 100644 index 000000000000..90b150ae9d68 --- /dev/null +++ b/cpp/ql/test/library-tests/calls/calls1.h @@ -0,0 +1,2 @@ + +extern void swap(int*, int*); diff --git a/cpp/ql/test/library-tests/calls/calls2.cpp b/cpp/ql/test/library-tests/calls/calls2.cpp new file mode 100644 index 000000000000..4f10faa3f208 --- /dev/null +++ b/cpp/ql/test/library-tests/calls/calls2.cpp @@ -0,0 +1,20 @@ +#include "calls1.h" + +void g() { + int x = 2; + int y = 4; + swap(&x,&y); +} + +void negate(int& c) { c = -c; } + +template void compute(Iter b, Fct f) +{ + f(b); +} + +void f(int aa) +{ + compute(aa, negate); +} + diff --git a/cpp/ql/test/library-tests/calls/calls3.cpp b/cpp/ql/test/library-tests/calls/calls3.cpp new file mode 100644 index 000000000000..32d11c82de1c --- /dev/null +++ b/cpp/ql/test/library-tests/calls/calls3.cpp @@ -0,0 +1,13 @@ + +class someClass { +public: + void f(void); + int g(int i, int j); +}; + +void fun3(someClass *sc) { + int i; + sc->f(); + i = sc->g(1, 2); +} + diff --git a/cpp/ql/test/library-tests/calls/calls4.cpp b/cpp/ql/test/library-tests/calls/calls4.cpp new file mode 100644 index 000000000000..0823e0e868aa --- /dev/null +++ b/cpp/ql/test/library-tests/calls/calls4.cpp @@ -0,0 +1,11 @@ + +class CThisCall { + struct S { + bool (CThisCall::*b)(); + }; + const S *s; + void f() { + if ((this->*s->b)()) ; + } +}; + diff --git a/cpp/ql/test/library-tests/cast_specifiers/cast_specifiers.c b/cpp/ql/test/library-tests/cast_specifiers/cast_specifiers.c new file mode 100644 index 000000000000..6a20dcd4fa51 --- /dev/null +++ b/cpp/ql/test/library-tests/cast_specifiers/cast_specifiers.c @@ -0,0 +1,14 @@ +// Compilable with: +// gcc -c cast_specifiers.c + +void f(void) { + int x = 3; + x = x; + x = (int)x; + x = (const long)x; // [MISSING: 'const', 'volatile' specifiers on casts on lines 8 .. 12] + x = (volatile int)x; // [MISSING: implicit cast back to int] + x = (const int)x; // [MISSING: implicit cast back to int] + x = (const volatile int)x; // [MISSING: implicit cast back to int] + x = (volatile const int)x; // [MISSING: implicit cast back to int] +} + diff --git a/cpp/ql/test/library-tests/cast_specifiers/cast_specifiers.expected b/cpp/ql/test/library-tests/cast_specifiers/cast_specifiers.expected new file mode 100644 index 000000000000..5757d70c4aa2 --- /dev/null +++ b/cpp/ql/test/library-tests/cast_specifiers/cast_specifiers.expected @@ -0,0 +1,7 @@ +| 7 | (int)... | int | explicit | -------- | ----- | +| 8 | (int)... | int | implicit | -------- | ----- | +| 8 | (long)... | long | explicit | -------- | ----- | +| 9 | (int)... | int | explicit | -------- | ----- | +| 10 | (int)... | int | explicit | -------- | ----- | +| 11 | (int)... | int | explicit | -------- | ----- | +| 12 | (int)... | int | explicit | -------- | ----- | diff --git a/cpp/ql/test/library-tests/cast_specifiers/cast_specifiers.ql b/cpp/ql/test/library-tests/cast_specifiers/cast_specifiers.ql new file mode 100644 index 000000000000..af588809d4ed --- /dev/null +++ b/cpp/ql/test/library-tests/cast_specifiers/cast_specifiers.ql @@ -0,0 +1,23 @@ +import cpp + +from Cast c, string cs, string ts, + string volatile, string const, string explicit, string padding +where if c.getType().isVolatile() + then volatile = "Volatile" + else volatile = "--------" + and if c.getType().isConst() + then const = "Const" + else const = "-----" + and if c.isImplicit() + then explicit = "implicit" + else explicit = "explicit" + and padding = " " + and cs = c.toString() + and ts = c.getType().toString() +select c.getLocation().getStartLine(), + (cs + padding).prefix(cs.length().maximum(23)), + (ts + padding).prefix(ts.length().maximum(18)), + explicit, + volatile, + const + diff --git a/cpp/ql/test/library-tests/clang_builtin_macros/bar.h b/cpp/ql/test/library-tests/clang_builtin_macros/bar.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/cpp/ql/test/library-tests/clang_builtin_macros/diag.expected b/cpp/ql/test/library-tests/clang_builtin_macros/diag.expected new file mode 100644 index 000000000000..a4a15c0b2ef9 --- /dev/null +++ b/cpp/ql/test/library-tests/clang_builtin_macros/diag.expected @@ -0,0 +1,3 @@ +| file://:0:0:0:0 | There was an error during this compilation | 4 | Error occurred | +| test.cpp:59:50:59:50 | expected a file name | 4 | exp_file_name | +| test.cpp:59:55:59:55 | expected a ';' | 4 | exp_semicolon | diff --git a/cpp/ql/test/library-tests/clang_builtin_macros/diag.ql b/cpp/ql/test/library-tests/clang_builtin_macros/diag.ql new file mode 100644 index 000000000000..53e8ea5de9da --- /dev/null +++ b/cpp/ql/test/library-tests/clang_builtin_macros/diag.ql @@ -0,0 +1,4 @@ +import cpp + +from Diagnostic d +select d, d.getSeverity(), d.getTag() diff --git a/cpp/ql/test/library-tests/clang_builtin_macros/extended.c b/cpp/ql/test/library-tests/clang_builtin_macros/extended.c new file mode 100644 index 000000000000..b38df35c9993 --- /dev/null +++ b/cpp/ql/test/library-tests/clang_builtin_macros/extended.c @@ -0,0 +1,3 @@ +static int has_nullptr_f = __has_feature(cxx_nullptr); +static int has_nullptr_e = __has_extension(cxx_nullptr); +// semmle-extractor-options: --edg --clang --edg --c++ --edg --nullptr diff --git a/cpp/ql/test/library-tests/clang_builtin_macros/gcc492.c b/cpp/ql/test/library-tests/clang_builtin_macros/gcc492.c new file mode 100644 index 000000000000..5fcb8aaee49c --- /dev/null +++ b/cpp/ql/test/library-tests/clang_builtin_macros/gcc492.c @@ -0,0 +1,38 @@ +#if defined(__has_include) +static int has_include = 1; +#else +static int has_include = 0; +#endif + +#if __has_include("bar.h") +static int has_present_include = 1; +#else +static int has_present_include = 0; +#endif + +#define BAR "bar.h" +#if __has_include(BAR) +static int has_macro_include = 1; +#else +static int has_macro_include = 0; +#endif + +#if __has_include("foo.h") +static int has_missing_include = 1; +#else +static int has_missing_include = 0; +#endif + +#if __has_include_next("foo.h") +static int has_missing_include_next = 1; +#else +static int has_missing_include_next = 0; +#endif + +#if __has_include() +static int has_missing_system_include = 1; +#else +static int has_missing_system_include = 0; +#endif + +// semmle-extractor-options: --gnu_version 40902 -D__has_include(STR)=__has_include__(STR) -D__has_include_next(STR)=__has_include_next__(STR) diff --git a/cpp/ql/test/library-tests/clang_builtin_macros/h1.h b/cpp/ql/test/library-tests/clang_builtin_macros/h1.h new file mode 100644 index 000000000000..58a2e01dde65 --- /dev/null +++ b/cpp/ql/test/library-tests/clang_builtin_macros/h1.h @@ -0,0 +1,5 @@ + +#include "h2.h" + +static int include_level_1 = __INCLUDE_LEVEL__; + diff --git a/cpp/ql/test/library-tests/clang_builtin_macros/h2.h b/cpp/ql/test/library-tests/clang_builtin_macros/h2.h new file mode 100644 index 000000000000..b7b03d70d655 --- /dev/null +++ b/cpp/ql/test/library-tests/clang_builtin_macros/h2.h @@ -0,0 +1,3 @@ + +static int include_level_2 = __INCLUDE_LEVEL__; + diff --git a/cpp/ql/test/library-tests/clang_builtin_macros/options b/cpp/ql/test/library-tests/clang_builtin_macros/options new file mode 100644 index 000000000000..93878bfd66cb --- /dev/null +++ b/cpp/ql/test/library-tests/clang_builtin_macros/options @@ -0,0 +1 @@ +extractor_flags: --edg --clang diff --git a/cpp/ql/test/library-tests/clang_builtin_macros/test.cpp b/cpp/ql/test/library-tests/clang_builtin_macros/test.cpp new file mode 100644 index 000000000000..14023a67063a --- /dev/null +++ b/cpp/ql/test/library-tests/clang_builtin_macros/test.cpp @@ -0,0 +1,63 @@ +// For the canonical behaviour, run: clang -E -w test.cpp +#define __builtin_TRAP __builtin_trap +#define BAR "bar.h" +// semmle-extractor-options: --edg --clang --expect_errors +#if defined(__has_include) +static int has_include = 1; +#else +static int has_include = 0; +#endif +static int has_builtin_trap = __has_builtin(__builtin_trap); +static int has_builtin_TRAP = __has_builtin(__builtin_TRAP); +static int has_builtin_door = __has_builtin(__builtin_door); +static int has_feature_cxx_rvalue_references = __has_feature(cxx_rvalue_references); +static int has_feature_crr_xvalue_xefexences = __has_feature(crr_xvalue_xefexences); +static int has_extension_cxx_rvalue_references = __has_feature(cxx_rvalue_references); +static int has_extension_crr_xvalue_xefexences = __has_feature(crr_xvalue_xefexences); +static int has_attribute_unused = __has_attribute(unused); +static int has_attribute_unused__ = __has_attribute(__unused__); +static int has_attribute_recycle = __has_attribute(recycle); +static int has_present_include = __has_include("bar.h"); +static int has_macro_include = __has_include(BAR); +static int has_missing_include = __has_include("foo.h"); +static int has_missing_include_next = __has_include_next("foo.h"); +static int has_missing_system_include = __has_include(); + +static int has_nullptr_f = __has_feature(cxx_nullptr); +static int has_nullptr_e = __has_extension(cxx_nullptr); +static int has_nullptr__ = __has_extension(__cxx_nullptr__); + +static int objc_array_literals = __has_feature(objc_array_literals); +static int objc_dictionary_literals = __has_feature(objc_dictionary_literals); +static int objc_boxed_expressions = __has_feature(objc_boxed_expressions); +static int objc_made_up_thing = __has_feature(objc_made_up_thing); + +#if __has_builtin(__builtin_trap) +static int if_has_builtin_trap = 1; +#else +static int if_has_builtin_trap = 0; +#endif + +#if __has_builtin(__builtin_TRAP) +static int if_has_builtin_TRAP = 1; +#else +static int if_has_builtin_TRAP = 0; +#endif + +#if __has_include("bar.h") +static int if_has_present_include = 1; +#else +static int if_has_present_include = 0; +#endif + +#if __has_include(BAR) +static int if_has_macro_include = 1; +#else +static int if_has_macro_include = 0; +#endif + +static int has_malformed_include = __has_include(bar_h); + +static int include_level_0 = __INCLUDE_LEVEL__; +#include "h1.h" + diff --git a/cpp/ql/test/library-tests/clang_builtin_macros/values.expected b/cpp/ql/test/library-tests/clang_builtin_macros/values.expected new file mode 100644 index 000000000000..d51823065b6c --- /dev/null +++ b/cpp/ql/test/library-tests/clang_builtin_macros/values.expected @@ -0,0 +1,43 @@ +| | fp_offset | | +| | gp_offset | | +| | overflow_arg_area | | +| | reg_save_area | | +| extended.c | has_nullptr_e | 1 | +| extended.c | has_nullptr_f | 1 | +| gcc492.c | has_include | 1 | +| gcc492.c | has_macro_include | 1 | +| gcc492.c | has_missing_include | 0 | +| gcc492.c | has_missing_include_next | 0 | +| gcc492.c | has_missing_system_include | 0 | +| gcc492.c | has_present_include | 1 | +| h1.h | include_level_1 | 1 | +| h2.h | include_level_2 | 2 | +| test.cpp | has_attribute_recycle | 0 | +| test.cpp | has_attribute_unused | 1 | +| test.cpp | has_attribute_unused__ | 1 | +| test.cpp | has_builtin_TRAP | 0 | +| test.cpp | has_builtin_door | 0 | +| test.cpp | has_builtin_trap | 1 | +| test.cpp | has_extension_crr_xvalue_xefexences | 0 | +| test.cpp | has_extension_cxx_rvalue_references | 1 | +| test.cpp | has_feature_crr_xvalue_xefexences | 0 | +| test.cpp | has_feature_cxx_rvalue_references | 1 | +| test.cpp | has_include | 1 | +| test.cpp | has_macro_include | 1 | +| test.cpp | has_malformed_include | 0 | +| test.cpp | has_missing_include | 0 | +| test.cpp | has_missing_include_next | 0 | +| test.cpp | has_missing_system_include | 0 | +| test.cpp | has_nullptr__ | 1 | +| test.cpp | has_nullptr_e | 1 | +| test.cpp | has_nullptr_f | 1 | +| test.cpp | has_present_include | 1 | +| test.cpp | if_has_builtin_TRAP | 0 | +| test.cpp | if_has_builtin_trap | 1 | +| test.cpp | if_has_macro_include | 1 | +| test.cpp | if_has_present_include | 1 | +| test.cpp | include_level_0 | 0 | +| test.cpp | objc_array_literals | 0 | +| test.cpp | objc_boxed_expressions | 0 | +| test.cpp | objc_dictionary_literals | 0 | +| test.cpp | objc_made_up_thing | 0 | diff --git a/cpp/ql/test/library-tests/clang_builtin_macros/values.ql b/cpp/ql/test/library-tests/clang_builtin_macros/values.ql new file mode 100644 index 000000000000..a5ac1514a9bd --- /dev/null +++ b/cpp/ql/test/library-tests/clang_builtin_macros/values.ql @@ -0,0 +1,10 @@ +import cpp + +string varInit(Variable v) { + if exists(v.getInitializer().getExpr().getValue()) + then result = v.getInitializer().getExpr().getValue().toString() + else result = "" +} + +from Variable v +select v.getFile().toString(), v.getName(), varInit(v) diff --git a/cpp/ql/test/library-tests/clang_c99_default/c89.c b/cpp/ql/test/library-tests/clang_c99_default/c89.c new file mode 100644 index 000000000000..8e7a5ccc274c --- /dev/null +++ b/cpp/ql/test/library-tests/clang_c99_default/c89.c @@ -0,0 +1,2 @@ +void g(int restrict); +// semmle-extractor-options: --clang -std=gnu89 diff --git a/cpp/ql/test/library-tests/clang_c99_default/c99.c b/cpp/ql/test/library-tests/clang_c99_default/c99.c new file mode 100644 index 000000000000..a367c102b1e1 --- /dev/null +++ b/cpp/ql/test/library-tests/clang_c99_default/c99.c @@ -0,0 +1,3 @@ +// NB: In C89, "restrict" is a normal identifier, whereas in C99 it is a keyword with special meaning. +int f(void* restrict, void* restrict); +// semmle-extractor-options: --clang diff --git a/cpp/ql/test/library-tests/clang_c99_default/clang_c99_default.expected b/cpp/ql/test/library-tests/clang_c99_default/clang_c99_default.expected new file mode 100644 index 000000000000..f6dd2a512fa2 --- /dev/null +++ b/cpp/ql/test/library-tests/clang_c99_default/clang_c99_default.expected @@ -0,0 +1,7 @@ +| c89.c:1:12:1:19 | restrict | restrict | +| c99.c:2:7:2:10 | p#0 | p#0 | +| c99.c:2:23:2:26 | p#1 | p#1 | +| file://:0:0:0:0 | fp_offset | fp_offset | +| file://:0:0:0:0 | gp_offset | gp_offset | +| file://:0:0:0:0 | overflow_arg_area | overflow_arg_area | +| file://:0:0:0:0 | reg_save_area | reg_save_area | diff --git a/cpp/ql/test/library-tests/clang_c99_default/clang_c99_default.ql b/cpp/ql/test/library-tests/clang_c99_default/clang_c99_default.ql new file mode 100644 index 000000000000..c7be0c02925f --- /dev/null +++ b/cpp/ql/test/library-tests/clang_c99_default/clang_c99_default.ql @@ -0,0 +1,3 @@ +import cpp + +from Variable v select v, v.getName() diff --git a/cpp/ql/test/library-tests/clang_cpp14_17/cpp14.cpp b/cpp/ql/test/library-tests/clang_cpp14_17/cpp14.cpp new file mode 100644 index 000000000000..c1c1f456ff2c --- /dev/null +++ b/cpp/ql/test/library-tests/clang_cpp14_17/cpp14.cpp @@ -0,0 +1,15 @@ +// semmle-extractor-options: -std=c++14 + + + +template +constexpr T pi = T(3.141592653589793238462643383); + +// Usual specialization rules apply: +template<> +constexpr const char* pi = "pi"; + +int main() { + auto lambda = [](auto x, auto y) {return x + y;}; + return lambda(1.0, 1.0) + lambda(4, 4) == 10.0; +} diff --git a/cpp/ql/test/library-tests/clang_cpp14_17/cpp17.cpp b/cpp/ql/test/library-tests/clang_cpp14_17/cpp17.cpp new file mode 100644 index 000000000000..ac43b540b627 --- /dev/null +++ b/cpp/ql/test/library-tests/clang_cpp14_17/cpp17.cpp @@ -0,0 +1,22 @@ +// semmle-extractor-options: -std=c++17 + +template +struct Pair { + Pair(T t, U u) {} +}; + +// TODO: accepted by GCC 7.1, rejected by the extractor +//template +//bool all(Args... args) { return (... && args); } +// +//bool b = all(true, true, true, false); + +namespace Foo::Bar { + static_assert(true); +} + +int main() { + static_assert(true); + // Pair p(5.0, false); // TODO: accepted by GCC 7.1, rejected by the extractor + return 0; +} diff --git a/cpp/ql/test/library-tests/clang_cpp14_17/test.expected b/cpp/ql/test/library-tests/clang_cpp14_17/test.expected new file mode 100644 index 000000000000..2a4f078a25fc --- /dev/null +++ b/cpp/ql/test/library-tests/clang_cpp14_17/test.expected @@ -0,0 +1 @@ +| 1 | diff --git a/cpp/ql/test/library-tests/clang_cpp14_17/test.ql b/cpp/ql/test/library-tests/clang_cpp14_17/test.ql new file mode 100644 index 000000000000..82198eaf87be --- /dev/null +++ b/cpp/ql/test/library-tests/clang_cpp14_17/test.ql @@ -0,0 +1 @@ +select 1 diff --git a/cpp/ql/test/library-tests/clang_ms/clang_ms.cpp b/cpp/ql/test/library-tests/clang_ms/clang_ms.cpp new file mode 100644 index 000000000000..e6ba13425522 --- /dev/null +++ b/cpp/ql/test/library-tests/clang_ms/clang_ms.cpp @@ -0,0 +1,18 @@ +__declspec(align(32)) struct mystruct { short myshort; }; + +int main(void) { + return 0ui32; +} + +void ms_asm (void) + __asm ("foo"); + +void gnu_asm() { + __asm nop +} + + + +// Test for CPP-184 +_Pragma("clang diagnostic push") +_Pragma("clang diagnostic pop") diff --git a/cpp/ql/test/library-tests/clang_ms/declspec.expected b/cpp/ql/test/library-tests/clang_ms/declspec.expected new file mode 100644 index 000000000000..7f93159792d8 --- /dev/null +++ b/cpp/ql/test/library-tests/clang_ms/declspec.expected @@ -0,0 +1 @@ +| clang_ms.cpp:1:12:1:16 | align | diff --git a/cpp/ql/test/library-tests/clang_ms/declspec.ql b/cpp/ql/test/library-tests/clang_ms/declspec.ql new file mode 100644 index 000000000000..06576211d947 --- /dev/null +++ b/cpp/ql/test/library-tests/clang_ms/declspec.ql @@ -0,0 +1,5 @@ +import cpp + +from Declspec ds +select ds + diff --git a/cpp/ql/test/library-tests/clang_ms/element.expected b/cpp/ql/test/library-tests/clang_ms/element.expected new file mode 100644 index 000000000000..763649f774f6 --- /dev/null +++ b/cpp/ql/test/library-tests/clang_ms/element.expected @@ -0,0 +1,138 @@ +| clang_ms.cpp:0:0:0:0 | clang_ms.cpp | +| clang_ms.cpp:1:12:1:16 | align | +| clang_ms.cpp:1:18:1:19 | 32 | +| clang_ms.cpp:1:30:1:30 | declaration of operator= | +| clang_ms.cpp:1:30:1:30 | declaration of operator= | +| clang_ms.cpp:1:30:1:30 | operator= | +| clang_ms.cpp:1:30:1:30 | operator= | +| clang_ms.cpp:1:30:1:37 | definition of mystruct | +| clang_ms.cpp:1:30:1:37 | mystruct | +| clang_ms.cpp:1:47:1:53 | definition of myshort | +| clang_ms.cpp:1:47:1:53 | myshort | +| clang_ms.cpp:3:5:3:8 | definition of main | +| clang_ms.cpp:3:5:3:8 | main | +| clang_ms.cpp:3:16:5:1 | { ... } | +| clang_ms.cpp:4:5:4:17 | return ... | +| clang_ms.cpp:4:12:4:16 | 0 | +| clang_ms.cpp:4:12:4:16 | (int)... | +| clang_ms.cpp:7:6:7:11 | declaration of ms_asm | +| clang_ms.cpp:7:6:7:11 | ms_asm | +| clang_ms.cpp:10:6:10:12 | definition of gnu_asm | +| clang_ms.cpp:10:6:10:12 | gnu_asm | +| clang_ms.cpp:10:16:12:1 | { ... } | +| clang_ms.cpp:11:5:11:13 | asm statement | +| clang_ms.cpp:12:1:12:1 | return ... | +| clang_ms.cpp:16:1:16:19 | // Test for CPP-184 | +| file://:0:0:0:0 | | +| file://:0:0:0:0 | (global namespace) | +| file://:0:0:0:0 | _Complex __float128 | +| file://:0:0:0:0 | _Complex double | +| file://:0:0:0:0 | _Complex float | +| file://:0:0:0:0 | _Complex long double | +| file://:0:0:0:0 | _Decimal32 | +| file://:0:0:0:0 | _Decimal64 | +| file://:0:0:0:0 | _Decimal128 | +| file://:0:0:0:0 | _Float32 | +| file://:0:0:0:0 | _Float32x | +| file://:0:0:0:0 | _Float64 | +| file://:0:0:0:0 | _Float64x | +| file://:0:0:0:0 | _Float128 | +| file://:0:0:0:0 | _Float128x | +| file://:0:0:0:0 | _Imaginary double | +| file://:0:0:0:0 | _Imaginary float | +| file://:0:0:0:0 | _Imaginary long double | +| file://:0:0:0:0 | __block | +| file://:0:0:0:0 | __float128 | +| file://:0:0:0:0 | __int128 | +| file://:0:0:0:0 | __interface | +| file://:0:0:0:0 | __ptr32 | +| file://:0:0:0:0 | __ptr64 | +| file://:0:0:0:0 | __sptr | +| file://:0:0:0:0 | __uptr | +| file://:0:0:0:0 | __va_list_tag | +| file://:0:0:0:0 | __va_list_tag & | +| file://:0:0:0:0 | abstract | +| file://:0:0:0:0 | atomic | +| file://:0:0:0:0 | auto | +| file://:0:0:0:0 | auto | +| file://:0:0:0:0 | bool | +| file://:0:0:0:0 | char | +| file://:0:0:0:0 | char16_t | +| file://:0:0:0:0 | char32_t | +| file://:0:0:0:0 | const | +| file://:0:0:0:0 | const mystruct | +| file://:0:0:0:0 | const mystruct & | +| file://:0:0:0:0 | declaration of 1st parameter | +| file://:0:0:0:0 | declaration of 1st parameter | +| file://:0:0:0:0 | decltype(nullptr) | +| file://:0:0:0:0 | definition of __va_list_tag | +| file://:0:0:0:0 | definition of fp_offset | +| file://:0:0:0:0 | definition of gp_offset | +| file://:0:0:0:0 | definition of overflow_arg_area | +| file://:0:0:0:0 | definition of reg_save_area | +| file://:0:0:0:0 | dllexport | +| file://:0:0:0:0 | dllimport | +| file://:0:0:0:0 | double | +| file://:0:0:0:0 | error | +| file://:0:0:0:0 | explicit | +| file://:0:0:0:0 | extern | +| file://:0:0:0:0 | far | +| file://:0:0:0:0 | float | +| file://:0:0:0:0 | forceinline | +| file://:0:0:0:0 | fp_offset | +| file://:0:0:0:0 | gp_offset | +| file://:0:0:0:0 | implicit_int | +| file://:0:0:0:0 | inline | +| file://:0:0:0:0 | int | +| file://:0:0:0:0 | long | +| file://:0:0:0:0 | long double | +| file://:0:0:0:0 | long long | +| file://:0:0:0:0 | microsoft_inline | +| file://:0:0:0:0 | mystruct & | +| file://:0:0:0:0 | mystruct && | +| file://:0:0:0:0 | naked | +| file://:0:0:0:0 | near | +| file://:0:0:0:0 | noalias | +| file://:0:0:0:0 | noinline | +| file://:0:0:0:0 | noreturn | +| file://:0:0:0:0 | nothrow | +| file://:0:0:0:0 | novtable | +| file://:0:0:0:0 | operator= | +| file://:0:0:0:0 | operator= | +| file://:0:0:0:0 | optional | +| file://:0:0:0:0 | overflow_arg_area | +| file://:0:0:0:0 | override | +| file://:0:0:0:0 | p#0 | +| file://:0:0:0:0 | p#0 | +| file://:0:0:0:0 | private | +| file://:0:0:0:0 | protected | +| file://:0:0:0:0 | public | +| file://:0:0:0:0 | pure | +| file://:0:0:0:0 | reg_save_area | +| file://:0:0:0:0 | register | +| file://:0:0:0:0 | restrict | +| file://:0:0:0:0 | sealed | +| file://:0:0:0:0 | selectany | +| file://:0:0:0:0 | short | +| file://:0:0:0:0 | signed __int128 | +| file://:0:0:0:0 | signed char | +| file://:0:0:0:0 | signed int | +| file://:0:0:0:0 | signed long | +| file://:0:0:0:0 | signed long long | +| file://:0:0:0:0 | signed short | +| file://:0:0:0:0 | static | +| file://:0:0:0:0 | thread | +| file://:0:0:0:0 | unaligned | +| file://:0:0:0:0 | unknown | +| file://:0:0:0:0 | unsigned __int128 | +| file://:0:0:0:0 | unsigned char | +| file://:0:0:0:0 | unsigned int | +| file://:0:0:0:0 | unsigned long | +| file://:0:0:0:0 | unsigned long long | +| file://:0:0:0:0 | unsigned short | +| file://:0:0:0:0 | varargs | +| file://:0:0:0:0 | virtual | +| file://:0:0:0:0 | void | +| file://:0:0:0:0 | void * | +| file://:0:0:0:0 | volatile | +| file://:0:0:0:0 | wchar_t | diff --git a/cpp/ql/test/library-tests/clang_ms/element.ql b/cpp/ql/test/library-tests/clang_ms/element.ql new file mode 100644 index 000000000000..8d2ce8f5e94f --- /dev/null +++ b/cpp/ql/test/library-tests/clang_ms/element.ql @@ -0,0 +1,5 @@ +import cpp + +from Element e +where not e instanceof Folder +select e diff --git a/cpp/ql/test/library-tests/clang_ms/options b/cpp/ql/test/library-tests/clang_ms/options new file mode 100644 index 000000000000..1f8eca1b8a68 --- /dev/null +++ b/cpp/ql/test/library-tests/clang_ms/options @@ -0,0 +1 @@ +extractor_flags: --edg --clang --edg --ms_extensions diff --git a/cpp/ql/test/library-tests/classes/base_classes/base_classes.cpp b/cpp/ql/test/library-tests/classes/base_classes/base_classes.cpp new file mode 100644 index 000000000000..48da325c7bd6 --- /dev/null +++ b/cpp/ql/test/library-tests/classes/base_classes/base_classes.cpp @@ -0,0 +1,18 @@ +struct empty { }; + +template +struct indirect { + typedef empty real; +}; + +template +struct S : indirect::real { +}; +/* +Currently 'indirect' isn't in the database; the base class is +simply 'empty'. We might want to also include 'indirect', with a +way to reach the unevaluated 'indirect::real'. +*/ + +S x; + diff --git a/cpp/ql/test/library-tests/classes/base_classes/base_classes.expected b/cpp/ql/test/library-tests/classes/base_classes/base_classes.expected new file mode 100644 index 000000000000..eb6db93c3bfa --- /dev/null +++ b/cpp/ql/test/library-tests/classes/base_classes/base_classes.expected @@ -0,0 +1,5 @@ +| base_classes.cpp:1:8:1:12 | empty | | +| base_classes.cpp:4:8:4:15 | indirect | | +| base_classes.cpp:9:8:9:8 | S | empty | +| base_classes.cpp:9:8:9:8 | S | empty | +| file://:0:0:0:0 | __va_list_tag | | diff --git a/cpp/ql/test/library-tests/classes/base_classes/base_classes.ql b/cpp/ql/test/library-tests/classes/base_classes/base_classes.ql new file mode 100644 index 000000000000..d9c228e768c7 --- /dev/null +++ b/cpp/ql/test/library-tests/classes/base_classes/base_classes.ql @@ -0,0 +1,7 @@ +import cpp + +from Class c, string s +where if exists(c.getABaseClass()) + then s = c.getABaseClass().toString() + else s = "" +select c, s diff --git a/cpp/ql/test/library-tests/classes/classes/Classes.ms.cpp b/cpp/ql/test/library-tests/classes/classes/Classes.ms.cpp new file mode 100644 index 000000000000..4dafda9752f8 --- /dev/null +++ b/cpp/ql/test/library-tests/classes/classes/Classes.ms.cpp @@ -0,0 +1,41 @@ +class A { + +}; + + +class B { + +}; + +class C: A, B { + +}; + +class D: C { + + class E; + +}; + +class D::E { + + class F; + + class G { + public: + /* Non-trivial constructor and destructor */ + G() { 0; } + ~G() { 0; } + }; + +}; + +class D::E::F: D::E::G { + /* Should have generated constructor and destructor, because of D::E::G */ + + static int m() { + D::E::F def; /* Should trigger creation of D::E::F's generated constructor and destructor. */ + } +}; + +// semmle-extractor-options: --microsoft diff --git a/cpp/ql/test/library-tests/classes/classes/Classes1.expected b/cpp/ql/test/library-tests/classes/classes/Classes1.expected new file mode 100644 index 000000000000..88d311604208 --- /dev/null +++ b/cpp/ql/test/library-tests/classes/classes/Classes1.expected @@ -0,0 +1,11 @@ +| Classes.ms.cpp:1:7:1:7 | A | ----- | +| Classes.ms.cpp:6:7:6:7 | B | ----- | +| Classes.ms.cpp:10:7:10:7 | C | ----- | +| Classes.ms.cpp:14:7:14:7 | D | ----- | +| Classes.ms.cpp:20:7:20:10 | E | ----- | +| Classes.ms.cpp:24:8:24:8 | G | ----- | +| Classes.ms.cpp:33:7:33:13 | F | ----- | +| classes.cpp:2:7:2:8 | C1 | final | +| classes.cpp:5:7:5:8 | C2 | final | +| more_classes.cpp:2:7:2:8 | D1 | ----- | +| more_classes.cpp:5:7:5:8 | D2 | final | diff --git a/cpp/ql/test/library-tests/classes/classes/Classes1.ql b/cpp/ql/test/library-tests/classes/classes/Classes1.ql new file mode 100644 index 000000000000..7ca760f9dcce --- /dev/null +++ b/cpp/ql/test/library-tests/classes/classes/Classes1.ql @@ -0,0 +1,7 @@ +import cpp + +from Class c, string isFinal +where c.getName() != "__va_list_tag" + and if c.isFinal() then isFinal = "final" else isFinal = "-----" +select c, isFinal + diff --git a/cpp/ql/test/library-tests/classes/classes/Classes2.expected b/cpp/ql/test/library-tests/classes/classes/Classes2.expected new file mode 100644 index 000000000000..6dfebaab3d66 --- /dev/null +++ b/cpp/ql/test/library-tests/classes/classes/Classes2.expected @@ -0,0 +1,3 @@ +| Classes.ms.cpp:20:7:20:10 | E | +| Classes.ms.cpp:24:8:24:8 | G | +| Classes.ms.cpp:33:7:33:13 | F | diff --git a/cpp/ql/test/library-tests/classes/classes/Classes2.ql b/cpp/ql/test/library-tests/classes/classes/Classes2.ql new file mode 100644 index 000000000000..f11a7b3375f8 --- /dev/null +++ b/cpp/ql/test/library-tests/classes/classes/Classes2.ql @@ -0,0 +1,5 @@ +import cpp + +from NestedClass c +select c + diff --git a/cpp/ql/test/library-tests/classes/classes/Classes3.expected b/cpp/ql/test/library-tests/classes/classes/Classes3.expected new file mode 100644 index 000000000000..fa19cef1d709 --- /dev/null +++ b/cpp/ql/test/library-tests/classes/classes/Classes3.expected @@ -0,0 +1 @@ +| Classes.ms.cpp:14:7:14:7 | D | Classes.ms.cpp:20:7:20:10 | E | Classes.ms.cpp:33:7:33:13 | F | diff --git a/cpp/ql/test/library-tests/classes/classes/Classes3.ql b/cpp/ql/test/library-tests/classes/classes/Classes3.ql new file mode 100644 index 000000000000..10e6f109d4fd --- /dev/null +++ b/cpp/ql/test/library-tests/classes/classes/Classes3.ql @@ -0,0 +1,9 @@ +import cpp + +from Class d, NestedClass e, NestedClass f +where not d instanceof NestedClass and d.isTopLevel() + and e.getDeclaringType() = d + and f.getDeclaringType() = e + and f.hasName("F") +select d, e, f + diff --git a/cpp/ql/test/library-tests/classes/classes/Classes4.expected b/cpp/ql/test/library-tests/classes/classes/Classes4.expected new file mode 100644 index 000000000000..bb52bcc5fd5b --- /dev/null +++ b/cpp/ql/test/library-tests/classes/classes/Classes4.expected @@ -0,0 +1 @@ +| Classes.ms.cpp:33:7:33:13 | F | Classes.ms.cpp:24:8:24:8 | G | diff --git a/cpp/ql/test/library-tests/classes/classes/Classes4.ql b/cpp/ql/test/library-tests/classes/classes/Classes4.ql new file mode 100644 index 000000000000..896ed1f15cf5 --- /dev/null +++ b/cpp/ql/test/library-tests/classes/classes/Classes4.ql @@ -0,0 +1,7 @@ +import cpp + +from NestedClass f, NestedClass g +where f.hasName("F") + and g.hasName("G") + and f.getABaseClass() = g +select f, g diff --git a/cpp/ql/test/library-tests/classes/classes/Classes5.expected b/cpp/ql/test/library-tests/classes/classes/Classes5.expected new file mode 100644 index 000000000000..f7fc4b1193f2 --- /dev/null +++ b/cpp/ql/test/library-tests/classes/classes/Classes5.expected @@ -0,0 +1 @@ +| Classes.ms.cpp:10:7:10:7 | C | diff --git a/cpp/ql/test/library-tests/classes/classes/Classes5.ql b/cpp/ql/test/library-tests/classes/classes/Classes5.ql new file mode 100644 index 000000000000..071aa1f82aee --- /dev/null +++ b/cpp/ql/test/library-tests/classes/classes/Classes5.ql @@ -0,0 +1,5 @@ +import cpp + +from Class c +where count(c.getABaseClass()) > 1 +select c diff --git a/cpp/ql/test/library-tests/classes/classes/Classes6.expected b/cpp/ql/test/library-tests/classes/classes/Classes6.expected new file mode 100644 index 000000000000..bb52bcc5fd5b --- /dev/null +++ b/cpp/ql/test/library-tests/classes/classes/Classes6.expected @@ -0,0 +1 @@ +| Classes.ms.cpp:33:7:33:13 | F | Classes.ms.cpp:24:8:24:8 | G | diff --git a/cpp/ql/test/library-tests/classes/classes/Classes6.ql b/cpp/ql/test/library-tests/classes/classes/Classes6.ql new file mode 100644 index 000000000000..2c016fa59552 --- /dev/null +++ b/cpp/ql/test/library-tests/classes/classes/Classes6.ql @@ -0,0 +1,6 @@ +import cpp + +from NestedClass f, NestedClass g +where g.getADerivedClass() = f + and g.getDeclaringType().getAMember() = f +select f, g diff --git a/cpp/ql/test/library-tests/classes/classes/Classes7.expected b/cpp/ql/test/library-tests/classes/classes/Classes7.expected new file mode 100644 index 000000000000..06288ef21b41 --- /dev/null +++ b/cpp/ql/test/library-tests/classes/classes/Classes7.expected @@ -0,0 +1 @@ +| Classes.ms.cpp:33:13:33:13 | F | Classes.ms.cpp:27:8:27:8 | G | diff --git a/cpp/ql/test/library-tests/classes/classes/Classes7.ql b/cpp/ql/test/library-tests/classes/classes/Classes7.ql new file mode 100644 index 000000000000..1a27224d287b --- /dev/null +++ b/cpp/ql/test/library-tests/classes/classes/Classes7.ql @@ -0,0 +1,8 @@ +import cpp + +from Constructor f, Constructor g +where + f.isCompilerGenerated() and + f.calls(g) and + not g.isCompilerGenerated() +select f, g diff --git a/cpp/ql/test/library-tests/classes/classes/Classes8.expected b/cpp/ql/test/library-tests/classes/classes/Classes8.expected new file mode 100644 index 000000000000..7a5db967b572 --- /dev/null +++ b/cpp/ql/test/library-tests/classes/classes/Classes8.expected @@ -0,0 +1 @@ +| Classes.ms.cpp:33:13:33:13 | ~F | Classes.ms.cpp:28:7:28:8 | ~G | diff --git a/cpp/ql/test/library-tests/classes/classes/Classes8.ql b/cpp/ql/test/library-tests/classes/classes/Classes8.ql new file mode 100644 index 000000000000..2d7d17eb80e0 --- /dev/null +++ b/cpp/ql/test/library-tests/classes/classes/Classes8.ql @@ -0,0 +1,8 @@ +import cpp + +from Destructor f, Destructor g +where + f.isCompilerGenerated() and + f.calls(g) and + not g.isCompilerGenerated() +select f, g diff --git a/cpp/ql/test/library-tests/classes/classes/MetricClasses1.expected b/cpp/ql/test/library-tests/classes/classes/MetricClasses1.expected new file mode 100644 index 000000000000..de1fbfe0c5d7 --- /dev/null +++ b/cpp/ql/test/library-tests/classes/classes/MetricClasses1.expected @@ -0,0 +1,11 @@ +| Classes.ms.cpp:1:7:1:7 | A | 0 | +| Classes.ms.cpp:6:7:6:7 | B | 0 | +| Classes.ms.cpp:10:7:10:7 | C | 0 | +| Classes.ms.cpp:14:7:14:7 | D | 0 | +| Classes.ms.cpp:20:7:20:10 | E | 1 | +| Classes.ms.cpp:24:8:24:8 | G | 2 | +| Classes.ms.cpp:33:7:33:13 | F | 2 | +| classes.cpp:2:7:2:8 | C1 | 0 | +| classes.cpp:5:7:5:8 | C2 | 0 | +| more_classes.cpp:2:7:2:8 | D1 | 0 | +| more_classes.cpp:5:7:5:8 | D2 | 0 | diff --git a/cpp/ql/test/library-tests/classes/classes/MetricClasses1.ql b/cpp/ql/test/library-tests/classes/classes/MetricClasses1.ql new file mode 100644 index 000000000000..70f8a5dfdd41 --- /dev/null +++ b/cpp/ql/test/library-tests/classes/classes/MetricClasses1.ql @@ -0,0 +1,6 @@ +import cpp + +from Class c +where c.getName() != "__va_list_tag" +select c, c.getMetrics().getNestingLevel() + diff --git a/cpp/ql/test/library-tests/classes/classes/MetricClasses2.expected b/cpp/ql/test/library-tests/classes/classes/MetricClasses2.expected new file mode 100644 index 000000000000..e63b12de2e08 --- /dev/null +++ b/cpp/ql/test/library-tests/classes/classes/MetricClasses2.expected @@ -0,0 +1,11 @@ +| Classes.ms.cpp:1:7:1:7 | A | 0 | +| Classes.ms.cpp:6:7:6:7 | B | 0 | +| Classes.ms.cpp:10:7:10:7 | C | 1 | +| Classes.ms.cpp:14:7:14:7 | D | 2 | +| Classes.ms.cpp:20:7:20:10 | E | 0 | +| Classes.ms.cpp:24:8:24:8 | G | 0 | +| Classes.ms.cpp:33:7:33:13 | F | 1 | +| classes.cpp:2:7:2:8 | C1 | 0 | +| classes.cpp:5:7:5:8 | C2 | 0 | +| more_classes.cpp:2:7:2:8 | D1 | 0 | +| more_classes.cpp:5:7:5:8 | D2 | 0 | diff --git a/cpp/ql/test/library-tests/classes/classes/MetricClasses2.ql b/cpp/ql/test/library-tests/classes/classes/MetricClasses2.ql new file mode 100644 index 000000000000..0753bab39b6f --- /dev/null +++ b/cpp/ql/test/library-tests/classes/classes/MetricClasses2.ql @@ -0,0 +1,6 @@ +import cpp + +from Class c +where c.getName() != "__va_list_tag" +select c, c.getMetrics().getInheritanceDepth() + diff --git a/cpp/ql/test/library-tests/classes/classes/classes.cpp b/cpp/ql/test/library-tests/classes/classes/classes.cpp new file mode 100644 index 000000000000..c4221f021ba7 --- /dev/null +++ b/cpp/ql/test/library-tests/classes/classes/classes.cpp @@ -0,0 +1,7 @@ +// semmle-extractor-options: --gnu_version 40700 +class C1 final { +}; + +class C2 __final { +}; + diff --git a/cpp/ql/test/library-tests/classes/classes/more_classes.cpp b/cpp/ql/test/library-tests/classes/classes/more_classes.cpp new file mode 100644 index 000000000000..04cc94cf3441 --- /dev/null +++ b/cpp/ql/test/library-tests/classes/classes/more_classes.cpp @@ -0,0 +1,7 @@ +// semmle-extractor-options: --edg --no_c++11 --gnu_version 40700 +class D1 final { +}; + +class D2 __final { +}; + diff --git a/cpp/ql/test/library-tests/classes/defcon/defcon.cpp b/cpp/ql/test/library-tests/classes/defcon/defcon.cpp new file mode 100644 index 000000000000..6be06cb17e5d --- /dev/null +++ b/cpp/ql/test/library-tests/classes/defcon/defcon.cpp @@ -0,0 +1,15 @@ +class A { + A(); +}; + +class B { + B(int x); +}; + +class C { + C(int x = 0); +}; + +class D { + D(...); +}; diff --git a/cpp/ql/test/library-tests/classes/defcon/defcon.expected b/cpp/ql/test/library-tests/classes/defcon/defcon.expected new file mode 100644 index 000000000000..0a363773cc31 --- /dev/null +++ b/cpp/ql/test/library-tests/classes/defcon/defcon.expected @@ -0,0 +1,3 @@ +| A | +| C | +| D | diff --git a/cpp/ql/test/library-tests/classes/defcon/defcon.ql b/cpp/ql/test/library-tests/classes/defcon/defcon.ql new file mode 100644 index 000000000000..32a02897b3ff --- /dev/null +++ b/cpp/ql/test/library-tests/classes/defcon/defcon.ql @@ -0,0 +1,5 @@ +import cpp + +from Constructor c +where c.isDefault() +select c.getName() diff --git a/cpp/ql/test/library-tests/classes/derivations/derivations/common.h b/cpp/ql/test/library-tests/classes/derivations/derivations/common.h new file mode 100644 index 000000000000..21b7a81de2b2 --- /dev/null +++ b/cpp/ql/test/library-tests/classes/derivations/derivations/common.h @@ -0,0 +1,5 @@ +struct A {}; +struct B : A {}; +struct C : VISIBILITY A {}; +struct D : BASE {}; +struct DERIVED : A {}; diff --git a/cpp/ql/test/library-tests/classes/derivations/derivations/derivations.expected b/cpp/ql/test/library-tests/classes/derivations/derivations/derivations.expected new file mode 100644 index 000000000000..86824d5fec54 --- /dev/null +++ b/cpp/ql/test/library-tests/classes/derivations/derivations/derivations.expected @@ -0,0 +1,7 @@ +| common.h:2:12:2:12 | derivation | 2 | common.h:2:8:2:8 | B | file://:0:0:0:0 | public | common.h:1:8:1:8 | A | +| common.h:3:12:3:23 | derivation | 3 | common.h:3:8:3:8 | C | file://:0:0:0:0 | private | common.h:1:8:1:8 | A | +| common.h:3:12:3:23 | derivation | 3 | common.h:3:8:3:8 | C | file://:0:0:0:0 | public | common.h:1:8:1:8 | A | +| common.h:4:12:4:15 | derivation | 4 | common.h:4:8:4:8 | D | file://:0:0:0:0 | public | common.h:1:8:1:8 | A | +| common.h:4:12:4:15 | derivation | 4 | common.h:4:8:4:8 | D | file://:0:0:0:0 | public | common.h:2:8:2:8 | B | +| common.h:5:18:5:18 | derivation | 5 | common.h:5:8:5:14 | E | file://:0:0:0:0 | public | common.h:1:8:1:8 | A | +| common.h:5:18:5:18 | derivation | 5 | common.h:5:8:5:14 | F | file://:0:0:0:0 | public | common.h:1:8:1:8 | A | diff --git a/cpp/ql/test/library-tests/classes/derivations/derivations/derivations.ql b/cpp/ql/test/library-tests/classes/derivations/derivations/derivations.ql new file mode 100644 index 000000000000..1ae00d09f8ca --- /dev/null +++ b/cpp/ql/test/library-tests/classes/derivations/derivations/derivations.ql @@ -0,0 +1,4 @@ +import cpp + +from ClassDerivation cd +select cd, cd.getLocation().getStartLine(), cd.getDerivedClass(), cd.getASpecifier(), cd.getBaseClass() diff --git a/cpp/ql/test/library-tests/classes/derivations/derivations/one.cpp b/cpp/ql/test/library-tests/classes/derivations/derivations/one.cpp new file mode 100644 index 000000000000..352864d526f5 --- /dev/null +++ b/cpp/ql/test/library-tests/classes/derivations/derivations/one.cpp @@ -0,0 +1,4 @@ +#define VISIBILITY public +#define BASE A +#define DERIVED E +#include "common.h" diff --git a/cpp/ql/test/library-tests/classes/derivations/derivations/two.cpp b/cpp/ql/test/library-tests/classes/derivations/derivations/two.cpp new file mode 100644 index 000000000000..8e0ae128a813 --- /dev/null +++ b/cpp/ql/test/library-tests/classes/derivations/derivations/two.cpp @@ -0,0 +1,4 @@ +#define VISIBILITY private +#define BASE B +#define DERIVED F +#include "common.h" diff --git a/cpp/ql/test/library-tests/classes/derivations/offsets/field_offset_in.expected b/cpp/ql/test/library-tests/classes/derivations/offsets/field_offset_in.expected new file mode 100644 index 000000000000..bd38a691d354 --- /dev/null +++ b/cpp/ql/test/library-tests/classes/derivations/offsets/field_offset_in.expected @@ -0,0 +1,86 @@ +| Base | Base | b1 | 0 | +| Base | Base | b1f | 4 | +| Base2 | Base2 | b2 | 0 | +| Base2 | Base2 | b2f | 4 | +| Bottom | Base | b1 | 40 | +| Bottom | Base | b1f | 44 | +| Bottom | Bottom | b1 | 32 | +| Bottom | Bottom | b1f | 36 | +| Bottom | Left | l1 | 8 | +| Bottom | Left | l1f | 12 | +| Bottom | Right | r1 | 24 | +| Bottom | Right | r1f | 28 | +| DeepInheritance | Base | b1 | 0 | +| DeepInheritance | Base | b1 | 24 | +| DeepInheritance | Base | b1f | 4 | +| DeepInheritance | Base | b1f | 28 | +| DeepInheritance | Base2 | b2 | 8 | +| DeepInheritance | Base2 | b2f | 12 | +| DeepInheritance | DeepInheritance | di1 | 40 | +| DeepInheritance | DeepInheritance | di1f | 44 | +| DeepInheritance | MultipleInheritance | mi1 | 16 | +| DeepInheritance | MultipleInheritance | mi1f | 20 | +| DeepInheritance | SingleInheritance | si1 | 32 | +| DeepInheritance | SingleInheritance | si1f | 36 | +| DeepSingleInheritance | Base | b1 | 0 | +| DeepSingleInheritance | Base | b1f | 4 | +| DeepSingleInheritance | DeepSingleInheritance | dsi1 | 16 | +| DeepSingleInheritance | DeepSingleInheritance | dsi1f | 20 | +| DeepSingleInheritance | SingleInheritance | si1 | 8 | +| DeepSingleInheritance | SingleInheritance | si1f | 12 | +| EffectivelyVirtual | Base | b1 | 8 | +| EffectivelyVirtual | Base | b1 | 40 | +| EffectivelyVirtual | Base | b1f | 12 | +| EffectivelyVirtual | Base | b1f | 44 | +| EffectivelyVirtual | Base2 | b2 | 16 | +| EffectivelyVirtual | Base2 | b2f | 20 | +| EffectivelyVirtual | EffectivelyVirtual | ev1 | 32 | +| EffectivelyVirtual | EffectivelyVirtual | ev1f | 36 | +| EffectivelyVirtual | MultipleInheritance | mi1 | 24 | +| EffectivelyVirtual | MultipleInheritance | mi1f | 28 | +| EffectivelyVirtual | SingleInheritance | si1 | 48 | +| EffectivelyVirtual | SingleInheritance | si1f | 52 | +| InheritsVTable | InheritsVTable | iv1 | 16 | +| InheritsVTable | InheritsVTable | iv1f | 20 | +| InheritsVTable | PolymorphicBase | pb1 | 8 | +| InheritsVTable | PolymorphicBase | pb1f | 12 | +| IntroducesVTable | Base | b1 | 8 | +| IntroducesVTable | Base | b1f | 12 | +| IntroducesVTable | IntroducesVTable | iv2 | 16 | +| IntroducesVTable | IntroducesVTable | iv2f | 20 | +| Left | Base | b1 | 16 | +| Left | Base | b1f | 20 | +| Left | Left | l1 | 8 | +| Left | Left | l1f | 12 | +| MultipleInheritance | Base | b1 | 0 | +| MultipleInheritance | Base | b1f | 4 | +| MultipleInheritance | Base2 | b2 | 8 | +| MultipleInheritance | Base2 | b2f | 12 | +| MultipleInheritance | MultipleInheritance | mi1 | 16 | +| MultipleInheritance | MultipleInheritance | mi1f | 20 | +| PolymorphicBase | PolymorphicBase | pb1 | 8 | +| PolymorphicBase | PolymorphicBase | pb1f | 12 | +| Right | Base | b1 | 16 | +| Right | Base | b1f | 20 | +| Right | Right | r1 | 8 | +| Right | Right | r1f | 12 | +| SingleInheritance | Base | b1 | 0 | +| SingleInheritance | Base | b1f | 4 | +| SingleInheritance | SingleInheritance | si1 | 8 | +| SingleInheritance | SingleInheritance | si1f | 12 | +| VirtualInheritance1 | Base | b1 | 16 | +| VirtualInheritance1 | Base | b1f | 20 | +| VirtualInheritance1 | VirtualInheritance1 | vi1 | 8 | +| VirtualInheritance1 | VirtualInheritance1 | vi1f | 12 | +| VirtualInheritance2 | Base | b1 | 24 | +| VirtualInheritance2 | Base | b1f | 28 | +| VirtualInheritance2 | Base2 | b2 | 32 | +| VirtualInheritance2 | Base2 | b2f | 36 | +| VirtualInheritance2 | VirtualInheritance1 | vi1 | 8 | +| VirtualInheritance2 | VirtualInheritance1 | vi1f | 12 | +| VirtualInheritance2 | VirtualInheritance2 | vi2 | 16 | +| VirtualInheritance2 | VirtualInheritance2 | vi2f | 20 | +| __va_list_tag | __va_list_tag | fp_offset | 4 | +| __va_list_tag | __va_list_tag | gp_offset | 0 | +| __va_list_tag | __va_list_tag | overflow_arg_area | 8 | +| __va_list_tag | __va_list_tag | reg_save_area | 16 | diff --git a/cpp/ql/test/library-tests/classes/derivations/offsets/field_offset_in.ql b/cpp/ql/test/library-tests/classes/derivations/offsets/field_offset_in.ql new file mode 100644 index 000000000000..4be54d1f408d --- /dev/null +++ b/cpp/ql/test/library-tests/classes/derivations/offsets/field_offset_in.ql @@ -0,0 +1,5 @@ +import cpp + +from Class derived, Field field, int offset +where field.getAByteOffsetIn(derived) = offset +select derived.toString(), field.getDeclaringType().toString(), field.toString(), offset diff --git a/cpp/ql/test/library-tests/classes/derivations/offsets/offset_in.expected b/cpp/ql/test/library-tests/classes/derivations/offsets/offset_in.expected new file mode 100644 index 000000000000..0c5e1ce681ab --- /dev/null +++ b/cpp/ql/test/library-tests/classes/derivations/offsets/offset_in.expected @@ -0,0 +1,44 @@ +| Base | Base | 0 | +| Base2 | Base2 | 0 | +| Bottom | Base | 40 | +| Bottom | Bottom | 0 | +| Bottom | Left | 0 | +| Bottom | Right | 16 | +| DeepInheritance | Base | 0 | +| DeepInheritance | Base | 24 | +| DeepInheritance | Base2 | 8 | +| DeepInheritance | DeepInheritance | 0 | +| DeepInheritance | MultipleInheritance | 0 | +| DeepInheritance | SingleInheritance | 24 | +| DeepSingleInheritance | Base | 0 | +| DeepSingleInheritance | DeepSingleInheritance | 0 | +| DeepSingleInheritance | SingleInheritance | 0 | +| EffectivelyVirtual | Base | 8 | +| EffectivelyVirtual | Base | 40 | +| EffectivelyVirtual | Base2 | 16 | +| EffectivelyVirtual | EffectivelyVirtual | 0 | +| EffectivelyVirtual | MultipleInheritance | 8 | +| EffectivelyVirtual | SingleInheritance | 40 | +| Incomplete | Incomplete | 0 | +| InheritsVTable | InheritsVTable | 0 | +| InheritsVTable | PolymorphicBase | 0 | +| IntroducesVTable | Base | 8 | +| IntroducesVTable | IntroducesVTable | 0 | +| Left | Base | 16 | +| Left | Left | 0 | +| MultipleInheritance | Base | 0 | +| MultipleInheritance | Base2 | 8 | +| MultipleInheritance | MultipleInheritance | 0 | +| PolymorphicBase | PolymorphicBase | 0 | +| Right | Base | 16 | +| Right | Right | 0 | +| SingleInheritance | Base | 0 | +| SingleInheritance | SingleInheritance | 0 | +| TemplateClass | TemplateClass | 0 | +| VirtualInheritance1 | Base | 16 | +| VirtualInheritance1 | VirtualInheritance1 | 0 | +| VirtualInheritance2 | Base | 24 | +| VirtualInheritance2 | Base2 | 32 | +| VirtualInheritance2 | VirtualInheritance1 | 0 | +| VirtualInheritance2 | VirtualInheritance2 | 0 | +| __va_list_tag | __va_list_tag | 0 | diff --git a/cpp/ql/test/library-tests/classes/derivations/offsets/offset_in.ql b/cpp/ql/test/library-tests/classes/derivations/offsets/offset_in.ql new file mode 100644 index 000000000000..51ebeb6d3b38 --- /dev/null +++ b/cpp/ql/test/library-tests/classes/derivations/offsets/offset_in.ql @@ -0,0 +1,5 @@ +import cpp + +from Class derived, Class base, int offset +where derived.getABaseClassByteOffset(base) = offset +select derived.toString(), base.toString(), offset diff --git a/cpp/ql/test/library-tests/classes/derivations/offsets/offsets.cpp b/cpp/ql/test/library-tests/classes/derivations/offsets/offsets.cpp new file mode 100644 index 000000000000..fed096f45dfd --- /dev/null +++ b/cpp/ql/test/library-tests/classes/derivations/offsets/offsets.cpp @@ -0,0 +1,84 @@ +struct Base { + int b1; + float b1f; +}; + +struct SingleInheritance : Base { + int si1; + float si1f; +}; + +struct Base2 { + int b2; + float b2f; +}; + +struct MultipleInheritance : Base, Base2 { + int mi1; + float mi1f; +}; + +struct DeepInheritance : MultipleInheritance, SingleInheritance { + int di1; + float di1f; +}; + +struct VirtualInheritance1 : virtual Base { + int vi1; + float vi1f; +}; + +struct VirtualInheritance2 : VirtualInheritance1, virtual Base, virtual Base2 { + int vi2; + float vi2f; +}; + +struct EffectivelyVirtual : virtual SingleInheritance, MultipleInheritance { + int ev1; + float ev1f; +}; + +struct PolymorphicBase { + virtual ~PolymorphicBase(); + int pb1; + float pb1f; +}; + +struct InheritsVTable : PolymorphicBase { + int iv1; + float iv1f; +}; + +struct IntroducesVTable : Base { + virtual ~IntroducesVTable(); + int iv2; + float iv2f; +}; + +struct Left : virtual Base { + int l1; + float l1f; +}; + +struct Right : virtual Base { + int r1; + float r1f; +}; + +struct Bottom : Left, Right { + int b1; + float b1f; +}; + +struct DeepSingleInheritance : SingleInheritance { + int dsi1; + float dsi1f; +}; + +struct Incomplete; +Incomplete* p; + +template +struct TemplateClass : Base +{ +}; diff --git a/cpp/ql/test/library-tests/classes/derivations/offsets/offsets.expected b/cpp/ql/test/library-tests/classes/derivations/offsets/offsets.expected new file mode 100644 index 000000000000..62d0a1d378c7 --- /dev/null +++ b/cpp/ql/test/library-tests/classes/derivations/offsets/offsets.expected @@ -0,0 +1,12 @@ +| 6 | SingleInheritance | Base | 0 | +| 16 | MultipleInheritance | Base | 0 | +| 16 | MultipleInheritance | Base2 | 8 | +| 21 | DeepInheritance | MultipleInheritance | 0 | +| 21 | DeepInheritance | SingleInheritance | 24 | +| 31 | VirtualInheritance2 | VirtualInheritance1 | 0 | +| 36 | EffectivelyVirtual | MultipleInheritance | 8 | +| 47 | InheritsVTable | PolymorphicBase | 0 | +| 52 | IntroducesVTable | Base | 8 | +| 68 | Bottom | Left | 0 | +| 68 | Bottom | Right | 16 | +| 73 | DeepSingleInheritance | SingleInheritance | 0 | diff --git a/cpp/ql/test/library-tests/classes/derivations/offsets/offsets.ql b/cpp/ql/test/library-tests/classes/derivations/offsets/offsets.ql new file mode 100644 index 000000000000..ba15b608e724 --- /dev/null +++ b/cpp/ql/test/library-tests/classes/derivations/offsets/offsets.ql @@ -0,0 +1,4 @@ +import cpp + +from ClassDerivation cd +select cd.getLocation().getStartLine(), cd.getDerivedClass().toString(), cd.getBaseClass().toString(), cd.getByteOffset() diff --git a/cpp/ql/test/library-tests/classes/derivations/offsets/vboffsets.expected b/cpp/ql/test/library-tests/classes/derivations/offsets/vboffsets.expected new file mode 100644 index 000000000000..40c8a9b4b128 --- /dev/null +++ b/cpp/ql/test/library-tests/classes/derivations/offsets/vboffsets.expected @@ -0,0 +1,7 @@ +| Bottom | Base | 40 | +| EffectivelyVirtual | SingleInheritance | 40 | +| Left | Base | 16 | +| Right | Base | 16 | +| VirtualInheritance1 | Base | 16 | +| VirtualInheritance2 | Base | 24 | +| VirtualInheritance2 | Base2 | 32 | diff --git a/cpp/ql/test/library-tests/classes/derivations/offsets/vboffsets.ql b/cpp/ql/test/library-tests/classes/derivations/offsets/vboffsets.ql new file mode 100644 index 000000000000..aca23d1c90ab --- /dev/null +++ b/cpp/ql/test/library-tests/classes/derivations/offsets/vboffsets.ql @@ -0,0 +1,5 @@ +import cpp + +from Class derived, Class base, int offset +where derived.getVirtualBaseClassByteOffset(base) = offset +select derived.toString(), base.toString(), offset diff --git a/cpp/ql/test/library-tests/classes/structlikeclass/StructLikeClass.cpp b/cpp/ql/test/library-tests/classes/structlikeclass/StructLikeClass.cpp new file mode 100644 index 000000000000..308ee6ca2a6a --- /dev/null +++ b/cpp/ql/test/library-tests/classes/structlikeclass/StructLikeClass.cpp @@ -0,0 +1,40 @@ + +struct Struct +{ + int get_x() {return x;} + int get_y() {return y;} + void set_x(int _x) {x = _x;} + void set_y(int _y) {y = _y;} + void something_else() {}; + + int x, y; +}; + +class StructLikeClass +{ +public: + StructLikeClass() : x(0), y(0) {} + + int get_x() {return x;} + int get_y() {return y;} + void set_x(int _x) {x = _x;} + void set_y(int _y) {y = _y;} + +private: + int x, y; +}; + +class NotStructLikeClass +{ +public: + NotStructLikeClass() : x(0), y(0) {} + + int get_x() {return x;} + int get_y() {return y;} + void set_x(int _x) {x = _x;} + void set_y(int _y) {y = _y;} + void something_else() {}; + +private: + int x, y; +}; diff --git a/cpp/ql/test/library-tests/classes/structlikeclass/StructLikeClass.expected b/cpp/ql/test/library-tests/classes/structlikeclass/StructLikeClass.expected new file mode 100644 index 000000000000..4e05a43bedde --- /dev/null +++ b/cpp/ql/test/library-tests/classes/structlikeclass/StructLikeClass.expected @@ -0,0 +1,8 @@ +| StructLikeClass.cpp:2:8:2:13 | Struct | getAGetter | StructLikeClass.cpp:4:6:4:10 | get_x | StructLikeClass.cpp:10:6:10:6 | x | +| StructLikeClass.cpp:2:8:2:13 | Struct | getAGetter | StructLikeClass.cpp:5:6:5:10 | get_y | StructLikeClass.cpp:10:9:10:9 | y | +| StructLikeClass.cpp:2:8:2:13 | Struct | getASetter | StructLikeClass.cpp:6:7:6:11 | set_x | StructLikeClass.cpp:10:6:10:6 | x | +| StructLikeClass.cpp:2:8:2:13 | Struct | getASetter | StructLikeClass.cpp:7:7:7:11 | set_y | StructLikeClass.cpp:10:9:10:9 | y | +| StructLikeClass.cpp:13:7:13:21 | StructLikeClass | getAGetter | StructLikeClass.cpp:18:6:18:10 | get_x | StructLikeClass.cpp:24:6:24:6 | x | +| StructLikeClass.cpp:13:7:13:21 | StructLikeClass | getAGetter | StructLikeClass.cpp:19:6:19:10 | get_y | StructLikeClass.cpp:24:9:24:9 | y | +| StructLikeClass.cpp:13:7:13:21 | StructLikeClass | getASetter | StructLikeClass.cpp:20:7:20:11 | set_x | StructLikeClass.cpp:24:6:24:6 | x | +| StructLikeClass.cpp:13:7:13:21 | StructLikeClass | getASetter | StructLikeClass.cpp:21:7:21:11 | set_y | StructLikeClass.cpp:24:9:24:9 | y | diff --git a/cpp/ql/test/library-tests/classes/structlikeclass/StructLikeClass.ql b/cpp/ql/test/library-tests/classes/structlikeclass/StructLikeClass.ql new file mode 100644 index 000000000000..27b04c5d04de --- /dev/null +++ b/cpp/ql/test/library-tests/classes/structlikeclass/StructLikeClass.ql @@ -0,0 +1,12 @@ +import cpp + +from StructLikeClass slc, string relation, Function f, Variable v +where + ( + slc.getASetter(v) = f and + relation = "getASetter" + ) or ( + slc.getAGetter(v) = f and + relation = "getAGetter" + ) +select slc, relation, f, v diff --git a/cpp/ql/test/library-tests/classes/variadic/expr.expected b/cpp/ql/test/library-tests/classes/variadic/expr.expected new file mode 100644 index 000000000000..acc511a45fa8 --- /dev/null +++ b/cpp/ql/test/library-tests/classes/variadic/expr.expected @@ -0,0 +1,6 @@ +| file://:0:0:0:0 | 0 | +| file://:0:0:0:0 | this | +| test.cpp:4:9:4:9 | call to f | +| test.cpp:4:9:4:9 | f | +| test.cpp:4:9:4:11 | call to expression | +| test.cpp:10:5:10:11 | call to Foo | diff --git a/cpp/ql/test/library-tests/classes/variadic/expr.ql b/cpp/ql/test/library-tests/classes/variadic/expr.ql new file mode 100644 index 000000000000..3c2a3269e72e --- /dev/null +++ b/cpp/ql/test/library-tests/classes/variadic/expr.ql @@ -0,0 +1,5 @@ +import cpp + +from Expr e +select e + diff --git a/cpp/ql/test/library-tests/classes/variadic/test.cpp b/cpp/ql/test/library-tests/classes/variadic/test.cpp new file mode 100644 index 000000000000..451005a91bbe --- /dev/null +++ b/cpp/ql/test/library-tests/classes/variadic/test.cpp @@ -0,0 +1,12 @@ +template +struct Foo { + Foo() { + f(); + } + void f(Xs... xs) {} +}; + +int main() { + Foo<>(); +} + diff --git a/cpp/ql/test/library-tests/comments/binding/a.c b/cpp/ql/test/library-tests/comments/binding/a.c new file mode 100644 index 000000000000..376f0a8da697 --- /dev/null +++ b/cpp/ql/test/library-tests/comments/binding/a.c @@ -0,0 +1,4 @@ + +#include "h.h" +#include "cb.h" + diff --git a/cpp/ql/test/library-tests/comments/binding/b.c b/cpp/ql/test/library-tests/comments/binding/b.c new file mode 100644 index 000000000000..e78d1483f87f --- /dev/null +++ b/cpp/ql/test/library-tests/comments/binding/b.c @@ -0,0 +1,4 @@ + +#include "i.h" +#include "cb.h" + diff --git a/cpp/ql/test/library-tests/comments/binding/cb.h b/cpp/ql/test/library-tests/comments/binding/cb.h new file mode 100644 index 000000000000..7c1073ef882c --- /dev/null +++ b/cpp/ql/test/library-tests/comments/binding/cb.h @@ -0,0 +1,13 @@ + +/* Foo */ + +#include "h.h" + +typedef int foo; + +/* Bar */ + +#include "i.h" + +typedef int bar; + diff --git a/cpp/ql/test/library-tests/comments/binding/commentBinding.expected b/cpp/ql/test/library-tests/comments/binding/commentBinding.expected new file mode 100644 index 000000000000..be0290274f06 --- /dev/null +++ b/cpp/ql/test/library-tests/comments/binding/commentBinding.expected @@ -0,0 +1,11 @@ +| cb.h:2:1:2:9 | /* Foo */ | | +| cb.h:8:1:8:9 | /* Bar */ | | +| macros.c:2:1:2:16 | // On define MF1 | #define MF1 foo | +| macros.c:6:1:6:16 | // On define MI1 | #define MI1 foo | +| macros.c:11:5:11:21 | // On define MF2I | #define MF2I foo | +| multi.c:2:1:2:19 | // Multi 1, 2 and 3 | declaration of multi1 | +| multi.c:2:1:2:19 | // Multi 1, 2 and 3 | declaration of multi2 | +| multi.c:2:1:2:19 | // Multi 1, 2 and 3 | declaration of multi3 | +| multi.c:5:27:5:36 | // Multi 3 | declaration of multi3 | +| templates.cpp:3:3:3:8 | // Foo | declaration of foo | +| templates.cpp:7:3:7:8 | // Bar | definition of bar | diff --git a/cpp/ql/test/library-tests/comments/binding/commentBinding.ql b/cpp/ql/test/library-tests/comments/binding/commentBinding.ql new file mode 100644 index 000000000000..79ec5435735b --- /dev/null +++ b/cpp/ql/test/library-tests/comments/binding/commentBinding.ql @@ -0,0 +1,8 @@ +import cpp + +from Comment c, string s +where if exists(c.getCommentedElement()) + then s = c.getCommentedElement().toString() + else s = "" +select c, s + diff --git a/cpp/ql/test/library-tests/comments/binding/h.h b/cpp/ql/test/library-tests/comments/binding/h.h new file mode 100644 index 000000000000..2946de559af0 --- /dev/null +++ b/cpp/ql/test/library-tests/comments/binding/h.h @@ -0,0 +1,9 @@ + +#ifndef __H_H__ +#define __H_H__ + +extern int i; +extern int j; + +#endif + diff --git a/cpp/ql/test/library-tests/comments/binding/i.h b/cpp/ql/test/library-tests/comments/binding/i.h new file mode 100644 index 000000000000..c0d56640a7cc --- /dev/null +++ b/cpp/ql/test/library-tests/comments/binding/i.h @@ -0,0 +1,9 @@ + +#ifndef __I_H__ +#define __I_H__ + +extern int i; +extern int j; + +#endif + diff --git a/cpp/ql/test/library-tests/comments/binding/macros.c b/cpp/ql/test/library-tests/comments/binding/macros.c new file mode 100644 index 000000000000..c988fed92581 --- /dev/null +++ b/cpp/ql/test/library-tests/comments/binding/macros.c @@ -0,0 +1,15 @@ + +// On define MF1 +#define MF1 foo +void mf1(void); + +// On define MI1 +#define MI1 foo +int mi1; + +void mf2(void) { + // On define MF2I + #define MF2I foo + int mf2i; +} + diff --git a/cpp/ql/test/library-tests/comments/binding/multi.c b/cpp/ql/test/library-tests/comments/binding/multi.c new file mode 100644 index 000000000000..4f71d7e2f042 --- /dev/null +++ b/cpp/ql/test/library-tests/comments/binding/multi.c @@ -0,0 +1,9 @@ + +// Multi 1, 2 and 3 +static void multi1(void); +static void multi2(void); +static void multi3(void); // Multi 3 +static void multi4(void); +static void multi5(void); +static void multi6(void); + diff --git a/cpp/ql/test/library-tests/comments/binding/templates.cpp b/cpp/ql/test/library-tests/comments/binding/templates.cpp new file mode 100644 index 000000000000..2c76db6a915f --- /dev/null +++ b/cpp/ql/test/library-tests/comments/binding/templates.cpp @@ -0,0 +1,12 @@ +template +class Cl { + // Foo + void foo(void) { + } + + // Bar + template + void bar(void) { + } +}; + diff --git a/cpp/ql/test/library-tests/comments/comments/comments.c b/cpp/ql/test/library-tests/comments/comments/comments.c new file mode 100644 index 000000000000..61022bfd43e7 --- /dev/null +++ b/cpp/ql/test/library-tests/comments/comments/comments.c @@ -0,0 +1,44 @@ + +int f /* on secondary f */ (char x /* on secondary x */); + +extern int i /* on secondary i */; + +int f /* on f */ (char x /* on x */) { + int j /* on j */; + + return 5; +} + +int i /* on i */; + +void f2(void) { + extern f2i; // On f2i +} + +void f3a(void) { + static void (*fp)(void); +} + +// On f3b +static void f3b(void); + +// On define FOO +#define FOO bar + +// On undef FOO +#undef FOO + +// On struct my_struct +struct my_struct { + int i; +}; + +// On union my_union +union my_union { + int i; + char c; +}; + +// This is a legal alternative way to achieve a \ +multi-line comment on j. +int j; diff --git a/cpp/ql/test/library-tests/comments/comments/comments.expected b/cpp/ql/test/library-tests/comments/comments/comments.expected new file mode 100644 index 000000000000..16b14be4c9e8 --- /dev/null +++ b/cpp/ql/test/library-tests/comments/comments/comments.expected @@ -0,0 +1,35 @@ +| comments.c:2:7:2:26 | /* on secondary f */ | 1 | comments.c:2:5:2:5 | declaration of f | +| comments.c:2:36:2:55 | /* on secondary x */ | 1 | comments.c:4:12:4:12 | declaration of i | +| comments.c:4:14:4:33 | /* on secondary i */ | 1 | comments.c:4:12:4:12 | declaration of i | +| comments.c:6:7:6:16 | /* on f */ | 1 | comments.c:6:5:6:5 | definition of f | +| comments.c:6:26:6:35 | /* on x */ | 1 | comments.c:6:38:10:1 | { ... } | +| comments.c:7:11:7:20 | /* on j */ | 1 | comments.c:7:9:7:9 | definition of j | +| comments.c:12:7:12:16 | /* on i */ | 1 | comments.c:12:5:12:5 | definition of i | +| comments.c:15:17:15:25 | // On f2i | 1 | comments.c:15:12:15:14 | declaration of f2i | +| comments.c:22:1:22:9 | // On f3b | 1 | comments.c:23:13:23:15 | declaration of f3b | +| comments.c:25:1:25:16 | // On define FOO | 1 | comments.c:26:1:26:15 | #define FOO bar | +| comments.c:28:1:28:15 | // On undef FOO | 1 | comments.c:29:1:29:10 | #undef FOO | +| comments.c:31:1:31:22 | // On struct my_struct | 1 | comments.c:32:8:32:16 | definition of my_struct | +| comments.c:36:1:36:20 | // On union my_union | 1 | comments.c:37:7:37:14 | definition of my_union | +| comments.c:42:1:43:24 | // This is a legal alternative way to achieve a \\\nmulti-line comment on j. | 1 | comments.c:44:5:44:5 | definition of j | +| comments_99.c:2:4:2:35 | /* This is wrong for INT_MIN. */ | 1 | comments_99.c:3:4:6:15 | if (...) ... | +| comments_cpp.cpp:1:1:1:40 | // semmle-extractor-options: --microsoft | 1 | comments_cpp.cpp:2:1:2:27 | #define FIRST_VALUE 0x0001 | +| comments_cpp.cpp:2:29:2:53 | // Comment on FIRST_VALUE | 1 | comments_cpp.cpp:2:1:2:27 | #define FIRST_VALUE 0x0001 | +| comments_cpp.cpp:3:29:3:52 | // that spans two lines. | 1 | comments_cpp.cpp:4:1:4:27 | #define SECOND_VALUE 0x0002 | +| comments_cpp.cpp:4:29:4:54 | // Comment on SECOND_VALUE | 1 | comments_cpp.cpp:4:1:4:27 | #define SECOND_VALUE 0x0002 | +| comments_cpp.cpp:5:29:5:52 | // that spans two lines. | 1 | comments_cpp.cpp:8:1:8:18 | #ifdef __cplusplus | +| comments_cpp.cpp:9:14:9:40 | /* comment on extern "C" */ | 1 | comments_cpp.cpp:11:1:11:6 | #endif | +| comments_cpp.cpp:18:21:18:50 | // comment on throw bad_cast() | 1 | comments_cpp.cpp:19:1:19:5 | #else | +| comments_cpp.cpp:20:12:20:32 | // comment on abort() | 1 | comments_cpp.cpp:21:1:21:6 | #endif | +| comments_cpp.cpp:30:13:30:35 | // comment on operator- | 1 | comments_cpp.cpp:34:9:34:18 | declaration of operator[] | +| comments_cpp.cpp:40:1:40:20 | // On myns namespace | 1 | comments_cpp.cpp:41:11:41:14 | myns | +| comments_cpp.cpp:41:18:41:42 | // Also on myns namespace | 1 | comments_cpp.cpp:41:11:41:14 | myns | +| comments_cpp.cpp:45:1:45:20 | // On class my_class | 1 | comments_cpp.cpp:46:7:46:14 | definition of my_class | +| comments_cpp.cpp:47:5:47:22 | // On my_class_int | 1 | comments_cpp.cpp:48:9:48:20 | definition of my_class_int | +| comments_cpp.cpp:48:23:48:45 | // Also on my_class_int | 1 | comments_cpp.cpp:48:9:48:20 | definition of my_class_int | +| comments_cpp.cpp:51:1:51:25 | // On outer static assert | 1 | comments_cpp.cpp:52:1:52:46 | static_assert(..., "Addition is sane") | +| comments_cpp.cpp:55:3:55:27 | // On inner static assert | 1 | comments_cpp.cpp:56:3:56:63 | static_assert(..., "Type sizes are sane") | +| comments_cpp.cpp:56:65:56:94 | // Also on inner static assert | 1 | comments_cpp.cpp:56:3:56:63 | static_assert(..., "Type sizes are sane") | +| comments_cpp.cpp:59:1:59:22 | // More myns namespace | 1 | comments_cpp.cpp:60:11:60:14 | myns | +| shared.h:8:3:8:27 | /* This is a comment. */ | 1 | shared.h:9:3:10:3 | while (...) ... | +| shared.h:13:1:13:30 | /* This is another comment. */ | 1 | shared.h:14:12:14:20 | declaration of sharedInt | diff --git a/cpp/ql/test/library-tests/comments/comments/comments.ql b/cpp/ql/test/library-tests/comments/comments/comments.ql new file mode 100644 index 000000000000..97f5af2058fb --- /dev/null +++ b/cpp/ql/test/library-tests/comments/comments/comments.ql @@ -0,0 +1,5 @@ +import cpp + +from Comment c, Element e +where e = c.getCommentedElement() +select c, strictcount(c.getCommentedElement()), e diff --git a/cpp/ql/test/library-tests/comments/comments/comments_99.c b/cpp/ql/test/library-tests/comments/comments/comments_99.c new file mode 100644 index 000000000000..a1a8e4f026ef --- /dev/null +++ b/cpp/ql/test/library-tests/comments/comments/comments_99.c @@ -0,0 +1,8 @@ +static int abs(int x) { + /* This is wrong for INT_MIN. */ + if(x > 0) + return x; + else + return -x; +} +// semmle-extractor-options: -std=c99 diff --git a/cpp/ql/test/library-tests/comments/comments/comments_cpp.cpp b/cpp/ql/test/library-tests/comments/comments/comments_cpp.cpp new file mode 100644 index 000000000000..536c5d1f6838 --- /dev/null +++ b/cpp/ql/test/library-tests/comments/comments/comments_cpp.cpp @@ -0,0 +1,62 @@ +// semmle-extractor-options: --microsoft +#define FIRST_VALUE 0x0001 // Comment on FIRST_VALUE + // that spans two lines. +#define SECOND_VALUE 0x0002 // Comment on SECOND_VALUE + // that spans two lines. + +#define __cplusplus +#ifdef __cplusplus +extern "C" { /* comment on extern "C" */ +} +#endif + +class bad_cast; +template inline + const T& myTemplateFunction() + { +#if _HAS_EXCEPTIONS + throw bad_cast(); // comment on throw bad_cast() +#else + abort(); // comment on abort() +#endif + } + +template +class myClass { +public: + typedef int iterator; + + int operator-(const iterator &iter) const + { // comment on operator- + return 1; + } + + int operator[](int off) const + { // comment on operator[] + return 2; + } +}; + +// On myns namespace +namespace myns { // Also on myns namespace + void myns_fn(void); +} + +// On class my_class +class my_class { + // On my_class_int + int my_class_int; // Also on my_class_int +}; + +// On outer static assert +static_assert(3 + 4 == 7, "Addition is sane"); + +void sa_fun(int x, int y) { + // On inner static assert + static_assert(sizeof(x) == sizeof(y), "Type sizes are sane"); // Also on inner static assert +} + +// More myns namespace +namespace myns { +} + diff --git a/cpp/ql/test/library-tests/comments/comments/shared.h b/cpp/ql/test/library-tests/comments/comments/shared.h new file mode 100644 index 000000000000..614bfeb535b7 --- /dev/null +++ b/cpp/ql/test/library-tests/comments/comments/shared.h @@ -0,0 +1,15 @@ + +#ifdef WIBBLE +#define FOO BAR +#endif + +void sharedFun(void) +{ + /* This is a comment. */ + while (1) { + } +} + +/* This is another comment. */ +extern int sharedInt; + diff --git a/cpp/ql/test/library-tests/comments/comments/shared1.c b/cpp/ql/test/library-tests/comments/comments/shared1.c new file mode 100644 index 000000000000..9ea6207c7131 --- /dev/null +++ b/cpp/ql/test/library-tests/comments/comments/shared1.c @@ -0,0 +1,5 @@ + +#define WIBBLE 1 + +#include "shared.h" + diff --git a/cpp/ql/test/library-tests/comments/comments/shared2.c b/cpp/ql/test/library-tests/comments/comments/shared2.c new file mode 100644 index 000000000000..f07fc839a10a --- /dev/null +++ b/cpp/ql/test/library-tests/comments/comments/shared2.c @@ -0,0 +1,3 @@ + +#include "shared.h" + diff --git a/cpp/ql/test/library-tests/compiler_generated/c.c b/cpp/ql/test/library-tests/compiler_generated/c.c new file mode 100644 index 000000000000..911418342616 --- /dev/null +++ b/cpp/ql/test/library-tests/compiler_generated/c.c @@ -0,0 +1,33 @@ + +void f1(void) { + int x; + long y; + long long z; + x = 3; + y = x; // Compiler generated cast + z = x; // Compiler generated cast + // Compiler generated return +} + +void f2(void) { + int x; + x = 3; + return; +} + +void f3(void) { + int x; + x = 3; + return; + x = 4; + // No compiler generated return here, as this is unreachable +} + +void f4(void) { + int x; + while (1) { + x = 3; + } + // No compiler generated return here, as this is unreachable +} + diff --git a/cpp/ql/test/library-tests/compiler_generated/compilerGenerated.expected b/cpp/ql/test/library-tests/compiler_generated/compilerGenerated.expected new file mode 100644 index 000000000000..f7c1dd25dc5a --- /dev/null +++ b/cpp/ql/test/library-tests/compiler_generated/compilerGenerated.expected @@ -0,0 +1,19 @@ +| c.c:7:9:7:9 | (long)... | Expr | +| c.c:8:9:8:9 | (long long)... | Expr | +| c.c:10:1:10:1 | return ... | Stmt | +| cpp.cpp:4:7:4:7 | MySuperClass | Function | +| cpp.cpp:4:7:4:7 | operator= | Function | +| cpp.cpp:6:33:6:33 | return ... | Stmt | +| cpp.cpp:7:34:7:34 | return ... | Stmt | +| cpp.cpp:10:7:10:7 | MyClass | Function | +| cpp.cpp:10:7:10:7 | MyClass | Function | +| cpp.cpp:10:7:10:7 | MyClass | Function | +| cpp.cpp:10:7:10:7 | call to MySuperClass | Expr | +| cpp.cpp:10:7:10:7 | call to ~MySuperClass | Expr | +| cpp.cpp:10:7:10:7 | operator= | Function | +| cpp.cpp:10:7:10:7 | operator= | Function | +| cpp.cpp:10:7:10:7 | ~MyClass | Function | +| cpp.cpp:15:5:15:12 | call to ~MyClass | Expr | +| cpp.cpp:16:1:16:1 | return ... | Stmt | +| file://:0:0:0:0 | operator delete | Function | +| file://:0:0:0:0 | operator new | Function | diff --git a/cpp/ql/test/library-tests/compiler_generated/compilerGenerated.ql b/cpp/ql/test/library-tests/compiler_generated/compilerGenerated.ql new file mode 100644 index 000000000000..4676849ecfd6 --- /dev/null +++ b/cpp/ql/test/library-tests/compiler_generated/compilerGenerated.ql @@ -0,0 +1,9 @@ +import cpp + +from Element e, string type +where ((Function)e).isCompilerGenerated() and type = "Function" + or ((Expr) e).isCompilerGenerated() and type = "Expr" + or ((Variable)e).isCompilerGenerated() and type = "Variable" + or ((Stmt) e).isCompilerGenerated() and type = "Stmt" +select e, type + diff --git a/cpp/ql/test/library-tests/compiler_generated/cpp.cpp b/cpp/ql/test/library-tests/compiler_generated/cpp.cpp new file mode 100644 index 000000000000..058bba7ba411 --- /dev/null +++ b/cpp/ql/test/library-tests/compiler_generated/cpp.cpp @@ -0,0 +1,17 @@ + +int x; + +class MySuperClass { + public: + MySuperClass() { x = 1; } + ~MySuperClass() { x = 2; } +}; + +class MyClass : MySuperClass { +}; + +void g1(void) { + MyClass *m = new MyClass(); + delete m; +} + diff --git a/cpp/ql/test/library-tests/complex_numbers/conjugation.c b/cpp/ql/test/library-tests/complex_numbers/conjugation.c new file mode 100644 index 000000000000..ee379baf1ccb --- /dev/null +++ b/cpp/ql/test/library-tests/complex_numbers/conjugation.c @@ -0,0 +1,5 @@ +// semmle-extractor-options: --edg --c99 +void f(_Complex double x) { + x = ~x; +} + diff --git a/cpp/ql/test/library-tests/complex_numbers/expr.expected b/cpp/ql/test/library-tests/complex_numbers/expr.expected new file mode 100644 index 000000000000..a7174c9900a3 --- /dev/null +++ b/cpp/ql/test/library-tests/complex_numbers/expr.expected @@ -0,0 +1,128 @@ +| conjugation.c:3:5:3:5 | x | AnalysedExpr | +| conjugation.c:3:5:3:5 | x | CompileTimeVariableExpr | +| conjugation.c:3:5:3:5 | x | VariableAccess | +| conjugation.c:3:5:3:10 | ... = ... | AnalysedExpr | +| conjugation.c:3:5:3:10 | ... = ... | AssignExpr | +| conjugation.c:3:5:3:10 | ... = ... | CompileTimeVariableExpr | +| conjugation.c:3:5:3:10 | ... = ... | ExprInVoidContext | +| conjugation.c:3:5:3:10 | ... = ... | NameQualifiableElement | +| conjugation.c:3:9:3:10 | ~ ... | AnalysedExpr | +| conjugation.c:3:9:3:10 | ~ ... | CompileTimeVariableExpr | +| conjugation.c:3:9:3:10 | ~ ... | ConjugationExpr | +| conjugation.c:3:9:3:10 | ~ ... | NameQualifiableElement | +| conjugation.c:3:10:3:10 | x | AnalysedExpr | +| conjugation.c:3:10:3:10 | x | CompileTimeVariableExpr | +| conjugation.c:3:10:3:10 | x | VariableAccess | +| test.c:5:5:5:5 | z | AnalysedExpr | +| test.c:5:5:5:5 | z | CompileTimeVariableExpr | +| test.c:5:5:5:5 | z | VariableAccess | +| test.c:5:5:5:13 | ... = ... | AnalysedExpr | +| test.c:5:5:5:13 | ... = ... | AssignExpr | +| test.c:5:5:5:13 | ... = ... | CompileTimeVariableExpr | +| test.c:5:5:5:13 | ... = ... | ExprInVoidContext | +| test.c:5:5:5:13 | ... = ... | NameQualifiableElement | +| test.c:5:9:5:9 | x | AnalysedExpr | +| test.c:5:9:5:9 | x | CompileTimeVariableExpr | +| test.c:5:9:5:9 | x | VariableAccess | +| test.c:5:9:5:13 | ... * ... | AnalysedExpr | +| test.c:5:9:5:13 | ... * ... | CompileTimeVariableExpr | +| test.c:5:9:5:13 | ... * ... | ImaginaryMulExpr | +| test.c:5:9:5:13 | ... * ... | NameQualifiableElement | +| test.c:5:13:5:13 | y | AnalysedExpr | +| test.c:5:13:5:13 | y | CompileTimeVariableExpr | +| test.c:5:13:5:13 | y | VariableAccess | +| test.c:6:5:6:5 | z | AnalysedExpr | +| test.c:6:5:6:5 | z | CompileTimeVariableExpr | +| test.c:6:5:6:5 | z | VariableAccess | +| test.c:6:5:6:13 | ... = ... | AnalysedExpr | +| test.c:6:5:6:13 | ... = ... | AssignExpr | +| test.c:6:5:6:13 | ... = ... | CompileTimeVariableExpr | +| test.c:6:5:6:13 | ... = ... | ExprInVoidContext | +| test.c:6:5:6:13 | ... = ... | NameQualifiableElement | +| test.c:6:9:6:9 | z | AnalysedExpr | +| test.c:6:9:6:9 | z | CompileTimeVariableExpr | +| test.c:6:9:6:9 | z | VariableAccess | +| test.c:6:9:6:13 | (double)... | AnalysedExpr | +| test.c:6:9:6:13 | (double)... | CStyleCast | +| test.c:6:9:6:13 | (double)... | CompileTimeVariableExpr | +| test.c:6:9:6:13 | (double)... | FloatingPointConversion | +| test.c:6:9:6:13 | (double)... | NameQualifiableElement | +| test.c:6:9:6:13 | ... / ... | AnalysedExpr | +| test.c:6:9:6:13 | ... / ... | CompileTimeVariableExpr | +| test.c:6:9:6:13 | ... / ... | ImaginaryDivExpr | +| test.c:6:9:6:13 | ... / ... | NameQualifiableElement | +| test.c:6:13:6:13 | y | AnalysedExpr | +| test.c:6:13:6:13 | y | CompileTimeVariableExpr | +| test.c:6:13:6:13 | y | VariableAccess | +| test.c:7:5:7:5 | w | AnalysedExpr | +| test.c:7:5:7:5 | w | CompileTimeVariableExpr | +| test.c:7:5:7:5 | w | VariableAccess | +| test.c:7:5:7:13 | ... = ... | AnalysedExpr | +| test.c:7:5:7:13 | ... = ... | AssignExpr | +| test.c:7:5:7:13 | ... = ... | CompileTimeVariableExpr | +| test.c:7:5:7:13 | ... = ... | ExprInVoidContext | +| test.c:7:5:7:13 | ... = ... | NameQualifiableElement | +| test.c:7:9:7:9 | z | AnalysedExpr | +| test.c:7:9:7:9 | z | CompileTimeVariableExpr | +| test.c:7:9:7:9 | z | VariableAccess | +| test.c:7:9:7:13 | ... + ... | AnalysedExpr | +| test.c:7:9:7:13 | ... + ... | CompileTimeVariableExpr | +| test.c:7:9:7:13 | ... + ... | NameQualifiableElement | +| test.c:7:9:7:13 | ... + ... | RealImaginaryAddExpr | +| test.c:7:13:7:13 | x | AnalysedExpr | +| test.c:7:13:7:13 | x | CompileTimeVariableExpr | +| test.c:7:13:7:13 | x | VariableAccess | +| test.c:8:5:8:5 | w | AnalysedExpr | +| test.c:8:5:8:5 | w | CompileTimeVariableExpr | +| test.c:8:5:8:5 | w | VariableAccess | +| test.c:8:5:8:13 | ... = ... | AnalysedExpr | +| test.c:8:5:8:13 | ... = ... | AssignExpr | +| test.c:8:5:8:13 | ... = ... | CompileTimeVariableExpr | +| test.c:8:5:8:13 | ... = ... | ExprInVoidContext | +| test.c:8:5:8:13 | ... = ... | NameQualifiableElement | +| test.c:8:9:8:9 | x | AnalysedExpr | +| test.c:8:9:8:9 | x | CompileTimeVariableExpr | +| test.c:8:9:8:9 | x | VariableAccess | +| test.c:8:9:8:13 | ... + ... | AnalysedExpr | +| test.c:8:9:8:13 | ... + ... | CompileTimeVariableExpr | +| test.c:8:9:8:13 | ... + ... | ImaginaryRealAddExpr | +| test.c:8:9:8:13 | ... + ... | NameQualifiableElement | +| test.c:8:13:8:13 | z | AnalysedExpr | +| test.c:8:13:8:13 | z | CompileTimeVariableExpr | +| test.c:8:13:8:13 | z | VariableAccess | +| test.c:9:5:9:5 | w | AnalysedExpr | +| test.c:9:5:9:5 | w | CompileTimeVariableExpr | +| test.c:9:5:9:5 | w | VariableAccess | +| test.c:9:5:9:13 | ... = ... | AnalysedExpr | +| test.c:9:5:9:13 | ... = ... | AssignExpr | +| test.c:9:5:9:13 | ... = ... | CompileTimeVariableExpr | +| test.c:9:5:9:13 | ... = ... | ExprInVoidContext | +| test.c:9:5:9:13 | ... = ... | NameQualifiableElement | +| test.c:9:9:9:9 | z | AnalysedExpr | +| test.c:9:9:9:9 | z | CompileTimeVariableExpr | +| test.c:9:9:9:9 | z | VariableAccess | +| test.c:9:9:9:13 | ... - ... | AnalysedExpr | +| test.c:9:9:9:13 | ... - ... | CompileTimeVariableExpr | +| test.c:9:9:9:13 | ... - ... | NameQualifiableElement | +| test.c:9:9:9:13 | ... - ... | RealImaginarySubExpr | +| test.c:9:13:9:13 | x | AnalysedExpr | +| test.c:9:13:9:13 | x | CompileTimeVariableExpr | +| test.c:9:13:9:13 | x | VariableAccess | +| test.c:10:5:10:5 | w | AnalysedExpr | +| test.c:10:5:10:5 | w | CompileTimeVariableExpr | +| test.c:10:5:10:5 | w | VariableAccess | +| test.c:10:5:10:13 | ... = ... | AnalysedExpr | +| test.c:10:5:10:13 | ... = ... | AssignExpr | +| test.c:10:5:10:13 | ... = ... | CompileTimeVariableExpr | +| test.c:10:5:10:13 | ... = ... | ExprInVoidContext | +| test.c:10:5:10:13 | ... = ... | NameQualifiableElement | +| test.c:10:9:10:9 | x | AnalysedExpr | +| test.c:10:9:10:9 | x | CompileTimeVariableExpr | +| test.c:10:9:10:9 | x | VariableAccess | +| test.c:10:9:10:13 | ... - ... | AnalysedExpr | +| test.c:10:9:10:13 | ... - ... | CompileTimeVariableExpr | +| test.c:10:9:10:13 | ... - ... | ImaginaryRealSubExpr | +| test.c:10:9:10:13 | ... - ... | NameQualifiableElement | +| test.c:10:13:10:13 | z | AnalysedExpr | +| test.c:10:13:10:13 | z | CompileTimeVariableExpr | +| test.c:10:13:10:13 | z | VariableAccess | diff --git a/cpp/ql/test/library-tests/complex_numbers/expr.ql b/cpp/ql/test/library-tests/complex_numbers/expr.ql new file mode 100644 index 000000000000..00335eb188dc --- /dev/null +++ b/cpp/ql/test/library-tests/complex_numbers/expr.ql @@ -0,0 +1,5 @@ +import cpp + +from Expr e +select e, e.getAQlClass() + diff --git a/cpp/ql/test/library-tests/complex_numbers/test.c b/cpp/ql/test/library-tests/complex_numbers/test.c new file mode 100644 index 000000000000..b9913899d7c4 --- /dev/null +++ b/cpp/ql/test/library-tests/complex_numbers/test.c @@ -0,0 +1,12 @@ +// semmle-extractor-options: --microsoft --edg --c99 +void f(_Imaginary double x, _Imaginary double y) { + double z; + _Complex double w; + z = x * y; + z = z / y; + w = z + x; + w = x + z; + w = z - x; + w = x - z; +} + diff --git a/cpp/ql/test/library-tests/complexity/Complexity.expected b/cpp/ql/test/library-tests/complexity/Complexity.expected new file mode 100644 index 000000000000..305f7c2e275d --- /dev/null +++ b/cpp/ql/test/library-tests/complexity/Complexity.expected @@ -0,0 +1,4 @@ +| complexity.c:1:6:1:8 | cc1 | 1 | +| complexity.c:23:6:23:8 | cc2 | 2 | +| complexity.c:33:6:33:8 | cc3 | 3 | +| complexity.c:44:6:44:8 | cc4 | 4 | diff --git a/cpp/ql/test/library-tests/complexity/Complexity.ql b/cpp/ql/test/library-tests/complexity/Complexity.ql new file mode 100644 index 000000000000..a983f062cd48 --- /dev/null +++ b/cpp/ql/test/library-tests/complexity/Complexity.ql @@ -0,0 +1,4 @@ +import cpp + +from MetricFunction f +select f, f.getCyclomaticComplexity() diff --git a/cpp/ql/test/library-tests/complexity/complexity.c b/cpp/ql/test/library-tests/complexity/complexity.c new file mode 100644 index 000000000000..ee7389556897 --- /dev/null +++ b/cpp/ql/test/library-tests/complexity/complexity.c @@ -0,0 +1,60 @@ +void cc1(int val) +{ + switch (val) { + } + switch (val) { + case 1: + default: + break; + } + switch (val) { + default: + case 1: + break; + } + switch (val) { + case 1: + default: + case 2: + break; + } +} + +void cc2(int val) +{ + switch (val) { + case 1: + break; + default: + break; + } +} + +void cc3(int val) +{ + switch (val) { + case 1: + case 2: + break; + case 3: + break; + } +} + +void cc4(int val) +{ + switch (val) { + case 1: + case 2: + break; + case 3: + break; + case 4: + break; + case 2+3: + default: + case 3+3: + case 7: + break; + } +} diff --git a/cpp/ql/test/library-tests/conditions/diag.expected b/cpp/ql/test/library-tests/conditions/diag.expected new file mode 100644 index 000000000000..c79f6db4beb6 --- /dev/null +++ b/cpp/ql/test/library-tests/conditions/diag.expected @@ -0,0 +1,3 @@ +| file://:0:0:0:0 | There was an error during this compilation | 4 | Error occurred | +| test.cpp:3:12:3:12 | a condition declaration must include an initializer | 4 | condition_decl_must_have_initializer | +| test.cpp:3:12:3:12 | expected an identifier | 4 | exp_identifier | diff --git a/cpp/ql/test/library-tests/conditions/diag.ql b/cpp/ql/test/library-tests/conditions/diag.ql new file mode 100644 index 000000000000..53e8ea5de9da --- /dev/null +++ b/cpp/ql/test/library-tests/conditions/diag.ql @@ -0,0 +1,4 @@ +import cpp + +from Diagnostic d +select d, d.getSeverity(), d.getTag() diff --git a/cpp/ql/test/library-tests/conditions/elements.expected b/cpp/ql/test/library-tests/conditions/elements.expected new file mode 100644 index 000000000000..3a4d6ced0ed0 --- /dev/null +++ b/cpp/ql/test/library-tests/conditions/elements.expected @@ -0,0 +1,123 @@ +| file://:0:0:0:0 | | +| file://:0:0:0:0 | (global namespace) | +| file://:0:0:0:0 | | +| file://:0:0:0:0 | | +| file://:0:0:0:0 | | +| file://:0:0:0:0 | There was an error during this compilation | +| file://:0:0:0:0 | _Complex __float128 | +| file://:0:0:0:0 | _Complex double | +| file://:0:0:0:0 | _Complex float | +| file://:0:0:0:0 | _Complex long double | +| file://:0:0:0:0 | _Decimal32 | +| file://:0:0:0:0 | _Decimal64 | +| file://:0:0:0:0 | _Decimal128 | +| file://:0:0:0:0 | _Float32 | +| file://:0:0:0:0 | _Float32x | +| file://:0:0:0:0 | _Float64 | +| file://:0:0:0:0 | _Float64x | +| file://:0:0:0:0 | _Float128 | +| file://:0:0:0:0 | _Float128x | +| file://:0:0:0:0 | _Imaginary double | +| file://:0:0:0:0 | _Imaginary float | +| file://:0:0:0:0 | _Imaginary long double | +| file://:0:0:0:0 | __block | +| file://:0:0:0:0 | __float128 | +| file://:0:0:0:0 | __int128 | +| file://:0:0:0:0 | __interface | +| file://:0:0:0:0 | __ptr32 | +| file://:0:0:0:0 | __ptr64 | +| file://:0:0:0:0 | __sptr | +| file://:0:0:0:0 | __uptr | +| file://:0:0:0:0 | __va_list_tag | +| file://:0:0:0:0 | __va_list_tag & | +| file://:0:0:0:0 | abstract | +| file://:0:0:0:0 | atomic | +| file://:0:0:0:0 | auto | +| file://:0:0:0:0 | auto | +| file://:0:0:0:0 | bool | +| file://:0:0:0:0 | char | +| file://:0:0:0:0 | char16_t | +| file://:0:0:0:0 | char32_t | +| file://:0:0:0:0 | const | +| file://:0:0:0:0 | decltype(nullptr) | +| file://:0:0:0:0 | definition of | +| file://:0:0:0:0 | definition of __va_list_tag | +| file://:0:0:0:0 | definition of fp_offset | +| file://:0:0:0:0 | definition of gp_offset | +| file://:0:0:0:0 | definition of overflow_arg_area | +| file://:0:0:0:0 | definition of reg_save_area | +| file://:0:0:0:0 | dllexport | +| file://:0:0:0:0 | dllimport | +| file://:0:0:0:0 | double | +| file://:0:0:0:0 | error | +| file://:0:0:0:0 | explicit | +| file://:0:0:0:0 | extern | +| file://:0:0:0:0 | far | +| file://:0:0:0:0 | float | +| file://:0:0:0:0 | forceinline | +| file://:0:0:0:0 | fp_offset | +| file://:0:0:0:0 | gp_offset | +| file://:0:0:0:0 | implicit_int | +| file://:0:0:0:0 | initializer for | +| file://:0:0:0:0 | inline | +| file://:0:0:0:0 | int | +| file://:0:0:0:0 | long | +| file://:0:0:0:0 | long double | +| file://:0:0:0:0 | long long | +| file://:0:0:0:0 | microsoft_inline | +| file://:0:0:0:0 | naked | +| file://:0:0:0:0 | near | +| file://:0:0:0:0 | noalias | +| file://:0:0:0:0 | noinline | +| file://:0:0:0:0 | noreturn | +| file://:0:0:0:0 | nothrow | +| file://:0:0:0:0 | novtable | +| file://:0:0:0:0 | operator= | +| file://:0:0:0:0 | operator= | +| file://:0:0:0:0 | optional | +| file://:0:0:0:0 | overflow_arg_area | +| file://:0:0:0:0 | override | +| file://:0:0:0:0 | private | +| file://:0:0:0:0 | protected | +| file://:0:0:0:0 | public | +| file://:0:0:0:0 | pure | +| file://:0:0:0:0 | reg_save_area | +| file://:0:0:0:0 | register | +| file://:0:0:0:0 | restrict | +| file://:0:0:0:0 | sealed | +| file://:0:0:0:0 | selectany | +| file://:0:0:0:0 | short | +| file://:0:0:0:0 | signed __int128 | +| file://:0:0:0:0 | signed char | +| file://:0:0:0:0 | signed int | +| file://:0:0:0:0 | signed long | +| file://:0:0:0:0 | signed long long | +| file://:0:0:0:0 | signed short | +| file://:0:0:0:0 | static | +| file://:0:0:0:0 | thread | +| file://:0:0:0:0 | unaligned | +| file://:0:0:0:0 | unknown | +| file://:0:0:0:0 | unsigned __int128 | +| file://:0:0:0:0 | unsigned char | +| file://:0:0:0:0 | unsigned int | +| file://:0:0:0:0 | unsigned long | +| file://:0:0:0:0 | unsigned long long | +| file://:0:0:0:0 | unsigned short | +| file://:0:0:0:0 | varargs | +| file://:0:0:0:0 | virtual | +| file://:0:0:0:0 | void | +| file://:0:0:0:0 | void * | +| file://:0:0:0:0 | volatile | +| file://:0:0:0:0 | wchar_t | +| test.cpp:0:0:0:0 | test.cpp | +| test.cpp:2:6:2:6 | definition of f | +| test.cpp:2:6:2:6 | f | +| test.cpp:2:10:4:1 | { ... } | +| test.cpp:3:5:3:15 | if (...) ... | +| test.cpp:3:9:3:12 | (condition decl) | +| test.cpp:3:12:3:12 | | +| test.cpp:3:12:3:12 | a condition declaration must include an initializer | +| test.cpp:3:12:3:12 | declaration of | +| test.cpp:3:12:3:12 | expected an identifier | +| test.cpp:3:14:3:15 | { ... } | +| test.cpp:4:1:4:1 | return ... | diff --git a/cpp/ql/test/library-tests/conditions/elements.ql b/cpp/ql/test/library-tests/conditions/elements.ql new file mode 100644 index 000000000000..8d2ce8f5e94f --- /dev/null +++ b/cpp/ql/test/library-tests/conditions/elements.ql @@ -0,0 +1,5 @@ +import cpp + +from Element e +where not e instanceof Folder +select e diff --git a/cpp/ql/test/library-tests/conditions/options b/cpp/ql/test/library-tests/conditions/options new file mode 100644 index 000000000000..4181beff3c28 --- /dev/null +++ b/cpp/ql/test/library-tests/conditions/options @@ -0,0 +1 @@ +extractor_flags: --expect_errors diff --git a/cpp/ql/test/library-tests/conditions/test.cpp b/cpp/ql/test/library-tests/conditions/test.cpp new file mode 100644 index 000000000000..6b56368b6d2b --- /dev/null +++ b/cpp/ql/test/library-tests/conditions/test.cpp @@ -0,0 +1,5 @@ + +void f() { + if (int) {} +} + diff --git a/cpp/ql/test/library-tests/conditions/vars.expected b/cpp/ql/test/library-tests/conditions/vars.expected new file mode 100644 index 000000000000..f90baf9ebc17 --- /dev/null +++ b/cpp/ql/test/library-tests/conditions/vars.expected @@ -0,0 +1,6 @@ +| file://:0:0:0:0 | | +| file://:0:0:0:0 | fp_offset | +| file://:0:0:0:0 | gp_offset | +| file://:0:0:0:0 | overflow_arg_area | +| file://:0:0:0:0 | reg_save_area | +| test.cpp:3:12:3:12 | | diff --git a/cpp/ql/test/library-tests/conditions/vars.ql b/cpp/ql/test/library-tests/conditions/vars.ql new file mode 100644 index 000000000000..14c3aed68dde --- /dev/null +++ b/cpp/ql/test/library-tests/conditions/vars.ql @@ -0,0 +1,5 @@ +import cpp + +from Variable v +select v + diff --git a/cpp/ql/test/library-tests/constants/.gitattributes b/cpp/ql/test/library-tests/constants/.gitattributes new file mode 100644 index 000000000000..0c1501f4d141 --- /dev/null +++ b/cpp/ql/test/library-tests/constants/.gitattributes @@ -0,0 +1 @@ +constants.cpp text eol=lf diff --git a/cpp/ql/test/library-tests/constants/constants/aggregates.expected b/cpp/ql/test/library-tests/constants/constants/aggregates.expected new file mode 100644 index 000000000000..9c1e212c6379 --- /dev/null +++ b/cpp/ql/test/library-tests/constants/constants/aggregates.expected @@ -0,0 +1,4 @@ +| float[3] | +| float[3][3] | +| foo[1] | +| struct | diff --git a/cpp/ql/test/library-tests/constants/constants/aggregates.ql b/cpp/ql/test/library-tests/constants/constants/aggregates.ql new file mode 100644 index 000000000000..20c1d931de53 --- /dev/null +++ b/cpp/ql/test/library-tests/constants/constants/aggregates.ql @@ -0,0 +1,4 @@ +import cpp + +from AggregateLiteral al +select al.getType().toString() diff --git a/cpp/ql/test/library-tests/constants/constants/chars.expected b/cpp/ql/test/library-tests/constants/constants/chars.expected new file mode 100644 index 000000000000..6d55ab74539e --- /dev/null +++ b/cpp/ql/test/library-tests/constants/constants/chars.expected @@ -0,0 +1,8 @@ +| 10 | '\\n' | \\n | +| 10 | L'\\n' | \\n | +| 72 | 'H' | H | +| 72 | L'H' | H | +| 101 | '\\\ne' | \\\ne | +| 101 | L'\\\ne' | \\\ne | +| 159 | L'\\x9f' | \\x9f | +| -97 | '\\x9f' | \\x9f | \ No newline at end of file diff --git a/cpp/ql/test/library-tests/constants/constants/chars.ql b/cpp/ql/test/library-tests/constants/constants/chars.ql new file mode 100644 index 000000000000..0e4f220fe6d2 --- /dev/null +++ b/cpp/ql/test/library-tests/constants/constants/chars.ql @@ -0,0 +1,6 @@ +import cpp + +from CharLiteral cl +select cl.getValue(), + cl.getValueText(), + cl.getCharacter() diff --git a/cpp/ql/test/library-tests/constants/constants/constants.cpp b/cpp/ql/test/library-tests/constants/constants/constants.cpp new file mode 100644 index 000000000000..9dd4e7321861 --- /dev/null +++ b/cpp/ql/test/library-tests/constants/constants/constants.cpp @@ -0,0 +1,57 @@ +#define Wrap1(s) s +#define Wrap2(s) Wrap1(s) + +#define Widen1(s) L ## s +#define Widen2(s) Widen1(s) + +void argused(...); + +void narrow() { + const char* nzero = "Narrow \xa2 Zero"; + const char* none = Wrap1("Narrow \xa2 One"); + const char* ntwo = Wrap2("Narrow \xa2 Two"); + char nthree = 'H'; + char nfour = '\x9f'; + char newline = '\n'; + char exotic = '\ +e'; + + argused(nzero, none, ntwo, nthree, nfour, newline, exotic); +} + +void wide() { + const wchar_t* wzero = L"Wide \x20ac Zero"; + const wchar_t* wone = Widen1("Wide \x20ac One"); + const wchar_t* wtwo = Widen2("Wide \x20ac Two"); + wchar_t wthree = L'H'; + wchar_t wfour = L'\x9f'; + wchar_t wewline = L'\n'; + wchar_t wxotic = L'\ +e'; + + argused(wzero, wone, wtwo, wthree, wfour, wewline, wxotic); +} + +#define ONE_HUNDRED 100 + +void integers() { + int zero = 0; + + int dec = 100; + int hex = 0x64; + int oct = 0144; + + int computed = 40 + 60; + int macroish = Wrap1(40) + Wrap1(60); + int full_macro = ONE_HUNDRED; + + argused(zero, dec, hex, oct, computed, macroish, full_macro); +} + +void aggregates() { + float id[3][3] = {{1., 0., 0.}, {0., 1., 0.}, {0., 0., 1.}}; + struct {int x; int y;} x_axis = {1, 0}; + + argused(id, x_axis); +} +// semmle-extractor-options: --edg --target --edg win64 diff --git a/cpp/ql/test/library-tests/constants/constants/ints.expected b/cpp/ql/test/library-tests/constants/constants/ints.expected new file mode 100644 index 000000000000..0f9bd276b1ab --- /dev/null +++ b/cpp/ql/test/library-tests/constants/constants/ints.expected @@ -0,0 +1,6 @@ +| constants.cpp:38:7:38:10 | zero | 0 | 0 | Literal | +| constants.cpp:38:7:38:10 | zero | 0 | 0 | Zero | +| constants.cpp:40:7:40:9 | dec | 100 | 100 | Literal | +| constants.cpp:41:7:41:9 | hex | 100 | 0x64 | HexLiteral | +| constants.cpp:42:7:42:9 | oct | 100 | 0144 | OctalLiteral | +| constants.cpp:46:7:46:16 | full_macro | 100 | ONE_HUNDRED | Literal | diff --git a/cpp/ql/test/library-tests/constants/constants/ints.ql b/cpp/ql/test/library-tests/constants/constants/ints.ql new file mode 100644 index 000000000000..853e1feadc4c --- /dev/null +++ b/cpp/ql/test/library-tests/constants/constants/ints.ql @@ -0,0 +1,8 @@ +import cpp + +from LocalVariable v, Literal l, string clazz +where l = v.getInitializer().getExpr() + and v.getFunction().hasName("integers") + and clazz = l.getAQlClass() + and (clazz.matches("%Literal") or clazz = "Zero") +select v, l.getValue(), l.getValueText(), clazz diff --git a/cpp/ql/test/library-tests/constants/constants/ms_agg_2.c b/cpp/ql/test/library-tests/constants/constants/ms_agg_2.c new file mode 100644 index 000000000000..92d14cd91dd5 --- /dev/null +++ b/cpp/ql/test/library-tests/constants/constants/ms_agg_2.c @@ -0,0 +1,14 @@ + +typedef struct foo { + int x; +} foo; + +foo foomaker(); + +void test() { + foo bar[] = { + foomaker() + }; +} + +// semmle-extractor-options: --microsoft --microsoft_version 1800 diff --git a/cpp/ql/test/library-tests/constants/constants/strings.expected b/cpp/ql/test/library-tests/constants/constants/strings.expected new file mode 100644 index 000000000000..f2ffb4f52501 --- /dev/null +++ b/cpp/ql/test/library-tests/constants/constants/strings.expected @@ -0,0 +1,6 @@ +| Narrow \u00a2 One | "Narrow \\xa2 One" | +| Narrow \u00a2 Two | "Narrow \\xa2 Two" | +| Narrow \u00a2 Zero | "Narrow \\xa2 Zero" | +| Wide \u20ac One | Widen1("Wide \\x20ac One" | +| Wide \u20ac Two | Widen2("Wide \\x20ac Two" | +| Wide \u20ac Zero | L"Wide \\x20ac Zero" | diff --git a/cpp/ql/test/library-tests/constants/constants/strings.ql b/cpp/ql/test/library-tests/constants/constants/strings.ql new file mode 100644 index 000000000000..a412155cee71 --- /dev/null +++ b/cpp/ql/test/library-tests/constants/constants/strings.ql @@ -0,0 +1,5 @@ +import cpp + +from StringLiteral sl +select sl.getValue().regexpReplaceAll("\\\\x00+", "\\\\x0"), // Normalise for different sizeof(wchar_t) + sl.getValueText() diff --git a/cpp/ql/test/library-tests/constants/initializerexpr/clang.c b/cpp/ql/test/library-tests/constants/initializerexpr/clang.c new file mode 100644 index 000000000000..99871ecbefbc --- /dev/null +++ b/cpp/ql/test/library-tests/constants/initializerexpr/clang.c @@ -0,0 +1,5 @@ +typedef float MyType; +static const MyType a = 36; +static const MyType b = a; +static const MyType c = 2 * a; +// semmle-extractor-options: --clang diff --git a/cpp/ql/test/library-tests/constants/initializerexpr/initializerexpr.cpp b/cpp/ql/test/library-tests/constants/initializerexpr/initializerexpr.cpp new file mode 100644 index 000000000000..aa7edb9f1671 --- /dev/null +++ b/cpp/ql/test/library-tests/constants/initializerexpr/initializerexpr.cpp @@ -0,0 +1,16 @@ + +extern const int myConst1; +const int myConst2 = 20; + +int thirty() {return 30;} + +void func1(int x = myConst1, int y = myConst2, int z = thirty()) +{ +} + +void func2() +{ + func1(); // call with default parameters +} + +const int myConst1 = 10; diff --git a/cpp/ql/test/library-tests/constants/initializerexpr/initializerexpr.expected b/cpp/ql/test/library-tests/constants/initializerexpr/initializerexpr.expected new file mode 100644 index 000000000000..36b75841b8d1 --- /dev/null +++ b/cpp/ql/test/library-tests/constants/initializerexpr/initializerexpr.expected @@ -0,0 +1,14 @@ +| clang.c:2:25:2:26 | 36 | a | | +| clang.c:2:25:2:26 | (MyType)... | | | +| clang.c:3:25:3:25 | a | b | | +| clang.c:4:25:4:25 | 2 | | | +| clang.c:4:25:4:25 | (float)... | | | +| clang.c:4:25:4:29 | ... * ... | c | | +| clang.c:4:29:4:29 | a | | | +| initializerexpr.cpp:3:22:3:23 | 20 | myConst2 | | +| initializerexpr.cpp:5:22:5:23 | 30 | | thirty | +| initializerexpr.cpp:7:20:7:27 | myConst1 | x | func1 | +| initializerexpr.cpp:7:38:7:45 | myConst2 | y | func1 | +| initializerexpr.cpp:7:56:7:61 | call to thirty | z | func1 | +| initializerexpr.cpp:13:2:13:6 | call to func1 | | func2 | +| initializerexpr.cpp:16:22:16:23 | 10 | myConst1 | | diff --git a/cpp/ql/test/library-tests/constants/initializerexpr/initializerexpr.ql b/cpp/ql/test/library-tests/constants/initializerexpr/initializerexpr.ql new file mode 100644 index 000000000000..d09a91f81be3 --- /dev/null +++ b/cpp/ql/test/library-tests/constants/initializerexpr/initializerexpr.ql @@ -0,0 +1,10 @@ +import cpp + +Declaration initializedDecl(Expr e) { + exists(Initializer i | i.getExpr() = e and i.getDeclaration() = result) +} + +from Expr e, string enclosedBy, string initializes +where if exists(e.getEnclosingFunction()) then enclosedBy = e.getEnclosingFunction().toString() else enclosedBy = "" +and if exists(initializedDecl(e)) then initializes = initializedDecl(e).toString() else initializes = "" +select e, initializes, enclosedBy diff --git a/cpp/ql/test/library-tests/constants/initializers/initializers.c b/cpp/ql/test/library-tests/constants/initializers/initializers.c new file mode 100644 index 000000000000..76008f4572d3 --- /dev/null +++ b/cpp/ql/test/library-tests/constants/initializers/initializers.c @@ -0,0 +1,19 @@ + +int i = 5; +int j = 6; +int k = 7; +int l = 8; +int m = 9; + +void f(void) { + int *p; + + j++; + k = 8; + + p = &l; + *p = 9; + + p = &m; // We will conservatively assume that m might be modified +} + diff --git a/cpp/ql/test/library-tests/constants/initializers/initializers.cpp b/cpp/ql/test/library-tests/constants/initializers/initializers.cpp new file mode 100644 index 000000000000..e4dec1ff5d1a --- /dev/null +++ b/cpp/ql/test/library-tests/constants/initializers/initializers.cpp @@ -0,0 +1,11 @@ + +int cppi = 3; +int cppj = 4; + +void g(void) { + int &ri = cppi; + int &rj = cppj; + + ri = 4; +} + diff --git a/cpp/ql/test/library-tests/constants/initializers/initializers.expected b/cpp/ql/test/library-tests/constants/initializers/initializers.expected new file mode 100644 index 000000000000..279429ecf1fb --- /dev/null +++ b/cpp/ql/test/library-tests/constants/initializers/initializers.expected @@ -0,0 +1,2 @@ +| initializers.c:2:9:2:9 | 5 | initializers.c:2:5:2:5 | i | +| initializers.cpp:3:12:3:12 | 4 | initializers.cpp:3:5:3:8 | cppj | diff --git a/cpp/ql/test/library-tests/constants/initializers/initializers.ql b/cpp/ql/test/library-tests/constants/initializers/initializers.ql new file mode 100644 index 000000000000..0086631eefd3 --- /dev/null +++ b/cpp/ql/test/library-tests/constants/initializers/initializers.ql @@ -0,0 +1,7 @@ +import cpp +import Best_Practices.Magic_Constants.MagicConstants + +from Literal l, Variable v +where literalIsConstantInitializer(l, v) +select l, v + diff --git a/cpp/ql/test/library-tests/constants/strlen/expr.expected b/cpp/ql/test/library-tests/constants/strlen/expr.expected new file mode 100644 index 000000000000..570ac1317f49 --- /dev/null +++ b/cpp/ql/test/library-tests/constants/strlen/expr.expected @@ -0,0 +1,4 @@ +| strlen.cpp:11:39:11:48 | array to pointer conversion | +| strlen.cpp:11:39:11:48 | file.ext | +| strlen.cpp:12:35:12:40 | call to strlen | +| strlen.cpp:12:42:12:43 | fn | diff --git a/cpp/ql/test/library-tests/constants/strlen/expr.ql b/cpp/ql/test/library-tests/constants/strlen/expr.ql new file mode 100644 index 000000000000..3c2a3269e72e --- /dev/null +++ b/cpp/ql/test/library-tests/constants/strlen/expr.ql @@ -0,0 +1,5 @@ +import cpp + +from Expr e +select e + diff --git a/cpp/ql/test/library-tests/constants/strlen/strlen.cpp b/cpp/ql/test/library-tests/constants/strlen/strlen.cpp new file mode 100644 index 000000000000..393d977d4a06 --- /dev/null +++ b/cpp/ql/test/library-tests/constants/strlen/strlen.cpp @@ -0,0 +1,14 @@ + +typedef long unsigned int size_t; + +extern "C" { + extern size_t strlen (const char *__s) throw () + __attribute__ ((__pure__)) + __attribute__ ((__nonnull__ (1))); +} + +void fun(void) { + static constexpr const char *fn = "file.ext"; + static constexpr size_t len = strlen(fn); +} + diff --git a/cpp/ql/test/library-tests/controlflow/assume/cfg.expected b/cpp/ql/test/library-tests/controlflow/assume/cfg.expected new file mode 100644 index 000000000000..d7d91a1a4666 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/assume/cfg.expected @@ -0,0 +1,110 @@ +| simple | f | 2 | 1 | simple.cpp:2:6:2:6 | f | | +| simple | g | 3 | 1 | simple.cpp:3:6:3:6 | g | | +| simple | h | 4 | 4 | simple.cpp:4:6:4:6 | h | | +| simple | h | 5 | 1 | simple.cpp:5:1:11:1 | { ... } | ExprStmt | +| simple | h | 6 | 2 | simple.cpp:6:2:6:5 | ExprStmt | call to f | +| simple | h | 6 | 3 | simple.cpp:6:2:6:2 | call to f | ExprStmt | +| simple | h | 8 | 1 | simple.cpp:8:11:8:11 | 0 | | +| simple | h | 8 | 1 | simple.cpp:8:11:8:11 | (bool)... | | +| simple | h | 8 | 4 | simple.cpp:8:2:8:13 | ExprStmt | __assume(...) | +| simple | h | 8 | 5 | simple.cpp:8:2:8:12 | __assume(...) | | +| simple | h | 10 | 1 | simple.cpp:10:2:10:5 | ExprStmt | call to g | +| simple | h | 10 | 2 | simple.cpp:10:2:10:2 | call to g | return ... | +| simple | h | 11 | 3 | simple.cpp:11:1:11:1 | return ... | h | +| simple | i | 12 | 1 | simple.cpp:12:6:12:6 | i | | +| simple | j | 13 | 1 | simple.cpp:13:6:13:6 | j | | +| simple | k | 14 | 4 | simple.cpp:14:6:14:6 | k | | +| simple | k | 15 | 1 | simple.cpp:15:1:19:1 | { ... } | ExprStmt | +| simple | k | 16 | 2 | simple.cpp:16:2:16:5 | ExprStmt | call to i | +| simple | k | 16 | 3 | simple.cpp:16:2:16:2 | call to i | ExprStmt | +| simple | k | 17 | 4 | simple.cpp:17:2:17:5 | ExprStmt | call to h | +| simple | k | 17 | 5 | simple.cpp:17:2:17:2 | call to h | | +| simple | k | 18 | 1 | simple.cpp:18:2:18:5 | ExprStmt | call to j | +| simple | k | 18 | 2 | simple.cpp:18:2:18:2 | call to j | return ... | +| simple | k | 19 | 3 | simple.cpp:19:1:19:1 | return ... | k | +| switch | doThing | 2 | 1 | switch.cpp:2:6:2:12 | doThing | | +| switch | switchTest1 | 4 | 37 | switch.cpp:4:6:4:16 | switchTest1 | | +| switch | switchTest1 | 5 | 1 | switch.cpp:5:1:30:1 | { ... } | declaration | +| switch | switchTest1 | 6 | 2 | switch.cpp:6:2:6:10 | declaration | declaration | +| switch | switchTest1 | 7 | 3 | switch.cpp:7:2:7:10 | declaration | switch (...) ... | +| switch | switchTest1 | 9 | 4 | switch.cpp:9:2:27:2 | switch (...) ... | i | +| switch | switchTest1 | 9 | 5 | switch.cpp:9:10:9:10 | i | { ... } | +| switch | switchTest1 | 9 | 6 | switch.cpp:9:13:27:2 | { ... } | case ...: | +| switch | switchTest1 | 9 | 6 | switch.cpp:9:13:27:2 | { ... } | default: | +| switch | switchTest1 | 10 | 1 | switch.cpp:10:8:10:8 | 1 | | +| switch | switchTest1 | 10 | 7 | switch.cpp:10:3:10:9 | case ...: | ExprStmt | +| switch | switchTest1 | 11 | 8 | switch.cpp:11:4:11:16 | ExprStmt | ptr | +| switch | switchTest1 | 11 | 9 | switch.cpp:11:12:11:14 | ptr | call to doThing | +| switch | switchTest1 | 11 | 10 | switch.cpp:11:4:11:10 | call to doThing | ExprStmt | +| switch | switchTest1 | 12 | 11 | switch.cpp:12:4:12:12 | ExprStmt | x | +| switch | switchTest1 | 12 | 12 | switch.cpp:12:11:12:11 | x | & ... | +| switch | switchTest1 | 12 | 13 | switch.cpp:12:10:12:11 | & ... | ptr | +| switch | switchTest1 | 12 | 14 | switch.cpp:12:4:12:6 | ptr | ... = ... | +| switch | switchTest1 | 12 | 15 | switch.cpp:12:4:12:11 | ... = ... | ExprStmt | +| switch | switchTest1 | 13 | 16 | switch.cpp:13:4:13:16 | ExprStmt | ptr | +| switch | switchTest1 | 13 | 17 | switch.cpp:13:12:13:14 | ptr | call to doThing | +| switch | switchTest1 | 13 | 18 | switch.cpp:13:4:13:10 | call to doThing | return ... | +| switch | switchTest1 | 14 | 19 | switch.cpp:14:4:14:10 | return ... | switchTest1 | +| switch | switchTest1 | 15 | 1 | switch.cpp:15:8:15:8 | 2 | | +| switch | switchTest1 | 15 | 7 | switch.cpp:15:3:15:9 | case ...: | ExprStmt | +| switch | switchTest1 | 16 | 8 | switch.cpp:16:4:16:16 | ExprStmt | ptr | +| switch | switchTest1 | 16 | 9 | switch.cpp:16:12:16:14 | ptr | call to doThing | +| switch | switchTest1 | 16 | 10 | switch.cpp:16:4:16:10 | call to doThing | ExprStmt | +| switch | switchTest1 | 17 | 11 | switch.cpp:17:4:17:12 | ExprStmt | y | +| switch | switchTest1 | 17 | 12 | switch.cpp:17:11:17:11 | y | & ... | +| switch | switchTest1 | 17 | 13 | switch.cpp:17:10:17:11 | & ... | ptr | +| switch | switchTest1 | 17 | 14 | switch.cpp:17:4:17:6 | ptr | ... = ... | +| switch | switchTest1 | 17 | 15 | switch.cpp:17:4:17:11 | ... = ... | ExprStmt | +| switch | switchTest1 | 18 | 16 | switch.cpp:18:4:18:16 | ExprStmt | ptr | +| switch | switchTest1 | 18 | 17 | switch.cpp:18:12:18:14 | ptr | call to doThing | +| switch | switchTest1 | 18 | 18 | switch.cpp:18:4:18:10 | call to doThing | return ... | +| switch | switchTest1 | 19 | 19 | switch.cpp:19:4:19:10 | return ... | switchTest1 | +| switch | switchTest1 | 20 | 7 | switch.cpp:20:3:20:10 | default: | ExprStmt | +| switch | switchTest1 | 21 | 1 | switch.cpp:21:13:21:13 | 0 | | +| switch | switchTest1 | 21 | 1 | switch.cpp:21:13:21:13 | (bool)... | | +| switch | switchTest1 | 21 | 8 | switch.cpp:21:4:21:15 | ExprStmt | __assume(...) | +| switch | switchTest1 | 21 | 9 | switch.cpp:21:4:21:14 | __assume(...) | | +| switch | switchTest1 | 29 | 1 | switch.cpp:29:2:29:14 | ExprStmt | ptr | +| switch | switchTest1 | 29 | 2 | switch.cpp:29:10:29:12 | ptr | call to doThing | +| switch | switchTest1 | 29 | 3 | switch.cpp:29:2:29:8 | call to doThing | return ... | +| switch | switchTest1 | 30 | 4 | switch.cpp:30:1:30:1 | return ... | switchTest1 | +| switch | switchTest2 | 32 | 37 | switch.cpp:32:6:32:16 | switchTest2 | | +| switch | switchTest2 | 33 | 1 | switch.cpp:33:1:51:1 | { ... } | declaration | +| switch | switchTest2 | 34 | 2 | switch.cpp:34:2:34:10 | declaration | declaration | +| switch | switchTest2 | 35 | 3 | switch.cpp:35:2:35:10 | declaration | switch (...) ... | +| switch | switchTest2 | 37 | 4 | switch.cpp:37:2:48:2 | switch (...) ... | i | +| switch | switchTest2 | 37 | 5 | switch.cpp:37:10:37:10 | i | { ... } | +| switch | switchTest2 | 37 | 6 | switch.cpp:37:13:48:2 | { ... } | ExprStmt | +| switch | switchTest2 | 37 | 6 | switch.cpp:37:13:48:2 | { ... } | case ...: | +| switch | switchTest2 | 38 | 1 | switch.cpp:38:8:38:8 | 1 | | +| switch | switchTest2 | 38 | 7 | switch.cpp:38:3:38:9 | case ...: | ExprStmt | +| switch | switchTest2 | 39 | 8 | switch.cpp:39:4:39:16 | ExprStmt | ptr | +| switch | switchTest2 | 39 | 9 | switch.cpp:39:12:39:14 | ptr | call to doThing | +| switch | switchTest2 | 39 | 10 | switch.cpp:39:4:39:10 | call to doThing | ExprStmt | +| switch | switchTest2 | 40 | 11 | switch.cpp:40:4:40:12 | ExprStmt | x | +| switch | switchTest2 | 40 | 12 | switch.cpp:40:11:40:11 | x | & ... | +| switch | switchTest2 | 40 | 13 | switch.cpp:40:10:40:11 | & ... | ptr | +| switch | switchTest2 | 40 | 14 | switch.cpp:40:4:40:6 | ptr | ... = ... | +| switch | switchTest2 | 40 | 15 | switch.cpp:40:4:40:11 | ... = ... | ExprStmt | +| switch | switchTest2 | 41 | 16 | switch.cpp:41:4:41:16 | ExprStmt | ptr | +| switch | switchTest2 | 41 | 17 | switch.cpp:41:12:41:14 | ptr | call to doThing | +| switch | switchTest2 | 41 | 18 | switch.cpp:41:4:41:10 | call to doThing | return ... | +| switch | switchTest2 | 42 | 19 | switch.cpp:42:4:42:10 | return ... | switchTest2 | +| switch | switchTest2 | 43 | 1 | switch.cpp:43:8:43:8 | 2 | | +| switch | switchTest2 | 43 | 7 | switch.cpp:43:3:43:9 | case ...: | ExprStmt | +| switch | switchTest2 | 44 | 8 | switch.cpp:44:4:44:16 | ExprStmt | ptr | +| switch | switchTest2 | 44 | 9 | switch.cpp:44:12:44:14 | ptr | call to doThing | +| switch | switchTest2 | 44 | 10 | switch.cpp:44:4:44:10 | call to doThing | ExprStmt | +| switch | switchTest2 | 45 | 11 | switch.cpp:45:4:45:12 | ExprStmt | y | +| switch | switchTest2 | 45 | 12 | switch.cpp:45:11:45:11 | y | & ... | +| switch | switchTest2 | 45 | 13 | switch.cpp:45:10:45:11 | & ... | ptr | +| switch | switchTest2 | 45 | 14 | switch.cpp:45:4:45:6 | ptr | ... = ... | +| switch | switchTest2 | 45 | 15 | switch.cpp:45:4:45:11 | ... = ... | ExprStmt | +| switch | switchTest2 | 46 | 16 | switch.cpp:46:4:46:16 | ExprStmt | ptr | +| switch | switchTest2 | 46 | 17 | switch.cpp:46:12:46:14 | ptr | call to doThing | +| switch | switchTest2 | 46 | 18 | switch.cpp:46:4:46:10 | call to doThing | return ... | +| switch | switchTest2 | 47 | 19 | switch.cpp:47:4:47:10 | return ... | switchTest2 | +| switch | switchTest2 | 50 | 7 | switch.cpp:50:2:50:14 | ExprStmt | ptr | +| switch | switchTest2 | 50 | 8 | switch.cpp:50:10:50:12 | ptr | call to doThing | +| switch | switchTest2 | 50 | 9 | switch.cpp:50:2:50:8 | call to doThing | return ... | +| switch | switchTest2 | 51 | 10 | switch.cpp:51:1:51:1 | return ... | switchTest2 | diff --git a/cpp/ql/test/library-tests/controlflow/assume/cfg.ql b/cpp/ql/test/library-tests/controlflow/assume/cfg.ql new file mode 100644 index 000000000000..521231b4958a --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/assume/cfg.ql @@ -0,0 +1,22 @@ +import cpp + +string getASuccessorOrNone(ControlFlowNode n) { + if exists(n.getASuccessor()) + then exists (ControlFlowNode s, string trueSucc, string falseSucc | + s = n.getASuccessor() + and if s = n.getATrueSuccessor() then trueSucc = " " + else trueSucc = "" + and if s = n.getAFalseSuccessor() then falseSucc = " " + else falseSucc = "" + and result = trueSucc + falseSucc + s.toString()) + else result = "" +} + +from ControlFlowNode n +select n.getLocation().getFile().getShortName(), + n.getControlFlowScope().toString(), + n.getLocation().getStartLine(), + count(n.getAPredecessor*()), // This helps order things sensibly + n, + getASuccessorOrNone(n) + diff --git a/cpp/ql/test/library-tests/controlflow/assume/interesting.expected b/cpp/ql/test/library-tests/controlflow/assume/interesting.expected new file mode 100644 index 000000000000..9ec3175bcac4 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/assume/interesting.expected @@ -0,0 +1,17 @@ +| simple.cpp:6:2:6:2 | call to f | 1 | 1 | reachable | | +| simple.cpp:8:2:8:12 | __assume(...) | 1 | 0 | reachable | | +| simple.cpp:10:2:10:2 | call to g | 1 | 1 | | | +| simple.cpp:16:2:16:2 | call to i | 1 | 1 | reachable | | +| simple.cpp:17:2:17:2 | call to h | 1 | 0 | reachable | | +| simple.cpp:18:2:18:2 | call to j | 1 | 1 | | | +| switch.cpp:11:4:11:10 | call to doThing | 1 | 1 | reachable | unassigned ptr | +| switch.cpp:13:4:13:10 | call to doThing | 1 | 1 | reachable | | +| switch.cpp:16:4:16:10 | call to doThing | 1 | 1 | reachable | unassigned ptr | +| switch.cpp:18:4:18:10 | call to doThing | 1 | 1 | reachable | | +| switch.cpp:21:4:21:14 | __assume(...) | 1 | 0 | reachable | unassigned ptr | +| switch.cpp:29:2:29:8 | call to doThing | 1 | 1 | | | +| switch.cpp:39:4:39:10 | call to doThing | 1 | 1 | reachable | unassigned ptr | +| switch.cpp:41:4:41:10 | call to doThing | 1 | 1 | reachable | | +| switch.cpp:44:4:44:10 | call to doThing | 1 | 1 | reachable | unassigned ptr | +| switch.cpp:46:4:46:10 | call to doThing | 1 | 1 | reachable | | +| switch.cpp:50:2:50:8 | call to doThing | 1 | 1 | reachable | unassigned ptr | diff --git a/cpp/ql/test/library-tests/controlflow/assume/interesting.ql b/cpp/ql/test/library-tests/controlflow/assume/interesting.ql new file mode 100644 index 000000000000..95e499b2f818 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/assume/interesting.ql @@ -0,0 +1,25 @@ +import cpp + +predicate interesting(ControlFlowNode n) { + n instanceof AssumeExpr or + n instanceof FunctionCall +} + +predicate ptrUnassigned(ControlFlowNode n) { + // declaration of `ptr` + n.(DeclStmt).getADeclaration().getName() = "ptr" or + + // flow + exists(ControlFlowNode pred | + ptrUnassigned(pred) and + pred.getASuccessor() = n and + not pred.(AssignExpr).getLValue().(Access).getTarget().getName() = "ptr" + ) +} + +from ControlFlowNode n, string r, string p +where + interesting(n) and + if reachable(n) then r = "reachable" else r = "" and + if ptrUnassigned(n) then p = "unassigned ptr" else p = "" +select n, count(n.getAPredecessor()), count(n.getASuccessor()), r, p diff --git a/cpp/ql/test/library-tests/controlflow/assume/options b/cpp/ql/test/library-tests/controlflow/assume/options new file mode 100644 index 000000000000..91adeb70204c --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/assume/options @@ -0,0 +1 @@ +extractor_flags: --microsoft diff --git a/cpp/ql/test/library-tests/controlflow/assume/simple.cpp b/cpp/ql/test/library-tests/controlflow/assume/simple.cpp new file mode 100644 index 000000000000..243510f412d9 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/assume/simple.cpp @@ -0,0 +1,19 @@ + +void f(); +void g(); +void h() +{ + f(); + + __assume(0); + + g(); // unreachable +} +void i(); +void j(); +void k() +{ + i(); + h(); + j(); // unreachable +} diff --git a/cpp/ql/test/library-tests/controlflow/assume/switch.cpp b/cpp/ql/test/library-tests/controlflow/assume/switch.cpp new file mode 100644 index 000000000000..1068b4e12b07 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/assume/switch.cpp @@ -0,0 +1,51 @@ + +void doThing(int *ptr); + +void switchTest1(int i) +{ + int *ptr; + int x, y; + + switch (i) { + case 1: + doThing(ptr); // ptr can't be assumed assigned here + ptr = &x; + doThing(ptr); // ptr can be assumed assigned here + return; + case 2: + doThing(ptr); // ptr can't be assumed assigned here + ptr = &y; + doThing(ptr); // ptr can be assumed assigned here + return; + default: + __assume(0); + // This tells the optimizer that the default + // cannot be reached. As so, it does not have to generate + // the extra code to check that 'p' has a value + // not represented by a case arm. This makes the switch + // run faster. + } + + doThing(ptr); // ptr can be assumed assigned here +} + +void switchTest2(int i) +{ + int *ptr; + int x, y; + + switch (i) { + case 1: + doThing(ptr); // ptr can't be assumed assigned here + ptr = &x; + doThing(ptr); // ptr can be assumed assigned here + return; + case 2: + doThing(ptr); // ptr can't be assumed assigned here + ptr = &y; + doThing(ptr); // ptr can be assumed assigned here + return; + } + + doThing(ptr); // ptr can't be assumed assigned here +} diff --git a/cpp/ql/test/library-tests/controlflow/controlflow/SsaCompleteness.expected b/cpp/ql/test/library-tests/controlflow/controlflow/SsaCompleteness.expected new file mode 100644 index 000000000000..f082a67fcf66 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/controlflow/SsaCompleteness.expected @@ -0,0 +1 @@ +| 0 | diff --git a/cpp/ql/test/library-tests/controlflow/controlflow/SsaCompleteness.ql b/cpp/ql/test/library-tests/controlflow/controlflow/SsaCompleteness.ql new file mode 100644 index 000000000000..8cce68940c98 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/controlflow/SsaCompleteness.ql @@ -0,0 +1,21 @@ +/** + * @name Ssa completeness test + * @description Ssa completeness test. If there is a SsaDefinition for a variable that reaches a use of that variable + * then there must be a SsaDefinition for that use + * @kind test + */ + +import cpp +import semmle.code.cpp.controlflow.SSA + +/* Count of number of uses of a LocalScopeVariable where no corresponding SSA definition exists, + but at least one SSA definition for that variable can reach that use. + Should always be zero *regardless* of the input */ +select +count(LocalScopeVariable v, Expr use | + exists(SsaDefinition def, BasicBlock db, BasicBlock ub | + def.getAUse(v) = use and db.contains(def.getDefinition()) and ub.contains(use) | + db.getASuccessor+() = ub + ) + and not exists(SsaDefinition def | def.getAUse(v) = use) +) \ No newline at end of file diff --git a/cpp/ql/test/library-tests/controlflow/controlflow/SsaCorrespondence.expected b/cpp/ql/test/library-tests/controlflow/controlflow/SsaCorrespondence.expected new file mode 100644 index 000000000000..f082a67fcf66 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/controlflow/SsaCorrespondence.expected @@ -0,0 +1 @@ +| 0 | diff --git a/cpp/ql/test/library-tests/controlflow/controlflow/SsaCorrespondence.ql b/cpp/ql/test/library-tests/controlflow/controlflow/SsaCorrespondence.ql new file mode 100644 index 000000000000..8eec528ddff5 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/controlflow/SsaCorrespondence.ql @@ -0,0 +1,23 @@ +/** + * @name Ssa correspondence test + * @description Ssa correspondence test. For each reachable definition or parameter there should be a corresponding SSA definition. + * @kind test + */ + +import cpp +import semmle.code.cpp.controlflow.SSA +import semmle.code.cpp.controlflow.SSAUtils + +/* Count of number of reachable definitions or parameters where the no corresponding SSA definition exists. + Should always be zero *regardless* of the input */ + +select +count(Variable v, ControlFlowNode def | var_definition(v, def) and not unreachable(def) and + not exists(SsaDefinition d | d.getAVariable() = v and d.getDefinition() = def) +) ++ +count(Parameter p | + exists(p.getAnAccess()) + and + not exists(SsaDefinition d | d.getAVariable() = p and d.getDefinition() = p.getFunction().getEntryPoint()) +) \ No newline at end of file diff --git a/cpp/ql/test/library-tests/controlflow/controlflow/SsaDefUsePairs.expected b/cpp/ql/test/library-tests/controlflow/controlflow/SsaDefUsePairs.expected new file mode 100644 index 000000000000..8c75de11e02a --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/controlflow/SsaDefUsePairs.expected @@ -0,0 +1,89 @@ +| pass_by_ref.cpp:9:19:28:1 | SSA definition | SSA def(n) | pass_by_ref.cpp:18:12:18:12 | n | +| pass_by_ref.cpp:9:19:28:1 | SSA definition | SSA def(n) | pass_by_ref.cpp:21:12:21:12 | n | +| pass_by_ref.cpp:12:18:12:35 | SSA definition | SSA def(uninitializedArray) | pass_by_ref.cpp:13:20:13:37 | uninitializedArray | +| pass_by_ref.cpp:13:20:13:37 | SSA definition | SSA def(uninitializedArray) | pass_by_ref.cpp:15:23:15:40 | uninitializedArray | +| pass_by_ref.cpp:13:20:13:37 | SSA definition | SSA def(uninitializedArray) | pass_by_ref.cpp:16:25:16:42 | uninitializedArray | +| pass_by_ref.cpp:13:20:13:37 | SSA definition | SSA def(uninitializedArray) | pass_by_ref.cpp:27:10:27:27 | uninitializedArray | +| pass_by_ref.cpp:18:12:18:12 | SSA definition | SSA def(i1) | pass_by_ref.cpp:19:22:19:23 | i1 | +| pass_by_ref.cpp:19:22:19:23 | SSA definition | SSA def(i1) | pass_by_ref.cpp:27:34:27:35 | i1 | +| pass_by_ref.cpp:21:12:21:12 | SSA definition | SSA def(i2) | pass_by_ref.cpp:22:21:22:22 | i2 | +| pass_by_ref.cpp:22:20:22:22 | SSA definition | SSA def(i2) | pass_by_ref.cpp:24:26:24:27 | i2 | +| pass_by_ref.cpp:22:20:22:22 | SSA definition | SSA def(i2) | pass_by_ref.cpp:25:27:25:28 | i2 | +| pass_by_ref.cpp:22:20:22:22 | SSA definition | SSA def(i2) | pass_by_ref.cpp:27:39:27:40 | i2 | +| pass_by_ref.cpp:32:12:32:12 | SSA definition | SSA phi(arr) | pass_by_ref.cpp:33:20:33:22 | arr | +| pass_by_ref.cpp:32:12:32:12 | SSA definition | SSA phi(n) | pass_by_ref.cpp:32:12:32:12 | n | +| pass_by_ref.cpp:39:12:39:12 | SSA definition | SSA phi(arr) | pass_by_ref.cpp:40:22:40:24 | arr | +| pass_by_ref.cpp:39:12:39:12 | SSA definition | SSA phi(n) | pass_by_ref.cpp:39:12:39:12 | n | +| pass_by_ref.cpp:40:22:40:24 | SSA definition | SSA def(arr) | pass_by_ref.cpp:41:20:41:22 | arr | +| pass_by_ref.cpp:45:20:55:1 | SSA definition | SSA def(n) | pass_by_ref.cpp:46:11:46:11 | n | +| pass_by_ref.cpp:45:20:55:1 | SSA definition | SSA def(n) | pass_by_ref.cpp:48:7:48:7 | n | +| pass_by_ref.cpp:46:11:46:11 | SSA definition | SSA def(i) | pass_by_ref.cpp:49:5:49:5 | i | +| pass_by_ref.cpp:53:3:53:24 | SSA definition | SSA phi(i) | pass_by_ref.cpp:53:22:53:22 | i | +| pass_by_ref.cpp:53:22:53:22 | SSA definition | SSA def(i) | pass_by_ref.cpp:54:10:54:10 | i | +| test.c:2:31:72:1 | SSA definition | SSA def(x) | test.c:7:9:7:9 | x | +| test.c:2:31:72:1 | SSA definition | SSA def(x) | test.c:14:9:14:9 | x | +| test.c:2:31:72:1 | SSA definition | SSA def(x) | test.c:17:8:17:8 | x | +| test.c:2:31:72:1 | SSA definition | SSA def(x) | test.c:26:9:26:9 | x | +| test.c:2:31:72:1 | SSA definition | SSA def(x) | test.c:31:10:31:10 | x | +| test.c:14:5:14:13 | SSA definition | SSA def(z) | test.c:20:16:20:16 | z | +| test.c:14:5:14:14 | SSA definition | SSA phi(y) | test.c:14:13:14:13 | y | +| test.c:31:5:31:10 | SSA definition | SSA def(z) | test.c:39:5:39:5 | z | +| test.c:31:5:31:11 | SSA definition | SSA phi(z) | test.c:31:5:31:5 | z | +| test.c:34:11:34:11 | SSA definition | SSA phi(x) | test.c:34:11:34:11 | x | +| test.c:34:11:34:11 | SSA definition | SSA phi(x) | test.c:36:9:36:9 | x | +| test.c:34:11:34:11 | SSA definition | SSA phi(y) | test.c:39:10:39:10 | y | +| test.c:39:5:39:10 | SSA definition | SSA def(z) | test.c:47:5:47:5 | z | +| test.c:42:16:42:16 | SSA definition | SSA phi(j) | test.c:42:16:42:16 | j | +| test.c:42:16:42:16 | SSA definition | SSA phi(j) | test.c:42:24:42:24 | j | +| test.c:42:16:42:16 | SSA definition | SSA phi(w) | test.c:47:10:47:10 | w | +| test.c:47:5:47:10 | SSA definition | SSA def(z) | test.c:52:12:52:12 | z | +| test.c:47:5:47:10 | SSA definition | SSA def(z) | test.c:66:5:66:5 | z | +| test.c:50:16:50:16 | SSA definition | SSA phi(j) | test.c:50:16:50:16 | j | +| test.c:50:16:50:16 | SSA definition | SSA phi(j) | test.c:50:24:50:24 | j | +| test.c:50:16:50:16 | SSA definition | SSA phi(x) | test.c:66:10:66:10 | x | +| test.c:51:9:51:14 | SSA definition | SSA def(y) | test.c:53:16:53:16 | y | +| test.c:64:5:64:5 | SSA definition | SSA phi(w) | test.c:66:18:66:18 | w | +| test.c:64:5:64:5 | SSA definition | SSA phi(y) | test.c:66:14:66:14 | y | +| test.c:70:5:70:10 | SSA definition | SSA def(w) | test.c:71:12:71:12 | w | +| test.c:74:19:89:1 | SSA definition | SSA def(a) | test.c:79:13:79:13 | a | +| test.c:74:19:89:1 | SSA definition | SSA def(a) | test.c:83:13:83:13 | a | +| test.c:74:19:89:1 | SSA definition | SSA def(a) | test.c:85:13:85:13 | a | +| test.c:80:13:80:18 | SSA definition | SSA def(c) | test.c:81:17:81:17 | c | +| test.c:83:9:84:18 | SSA definition | SSA phi(b) | test.c:88:12:88:12 | b | +| test.c:83:9:84:18 | SSA definition | SSA phi(c) | test.c:86:20:86:20 | c | +| test.c:91:33:97:1 | SSA definition | SSA def(cond) | test.c:93:9:93:12 | cond | +| test.c:96:5:96:11 | SSA definition | SSA phi(x) | test.c:96:9:96:9 | x | +| test.cpp:2:19:17:1 | SSA definition | SSA def(p) | test.cpp:3:11:3:11 | p | +| test.cpp:2:19:17:1 | SSA definition | SSA def(p) | test.cpp:5:13:5:13 | p | +| test.cpp:5:13:5:13 | SSA definition | SSA def(r0) | test.cpp:16:10:16:11 | r0 | +| test.cpp:6:13:6:13 | SSA definition | SSA def(r1) | test.cpp:7:13:7:14 | r1 | +| test.cpp:6:13:6:13 | SSA definition | SSA def(r1) | test.cpp:13:7:13:8 | r1 | +| test.cpp:6:13:6:13 | SSA definition | SSA def(r1) | test.cpp:16:15:16:16 | r1 | +| test.cpp:7:13:7:14 | SSA definition | SSA def(r2) | test.cpp:8:13:8:14 | r2 | +| test.cpp:7:13:7:14 | SSA definition | SSA def(r2) | test.cpp:10:5:10:6 | r2 | +| test.cpp:7:13:7:14 | SSA definition | SSA def(r2) | test.cpp:16:20:16:21 | r2 | +| test.cpp:8:12:8:14 | SSA definition | SSA def(q) | test.cpp:11:6:11:6 | q | +| test.cpp:8:12:8:14 | SSA definition | SSA def(q) | test.cpp:11:11:11:11 | q | +| test.cpp:8:12:8:14 | SSA definition | SSA def(q) | test.cpp:16:26:16:26 | q | +| test.cpp:9:19:9:19 | SSA definition | SSA phi(i) | test.cpp:9:19:9:19 | i | +| test.cpp:9:19:9:19 | SSA definition | SSA phi(i) | test.cpp:9:27:9:27 | i | +| test.cpp:9:19:9:19 | SSA definition | SSA phi(i) | test.cpp:12:9:12:9 | i | +| test.cpp:19:27:21:1 | SSA definition | SSA def(x) | test.cpp:20:3:20:3 | x | +| test.cpp:23:27:25:1 | SSA definition | SSA def(x) | test.cpp:24:5:24:5 | x | +| test.cpp:33:10:33:11 | SSA definition | SSA def(x) | test.cpp:35:19:35:19 | x | +| test.cpp:33:10:33:11 | SSA definition | SSA def(x) | test.cpp:36:19:36:19 | x | +| test.cpp:33:10:33:11 | SSA definition | SSA def(x) | test.cpp:37:22:37:22 | x | +| test.cpp:33:10:33:11 | SSA definition | SSA def(x) | test.cpp:41:16:41:16 | x | +| test.cpp:34:10:34:11 | SSA definition | SSA def(y) | test.cpp:38:20:38:20 | y | +| test.cpp:34:10:34:11 | SSA definition | SSA def(y) | test.cpp:39:20:39:20 | y | +| test.cpp:34:10:34:11 | SSA definition | SSA def(y) | test.cpp:40:23:40:23 | y | +| test.cpp:35:19:35:19 | SSA definition | SSA def(r1) | test.cpp:43:14:43:15 | r1 | +| test.cpp:36:19:36:19 | SSA definition | SSA def(r2) | test.cpp:43:19:43:20 | r2 | +| test.cpp:37:22:37:22 | SSA definition | SSA def(r3) | test.cpp:43:24:43:25 | r3 | +| test.cpp:38:19:38:20 | SSA definition | SSA def(p1) | test.cpp:43:30:43:31 | p1 | +| test.cpp:39:19:39:20 | SSA definition | SSA def(p2) | test.cpp:43:36:43:37 | p2 | +| test.cpp:40:22:40:23 | SSA definition | SSA def(p3) | test.cpp:43:42:43:43 | p3 | +| test.cpp:41:16:41:16 | SSA definition | SSA def(x) | test.cpp:42:17:42:17 | x | +| test.cpp:42:16:42:17 | SSA definition | SSA def(x) | test.cpp:43:10:43:10 | x | +| test.cpp:47:10:47:11 | SSA definition | SSA def(x) | test.cpp:48:27:48:27 | x | +| test.cpp:47:10:47:11 | SSA definition | SSA def(x) | test.cpp:49:10:49:10 | x | diff --git a/cpp/ql/test/library-tests/controlflow/controlflow/SsaDefUsePairs.ql b/cpp/ql/test/library-tests/controlflow/controlflow/SsaDefUsePairs.ql new file mode 100644 index 000000000000..2328645e098c --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/controlflow/SsaDefUsePairs.ql @@ -0,0 +1,12 @@ +/** + * @name Ssa def-use pairs test + * @description List all the uses for each SsaDefinition + * @kind test + */ + +import cpp +import semmle.code.cpp.controlflow.SSA + +from SsaDefinition def, LocalScopeVariable var, Expr use +where def.getAUse(var) = use +select def, def.toString(var), use diff --git a/cpp/ql/test/library-tests/controlflow/controlflow/SsaDefinedByParameter.expected b/cpp/ql/test/library-tests/controlflow/controlflow/SsaDefinedByParameter.expected new file mode 100644 index 000000000000..54a1f5d77298 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/controlflow/SsaDefinedByParameter.expected @@ -0,0 +1,22 @@ +| pass_by_ref.cpp:9:19:28:1 | SSA definition | pass_by_ref.cpp:9:16:9:16 | n | +| pass_by_ref.cpp:30:18:34:1 | SSA definition | pass_by_ref.cpp:30:15:30:15 | n | +| pass_by_ref.cpp:36:19:43:1 | SSA definition | pass_by_ref.cpp:36:16:36:16 | n | +| pass_by_ref.cpp:45:20:55:1 | SSA definition | pass_by_ref.cpp:45:17:45:17 | n | +| test.c:2:31:72:1 | SSA definition | test.c:2:14:2:14 | x | +| test.c:2:31:72:1 | SSA definition | test.c:2:21:2:21 | w | +| test.c:2:31:72:1 | SSA definition | test.c:2:28:2:28 | z | +| test.c:74:19:89:1 | SSA definition | test.c:74:16:74:16 | a | +| test.c:91:33:97:1 | SSA definition | test.c:91:27:91:30 | cond | +| test.cpp:2:19:17:1 | SSA definition | test.cpp:2:16:2:16 | p | +| test.cpp:19:27:21:1 | SSA definition | test.cpp:19:24:19:24 | x | +| test.cpp:23:27:25:1 | SSA definition | test.cpp:23:24:23:24 | x | +| test.cpp:93:3:93:4 | SSA definition | test.cpp:72:12:72:16 | nextp | +| test.cpp:93:3:93:4 | SSA definition | test.cpp:73:12:73:16 | nextr | +| test.cpp:93:3:93:4 | SSA definition | test.cpp:74:18:74:23 | nextcp | +| test.cpp:93:3:93:4 | SSA definition | test.cpp:75:18:75:23 | nextcr | +| test.cpp:93:3:93:4 | SSA definition | test.cpp:76:9:76:9 | i | +| test.cpp:93:3:93:4 | SSA definition | test.cpp:77:10:77:11 | ip | +| test.cpp:93:3:93:4 | SSA definition | test.cpp:78:10:78:11 | ir | +| test.cpp:93:3:93:4 | SSA definition | test.cpp:79:15:79:16 | ci | +| test.cpp:93:3:93:4 | SSA definition | test.cpp:80:16:80:18 | cip | +| test.cpp:93:3:93:4 | SSA definition | test.cpp:81:16:81:18 | cir | diff --git a/cpp/ql/test/library-tests/controlflow/controlflow/SsaDefinedByParameter.ql b/cpp/ql/test/library-tests/controlflow/controlflow/SsaDefinedByParameter.ql new file mode 100644 index 000000000000..fa724c49c442 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/controlflow/SsaDefinedByParameter.ql @@ -0,0 +1,12 @@ +/** + * @name Ssa definedByParameter test + * @description check the results of the definedByParameter method + * @kind test + */ + +import cpp +import semmle.code.cpp.controlflow.SSA + +from SsaDefinition def, Parameter p +where def.definedByParameter(p) +select def, p diff --git a/cpp/ql/test/library-tests/controlflow/controlflow/SsaDominance.expected b/cpp/ql/test/library-tests/controlflow/controlflow/SsaDominance.expected new file mode 100644 index 000000000000..f082a67fcf66 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/controlflow/SsaDominance.expected @@ -0,0 +1 @@ +| 0 | diff --git a/cpp/ql/test/library-tests/controlflow/controlflow/SsaDominance.ql b/cpp/ql/test/library-tests/controlflow/controlflow/SsaDominance.ql new file mode 100644 index 000000000000..78a6f4d4a891 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/controlflow/SsaDominance.ql @@ -0,0 +1,21 @@ +/** + * @name SSA dominance property test + * @description SSA dominance property test. SSA definitions *must* dominate all uses + * @kind test + */ + +import cpp +import semmle.code.cpp.controlflow.SSA + +/* Count of number of SSA def-use pairs where the defn does not dominate the use. + Should always be zero *regardless* of the input */ + +select +count(SsaDefinition d, LocalScopeVariable v, Expr u | + d.getAUse(v) = u and + not exists(BasicBlock bd, BasicBlock bu | bd.contains((ControlFlowNode)d) and bu.contains(u) | + bbStrictlyDominates(bd, bu) + or + exists(int i, int j | bd = bu and bd.getNode(i) = d and bu.getNode(j) = u and i <= j) + ) +) \ No newline at end of file diff --git a/cpp/ql/test/library-tests/controlflow/controlflow/SsaLt.expected b/cpp/ql/test/library-tests/controlflow/controlflow/SsaLt.expected new file mode 100644 index 000000000000..d73a83461567 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/controlflow/SsaLt.expected @@ -0,0 +1,56 @@ +| test.c:2:31:72:1 | SSA definition | test.c:2:14:2:14 | x | < | test.c:7:13:7:13 | 0 | 10 | 11 | +| test.c:2:31:72:1 | SSA definition | test.c:2:14:2:14 | x | < | test.c:17:12:17:12 | 0 | 18 | 26 | +| test.c:2:31:72:1 | SSA definition | test.c:2:14:2:14 | x | < | test.c:17:12:17:12 | 0 | 26 | 28 | +| test.c:2:31:72:1 | SSA definition | test.c:2:14:2:14 | x | < | test.c:17:12:17:12 | 0 | 31 | 37 | +| test.c:2:31:72:1 | SSA definition | test.c:2:14:2:14 | x | < | test.c:17:12:17:12 | 0 | 34 | 34 | +| test.c:2:31:72:1 | SSA definition | test.c:2:14:2:14 | x | < | test.c:17:12:17:12 | 0 | 34 | 36 | +| test.c:2:31:72:1 | SSA definition | test.c:2:14:2:14 | x | < | test.c:17:12:17:12 | 0 | 39 | 42 | +| test.c:2:31:72:1 | SSA definition | test.c:2:14:2:14 | x | < | test.c:17:12:17:12 | 0 | 42 | 42 | +| test.c:2:31:72:1 | SSA definition | test.c:2:14:2:14 | x | < | test.c:17:12:17:12 | 0 | 47 | 50 | +| test.c:2:31:72:1 | SSA definition | test.c:2:14:2:14 | x | < | test.c:17:12:17:12 | 0 | 50 | 50 | +| test.c:2:31:72:1 | SSA definition | test.c:2:14:2:14 | x | < | test.c:17:12:17:12 | 0 | 50 | 52 | +| test.c:2:31:72:1 | SSA definition | test.c:2:14:2:14 | x | < | test.c:17:12:17:12 | 0 | 53 | 53 | +| test.c:2:31:72:1 | SSA definition | test.c:2:14:2:14 | x | < | test.c:17:12:17:12 | 0 | 53 | 55 | +| test.c:2:31:72:1 | SSA definition | test.c:2:14:2:14 | x | < | test.c:17:12:17:12 | 0 | 56 | 63 | +| test.c:2:31:72:1 | SSA definition | test.c:2:14:2:14 | x | < | test.c:17:12:17:12 | 0 | 59 | 61 | +| test.c:2:31:72:1 | SSA definition | test.c:2:14:2:14 | x | < | test.c:17:12:17:12 | 0 | 64 | 71 | +| test.c:2:31:72:1 | SSA definition | test.c:2:14:2:14 | x | > | test.c:7:13:7:13 | 0 | 7 | 9 | +| test.c:2:31:72:1 | SSA definition | test.c:2:14:2:14 | x | > | test.c:17:12:17:12 | 0 | 20 | 20 | +| test.c:34:11:34:11 | SSA definition | test.c:2:14:2:14 | x | < | test.c:34:15:34:15 | 0 | 39 | 42 | +| test.c:34:11:34:11 | SSA definition | test.c:2:14:2:14 | x | < | test.c:34:15:34:15 | 0 | 42 | 42 | +| test.c:34:11:34:11 | SSA definition | test.c:2:14:2:14 | x | < | test.c:34:15:34:15 | 0 | 47 | 50 | +| test.c:34:11:34:11 | SSA definition | test.c:2:14:2:14 | x | < | test.c:34:15:34:15 | 0 | 50 | 50 | +| test.c:34:11:34:11 | SSA definition | test.c:2:14:2:14 | x | < | test.c:34:15:34:15 | 0 | 50 | 52 | +| test.c:34:11:34:11 | SSA definition | test.c:2:14:2:14 | x | < | test.c:34:15:34:15 | 0 | 53 | 53 | +| test.c:34:11:34:11 | SSA definition | test.c:2:14:2:14 | x | < | test.c:34:15:34:15 | 0 | 53 | 55 | +| test.c:34:11:34:11 | SSA definition | test.c:2:14:2:14 | x | < | test.c:34:15:34:15 | 0 | 56 | 63 | +| test.c:34:11:34:11 | SSA definition | test.c:2:14:2:14 | x | < | test.c:34:15:34:15 | 0 | 59 | 61 | +| test.c:34:11:34:11 | SSA definition | test.c:2:14:2:14 | x | < | test.c:34:15:34:15 | 0 | 64 | 71 | +| test.c:34:11:34:11 | SSA definition | test.c:2:14:2:14 | x | > | test.c:34:15:34:15 | 0 | 34 | 36 | +| test.c:42:16:42:16 | SSA definition | test.c:3:9:3:9 | j | < | test.c:42:20:42:21 | 10 | 42 | 42 | +| test.c:42:16:42:16 | SSA definition | test.c:3:9:3:9 | j | > | test.c:42:20:42:21 | 10 | 47 | 50 | +| test.c:42:16:42:16 | SSA definition | test.c:3:9:3:9 | j | > | test.c:42:20:42:21 | 10 | 50 | 50 | +| test.c:42:16:42:16 | SSA definition | test.c:3:9:3:9 | j | > | test.c:42:20:42:21 | 10 | 50 | 52 | +| test.c:42:16:42:16 | SSA definition | test.c:3:9:3:9 | j | > | test.c:42:20:42:21 | 10 | 53 | 53 | +| test.c:42:16:42:16 | SSA definition | test.c:3:9:3:9 | j | > | test.c:42:20:42:21 | 10 | 53 | 55 | +| test.c:42:16:42:16 | SSA definition | test.c:3:9:3:9 | j | > | test.c:42:20:42:21 | 10 | 56 | 63 | +| test.c:42:16:42:16 | SSA definition | test.c:3:9:3:9 | j | > | test.c:42:20:42:21 | 10 | 59 | 61 | +| test.c:42:16:42:16 | SSA definition | test.c:3:9:3:9 | j | > | test.c:42:20:42:21 | 10 | 64 | 71 | +| test.c:47:5:47:10 | SSA definition | test.c:2:28:2:28 | z | < | test.c:52:16:52:16 | 0 | 59 | 61 | +| test.c:47:5:47:10 | SSA definition | test.c:2:28:2:28 | z | > | test.c:52:16:52:16 | 0 | 53 | 53 | +| test.c:47:5:47:10 | SSA definition | test.c:2:28:2:28 | z | > | test.c:52:16:52:16 | 0 | 53 | 55 | +| test.c:47:5:47:10 | SSA definition | test.c:2:28:2:28 | z | > | test.c:52:16:52:16 | 0 | 56 | 63 | +| test.c:50:16:50:16 | SSA definition | test.c:3:9:3:9 | j | < | test.c:50:20:50:21 | 10 | 50 | 50 | +| test.c:50:16:50:16 | SSA definition | test.c:3:9:3:9 | j | < | test.c:50:20:50:21 | 10 | 50 | 52 | +| test.c:50:16:50:16 | SSA definition | test.c:3:9:3:9 | j | < | test.c:50:20:50:21 | 10 | 53 | 53 | +| test.c:50:16:50:16 | SSA definition | test.c:3:9:3:9 | j | < | test.c:50:20:50:21 | 10 | 53 | 55 | +| test.c:50:16:50:16 | SSA definition | test.c:3:9:3:9 | j | < | test.c:50:20:50:21 | 10 | 56 | 63 | +| test.c:50:16:50:16 | SSA definition | test.c:3:9:3:9 | j | < | test.c:50:20:50:21 | 10 | 59 | 61 | +| test.c:51:9:51:14 | SSA definition | test.c:4:10:4:10 | y | < | test.c:53:20:53:20 | 0 | 56 | 63 | +| test.c:51:9:51:14 | SSA definition | test.c:4:10:4:10 | y | > | test.c:53:20:53:20 | 0 | 53 | 55 | +| test.c:74:19:89:1 | SSA definition | test.c:74:16:74:16 | a | > | test.c:79:17:79:19 | 100 | 79 | 81 | +| test.cpp:9:19:9:19 | SSA definition | test.cpp:9:12:9:12 | i | < | test.cpp:9:23:9:24 | 10 | 9 | 9 | +| test.cpp:9:19:9:19 | SSA definition | test.cpp:9:12:9:12 | i | < | test.cpp:9:23:9:24 | 10 | 9 | 12 | +| test.cpp:9:19:9:19 | SSA definition | test.cpp:9:12:9:12 | i | < | test.cpp:9:23:9:24 | 10 | 12 | 13 | +| test.cpp:9:19:9:19 | SSA definition | test.cpp:9:12:9:12 | i | > | test.cpp:9:23:9:24 | 10 | 16 | 2 | +| test.cpp:9:19:9:19 | SSA definition | test.cpp:9:12:9:12 | i | > | test.cpp:12:13:12:13 | 5 | 12 | 13 | diff --git a/cpp/ql/test/library-tests/controlflow/controlflow/SsaLt.ql b/cpp/ql/test/library-tests/controlflow/controlflow/SsaLt.ql new file mode 100644 index 000000000000..7b3cc49c6826 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/controlflow/SsaLt.ql @@ -0,0 +1,20 @@ +/** + * @name Ssa def-use pairs test + * @description List all an ensured limits. + * @kind test + */ + +import cpp +import semmle.code.cpp.controlflow.SSA +import semmle.code.cpp.controlflow.Guards + +from GuardedSsa def, LocalScopeVariable var, Expr other, int k, int start, int end, string op +where +exists(BasicBlock block | + def.isLt(var, other, k, block, true) and op = "<" + or + def.isLt(var, other, k, block, false) and op = ">" | + block.hasLocationInfo(_, start, _, end, _) +) + +select def, var, op, other, start, end diff --git a/cpp/ql/test/library-tests/controlflow/controlflow/SsaPhiInputs.expected b/cpp/ql/test/library-tests/controlflow/controlflow/SsaPhiInputs.expected new file mode 100644 index 000000000000..fc3dea956cd4 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/controlflow/SsaPhiInputs.expected @@ -0,0 +1,46 @@ +| pass_by_ref.cpp | 32 | SSA phi(arr) | 31 | SSA def(arr) | +| pass_by_ref.cpp | 32 | SSA phi(arr) | 33 | SSA def(arr) | +| pass_by_ref.cpp | 32 | SSA phi(n) | 30 | SSA def(n) | +| pass_by_ref.cpp | 32 | SSA phi(n) | 32 | SSA def(n) | +| pass_by_ref.cpp | 39 | SSA phi(arr) | 37 | SSA def(arr) | +| pass_by_ref.cpp | 39 | SSA phi(arr) | 41 | SSA def(arr) | +| pass_by_ref.cpp | 39 | SSA phi(n) | 36 | SSA def(n) | +| pass_by_ref.cpp | 39 | SSA phi(n) | 39 | SSA def(n) | +| pass_by_ref.cpp | 53 | SSA phi(i) | 46 | SSA def(i) | +| pass_by_ref.cpp | 53 | SSA phi(i) | 49 | SSA def(i) | +| test.c | 14 | SSA phi(y) | 8 | SSA def(y) | +| test.c | 14 | SSA phi(y) | 11 | SSA def(y) | +| test.c | 31 | SSA phi(y) | 18 | SSA def(y) | +| test.c | 31 | SSA phi(y) | 27 | SSA def(y) | +| test.c | 31 | SSA phi(z) | 23 | SSA def(z) | +| test.c | 31 | SSA phi(z) | 28 | SSA def(z) | +| test.c | 34 | SSA phi(x) | 2 | SSA def(x) | +| test.c | 34 | SSA phi(x) | 36 | SSA def(x) | +| test.c | 34 | SSA phi(y) | 31 | SSA phi(y) | +| test.c | 34 | SSA phi(y) | 35 | SSA def(y) | +| test.c | 42 | SSA phi(j) | 42 | SSA def(j) | +| test.c | 42 | SSA phi(w) | 2 | SSA def(w) | +| test.c | 42 | SSA phi(w) | 44 | SSA def(w) | +| test.c | 42 | SSA phi(y) | 34 | SSA phi(y) | +| test.c | 42 | SSA phi(y) | 43 | SSA def(y) | +| test.c | 50 | SSA phi(j) | 50 | SSA def(j) | +| test.c | 50 | SSA phi(w) | 42 | SSA phi(w) | +| test.c | 50 | SSA phi(w) | 50 | SSA phi(w) | +| test.c | 50 | SSA phi(w) | 57 | SSA def(w) | +| test.c | 50 | SSA phi(w) | 60 | SSA def(w) | +| test.c | 50 | SSA phi(x) | 34 | SSA phi(x) | +| test.c | 50 | SSA phi(x) | 50 | SSA phi(x) | +| test.c | 50 | SSA phi(x) | 63 | SSA def(x) | +| test.c | 50 | SSA phi(y) | 42 | SSA phi(y) | +| test.c | 50 | SSA phi(y) | 51 | SSA def(y) | +| test.c | 64 | SSA phi(w) | 50 | SSA phi(w) | +| test.c | 64 | SSA phi(w) | 54 | SSA def(w) | +| test.c | 64 | SSA phi(y) | 50 | SSA phi(y) | +| test.c | 64 | SSA phi(y) | 51 | SSA def(y) | +| test.c | 77 | SSA phi(c) | 83 | SSA phi(c) | +| test.c | 83 | SSA phi(b) | 78 | SSA def(b) | +| test.c | 83 | SSA phi(b) | 81 | SSA def(b) | +| test.c | 83 | SSA phi(c) | 77 | SSA phi(c) | +| test.c | 83 | SSA phi(c) | 80 | SSA def(c) | +| test.c | 96 | SSA phi(x) | 94 | SSA def(x) | +| test.cpp | 9 | SSA phi(i) | 9 | SSA def(i) | diff --git a/cpp/ql/test/library-tests/controlflow/controlflow/SsaPhiInputs.ql b/cpp/ql/test/library-tests/controlflow/controlflow/SsaPhiInputs.ql new file mode 100644 index 000000000000..981db4ebb648 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/controlflow/SsaPhiInputs.ql @@ -0,0 +1,15 @@ +/** + * @name Ssa phi-node inputs test + * @description List all the inputs for each SSA phi-node + * @kind test + */ + +import cpp +import semmle.code.cpp.controlflow.SSA + +from File file, SsaDefinition phi, LocalScopeVariable var, SsaDefinition input, int philine, int inputline +where phi.getAPhiInput(var) = input and +file = phi.getLocation().getFile() and +philine = phi.getLocation().getStartLine() and +inputline = input.getLocation().getStartLine() +select file.getAbsolutePath(), philine, phi.toString(var), inputline, input.toString(var) diff --git a/cpp/ql/test/library-tests/controlflow/controlflow/SsaUniqueness.expected b/cpp/ql/test/library-tests/controlflow/controlflow/SsaUniqueness.expected new file mode 100644 index 000000000000..f082a67fcf66 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/controlflow/SsaUniqueness.expected @@ -0,0 +1 @@ +| 0 | diff --git a/cpp/ql/test/library-tests/controlflow/controlflow/SsaUniqueness.ql b/cpp/ql/test/library-tests/controlflow/controlflow/SsaUniqueness.ql new file mode 100644 index 000000000000..f7938c8e30cf --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/controlflow/SsaUniqueness.ql @@ -0,0 +1,17 @@ +/** + * @name SSA unique definition test + * @description SSA unique definition test. For each use there must be zero or one SSA definitions. + * @kind test + */ + +import cpp +import semmle.code.cpp.controlflow.SSA + +/* Count of number of uses where the number of SSA definitions exceeds one. + Should always be zero *regardless* of the input */ + +select +count(SsaDefinition d1, SsaDefinition d2, Expr u, LocalScopeVariable v | + d1.getAUse(v) = u and + d2.getAUse(v) = u and not d1 = d2 +) diff --git a/cpp/ql/test/library-tests/controlflow/controlflow/pass_by_ref.cpp b/cpp/ql/test/library-tests/controlflow/controlflow/pass_by_ref.cpp new file mode 100644 index 000000000000..2633786d2434 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/controlflow/pass_by_ref.cpp @@ -0,0 +1,55 @@ +void arrayParameter(int a[4]); +void pointerParameter(int *a); +void referenceParameter(int &n); + +void constArrayParameter(const int a[4]); +void constPointerParameter(const int *a); +void constReferenceParameter(const int &n); + +int caller(int n) { + int uninitializedArray[4]; + + arrayParameter(uninitializedArray); + pointerParameter(uninitializedArray); + + constArrayParameter(uninitializedArray); + constPointerParameter(uninitializedArray); + + int i1 = n; + referenceParameter(i1); + + int i2 = n; + pointerParameter(&i2); + + constPointerParameter(&i2); + constReferenceParameter(i2); + + return uninitializedArray[0] + i1 + i2; +} + +void loop(int n) { + int arr[4] = {0}; + while (--n) + arrayParameter(arr); +} + +void loop2(int n) { + int arr[4] = {0}; + + while (--n) { + pointerParameter(arr); + arrayParameter(arr); + } +} + +int afterIf(int n) { + int i = n; + + if (n) { + i++; + } + // The following access to `i` should be the first control-flow node in its + // basic block. + referenceParameter(i); + return i; +} diff --git a/cpp/ql/test/library-tests/controlflow/controlflow/test.c b/cpp/ql/test/library-tests/controlflow/controlflow/test.c new file mode 100644 index 000000000000..2205a8f5b3b5 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/controlflow/test.c @@ -0,0 +1,98 @@ + +int test(int x, int w, int z) { + int j; + long y = 50; + + // if-else, multiple statements in block + if (x > 0) { + y = 20; + z = 10; + } else { + y = 30; + } + + z = x + y; + + // if-else with return in one branch + if(x < 0) + y = 40; + else + return z; + + // this is not the start of a BB due to the return + z = 10; + + // single-branch if-else + if (x == 0) { + y = 60; + z = 10; + } + + z += x; + + // while loop + while(x > 0) { + y = 10; + x--; + } + + z += y; + + // for loop + for(j = 0; j < 10; j++) { + y = 0; + w = 10; + } + + z += w; + + // nested control flow + for(j = 0; j < 10; j++) { + y = 30; + if(z > 0) + if(y > 0) { + w = 0; + break; + } else { + w = 20; + } + else { + w = 10; + continue; + } + x = 0; + } + + z += x + y + w; + + // nested control-flow + + w = 40; + return w; +} + +void test2(int a) { + /* Some more complex flow control */ + int b, c; + for (;;) { + b = 10; + if (a > 100) { + c = 10; + b = c; + } + if (a == 10) + break; + if (a == 20) + return c; + } + return b; +} + +void partly_undefined(int cond) { + int x; + if (cond) { + x = 1; + } + use(x); +} + diff --git a/cpp/ql/test/library-tests/controlflow/controlflow/test.cpp b/cpp/ql/test/library-tests/controlflow/controlflow/test.cpp new file mode 100644 index 000000000000..c70da661f651 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/controlflow/test.cpp @@ -0,0 +1,94 @@ +// Test for reference types. +int test0(int &p) { + int x = p; + int y = 0; + int& r0 = p; + int& r1 = x; + int& r2 = r1; + int* q = &r2; + for (int i = 0; i < 10; i++) { + r2++; + *q = *q + 1; + if (i > 5) { + r1--; + } + } + return r0 + r1 + r2 + *q; +} + +void test_helper1(int& x) { + x++; +} + +void test_helper2(int* x) { + (*x)++; +} + +typedef const int const_int; +typedef const int& const_int_ref; +typedef const int* const_int_ptr; + +// Test for safe forms of address taking. +int test1() { + int x = 1; + int y = 2; + const int& r1 = x; + const_int& r2 = x; + const_int_ref r3 = x; + const int* p1 = &y; + const_int* p2 = &y; + const_int_ptr p3 = &y; + test_helper1(x); + test_helper2(&x); + return x + r1 + r2 + r3 + *p1 + *p2 + *p3; +} + +int test2() { + int x = 1; + volatile const_int& r = x; + return x; +} + +// Test for constructor field initializers (ODASA-5773). We do not +// currently get SSA edges from the parameters of the constructor to their +// uses in the constructor field initializers. This is because the +// constructor field initializers are not currently connected to the +// control flow graph, which is a bug because initializers can have +// side-effects. +class Test2 { + Test2 *nextp_; + Test2 &nextr_; + const Test2 *nextcp_; + const Test2 &nextcr_; + int i_; + int *ip_; + int &ir_; + const int ci_; + const int *cip_; + const int &cir_; + +public: + Test2( + Test2 *nextp, + Test2 &nextr, + const Test2 *nextcp, + const Test2 &nextcr, + int i, + int *ip, + int &ir, + const int ci, + const int *cip, + const int &cir + ) : + nextp_(nextp), + nextr_(nextr), + nextcp_(nextcp), + nextcr_(nextcr), + i_(i), + ip_(ip), + ir_(ir), + ci_(ci), + cip_(cip), + cir_(cir) + {} +}; diff --git a/cpp/ql/test/library-tests/controlflow/dominance/bbIDominates.expected b/cpp/ql/test/library-tests/controlflow/dominance/bbIDominates.expected new file mode 100644 index 000000000000..c860887c1b80 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/dominance/bbIDominates.expected @@ -0,0 +1,34 @@ +| staticFalse | 4 | { ... } | 8 | { ... } | +| staticFalse | 8 | { ... } | 11 | if (...) ... | +| staticFalse | 11 | if (...) ... | 11 | { ... } | +| staticFalse | 11 | if (...) ... | 13 | { ... } | +| staticFalse | 11 | if (...) ... | 16 | ExprStmt | +| test | 2 | { ... } | 7 | { ... } | +| test | 2 | { ... } | 10 | { ... } | +| test | 2 | { ... } | 14 | ExprStmt | +| test | 14 | ExprStmt | 2 | test | +| test | 14 | ExprStmt | 18 | ExprStmt | +| test | 14 | ExprStmt | 20 | return ... | +| test | 18 | ExprStmt | 26 | { ... } | +| test | 18 | ExprStmt | 31 | ExprStmt | +| test | 31 | ExprStmt | 34 | x | +| test | 34 | x | 34 | { ... } | +| test | 34 | x | 39 | ExprStmt | +| test | 39 | ExprStmt | 42 | j | +| test | 42 | j | 42 | { ... } | +| test | 42 | j | 47 | ExprStmt | +| test | 47 | ExprStmt | 50 | j | +| test | 50 | j | 50 | { ... } | +| test | 50 | j | 64 | label ...: | +| test | 50 | { ... } | 50 | label ...: | +| test | 50 | { ... } | 53 | if (...) ... | +| test | 50 | { ... } | 59 | { ... } | +| test | 53 | if (...) ... | 53 | { ... } | +| test | 53 | if (...) ... | 56 | { ... } | +| test2 | 74 | { ... } | 77 | { ... } | +| test2 | 77 | { ... } | 79 | { ... } | +| test2 | 77 | { ... } | 83 | if (...) ... | +| test2 | 83 | if (...) ... | 74 | test2 | +| test2 | 83 | if (...) ... | 84 | break; | +| test2 | 83 | if (...) ... | 85 | if (...) ... | +| test2 | 85 | if (...) ... | 0 | { ... } | diff --git a/cpp/ql/test/library-tests/controlflow/dominance/bbIDominates.ql b/cpp/ql/test/library-tests/controlflow/dominance/bbIDominates.ql new file mode 100644 index 000000000000..e4428117b6e3 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/dominance/bbIDominates.ql @@ -0,0 +1,13 @@ +import cpp +import semmle.code.cpp.controlflow.Dominance + +from Function func, BasicBlock dominator, BasicBlock node +where + bbIDominates(dominator, node) + and dominator.getNode(0).getControlFlowScope() = func +select + func.getName(), + dominator.getNode(0).getLocation().getStartLine(), dominator.getNode(0).toString(), + node.getNode(0).getLocation().getStartLine(), node.getNode(0).toString() + + \ No newline at end of file diff --git a/cpp/ql/test/library-tests/controlflow/dominance/dominatedByStart.expected b/cpp/ql/test/library-tests/controlflow/dominance/dominatedByStart.expected new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/cpp/ql/test/library-tests/controlflow/dominance/dominatedByStart.ql b/cpp/ql/test/library-tests/controlflow/dominance/dominatedByStart.ql new file mode 100644 index 000000000000..b6a4ef8d1597 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/dominance/dominatedByStart.ql @@ -0,0 +1,25 @@ +// All nodes should be dominated by their associated start node + +import cpp +import semmle.code.cpp.controlflow.Dominance + +ControlFlowNode reachableIn(Function func) { + result = func.getEntryPoint() + or result = reachableIn(func).getASuccessor() +} + +predicate dominatedByStart(Function func, ControlFlowNode node) { + iDominates(func.getEntryPoint(), node) + or exists (ControlFlowNode dom | + dominatedByStart(func, dom) + and iDominates(dom, node)) +} + + +from Function func, ControlFlowNode entry, ControlFlowNode node +where + func.getEntryPoint() = entry + and reachableIn(func) = node + and entry != node + and not dominatedByStart(func, node) +select func, node diff --git a/cpp/ql/test/library-tests/controlflow/dominance/dominator.expected b/cpp/ql/test/library-tests/controlflow/dominance/dominator.expected new file mode 100644 index 000000000000..ca0b894b1bd3 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/dominance/dominator.expected @@ -0,0 +1,221 @@ +| staticFalse | 4 | { ... } | 5 | ExprStmt | +| staticFalse | 5 | ExprStmt | 5 | Hello | +| staticFalse | 5 | Hello | 5 | call to puts | +| staticFalse | 5 | call to puts | 6 | if (...) ... | +| staticFalse | 6 | 0 | 8 | { ... } | +| staticFalse | 6 | if (...) ... | 6 | 0 | +| staticFalse | 8 | { ... } | 9 | ExprStmt | +| staticFalse | 9 | ExprStmt | 9 | Reachable code | +| staticFalse | 9 | Reachable code | 9 | call to puts | +| staticFalse | 9 | call to puts | 11 | if (...) ... | +| staticFalse | 11 | call to runTimeDecision | 11 | { ... } | +| staticFalse | 11 | call to runTimeDecision | 13 | { ... } | +| staticFalse | 11 | call to runTimeDecision | 16 | ExprStmt | +| staticFalse | 11 | if (...) ... | 11 | call to runTimeDecision | +| staticFalse | 11 | { ... } | 12 | ExprStmt | +| staticFalse | 12 | Branch A | 12 | call to puts | +| staticFalse | 12 | ExprStmt | 12 | Branch A | +| staticFalse | 13 | { ... } | 14 | ExprStmt | +| staticFalse | 14 | Branch B | 14 | call to puts | +| staticFalse | 14 | ExprStmt | 14 | Branch B | +| staticFalse | 16 | All done | 16 | call to puts | +| staticFalse | 16 | ExprStmt | 16 | All done | +| staticFalse | 16 | call to puts | 17 | return ... | +| staticFalse | 17 | return ... | 4 | staticFalse | +| test | 2 | { ... } | 3 | declaration | +| test | 3 | declaration | 4 | declaration | +| test | 4 | 50 | 7 | if (...) ... | +| test | 4 | declaration | 4 | initializer for y | +| test | 4 | initializer for y | 4 | 50 | +| test | 7 | 0 | 7 | ... > ... | +| test | 7 | ... > ... | 7 | { ... } | +| test | 7 | ... > ... | 10 | { ... } | +| test | 7 | ... > ... | 14 | ExprStmt | +| test | 7 | if (...) ... | 7 | x | +| test | 7 | x | 7 | 0 | +| test | 7 | { ... } | 8 | ExprStmt | +| test | 8 | 20 | 8 | y | +| test | 8 | ... = ... | 9 | ExprStmt | +| test | 8 | ExprStmt | 8 | 20 | +| test | 8 | y | 8 | ... = ... | +| test | 9 | 10 | 9 | z | +| test | 9 | ExprStmt | 9 | 10 | +| test | 9 | z | 9 | ... = ... | +| test | 10 | { ... } | 11 | ExprStmt | +| test | 11 | 30 | 11 | y | +| test | 11 | ExprStmt | 11 | 30 | +| test | 11 | y | 11 | ... = ... | +| test | 14 | ... + ... | 14 | z | +| test | 14 | ... = ... | 17 | if (...) ... | +| test | 14 | ExprStmt | 14 | x | +| test | 14 | x | 14 | y | +| test | 14 | y | 14 | ... + ... | +| test | 14 | z | 14 | ... = ... | +| test | 17 | 0 | 17 | ... < ... | +| test | 17 | ... < ... | 2 | test | +| test | 17 | ... < ... | 18 | ExprStmt | +| test | 17 | ... < ... | 20 | return ... | +| test | 17 | if (...) ... | 17 | x | +| test | 17 | x | 17 | 0 | +| test | 18 | 40 | 18 | y | +| test | 18 | ... = ... | 23 | ExprStmt | +| test | 18 | ExprStmt | 18 | 40 | +| test | 18 | y | 18 | ... = ... | +| test | 20 | return ... | 20 | z | +| test | 23 | 10 | 23 | z | +| test | 23 | ... = ... | 26 | if (...) ... | +| test | 23 | ExprStmt | 23 | 10 | +| test | 23 | z | 23 | ... = ... | +| test | 26 | 0 | 26 | ... == ... | +| test | 26 | ... == ... | 26 | { ... } | +| test | 26 | ... == ... | 31 | ExprStmt | +| test | 26 | if (...) ... | 26 | x | +| test | 26 | x | 26 | 0 | +| test | 26 | { ... } | 27 | ExprStmt | +| test | 27 | 60 | 27 | y | +| test | 27 | ... = ... | 28 | ExprStmt | +| test | 27 | ExprStmt | 27 | 60 | +| test | 27 | y | 27 | ... = ... | +| test | 28 | 10 | 28 | z | +| test | 28 | ExprStmt | 28 | 10 | +| test | 28 | z | 28 | ... = ... | +| test | 31 | ... += ... | 34 | while (...) ... | +| test | 31 | ExprStmt | 31 | z | +| test | 31 | x | 31 | ... += ... | +| test | 31 | z | 31 | x | +| test | 34 | 0 | 34 | ... > ... | +| test | 34 | ... > ... | 34 | { ... } | +| test | 34 | ... > ... | 39 | ExprStmt | +| test | 34 | while (...) ... | 34 | x | +| test | 34 | x | 34 | 0 | +| test | 34 | { ... } | 35 | ExprStmt | +| test | 35 | 10 | 35 | y | +| test | 35 | ... = ... | 36 | ExprStmt | +| test | 35 | ExprStmt | 35 | 10 | +| test | 35 | y | 35 | ... = ... | +| test | 36 | ExprStmt | 36 | x | +| test | 36 | x | 36 | ... -- | +| test | 39 | ... += ... | 42 | for(...;...;...) ... | +| test | 39 | ExprStmt | 39 | z | +| test | 39 | y | 39 | ... += ... | +| test | 39 | z | 39 | y | +| test | 42 | 0 | 42 | j | +| test | 42 | 10 | 42 | ... < ... | +| test | 42 | ... < ... | 42 | { ... } | +| test | 42 | ... < ... | 47 | ExprStmt | +| test | 42 | ... = ... | 42 | j | +| test | 42 | ExprStmt | 42 | 0 | +| test | 42 | for(...;...;...) ... | 42 | ExprStmt | +| test | 42 | j | 42 | 10 | +| test | 42 | j | 42 | ... ++ | +| test | 42 | j | 42 | ... = ... | +| test | 42 | { ... } | 43 | ExprStmt | +| test | 43 | 0 | 43 | y | +| test | 43 | ... = ... | 44 | ExprStmt | +| test | 43 | ExprStmt | 43 | 0 | +| test | 43 | y | 43 | ... = ... | +| test | 44 | 10 | 44 | w | +| test | 44 | ... = ... | 42 | j | +| test | 44 | ExprStmt | 44 | 10 | +| test | 44 | w | 44 | ... = ... | +| test | 47 | ... += ... | 50 | for(...;...;...) ... | +| test | 47 | ExprStmt | 47 | z | +| test | 47 | w | 47 | ... += ... | +| test | 47 | z | 47 | w | +| test | 50 | 0 | 50 | j | +| test | 50 | 10 | 50 | ... < ... | +| test | 50 | ... < ... | 50 | { ... } | +| test | 50 | ... < ... | 64 | label ...: | +| test | 50 | ... = ... | 50 | j | +| test | 50 | ExprStmt | 50 | 0 | +| test | 50 | for(...;...;...) ... | 50 | ExprStmt | +| test | 50 | j | 50 | 10 | +| test | 50 | j | 50 | ... ++ | +| test | 50 | j | 50 | ... = ... | +| test | 50 | label ...: | 50 | j | +| test | 50 | { ... } | 51 | ExprStmt | +| test | 51 | 30 | 51 | y | +| test | 51 | ... = ... | 52 | if (...) ... | +| test | 51 | ExprStmt | 51 | 30 | +| test | 51 | y | 51 | ... = ... | +| test | 52 | 0 | 52 | ... > ... | +| test | 52 | ... > ... | 50 | label ...: | +| test | 52 | ... > ... | 53 | if (...) ... | +| test | 52 | ... > ... | 59 | { ... } | +| test | 52 | if (...) ... | 52 | z | +| test | 52 | z | 52 | 0 | +| test | 53 | 0 | 53 | ... > ... | +| test | 53 | ... > ... | 53 | { ... } | +| test | 53 | ... > ... | 56 | { ... } | +| test | 53 | if (...) ... | 53 | y | +| test | 53 | y | 53 | 0 | +| test | 53 | { ... } | 54 | ExprStmt | +| test | 54 | 0 | 54 | w | +| test | 54 | ... = ... | 55 | break; | +| test | 54 | ExprStmt | 54 | 0 | +| test | 54 | w | 54 | ... = ... | +| test | 56 | { ... } | 57 | ExprStmt | +| test | 57 | 20 | 57 | w | +| test | 57 | ... = ... | 63 | ExprStmt | +| test | 57 | ExprStmt | 57 | 20 | +| test | 57 | w | 57 | ... = ... | +| test | 59 | { ... } | 60 | ExprStmt | +| test | 60 | 10 | 60 | w | +| test | 60 | ... = ... | 61 | continue; | +| test | 60 | ExprStmt | 60 | 10 | +| test | 60 | w | 60 | ... = ... | +| test | 63 | 0 | 63 | x | +| test | 63 | ExprStmt | 63 | 0 | +| test | 63 | x | 63 | ... = ... | +| test | 64 | label ...: | 66 | ExprStmt | +| test | 66 | ... + ... | 66 | ... += ... | +| test | 66 | ... + ... | 66 | w | +| test | 66 | ... += ... | 70 | ExprStmt | +| test | 66 | ExprStmt | 66 | z | +| test | 66 | w | 66 | ... + ... | +| test | 66 | x | 66 | y | +| test | 66 | y | 66 | ... + ... | +| test | 66 | z | 66 | x | +| test | 70 | 40 | 70 | w | +| test | 70 | ... = ... | 71 | return ... | +| test | 70 | ExprStmt | 70 | 40 | +| test | 70 | w | 70 | ... = ... | +| test | 71 | return ... | 71 | w | +| test2 | 0 | { ... } | 86 | ExprStmt | +| test2 | 74 | { ... } | 76 | declaration | +| test2 | 76 | declaration | 77 | for(...;...;...) ... | +| test2 | 77 | for(...;...;...) ... | 77 | { ... } | +| test2 | 77 | { ... } | 78 | ExprStmt | +| test2 | 78 | 10 | 78 | b | +| test2 | 78 | ... = ... | 79 | if (...) ... | +| test2 | 78 | ExprStmt | 78 | 10 | +| test2 | 78 | b | 78 | ... = ... | +| test2 | 79 | 100 | 79 | ... > ... | +| test2 | 79 | ... > ... | 79 | { ... } | +| test2 | 79 | ... > ... | 83 | if (...) ... | +| test2 | 79 | a | 79 | 100 | +| test2 | 79 | if (...) ... | 79 | a | +| test2 | 79 | { ... } | 80 | ExprStmt | +| test2 | 80 | 10 | 80 | c | +| test2 | 80 | ... = ... | 81 | ExprStmt | +| test2 | 80 | ExprStmt | 80 | 10 | +| test2 | 80 | c | 80 | ... = ... | +| test2 | 81 | ExprStmt | 81 | c | +| test2 | 81 | b | 81 | ... = ... | +| test2 | 81 | c | 81 | b | +| test2 | 83 | 10 | 83 | ... == ... | +| test2 | 83 | ... == ... | 74 | test2 | +| test2 | 83 | ... == ... | 84 | break; | +| test2 | 83 | ... == ... | 85 | if (...) ... | +| test2 | 83 | a | 83 | 10 | +| test2 | 83 | if (...) ... | 83 | a | +| test2 | 84 | break; | 87 | label ...: | +| test2 | 85 | 20 | 85 | ... == ... | +| test2 | 85 | ... == ... | 0 | { ... } | +| test2 | 85 | a | 85 | 20 | +| test2 | 85 | if (...) ... | 85 | a | +| test2 | 86 | ExprStmt | 86 | c | +| test2 | 86 | c | 86 | return ... | +| test2 | 87 | label ...: | 88 | ExprStmt | +| test2 | 88 | ExprStmt | 88 | b | +| test2 | 88 | b | 88 | return ... | diff --git a/cpp/ql/test/library-tests/controlflow/dominance/dominator.ql b/cpp/ql/test/library-tests/controlflow/dominance/dominator.ql new file mode 100644 index 000000000000..10f185da4773 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/dominance/dominator.ql @@ -0,0 +1,11 @@ +import cpp +import semmle.code.cpp.controlflow.Dominance + +from Function func, ControlFlowNode dominator, ControlFlowNode node +where + iDominates(dominator, node) + and dominator.getControlFlowScope() = func +select + func.getName(), + dominator.getLocation().getStartLine(), dominator.toString(), + node.getLocation().getStartLine(), node.toString() diff --git a/cpp/ql/test/library-tests/controlflow/dominance/dominatorExists.expected b/cpp/ql/test/library-tests/controlflow/dominance/dominatorExists.expected new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/cpp/ql/test/library-tests/controlflow/dominance/dominatorExists.ql b/cpp/ql/test/library-tests/controlflow/dominance/dominatorExists.ql new file mode 100644 index 000000000000..12a58c044d52 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/dominance/dominatorExists.ql @@ -0,0 +1,17 @@ +// Every reachable node has a dominator + +import cpp +import semmle.code.cpp.controlflow.Dominance + +ControlFlowNode reachableIn(Function func) { + result = func.getEntryPoint() + or result = reachableIn(func).getASuccessor() +} + + +from Function func, ControlFlowNode node +where + node = reachableIn(func) + and node != func.getEntryPoint() + and not iDominates(_, node) +select func, node diff --git a/cpp/ql/test/library-tests/controlflow/dominance/dominatorUnique.expected b/cpp/ql/test/library-tests/controlflow/dominance/dominatorUnique.expected new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/cpp/ql/test/library-tests/controlflow/dominance/dominatorUnique.ql b/cpp/ql/test/library-tests/controlflow/dominance/dominatorUnique.ql new file mode 100644 index 000000000000..5911d2d0cb45 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/dominance/dominatorUnique.ql @@ -0,0 +1,12 @@ +// Every reachable node has a dominator + +import cpp +import semmle.code.cpp.controlflow.Dominance + +from Function func, ControlFlowNode dom1, ControlFlowNode dom2, ControlFlowNode node +where + iDominates(dom1, node) + and iDominates(dom2, node) + and dom1 != dom2 + and node.getControlFlowScope() = func +select func, node, dom1, dom2 diff --git a/cpp/ql/test/library-tests/controlflow/dominance/staticFalse.c b/cpp/ql/test/library-tests/controlflow/dominance/staticFalse.c new file mode 100644 index 000000000000..d0a8c058815d --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/dominance/staticFalse.c @@ -0,0 +1,17 @@ +extern int puts(const char *message); +extern int runTimeDecision(); + +void staticFalse() { + puts("Hello"); + if (0) { + puts("Unreachable code"); + } else { + puts("Reachable code"); + } + if (runTimeDecision()) { + puts("Branch A"); + } else { + puts("Branch B"); + } + puts("All done"); +} diff --git a/cpp/ql/test/library-tests/controlflow/dominance/test.c b/cpp/ql/test/library-tests/controlflow/dominance/test.c new file mode 100644 index 000000000000..9f9781278e84 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/dominance/test.c @@ -0,0 +1,89 @@ + +int test(int x, int w, int z) { + int j; + long y = 50; + + // if-else, multiple statements in block + if (x > 0) { + y = 20; + z = 10; + } else { + y = 30; + } + + z = x + y; + + // if-else with return in one branch + if(x < 0) + y = 40; + else + return z; + + // this is not the start of a BB due to the return + z = 10; + + // single-branch if-else + if (x == 0) { + y = 60; + z = 10; + } + + z += x; + + // while loop + while(x > 0) { + y = 10; + x--; + } + + z += y; + + // for loop + for(j = 0; j < 10; j++) { + y = 0; + w = 10; + } + + z += w; + + // nested control flow + for(j = 0; j < 10; j++) { + y = 30; + if(z > 0) + if(y > 0) { + w = 0; + break; + } else { + w = 20; + } + else { + w = 10; + continue; + } + x = 0; + } + + z += x + y + w; + + // nested control-flow + + w = 40; + return w; +} + +void test2(int a) { + /* Some more complex flow control */ + int b, c; + for (;;) { + b = 10; + if (a > 100) { + c = 10; + b = c; + } + if (a == 10) + break; + if (a == 20) + return c; + } + return b; +} diff --git a/cpp/ql/test/library-tests/controlflow/exits/exits.expected b/cpp/ql/test/library-tests/controlflow/exits/exits.expected new file mode 100644 index 000000000000..e50f4571d271 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/exits/exits.expected @@ -0,0 +1,30 @@ +| file://:0:0:0:0 | operator= | __va_list_tag::operator= | returns | +| file://:0:0:0:0 | operator= | __va_list_tag::operator= | returns | +| test.cpp:1:6:1:9 | exit | exit | exits | +| test.cpp:2:6:2:10 | _exit | _exit | exits | +| test.cpp:3:6:3:10 | abort | abort | exits | +| test.cpp:4:6:4:10 | error | error | exits | +| test.cpp:5:6:5:18 | __assert_fail | __assert_fail | exits | +| test.cpp:7:6:7:12 | longjmp | longjmp | exits | +| test.cpp:8:6:8:15 | DoesReturn | DoesReturn | returns | +| test.cpp:9:6:9:18 | DoesNotReturn | DoesNotReturn | exits | +| test.cpp:13:8:13:11 | exit | MyStuff::exit | returns | +| test.cpp:14:8:14:12 | _exit | MyStuff::_exit | returns | +| test.cpp:15:8:15:12 | abort | MyStuff::abort | returns | +| test.cpp:16:8:16:12 | error | MyStuff::error | returns | +| test.cpp:17:8:17:20 | __assert_fail | MyStuff::__assert_fail | returns | +| test.cpp:18:8:18:14 | longjmp | MyStuff::longjmp | returns | +| test.cpp:19:8:19:17 | DoesReturn | MyStuff::DoesReturn | returns | +| test.cpp:20:8:20:20 | DoesNotReturn | MyStuff::DoesNotReturn | exits | +| test.cpp:23:7:23:7 | operator= | MyClass::operator= | returns | +| test.cpp:23:7:23:7 | operator= | MyClass::operator= | returns | +| test.cpp:25:8:25:11 | exit | MyClass::exit | returns | +| test.cpp:26:8:26:12 | _exit | MyClass::_exit | returns | +| test.cpp:27:8:27:12 | abort | MyClass::abort | returns | +| test.cpp:28:8:28:12 | error | MyClass::error | returns | +| test.cpp:29:8:29:20 | __assert_fail | MyClass::__assert_fail | returns | +| test.cpp:30:8:30:14 | longjmp | MyClass::longjmp | returns | +| test.cpp:31:8:31:17 | DoesReturn | MyClass::DoesReturn | returns | +| test.cpp:32:8:32:20 | DoesNotReturn | MyClass::DoesNotReturn | exits | +| test.ms.cpp:1:6:1:17 | MsDoesReturn | MsDoesReturn | returns | +| test.ms.cpp:2:27:2:41 | MsDoesNotReturn | MsDoesNotReturn | exits | diff --git a/cpp/ql/test/library-tests/controlflow/exits/exits.ql b/cpp/ql/test/library-tests/controlflow/exits/exits.ql new file mode 100644 index 000000000000..b51c22790a00 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/exits/exits.ql @@ -0,0 +1,5 @@ +import cpp + +from Options opts, Function f, string status +where if opts.exits(f) then status = "exits" else status = "returns" +select f, f.getQualifiedName(), status diff --git a/cpp/ql/test/library-tests/controlflow/exits/test.cpp b/cpp/ql/test/library-tests/controlflow/exits/test.cpp new file mode 100644 index 000000000000..38f14d938954 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/exits/test.cpp @@ -0,0 +1,33 @@ +void exit(int status); +void _exit(int status); +void abort(void); +void error(int status, int errnum, const char *format, ...); +void __assert_fail(const char * assertion, const char * file, unsigned int line, const char * function); +typedef int jmp_buf[4]; +void longjmp(jmp_buf env, int value); +void DoesReturn(); +void DoesNotReturn() __attribute__((noreturn)); + +namespace MyStuff +{ + void exit(int status); + void _exit(int status); + void abort(void); + void error(int status, int errnum, const char *format, ...); + void __assert_fail(const char * assertion, const char * file, unsigned int line, const char * function); + void longjmp(jmp_buf env, int value); + void DoesReturn(); + void DoesNotReturn() __attribute__((noreturn)); +} + +class MyClass +{ + void exit(int status); + void _exit(int status); + void abort(void); + void error(int status, int errnum, const char *format, ...); + void __assert_fail(const char * assertion, const char * file, unsigned int line, const char * function); + void longjmp(jmp_buf env, int value); + void DoesReturn(); + void DoesNotReturn() __attribute__((noreturn)); +}; diff --git a/cpp/ql/test/library-tests/controlflow/exits/test.ms.cpp b/cpp/ql/test/library-tests/controlflow/exits/test.ms.cpp new file mode 100644 index 000000000000..93c23ef1d37f --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/exits/test.ms.cpp @@ -0,0 +1,3 @@ +void MsDoesReturn(); +__declspec(noreturn) void MsDoesNotReturn(); +// semmle-extractor-options: --microsoft diff --git a/cpp/ql/test/library-tests/controlflow/guards/Guards.expected b/cpp/ql/test/library-tests/controlflow/guards/Guards.expected new file mode 100644 index 000000000000..b73e7064339e --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/guards/Guards.expected @@ -0,0 +1,32 @@ +| test.c:7:9:7:13 | ... > ... | +| test.c:17:8:17:12 | ... < ... | +| test.c:17:8:17:21 | ... && ... | +| test.c:17:17:17:21 | ... > ... | +| test.c:26:11:26:15 | ... > ... | +| test.c:34:16:34:21 | ... < ... | +| test.c:42:16:42:21 | ... < ... | +| test.c:44:12:44:16 | ... > ... | +| test.c:45:16:45:20 | ... > ... | +| test.c:58:9:58:14 | ... == ... | +| test.c:58:9:58:23 | ... \|\| ... | +| test.c:58:19:58:23 | ... < ... | +| test.c:75:9:75:14 | ... == ... | +| test.c:85:8:85:13 | ... == ... | +| test.c:85:8:85:23 | ... && ... | +| test.c:85:18:85:23 | ... != ... | +| test.c:94:11:94:16 | ... != ... | +| test.c:102:16:102:21 | ... < ... | +| test.c:109:9:109:14 | ... == ... | +| test.c:109:9:109:23 | ... \|\| ... | +| test.c:109:19:109:23 | ... < ... | +| test.c:126:7:126:7 | 1 | +| test.c:126:7:126:28 | ... && ... | +| test.c:126:12:126:26 | call to test3_condition | +| test.c:131:7:131:7 | b | +| test.c:137:7:137:7 | 0 | +| test.c:138:9:138:9 | i | +| test.c:146:7:146:8 | ! ... | +| test.c:146:8:146:8 | x | +| test.cpp:18:8:18:10 | call to get | +| test.cpp:31:7:31:13 | ... == ... | +| test.cpp:42:13:42:20 | call to getABool | diff --git a/cpp/ql/test/library-tests/controlflow/guards/Guards.ql b/cpp/ql/test/library-tests/controlflow/guards/Guards.ql new file mode 100644 index 000000000000..822bf0d17700 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/guards/Guards.ql @@ -0,0 +1,5 @@ +import cpp +import semmle.code.cpp.controlflow.Guards + +from GuardCondition guard +select guard \ No newline at end of file diff --git a/cpp/ql/test/library-tests/controlflow/guards/GuardsCompare.expected b/cpp/ql/test/library-tests/controlflow/guards/GuardsCompare.expected new file mode 100644 index 000000000000..58068f3991df --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/guards/GuardsCompare.expected @@ -0,0 +1,88 @@ +| 7 | 0 < x+0 when ... > ... is true | +| 7 | 0 >= x+0 when ... > ... is false | +| 7 | x < 0+1 when ... > ... is false | +| 7 | x >= 0+1 when ... > ... is true | +| 17 | 0 < x+1 when ... < ... is false | +| 17 | 0 >= x+1 when ... && ... is true | +| 17 | 0 >= x+1 when ... < ... is true | +| 17 | 1 < y+0 when ... && ... is true | +| 17 | 1 < y+0 when ... > ... is true | +| 17 | 1 >= y+0 when ... > ... is false | +| 17 | x < 0+0 when ... && ... is true | +| 17 | x < 0+0 when ... < ... is true | +| 17 | x >= 0+0 when ... < ... is false | +| 17 | y < 1+1 when ... > ... is false | +| 17 | y >= 1+1 when ... && ... is true | +| 17 | y >= 1+1 when ... > ... is true | +| 26 | 0 < x+0 when ... > ... is true | +| 26 | 0 >= x+0 when ... > ... is false | +| 26 | x < 0+1 when ... > ... is false | +| 26 | x >= 0+1 when ... > ... is true | +| 31 | - ... != x+0 when ... == ... is false | +| 31 | - ... == x+0 when ... == ... is true | +| 31 | x != - ...+0 when ... == ... is false | +| 31 | x == - ...+0 when ... == ... is true | +| 34 | 10 < j+1 when ... < ... is false | +| 34 | 10 >= j+1 when ... < ... is true | +| 34 | j < 10+0 when ... < ... is true | +| 34 | j >= 10+0 when ... < ... is false | +| 42 | 10 < j+1 when ... < ... is false | +| 42 | 10 >= j+1 when ... < ... is true | +| 42 | j < 10+0 when ... < ... is true | +| 42 | j >= 10+0 when ... < ... is false | +| 44 | 0 < z+0 when ... > ... is true | +| 44 | 0 >= z+0 when ... > ... is false | +| 44 | z < 0+1 when ... > ... is false | +| 44 | z >= 0+1 when ... > ... is true | +| 45 | 0 < y+0 when ... > ... is true | +| 45 | 0 >= y+0 when ... > ... is false | +| 45 | y < 0+1 when ... > ... is false | +| 45 | y >= 0+1 when ... > ... is true | +| 58 | 0 != x+0 when ... == ... is false | +| 58 | 0 != x+0 when ... \|\| ... is false | +| 58 | 0 < y+1 when ... < ... is false | +| 58 | 0 < y+1 when ... \|\| ... is false | +| 58 | 0 == x+0 when ... == ... is true | +| 58 | 0 >= y+1 when ... < ... is true | +| 58 | x != 0+0 when ... == ... is false | +| 58 | x != 0+0 when ... \|\| ... is false | +| 58 | x == 0+0 when ... == ... is true | +| 58 | y < 0+0 when ... < ... is true | +| 58 | y >= 0+0 when ... < ... is false | +| 58 | y >= 0+0 when ... \|\| ... is false | +| 75 | 0 != x+0 when ... == ... is false | +| 75 | 0 == x+0 when ... == ... is true | +| 75 | x != 0+0 when ... == ... is false | +| 75 | x == 0+0 when ... == ... is true | +| 85 | 0 != x+0 when ... == ... is false | +| 85 | 0 != y+0 when ... != ... is true | +| 85 | 0 != y+0 when ... && ... is true | +| 85 | 0 == x+0 when ... && ... is true | +| 85 | 0 == x+0 when ... == ... is true | +| 85 | 0 == y+0 when ... != ... is false | +| 85 | x != 0+0 when ... == ... is false | +| 85 | x == 0+0 when ... && ... is true | +| 85 | x == 0+0 when ... == ... is true | +| 85 | y != 0+0 when ... != ... is true | +| 85 | y != 0+0 when ... && ... is true | +| 85 | y == 0+0 when ... != ... is false | +| 94 | 0 != x+0 when ... != ... is true | +| 94 | 0 == x+0 when ... != ... is false | +| 94 | x != 0+0 when ... != ... is true | +| 94 | x == 0+0 when ... != ... is false | +| 102 | 10 < j+1 when ... < ... is false | +| 102 | 10 >= j+1 when ... < ... is true | +| 102 | j < 10+0 when ... < ... is true | +| 102 | j >= 10+0 when ... < ... is false | +| 109 | 0 != x+0 when ... == ... is false | +| 109 | 0 != x+0 when ... \|\| ... is false | +| 109 | 0 < y+1 when ... < ... is false | +| 109 | 0 < y+1 when ... \|\| ... is false | +| 109 | 0 == x+0 when ... == ... is true | +| 109 | 0 >= y+1 when ... < ... is true | +| 109 | x != 0+0 when ... == ... is false | +| 109 | x != 0+0 when ... \|\| ... is false | +| 109 | x == 0+0 when ... == ... is true | +| 109 | y < 0+0 when ... < ... is true | +| 109 | y >= 0+0 when ... < ... is false | +| 109 | y >= 0+0 when ... \|\| ... is false | diff --git a/cpp/ql/test/library-tests/controlflow/guards/GuardsCompare.ql b/cpp/ql/test/library-tests/controlflow/guards/GuardsCompare.ql new file mode 100644 index 000000000000..6adc9db4610a --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/guards/GuardsCompare.ql @@ -0,0 +1,25 @@ +/** + * @name Guards comparison test + * @description List comparison parts. + * @kind test + */ + +import cpp +import semmle.code.cpp.controlflow.Guards + + +from GuardCondition guard, Expr left, Expr right, int k, string which, string op, string msg +where +(exists(boolean sense | sense = true and which = "true" or sense = false and which = "false" | + guard.comparesLt(left, right, k, true, sense) and op = " < " + or + guard.comparesLt(left, right, k, false, sense) and op = " >= " + or + guard.comparesEq(left, right, k, true, sense) and op = " == " + or + guard.comparesEq(left, right, k, false, sense) and op = " != " + ) +) +and msg = left + op + right + "+" + k + " when " + guard + " is " + which + +select guard.getLocation().getStartLine(), msg \ No newline at end of file diff --git a/cpp/ql/test/library-tests/controlflow/guards/GuardsControl.expected b/cpp/ql/test/library-tests/controlflow/guards/GuardsControl.expected new file mode 100644 index 000000000000..a9f1c7aa6c41 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/guards/GuardsControl.expected @@ -0,0 +1,90 @@ +| test.c:7:9:7:13 | ... > ... | false | 10 | 11 | +| test.c:7:9:7:13 | ... > ... | true | 7 | 9 | +| test.c:17:8:17:12 | ... < ... | true | 17 | 17 | +| test.c:17:8:17:12 | ... < ... | true | 18 | 18 | +| test.c:17:8:17:21 | ... && ... | true | 18 | 18 | +| test.c:17:17:17:21 | ... > ... | true | 18 | 18 | +| test.c:26:11:26:15 | ... > ... | false | 2 | 2 | +| test.c:26:11:26:15 | ... > ... | false | 31 | 34 | +| test.c:26:11:26:15 | ... > ... | false | 34 | 34 | +| test.c:26:11:26:15 | ... > ... | false | 39 | 42 | +| test.c:26:11:26:15 | ... > ... | false | 42 | 42 | +| test.c:26:11:26:15 | ... > ... | false | 42 | 44 | +| test.c:26:11:26:15 | ... > ... | false | 45 | 45 | +| test.c:26:11:26:15 | ... > ... | false | 45 | 47 | +| test.c:26:11:26:15 | ... > ... | false | 48 | 55 | +| test.c:26:11:26:15 | ... > ... | false | 51 | 53 | +| test.c:26:11:26:15 | ... > ... | false | 56 | 58 | +| test.c:26:11:26:15 | ... > ... | false | 58 | 58 | +| test.c:26:11:26:15 | ... > ... | false | 58 | 66 | +| test.c:26:11:26:15 | ... > ... | false | 62 | 62 | +| test.c:26:11:26:15 | ... > ... | true | 26 | 28 | +| test.c:34:16:34:21 | ... < ... | false | 2 | 2 | +| test.c:34:16:34:21 | ... < ... | false | 39 | 42 | +| test.c:34:16:34:21 | ... < ... | false | 42 | 42 | +| test.c:34:16:34:21 | ... < ... | false | 42 | 44 | +| test.c:34:16:34:21 | ... < ... | false | 45 | 45 | +| test.c:34:16:34:21 | ... < ... | false | 45 | 47 | +| test.c:34:16:34:21 | ... < ... | false | 48 | 55 | +| test.c:34:16:34:21 | ... < ... | false | 51 | 53 | +| test.c:34:16:34:21 | ... < ... | false | 56 | 58 | +| test.c:34:16:34:21 | ... < ... | false | 58 | 58 | +| test.c:34:16:34:21 | ... < ... | false | 58 | 66 | +| test.c:34:16:34:21 | ... < ... | false | 62 | 62 | +| test.c:34:16:34:21 | ... < ... | true | 34 | 34 | +| test.c:42:16:42:21 | ... < ... | true | 42 | 42 | +| test.c:42:16:42:21 | ... < ... | true | 42 | 44 | +| test.c:42:16:42:21 | ... < ... | true | 45 | 45 | +| test.c:42:16:42:21 | ... < ... | true | 45 | 47 | +| test.c:42:16:42:21 | ... < ... | true | 48 | 55 | +| test.c:42:16:42:21 | ... < ... | true | 51 | 53 | +| test.c:44:12:44:16 | ... > ... | false | 51 | 53 | +| test.c:44:12:44:16 | ... > ... | true | 45 | 45 | +| test.c:44:12:44:16 | ... > ... | true | 45 | 47 | +| test.c:44:12:44:16 | ... > ... | true | 48 | 55 | +| test.c:45:16:45:20 | ... > ... | false | 48 | 55 | +| test.c:45:16:45:20 | ... > ... | true | 45 | 47 | +| test.c:58:9:58:14 | ... == ... | false | 58 | 58 | +| test.c:58:9:58:14 | ... == ... | false | 62 | 62 | +| test.c:58:9:58:23 | ... \|\| ... | false | 62 | 62 | +| test.c:58:19:58:23 | ... < ... | false | 62 | 62 | +| test.c:75:9:75:14 | ... == ... | false | 78 | 79 | +| test.c:75:9:75:14 | ... == ... | true | 75 | 77 | +| test.c:85:8:85:13 | ... == ... | true | 85 | 85 | +| test.c:85:8:85:13 | ... == ... | true | 86 | 86 | +| test.c:85:8:85:23 | ... && ... | true | 86 | 86 | +| test.c:85:18:85:23 | ... != ... | true | 86 | 86 | +| test.c:94:11:94:16 | ... != ... | false | 70 | 70 | +| test.c:94:11:94:16 | ... != ... | false | 99 | 102 | +| test.c:94:11:94:16 | ... != ... | false | 102 | 102 | +| test.c:94:11:94:16 | ... != ... | false | 107 | 109 | +| test.c:94:11:94:16 | ... != ... | false | 109 | 109 | +| test.c:94:11:94:16 | ... != ... | false | 109 | 117 | +| test.c:94:11:94:16 | ... != ... | false | 113 | 113 | +| test.c:94:11:94:16 | ... != ... | true | 94 | 96 | +| test.c:102:16:102:21 | ... < ... | false | 70 | 70 | +| test.c:102:16:102:21 | ... < ... | false | 107 | 109 | +| test.c:102:16:102:21 | ... < ... | false | 109 | 109 | +| test.c:102:16:102:21 | ... < ... | false | 109 | 117 | +| test.c:102:16:102:21 | ... < ... | false | 113 | 113 | +| test.c:102:16:102:21 | ... < ... | true | 102 | 102 | +| test.c:109:9:109:14 | ... == ... | false | 109 | 109 | +| test.c:109:9:109:14 | ... == ... | false | 113 | 113 | +| test.c:109:9:109:23 | ... \|\| ... | false | 113 | 113 | +| test.c:109:19:109:23 | ... < ... | false | 113 | 113 | +| test.c:126:7:126:7 | 1 | true | 126 | 126 | +| test.c:126:7:126:7 | 1 | true | 126 | 128 | +| test.c:126:7:126:7 | 1 | true | 131 | 131 | +| test.c:126:7:126:7 | 1 | true | 131 | 132 | +| test.c:126:7:126:7 | 1 | true | 134 | 123 | +| test.c:126:7:126:28 | ... && ... | true | 126 | 128 | +| test.c:126:12:126:26 | call to test3_condition | true | 126 | 128 | +| test.c:131:7:131:7 | b | true | 131 | 132 | +| test.c:137:7:137:7 | 0 | false | 142 | 136 | +| test.c:138:9:138:9 | i | true | 138 | 139 | +| test.c:146:7:146:8 | ! ... | true | 146 | 147 | +| test.c:146:8:146:8 | x | false | 146 | 147 | +| test.cpp:18:8:18:10 | call to get | false | 20 | 16 | +| test.cpp:31:7:31:13 | ... == ... | false | 34 | 34 | +| test.cpp:31:7:31:13 | ... == ... | true | 31 | 32 | +| test.cpp:42:13:42:20 | call to getABool | true | 43 | 45 | diff --git a/cpp/ql/test/library-tests/controlflow/guards/GuardsControl.ql b/cpp/ql/test/library-tests/controlflow/guards/GuardsControl.ql new file mode 100644 index 000000000000..fecfe8f7d61f --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/guards/GuardsControl.ql @@ -0,0 +1,17 @@ +/** + * @name Guards control test + * @description List which guards control which blocks + * @kind test + */ + +import cpp +import semmle.code.cpp.controlflow.Guards + + +from GuardCondition guard, boolean sense, int start, int end +where +exists(BasicBlock block | + guard.controls(block, sense) and + block.hasLocationInfo(_, start, _, end, _) +) +select guard, sense, start, end diff --git a/cpp/ql/test/library-tests/controlflow/guards/GuardsEnsure.expected b/cpp/ql/test/library-tests/controlflow/guards/GuardsEnsure.expected new file mode 100644 index 000000000000..99a1097618d8 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/guards/GuardsEnsure.expected @@ -0,0 +1,160 @@ +| test.c:7:9:7:13 | ... > ... | test.c:7:9:7:9 | x | < | test.c:7:13:7:13 | 0 | 1 | 10 | 11 | +| test.c:7:9:7:13 | ... > ... | test.c:7:9:7:9 | x | >= | test.c:7:13:7:13 | 0 | 1 | 7 | 9 | +| test.c:7:9:7:13 | ... > ... | test.c:7:13:7:13 | 0 | < | test.c:7:9:7:9 | x | 0 | 7 | 9 | +| test.c:7:9:7:13 | ... > ... | test.c:7:13:7:13 | 0 | >= | test.c:7:9:7:9 | x | 0 | 10 | 11 | +| test.c:17:8:17:12 | ... < ... | test.c:17:8:17:8 | x | < | test.c:17:12:17:12 | 0 | 0 | 17 | 17 | +| test.c:17:8:17:12 | ... < ... | test.c:17:8:17:8 | x | < | test.c:17:12:17:12 | 0 | 0 | 18 | 18 | +| test.c:17:8:17:12 | ... < ... | test.c:17:12:17:12 | 0 | >= | test.c:17:8:17:8 | x | 1 | 17 | 17 | +| test.c:17:8:17:12 | ... < ... | test.c:17:12:17:12 | 0 | >= | test.c:17:8:17:8 | x | 1 | 18 | 18 | +| test.c:17:8:17:21 | ... && ... | test.c:17:8:17:8 | x | < | test.c:17:12:17:12 | 0 | 0 | 18 | 18 | +| test.c:17:8:17:21 | ... && ... | test.c:17:12:17:12 | 0 | >= | test.c:17:8:17:8 | x | 1 | 18 | 18 | +| test.c:17:8:17:21 | ... && ... | test.c:17:17:17:17 | y | >= | test.c:17:21:17:21 | 1 | 1 | 18 | 18 | +| test.c:17:8:17:21 | ... && ... | test.c:17:21:17:21 | 1 | < | test.c:17:17:17:17 | y | 0 | 18 | 18 | +| test.c:17:17:17:21 | ... > ... | test.c:17:17:17:17 | y | >= | test.c:17:21:17:21 | 1 | 1 | 18 | 18 | +| test.c:17:17:17:21 | ... > ... | test.c:17:21:17:21 | 1 | < | test.c:17:17:17:17 | y | 0 | 18 | 18 | +| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | < | test.c:26:15:26:15 | 0 | 1 | 2 | 2 | +| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | < | test.c:26:15:26:15 | 0 | 1 | 31 | 34 | +| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | < | test.c:26:15:26:15 | 0 | 1 | 34 | 34 | +| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | < | test.c:26:15:26:15 | 0 | 1 | 39 | 42 | +| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | < | test.c:26:15:26:15 | 0 | 1 | 42 | 42 | +| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | < | test.c:26:15:26:15 | 0 | 1 | 42 | 44 | +| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | < | test.c:26:15:26:15 | 0 | 1 | 45 | 45 | +| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | < | test.c:26:15:26:15 | 0 | 1 | 45 | 47 | +| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | < | test.c:26:15:26:15 | 0 | 1 | 48 | 55 | +| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | < | test.c:26:15:26:15 | 0 | 1 | 51 | 53 | +| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | < | test.c:26:15:26:15 | 0 | 1 | 56 | 58 | +| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | < | test.c:26:15:26:15 | 0 | 1 | 58 | 58 | +| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | < | test.c:26:15:26:15 | 0 | 1 | 58 | 66 | +| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | < | test.c:26:15:26:15 | 0 | 1 | 62 | 62 | +| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | >= | test.c:26:15:26:15 | 0 | 1 | 26 | 28 | +| test.c:26:11:26:15 | ... > ... | test.c:26:15:26:15 | 0 | < | test.c:26:11:26:11 | x | 0 | 26 | 28 | +| test.c:26:11:26:15 | ... > ... | test.c:26:15:26:15 | 0 | >= | test.c:26:11:26:11 | x | 0 | 2 | 2 | +| test.c:26:11:26:15 | ... > ... | test.c:26:15:26:15 | 0 | >= | test.c:26:11:26:11 | x | 0 | 31 | 34 | +| test.c:26:11:26:15 | ... > ... | test.c:26:15:26:15 | 0 | >= | test.c:26:11:26:11 | x | 0 | 34 | 34 | +| test.c:26:11:26:15 | ... > ... | test.c:26:15:26:15 | 0 | >= | test.c:26:11:26:11 | x | 0 | 39 | 42 | +| test.c:26:11:26:15 | ... > ... | test.c:26:15:26:15 | 0 | >= | test.c:26:11:26:11 | x | 0 | 42 | 42 | +| test.c:26:11:26:15 | ... > ... | test.c:26:15:26:15 | 0 | >= | test.c:26:11:26:11 | x | 0 | 42 | 44 | +| test.c:26:11:26:15 | ... > ... | test.c:26:15:26:15 | 0 | >= | test.c:26:11:26:11 | x | 0 | 45 | 45 | +| test.c:26:11:26:15 | ... > ... | test.c:26:15:26:15 | 0 | >= | test.c:26:11:26:11 | x | 0 | 45 | 47 | +| test.c:26:11:26:15 | ... > ... | test.c:26:15:26:15 | 0 | >= | test.c:26:11:26:11 | x | 0 | 48 | 55 | +| test.c:26:11:26:15 | ... > ... | test.c:26:15:26:15 | 0 | >= | test.c:26:11:26:11 | x | 0 | 51 | 53 | +| test.c:26:11:26:15 | ... > ... | test.c:26:15:26:15 | 0 | >= | test.c:26:11:26:11 | x | 0 | 56 | 58 | +| test.c:26:11:26:15 | ... > ... | test.c:26:15:26:15 | 0 | >= | test.c:26:11:26:11 | x | 0 | 58 | 58 | +| test.c:26:11:26:15 | ... > ... | test.c:26:15:26:15 | 0 | >= | test.c:26:11:26:11 | x | 0 | 58 | 66 | +| test.c:26:11:26:15 | ... > ... | test.c:26:15:26:15 | 0 | >= | test.c:26:11:26:11 | x | 0 | 62 | 62 | +| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:16 | j | < | test.c:34:20:34:21 | 10 | 0 | 34 | 34 | +| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:16 | j | >= | test.c:34:20:34:21 | 10 | 0 | 2 | 2 | +| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:16 | j | >= | test.c:34:20:34:21 | 10 | 0 | 39 | 42 | +| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:16 | j | >= | test.c:34:20:34:21 | 10 | 0 | 42 | 42 | +| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:16 | j | >= | test.c:34:20:34:21 | 10 | 0 | 42 | 44 | +| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:16 | j | >= | test.c:34:20:34:21 | 10 | 0 | 45 | 45 | +| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:16 | j | >= | test.c:34:20:34:21 | 10 | 0 | 45 | 47 | +| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:16 | j | >= | test.c:34:20:34:21 | 10 | 0 | 48 | 55 | +| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:16 | j | >= | test.c:34:20:34:21 | 10 | 0 | 51 | 53 | +| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:16 | j | >= | test.c:34:20:34:21 | 10 | 0 | 56 | 58 | +| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:16 | j | >= | test.c:34:20:34:21 | 10 | 0 | 58 | 58 | +| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:16 | j | >= | test.c:34:20:34:21 | 10 | 0 | 58 | 66 | +| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:16 | j | >= | test.c:34:20:34:21 | 10 | 0 | 62 | 62 | +| test.c:34:16:34:21 | ... < ... | test.c:34:20:34:21 | 10 | < | test.c:34:16:34:16 | j | 1 | 2 | 2 | +| test.c:34:16:34:21 | ... < ... | test.c:34:20:34:21 | 10 | < | test.c:34:16:34:16 | j | 1 | 39 | 42 | +| test.c:34:16:34:21 | ... < ... | test.c:34:20:34:21 | 10 | < | test.c:34:16:34:16 | j | 1 | 42 | 42 | +| test.c:34:16:34:21 | ... < ... | test.c:34:20:34:21 | 10 | < | test.c:34:16:34:16 | j | 1 | 42 | 44 | +| test.c:34:16:34:21 | ... < ... | test.c:34:20:34:21 | 10 | < | test.c:34:16:34:16 | j | 1 | 45 | 45 | +| test.c:34:16:34:21 | ... < ... | test.c:34:20:34:21 | 10 | < | test.c:34:16:34:16 | j | 1 | 45 | 47 | +| test.c:34:16:34:21 | ... < ... | test.c:34:20:34:21 | 10 | < | test.c:34:16:34:16 | j | 1 | 48 | 55 | +| test.c:34:16:34:21 | ... < ... | test.c:34:20:34:21 | 10 | < | test.c:34:16:34:16 | j | 1 | 51 | 53 | +| test.c:34:16:34:21 | ... < ... | test.c:34:20:34:21 | 10 | < | test.c:34:16:34:16 | j | 1 | 56 | 58 | +| test.c:34:16:34:21 | ... < ... | test.c:34:20:34:21 | 10 | < | test.c:34:16:34:16 | j | 1 | 58 | 58 | +| test.c:34:16:34:21 | ... < ... | test.c:34:20:34:21 | 10 | < | test.c:34:16:34:16 | j | 1 | 58 | 66 | +| test.c:34:16:34:21 | ... < ... | test.c:34:20:34:21 | 10 | < | test.c:34:16:34:16 | j | 1 | 62 | 62 | +| test.c:34:16:34:21 | ... < ... | test.c:34:20:34:21 | 10 | >= | test.c:34:16:34:16 | j | 1 | 34 | 34 | +| test.c:42:16:42:21 | ... < ... | test.c:42:16:42:16 | j | < | test.c:42:20:42:21 | 10 | 0 | 42 | 42 | +| test.c:42:16:42:21 | ... < ... | test.c:42:16:42:16 | j | < | test.c:42:20:42:21 | 10 | 0 | 42 | 44 | +| test.c:42:16:42:21 | ... < ... | test.c:42:16:42:16 | j | < | test.c:42:20:42:21 | 10 | 0 | 45 | 45 | +| test.c:42:16:42:21 | ... < ... | test.c:42:16:42:16 | j | < | test.c:42:20:42:21 | 10 | 0 | 45 | 47 | +| test.c:42:16:42:21 | ... < ... | test.c:42:16:42:16 | j | < | test.c:42:20:42:21 | 10 | 0 | 48 | 55 | +| test.c:42:16:42:21 | ... < ... | test.c:42:16:42:16 | j | < | test.c:42:20:42:21 | 10 | 0 | 51 | 53 | +| test.c:42:16:42:21 | ... < ... | test.c:42:20:42:21 | 10 | >= | test.c:42:16:42:16 | j | 1 | 42 | 42 | +| test.c:42:16:42:21 | ... < ... | test.c:42:20:42:21 | 10 | >= | test.c:42:16:42:16 | j | 1 | 42 | 44 | +| test.c:42:16:42:21 | ... < ... | test.c:42:20:42:21 | 10 | >= | test.c:42:16:42:16 | j | 1 | 45 | 45 | +| test.c:42:16:42:21 | ... < ... | test.c:42:20:42:21 | 10 | >= | test.c:42:16:42:16 | j | 1 | 45 | 47 | +| test.c:42:16:42:21 | ... < ... | test.c:42:20:42:21 | 10 | >= | test.c:42:16:42:16 | j | 1 | 48 | 55 | +| test.c:42:16:42:21 | ... < ... | test.c:42:20:42:21 | 10 | >= | test.c:42:16:42:16 | j | 1 | 51 | 53 | +| test.c:44:12:44:16 | ... > ... | test.c:44:12:44:12 | z | < | test.c:44:16:44:16 | 0 | 1 | 51 | 53 | +| test.c:44:12:44:16 | ... > ... | test.c:44:12:44:12 | z | >= | test.c:44:16:44:16 | 0 | 1 | 45 | 45 | +| test.c:44:12:44:16 | ... > ... | test.c:44:12:44:12 | z | >= | test.c:44:16:44:16 | 0 | 1 | 45 | 47 | +| test.c:44:12:44:16 | ... > ... | test.c:44:12:44:12 | z | >= | test.c:44:16:44:16 | 0 | 1 | 48 | 55 | +| test.c:44:12:44:16 | ... > ... | test.c:44:16:44:16 | 0 | < | test.c:44:12:44:12 | z | 0 | 45 | 45 | +| test.c:44:12:44:16 | ... > ... | test.c:44:16:44:16 | 0 | < | test.c:44:12:44:12 | z | 0 | 45 | 47 | +| test.c:44:12:44:16 | ... > ... | test.c:44:16:44:16 | 0 | < | test.c:44:12:44:12 | z | 0 | 48 | 55 | +| test.c:44:12:44:16 | ... > ... | test.c:44:16:44:16 | 0 | >= | test.c:44:12:44:12 | z | 0 | 51 | 53 | +| test.c:45:16:45:20 | ... > ... | test.c:45:16:45:16 | y | < | test.c:45:20:45:20 | 0 | 1 | 48 | 55 | +| test.c:45:16:45:20 | ... > ... | test.c:45:16:45:16 | y | >= | test.c:45:20:45:20 | 0 | 1 | 45 | 47 | +| test.c:45:16:45:20 | ... > ... | test.c:45:20:45:20 | 0 | < | test.c:45:16:45:16 | y | 0 | 45 | 47 | +| test.c:45:16:45:20 | ... > ... | test.c:45:20:45:20 | 0 | >= | test.c:45:16:45:16 | y | 0 | 48 | 55 | +| test.c:58:9:58:14 | ... == ... | test.c:58:9:58:9 | x | != | test.c:58:14:58:14 | 0 | 0 | 58 | 58 | +| test.c:58:9:58:14 | ... == ... | test.c:58:9:58:9 | x | != | test.c:58:14:58:14 | 0 | 0 | 62 | 62 | +| test.c:58:9:58:14 | ... == ... | test.c:58:14:58:14 | 0 | != | test.c:58:9:58:9 | x | 0 | 58 | 58 | +| test.c:58:9:58:14 | ... == ... | test.c:58:14:58:14 | 0 | != | test.c:58:9:58:9 | x | 0 | 62 | 62 | +| test.c:58:9:58:23 | ... \|\| ... | test.c:58:9:58:9 | x | != | test.c:58:14:58:14 | 0 | 0 | 62 | 62 | +| test.c:58:9:58:23 | ... \|\| ... | test.c:58:14:58:14 | 0 | != | test.c:58:9:58:9 | x | 0 | 62 | 62 | +| test.c:58:9:58:23 | ... \|\| ... | test.c:58:19:58:19 | y | >= | test.c:58:23:58:23 | 0 | 0 | 62 | 62 | +| test.c:58:9:58:23 | ... \|\| ... | test.c:58:23:58:23 | 0 | < | test.c:58:19:58:19 | y | 1 | 62 | 62 | +| test.c:58:19:58:23 | ... < ... | test.c:58:19:58:19 | y | >= | test.c:58:23:58:23 | 0 | 0 | 62 | 62 | +| test.c:58:19:58:23 | ... < ... | test.c:58:23:58:23 | 0 | < | test.c:58:19:58:19 | y | 1 | 62 | 62 | +| test.c:75:9:75:14 | ... == ... | test.c:75:9:75:9 | x | != | test.c:75:14:75:14 | 0 | 0 | 78 | 79 | +| test.c:75:9:75:14 | ... == ... | test.c:75:9:75:9 | x | == | test.c:75:14:75:14 | 0 | 0 | 75 | 77 | +| test.c:75:9:75:14 | ... == ... | test.c:75:14:75:14 | 0 | != | test.c:75:9:75:9 | x | 0 | 78 | 79 | +| test.c:75:9:75:14 | ... == ... | test.c:75:14:75:14 | 0 | == | test.c:75:9:75:9 | x | 0 | 75 | 77 | +| test.c:85:8:85:13 | ... == ... | test.c:85:8:85:8 | x | == | test.c:85:13:85:13 | 0 | 0 | 85 | 85 | +| test.c:85:8:85:13 | ... == ... | test.c:85:8:85:8 | x | == | test.c:85:13:85:13 | 0 | 0 | 86 | 86 | +| test.c:85:8:85:13 | ... == ... | test.c:85:13:85:13 | 0 | == | test.c:85:8:85:8 | x | 0 | 85 | 85 | +| test.c:85:8:85:13 | ... == ... | test.c:85:13:85:13 | 0 | == | test.c:85:8:85:8 | x | 0 | 86 | 86 | +| test.c:85:8:85:23 | ... && ... | test.c:85:8:85:8 | x | == | test.c:85:13:85:13 | 0 | 0 | 86 | 86 | +| test.c:85:8:85:23 | ... && ... | test.c:85:13:85:13 | 0 | == | test.c:85:8:85:8 | x | 0 | 86 | 86 | +| test.c:85:8:85:23 | ... && ... | test.c:85:18:85:18 | y | != | test.c:85:23:85:23 | 0 | 0 | 86 | 86 | +| test.c:85:8:85:23 | ... && ... | test.c:85:23:85:23 | 0 | != | test.c:85:18:85:18 | y | 0 | 86 | 86 | +| test.c:85:18:85:23 | ... != ... | test.c:85:18:85:18 | y | != | test.c:85:23:85:23 | 0 | 0 | 86 | 86 | +| test.c:85:18:85:23 | ... != ... | test.c:85:23:85:23 | 0 | != | test.c:85:18:85:18 | y | 0 | 86 | 86 | +| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | != | test.c:94:16:94:16 | 0 | 0 | 94 | 96 | +| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | test.c:94:16:94:16 | 0 | 0 | 70 | 70 | +| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | test.c:94:16:94:16 | 0 | 0 | 99 | 102 | +| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | test.c:94:16:94:16 | 0 | 0 | 102 | 102 | +| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | test.c:94:16:94:16 | 0 | 0 | 107 | 109 | +| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | test.c:94:16:94:16 | 0 | 0 | 109 | 109 | +| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | test.c:94:16:94:16 | 0 | 0 | 109 | 117 | +| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | test.c:94:16:94:16 | 0 | 0 | 113 | 113 | +| test.c:94:11:94:16 | ... != ... | test.c:94:16:94:16 | 0 | != | test.c:94:11:94:11 | x | 0 | 94 | 96 | +| test.c:94:11:94:16 | ... != ... | test.c:94:16:94:16 | 0 | == | test.c:94:11:94:11 | x | 0 | 70 | 70 | +| test.c:94:11:94:16 | ... != ... | test.c:94:16:94:16 | 0 | == | test.c:94:11:94:11 | x | 0 | 99 | 102 | +| test.c:94:11:94:16 | ... != ... | test.c:94:16:94:16 | 0 | == | test.c:94:11:94:11 | x | 0 | 102 | 102 | +| test.c:94:11:94:16 | ... != ... | test.c:94:16:94:16 | 0 | == | test.c:94:11:94:11 | x | 0 | 107 | 109 | +| test.c:94:11:94:16 | ... != ... | test.c:94:16:94:16 | 0 | == | test.c:94:11:94:11 | x | 0 | 109 | 109 | +| test.c:94:11:94:16 | ... != ... | test.c:94:16:94:16 | 0 | == | test.c:94:11:94:11 | x | 0 | 109 | 117 | +| test.c:94:11:94:16 | ... != ... | test.c:94:16:94:16 | 0 | == | test.c:94:11:94:11 | x | 0 | 113 | 113 | +| test.c:102:16:102:21 | ... < ... | test.c:102:16:102:16 | j | < | test.c:102:20:102:21 | 10 | 0 | 102 | 102 | +| test.c:102:16:102:21 | ... < ... | test.c:102:16:102:16 | j | >= | test.c:102:20:102:21 | 10 | 0 | 70 | 70 | +| test.c:102:16:102:21 | ... < ... | test.c:102:16:102:16 | j | >= | test.c:102:20:102:21 | 10 | 0 | 107 | 109 | +| test.c:102:16:102:21 | ... < ... | test.c:102:16:102:16 | j | >= | test.c:102:20:102:21 | 10 | 0 | 109 | 109 | +| test.c:102:16:102:21 | ... < ... | test.c:102:16:102:16 | j | >= | test.c:102:20:102:21 | 10 | 0 | 109 | 117 | +| test.c:102:16:102:21 | ... < ... | test.c:102:16:102:16 | j | >= | test.c:102:20:102:21 | 10 | 0 | 113 | 113 | +| test.c:102:16:102:21 | ... < ... | test.c:102:20:102:21 | 10 | < | test.c:102:16:102:16 | j | 1 | 70 | 70 | +| test.c:102:16:102:21 | ... < ... | test.c:102:20:102:21 | 10 | < | test.c:102:16:102:16 | j | 1 | 107 | 109 | +| test.c:102:16:102:21 | ... < ... | test.c:102:20:102:21 | 10 | < | test.c:102:16:102:16 | j | 1 | 109 | 109 | +| test.c:102:16:102:21 | ... < ... | test.c:102:20:102:21 | 10 | < | test.c:102:16:102:16 | j | 1 | 109 | 117 | +| test.c:102:16:102:21 | ... < ... | test.c:102:20:102:21 | 10 | < | test.c:102:16:102:16 | j | 1 | 113 | 113 | +| test.c:102:16:102:21 | ... < ... | test.c:102:20:102:21 | 10 | >= | test.c:102:16:102:16 | j | 1 | 102 | 102 | +| test.c:109:9:109:14 | ... == ... | test.c:109:9:109:9 | x | != | test.c:109:14:109:14 | 0 | 0 | 109 | 109 | +| test.c:109:9:109:14 | ... == ... | test.c:109:9:109:9 | x | != | test.c:109:14:109:14 | 0 | 0 | 113 | 113 | +| test.c:109:9:109:14 | ... == ... | test.c:109:14:109:14 | 0 | != | test.c:109:9:109:9 | x | 0 | 109 | 109 | +| test.c:109:9:109:14 | ... == ... | test.c:109:14:109:14 | 0 | != | test.c:109:9:109:9 | x | 0 | 113 | 113 | +| test.c:109:9:109:23 | ... \|\| ... | test.c:109:9:109:9 | x | != | test.c:109:14:109:14 | 0 | 0 | 113 | 113 | +| test.c:109:9:109:23 | ... \|\| ... | test.c:109:14:109:14 | 0 | != | test.c:109:9:109:9 | x | 0 | 113 | 113 | +| test.c:109:9:109:23 | ... \|\| ... | test.c:109:19:109:19 | y | >= | test.c:109:23:109:23 | 0 | 0 | 113 | 113 | +| test.c:109:9:109:23 | ... \|\| ... | test.c:109:23:109:23 | 0 | < | test.c:109:19:109:19 | y | 1 | 113 | 113 | +| test.c:109:19:109:23 | ... < ... | test.c:109:19:109:19 | y | >= | test.c:109:23:109:23 | 0 | 0 | 113 | 113 | +| test.c:109:19:109:23 | ... < ... | test.c:109:23:109:23 | 0 | < | test.c:109:19:109:19 | y | 1 | 113 | 113 | +| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | != | test.cpp:31:12:31:13 | - ... | 0 | 34 | 34 | +| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | == | test.cpp:31:12:31:13 | - ... | 0 | 31 | 32 | +| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:12:31:13 | - ... | != | test.cpp:31:7:31:7 | x | 0 | 34 | 34 | +| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:12:31:13 | - ... | == | test.cpp:31:7:31:7 | x | 0 | 31 | 32 | diff --git a/cpp/ql/test/library-tests/controlflow/guards/GuardsEnsure.ql b/cpp/ql/test/library-tests/controlflow/guards/GuardsEnsure.ql new file mode 100644 index 000000000000..67e0cf698a1c --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/guards/GuardsEnsure.ql @@ -0,0 +1,23 @@ +/** + * @name Guards control test + * @description List which guards ensure which inequalities apply to which blocks + * @kind test + */ + +import cpp +import semmle.code.cpp.controlflow.Guards + + +from GuardCondition guard, Expr left, Expr right, int k, int start, int end, string op +where +exists(BasicBlock block | + guard.ensuresLt(left, right, k, block, true) and op = "<" + or + guard.ensuresLt(left, right, k, block, false) and op = ">=" + or + guard.ensuresEq(left, right, k, block, true) and op = "==" + or + guard.ensuresEq(left, right, k, block, false) and op = "!=" | + block.hasLocationInfo(_, start, _, end, _) +) +select guard, left, op, right, k, start, end diff --git a/cpp/ql/test/library-tests/controlflow/guards/test.c b/cpp/ql/test/library-tests/controlflow/guards/test.c new file mode 100644 index 000000000000..186244d4fca2 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/guards/test.c @@ -0,0 +1,149 @@ + +int test(int x, int w, int z) { + int j; + long y = 50; + + // simple comparison + if (x > 0) { + y = 20; + z = 10; + } else { + y = 30; + } + + z = x + y; + + // More complex + if(x < 0 && y > 1) + y = 40; + else + y = 20; /* The && expression does not control this block as the x<0 expression jumps here if false. */ + + + z = 10; + + // while loop + while(x > 0) { + y = 10; + x--; + } + + z += y; + + // for loop + for(j = 0; j < 10; j++) { + y = 0; + w = 10; + } + + z += w; + + // nested control flow + for(j = 0; j < 10; j++) { + y = 30; + if(z > 0) + if(y > 0) { + w = 0; + break; + } else { + w = 20; + } + else { + w = 10; + continue; + } + x = 0; + } + + if (x == 0 || y < 0) { + y = 60; + z = 10; + } else + return z; + + z += x; + + return 0; +} + + +int test2(int x, int w, int z) { + int j; + long y = 50; + + // simple comparison + if (x == 0) { + y = 20; + z = 10; + } else { + y = 30; + } + + z = x + y; + + // More complex + if(x == 0 && y != 0) + y = 40; + else + y = 20; + + + z = 10; + + // while loop + while(x != 0) { + y = 10; + x--; + } + + z += y; + + // for loop + for(j = 0; j < 10; j++) { + y = 0; + w = 10; + } + + z += w; + + if (x == 0 || y < 0) { + y = 60; + z = 10; + } else + return z; + + z += x; + + return 0; +} + +int test3_condition(); +void test3_action(); + +void test3() { + int b = 0; + + if (1 && test3_condition()) { + b = 1; + test3_action(); + } + + if (b) { + test3_action(); + } +} + +void test4(int i) { + if (0) { + if (i) { + ; + } + } + return; +} + +void test5(int x) { + if (!x) { + test3(); + } +} diff --git a/cpp/ql/test/library-tests/controlflow/guards/test.cpp b/cpp/ql/test/library-tests/controlflow/guards/test.cpp new file mode 100644 index 000000000000..46d894813f9b --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/guards/test.cpp @@ -0,0 +1,54 @@ +class X +{ +public: + void set(); +}; + +class Y +{ +public: + Y* f(); + + const X* get() const { return 0; } + X* get() { return 0; } +}; + +Y* Y::f() +{ + if ( get() ) + get()->set(); + return 0; +} + +class Error { +public: + Error() {} +}; + +bool getABool(); + +void doSomething(int x) { + if (x == -1) { + throw new Error(); + } +} + +void doSomethingElse(int x); + +bool testWithCatch0(int v) +{ + try + { + if( getABool() ) + { + doSomething(v); + return true; + } + } + catch(Error &e) + { + doSomethingElse(v); + } + + return false; +} diff --git a/cpp/ql/test/library-tests/controlflow/loopentrycondition/forstmt.cpp b/cpp/ql/test/library-tests/controlflow/loopentrycondition/forstmt.cpp new file mode 100644 index 000000000000..8345e3a2da61 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/loopentrycondition/forstmt.cpp @@ -0,0 +1,102 @@ +// GOOD = at least one iteration +// BAD = possibly no iterations + +void test1() { + for (int i = 0; i < 10; i++) { // GOOD + } +} + +void test2() { + for (int i = 0, j = 1; i + j < 10; i++) { // GOOD + } +} + +void test3() { + int j = 2; + for (int i = j = 1; i + j < 10; i++) { // GOOD + } +} + +void test4() { + int i = 2, j = 3; + for (i = j = 1; i + j < 10; i++) { // GOOD + } +} + +void test5() { + int i, k; + for (i = k = 0; i < 10; i++) { // GOOD + } +} + +void test6() { + int i = 0; + for (; i < 10; i++) { // GOOD + i = 1; + } +} + +void test7() { + int i = 0; + for (i = 1; i < 10; i++) { // GOOD + i = 1; + } +} + +void test8() { + int i = 0; + i = 1; + for (; i < 10; i++) { // GOOD (NOT REPORTED) + } +} + +void test9() { + bool done = false; + for (; !done; ) { // GOOD + done = true; + } +} + +void test10(int i) { + bool done = false; + for (; i++; i < 10) { // BAD + for (; !done; ) { // BAD + done = true; + } + } +} + +void test11(int i) { + for (; i++; i < 10) { // BAD + bool done = false; + for (; !done; ) { // GOOD + done = true; + } + } +} + +void test12(int max) { + int i, k; + int max_index = 0; + for (i = k = 0; i < max; i++) { // BAD + max_index = i; + } + for (i = 0; i <= max_index; i++) { // BAD + } +} + +void test13() { + int i; + for (i = 1; i > 0; ) { // GOOD + &i; + } +} + +void test14(bool b) { + int i = 1; + while (b) { + for (; i > 0; ) { // BAD + &i; + } + } +} \ No newline at end of file diff --git a/cpp/ql/test/library-tests/controlflow/loopentrycondition/forstmt01.expected b/cpp/ql/test/library-tests/controlflow/loopentrycondition/forstmt01.expected new file mode 100644 index 000000000000..f290d0e3924a --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/loopentrycondition/forstmt01.expected @@ -0,0 +1,10 @@ +| forstmt.cpp:5:5:6:5 | for(...;...;...) ... | +| forstmt.cpp:10:5:11:5 | for(...;...;...) ... | +| forstmt.cpp:16:5:17:5 | for(...;...;...) ... | +| forstmt.cpp:22:5:23:5 | for(...;...;...) ... | +| forstmt.cpp:28:5:29:5 | for(...;...;...) ... | +| forstmt.cpp:34:5:36:5 | for(...;...;...) ... | +| forstmt.cpp:41:5:43:5 | for(...;...;...) ... | +| forstmt.cpp:55:5:57:5 | for(...;...;...) ... | +| forstmt.cpp:72:9:74:9 | for(...;...;...) ... | +| forstmt.cpp:90:5:92:5 | for(...;...;...) ... | diff --git a/cpp/ql/test/library-tests/controlflow/loopentrycondition/forstmt01.ql b/cpp/ql/test/library-tests/controlflow/loopentrycondition/forstmt01.ql new file mode 100644 index 000000000000..9e22719a6506 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/loopentrycondition/forstmt01.ql @@ -0,0 +1,5 @@ +import cpp + +from ForStmt for +where for.conditionAlwaysTrueUponEntry() +select for \ No newline at end of file diff --git a/cpp/ql/test/library-tests/controlflow/loopentrycondition/whilestmt.cpp b/cpp/ql/test/library-tests/controlflow/loopentrycondition/whilestmt.cpp new file mode 100644 index 000000000000..e0ddb1b42f15 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/loopentrycondition/whilestmt.cpp @@ -0,0 +1,64 @@ +// GOOD = at least one iteration +// BAD = possibly no iterations + +void test1() { + bool done = false; + while (!done) { // GOOD + done = true; + } +} + +void test2() { + bool done = true; + done = false; + while (!done) { // GOOD (NOT REPORTED) + done = true; + } +} + +void test3(int i) { + bool done = false; + for (; i++; i < 10) { + while (!done) { // BAD + done = true; + } + } +} + +void test4(int i) { + for (; i++; i < 10) { + bool done = false; + while (!done) { // GOOD + done = true; + } + } +} + +void test5(int max) { + int i = 0, k = 0; + int max_index = 0; + while (i < max) { // BAD + max_index = i; + i++; + } + i = 0; + while (i <= max_index) { // BAD + i++; + } +} + +void test6() { + int i = 1; + while (i > 0) { // GOOD + &i; + } +} + +void test7(bool b) { + int i = 1; + while (b) { // BAD + while (i > 0) { // BAD + &i; + } + } +} \ No newline at end of file diff --git a/cpp/ql/test/library-tests/controlflow/loopentrycondition/whilestmt01.expected b/cpp/ql/test/library-tests/controlflow/loopentrycondition/whilestmt01.expected new file mode 100644 index 000000000000..66968b5263d4 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/loopentrycondition/whilestmt01.expected @@ -0,0 +1,3 @@ +| whilestmt.cpp:6:5:8:5 | while (...) ... | +| whilestmt.cpp:31:9:33:9 | while (...) ... | +| whilestmt.cpp:52:5:54:5 | while (...) ... | diff --git a/cpp/ql/test/library-tests/controlflow/loopentrycondition/whilestmt01.ql b/cpp/ql/test/library-tests/controlflow/loopentrycondition/whilestmt01.ql new file mode 100644 index 000000000000..0ed91f67f474 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/loopentrycondition/whilestmt01.ql @@ -0,0 +1,5 @@ +import cpp + +from WhileStmt while +where while.conditionAlwaysTrueUponEntry() +select while \ No newline at end of file diff --git a/cpp/ql/test/library-tests/controlflow/primitives/cfg.expected b/cpp/ql/test/library-tests/controlflow/primitives/cfg.expected new file mode 100644 index 000000000000..95b77212e612 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/primitives/cfg.expected @@ -0,0 +1,241 @@ +| | cpp_range_based_for | 0 | 1 | file://:0:0:0:0 | (reference dereference) | | +| | cpp_range_based_for | 0 | 1 | file://:0:0:0:0 | (reference dereference) | | +| | cpp_range_based_for | 0 | 1 | file://:0:0:0:0 | (reference to) | | +| | cpp_range_based_for | 0 | 9 | file://:0:0:0:0 | initializer for (__range) | declaration | +| | cpp_range_based_for | 0 | 11 | file://:0:0:0:0 | (__range) | call to begin | +| | cpp_range_based_for | 0 | 13 | file://:0:0:0:0 | initializer for (__begin) | (__range) | +| | cpp_range_based_for | 0 | 14 | file://:0:0:0:0 | (__range) | call to end | +| | cpp_range_based_for | 0 | 16 | file://:0:0:0:0 | initializer for (__end) | (__end) | +| | cpp_range_based_for | 0 | 28 | file://:0:0:0:0 | (__begin) | call to operator!= | +| | cpp_range_based_for | 0 | 28 | file://:0:0:0:0 | (__begin) | call to operator* | +| | cpp_range_based_for | 0 | 28 | file://:0:0:0:0 | (__begin) | call to operator++ | +| | cpp_range_based_for | 0 | 28 | file://:0:0:0:0 | (__end) | (__begin) | +| cpp | CopyConstructorClass | 28 | 1 | cpp.cpp:28:5:28:24 | CopyConstructorClass | | +| cpp | CopyConstructorClass | 30 | 1 | cpp.cpp:30:5:30:24 | CopyConstructorClass | | +| cpp | IntVectorIter | 4 | 1 | cpp.cpp:4:7:4:7 | IntVectorIter | | +| cpp | IntVectorIter | 4 | 1 | cpp.cpp:4:7:4:7 | { ... } | return ... | +| cpp | IntVectorIter | 4 | 2 | cpp.cpp:4:7:4:7 | return ... | IntVectorIter | +| cpp | IntVectorIter | 4 | 3 | cpp.cpp:4:7:4:7 | IntVectorIter | | +| cpp | IntVectorIter | 6 | 1 | cpp.cpp:6:5:6:17 | IntVectorIter | | +| cpp | begin | 14 | 1 | cpp.cpp:14:19:14:23 | begin | | +| cpp | cpp_CopyConstructorClass | 33 | 1 | cpp.cpp:33:71:35:1 | { ... } | return ... | +| cpp | cpp_CopyConstructorClass | 33 | 5 | cpp.cpp:33:22:33:45 | cpp_CopyConstructorClass | | +| cpp | cpp_CopyConstructorClass | 34 | 1 | cpp.cpp:34:33:34:33 | (const CopyConstructorClass)... | | +| cpp | cpp_CopyConstructorClass | 34 | 1 | cpp.cpp:34:33:34:33 | (reference to) | | +| cpp | cpp_CopyConstructorClass | 34 | 2 | cpp.cpp:34:5:34:35 | return ... | x | +| cpp | cpp_CopyConstructorClass | 34 | 3 | cpp.cpp:34:33:34:33 | x | call to CopyConstructorClass | +| cpp | cpp_CopyConstructorClass | 34 | 4 | cpp.cpp:34:5:34:35 | call to CopyConstructorClass | cpp_CopyConstructorClass | +| cpp | cpp_range_based_for | 18 | 1 | cpp.cpp:18:32:24:1 | { ... } | declaration | +| cpp | cpp_range_based_for | 18 | 30 | cpp.cpp:18:6:18:24 | cpp_range_based_for | | +| cpp | cpp_range_based_for | 19 | 2 | cpp.cpp:19:5:19:18 | declaration | declaration | +| cpp | cpp_range_based_for | 20 | 3 | cpp.cpp:20:5:20:14 | declaration | initializer for j | +| cpp | cpp_range_based_for | 20 | 4 | cpp.cpp:20:12:20:13 | initializer for j | 0 | +| cpp | cpp_range_based_for | 20 | 5 | cpp.cpp:20:12:20:13 | 0 | for(...:...) ... | +| cpp | cpp_range_based_for | 22 | 1 | cpp.cpp:22:18:22:20 | (reference to) | | +| cpp | cpp_range_based_for | 22 | 1 | cpp.cpp:22:18:22:21 | (reference dereference) | | +| cpp | cpp_range_based_for | 22 | 6 | cpp.cpp:22:5:23:12 | for(...:...) ... | declaration | +| cpp | cpp_range_based_for | 22 | 7 | cpp.cpp:22:5:23:12 | declaration | vec | +| cpp | cpp_range_based_for | 22 | 8 | cpp.cpp:22:18:22:20 | vec | initializer for (__range) | +| cpp | cpp_range_based_for | 22 | 10 | cpp.cpp:22:5:23:12 | declaration | (__range) | +| cpp | cpp_range_based_for | 22 | 12 | cpp.cpp:22:18:22:18 | call to begin | initializer for (__begin) | +| cpp | cpp_range_based_for | 22 | 15 | cpp.cpp:22:18:22:18 | call to end | initializer for (__end) | +| cpp | cpp_range_based_for | 22 | 28 | cpp.cpp:22:5:23:12 | declaration | (__begin) | +| cpp | cpp_range_based_for | 22 | 28 | cpp.cpp:22:14:22:14 | initializer for i | ExprStmt | +| cpp | cpp_range_based_for | 22 | 28 | cpp.cpp:22:18:22:18 | call to operator!= | return ... | +| cpp | cpp_range_based_for | 22 | 28 | cpp.cpp:22:18:22:18 | call to operator!= | declaration | +| cpp | cpp_range_based_for | 22 | 28 | cpp.cpp:22:18:22:18 | call to operator* | initializer for i | +| cpp | cpp_range_based_for | 22 | 28 | cpp.cpp:22:18:22:18 | call to operator++ | (__end) | +| cpp | cpp_range_based_for | 23 | 28 | cpp.cpp:23:9:23:9 | j | ... ++ | +| cpp | cpp_range_based_for | 23 | 28 | cpp.cpp:23:9:23:11 | ... ++ | (__begin) | +| cpp | cpp_range_based_for | 23 | 28 | cpp.cpp:23:9:23:12 | ExprStmt | j | +| cpp | cpp_range_based_for | 24 | 29 | cpp.cpp:24:1:24:1 | return ... | cpp_range_based_for | +| cpp | end | 15 | 1 | cpp.cpp:15:19:15:21 | end | | +| cpp | operator!= | 7 | 1 | cpp.cpp:7:10:7:19 | operator!= | | +| cpp | operator* | 8 | 1 | cpp.cpp:8:9:8:17 | operator* | | +| cpp | operator++ | 9 | 1 | cpp.cpp:9:20:9:29 | operator++ | | +| cpp | operator= | 4 | 1 | cpp.cpp:4:7:4:7 | operator= | | +| cpp | operator= | 4 | 1 | cpp.cpp:4:7:4:7 | operator= | | +| cpp | operator= | 12 | 1 | cpp.cpp:12:7:12:7 | operator= | | +| cpp | operator= | 12 | 1 | cpp.cpp:12:7:12:7 | operator= | | +| cpp | operator= | 26 | 1 | cpp.cpp:26:7:26:7 | operator= | | +| cpp | ~CopyConstructorClass | 29 | 1 | cpp.cpp:29:5:29:25 | ~CopyConstructorClass | | +| ms | ms_try_except | 2 | 1 | ms.cpp:2:27:18:1 | { ... } | declaration | +| ms | ms_try_except | 2 | 27 | ms.cpp:2:6:2:18 | ms_try_except | | +| ms | ms_try_except | 3 | 2 | ms.cpp:3:5:3:10 | declaration | __try { ... } __except( ... ) { ... } | +| ms | ms_try_except | 5 | 3 | ms.cpp:5:5:7:5 | __try { ... } __except( ... ) { ... } | { ... } | +| ms | ms_try_except | 5 | 4 | ms.cpp:5:11:7:5 | { ... } | ExprStmt | +| ms | ms_try_except | 6 | 5 | ms.cpp:6:9:6:14 | ExprStmt | 1 | +| ms | ms_try_except | 6 | 6 | ms.cpp:6:13:6:13 | 1 | x | +| ms | ms_try_except | 6 | 7 | ms.cpp:6:9:6:9 | x | ... = ... | +| ms | ms_try_except | 6 | 8 | ms.cpp:6:9:6:13 | ... = ... | __try { ... } __finally { ... } | +| ms | ms_try_except | 8 | 1 | ms.cpp:8:15:8:15 | j | ms_try_except | +| ms | ms_try_except | 8 | 1 | ms.cpp:8:15:8:15 | j | { ... } | +| ms | ms_try_except | 8 | 2 | ms.cpp:8:18:10:5 | { ... } | ExprStmt | +| ms | ms_try_except | 9 | 3 | ms.cpp:9:9:9:14 | ExprStmt | 2 | +| ms | ms_try_except | 9 | 4 | ms.cpp:9:13:9:13 | 2 | x | +| ms | ms_try_except | 9 | 5 | ms.cpp:9:9:9:9 | x | ... = ... | +| ms | ms_try_except | 9 | 6 | ms.cpp:9:9:9:13 | ... = ... | __try { ... } __finally { ... } | +| ms | ms_try_except | 12 | 15 | ms.cpp:12:5:14:5 | __try { ... } __finally { ... } | { ... } | +| ms | ms_try_except | 12 | 16 | ms.cpp:12:11:14:5 | { ... } | ExprStmt | +| ms | ms_try_except | 13 | 17 | ms.cpp:13:9:13:14 | ExprStmt | 3 | +| ms | ms_try_except | 13 | 18 | ms.cpp:13:13:13:13 | 3 | x | +| ms | ms_try_except | 13 | 19 | ms.cpp:13:9:13:9 | x | ... = ... | +| ms | ms_try_except | 13 | 20 | ms.cpp:13:9:13:13 | ... = ... | { ... } | +| ms | ms_try_except | 15 | 21 | ms.cpp:15:15:17:5 | { ... } | ExprStmt | +| ms | ms_try_except | 16 | 22 | ms.cpp:16:9:16:14 | ExprStmt | 4 | +| ms | ms_try_except | 16 | 23 | ms.cpp:16:13:16:13 | 4 | x | +| ms | ms_try_except | 16 | 24 | ms.cpp:16:9:16:9 | x | ... = ... | +| ms | ms_try_except | 16 | 25 | ms.cpp:16:9:16:13 | ... = ... | ms_try_except | +| ms | ms_try_except | 16 | 25 | ms.cpp:16:9:16:13 | ... = ... | return ... | +| ms | ms_try_except | 18 | 26 | ms.cpp:18:1:18:1 | return ... | ms_try_except | +| test | f_agg | 26 | 1 | test.c:26:18:28:1 | { ... } | declaration | +| test | f_agg | 26 | 14 | test.c:26:6:26:10 | f_agg | | +| test | f_agg | 27 | 2 | test.c:27:5:27:35 | declaration | initializer for i | +| test | f_agg | 27 | 3 | test.c:27:14:27:34 | initializer for i | 1 | +| test | f_agg | 27 | 4 | test.c:27:16:27:16 | 1 | 2 | +| test | f_agg | 27 | 5 | test.c:27:19:27:19 | 2 | 3 | +| test | f_agg | 27 | 6 | test.c:27:23:27:23 | 3 | ... + ... | +| test | f_agg | 27 | 7 | test.c:27:19:27:23 | ... + ... | 4 | +| test | f_agg | 27 | 8 | test.c:27:26:27:26 | 4 | 5 | +| test | f_agg | 27 | 9 | test.c:27:30:27:30 | 5 | ... + ... | +| test | f_agg | 27 | 10 | test.c:27:26:27:30 | ... + ... | 6 | +| test | f_agg | 27 | 11 | test.c:27:33:27:33 | 6 | {...} | +| test | f_agg | 27 | 12 | test.c:27:14:27:34 | {...} | return ... | +| test | f_agg | 28 | 13 | test.c:28:1:28:1 | return ... | f_agg | +| test | f_computed_goto | 36 | 1 | test.c:36:28:47:1 | { ... } | declaration | +| test | f_computed_goto | 36 | 22 | test.c:36:6:36:20 | f_computed_goto | | +| test | f_computed_goto | 37 | 2 | test.c:37:5:37:10 | declaration | declaration | +| test | f_computed_goto | 38 | 3 | test.c:38:5:38:14 | declaration | ExprStmt | +| test | f_computed_goto | 39 | 4 | test.c:39:5:39:11 | ExprStmt | 10 | +| test | f_computed_goto | 39 | 5 | test.c:39:9:39:10 | 10 | x | +| test | f_computed_goto | 39 | 6 | test.c:39:5:39:5 | x | ... = ... | +| test | f_computed_goto | 39 | 7 | test.c:39:5:39:10 | ... = ... | label ...: | +| test | f_computed_goto | 40 | 8 | test.c:40:1:40:8 | label ...: | ExprStmt | +| test | f_computed_goto | 41 | 9 | test.c:41:5:41:8 | ExprStmt | x | +| test | f_computed_goto | 41 | 10 | test.c:41:5:41:5 | x | ... -- | +| test | f_computed_goto | 41 | 11 | test.c:41:5:41:7 | ... -- | ExprStmt | +| test | f_computed_goto | 42 | 12 | test.c:42:5:42:20 | ExprStmt | &&myLabel | +| test | f_computed_goto | 42 | 13 | test.c:42:11:42:19 | &&myLabel | ptr | +| test | f_computed_goto | 42 | 14 | test.c:42:5:42:7 | ptr | ... = ... | +| test | f_computed_goto | 42 | 15 | test.c:42:5:42:19 | ... = ... | ExprStmt | +| test | f_computed_goto | 43 | 16 | test.c:43:5:43:8 | ExprStmt | x | +| test | f_computed_goto | 43 | 17 | test.c:43:5:43:5 | x | ... -- | +| test | f_computed_goto | 43 | 18 | test.c:43:5:43:7 | ... -- | if (...) ... | +| test | f_computed_goto | 44 | 19 | test.c:44:5:46:5 | if (...) ... | x | +| test | f_computed_goto | 44 | 20 | test.c:44:9:44:9 | x | return ... | +| test | f_computed_goto | 44 | 20 | test.c:44:9:44:9 | x | { ... } | +| test | f_computed_goto | 44 | 21 | test.c:44:12:46:5 | { ... } | computed goto ... | +| test | f_computed_goto | 45 | 22 | test.c:45:9:45:18 | computed goto ... | ptr | +| test | f_computed_goto | 45 | 23 | test.c:45:15:45:17 | ptr | | +| test | f_computed_goto | 47 | 21 | test.c:47:1:47:1 | return ... | f_computed_goto | +| test | f_do_while | 30 | 1 | test.c:30:24:34:1 | { ... } | do (...) ... | +| test | f_do_while | 30 | 9 | test.c:30:6:30:15 | f_do_while | | +| test | f_do_while | 31 | 2 | test.c:31:5:33:16 | do (...) ... | { ... } | +| test | f_do_while | 31 | 7 | test.c:31:8:33:5 | { ... } | ExprStmt | +| test | f_do_while | 32 | 7 | test.c:32:9:32:9 | i | ... -- | +| test | f_do_while | 32 | 7 | test.c:32:9:32:11 | ... -- | i | +| test | f_do_while | 32 | 7 | test.c:32:9:32:12 | ExprStmt | i | +| test | f_do_while | 33 | 7 | test.c:33:14:33:14 | i | return ... | +| test | f_do_while | 33 | 7 | test.c:33:14:33:14 | i | { ... } | +| test | f_do_while | 34 | 8 | test.c:34:1:34:1 | return ... | f_do_while | +| test | f_if_quest2 | 14 | 1 | test.c:14:32:18:1 | { ... } | if (...) ... | +| test | f_if_quest2 | 14 | 12 | test.c:14:6:14:16 | f_if_quest2 | | +| test | f_if_quest2 | 15 | 1 | test.c:15:9:15:9 | x | | +| test | f_if_quest2 | 15 | 2 | test.c:15:5:17:5 | if (...) ... | ... ? ... : ... | +| test | f_if_quest2 | 15 | 3 | test.c:15:9:15:14 | ... ? ... : ... | x | +| test | f_if_quest2 | 15 | 4 | test.c:15:9:15:9 | x | y | +| test | f_if_quest2 | 15 | 4 | test.c:15:9:15:9 | x | { ... } | +| test | f_if_quest2 | 15 | 5 | test.c:15:14:15:14 | y | return ... | +| test | f_if_quest2 | 15 | 5 | test.c:15:14:15:14 | y | { ... } | +| test | f_if_quest2 | 15 | 6 | test.c:15:17:17:5 | { ... } | ExprStmt | +| test | f_if_quest2 | 16 | 7 | test.c:16:9:16:14 | ExprStmt | 3 | +| test | f_if_quest2 | 16 | 8 | test.c:16:13:16:13 | 3 | x | +| test | f_if_quest2 | 16 | 9 | test.c:16:9:16:9 | x | ... = ... | +| test | f_if_quest2 | 16 | 10 | test.c:16:9:16:13 | ... = ... | return ... | +| test | f_if_quest2 | 18 | 11 | test.c:18:1:18:1 | return ... | f_if_quest2 | +| test | f_if_quest3 | 20 | 1 | test.c:20:39:24:1 | { ... } | if (...) ... | +| test | f_if_quest3 | 20 | 13 | test.c:20:6:20:16 | f_if_quest3 | | +| test | f_if_quest3 | 21 | 2 | test.c:21:5:23:5 | if (...) ... | ... ? ... : ... | +| test | f_if_quest3 | 21 | 3 | test.c:21:9:21:17 | ... ? ... : ... | x | +| test | f_if_quest3 | 21 | 4 | test.c:21:9:21:9 | x | z | +| test | f_if_quest3 | 21 | 4 | test.c:21:9:21:9 | x | y | +| test | f_if_quest3 | 21 | 5 | test.c:21:13:21:13 | y | return ... | +| test | f_if_quest3 | 21 | 5 | test.c:21:13:21:13 | y | { ... } | +| test | f_if_quest3 | 21 | 5 | test.c:21:17:21:17 | z | return ... | +| test | f_if_quest3 | 21 | 5 | test.c:21:17:21:17 | z | { ... } | +| test | f_if_quest3 | 21 | 7 | test.c:21:20:23:5 | { ... } | ExprStmt | +| test | f_if_quest3 | 22 | 8 | test.c:22:9:22:14 | ExprStmt | 3 | +| test | f_if_quest3 | 22 | 9 | test.c:22:13:22:13 | 3 | x | +| test | f_if_quest3 | 22 | 10 | test.c:22:9:22:9 | x | ... = ... | +| test | f_if_quest3 | 22 | 11 | test.c:22:9:22:13 | ... = ... | return ... | +| test | f_if_quest3 | 24 | 12 | test.c:24:1:24:1 | return ... | f_if_quest3 | +| test | f_land | 2 | 1 | test.c:2:27:6:1 | { ... } | if (...) ... | +| test | f_land | 2 | 12 | test.c:2:6:2:11 | f_land | | +| test | f_land | 3 | 2 | test.c:3:5:5:5 | if (...) ... | ... && ... | +| test | f_land | 3 | 3 | test.c:3:9:3:14 | ... && ... | x | +| test | f_land | 3 | 4 | test.c:3:9:3:9 | x | return ... | +| test | f_land | 3 | 4 | test.c:3:9:3:9 | x | y | +| test | f_land | 3 | 5 | test.c:3:14:3:14 | y | return ... | +| test | f_land | 3 | 5 | test.c:3:14:3:14 | y | { ... } | +| test | f_land | 3 | 6 | test.c:3:17:5:5 | { ... } | ExprStmt | +| test | f_land | 4 | 7 | test.c:4:9:4:14 | ExprStmt | 3 | +| test | f_land | 4 | 8 | test.c:4:13:4:13 | 3 | x | +| test | f_land | 4 | 9 | test.c:4:9:4:9 | x | ... = ... | +| test | f_land | 4 | 10 | test.c:4:9:4:13 | ... = ... | return ... | +| test | f_land | 6 | 11 | test.c:6:1:6:1 | return ... | f_land | +| test | f_lor | 8 | 1 | test.c:8:26:12:1 | { ... } | if (...) ... | +| test | f_lor | 8 | 12 | test.c:8:6:8:10 | f_lor | | +| test | f_lor | 9 | 2 | test.c:9:5:11:5 | if (...) ... | ... \|\| ... | +| test | f_lor | 9 | 3 | test.c:9:9:9:14 | ... \|\| ... | x | +| test | f_lor | 9 | 4 | test.c:9:9:9:9 | x | y | +| test | f_lor | 9 | 4 | test.c:9:9:9:9 | x | { ... } | +| test | f_lor | 9 | 5 | test.c:9:14:9:14 | y | return ... | +| test | f_lor | 9 | 5 | test.c:9:14:9:14 | y | { ... } | +| test | f_lor | 9 | 6 | test.c:9:17:11:5 | { ... } | ExprStmt | +| test | f_lor | 10 | 7 | test.c:10:9:10:14 | ExprStmt | 3 | +| test | f_lor | 10 | 8 | test.c:10:13:10:13 | 3 | x | +| test | f_lor | 10 | 9 | test.c:10:9:10:9 | x | ... = ... | +| test | f_lor | 10 | 10 | test.c:10:9:10:13 | ... = ... | return ... | +| test | f_lor | 12 | 11 | test.c:12:1:12:1 | return ... | f_lor | +| test | odasa4753a | 49 | 1 | test.c:49:22:54:1 | { ... } | if (...) ... | +| test | odasa4753a | 49 | 9 | test.c:49:5:49:14 | odasa4753a | | +| test | odasa4753a | 50 | 2 | test.c:50:5:52:5 | if (...) ... | 1 | +| test | odasa4753a | 50 | 3 | test.c:50:9:50:9 | 1 | { ... } | +| test | odasa4753a | 50 | 4 | test.c:50:12:52:5 | { ... } | return ... | +| test | odasa4753a | 51 | 5 | test.c:51:9:51:17 | return ... | 1 | +| test | odasa4753a | 51 | 6 | test.c:51:16:51:16 | 1 | odasa4753a | +| test | odasa4753a | 53 | 1 | test.c:53:5:53:13 | return ... | 0 | +| test | odasa4753a | 53 | 2 | test.c:53:12:53:12 | 0 | odasa4753a | +| test | odasa4753b | 56 | 1 | test.c:56:22:61:1 | { ... } | if (...) ... | +| test | odasa4753b | 56 | 9 | test.c:56:5:56:14 | odasa4753b | | +| test | odasa4753b | 57 | 1 | test.c:57:12:59:5 | { ... } | return ... | +| test | odasa4753b | 57 | 2 | test.c:57:5:59:5 | if (...) ... | 0 | +| test | odasa4753b | 57 | 3 | test.c:57:9:57:9 | 0 | return ... | +| test | odasa4753b | 58 | 2 | test.c:58:9:58:17 | return ... | 1 | +| test | odasa4753b | 58 | 3 | test.c:58:16:58:16 | 1 | odasa4753b | +| test | odasa4753b | 60 | 4 | test.c:60:5:60:13 | return ... | 0 | +| test | odasa4753b | 60 | 5 | test.c:60:12:60:12 | 0 | odasa4753b | +| test | odasa4762a | 63 | 11 | test.c:63:5:63:14 | odasa4762a | | +| test | odasa4762a | 64 | 1 | test.c:64:1:69:1 | { ... } | if (...) ... | +| test | odasa4762a | 65 | 2 | test.c:65:3:67:3 | if (...) ... | ... && ... | +| test | odasa4762a | 65 | 3 | test.c:65:6:65:11 | ... && ... | 1 | +| test | odasa4762a | 65 | 4 | test.c:65:6:65:6 | 1 | b | +| test | odasa4762a | 65 | 5 | test.c:65:11:65:11 | b | return ... | +| test | odasa4762a | 65 | 5 | test.c:65:11:65:11 | b | { ... } | +| test | odasa4762a | 65 | 6 | test.c:65:14:67:3 | { ... } | return ... | +| test | odasa4762a | 66 | 7 | test.c:66:5:66:13 | return ... | 1 | +| test | odasa4762a | 66 | 8 | test.c:66:12:66:12 | 1 | odasa4762a | +| test | odasa4762a | 68 | 6 | test.c:68:3:68:11 | return ... | 0 | +| test | odasa4762a | 68 | 7 | test.c:68:10:68:10 | 0 | odasa4762a | +| test | odasa4762b | 71 | 11 | test.c:71:5:71:14 | odasa4762b | | +| test | odasa4762b | 72 | 1 | test.c:72:1:77:1 | { ... } | if (...) ... | +| test | odasa4762b | 73 | 2 | test.c:73:3:75:3 | if (...) ... | ... \|\| ... | +| test | odasa4762b | 73 | 3 | test.c:73:6:73:11 | ... \|\| ... | 0 | +| test | odasa4762b | 73 | 4 | test.c:73:6:73:6 | 0 | b | +| test | odasa4762b | 73 | 5 | test.c:73:11:73:11 | b | return ... | +| test | odasa4762b | 73 | 5 | test.c:73:11:73:11 | b | { ... } | +| test | odasa4762b | 73 | 6 | test.c:73:14:75:3 | { ... } | return ... | +| test | odasa4762b | 74 | 7 | test.c:74:5:74:13 | return ... | 1 | +| test | odasa4762b | 74 | 8 | test.c:74:12:74:12 | 1 | odasa4762b | +| test | odasa4762b | 76 | 6 | test.c:76:3:76:11 | return ... | 0 | +| test | odasa4762b | 76 | 7 | test.c:76:10:76:10 | 0 | odasa4762b | diff --git a/cpp/ql/test/library-tests/controlflow/primitives/cfg.ql b/cpp/ql/test/library-tests/controlflow/primitives/cfg.ql new file mode 100644 index 000000000000..521231b4958a --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/primitives/cfg.ql @@ -0,0 +1,22 @@ +import cpp + +string getASuccessorOrNone(ControlFlowNode n) { + if exists(n.getASuccessor()) + then exists (ControlFlowNode s, string trueSucc, string falseSucc | + s = n.getASuccessor() + and if s = n.getATrueSuccessor() then trueSucc = " " + else trueSucc = "" + and if s = n.getAFalseSuccessor() then falseSucc = " " + else falseSucc = "" + and result = trueSucc + falseSucc + s.toString()) + else result = "" +} + +from ControlFlowNode n +select n.getLocation().getFile().getShortName(), + n.getControlFlowScope().toString(), + n.getLocation().getStartLine(), + count(n.getAPredecessor*()), // This helps order things sensibly + n, + getASuccessorOrNone(n) + diff --git a/cpp/ql/test/library-tests/controlflow/primitives/cpp.cpp b/cpp/ql/test/library-tests/controlflow/primitives/cpp.cpp new file mode 100644 index 000000000000..980c1bd071b6 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/primitives/cpp.cpp @@ -0,0 +1,36 @@ + +class IntVector; + +class IntVectorIter { + public: + IntVectorIter (IntVector *p_vec, int pos); + bool operator!= (IntVectorIter& other); + int operator* (); + IntVectorIter& operator++ (); +}; + +class IntVector { + public: + IntVectorIter begin (); + IntVectorIter end (); +}; + +void cpp_range_based_for(void) { + IntVector vec; + int j = 0; + + for (int i : vec) + j++; +} + +class CopyConstructorClass { + public: + CopyConstructorClass(); + ~CopyConstructorClass(); + CopyConstructorClass(const CopyConstructorClass &x); +}; + +CopyConstructorClass cpp_CopyConstructorClass(CopyConstructorClass x) { + return CopyConstructorClass(x); +} + diff --git a/cpp/ql/test/library-tests/controlflow/primitives/falseSucc.expected b/cpp/ql/test/library-tests/controlflow/primitives/falseSucc.expected new file mode 100644 index 000000000000..9588904760cd --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/primitives/falseSucc.expected @@ -0,0 +1,17 @@ +| cpp.cpp:22:18:22:18 | call to operator!= | cpp.cpp:24:1:24:1 | return ... | +| ms.cpp:8:15:8:15 | j | ms.cpp:2:6:2:18 | ms_try_except | +| test.c:3:9:3:9 | x | test.c:6:1:6:1 | return ... | +| test.c:3:14:3:14 | y | test.c:6:1:6:1 | return ... | +| test.c:9:9:9:9 | x | test.c:9:14:9:14 | y | +| test.c:9:14:9:14 | y | test.c:12:1:12:1 | return ... | +| test.c:15:9:15:9 | x | test.c:15:14:15:14 | y | +| test.c:15:14:15:14 | y | test.c:18:1:18:1 | return ... | +| test.c:21:9:21:9 | x | test.c:21:17:21:17 | z | +| test.c:21:13:21:13 | y | test.c:24:1:24:1 | return ... | +| test.c:21:17:21:17 | z | test.c:24:1:24:1 | return ... | +| test.c:33:14:33:14 | i | test.c:34:1:34:1 | return ... | +| test.c:44:9:44:9 | x | test.c:47:1:47:1 | return ... | +| test.c:57:9:57:9 | 0 | test.c:60:5:60:13 | return ... | +| test.c:65:11:65:11 | b | test.c:68:3:68:11 | return ... | +| test.c:73:6:73:6 | 0 | test.c:73:11:73:11 | b | +| test.c:73:11:73:11 | b | test.c:76:3:76:11 | return ... | diff --git a/cpp/ql/test/library-tests/controlflow/primitives/falseSucc.ql b/cpp/ql/test/library-tests/controlflow/primitives/falseSucc.ql new file mode 100644 index 000000000000..d9107a47091c --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/primitives/falseSucc.ql @@ -0,0 +1,6 @@ +// Regression test for ODASA-4753 and ODASA-4762. + +import cpp + +from ControlFlowNode x +select x, x.getAFalseSuccessor() diff --git a/cpp/ql/test/library-tests/controlflow/primitives/ms.cpp b/cpp/ql/test/library-tests/controlflow/primitives/ms.cpp new file mode 100644 index 000000000000..9258314f64f3 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/primitives/ms.cpp @@ -0,0 +1,19 @@ +// semmle-extractor-options: --microsoft +void ms_try_except(int j) { + int x; + + __try { + x = 1; + } + __except (j) { + x = 2; + } + + __try { + x = 3; + } + __finally { + x = 4; + } +} + diff --git a/cpp/ql/test/library-tests/controlflow/primitives/test.c b/cpp/ql/test/library-tests/controlflow/primitives/test.c new file mode 100644 index 000000000000..ce9827f9ceb9 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/primitives/test.c @@ -0,0 +1,77 @@ + +void f_land(int x, int y) { + if (x && y) { + x = 3; + } +} + +void f_lor(int x, int y) { + if (x || y) { + x = 3; + } +} + +void f_if_quest2(int x, int y) { + if (x ?: y) { + x = 3; + } +} + +void f_if_quest3(int x, int y, int z) { + if (x ? y : z) { + x = 3; + } +} + +void f_agg(void) { + int i[] = {1, 2 + 3, 4 + 5, 6}; +} + +void f_do_while(int i) { + do { + i--; + } while (i); +} + +void f_computed_goto(void) { + int x; + void *ptr; + x = 10; +myLabel: + x--; + ptr = &&myLabel; + x--; + if (x) { + goto *ptr; + } +} + +int odasa4753a(void) { + if (1) { + return 1; + } + return 0; +} + +int odasa4753b(void) { + if (0) { + return 1; + } + return 0; +} + +int odasa4762a(int b) +{ + if(1 && b) { + return 1; + } + return 0; +} + +int odasa4762b(int b) +{ + if(0 || b) { + return 1; + } + return 0; +} diff --git a/cpp/ql/test/library-tests/controlflow/primitives/trueSucc.expected b/cpp/ql/test/library-tests/controlflow/primitives/trueSucc.expected new file mode 100644 index 000000000000..afb087f9f777 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/primitives/trueSucc.expected @@ -0,0 +1,17 @@ +| cpp.cpp:22:18:22:18 | call to operator!= | cpp.cpp:22:5:23:12 | declaration | +| ms.cpp:8:15:8:15 | j | ms.cpp:8:18:10:5 | { ... } | +| test.c:3:9:3:9 | x | test.c:3:14:3:14 | y | +| test.c:3:14:3:14 | y | test.c:3:17:5:5 | { ... } | +| test.c:9:9:9:9 | x | test.c:9:17:11:5 | { ... } | +| test.c:9:14:9:14 | y | test.c:9:17:11:5 | { ... } | +| test.c:15:9:15:9 | x | test.c:15:17:17:5 | { ... } | +| test.c:15:14:15:14 | y | test.c:15:17:17:5 | { ... } | +| test.c:21:9:21:9 | x | test.c:21:13:21:13 | y | +| test.c:21:13:21:13 | y | test.c:21:20:23:5 | { ... } | +| test.c:21:17:21:17 | z | test.c:21:20:23:5 | { ... } | +| test.c:33:14:33:14 | i | test.c:31:8:33:5 | { ... } | +| test.c:44:9:44:9 | x | test.c:44:12:46:5 | { ... } | +| test.c:50:9:50:9 | 1 | test.c:50:12:52:5 | { ... } | +| test.c:65:6:65:6 | 1 | test.c:65:11:65:11 | b | +| test.c:65:11:65:11 | b | test.c:65:14:67:3 | { ... } | +| test.c:73:11:73:11 | b | test.c:73:14:75:3 | { ... } | diff --git a/cpp/ql/test/library-tests/controlflow/primitives/trueSucc.ql b/cpp/ql/test/library-tests/controlflow/primitives/trueSucc.ql new file mode 100644 index 000000000000..0d230dc288a6 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/primitives/trueSucc.ql @@ -0,0 +1,6 @@ +// Regression tests for ODASA-4753 and ODASA-4762. + +import cpp + +from ControlFlowNode x +select x, x.getATrueSuccessor() diff --git a/cpp/ql/test/library-tests/controlflow_stresstest/SsaDefUsePairs.expected b/cpp/ql/test/library-tests/controlflow_stresstest/SsaDefUsePairs.expected new file mode 100644 index 000000000000..f7ab14058dc6 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow_stresstest/SsaDefUsePairs.expected @@ -0,0 +1,2501 @@ +| ssa_stress.c:3:12:3:13 | SSA definition | SSA def(ret) | ssa_stress.c:5:3:5:5 | ret | +| ssa_stress.c:5:3:5:10 | SSA definition | SSA def(ret) | ssa_stress.c:5:13:5:15 | ret | +| ssa_stress.c:5:13:5:20 | SSA definition | SSA def(ret) | ssa_stress.c:5:23:5:25 | ret | +| ssa_stress.c:5:23:5:30 | SSA definition | SSA def(ret) | ssa_stress.c:5:33:5:35 | ret | +| ssa_stress.c:5:33:5:40 | SSA definition | SSA def(ret) | ssa_stress.c:5:43:5:45 | ret | +| ssa_stress.c:5:43:5:50 | SSA definition | SSA def(ret) | ssa_stress.c:5:53:5:55 | ret | +| ssa_stress.c:5:53:5:60 | SSA definition | SSA def(ret) | ssa_stress.c:5:63:5:65 | ret | +| ssa_stress.c:5:63:5:70 | SSA definition | SSA def(ret) | ssa_stress.c:5:73:5:75 | ret | +| ssa_stress.c:5:73:5:80 | SSA definition | SSA def(ret) | ssa_stress.c:5:83:5:85 | ret | +| ssa_stress.c:5:83:5:90 | SSA definition | SSA def(ret) | ssa_stress.c:5:93:5:95 | ret | +| ssa_stress.c:5:93:5:100 | SSA definition | SSA def(ret) | ssa_stress.c:6:3:6:5 | ret | +| ssa_stress.c:6:3:6:10 | SSA definition | SSA def(ret) | ssa_stress.c:6:13:6:15 | ret | +| ssa_stress.c:6:13:6:20 | SSA definition | SSA def(ret) | ssa_stress.c:6:23:6:25 | ret | +| ssa_stress.c:6:23:6:30 | SSA definition | SSA def(ret) | ssa_stress.c:6:33:6:35 | ret | +| ssa_stress.c:6:33:6:40 | SSA definition | SSA def(ret) | ssa_stress.c:6:43:6:45 | ret | +| ssa_stress.c:6:43:6:50 | SSA definition | SSA def(ret) | ssa_stress.c:6:53:6:55 | ret | +| ssa_stress.c:6:53:6:60 | SSA definition | SSA def(ret) | ssa_stress.c:6:63:6:65 | ret | +| ssa_stress.c:6:63:6:70 | SSA definition | SSA def(ret) | ssa_stress.c:6:73:6:75 | ret | +| ssa_stress.c:6:73:6:80 | SSA definition | SSA def(ret) | ssa_stress.c:6:83:6:85 | ret | +| ssa_stress.c:6:83:6:90 | SSA definition | SSA def(ret) | ssa_stress.c:6:93:6:95 | ret | +| ssa_stress.c:6:93:6:100 | SSA definition | SSA def(ret) | ssa_stress.c:7:3:7:5 | ret | +| ssa_stress.c:7:3:7:10 | SSA definition | SSA def(ret) | ssa_stress.c:7:13:7:15 | ret | +| ssa_stress.c:7:13:7:20 | SSA definition | SSA def(ret) | ssa_stress.c:7:23:7:25 | ret | +| ssa_stress.c:7:23:7:30 | SSA definition | SSA def(ret) | ssa_stress.c:7:33:7:35 | ret | +| ssa_stress.c:7:33:7:40 | SSA definition | SSA def(ret) | ssa_stress.c:7:43:7:45 | ret | +| ssa_stress.c:7:43:7:50 | SSA definition | SSA def(ret) | ssa_stress.c:7:53:7:55 | ret | +| ssa_stress.c:7:53:7:60 | SSA definition | SSA def(ret) | ssa_stress.c:7:63:7:65 | ret | +| ssa_stress.c:7:63:7:70 | SSA definition | SSA def(ret) | ssa_stress.c:7:73:7:75 | ret | +| ssa_stress.c:7:73:7:80 | SSA definition | SSA def(ret) | ssa_stress.c:7:83:7:85 | ret | +| ssa_stress.c:7:83:7:90 | SSA definition | SSA def(ret) | ssa_stress.c:7:93:7:95 | ret | +| ssa_stress.c:7:93:7:100 | SSA definition | SSA def(ret) | ssa_stress.c:8:3:8:5 | ret | +| ssa_stress.c:8:3:8:10 | SSA definition | SSA def(ret) | ssa_stress.c:8:13:8:15 | ret | +| ssa_stress.c:8:13:8:20 | SSA definition | SSA def(ret) | ssa_stress.c:8:23:8:25 | ret | +| ssa_stress.c:8:23:8:30 | SSA definition | SSA def(ret) | ssa_stress.c:8:33:8:35 | ret | +| ssa_stress.c:8:33:8:40 | SSA definition | SSA def(ret) | ssa_stress.c:8:43:8:45 | ret | +| ssa_stress.c:8:43:8:50 | SSA definition | SSA def(ret) | ssa_stress.c:8:53:8:55 | ret | +| ssa_stress.c:8:53:8:60 | SSA definition | SSA def(ret) | ssa_stress.c:8:63:8:65 | ret | +| ssa_stress.c:8:63:8:70 | SSA definition | SSA def(ret) | ssa_stress.c:8:73:8:75 | ret | +| ssa_stress.c:8:73:8:80 | SSA definition | SSA def(ret) | ssa_stress.c:8:83:8:85 | ret | +| ssa_stress.c:8:83:8:90 | SSA definition | SSA def(ret) | ssa_stress.c:8:93:8:95 | ret | +| ssa_stress.c:8:93:8:100 | SSA definition | SSA def(ret) | ssa_stress.c:9:3:9:5 | ret | +| ssa_stress.c:9:3:9:10 | SSA definition | SSA def(ret) | ssa_stress.c:9:13:9:15 | ret | +| ssa_stress.c:9:13:9:20 | SSA definition | SSA def(ret) | ssa_stress.c:9:23:9:25 | ret | +| ssa_stress.c:9:23:9:30 | SSA definition | SSA def(ret) | ssa_stress.c:9:33:9:35 | ret | +| ssa_stress.c:9:33:9:40 | SSA definition | SSA def(ret) | ssa_stress.c:9:43:9:45 | ret | +| ssa_stress.c:9:43:9:50 | SSA definition | SSA def(ret) | ssa_stress.c:9:53:9:55 | ret | +| ssa_stress.c:9:53:9:60 | SSA definition | SSA def(ret) | ssa_stress.c:9:63:9:65 | ret | +| ssa_stress.c:9:63:9:70 | SSA definition | SSA def(ret) | ssa_stress.c:9:73:9:75 | ret | +| ssa_stress.c:9:73:9:80 | SSA definition | SSA def(ret) | ssa_stress.c:9:83:9:85 | ret | +| ssa_stress.c:9:83:9:90 | SSA definition | SSA def(ret) | ssa_stress.c:9:93:9:95 | ret | +| ssa_stress.c:9:93:9:100 | SSA definition | SSA def(ret) | ssa_stress.c:10:3:10:5 | ret | +| ssa_stress.c:10:3:10:10 | SSA definition | SSA def(ret) | ssa_stress.c:10:13:10:15 | ret | +| ssa_stress.c:10:13:10:20 | SSA definition | SSA def(ret) | ssa_stress.c:10:23:10:25 | ret | +| ssa_stress.c:10:23:10:30 | SSA definition | SSA def(ret) | ssa_stress.c:10:33:10:35 | ret | +| ssa_stress.c:10:33:10:40 | SSA definition | SSA def(ret) | ssa_stress.c:10:43:10:45 | ret | +| ssa_stress.c:10:43:10:50 | SSA definition | SSA def(ret) | ssa_stress.c:10:53:10:55 | ret | +| ssa_stress.c:10:53:10:60 | SSA definition | SSA def(ret) | ssa_stress.c:10:63:10:65 | ret | +| ssa_stress.c:10:63:10:70 | SSA definition | SSA def(ret) | ssa_stress.c:10:73:10:75 | ret | +| ssa_stress.c:10:73:10:80 | SSA definition | SSA def(ret) | ssa_stress.c:10:83:10:85 | ret | +| ssa_stress.c:10:83:10:90 | SSA definition | SSA def(ret) | ssa_stress.c:10:93:10:95 | ret | +| ssa_stress.c:10:93:10:100 | SSA definition | SSA def(ret) | ssa_stress.c:11:3:11:5 | ret | +| ssa_stress.c:11:3:11:10 | SSA definition | SSA def(ret) | ssa_stress.c:11:13:11:15 | ret | +| ssa_stress.c:11:13:11:20 | SSA definition | SSA def(ret) | ssa_stress.c:11:23:11:25 | ret | +| ssa_stress.c:11:23:11:30 | SSA definition | SSA def(ret) | ssa_stress.c:11:33:11:35 | ret | +| ssa_stress.c:11:33:11:40 | SSA definition | SSA def(ret) | ssa_stress.c:11:43:11:45 | ret | +| ssa_stress.c:11:43:11:50 | SSA definition | SSA def(ret) | ssa_stress.c:11:53:11:55 | ret | +| ssa_stress.c:11:53:11:60 | SSA definition | SSA def(ret) | ssa_stress.c:11:63:11:65 | ret | +| ssa_stress.c:11:63:11:70 | SSA definition | SSA def(ret) | ssa_stress.c:11:73:11:75 | ret | +| ssa_stress.c:11:73:11:80 | SSA definition | SSA def(ret) | ssa_stress.c:11:83:11:85 | ret | +| ssa_stress.c:11:83:11:90 | SSA definition | SSA def(ret) | ssa_stress.c:11:93:11:95 | ret | +| ssa_stress.c:11:93:11:100 | SSA definition | SSA def(ret) | ssa_stress.c:12:3:12:5 | ret | +| ssa_stress.c:12:3:12:10 | SSA definition | SSA def(ret) | ssa_stress.c:12:13:12:15 | ret | +| ssa_stress.c:12:13:12:20 | SSA definition | SSA def(ret) | ssa_stress.c:12:23:12:25 | ret | +| ssa_stress.c:12:23:12:30 | SSA definition | SSA def(ret) | ssa_stress.c:12:33:12:35 | ret | +| ssa_stress.c:12:33:12:40 | SSA definition | SSA def(ret) | ssa_stress.c:12:43:12:45 | ret | +| ssa_stress.c:12:43:12:50 | SSA definition | SSA def(ret) | ssa_stress.c:12:53:12:55 | ret | +| ssa_stress.c:12:53:12:60 | SSA definition | SSA def(ret) | ssa_stress.c:12:63:12:65 | ret | +| ssa_stress.c:12:63:12:70 | SSA definition | SSA def(ret) | ssa_stress.c:12:73:12:75 | ret | +| ssa_stress.c:12:73:12:80 | SSA definition | SSA def(ret) | ssa_stress.c:12:83:12:85 | ret | +| ssa_stress.c:12:83:12:90 | SSA definition | SSA def(ret) | ssa_stress.c:12:93:12:95 | ret | +| ssa_stress.c:12:93:12:100 | SSA definition | SSA def(ret) | ssa_stress.c:13:3:13:5 | ret | +| ssa_stress.c:13:3:13:10 | SSA definition | SSA def(ret) | ssa_stress.c:13:13:13:15 | ret | +| ssa_stress.c:13:13:13:20 | SSA definition | SSA def(ret) | ssa_stress.c:13:23:13:25 | ret | +| ssa_stress.c:13:23:13:30 | SSA definition | SSA def(ret) | ssa_stress.c:13:33:13:35 | ret | +| ssa_stress.c:13:33:13:40 | SSA definition | SSA def(ret) | ssa_stress.c:13:43:13:45 | ret | +| ssa_stress.c:13:43:13:50 | SSA definition | SSA def(ret) | ssa_stress.c:13:53:13:55 | ret | +| ssa_stress.c:13:53:13:60 | SSA definition | SSA def(ret) | ssa_stress.c:13:63:13:65 | ret | +| ssa_stress.c:13:63:13:70 | SSA definition | SSA def(ret) | ssa_stress.c:13:73:13:75 | ret | +| ssa_stress.c:13:73:13:80 | SSA definition | SSA def(ret) | ssa_stress.c:13:83:13:85 | ret | +| ssa_stress.c:13:83:13:90 | SSA definition | SSA def(ret) | ssa_stress.c:13:93:13:95 | ret | +| ssa_stress.c:13:93:13:100 | SSA definition | SSA def(ret) | ssa_stress.c:14:3:14:5 | ret | +| ssa_stress.c:14:3:14:10 | SSA definition | SSA def(ret) | ssa_stress.c:14:13:14:15 | ret | +| ssa_stress.c:14:13:14:20 | SSA definition | SSA def(ret) | ssa_stress.c:14:23:14:25 | ret | +| ssa_stress.c:14:23:14:30 | SSA definition | SSA def(ret) | ssa_stress.c:14:33:14:35 | ret | +| ssa_stress.c:14:33:14:40 | SSA definition | SSA def(ret) | ssa_stress.c:14:43:14:45 | ret | +| ssa_stress.c:14:43:14:50 | SSA definition | SSA def(ret) | ssa_stress.c:14:53:14:55 | ret | +| ssa_stress.c:14:53:14:60 | SSA definition | SSA def(ret) | ssa_stress.c:14:63:14:65 | ret | +| ssa_stress.c:14:63:14:70 | SSA definition | SSA def(ret) | ssa_stress.c:14:73:14:75 | ret | +| ssa_stress.c:14:73:14:80 | SSA definition | SSA def(ret) | ssa_stress.c:14:83:14:85 | ret | +| ssa_stress.c:14:83:14:90 | SSA definition | SSA def(ret) | ssa_stress.c:14:93:14:95 | ret | +| ssa_stress.c:14:93:14:100 | SSA definition | SSA def(ret) | ssa_stress.c:17:3:17:5 | ret | +| ssa_stress.c:17:3:17:10 | SSA definition | SSA def(ret) | ssa_stress.c:17:13:17:15 | ret | +| ssa_stress.c:17:13:17:20 | SSA definition | SSA def(ret) | ssa_stress.c:17:23:17:25 | ret | +| ssa_stress.c:17:23:17:30 | SSA definition | SSA def(ret) | ssa_stress.c:17:33:17:35 | ret | +| ssa_stress.c:17:33:17:40 | SSA definition | SSA def(ret) | ssa_stress.c:17:43:17:45 | ret | +| ssa_stress.c:17:43:17:50 | SSA definition | SSA def(ret) | ssa_stress.c:17:53:17:55 | ret | +| ssa_stress.c:17:53:17:60 | SSA definition | SSA def(ret) | ssa_stress.c:17:63:17:65 | ret | +| ssa_stress.c:17:63:17:70 | SSA definition | SSA def(ret) | ssa_stress.c:17:73:17:75 | ret | +| ssa_stress.c:17:73:17:80 | SSA definition | SSA def(ret) | ssa_stress.c:17:83:17:85 | ret | +| ssa_stress.c:17:83:17:90 | SSA definition | SSA def(ret) | ssa_stress.c:17:93:17:95 | ret | +| ssa_stress.c:17:93:17:100 | SSA definition | SSA def(ret) | ssa_stress.c:18:3:18:5 | ret | +| ssa_stress.c:18:3:18:10 | SSA definition | SSA def(ret) | ssa_stress.c:18:13:18:15 | ret | +| ssa_stress.c:18:13:18:20 | SSA definition | SSA def(ret) | ssa_stress.c:18:23:18:25 | ret | +| ssa_stress.c:18:23:18:30 | SSA definition | SSA def(ret) | ssa_stress.c:18:33:18:35 | ret | +| ssa_stress.c:18:33:18:40 | SSA definition | SSA def(ret) | ssa_stress.c:18:43:18:45 | ret | +| ssa_stress.c:18:43:18:50 | SSA definition | SSA def(ret) | ssa_stress.c:18:53:18:55 | ret | +| ssa_stress.c:18:53:18:60 | SSA definition | SSA def(ret) | ssa_stress.c:18:63:18:65 | ret | +| ssa_stress.c:18:63:18:70 | SSA definition | SSA def(ret) | ssa_stress.c:18:73:18:75 | ret | +| ssa_stress.c:18:73:18:80 | SSA definition | SSA def(ret) | ssa_stress.c:18:83:18:85 | ret | +| ssa_stress.c:18:83:18:90 | SSA definition | SSA def(ret) | ssa_stress.c:18:93:18:95 | ret | +| ssa_stress.c:18:93:18:100 | SSA definition | SSA def(ret) | ssa_stress.c:19:3:19:5 | ret | +| ssa_stress.c:19:3:19:10 | SSA definition | SSA def(ret) | ssa_stress.c:19:13:19:15 | ret | +| ssa_stress.c:19:13:19:20 | SSA definition | SSA def(ret) | ssa_stress.c:19:23:19:25 | ret | +| ssa_stress.c:19:23:19:30 | SSA definition | SSA def(ret) | ssa_stress.c:19:33:19:35 | ret | +| ssa_stress.c:19:33:19:40 | SSA definition | SSA def(ret) | ssa_stress.c:19:43:19:45 | ret | +| ssa_stress.c:19:43:19:50 | SSA definition | SSA def(ret) | ssa_stress.c:19:53:19:55 | ret | +| ssa_stress.c:19:53:19:60 | SSA definition | SSA def(ret) | ssa_stress.c:19:63:19:65 | ret | +| ssa_stress.c:19:63:19:70 | SSA definition | SSA def(ret) | ssa_stress.c:19:73:19:75 | ret | +| ssa_stress.c:19:73:19:80 | SSA definition | SSA def(ret) | ssa_stress.c:19:83:19:85 | ret | +| ssa_stress.c:19:83:19:90 | SSA definition | SSA def(ret) | ssa_stress.c:19:93:19:95 | ret | +| ssa_stress.c:19:93:19:100 | SSA definition | SSA def(ret) | ssa_stress.c:20:3:20:5 | ret | +| ssa_stress.c:20:3:20:10 | SSA definition | SSA def(ret) | ssa_stress.c:20:13:20:15 | ret | +| ssa_stress.c:20:13:20:20 | SSA definition | SSA def(ret) | ssa_stress.c:20:23:20:25 | ret | +| ssa_stress.c:20:23:20:30 | SSA definition | SSA def(ret) | ssa_stress.c:20:33:20:35 | ret | +| ssa_stress.c:20:33:20:40 | SSA definition | SSA def(ret) | ssa_stress.c:20:43:20:45 | ret | +| ssa_stress.c:20:43:20:50 | SSA definition | SSA def(ret) | ssa_stress.c:20:53:20:55 | ret | +| ssa_stress.c:20:53:20:60 | SSA definition | SSA def(ret) | ssa_stress.c:20:63:20:65 | ret | +| ssa_stress.c:20:63:20:70 | SSA definition | SSA def(ret) | ssa_stress.c:20:73:20:75 | ret | +| ssa_stress.c:20:73:20:80 | SSA definition | SSA def(ret) | ssa_stress.c:20:83:20:85 | ret | +| ssa_stress.c:20:83:20:90 | SSA definition | SSA def(ret) | ssa_stress.c:20:93:20:95 | ret | +| ssa_stress.c:20:93:20:100 | SSA definition | SSA def(ret) | ssa_stress.c:21:3:21:5 | ret | +| ssa_stress.c:21:3:21:10 | SSA definition | SSA def(ret) | ssa_stress.c:21:13:21:15 | ret | +| ssa_stress.c:21:13:21:20 | SSA definition | SSA def(ret) | ssa_stress.c:21:23:21:25 | ret | +| ssa_stress.c:21:23:21:30 | SSA definition | SSA def(ret) | ssa_stress.c:21:33:21:35 | ret | +| ssa_stress.c:21:33:21:40 | SSA definition | SSA def(ret) | ssa_stress.c:21:43:21:45 | ret | +| ssa_stress.c:21:43:21:50 | SSA definition | SSA def(ret) | ssa_stress.c:21:53:21:55 | ret | +| ssa_stress.c:21:53:21:60 | SSA definition | SSA def(ret) | ssa_stress.c:21:63:21:65 | ret | +| ssa_stress.c:21:63:21:70 | SSA definition | SSA def(ret) | ssa_stress.c:21:73:21:75 | ret | +| ssa_stress.c:21:73:21:80 | SSA definition | SSA def(ret) | ssa_stress.c:21:83:21:85 | ret | +| ssa_stress.c:21:83:21:90 | SSA definition | SSA def(ret) | ssa_stress.c:21:93:21:95 | ret | +| ssa_stress.c:21:93:21:100 | SSA definition | SSA def(ret) | ssa_stress.c:22:3:22:5 | ret | +| ssa_stress.c:22:3:22:10 | SSA definition | SSA def(ret) | ssa_stress.c:22:13:22:15 | ret | +| ssa_stress.c:22:13:22:20 | SSA definition | SSA def(ret) | ssa_stress.c:22:23:22:25 | ret | +| ssa_stress.c:22:23:22:30 | SSA definition | SSA def(ret) | ssa_stress.c:22:33:22:35 | ret | +| ssa_stress.c:22:33:22:40 | SSA definition | SSA def(ret) | ssa_stress.c:22:43:22:45 | ret | +| ssa_stress.c:22:43:22:50 | SSA definition | SSA def(ret) | ssa_stress.c:22:53:22:55 | ret | +| ssa_stress.c:22:53:22:60 | SSA definition | SSA def(ret) | ssa_stress.c:22:63:22:65 | ret | +| ssa_stress.c:22:63:22:70 | SSA definition | SSA def(ret) | ssa_stress.c:22:73:22:75 | ret | +| ssa_stress.c:22:73:22:80 | SSA definition | SSA def(ret) | ssa_stress.c:22:83:22:85 | ret | +| ssa_stress.c:22:83:22:90 | SSA definition | SSA def(ret) | ssa_stress.c:22:93:22:95 | ret | +| ssa_stress.c:22:93:22:100 | SSA definition | SSA def(ret) | ssa_stress.c:23:3:23:5 | ret | +| ssa_stress.c:23:3:23:10 | SSA definition | SSA def(ret) | ssa_stress.c:23:13:23:15 | ret | +| ssa_stress.c:23:13:23:20 | SSA definition | SSA def(ret) | ssa_stress.c:23:23:23:25 | ret | +| ssa_stress.c:23:23:23:30 | SSA definition | SSA def(ret) | ssa_stress.c:23:33:23:35 | ret | +| ssa_stress.c:23:33:23:40 | SSA definition | SSA def(ret) | ssa_stress.c:23:43:23:45 | ret | +| ssa_stress.c:23:43:23:50 | SSA definition | SSA def(ret) | ssa_stress.c:23:53:23:55 | ret | +| ssa_stress.c:23:53:23:60 | SSA definition | SSA def(ret) | ssa_stress.c:23:63:23:65 | ret | +| ssa_stress.c:23:63:23:70 | SSA definition | SSA def(ret) | ssa_stress.c:23:73:23:75 | ret | +| ssa_stress.c:23:73:23:80 | SSA definition | SSA def(ret) | ssa_stress.c:23:83:23:85 | ret | +| ssa_stress.c:23:83:23:90 | SSA definition | SSA def(ret) | ssa_stress.c:23:93:23:95 | ret | +| ssa_stress.c:23:93:23:100 | SSA definition | SSA def(ret) | ssa_stress.c:24:3:24:5 | ret | +| ssa_stress.c:24:3:24:10 | SSA definition | SSA def(ret) | ssa_stress.c:24:13:24:15 | ret | +| ssa_stress.c:24:13:24:20 | SSA definition | SSA def(ret) | ssa_stress.c:24:23:24:25 | ret | +| ssa_stress.c:24:23:24:30 | SSA definition | SSA def(ret) | ssa_stress.c:24:33:24:35 | ret | +| ssa_stress.c:24:33:24:40 | SSA definition | SSA def(ret) | ssa_stress.c:24:43:24:45 | ret | +| ssa_stress.c:24:43:24:50 | SSA definition | SSA def(ret) | ssa_stress.c:24:53:24:55 | ret | +| ssa_stress.c:24:53:24:60 | SSA definition | SSA def(ret) | ssa_stress.c:24:63:24:65 | ret | +| ssa_stress.c:24:63:24:70 | SSA definition | SSA def(ret) | ssa_stress.c:24:73:24:75 | ret | +| ssa_stress.c:24:73:24:80 | SSA definition | SSA def(ret) | ssa_stress.c:24:83:24:85 | ret | +| ssa_stress.c:24:83:24:90 | SSA definition | SSA def(ret) | ssa_stress.c:24:93:24:95 | ret | +| ssa_stress.c:24:93:24:100 | SSA definition | SSA def(ret) | ssa_stress.c:25:3:25:5 | ret | +| ssa_stress.c:25:3:25:10 | SSA definition | SSA def(ret) | ssa_stress.c:25:13:25:15 | ret | +| ssa_stress.c:25:13:25:20 | SSA definition | SSA def(ret) | ssa_stress.c:25:23:25:25 | ret | +| ssa_stress.c:25:23:25:30 | SSA definition | SSA def(ret) | ssa_stress.c:25:33:25:35 | ret | +| ssa_stress.c:25:33:25:40 | SSA definition | SSA def(ret) | ssa_stress.c:25:43:25:45 | ret | +| ssa_stress.c:25:43:25:50 | SSA definition | SSA def(ret) | ssa_stress.c:25:53:25:55 | ret | +| ssa_stress.c:25:53:25:60 | SSA definition | SSA def(ret) | ssa_stress.c:25:63:25:65 | ret | +| ssa_stress.c:25:63:25:70 | SSA definition | SSA def(ret) | ssa_stress.c:25:73:25:75 | ret | +| ssa_stress.c:25:73:25:80 | SSA definition | SSA def(ret) | ssa_stress.c:25:83:25:85 | ret | +| ssa_stress.c:25:83:25:90 | SSA definition | SSA def(ret) | ssa_stress.c:25:93:25:95 | ret | +| ssa_stress.c:25:93:25:100 | SSA definition | SSA def(ret) | ssa_stress.c:26:3:26:5 | ret | +| ssa_stress.c:26:3:26:10 | SSA definition | SSA def(ret) | ssa_stress.c:26:13:26:15 | ret | +| ssa_stress.c:26:13:26:20 | SSA definition | SSA def(ret) | ssa_stress.c:26:23:26:25 | ret | +| ssa_stress.c:26:23:26:30 | SSA definition | SSA def(ret) | ssa_stress.c:26:33:26:35 | ret | +| ssa_stress.c:26:33:26:40 | SSA definition | SSA def(ret) | ssa_stress.c:26:43:26:45 | ret | +| ssa_stress.c:26:43:26:50 | SSA definition | SSA def(ret) | ssa_stress.c:26:53:26:55 | ret | +| ssa_stress.c:26:53:26:60 | SSA definition | SSA def(ret) | ssa_stress.c:26:63:26:65 | ret | +| ssa_stress.c:26:63:26:70 | SSA definition | SSA def(ret) | ssa_stress.c:26:73:26:75 | ret | +| ssa_stress.c:26:73:26:80 | SSA definition | SSA def(ret) | ssa_stress.c:26:83:26:85 | ret | +| ssa_stress.c:26:83:26:90 | SSA definition | SSA def(ret) | ssa_stress.c:26:93:26:95 | ret | +| ssa_stress.c:26:93:26:100 | SSA definition | SSA def(ret) | ssa_stress.c:29:3:29:5 | ret | +| ssa_stress.c:29:3:29:10 | SSA definition | SSA def(ret) | ssa_stress.c:29:13:29:15 | ret | +| ssa_stress.c:29:13:29:20 | SSA definition | SSA def(ret) | ssa_stress.c:29:23:29:25 | ret | +| ssa_stress.c:29:23:29:30 | SSA definition | SSA def(ret) | ssa_stress.c:29:33:29:35 | ret | +| ssa_stress.c:29:33:29:40 | SSA definition | SSA def(ret) | ssa_stress.c:29:43:29:45 | ret | +| ssa_stress.c:29:43:29:50 | SSA definition | SSA def(ret) | ssa_stress.c:29:53:29:55 | ret | +| ssa_stress.c:29:53:29:60 | SSA definition | SSA def(ret) | ssa_stress.c:29:63:29:65 | ret | +| ssa_stress.c:29:63:29:70 | SSA definition | SSA def(ret) | ssa_stress.c:29:73:29:75 | ret | +| ssa_stress.c:29:73:29:80 | SSA definition | SSA def(ret) | ssa_stress.c:29:83:29:85 | ret | +| ssa_stress.c:29:83:29:90 | SSA definition | SSA def(ret) | ssa_stress.c:29:93:29:95 | ret | +| ssa_stress.c:29:93:29:100 | SSA definition | SSA def(ret) | ssa_stress.c:30:3:30:5 | ret | +| ssa_stress.c:30:3:30:10 | SSA definition | SSA def(ret) | ssa_stress.c:30:13:30:15 | ret | +| ssa_stress.c:30:13:30:20 | SSA definition | SSA def(ret) | ssa_stress.c:30:23:30:25 | ret | +| ssa_stress.c:30:23:30:30 | SSA definition | SSA def(ret) | ssa_stress.c:30:33:30:35 | ret | +| ssa_stress.c:30:33:30:40 | SSA definition | SSA def(ret) | ssa_stress.c:30:43:30:45 | ret | +| ssa_stress.c:30:43:30:50 | SSA definition | SSA def(ret) | ssa_stress.c:30:53:30:55 | ret | +| ssa_stress.c:30:53:30:60 | SSA definition | SSA def(ret) | ssa_stress.c:30:63:30:65 | ret | +| ssa_stress.c:30:63:30:70 | SSA definition | SSA def(ret) | ssa_stress.c:30:73:30:75 | ret | +| ssa_stress.c:30:73:30:80 | SSA definition | SSA def(ret) | ssa_stress.c:30:83:30:85 | ret | +| ssa_stress.c:30:83:30:90 | SSA definition | SSA def(ret) | ssa_stress.c:30:93:30:95 | ret | +| ssa_stress.c:30:93:30:100 | SSA definition | SSA def(ret) | ssa_stress.c:31:3:31:5 | ret | +| ssa_stress.c:31:3:31:10 | SSA definition | SSA def(ret) | ssa_stress.c:31:13:31:15 | ret | +| ssa_stress.c:31:13:31:20 | SSA definition | SSA def(ret) | ssa_stress.c:31:23:31:25 | ret | +| ssa_stress.c:31:23:31:30 | SSA definition | SSA def(ret) | ssa_stress.c:31:33:31:35 | ret | +| ssa_stress.c:31:33:31:40 | SSA definition | SSA def(ret) | ssa_stress.c:31:43:31:45 | ret | +| ssa_stress.c:31:43:31:50 | SSA definition | SSA def(ret) | ssa_stress.c:31:53:31:55 | ret | +| ssa_stress.c:31:53:31:60 | SSA definition | SSA def(ret) | ssa_stress.c:31:63:31:65 | ret | +| ssa_stress.c:31:63:31:70 | SSA definition | SSA def(ret) | ssa_stress.c:31:73:31:75 | ret | +| ssa_stress.c:31:73:31:80 | SSA definition | SSA def(ret) | ssa_stress.c:31:83:31:85 | ret | +| ssa_stress.c:31:83:31:90 | SSA definition | SSA def(ret) | ssa_stress.c:31:93:31:95 | ret | +| ssa_stress.c:31:93:31:100 | SSA definition | SSA def(ret) | ssa_stress.c:32:3:32:5 | ret | +| ssa_stress.c:32:3:32:10 | SSA definition | SSA def(ret) | ssa_stress.c:32:13:32:15 | ret | +| ssa_stress.c:32:13:32:20 | SSA definition | SSA def(ret) | ssa_stress.c:32:23:32:25 | ret | +| ssa_stress.c:32:23:32:30 | SSA definition | SSA def(ret) | ssa_stress.c:32:33:32:35 | ret | +| ssa_stress.c:32:33:32:40 | SSA definition | SSA def(ret) | ssa_stress.c:32:43:32:45 | ret | +| ssa_stress.c:32:43:32:50 | SSA definition | SSA def(ret) | ssa_stress.c:32:53:32:55 | ret | +| ssa_stress.c:32:53:32:60 | SSA definition | SSA def(ret) | ssa_stress.c:32:63:32:65 | ret | +| ssa_stress.c:32:63:32:70 | SSA definition | SSA def(ret) | ssa_stress.c:32:73:32:75 | ret | +| ssa_stress.c:32:73:32:80 | SSA definition | SSA def(ret) | ssa_stress.c:32:83:32:85 | ret | +| ssa_stress.c:32:83:32:90 | SSA definition | SSA def(ret) | ssa_stress.c:32:93:32:95 | ret | +| ssa_stress.c:32:93:32:100 | SSA definition | SSA def(ret) | ssa_stress.c:33:3:33:5 | ret | +| ssa_stress.c:33:3:33:10 | SSA definition | SSA def(ret) | ssa_stress.c:33:13:33:15 | ret | +| ssa_stress.c:33:13:33:20 | SSA definition | SSA def(ret) | ssa_stress.c:33:23:33:25 | ret | +| ssa_stress.c:33:23:33:30 | SSA definition | SSA def(ret) | ssa_stress.c:33:33:33:35 | ret | +| ssa_stress.c:33:33:33:40 | SSA definition | SSA def(ret) | ssa_stress.c:33:43:33:45 | ret | +| ssa_stress.c:33:43:33:50 | SSA definition | SSA def(ret) | ssa_stress.c:33:53:33:55 | ret | +| ssa_stress.c:33:53:33:60 | SSA definition | SSA def(ret) | ssa_stress.c:33:63:33:65 | ret | +| ssa_stress.c:33:63:33:70 | SSA definition | SSA def(ret) | ssa_stress.c:33:73:33:75 | ret | +| ssa_stress.c:33:73:33:80 | SSA definition | SSA def(ret) | ssa_stress.c:33:83:33:85 | ret | +| ssa_stress.c:33:83:33:90 | SSA definition | SSA def(ret) | ssa_stress.c:33:93:33:95 | ret | +| ssa_stress.c:33:93:33:100 | SSA definition | SSA def(ret) | ssa_stress.c:34:3:34:5 | ret | +| ssa_stress.c:34:3:34:10 | SSA definition | SSA def(ret) | ssa_stress.c:34:13:34:15 | ret | +| ssa_stress.c:34:13:34:20 | SSA definition | SSA def(ret) | ssa_stress.c:34:23:34:25 | ret | +| ssa_stress.c:34:23:34:30 | SSA definition | SSA def(ret) | ssa_stress.c:34:33:34:35 | ret | +| ssa_stress.c:34:33:34:40 | SSA definition | SSA def(ret) | ssa_stress.c:34:43:34:45 | ret | +| ssa_stress.c:34:43:34:50 | SSA definition | SSA def(ret) | ssa_stress.c:34:53:34:55 | ret | +| ssa_stress.c:34:53:34:60 | SSA definition | SSA def(ret) | ssa_stress.c:34:63:34:65 | ret | +| ssa_stress.c:34:63:34:70 | SSA definition | SSA def(ret) | ssa_stress.c:34:73:34:75 | ret | +| ssa_stress.c:34:73:34:80 | SSA definition | SSA def(ret) | ssa_stress.c:34:83:34:85 | ret | +| ssa_stress.c:34:83:34:90 | SSA definition | SSA def(ret) | ssa_stress.c:34:93:34:95 | ret | +| ssa_stress.c:34:93:34:100 | SSA definition | SSA def(ret) | ssa_stress.c:35:3:35:5 | ret | +| ssa_stress.c:35:3:35:10 | SSA definition | SSA def(ret) | ssa_stress.c:35:13:35:15 | ret | +| ssa_stress.c:35:13:35:20 | SSA definition | SSA def(ret) | ssa_stress.c:35:23:35:25 | ret | +| ssa_stress.c:35:23:35:30 | SSA definition | SSA def(ret) | ssa_stress.c:35:33:35:35 | ret | +| ssa_stress.c:35:33:35:40 | SSA definition | SSA def(ret) | ssa_stress.c:35:43:35:45 | ret | +| ssa_stress.c:35:43:35:50 | SSA definition | SSA def(ret) | ssa_stress.c:35:53:35:55 | ret | +| ssa_stress.c:35:53:35:60 | SSA definition | SSA def(ret) | ssa_stress.c:35:63:35:65 | ret | +| ssa_stress.c:35:63:35:70 | SSA definition | SSA def(ret) | ssa_stress.c:35:73:35:75 | ret | +| ssa_stress.c:35:73:35:80 | SSA definition | SSA def(ret) | ssa_stress.c:35:83:35:85 | ret | +| ssa_stress.c:35:83:35:90 | SSA definition | SSA def(ret) | ssa_stress.c:35:93:35:95 | ret | +| ssa_stress.c:35:93:35:100 | SSA definition | SSA def(ret) | ssa_stress.c:36:3:36:5 | ret | +| ssa_stress.c:36:3:36:10 | SSA definition | SSA def(ret) | ssa_stress.c:36:13:36:15 | ret | +| ssa_stress.c:36:13:36:20 | SSA definition | SSA def(ret) | ssa_stress.c:36:23:36:25 | ret | +| ssa_stress.c:36:23:36:30 | SSA definition | SSA def(ret) | ssa_stress.c:36:33:36:35 | ret | +| ssa_stress.c:36:33:36:40 | SSA definition | SSA def(ret) | ssa_stress.c:36:43:36:45 | ret | +| ssa_stress.c:36:43:36:50 | SSA definition | SSA def(ret) | ssa_stress.c:36:53:36:55 | ret | +| ssa_stress.c:36:53:36:60 | SSA definition | SSA def(ret) | ssa_stress.c:36:63:36:65 | ret | +| ssa_stress.c:36:63:36:70 | SSA definition | SSA def(ret) | ssa_stress.c:36:73:36:75 | ret | +| ssa_stress.c:36:73:36:80 | SSA definition | SSA def(ret) | ssa_stress.c:36:83:36:85 | ret | +| ssa_stress.c:36:83:36:90 | SSA definition | SSA def(ret) | ssa_stress.c:36:93:36:95 | ret | +| ssa_stress.c:36:93:36:100 | SSA definition | SSA def(ret) | ssa_stress.c:37:3:37:5 | ret | +| ssa_stress.c:37:3:37:10 | SSA definition | SSA def(ret) | ssa_stress.c:37:13:37:15 | ret | +| ssa_stress.c:37:13:37:20 | SSA definition | SSA def(ret) | ssa_stress.c:37:23:37:25 | ret | +| ssa_stress.c:37:23:37:30 | SSA definition | SSA def(ret) | ssa_stress.c:37:33:37:35 | ret | +| ssa_stress.c:37:33:37:40 | SSA definition | SSA def(ret) | ssa_stress.c:37:43:37:45 | ret | +| ssa_stress.c:37:43:37:50 | SSA definition | SSA def(ret) | ssa_stress.c:37:53:37:55 | ret | +| ssa_stress.c:37:53:37:60 | SSA definition | SSA def(ret) | ssa_stress.c:37:63:37:65 | ret | +| ssa_stress.c:37:63:37:70 | SSA definition | SSA def(ret) | ssa_stress.c:37:73:37:75 | ret | +| ssa_stress.c:37:73:37:80 | SSA definition | SSA def(ret) | ssa_stress.c:37:83:37:85 | ret | +| ssa_stress.c:37:83:37:90 | SSA definition | SSA def(ret) | ssa_stress.c:37:93:37:95 | ret | +| ssa_stress.c:37:93:37:100 | SSA definition | SSA def(ret) | ssa_stress.c:38:3:38:5 | ret | +| ssa_stress.c:38:3:38:10 | SSA definition | SSA def(ret) | ssa_stress.c:38:13:38:15 | ret | +| ssa_stress.c:38:13:38:20 | SSA definition | SSA def(ret) | ssa_stress.c:38:23:38:25 | ret | +| ssa_stress.c:38:23:38:30 | SSA definition | SSA def(ret) | ssa_stress.c:38:33:38:35 | ret | +| ssa_stress.c:38:33:38:40 | SSA definition | SSA def(ret) | ssa_stress.c:38:43:38:45 | ret | +| ssa_stress.c:38:43:38:50 | SSA definition | SSA def(ret) | ssa_stress.c:38:53:38:55 | ret | +| ssa_stress.c:38:53:38:60 | SSA definition | SSA def(ret) | ssa_stress.c:38:63:38:65 | ret | +| ssa_stress.c:38:63:38:70 | SSA definition | SSA def(ret) | ssa_stress.c:38:73:38:75 | ret | +| ssa_stress.c:38:73:38:80 | SSA definition | SSA def(ret) | ssa_stress.c:38:83:38:85 | ret | +| ssa_stress.c:38:83:38:90 | SSA definition | SSA def(ret) | ssa_stress.c:38:93:38:95 | ret | +| ssa_stress.c:38:93:38:100 | SSA definition | SSA def(ret) | ssa_stress.c:41:3:41:5 | ret | +| ssa_stress.c:41:3:41:10 | SSA definition | SSA def(ret) | ssa_stress.c:41:13:41:15 | ret | +| ssa_stress.c:41:13:41:20 | SSA definition | SSA def(ret) | ssa_stress.c:41:23:41:25 | ret | +| ssa_stress.c:41:23:41:30 | SSA definition | SSA def(ret) | ssa_stress.c:41:33:41:35 | ret | +| ssa_stress.c:41:33:41:40 | SSA definition | SSA def(ret) | ssa_stress.c:41:43:41:45 | ret | +| ssa_stress.c:41:43:41:50 | SSA definition | SSA def(ret) | ssa_stress.c:41:53:41:55 | ret | +| ssa_stress.c:41:53:41:60 | SSA definition | SSA def(ret) | ssa_stress.c:41:63:41:65 | ret | +| ssa_stress.c:41:63:41:70 | SSA definition | SSA def(ret) | ssa_stress.c:41:73:41:75 | ret | +| ssa_stress.c:41:73:41:80 | SSA definition | SSA def(ret) | ssa_stress.c:41:83:41:85 | ret | +| ssa_stress.c:41:83:41:90 | SSA definition | SSA def(ret) | ssa_stress.c:41:93:41:95 | ret | +| ssa_stress.c:41:93:41:100 | SSA definition | SSA def(ret) | ssa_stress.c:42:3:42:5 | ret | +| ssa_stress.c:42:3:42:10 | SSA definition | SSA def(ret) | ssa_stress.c:42:13:42:15 | ret | +| ssa_stress.c:42:13:42:20 | SSA definition | SSA def(ret) | ssa_stress.c:42:23:42:25 | ret | +| ssa_stress.c:42:23:42:30 | SSA definition | SSA def(ret) | ssa_stress.c:42:33:42:35 | ret | +| ssa_stress.c:42:33:42:40 | SSA definition | SSA def(ret) | ssa_stress.c:42:43:42:45 | ret | +| ssa_stress.c:42:43:42:50 | SSA definition | SSA def(ret) | ssa_stress.c:42:53:42:55 | ret | +| ssa_stress.c:42:53:42:60 | SSA definition | SSA def(ret) | ssa_stress.c:42:63:42:65 | ret | +| ssa_stress.c:42:63:42:70 | SSA definition | SSA def(ret) | ssa_stress.c:42:73:42:75 | ret | +| ssa_stress.c:42:73:42:80 | SSA definition | SSA def(ret) | ssa_stress.c:42:83:42:85 | ret | +| ssa_stress.c:42:83:42:90 | SSA definition | SSA def(ret) | ssa_stress.c:42:93:42:95 | ret | +| ssa_stress.c:42:93:42:100 | SSA definition | SSA def(ret) | ssa_stress.c:43:3:43:5 | ret | +| ssa_stress.c:43:3:43:10 | SSA definition | SSA def(ret) | ssa_stress.c:43:13:43:15 | ret | +| ssa_stress.c:43:13:43:20 | SSA definition | SSA def(ret) | ssa_stress.c:43:23:43:25 | ret | +| ssa_stress.c:43:23:43:30 | SSA definition | SSA def(ret) | ssa_stress.c:43:33:43:35 | ret | +| ssa_stress.c:43:33:43:40 | SSA definition | SSA def(ret) | ssa_stress.c:43:43:43:45 | ret | +| ssa_stress.c:43:43:43:50 | SSA definition | SSA def(ret) | ssa_stress.c:43:53:43:55 | ret | +| ssa_stress.c:43:53:43:60 | SSA definition | SSA def(ret) | ssa_stress.c:43:63:43:65 | ret | +| ssa_stress.c:43:63:43:70 | SSA definition | SSA def(ret) | ssa_stress.c:43:73:43:75 | ret | +| ssa_stress.c:43:73:43:80 | SSA definition | SSA def(ret) | ssa_stress.c:43:83:43:85 | ret | +| ssa_stress.c:43:83:43:90 | SSA definition | SSA def(ret) | ssa_stress.c:43:93:43:95 | ret | +| ssa_stress.c:43:93:43:100 | SSA definition | SSA def(ret) | ssa_stress.c:44:3:44:5 | ret | +| ssa_stress.c:44:3:44:10 | SSA definition | SSA def(ret) | ssa_stress.c:44:13:44:15 | ret | +| ssa_stress.c:44:13:44:20 | SSA definition | SSA def(ret) | ssa_stress.c:44:23:44:25 | ret | +| ssa_stress.c:44:23:44:30 | SSA definition | SSA def(ret) | ssa_stress.c:44:33:44:35 | ret | +| ssa_stress.c:44:33:44:40 | SSA definition | SSA def(ret) | ssa_stress.c:44:43:44:45 | ret | +| ssa_stress.c:44:43:44:50 | SSA definition | SSA def(ret) | ssa_stress.c:44:53:44:55 | ret | +| ssa_stress.c:44:53:44:60 | SSA definition | SSA def(ret) | ssa_stress.c:44:63:44:65 | ret | +| ssa_stress.c:44:63:44:70 | SSA definition | SSA def(ret) | ssa_stress.c:44:73:44:75 | ret | +| ssa_stress.c:44:73:44:80 | SSA definition | SSA def(ret) | ssa_stress.c:44:83:44:85 | ret | +| ssa_stress.c:44:83:44:90 | SSA definition | SSA def(ret) | ssa_stress.c:44:93:44:95 | ret | +| ssa_stress.c:44:93:44:100 | SSA definition | SSA def(ret) | ssa_stress.c:45:3:45:5 | ret | +| ssa_stress.c:45:3:45:10 | SSA definition | SSA def(ret) | ssa_stress.c:45:13:45:15 | ret | +| ssa_stress.c:45:13:45:20 | SSA definition | SSA def(ret) | ssa_stress.c:45:23:45:25 | ret | +| ssa_stress.c:45:23:45:30 | SSA definition | SSA def(ret) | ssa_stress.c:45:33:45:35 | ret | +| ssa_stress.c:45:33:45:40 | SSA definition | SSA def(ret) | ssa_stress.c:45:43:45:45 | ret | +| ssa_stress.c:45:43:45:50 | SSA definition | SSA def(ret) | ssa_stress.c:45:53:45:55 | ret | +| ssa_stress.c:45:53:45:60 | SSA definition | SSA def(ret) | ssa_stress.c:45:63:45:65 | ret | +| ssa_stress.c:45:63:45:70 | SSA definition | SSA def(ret) | ssa_stress.c:45:73:45:75 | ret | +| ssa_stress.c:45:73:45:80 | SSA definition | SSA def(ret) | ssa_stress.c:45:83:45:85 | ret | +| ssa_stress.c:45:83:45:90 | SSA definition | SSA def(ret) | ssa_stress.c:45:93:45:95 | ret | +| ssa_stress.c:45:93:45:100 | SSA definition | SSA def(ret) | ssa_stress.c:46:3:46:5 | ret | +| ssa_stress.c:46:3:46:10 | SSA definition | SSA def(ret) | ssa_stress.c:46:13:46:15 | ret | +| ssa_stress.c:46:13:46:20 | SSA definition | SSA def(ret) | ssa_stress.c:46:23:46:25 | ret | +| ssa_stress.c:46:23:46:30 | SSA definition | SSA def(ret) | ssa_stress.c:46:33:46:35 | ret | +| ssa_stress.c:46:33:46:40 | SSA definition | SSA def(ret) | ssa_stress.c:46:43:46:45 | ret | +| ssa_stress.c:46:43:46:50 | SSA definition | SSA def(ret) | ssa_stress.c:46:53:46:55 | ret | +| ssa_stress.c:46:53:46:60 | SSA definition | SSA def(ret) | ssa_stress.c:46:63:46:65 | ret | +| ssa_stress.c:46:63:46:70 | SSA definition | SSA def(ret) | ssa_stress.c:46:73:46:75 | ret | +| ssa_stress.c:46:73:46:80 | SSA definition | SSA def(ret) | ssa_stress.c:46:83:46:85 | ret | +| ssa_stress.c:46:83:46:90 | SSA definition | SSA def(ret) | ssa_stress.c:46:93:46:95 | ret | +| ssa_stress.c:46:93:46:100 | SSA definition | SSA def(ret) | ssa_stress.c:47:3:47:5 | ret | +| ssa_stress.c:47:3:47:10 | SSA definition | SSA def(ret) | ssa_stress.c:47:13:47:15 | ret | +| ssa_stress.c:47:13:47:20 | SSA definition | SSA def(ret) | ssa_stress.c:47:23:47:25 | ret | +| ssa_stress.c:47:23:47:30 | SSA definition | SSA def(ret) | ssa_stress.c:47:33:47:35 | ret | +| ssa_stress.c:47:33:47:40 | SSA definition | SSA def(ret) | ssa_stress.c:47:43:47:45 | ret | +| ssa_stress.c:47:43:47:50 | SSA definition | SSA def(ret) | ssa_stress.c:47:53:47:55 | ret | +| ssa_stress.c:47:53:47:60 | SSA definition | SSA def(ret) | ssa_stress.c:47:63:47:65 | ret | +| ssa_stress.c:47:63:47:70 | SSA definition | SSA def(ret) | ssa_stress.c:47:73:47:75 | ret | +| ssa_stress.c:47:73:47:80 | SSA definition | SSA def(ret) | ssa_stress.c:47:83:47:85 | ret | +| ssa_stress.c:47:83:47:90 | SSA definition | SSA def(ret) | ssa_stress.c:47:93:47:95 | ret | +| ssa_stress.c:47:93:47:100 | SSA definition | SSA def(ret) | ssa_stress.c:48:3:48:5 | ret | +| ssa_stress.c:48:3:48:10 | SSA definition | SSA def(ret) | ssa_stress.c:48:13:48:15 | ret | +| ssa_stress.c:48:13:48:20 | SSA definition | SSA def(ret) | ssa_stress.c:48:23:48:25 | ret | +| ssa_stress.c:48:23:48:30 | SSA definition | SSA def(ret) | ssa_stress.c:48:33:48:35 | ret | +| ssa_stress.c:48:33:48:40 | SSA definition | SSA def(ret) | ssa_stress.c:48:43:48:45 | ret | +| ssa_stress.c:48:43:48:50 | SSA definition | SSA def(ret) | ssa_stress.c:48:53:48:55 | ret | +| ssa_stress.c:48:53:48:60 | SSA definition | SSA def(ret) | ssa_stress.c:48:63:48:65 | ret | +| ssa_stress.c:48:63:48:70 | SSA definition | SSA def(ret) | ssa_stress.c:48:73:48:75 | ret | +| ssa_stress.c:48:73:48:80 | SSA definition | SSA def(ret) | ssa_stress.c:48:83:48:85 | ret | +| ssa_stress.c:48:83:48:90 | SSA definition | SSA def(ret) | ssa_stress.c:48:93:48:95 | ret | +| ssa_stress.c:48:93:48:100 | SSA definition | SSA def(ret) | ssa_stress.c:49:3:49:5 | ret | +| ssa_stress.c:49:3:49:10 | SSA definition | SSA def(ret) | ssa_stress.c:49:13:49:15 | ret | +| ssa_stress.c:49:13:49:20 | SSA definition | SSA def(ret) | ssa_stress.c:49:23:49:25 | ret | +| ssa_stress.c:49:23:49:30 | SSA definition | SSA def(ret) | ssa_stress.c:49:33:49:35 | ret | +| ssa_stress.c:49:33:49:40 | SSA definition | SSA def(ret) | ssa_stress.c:49:43:49:45 | ret | +| ssa_stress.c:49:43:49:50 | SSA definition | SSA def(ret) | ssa_stress.c:49:53:49:55 | ret | +| ssa_stress.c:49:53:49:60 | SSA definition | SSA def(ret) | ssa_stress.c:49:63:49:65 | ret | +| ssa_stress.c:49:63:49:70 | SSA definition | SSA def(ret) | ssa_stress.c:49:73:49:75 | ret | +| ssa_stress.c:49:73:49:80 | SSA definition | SSA def(ret) | ssa_stress.c:49:83:49:85 | ret | +| ssa_stress.c:49:83:49:90 | SSA definition | SSA def(ret) | ssa_stress.c:49:93:49:95 | ret | +| ssa_stress.c:49:93:49:100 | SSA definition | SSA def(ret) | ssa_stress.c:50:3:50:5 | ret | +| ssa_stress.c:50:3:50:10 | SSA definition | SSA def(ret) | ssa_stress.c:50:13:50:15 | ret | +| ssa_stress.c:50:13:50:20 | SSA definition | SSA def(ret) | ssa_stress.c:50:23:50:25 | ret | +| ssa_stress.c:50:23:50:30 | SSA definition | SSA def(ret) | ssa_stress.c:50:33:50:35 | ret | +| ssa_stress.c:50:33:50:40 | SSA definition | SSA def(ret) | ssa_stress.c:50:43:50:45 | ret | +| ssa_stress.c:50:43:50:50 | SSA definition | SSA def(ret) | ssa_stress.c:50:53:50:55 | ret | +| ssa_stress.c:50:53:50:60 | SSA definition | SSA def(ret) | ssa_stress.c:50:63:50:65 | ret | +| ssa_stress.c:50:63:50:70 | SSA definition | SSA def(ret) | ssa_stress.c:50:73:50:75 | ret | +| ssa_stress.c:50:73:50:80 | SSA definition | SSA def(ret) | ssa_stress.c:50:83:50:85 | ret | +| ssa_stress.c:50:83:50:90 | SSA definition | SSA def(ret) | ssa_stress.c:50:93:50:95 | ret | +| ssa_stress.c:50:93:50:100 | SSA definition | SSA def(ret) | ssa_stress.c:53:3:53:5 | ret | +| ssa_stress.c:53:3:53:10 | SSA definition | SSA def(ret) | ssa_stress.c:53:13:53:15 | ret | +| ssa_stress.c:53:13:53:20 | SSA definition | SSA def(ret) | ssa_stress.c:53:23:53:25 | ret | +| ssa_stress.c:53:23:53:30 | SSA definition | SSA def(ret) | ssa_stress.c:53:33:53:35 | ret | +| ssa_stress.c:53:33:53:40 | SSA definition | SSA def(ret) | ssa_stress.c:53:43:53:45 | ret | +| ssa_stress.c:53:43:53:50 | SSA definition | SSA def(ret) | ssa_stress.c:53:53:53:55 | ret | +| ssa_stress.c:53:53:53:60 | SSA definition | SSA def(ret) | ssa_stress.c:53:63:53:65 | ret | +| ssa_stress.c:53:63:53:70 | SSA definition | SSA def(ret) | ssa_stress.c:53:73:53:75 | ret | +| ssa_stress.c:53:73:53:80 | SSA definition | SSA def(ret) | ssa_stress.c:53:83:53:85 | ret | +| ssa_stress.c:53:83:53:90 | SSA definition | SSA def(ret) | ssa_stress.c:53:93:53:95 | ret | +| ssa_stress.c:53:93:53:100 | SSA definition | SSA def(ret) | ssa_stress.c:54:3:54:5 | ret | +| ssa_stress.c:54:3:54:10 | SSA definition | SSA def(ret) | ssa_stress.c:54:13:54:15 | ret | +| ssa_stress.c:54:13:54:20 | SSA definition | SSA def(ret) | ssa_stress.c:54:23:54:25 | ret | +| ssa_stress.c:54:23:54:30 | SSA definition | SSA def(ret) | ssa_stress.c:54:33:54:35 | ret | +| ssa_stress.c:54:33:54:40 | SSA definition | SSA def(ret) | ssa_stress.c:54:43:54:45 | ret | +| ssa_stress.c:54:43:54:50 | SSA definition | SSA def(ret) | ssa_stress.c:54:53:54:55 | ret | +| ssa_stress.c:54:53:54:60 | SSA definition | SSA def(ret) | ssa_stress.c:54:63:54:65 | ret | +| ssa_stress.c:54:63:54:70 | SSA definition | SSA def(ret) | ssa_stress.c:54:73:54:75 | ret | +| ssa_stress.c:54:73:54:80 | SSA definition | SSA def(ret) | ssa_stress.c:54:83:54:85 | ret | +| ssa_stress.c:54:83:54:90 | SSA definition | SSA def(ret) | ssa_stress.c:54:93:54:95 | ret | +| ssa_stress.c:54:93:54:100 | SSA definition | SSA def(ret) | ssa_stress.c:55:3:55:5 | ret | +| ssa_stress.c:55:3:55:10 | SSA definition | SSA def(ret) | ssa_stress.c:55:13:55:15 | ret | +| ssa_stress.c:55:13:55:20 | SSA definition | SSA def(ret) | ssa_stress.c:55:23:55:25 | ret | +| ssa_stress.c:55:23:55:30 | SSA definition | SSA def(ret) | ssa_stress.c:55:33:55:35 | ret | +| ssa_stress.c:55:33:55:40 | SSA definition | SSA def(ret) | ssa_stress.c:55:43:55:45 | ret | +| ssa_stress.c:55:43:55:50 | SSA definition | SSA def(ret) | ssa_stress.c:55:53:55:55 | ret | +| ssa_stress.c:55:53:55:60 | SSA definition | SSA def(ret) | ssa_stress.c:55:63:55:65 | ret | +| ssa_stress.c:55:63:55:70 | SSA definition | SSA def(ret) | ssa_stress.c:55:73:55:75 | ret | +| ssa_stress.c:55:73:55:80 | SSA definition | SSA def(ret) | ssa_stress.c:55:83:55:85 | ret | +| ssa_stress.c:55:83:55:90 | SSA definition | SSA def(ret) | ssa_stress.c:55:93:55:95 | ret | +| ssa_stress.c:55:93:55:100 | SSA definition | SSA def(ret) | ssa_stress.c:56:3:56:5 | ret | +| ssa_stress.c:56:3:56:10 | SSA definition | SSA def(ret) | ssa_stress.c:56:13:56:15 | ret | +| ssa_stress.c:56:13:56:20 | SSA definition | SSA def(ret) | ssa_stress.c:56:23:56:25 | ret | +| ssa_stress.c:56:23:56:30 | SSA definition | SSA def(ret) | ssa_stress.c:56:33:56:35 | ret | +| ssa_stress.c:56:33:56:40 | SSA definition | SSA def(ret) | ssa_stress.c:56:43:56:45 | ret | +| ssa_stress.c:56:43:56:50 | SSA definition | SSA def(ret) | ssa_stress.c:56:53:56:55 | ret | +| ssa_stress.c:56:53:56:60 | SSA definition | SSA def(ret) | ssa_stress.c:56:63:56:65 | ret | +| ssa_stress.c:56:63:56:70 | SSA definition | SSA def(ret) | ssa_stress.c:56:73:56:75 | ret | +| ssa_stress.c:56:73:56:80 | SSA definition | SSA def(ret) | ssa_stress.c:56:83:56:85 | ret | +| ssa_stress.c:56:83:56:90 | SSA definition | SSA def(ret) | ssa_stress.c:56:93:56:95 | ret | +| ssa_stress.c:56:93:56:100 | SSA definition | SSA def(ret) | ssa_stress.c:57:3:57:5 | ret | +| ssa_stress.c:57:3:57:10 | SSA definition | SSA def(ret) | ssa_stress.c:57:13:57:15 | ret | +| ssa_stress.c:57:13:57:20 | SSA definition | SSA def(ret) | ssa_stress.c:57:23:57:25 | ret | +| ssa_stress.c:57:23:57:30 | SSA definition | SSA def(ret) | ssa_stress.c:57:33:57:35 | ret | +| ssa_stress.c:57:33:57:40 | SSA definition | SSA def(ret) | ssa_stress.c:57:43:57:45 | ret | +| ssa_stress.c:57:43:57:50 | SSA definition | SSA def(ret) | ssa_stress.c:57:53:57:55 | ret | +| ssa_stress.c:57:53:57:60 | SSA definition | SSA def(ret) | ssa_stress.c:57:63:57:65 | ret | +| ssa_stress.c:57:63:57:70 | SSA definition | SSA def(ret) | ssa_stress.c:57:73:57:75 | ret | +| ssa_stress.c:57:73:57:80 | SSA definition | SSA def(ret) | ssa_stress.c:57:83:57:85 | ret | +| ssa_stress.c:57:83:57:90 | SSA definition | SSA def(ret) | ssa_stress.c:57:93:57:95 | ret | +| ssa_stress.c:57:93:57:100 | SSA definition | SSA def(ret) | ssa_stress.c:58:3:58:5 | ret | +| ssa_stress.c:58:3:58:10 | SSA definition | SSA def(ret) | ssa_stress.c:58:13:58:15 | ret | +| ssa_stress.c:58:13:58:20 | SSA definition | SSA def(ret) | ssa_stress.c:58:23:58:25 | ret | +| ssa_stress.c:58:23:58:30 | SSA definition | SSA def(ret) | ssa_stress.c:58:33:58:35 | ret | +| ssa_stress.c:58:33:58:40 | SSA definition | SSA def(ret) | ssa_stress.c:58:43:58:45 | ret | +| ssa_stress.c:58:43:58:50 | SSA definition | SSA def(ret) | ssa_stress.c:58:53:58:55 | ret | +| ssa_stress.c:58:53:58:60 | SSA definition | SSA def(ret) | ssa_stress.c:58:63:58:65 | ret | +| ssa_stress.c:58:63:58:70 | SSA definition | SSA def(ret) | ssa_stress.c:58:73:58:75 | ret | +| ssa_stress.c:58:73:58:80 | SSA definition | SSA def(ret) | ssa_stress.c:58:83:58:85 | ret | +| ssa_stress.c:58:83:58:90 | SSA definition | SSA def(ret) | ssa_stress.c:58:93:58:95 | ret | +| ssa_stress.c:58:93:58:100 | SSA definition | SSA def(ret) | ssa_stress.c:59:3:59:5 | ret | +| ssa_stress.c:59:3:59:10 | SSA definition | SSA def(ret) | ssa_stress.c:59:13:59:15 | ret | +| ssa_stress.c:59:13:59:20 | SSA definition | SSA def(ret) | ssa_stress.c:59:23:59:25 | ret | +| ssa_stress.c:59:23:59:30 | SSA definition | SSA def(ret) | ssa_stress.c:59:33:59:35 | ret | +| ssa_stress.c:59:33:59:40 | SSA definition | SSA def(ret) | ssa_stress.c:59:43:59:45 | ret | +| ssa_stress.c:59:43:59:50 | SSA definition | SSA def(ret) | ssa_stress.c:59:53:59:55 | ret | +| ssa_stress.c:59:53:59:60 | SSA definition | SSA def(ret) | ssa_stress.c:59:63:59:65 | ret | +| ssa_stress.c:59:63:59:70 | SSA definition | SSA def(ret) | ssa_stress.c:59:73:59:75 | ret | +| ssa_stress.c:59:73:59:80 | SSA definition | SSA def(ret) | ssa_stress.c:59:83:59:85 | ret | +| ssa_stress.c:59:83:59:90 | SSA definition | SSA def(ret) | ssa_stress.c:59:93:59:95 | ret | +| ssa_stress.c:59:93:59:100 | SSA definition | SSA def(ret) | ssa_stress.c:60:3:60:5 | ret | +| ssa_stress.c:60:3:60:10 | SSA definition | SSA def(ret) | ssa_stress.c:60:13:60:15 | ret | +| ssa_stress.c:60:13:60:20 | SSA definition | SSA def(ret) | ssa_stress.c:60:23:60:25 | ret | +| ssa_stress.c:60:23:60:30 | SSA definition | SSA def(ret) | ssa_stress.c:60:33:60:35 | ret | +| ssa_stress.c:60:33:60:40 | SSA definition | SSA def(ret) | ssa_stress.c:60:43:60:45 | ret | +| ssa_stress.c:60:43:60:50 | SSA definition | SSA def(ret) | ssa_stress.c:60:53:60:55 | ret | +| ssa_stress.c:60:53:60:60 | SSA definition | SSA def(ret) | ssa_stress.c:60:63:60:65 | ret | +| ssa_stress.c:60:63:60:70 | SSA definition | SSA def(ret) | ssa_stress.c:60:73:60:75 | ret | +| ssa_stress.c:60:73:60:80 | SSA definition | SSA def(ret) | ssa_stress.c:60:83:60:85 | ret | +| ssa_stress.c:60:83:60:90 | SSA definition | SSA def(ret) | ssa_stress.c:60:93:60:95 | ret | +| ssa_stress.c:60:93:60:100 | SSA definition | SSA def(ret) | ssa_stress.c:61:3:61:5 | ret | +| ssa_stress.c:61:3:61:10 | SSA definition | SSA def(ret) | ssa_stress.c:61:13:61:15 | ret | +| ssa_stress.c:61:13:61:20 | SSA definition | SSA def(ret) | ssa_stress.c:61:23:61:25 | ret | +| ssa_stress.c:61:23:61:30 | SSA definition | SSA def(ret) | ssa_stress.c:61:33:61:35 | ret | +| ssa_stress.c:61:33:61:40 | SSA definition | SSA def(ret) | ssa_stress.c:61:43:61:45 | ret | +| ssa_stress.c:61:43:61:50 | SSA definition | SSA def(ret) | ssa_stress.c:61:53:61:55 | ret | +| ssa_stress.c:61:53:61:60 | SSA definition | SSA def(ret) | ssa_stress.c:61:63:61:65 | ret | +| ssa_stress.c:61:63:61:70 | SSA definition | SSA def(ret) | ssa_stress.c:61:73:61:75 | ret | +| ssa_stress.c:61:73:61:80 | SSA definition | SSA def(ret) | ssa_stress.c:61:83:61:85 | ret | +| ssa_stress.c:61:83:61:90 | SSA definition | SSA def(ret) | ssa_stress.c:61:93:61:95 | ret | +| ssa_stress.c:61:93:61:100 | SSA definition | SSA def(ret) | ssa_stress.c:62:3:62:5 | ret | +| ssa_stress.c:62:3:62:10 | SSA definition | SSA def(ret) | ssa_stress.c:62:13:62:15 | ret | +| ssa_stress.c:62:13:62:20 | SSA definition | SSA def(ret) | ssa_stress.c:62:23:62:25 | ret | +| ssa_stress.c:62:23:62:30 | SSA definition | SSA def(ret) | ssa_stress.c:62:33:62:35 | ret | +| ssa_stress.c:62:33:62:40 | SSA definition | SSA def(ret) | ssa_stress.c:62:43:62:45 | ret | +| ssa_stress.c:62:43:62:50 | SSA definition | SSA def(ret) | ssa_stress.c:62:53:62:55 | ret | +| ssa_stress.c:62:53:62:60 | SSA definition | SSA def(ret) | ssa_stress.c:62:63:62:65 | ret | +| ssa_stress.c:62:63:62:70 | SSA definition | SSA def(ret) | ssa_stress.c:62:73:62:75 | ret | +| ssa_stress.c:62:73:62:80 | SSA definition | SSA def(ret) | ssa_stress.c:62:83:62:85 | ret | +| ssa_stress.c:62:83:62:90 | SSA definition | SSA def(ret) | ssa_stress.c:62:93:62:95 | ret | +| ssa_stress.c:62:93:62:100 | SSA definition | SSA def(ret) | ssa_stress.c:65:3:65:5 | ret | +| ssa_stress.c:65:3:65:10 | SSA definition | SSA def(ret) | ssa_stress.c:65:13:65:15 | ret | +| ssa_stress.c:65:13:65:20 | SSA definition | SSA def(ret) | ssa_stress.c:65:23:65:25 | ret | +| ssa_stress.c:65:23:65:30 | SSA definition | SSA def(ret) | ssa_stress.c:65:33:65:35 | ret | +| ssa_stress.c:65:33:65:40 | SSA definition | SSA def(ret) | ssa_stress.c:65:43:65:45 | ret | +| ssa_stress.c:65:43:65:50 | SSA definition | SSA def(ret) | ssa_stress.c:65:53:65:55 | ret | +| ssa_stress.c:65:53:65:60 | SSA definition | SSA def(ret) | ssa_stress.c:65:63:65:65 | ret | +| ssa_stress.c:65:63:65:70 | SSA definition | SSA def(ret) | ssa_stress.c:65:73:65:75 | ret | +| ssa_stress.c:65:73:65:80 | SSA definition | SSA def(ret) | ssa_stress.c:65:83:65:85 | ret | +| ssa_stress.c:65:83:65:90 | SSA definition | SSA def(ret) | ssa_stress.c:65:93:65:95 | ret | +| ssa_stress.c:65:93:65:100 | SSA definition | SSA def(ret) | ssa_stress.c:66:3:66:5 | ret | +| ssa_stress.c:66:3:66:10 | SSA definition | SSA def(ret) | ssa_stress.c:66:13:66:15 | ret | +| ssa_stress.c:66:13:66:20 | SSA definition | SSA def(ret) | ssa_stress.c:66:23:66:25 | ret | +| ssa_stress.c:66:23:66:30 | SSA definition | SSA def(ret) | ssa_stress.c:66:33:66:35 | ret | +| ssa_stress.c:66:33:66:40 | SSA definition | SSA def(ret) | ssa_stress.c:66:43:66:45 | ret | +| ssa_stress.c:66:43:66:50 | SSA definition | SSA def(ret) | ssa_stress.c:66:53:66:55 | ret | +| ssa_stress.c:66:53:66:60 | SSA definition | SSA def(ret) | ssa_stress.c:66:63:66:65 | ret | +| ssa_stress.c:66:63:66:70 | SSA definition | SSA def(ret) | ssa_stress.c:66:73:66:75 | ret | +| ssa_stress.c:66:73:66:80 | SSA definition | SSA def(ret) | ssa_stress.c:66:83:66:85 | ret | +| ssa_stress.c:66:83:66:90 | SSA definition | SSA def(ret) | ssa_stress.c:66:93:66:95 | ret | +| ssa_stress.c:66:93:66:100 | SSA definition | SSA def(ret) | ssa_stress.c:67:3:67:5 | ret | +| ssa_stress.c:67:3:67:10 | SSA definition | SSA def(ret) | ssa_stress.c:67:13:67:15 | ret | +| ssa_stress.c:67:13:67:20 | SSA definition | SSA def(ret) | ssa_stress.c:67:23:67:25 | ret | +| ssa_stress.c:67:23:67:30 | SSA definition | SSA def(ret) | ssa_stress.c:67:33:67:35 | ret | +| ssa_stress.c:67:33:67:40 | SSA definition | SSA def(ret) | ssa_stress.c:67:43:67:45 | ret | +| ssa_stress.c:67:43:67:50 | SSA definition | SSA def(ret) | ssa_stress.c:67:53:67:55 | ret | +| ssa_stress.c:67:53:67:60 | SSA definition | SSA def(ret) | ssa_stress.c:67:63:67:65 | ret | +| ssa_stress.c:67:63:67:70 | SSA definition | SSA def(ret) | ssa_stress.c:67:73:67:75 | ret | +| ssa_stress.c:67:73:67:80 | SSA definition | SSA def(ret) | ssa_stress.c:67:83:67:85 | ret | +| ssa_stress.c:67:83:67:90 | SSA definition | SSA def(ret) | ssa_stress.c:67:93:67:95 | ret | +| ssa_stress.c:67:93:67:100 | SSA definition | SSA def(ret) | ssa_stress.c:68:3:68:5 | ret | +| ssa_stress.c:68:3:68:10 | SSA definition | SSA def(ret) | ssa_stress.c:68:13:68:15 | ret | +| ssa_stress.c:68:13:68:20 | SSA definition | SSA def(ret) | ssa_stress.c:68:23:68:25 | ret | +| ssa_stress.c:68:23:68:30 | SSA definition | SSA def(ret) | ssa_stress.c:68:33:68:35 | ret | +| ssa_stress.c:68:33:68:40 | SSA definition | SSA def(ret) | ssa_stress.c:68:43:68:45 | ret | +| ssa_stress.c:68:43:68:50 | SSA definition | SSA def(ret) | ssa_stress.c:68:53:68:55 | ret | +| ssa_stress.c:68:53:68:60 | SSA definition | SSA def(ret) | ssa_stress.c:68:63:68:65 | ret | +| ssa_stress.c:68:63:68:70 | SSA definition | SSA def(ret) | ssa_stress.c:68:73:68:75 | ret | +| ssa_stress.c:68:73:68:80 | SSA definition | SSA def(ret) | ssa_stress.c:68:83:68:85 | ret | +| ssa_stress.c:68:83:68:90 | SSA definition | SSA def(ret) | ssa_stress.c:68:93:68:95 | ret | +| ssa_stress.c:68:93:68:100 | SSA definition | SSA def(ret) | ssa_stress.c:69:3:69:5 | ret | +| ssa_stress.c:69:3:69:10 | SSA definition | SSA def(ret) | ssa_stress.c:69:13:69:15 | ret | +| ssa_stress.c:69:13:69:20 | SSA definition | SSA def(ret) | ssa_stress.c:69:23:69:25 | ret | +| ssa_stress.c:69:23:69:30 | SSA definition | SSA def(ret) | ssa_stress.c:69:33:69:35 | ret | +| ssa_stress.c:69:33:69:40 | SSA definition | SSA def(ret) | ssa_stress.c:69:43:69:45 | ret | +| ssa_stress.c:69:43:69:50 | SSA definition | SSA def(ret) | ssa_stress.c:69:53:69:55 | ret | +| ssa_stress.c:69:53:69:60 | SSA definition | SSA def(ret) | ssa_stress.c:69:63:69:65 | ret | +| ssa_stress.c:69:63:69:70 | SSA definition | SSA def(ret) | ssa_stress.c:69:73:69:75 | ret | +| ssa_stress.c:69:73:69:80 | SSA definition | SSA def(ret) | ssa_stress.c:69:83:69:85 | ret | +| ssa_stress.c:69:83:69:90 | SSA definition | SSA def(ret) | ssa_stress.c:69:93:69:95 | ret | +| ssa_stress.c:69:93:69:100 | SSA definition | SSA def(ret) | ssa_stress.c:70:3:70:5 | ret | +| ssa_stress.c:70:3:70:10 | SSA definition | SSA def(ret) | ssa_stress.c:70:13:70:15 | ret | +| ssa_stress.c:70:13:70:20 | SSA definition | SSA def(ret) | ssa_stress.c:70:23:70:25 | ret | +| ssa_stress.c:70:23:70:30 | SSA definition | SSA def(ret) | ssa_stress.c:70:33:70:35 | ret | +| ssa_stress.c:70:33:70:40 | SSA definition | SSA def(ret) | ssa_stress.c:70:43:70:45 | ret | +| ssa_stress.c:70:43:70:50 | SSA definition | SSA def(ret) | ssa_stress.c:70:53:70:55 | ret | +| ssa_stress.c:70:53:70:60 | SSA definition | SSA def(ret) | ssa_stress.c:70:63:70:65 | ret | +| ssa_stress.c:70:63:70:70 | SSA definition | SSA def(ret) | ssa_stress.c:70:73:70:75 | ret | +| ssa_stress.c:70:73:70:80 | SSA definition | SSA def(ret) | ssa_stress.c:70:83:70:85 | ret | +| ssa_stress.c:70:83:70:90 | SSA definition | SSA def(ret) | ssa_stress.c:70:93:70:95 | ret | +| ssa_stress.c:70:93:70:100 | SSA definition | SSA def(ret) | ssa_stress.c:71:3:71:5 | ret | +| ssa_stress.c:71:3:71:10 | SSA definition | SSA def(ret) | ssa_stress.c:71:13:71:15 | ret | +| ssa_stress.c:71:13:71:20 | SSA definition | SSA def(ret) | ssa_stress.c:71:23:71:25 | ret | +| ssa_stress.c:71:23:71:30 | SSA definition | SSA def(ret) | ssa_stress.c:71:33:71:35 | ret | +| ssa_stress.c:71:33:71:40 | SSA definition | SSA def(ret) | ssa_stress.c:71:43:71:45 | ret | +| ssa_stress.c:71:43:71:50 | SSA definition | SSA def(ret) | ssa_stress.c:71:53:71:55 | ret | +| ssa_stress.c:71:53:71:60 | SSA definition | SSA def(ret) | ssa_stress.c:71:63:71:65 | ret | +| ssa_stress.c:71:63:71:70 | SSA definition | SSA def(ret) | ssa_stress.c:71:73:71:75 | ret | +| ssa_stress.c:71:73:71:80 | SSA definition | SSA def(ret) | ssa_stress.c:71:83:71:85 | ret | +| ssa_stress.c:71:83:71:90 | SSA definition | SSA def(ret) | ssa_stress.c:71:93:71:95 | ret | +| ssa_stress.c:71:93:71:100 | SSA definition | SSA def(ret) | ssa_stress.c:72:3:72:5 | ret | +| ssa_stress.c:72:3:72:10 | SSA definition | SSA def(ret) | ssa_stress.c:72:13:72:15 | ret | +| ssa_stress.c:72:13:72:20 | SSA definition | SSA def(ret) | ssa_stress.c:72:23:72:25 | ret | +| ssa_stress.c:72:23:72:30 | SSA definition | SSA def(ret) | ssa_stress.c:72:33:72:35 | ret | +| ssa_stress.c:72:33:72:40 | SSA definition | SSA def(ret) | ssa_stress.c:72:43:72:45 | ret | +| ssa_stress.c:72:43:72:50 | SSA definition | SSA def(ret) | ssa_stress.c:72:53:72:55 | ret | +| ssa_stress.c:72:53:72:60 | SSA definition | SSA def(ret) | ssa_stress.c:72:63:72:65 | ret | +| ssa_stress.c:72:63:72:70 | SSA definition | SSA def(ret) | ssa_stress.c:72:73:72:75 | ret | +| ssa_stress.c:72:73:72:80 | SSA definition | SSA def(ret) | ssa_stress.c:72:83:72:85 | ret | +| ssa_stress.c:72:83:72:90 | SSA definition | SSA def(ret) | ssa_stress.c:72:93:72:95 | ret | +| ssa_stress.c:72:93:72:100 | SSA definition | SSA def(ret) | ssa_stress.c:73:3:73:5 | ret | +| ssa_stress.c:73:3:73:10 | SSA definition | SSA def(ret) | ssa_stress.c:73:13:73:15 | ret | +| ssa_stress.c:73:13:73:20 | SSA definition | SSA def(ret) | ssa_stress.c:73:23:73:25 | ret | +| ssa_stress.c:73:23:73:30 | SSA definition | SSA def(ret) | ssa_stress.c:73:33:73:35 | ret | +| ssa_stress.c:73:33:73:40 | SSA definition | SSA def(ret) | ssa_stress.c:73:43:73:45 | ret | +| ssa_stress.c:73:43:73:50 | SSA definition | SSA def(ret) | ssa_stress.c:73:53:73:55 | ret | +| ssa_stress.c:73:53:73:60 | SSA definition | SSA def(ret) | ssa_stress.c:73:63:73:65 | ret | +| ssa_stress.c:73:63:73:70 | SSA definition | SSA def(ret) | ssa_stress.c:73:73:73:75 | ret | +| ssa_stress.c:73:73:73:80 | SSA definition | SSA def(ret) | ssa_stress.c:73:83:73:85 | ret | +| ssa_stress.c:73:83:73:90 | SSA definition | SSA def(ret) | ssa_stress.c:73:93:73:95 | ret | +| ssa_stress.c:73:93:73:100 | SSA definition | SSA def(ret) | ssa_stress.c:74:3:74:5 | ret | +| ssa_stress.c:74:3:74:10 | SSA definition | SSA def(ret) | ssa_stress.c:74:13:74:15 | ret | +| ssa_stress.c:74:13:74:20 | SSA definition | SSA def(ret) | ssa_stress.c:74:23:74:25 | ret | +| ssa_stress.c:74:23:74:30 | SSA definition | SSA def(ret) | ssa_stress.c:74:33:74:35 | ret | +| ssa_stress.c:74:33:74:40 | SSA definition | SSA def(ret) | ssa_stress.c:74:43:74:45 | ret | +| ssa_stress.c:74:43:74:50 | SSA definition | SSA def(ret) | ssa_stress.c:74:53:74:55 | ret | +| ssa_stress.c:74:53:74:60 | SSA definition | SSA def(ret) | ssa_stress.c:74:63:74:65 | ret | +| ssa_stress.c:74:63:74:70 | SSA definition | SSA def(ret) | ssa_stress.c:74:73:74:75 | ret | +| ssa_stress.c:74:73:74:80 | SSA definition | SSA def(ret) | ssa_stress.c:74:83:74:85 | ret | +| ssa_stress.c:74:83:74:90 | SSA definition | SSA def(ret) | ssa_stress.c:74:93:74:95 | ret | +| ssa_stress.c:74:93:74:100 | SSA definition | SSA def(ret) | ssa_stress.c:77:3:77:5 | ret | +| ssa_stress.c:77:3:77:10 | SSA definition | SSA def(ret) | ssa_stress.c:77:13:77:15 | ret | +| ssa_stress.c:77:13:77:20 | SSA definition | SSA def(ret) | ssa_stress.c:77:23:77:25 | ret | +| ssa_stress.c:77:23:77:30 | SSA definition | SSA def(ret) | ssa_stress.c:77:33:77:35 | ret | +| ssa_stress.c:77:33:77:40 | SSA definition | SSA def(ret) | ssa_stress.c:77:43:77:45 | ret | +| ssa_stress.c:77:43:77:50 | SSA definition | SSA def(ret) | ssa_stress.c:77:53:77:55 | ret | +| ssa_stress.c:77:53:77:60 | SSA definition | SSA def(ret) | ssa_stress.c:77:63:77:65 | ret | +| ssa_stress.c:77:63:77:70 | SSA definition | SSA def(ret) | ssa_stress.c:77:73:77:75 | ret | +| ssa_stress.c:77:73:77:80 | SSA definition | SSA def(ret) | ssa_stress.c:77:83:77:85 | ret | +| ssa_stress.c:77:83:77:90 | SSA definition | SSA def(ret) | ssa_stress.c:77:93:77:95 | ret | +| ssa_stress.c:77:93:77:100 | SSA definition | SSA def(ret) | ssa_stress.c:78:3:78:5 | ret | +| ssa_stress.c:78:3:78:10 | SSA definition | SSA def(ret) | ssa_stress.c:78:13:78:15 | ret | +| ssa_stress.c:78:13:78:20 | SSA definition | SSA def(ret) | ssa_stress.c:78:23:78:25 | ret | +| ssa_stress.c:78:23:78:30 | SSA definition | SSA def(ret) | ssa_stress.c:78:33:78:35 | ret | +| ssa_stress.c:78:33:78:40 | SSA definition | SSA def(ret) | ssa_stress.c:78:43:78:45 | ret | +| ssa_stress.c:78:43:78:50 | SSA definition | SSA def(ret) | ssa_stress.c:78:53:78:55 | ret | +| ssa_stress.c:78:53:78:60 | SSA definition | SSA def(ret) | ssa_stress.c:78:63:78:65 | ret | +| ssa_stress.c:78:63:78:70 | SSA definition | SSA def(ret) | ssa_stress.c:78:73:78:75 | ret | +| ssa_stress.c:78:73:78:80 | SSA definition | SSA def(ret) | ssa_stress.c:78:83:78:85 | ret | +| ssa_stress.c:78:83:78:90 | SSA definition | SSA def(ret) | ssa_stress.c:78:93:78:95 | ret | +| ssa_stress.c:78:93:78:100 | SSA definition | SSA def(ret) | ssa_stress.c:79:3:79:5 | ret | +| ssa_stress.c:79:3:79:10 | SSA definition | SSA def(ret) | ssa_stress.c:79:13:79:15 | ret | +| ssa_stress.c:79:13:79:20 | SSA definition | SSA def(ret) | ssa_stress.c:79:23:79:25 | ret | +| ssa_stress.c:79:23:79:30 | SSA definition | SSA def(ret) | ssa_stress.c:79:33:79:35 | ret | +| ssa_stress.c:79:33:79:40 | SSA definition | SSA def(ret) | ssa_stress.c:79:43:79:45 | ret | +| ssa_stress.c:79:43:79:50 | SSA definition | SSA def(ret) | ssa_stress.c:79:53:79:55 | ret | +| ssa_stress.c:79:53:79:60 | SSA definition | SSA def(ret) | ssa_stress.c:79:63:79:65 | ret | +| ssa_stress.c:79:63:79:70 | SSA definition | SSA def(ret) | ssa_stress.c:79:73:79:75 | ret | +| ssa_stress.c:79:73:79:80 | SSA definition | SSA def(ret) | ssa_stress.c:79:83:79:85 | ret | +| ssa_stress.c:79:83:79:90 | SSA definition | SSA def(ret) | ssa_stress.c:79:93:79:95 | ret | +| ssa_stress.c:79:93:79:100 | SSA definition | SSA def(ret) | ssa_stress.c:80:3:80:5 | ret | +| ssa_stress.c:80:3:80:10 | SSA definition | SSA def(ret) | ssa_stress.c:80:13:80:15 | ret | +| ssa_stress.c:80:13:80:20 | SSA definition | SSA def(ret) | ssa_stress.c:80:23:80:25 | ret | +| ssa_stress.c:80:23:80:30 | SSA definition | SSA def(ret) | ssa_stress.c:80:33:80:35 | ret | +| ssa_stress.c:80:33:80:40 | SSA definition | SSA def(ret) | ssa_stress.c:80:43:80:45 | ret | +| ssa_stress.c:80:43:80:50 | SSA definition | SSA def(ret) | ssa_stress.c:80:53:80:55 | ret | +| ssa_stress.c:80:53:80:60 | SSA definition | SSA def(ret) | ssa_stress.c:80:63:80:65 | ret | +| ssa_stress.c:80:63:80:70 | SSA definition | SSA def(ret) | ssa_stress.c:80:73:80:75 | ret | +| ssa_stress.c:80:73:80:80 | SSA definition | SSA def(ret) | ssa_stress.c:80:83:80:85 | ret | +| ssa_stress.c:80:83:80:90 | SSA definition | SSA def(ret) | ssa_stress.c:80:93:80:95 | ret | +| ssa_stress.c:80:93:80:100 | SSA definition | SSA def(ret) | ssa_stress.c:81:3:81:5 | ret | +| ssa_stress.c:81:3:81:10 | SSA definition | SSA def(ret) | ssa_stress.c:81:13:81:15 | ret | +| ssa_stress.c:81:13:81:20 | SSA definition | SSA def(ret) | ssa_stress.c:81:23:81:25 | ret | +| ssa_stress.c:81:23:81:30 | SSA definition | SSA def(ret) | ssa_stress.c:81:33:81:35 | ret | +| ssa_stress.c:81:33:81:40 | SSA definition | SSA def(ret) | ssa_stress.c:81:43:81:45 | ret | +| ssa_stress.c:81:43:81:50 | SSA definition | SSA def(ret) | ssa_stress.c:81:53:81:55 | ret | +| ssa_stress.c:81:53:81:60 | SSA definition | SSA def(ret) | ssa_stress.c:81:63:81:65 | ret | +| ssa_stress.c:81:63:81:70 | SSA definition | SSA def(ret) | ssa_stress.c:81:73:81:75 | ret | +| ssa_stress.c:81:73:81:80 | SSA definition | SSA def(ret) | ssa_stress.c:81:83:81:85 | ret | +| ssa_stress.c:81:83:81:90 | SSA definition | SSA def(ret) | ssa_stress.c:81:93:81:95 | ret | +| ssa_stress.c:81:93:81:100 | SSA definition | SSA def(ret) | ssa_stress.c:82:3:82:5 | ret | +| ssa_stress.c:82:3:82:10 | SSA definition | SSA def(ret) | ssa_stress.c:82:13:82:15 | ret | +| ssa_stress.c:82:13:82:20 | SSA definition | SSA def(ret) | ssa_stress.c:82:23:82:25 | ret | +| ssa_stress.c:82:23:82:30 | SSA definition | SSA def(ret) | ssa_stress.c:82:33:82:35 | ret | +| ssa_stress.c:82:33:82:40 | SSA definition | SSA def(ret) | ssa_stress.c:82:43:82:45 | ret | +| ssa_stress.c:82:43:82:50 | SSA definition | SSA def(ret) | ssa_stress.c:82:53:82:55 | ret | +| ssa_stress.c:82:53:82:60 | SSA definition | SSA def(ret) | ssa_stress.c:82:63:82:65 | ret | +| ssa_stress.c:82:63:82:70 | SSA definition | SSA def(ret) | ssa_stress.c:82:73:82:75 | ret | +| ssa_stress.c:82:73:82:80 | SSA definition | SSA def(ret) | ssa_stress.c:82:83:82:85 | ret | +| ssa_stress.c:82:83:82:90 | SSA definition | SSA def(ret) | ssa_stress.c:82:93:82:95 | ret | +| ssa_stress.c:82:93:82:100 | SSA definition | SSA def(ret) | ssa_stress.c:83:3:83:5 | ret | +| ssa_stress.c:83:3:83:10 | SSA definition | SSA def(ret) | ssa_stress.c:83:13:83:15 | ret | +| ssa_stress.c:83:13:83:20 | SSA definition | SSA def(ret) | ssa_stress.c:83:23:83:25 | ret | +| ssa_stress.c:83:23:83:30 | SSA definition | SSA def(ret) | ssa_stress.c:83:33:83:35 | ret | +| ssa_stress.c:83:33:83:40 | SSA definition | SSA def(ret) | ssa_stress.c:83:43:83:45 | ret | +| ssa_stress.c:83:43:83:50 | SSA definition | SSA def(ret) | ssa_stress.c:83:53:83:55 | ret | +| ssa_stress.c:83:53:83:60 | SSA definition | SSA def(ret) | ssa_stress.c:83:63:83:65 | ret | +| ssa_stress.c:83:63:83:70 | SSA definition | SSA def(ret) | ssa_stress.c:83:73:83:75 | ret | +| ssa_stress.c:83:73:83:80 | SSA definition | SSA def(ret) | ssa_stress.c:83:83:83:85 | ret | +| ssa_stress.c:83:83:83:90 | SSA definition | SSA def(ret) | ssa_stress.c:83:93:83:95 | ret | +| ssa_stress.c:83:93:83:100 | SSA definition | SSA def(ret) | ssa_stress.c:84:3:84:5 | ret | +| ssa_stress.c:84:3:84:10 | SSA definition | SSA def(ret) | ssa_stress.c:84:13:84:15 | ret | +| ssa_stress.c:84:13:84:20 | SSA definition | SSA def(ret) | ssa_stress.c:84:23:84:25 | ret | +| ssa_stress.c:84:23:84:30 | SSA definition | SSA def(ret) | ssa_stress.c:84:33:84:35 | ret | +| ssa_stress.c:84:33:84:40 | SSA definition | SSA def(ret) | ssa_stress.c:84:43:84:45 | ret | +| ssa_stress.c:84:43:84:50 | SSA definition | SSA def(ret) | ssa_stress.c:84:53:84:55 | ret | +| ssa_stress.c:84:53:84:60 | SSA definition | SSA def(ret) | ssa_stress.c:84:63:84:65 | ret | +| ssa_stress.c:84:63:84:70 | SSA definition | SSA def(ret) | ssa_stress.c:84:73:84:75 | ret | +| ssa_stress.c:84:73:84:80 | SSA definition | SSA def(ret) | ssa_stress.c:84:83:84:85 | ret | +| ssa_stress.c:84:83:84:90 | SSA definition | SSA def(ret) | ssa_stress.c:84:93:84:95 | ret | +| ssa_stress.c:84:93:84:100 | SSA definition | SSA def(ret) | ssa_stress.c:85:3:85:5 | ret | +| ssa_stress.c:85:3:85:10 | SSA definition | SSA def(ret) | ssa_stress.c:85:13:85:15 | ret | +| ssa_stress.c:85:13:85:20 | SSA definition | SSA def(ret) | ssa_stress.c:85:23:85:25 | ret | +| ssa_stress.c:85:23:85:30 | SSA definition | SSA def(ret) | ssa_stress.c:85:33:85:35 | ret | +| ssa_stress.c:85:33:85:40 | SSA definition | SSA def(ret) | ssa_stress.c:85:43:85:45 | ret | +| ssa_stress.c:85:43:85:50 | SSA definition | SSA def(ret) | ssa_stress.c:85:53:85:55 | ret | +| ssa_stress.c:85:53:85:60 | SSA definition | SSA def(ret) | ssa_stress.c:85:63:85:65 | ret | +| ssa_stress.c:85:63:85:70 | SSA definition | SSA def(ret) | ssa_stress.c:85:73:85:75 | ret | +| ssa_stress.c:85:73:85:80 | SSA definition | SSA def(ret) | ssa_stress.c:85:83:85:85 | ret | +| ssa_stress.c:85:83:85:90 | SSA definition | SSA def(ret) | ssa_stress.c:85:93:85:95 | ret | +| ssa_stress.c:85:93:85:100 | SSA definition | SSA def(ret) | ssa_stress.c:86:3:86:5 | ret | +| ssa_stress.c:86:3:86:10 | SSA definition | SSA def(ret) | ssa_stress.c:86:13:86:15 | ret | +| ssa_stress.c:86:13:86:20 | SSA definition | SSA def(ret) | ssa_stress.c:86:23:86:25 | ret | +| ssa_stress.c:86:23:86:30 | SSA definition | SSA def(ret) | ssa_stress.c:86:33:86:35 | ret | +| ssa_stress.c:86:33:86:40 | SSA definition | SSA def(ret) | ssa_stress.c:86:43:86:45 | ret | +| ssa_stress.c:86:43:86:50 | SSA definition | SSA def(ret) | ssa_stress.c:86:53:86:55 | ret | +| ssa_stress.c:86:53:86:60 | SSA definition | SSA def(ret) | ssa_stress.c:86:63:86:65 | ret | +| ssa_stress.c:86:63:86:70 | SSA definition | SSA def(ret) | ssa_stress.c:86:73:86:75 | ret | +| ssa_stress.c:86:73:86:80 | SSA definition | SSA def(ret) | ssa_stress.c:86:83:86:85 | ret | +| ssa_stress.c:86:83:86:90 | SSA definition | SSA def(ret) | ssa_stress.c:86:93:86:95 | ret | +| ssa_stress.c:86:93:86:100 | SSA definition | SSA def(ret) | ssa_stress.c:89:3:89:5 | ret | +| ssa_stress.c:89:3:89:10 | SSA definition | SSA def(ret) | ssa_stress.c:89:13:89:15 | ret | +| ssa_stress.c:89:13:89:20 | SSA definition | SSA def(ret) | ssa_stress.c:89:23:89:25 | ret | +| ssa_stress.c:89:23:89:30 | SSA definition | SSA def(ret) | ssa_stress.c:89:33:89:35 | ret | +| ssa_stress.c:89:33:89:40 | SSA definition | SSA def(ret) | ssa_stress.c:89:43:89:45 | ret | +| ssa_stress.c:89:43:89:50 | SSA definition | SSA def(ret) | ssa_stress.c:89:53:89:55 | ret | +| ssa_stress.c:89:53:89:60 | SSA definition | SSA def(ret) | ssa_stress.c:89:63:89:65 | ret | +| ssa_stress.c:89:63:89:70 | SSA definition | SSA def(ret) | ssa_stress.c:89:73:89:75 | ret | +| ssa_stress.c:89:73:89:80 | SSA definition | SSA def(ret) | ssa_stress.c:89:83:89:85 | ret | +| ssa_stress.c:89:83:89:90 | SSA definition | SSA def(ret) | ssa_stress.c:89:93:89:95 | ret | +| ssa_stress.c:89:93:89:100 | SSA definition | SSA def(ret) | ssa_stress.c:90:3:90:5 | ret | +| ssa_stress.c:90:3:90:10 | SSA definition | SSA def(ret) | ssa_stress.c:90:13:90:15 | ret | +| ssa_stress.c:90:13:90:20 | SSA definition | SSA def(ret) | ssa_stress.c:90:23:90:25 | ret | +| ssa_stress.c:90:23:90:30 | SSA definition | SSA def(ret) | ssa_stress.c:90:33:90:35 | ret | +| ssa_stress.c:90:33:90:40 | SSA definition | SSA def(ret) | ssa_stress.c:90:43:90:45 | ret | +| ssa_stress.c:90:43:90:50 | SSA definition | SSA def(ret) | ssa_stress.c:90:53:90:55 | ret | +| ssa_stress.c:90:53:90:60 | SSA definition | SSA def(ret) | ssa_stress.c:90:63:90:65 | ret | +| ssa_stress.c:90:63:90:70 | SSA definition | SSA def(ret) | ssa_stress.c:90:73:90:75 | ret | +| ssa_stress.c:90:73:90:80 | SSA definition | SSA def(ret) | ssa_stress.c:90:83:90:85 | ret | +| ssa_stress.c:90:83:90:90 | SSA definition | SSA def(ret) | ssa_stress.c:90:93:90:95 | ret | +| ssa_stress.c:90:93:90:100 | SSA definition | SSA def(ret) | ssa_stress.c:91:3:91:5 | ret | +| ssa_stress.c:91:3:91:10 | SSA definition | SSA def(ret) | ssa_stress.c:91:13:91:15 | ret | +| ssa_stress.c:91:13:91:20 | SSA definition | SSA def(ret) | ssa_stress.c:91:23:91:25 | ret | +| ssa_stress.c:91:23:91:30 | SSA definition | SSA def(ret) | ssa_stress.c:91:33:91:35 | ret | +| ssa_stress.c:91:33:91:40 | SSA definition | SSA def(ret) | ssa_stress.c:91:43:91:45 | ret | +| ssa_stress.c:91:43:91:50 | SSA definition | SSA def(ret) | ssa_stress.c:91:53:91:55 | ret | +| ssa_stress.c:91:53:91:60 | SSA definition | SSA def(ret) | ssa_stress.c:91:63:91:65 | ret | +| ssa_stress.c:91:63:91:70 | SSA definition | SSA def(ret) | ssa_stress.c:91:73:91:75 | ret | +| ssa_stress.c:91:73:91:80 | SSA definition | SSA def(ret) | ssa_stress.c:91:83:91:85 | ret | +| ssa_stress.c:91:83:91:90 | SSA definition | SSA def(ret) | ssa_stress.c:91:93:91:95 | ret | +| ssa_stress.c:91:93:91:100 | SSA definition | SSA def(ret) | ssa_stress.c:92:3:92:5 | ret | +| ssa_stress.c:92:3:92:10 | SSA definition | SSA def(ret) | ssa_stress.c:92:13:92:15 | ret | +| ssa_stress.c:92:13:92:20 | SSA definition | SSA def(ret) | ssa_stress.c:92:23:92:25 | ret | +| ssa_stress.c:92:23:92:30 | SSA definition | SSA def(ret) | ssa_stress.c:92:33:92:35 | ret | +| ssa_stress.c:92:33:92:40 | SSA definition | SSA def(ret) | ssa_stress.c:92:43:92:45 | ret | +| ssa_stress.c:92:43:92:50 | SSA definition | SSA def(ret) | ssa_stress.c:92:53:92:55 | ret | +| ssa_stress.c:92:53:92:60 | SSA definition | SSA def(ret) | ssa_stress.c:92:63:92:65 | ret | +| ssa_stress.c:92:63:92:70 | SSA definition | SSA def(ret) | ssa_stress.c:92:73:92:75 | ret | +| ssa_stress.c:92:73:92:80 | SSA definition | SSA def(ret) | ssa_stress.c:92:83:92:85 | ret | +| ssa_stress.c:92:83:92:90 | SSA definition | SSA def(ret) | ssa_stress.c:92:93:92:95 | ret | +| ssa_stress.c:92:93:92:100 | SSA definition | SSA def(ret) | ssa_stress.c:93:3:93:5 | ret | +| ssa_stress.c:93:3:93:10 | SSA definition | SSA def(ret) | ssa_stress.c:93:13:93:15 | ret | +| ssa_stress.c:93:13:93:20 | SSA definition | SSA def(ret) | ssa_stress.c:93:23:93:25 | ret | +| ssa_stress.c:93:23:93:30 | SSA definition | SSA def(ret) | ssa_stress.c:93:33:93:35 | ret | +| ssa_stress.c:93:33:93:40 | SSA definition | SSA def(ret) | ssa_stress.c:93:43:93:45 | ret | +| ssa_stress.c:93:43:93:50 | SSA definition | SSA def(ret) | ssa_stress.c:93:53:93:55 | ret | +| ssa_stress.c:93:53:93:60 | SSA definition | SSA def(ret) | ssa_stress.c:93:63:93:65 | ret | +| ssa_stress.c:93:63:93:70 | SSA definition | SSA def(ret) | ssa_stress.c:93:73:93:75 | ret | +| ssa_stress.c:93:73:93:80 | SSA definition | SSA def(ret) | ssa_stress.c:93:83:93:85 | ret | +| ssa_stress.c:93:83:93:90 | SSA definition | SSA def(ret) | ssa_stress.c:93:93:93:95 | ret | +| ssa_stress.c:93:93:93:100 | SSA definition | SSA def(ret) | ssa_stress.c:94:3:94:5 | ret | +| ssa_stress.c:94:3:94:10 | SSA definition | SSA def(ret) | ssa_stress.c:94:13:94:15 | ret | +| ssa_stress.c:94:13:94:20 | SSA definition | SSA def(ret) | ssa_stress.c:94:23:94:25 | ret | +| ssa_stress.c:94:23:94:30 | SSA definition | SSA def(ret) | ssa_stress.c:94:33:94:35 | ret | +| ssa_stress.c:94:33:94:40 | SSA definition | SSA def(ret) | ssa_stress.c:94:43:94:45 | ret | +| ssa_stress.c:94:43:94:50 | SSA definition | SSA def(ret) | ssa_stress.c:94:53:94:55 | ret | +| ssa_stress.c:94:53:94:60 | SSA definition | SSA def(ret) | ssa_stress.c:94:63:94:65 | ret | +| ssa_stress.c:94:63:94:70 | SSA definition | SSA def(ret) | ssa_stress.c:94:73:94:75 | ret | +| ssa_stress.c:94:73:94:80 | SSA definition | SSA def(ret) | ssa_stress.c:94:83:94:85 | ret | +| ssa_stress.c:94:83:94:90 | SSA definition | SSA def(ret) | ssa_stress.c:94:93:94:95 | ret | +| ssa_stress.c:94:93:94:100 | SSA definition | SSA def(ret) | ssa_stress.c:95:3:95:5 | ret | +| ssa_stress.c:95:3:95:10 | SSA definition | SSA def(ret) | ssa_stress.c:95:13:95:15 | ret | +| ssa_stress.c:95:13:95:20 | SSA definition | SSA def(ret) | ssa_stress.c:95:23:95:25 | ret | +| ssa_stress.c:95:23:95:30 | SSA definition | SSA def(ret) | ssa_stress.c:95:33:95:35 | ret | +| ssa_stress.c:95:33:95:40 | SSA definition | SSA def(ret) | ssa_stress.c:95:43:95:45 | ret | +| ssa_stress.c:95:43:95:50 | SSA definition | SSA def(ret) | ssa_stress.c:95:53:95:55 | ret | +| ssa_stress.c:95:53:95:60 | SSA definition | SSA def(ret) | ssa_stress.c:95:63:95:65 | ret | +| ssa_stress.c:95:63:95:70 | SSA definition | SSA def(ret) | ssa_stress.c:95:73:95:75 | ret | +| ssa_stress.c:95:73:95:80 | SSA definition | SSA def(ret) | ssa_stress.c:95:83:95:85 | ret | +| ssa_stress.c:95:83:95:90 | SSA definition | SSA def(ret) | ssa_stress.c:95:93:95:95 | ret | +| ssa_stress.c:95:93:95:100 | SSA definition | SSA def(ret) | ssa_stress.c:96:3:96:5 | ret | +| ssa_stress.c:96:3:96:10 | SSA definition | SSA def(ret) | ssa_stress.c:96:13:96:15 | ret | +| ssa_stress.c:96:13:96:20 | SSA definition | SSA def(ret) | ssa_stress.c:96:23:96:25 | ret | +| ssa_stress.c:96:23:96:30 | SSA definition | SSA def(ret) | ssa_stress.c:96:33:96:35 | ret | +| ssa_stress.c:96:33:96:40 | SSA definition | SSA def(ret) | ssa_stress.c:96:43:96:45 | ret | +| ssa_stress.c:96:43:96:50 | SSA definition | SSA def(ret) | ssa_stress.c:96:53:96:55 | ret | +| ssa_stress.c:96:53:96:60 | SSA definition | SSA def(ret) | ssa_stress.c:96:63:96:65 | ret | +| ssa_stress.c:96:63:96:70 | SSA definition | SSA def(ret) | ssa_stress.c:96:73:96:75 | ret | +| ssa_stress.c:96:73:96:80 | SSA definition | SSA def(ret) | ssa_stress.c:96:83:96:85 | ret | +| ssa_stress.c:96:83:96:90 | SSA definition | SSA def(ret) | ssa_stress.c:96:93:96:95 | ret | +| ssa_stress.c:96:93:96:100 | SSA definition | SSA def(ret) | ssa_stress.c:97:3:97:5 | ret | +| ssa_stress.c:97:3:97:10 | SSA definition | SSA def(ret) | ssa_stress.c:97:13:97:15 | ret | +| ssa_stress.c:97:13:97:20 | SSA definition | SSA def(ret) | ssa_stress.c:97:23:97:25 | ret | +| ssa_stress.c:97:23:97:30 | SSA definition | SSA def(ret) | ssa_stress.c:97:33:97:35 | ret | +| ssa_stress.c:97:33:97:40 | SSA definition | SSA def(ret) | ssa_stress.c:97:43:97:45 | ret | +| ssa_stress.c:97:43:97:50 | SSA definition | SSA def(ret) | ssa_stress.c:97:53:97:55 | ret | +| ssa_stress.c:97:53:97:60 | SSA definition | SSA def(ret) | ssa_stress.c:97:63:97:65 | ret | +| ssa_stress.c:97:63:97:70 | SSA definition | SSA def(ret) | ssa_stress.c:97:73:97:75 | ret | +| ssa_stress.c:97:73:97:80 | SSA definition | SSA def(ret) | ssa_stress.c:97:83:97:85 | ret | +| ssa_stress.c:97:83:97:90 | SSA definition | SSA def(ret) | ssa_stress.c:97:93:97:95 | ret | +| ssa_stress.c:97:93:97:100 | SSA definition | SSA def(ret) | ssa_stress.c:98:3:98:5 | ret | +| ssa_stress.c:98:3:98:10 | SSA definition | SSA def(ret) | ssa_stress.c:98:13:98:15 | ret | +| ssa_stress.c:98:13:98:20 | SSA definition | SSA def(ret) | ssa_stress.c:98:23:98:25 | ret | +| ssa_stress.c:98:23:98:30 | SSA definition | SSA def(ret) | ssa_stress.c:98:33:98:35 | ret | +| ssa_stress.c:98:33:98:40 | SSA definition | SSA def(ret) | ssa_stress.c:98:43:98:45 | ret | +| ssa_stress.c:98:43:98:50 | SSA definition | SSA def(ret) | ssa_stress.c:98:53:98:55 | ret | +| ssa_stress.c:98:53:98:60 | SSA definition | SSA def(ret) | ssa_stress.c:98:63:98:65 | ret | +| ssa_stress.c:98:63:98:70 | SSA definition | SSA def(ret) | ssa_stress.c:98:73:98:75 | ret | +| ssa_stress.c:98:73:98:80 | SSA definition | SSA def(ret) | ssa_stress.c:98:83:98:85 | ret | +| ssa_stress.c:98:83:98:90 | SSA definition | SSA def(ret) | ssa_stress.c:98:93:98:95 | ret | +| ssa_stress.c:98:93:98:100 | SSA definition | SSA def(ret) | ssa_stress.c:101:3:101:5 | ret | +| ssa_stress.c:101:3:101:10 | SSA definition | SSA def(ret) | ssa_stress.c:101:13:101:15 | ret | +| ssa_stress.c:101:13:101:20 | SSA definition | SSA def(ret) | ssa_stress.c:101:23:101:25 | ret | +| ssa_stress.c:101:23:101:30 | SSA definition | SSA def(ret) | ssa_stress.c:101:33:101:35 | ret | +| ssa_stress.c:101:33:101:40 | SSA definition | SSA def(ret) | ssa_stress.c:101:43:101:45 | ret | +| ssa_stress.c:101:43:101:50 | SSA definition | SSA def(ret) | ssa_stress.c:101:53:101:55 | ret | +| ssa_stress.c:101:53:101:60 | SSA definition | SSA def(ret) | ssa_stress.c:101:63:101:65 | ret | +| ssa_stress.c:101:63:101:70 | SSA definition | SSA def(ret) | ssa_stress.c:101:73:101:75 | ret | +| ssa_stress.c:101:73:101:80 | SSA definition | SSA def(ret) | ssa_stress.c:101:83:101:85 | ret | +| ssa_stress.c:101:83:101:90 | SSA definition | SSA def(ret) | ssa_stress.c:101:93:101:95 | ret | +| ssa_stress.c:101:93:101:100 | SSA definition | SSA def(ret) | ssa_stress.c:102:3:102:5 | ret | +| ssa_stress.c:102:3:102:10 | SSA definition | SSA def(ret) | ssa_stress.c:102:13:102:15 | ret | +| ssa_stress.c:102:13:102:20 | SSA definition | SSA def(ret) | ssa_stress.c:102:23:102:25 | ret | +| ssa_stress.c:102:23:102:30 | SSA definition | SSA def(ret) | ssa_stress.c:102:33:102:35 | ret | +| ssa_stress.c:102:33:102:40 | SSA definition | SSA def(ret) | ssa_stress.c:102:43:102:45 | ret | +| ssa_stress.c:102:43:102:50 | SSA definition | SSA def(ret) | ssa_stress.c:102:53:102:55 | ret | +| ssa_stress.c:102:53:102:60 | SSA definition | SSA def(ret) | ssa_stress.c:102:63:102:65 | ret | +| ssa_stress.c:102:63:102:70 | SSA definition | SSA def(ret) | ssa_stress.c:102:73:102:75 | ret | +| ssa_stress.c:102:73:102:80 | SSA definition | SSA def(ret) | ssa_stress.c:102:83:102:85 | ret | +| ssa_stress.c:102:83:102:90 | SSA definition | SSA def(ret) | ssa_stress.c:102:93:102:95 | ret | +| ssa_stress.c:102:93:102:100 | SSA definition | SSA def(ret) | ssa_stress.c:103:3:103:5 | ret | +| ssa_stress.c:103:3:103:10 | SSA definition | SSA def(ret) | ssa_stress.c:103:13:103:15 | ret | +| ssa_stress.c:103:13:103:20 | SSA definition | SSA def(ret) | ssa_stress.c:103:23:103:25 | ret | +| ssa_stress.c:103:23:103:30 | SSA definition | SSA def(ret) | ssa_stress.c:103:33:103:35 | ret | +| ssa_stress.c:103:33:103:40 | SSA definition | SSA def(ret) | ssa_stress.c:103:43:103:45 | ret | +| ssa_stress.c:103:43:103:50 | SSA definition | SSA def(ret) | ssa_stress.c:103:53:103:55 | ret | +| ssa_stress.c:103:53:103:60 | SSA definition | SSA def(ret) | ssa_stress.c:103:63:103:65 | ret | +| ssa_stress.c:103:63:103:70 | SSA definition | SSA def(ret) | ssa_stress.c:103:73:103:75 | ret | +| ssa_stress.c:103:73:103:80 | SSA definition | SSA def(ret) | ssa_stress.c:103:83:103:85 | ret | +| ssa_stress.c:103:83:103:90 | SSA definition | SSA def(ret) | ssa_stress.c:103:93:103:95 | ret | +| ssa_stress.c:103:93:103:100 | SSA definition | SSA def(ret) | ssa_stress.c:104:3:104:5 | ret | +| ssa_stress.c:104:3:104:10 | SSA definition | SSA def(ret) | ssa_stress.c:104:13:104:15 | ret | +| ssa_stress.c:104:13:104:20 | SSA definition | SSA def(ret) | ssa_stress.c:104:23:104:25 | ret | +| ssa_stress.c:104:23:104:30 | SSA definition | SSA def(ret) | ssa_stress.c:104:33:104:35 | ret | +| ssa_stress.c:104:33:104:40 | SSA definition | SSA def(ret) | ssa_stress.c:104:43:104:45 | ret | +| ssa_stress.c:104:43:104:50 | SSA definition | SSA def(ret) | ssa_stress.c:104:53:104:55 | ret | +| ssa_stress.c:104:53:104:60 | SSA definition | SSA def(ret) | ssa_stress.c:104:63:104:65 | ret | +| ssa_stress.c:104:63:104:70 | SSA definition | SSA def(ret) | ssa_stress.c:104:73:104:75 | ret | +| ssa_stress.c:104:73:104:80 | SSA definition | SSA def(ret) | ssa_stress.c:104:83:104:85 | ret | +| ssa_stress.c:104:83:104:90 | SSA definition | SSA def(ret) | ssa_stress.c:104:93:104:95 | ret | +| ssa_stress.c:104:93:104:100 | SSA definition | SSA def(ret) | ssa_stress.c:105:3:105:5 | ret | +| ssa_stress.c:105:3:105:10 | SSA definition | SSA def(ret) | ssa_stress.c:105:13:105:15 | ret | +| ssa_stress.c:105:13:105:20 | SSA definition | SSA def(ret) | ssa_stress.c:105:23:105:25 | ret | +| ssa_stress.c:105:23:105:30 | SSA definition | SSA def(ret) | ssa_stress.c:105:33:105:35 | ret | +| ssa_stress.c:105:33:105:40 | SSA definition | SSA def(ret) | ssa_stress.c:105:43:105:45 | ret | +| ssa_stress.c:105:43:105:50 | SSA definition | SSA def(ret) | ssa_stress.c:105:53:105:55 | ret | +| ssa_stress.c:105:53:105:60 | SSA definition | SSA def(ret) | ssa_stress.c:105:63:105:65 | ret | +| ssa_stress.c:105:63:105:70 | SSA definition | SSA def(ret) | ssa_stress.c:105:73:105:75 | ret | +| ssa_stress.c:105:73:105:80 | SSA definition | SSA def(ret) | ssa_stress.c:105:83:105:85 | ret | +| ssa_stress.c:105:83:105:90 | SSA definition | SSA def(ret) | ssa_stress.c:105:93:105:95 | ret | +| ssa_stress.c:105:93:105:100 | SSA definition | SSA def(ret) | ssa_stress.c:106:3:106:5 | ret | +| ssa_stress.c:106:3:106:10 | SSA definition | SSA def(ret) | ssa_stress.c:106:13:106:15 | ret | +| ssa_stress.c:106:13:106:20 | SSA definition | SSA def(ret) | ssa_stress.c:106:23:106:25 | ret | +| ssa_stress.c:106:23:106:30 | SSA definition | SSA def(ret) | ssa_stress.c:106:33:106:35 | ret | +| ssa_stress.c:106:33:106:40 | SSA definition | SSA def(ret) | ssa_stress.c:106:43:106:45 | ret | +| ssa_stress.c:106:43:106:50 | SSA definition | SSA def(ret) | ssa_stress.c:106:53:106:55 | ret | +| ssa_stress.c:106:53:106:60 | SSA definition | SSA def(ret) | ssa_stress.c:106:63:106:65 | ret | +| ssa_stress.c:106:63:106:70 | SSA definition | SSA def(ret) | ssa_stress.c:106:73:106:75 | ret | +| ssa_stress.c:106:73:106:80 | SSA definition | SSA def(ret) | ssa_stress.c:106:83:106:85 | ret | +| ssa_stress.c:106:83:106:90 | SSA definition | SSA def(ret) | ssa_stress.c:106:93:106:95 | ret | +| ssa_stress.c:106:93:106:100 | SSA definition | SSA def(ret) | ssa_stress.c:107:3:107:5 | ret | +| ssa_stress.c:107:3:107:10 | SSA definition | SSA def(ret) | ssa_stress.c:107:13:107:15 | ret | +| ssa_stress.c:107:13:107:20 | SSA definition | SSA def(ret) | ssa_stress.c:107:23:107:25 | ret | +| ssa_stress.c:107:23:107:30 | SSA definition | SSA def(ret) | ssa_stress.c:107:33:107:35 | ret | +| ssa_stress.c:107:33:107:40 | SSA definition | SSA def(ret) | ssa_stress.c:107:43:107:45 | ret | +| ssa_stress.c:107:43:107:50 | SSA definition | SSA def(ret) | ssa_stress.c:107:53:107:55 | ret | +| ssa_stress.c:107:53:107:60 | SSA definition | SSA def(ret) | ssa_stress.c:107:63:107:65 | ret | +| ssa_stress.c:107:63:107:70 | SSA definition | SSA def(ret) | ssa_stress.c:107:73:107:75 | ret | +| ssa_stress.c:107:73:107:80 | SSA definition | SSA def(ret) | ssa_stress.c:107:83:107:85 | ret | +| ssa_stress.c:107:83:107:90 | SSA definition | SSA def(ret) | ssa_stress.c:107:93:107:95 | ret | +| ssa_stress.c:107:93:107:100 | SSA definition | SSA def(ret) | ssa_stress.c:108:3:108:5 | ret | +| ssa_stress.c:108:3:108:10 | SSA definition | SSA def(ret) | ssa_stress.c:108:13:108:15 | ret | +| ssa_stress.c:108:13:108:20 | SSA definition | SSA def(ret) | ssa_stress.c:108:23:108:25 | ret | +| ssa_stress.c:108:23:108:30 | SSA definition | SSA def(ret) | ssa_stress.c:108:33:108:35 | ret | +| ssa_stress.c:108:33:108:40 | SSA definition | SSA def(ret) | ssa_stress.c:108:43:108:45 | ret | +| ssa_stress.c:108:43:108:50 | SSA definition | SSA def(ret) | ssa_stress.c:108:53:108:55 | ret | +| ssa_stress.c:108:53:108:60 | SSA definition | SSA def(ret) | ssa_stress.c:108:63:108:65 | ret | +| ssa_stress.c:108:63:108:70 | SSA definition | SSA def(ret) | ssa_stress.c:108:73:108:75 | ret | +| ssa_stress.c:108:73:108:80 | SSA definition | SSA def(ret) | ssa_stress.c:108:83:108:85 | ret | +| ssa_stress.c:108:83:108:90 | SSA definition | SSA def(ret) | ssa_stress.c:108:93:108:95 | ret | +| ssa_stress.c:108:93:108:100 | SSA definition | SSA def(ret) | ssa_stress.c:109:3:109:5 | ret | +| ssa_stress.c:109:3:109:10 | SSA definition | SSA def(ret) | ssa_stress.c:109:13:109:15 | ret | +| ssa_stress.c:109:13:109:20 | SSA definition | SSA def(ret) | ssa_stress.c:109:23:109:25 | ret | +| ssa_stress.c:109:23:109:30 | SSA definition | SSA def(ret) | ssa_stress.c:109:33:109:35 | ret | +| ssa_stress.c:109:33:109:40 | SSA definition | SSA def(ret) | ssa_stress.c:109:43:109:45 | ret | +| ssa_stress.c:109:43:109:50 | SSA definition | SSA def(ret) | ssa_stress.c:109:53:109:55 | ret | +| ssa_stress.c:109:53:109:60 | SSA definition | SSA def(ret) | ssa_stress.c:109:63:109:65 | ret | +| ssa_stress.c:109:63:109:70 | SSA definition | SSA def(ret) | ssa_stress.c:109:73:109:75 | ret | +| ssa_stress.c:109:73:109:80 | SSA definition | SSA def(ret) | ssa_stress.c:109:83:109:85 | ret | +| ssa_stress.c:109:83:109:90 | SSA definition | SSA def(ret) | ssa_stress.c:109:93:109:95 | ret | +| ssa_stress.c:109:93:109:100 | SSA definition | SSA def(ret) | ssa_stress.c:110:3:110:5 | ret | +| ssa_stress.c:110:3:110:10 | SSA definition | SSA def(ret) | ssa_stress.c:110:13:110:15 | ret | +| ssa_stress.c:110:13:110:20 | SSA definition | SSA def(ret) | ssa_stress.c:110:23:110:25 | ret | +| ssa_stress.c:110:23:110:30 | SSA definition | SSA def(ret) | ssa_stress.c:110:33:110:35 | ret | +| ssa_stress.c:110:33:110:40 | SSA definition | SSA def(ret) | ssa_stress.c:110:43:110:45 | ret | +| ssa_stress.c:110:43:110:50 | SSA definition | SSA def(ret) | ssa_stress.c:110:53:110:55 | ret | +| ssa_stress.c:110:53:110:60 | SSA definition | SSA def(ret) | ssa_stress.c:110:63:110:65 | ret | +| ssa_stress.c:110:63:110:70 | SSA definition | SSA def(ret) | ssa_stress.c:110:73:110:75 | ret | +| ssa_stress.c:110:73:110:80 | SSA definition | SSA def(ret) | ssa_stress.c:110:83:110:85 | ret | +| ssa_stress.c:110:83:110:90 | SSA definition | SSA def(ret) | ssa_stress.c:110:93:110:95 | ret | +| ssa_stress.c:110:93:110:100 | SSA definition | SSA def(ret) | ssa_stress.c:113:3:113:5 | ret | +| ssa_stress.c:113:3:113:10 | SSA definition | SSA def(ret) | ssa_stress.c:113:13:113:15 | ret | +| ssa_stress.c:113:13:113:20 | SSA definition | SSA def(ret) | ssa_stress.c:113:23:113:25 | ret | +| ssa_stress.c:113:23:113:30 | SSA definition | SSA def(ret) | ssa_stress.c:113:33:113:35 | ret | +| ssa_stress.c:113:33:113:40 | SSA definition | SSA def(ret) | ssa_stress.c:113:43:113:45 | ret | +| ssa_stress.c:113:43:113:50 | SSA definition | SSA def(ret) | ssa_stress.c:113:53:113:55 | ret | +| ssa_stress.c:113:53:113:60 | SSA definition | SSA def(ret) | ssa_stress.c:113:63:113:65 | ret | +| ssa_stress.c:113:63:113:70 | SSA definition | SSA def(ret) | ssa_stress.c:113:73:113:75 | ret | +| ssa_stress.c:113:73:113:80 | SSA definition | SSA def(ret) | ssa_stress.c:113:83:113:85 | ret | +| ssa_stress.c:113:83:113:90 | SSA definition | SSA def(ret) | ssa_stress.c:113:93:113:95 | ret | +| ssa_stress.c:113:93:113:100 | SSA definition | SSA def(ret) | ssa_stress.c:114:3:114:5 | ret | +| ssa_stress.c:114:3:114:10 | SSA definition | SSA def(ret) | ssa_stress.c:114:13:114:15 | ret | +| ssa_stress.c:114:13:114:20 | SSA definition | SSA def(ret) | ssa_stress.c:114:23:114:25 | ret | +| ssa_stress.c:114:23:114:30 | SSA definition | SSA def(ret) | ssa_stress.c:114:33:114:35 | ret | +| ssa_stress.c:114:33:114:40 | SSA definition | SSA def(ret) | ssa_stress.c:114:43:114:45 | ret | +| ssa_stress.c:114:43:114:50 | SSA definition | SSA def(ret) | ssa_stress.c:114:53:114:55 | ret | +| ssa_stress.c:114:53:114:60 | SSA definition | SSA def(ret) | ssa_stress.c:114:63:114:65 | ret | +| ssa_stress.c:114:63:114:70 | SSA definition | SSA def(ret) | ssa_stress.c:114:73:114:75 | ret | +| ssa_stress.c:114:73:114:80 | SSA definition | SSA def(ret) | ssa_stress.c:114:83:114:85 | ret | +| ssa_stress.c:114:83:114:90 | SSA definition | SSA def(ret) | ssa_stress.c:114:93:114:95 | ret | +| ssa_stress.c:114:93:114:100 | SSA definition | SSA def(ret) | ssa_stress.c:115:3:115:5 | ret | +| ssa_stress.c:115:3:115:10 | SSA definition | SSA def(ret) | ssa_stress.c:115:13:115:15 | ret | +| ssa_stress.c:115:13:115:20 | SSA definition | SSA def(ret) | ssa_stress.c:115:23:115:25 | ret | +| ssa_stress.c:115:23:115:30 | SSA definition | SSA def(ret) | ssa_stress.c:115:33:115:35 | ret | +| ssa_stress.c:115:33:115:40 | SSA definition | SSA def(ret) | ssa_stress.c:115:43:115:45 | ret | +| ssa_stress.c:115:43:115:50 | SSA definition | SSA def(ret) | ssa_stress.c:115:53:115:55 | ret | +| ssa_stress.c:115:53:115:60 | SSA definition | SSA def(ret) | ssa_stress.c:115:63:115:65 | ret | +| ssa_stress.c:115:63:115:70 | SSA definition | SSA def(ret) | ssa_stress.c:115:73:115:75 | ret | +| ssa_stress.c:115:73:115:80 | SSA definition | SSA def(ret) | ssa_stress.c:115:83:115:85 | ret | +| ssa_stress.c:115:83:115:90 | SSA definition | SSA def(ret) | ssa_stress.c:115:93:115:95 | ret | +| ssa_stress.c:115:93:115:100 | SSA definition | SSA def(ret) | ssa_stress.c:116:3:116:5 | ret | +| ssa_stress.c:116:3:116:10 | SSA definition | SSA def(ret) | ssa_stress.c:116:13:116:15 | ret | +| ssa_stress.c:116:13:116:20 | SSA definition | SSA def(ret) | ssa_stress.c:116:23:116:25 | ret | +| ssa_stress.c:116:23:116:30 | SSA definition | SSA def(ret) | ssa_stress.c:116:33:116:35 | ret | +| ssa_stress.c:116:33:116:40 | SSA definition | SSA def(ret) | ssa_stress.c:116:43:116:45 | ret | +| ssa_stress.c:116:43:116:50 | SSA definition | SSA def(ret) | ssa_stress.c:116:53:116:55 | ret | +| ssa_stress.c:116:53:116:60 | SSA definition | SSA def(ret) | ssa_stress.c:116:63:116:65 | ret | +| ssa_stress.c:116:63:116:70 | SSA definition | SSA def(ret) | ssa_stress.c:116:73:116:75 | ret | +| ssa_stress.c:116:73:116:80 | SSA definition | SSA def(ret) | ssa_stress.c:116:83:116:85 | ret | +| ssa_stress.c:116:83:116:90 | SSA definition | SSA def(ret) | ssa_stress.c:116:93:116:95 | ret | +| ssa_stress.c:116:93:116:100 | SSA definition | SSA def(ret) | ssa_stress.c:117:3:117:5 | ret | +| ssa_stress.c:117:3:117:10 | SSA definition | SSA def(ret) | ssa_stress.c:117:13:117:15 | ret | +| ssa_stress.c:117:13:117:20 | SSA definition | SSA def(ret) | ssa_stress.c:117:23:117:25 | ret | +| ssa_stress.c:117:23:117:30 | SSA definition | SSA def(ret) | ssa_stress.c:117:33:117:35 | ret | +| ssa_stress.c:117:33:117:40 | SSA definition | SSA def(ret) | ssa_stress.c:117:43:117:45 | ret | +| ssa_stress.c:117:43:117:50 | SSA definition | SSA def(ret) | ssa_stress.c:117:53:117:55 | ret | +| ssa_stress.c:117:53:117:60 | SSA definition | SSA def(ret) | ssa_stress.c:117:63:117:65 | ret | +| ssa_stress.c:117:63:117:70 | SSA definition | SSA def(ret) | ssa_stress.c:117:73:117:75 | ret | +| ssa_stress.c:117:73:117:80 | SSA definition | SSA def(ret) | ssa_stress.c:117:83:117:85 | ret | +| ssa_stress.c:117:83:117:90 | SSA definition | SSA def(ret) | ssa_stress.c:117:93:117:95 | ret | +| ssa_stress.c:117:93:117:100 | SSA definition | SSA def(ret) | ssa_stress.c:118:3:118:5 | ret | +| ssa_stress.c:118:3:118:10 | SSA definition | SSA def(ret) | ssa_stress.c:118:13:118:15 | ret | +| ssa_stress.c:118:13:118:20 | SSA definition | SSA def(ret) | ssa_stress.c:118:23:118:25 | ret | +| ssa_stress.c:118:23:118:30 | SSA definition | SSA def(ret) | ssa_stress.c:118:33:118:35 | ret | +| ssa_stress.c:118:33:118:40 | SSA definition | SSA def(ret) | ssa_stress.c:118:43:118:45 | ret | +| ssa_stress.c:118:43:118:50 | SSA definition | SSA def(ret) | ssa_stress.c:118:53:118:55 | ret | +| ssa_stress.c:118:53:118:60 | SSA definition | SSA def(ret) | ssa_stress.c:118:63:118:65 | ret | +| ssa_stress.c:118:63:118:70 | SSA definition | SSA def(ret) | ssa_stress.c:118:73:118:75 | ret | +| ssa_stress.c:118:73:118:80 | SSA definition | SSA def(ret) | ssa_stress.c:118:83:118:85 | ret | +| ssa_stress.c:118:83:118:90 | SSA definition | SSA def(ret) | ssa_stress.c:118:93:118:95 | ret | +| ssa_stress.c:118:93:118:100 | SSA definition | SSA def(ret) | ssa_stress.c:119:3:119:5 | ret | +| ssa_stress.c:119:3:119:10 | SSA definition | SSA def(ret) | ssa_stress.c:119:13:119:15 | ret | +| ssa_stress.c:119:13:119:20 | SSA definition | SSA def(ret) | ssa_stress.c:119:23:119:25 | ret | +| ssa_stress.c:119:23:119:30 | SSA definition | SSA def(ret) | ssa_stress.c:119:33:119:35 | ret | +| ssa_stress.c:119:33:119:40 | SSA definition | SSA def(ret) | ssa_stress.c:119:43:119:45 | ret | +| ssa_stress.c:119:43:119:50 | SSA definition | SSA def(ret) | ssa_stress.c:119:53:119:55 | ret | +| ssa_stress.c:119:53:119:60 | SSA definition | SSA def(ret) | ssa_stress.c:119:63:119:65 | ret | +| ssa_stress.c:119:63:119:70 | SSA definition | SSA def(ret) | ssa_stress.c:119:73:119:75 | ret | +| ssa_stress.c:119:73:119:80 | SSA definition | SSA def(ret) | ssa_stress.c:119:83:119:85 | ret | +| ssa_stress.c:119:83:119:90 | SSA definition | SSA def(ret) | ssa_stress.c:119:93:119:95 | ret | +| ssa_stress.c:119:93:119:100 | SSA definition | SSA def(ret) | ssa_stress.c:120:3:120:5 | ret | +| ssa_stress.c:120:3:120:10 | SSA definition | SSA def(ret) | ssa_stress.c:120:13:120:15 | ret | +| ssa_stress.c:120:13:120:20 | SSA definition | SSA def(ret) | ssa_stress.c:120:23:120:25 | ret | +| ssa_stress.c:120:23:120:30 | SSA definition | SSA def(ret) | ssa_stress.c:120:33:120:35 | ret | +| ssa_stress.c:120:33:120:40 | SSA definition | SSA def(ret) | ssa_stress.c:120:43:120:45 | ret | +| ssa_stress.c:120:43:120:50 | SSA definition | SSA def(ret) | ssa_stress.c:120:53:120:55 | ret | +| ssa_stress.c:120:53:120:60 | SSA definition | SSA def(ret) | ssa_stress.c:120:63:120:65 | ret | +| ssa_stress.c:120:63:120:70 | SSA definition | SSA def(ret) | ssa_stress.c:120:73:120:75 | ret | +| ssa_stress.c:120:73:120:80 | SSA definition | SSA def(ret) | ssa_stress.c:120:83:120:85 | ret | +| ssa_stress.c:120:83:120:90 | SSA definition | SSA def(ret) | ssa_stress.c:120:93:120:95 | ret | +| ssa_stress.c:120:93:120:100 | SSA definition | SSA def(ret) | ssa_stress.c:121:3:121:5 | ret | +| ssa_stress.c:121:3:121:10 | SSA definition | SSA def(ret) | ssa_stress.c:121:13:121:15 | ret | +| ssa_stress.c:121:13:121:20 | SSA definition | SSA def(ret) | ssa_stress.c:121:23:121:25 | ret | +| ssa_stress.c:121:23:121:30 | SSA definition | SSA def(ret) | ssa_stress.c:121:33:121:35 | ret | +| ssa_stress.c:121:33:121:40 | SSA definition | SSA def(ret) | ssa_stress.c:121:43:121:45 | ret | +| ssa_stress.c:121:43:121:50 | SSA definition | SSA def(ret) | ssa_stress.c:121:53:121:55 | ret | +| ssa_stress.c:121:53:121:60 | SSA definition | SSA def(ret) | ssa_stress.c:121:63:121:65 | ret | +| ssa_stress.c:121:63:121:70 | SSA definition | SSA def(ret) | ssa_stress.c:121:73:121:75 | ret | +| ssa_stress.c:121:73:121:80 | SSA definition | SSA def(ret) | ssa_stress.c:121:83:121:85 | ret | +| ssa_stress.c:121:83:121:90 | SSA definition | SSA def(ret) | ssa_stress.c:121:93:121:95 | ret | +| ssa_stress.c:121:93:121:100 | SSA definition | SSA def(ret) | ssa_stress.c:122:3:122:5 | ret | +| ssa_stress.c:122:3:122:10 | SSA definition | SSA def(ret) | ssa_stress.c:122:13:122:15 | ret | +| ssa_stress.c:122:13:122:20 | SSA definition | SSA def(ret) | ssa_stress.c:122:23:122:25 | ret | +| ssa_stress.c:122:23:122:30 | SSA definition | SSA def(ret) | ssa_stress.c:122:33:122:35 | ret | +| ssa_stress.c:122:33:122:40 | SSA definition | SSA def(ret) | ssa_stress.c:122:43:122:45 | ret | +| ssa_stress.c:122:43:122:50 | SSA definition | SSA def(ret) | ssa_stress.c:122:53:122:55 | ret | +| ssa_stress.c:122:53:122:60 | SSA definition | SSA def(ret) | ssa_stress.c:122:63:122:65 | ret | +| ssa_stress.c:122:63:122:70 | SSA definition | SSA def(ret) | ssa_stress.c:122:73:122:75 | ret | +| ssa_stress.c:122:73:122:80 | SSA definition | SSA def(ret) | ssa_stress.c:122:83:122:85 | ret | +| ssa_stress.c:122:83:122:90 | SSA definition | SSA def(ret) | ssa_stress.c:122:93:122:95 | ret | +| ssa_stress.c:122:93:122:100 | SSA definition | SSA def(ret) | ssa_stress.c:125:3:125:5 | ret | +| ssa_stress.c:125:3:125:10 | SSA definition | SSA def(ret) | ssa_stress.c:125:13:125:15 | ret | +| ssa_stress.c:125:13:125:20 | SSA definition | SSA def(ret) | ssa_stress.c:125:23:125:25 | ret | +| ssa_stress.c:125:23:125:30 | SSA definition | SSA def(ret) | ssa_stress.c:125:33:125:35 | ret | +| ssa_stress.c:125:33:125:40 | SSA definition | SSA def(ret) | ssa_stress.c:125:43:125:45 | ret | +| ssa_stress.c:125:43:125:50 | SSA definition | SSA def(ret) | ssa_stress.c:125:53:125:55 | ret | +| ssa_stress.c:125:53:125:60 | SSA definition | SSA def(ret) | ssa_stress.c:125:63:125:65 | ret | +| ssa_stress.c:125:63:125:70 | SSA definition | SSA def(ret) | ssa_stress.c:125:73:125:75 | ret | +| ssa_stress.c:125:73:125:80 | SSA definition | SSA def(ret) | ssa_stress.c:125:83:125:85 | ret | +| ssa_stress.c:125:83:125:90 | SSA definition | SSA def(ret) | ssa_stress.c:125:93:125:95 | ret | +| ssa_stress.c:125:93:125:100 | SSA definition | SSA def(ret) | ssa_stress.c:126:3:126:5 | ret | +| ssa_stress.c:126:3:126:10 | SSA definition | SSA def(ret) | ssa_stress.c:126:13:126:15 | ret | +| ssa_stress.c:126:13:126:20 | SSA definition | SSA def(ret) | ssa_stress.c:126:23:126:25 | ret | +| ssa_stress.c:126:23:126:30 | SSA definition | SSA def(ret) | ssa_stress.c:126:33:126:35 | ret | +| ssa_stress.c:126:33:126:40 | SSA definition | SSA def(ret) | ssa_stress.c:126:43:126:45 | ret | +| ssa_stress.c:126:43:126:50 | SSA definition | SSA def(ret) | ssa_stress.c:126:53:126:55 | ret | +| ssa_stress.c:126:53:126:60 | SSA definition | SSA def(ret) | ssa_stress.c:126:63:126:65 | ret | +| ssa_stress.c:126:63:126:70 | SSA definition | SSA def(ret) | ssa_stress.c:126:73:126:75 | ret | +| ssa_stress.c:126:73:126:80 | SSA definition | SSA def(ret) | ssa_stress.c:126:83:126:85 | ret | +| ssa_stress.c:126:83:126:90 | SSA definition | SSA def(ret) | ssa_stress.c:126:93:126:95 | ret | +| ssa_stress.c:126:93:126:100 | SSA definition | SSA def(ret) | ssa_stress.c:127:3:127:5 | ret | +| ssa_stress.c:127:3:127:10 | SSA definition | SSA def(ret) | ssa_stress.c:127:13:127:15 | ret | +| ssa_stress.c:127:13:127:20 | SSA definition | SSA def(ret) | ssa_stress.c:127:23:127:25 | ret | +| ssa_stress.c:127:23:127:30 | SSA definition | SSA def(ret) | ssa_stress.c:127:33:127:35 | ret | +| ssa_stress.c:127:33:127:40 | SSA definition | SSA def(ret) | ssa_stress.c:127:43:127:45 | ret | +| ssa_stress.c:127:43:127:50 | SSA definition | SSA def(ret) | ssa_stress.c:127:53:127:55 | ret | +| ssa_stress.c:127:53:127:60 | SSA definition | SSA def(ret) | ssa_stress.c:127:63:127:65 | ret | +| ssa_stress.c:127:63:127:70 | SSA definition | SSA def(ret) | ssa_stress.c:127:73:127:75 | ret | +| ssa_stress.c:127:73:127:80 | SSA definition | SSA def(ret) | ssa_stress.c:127:83:127:85 | ret | +| ssa_stress.c:127:83:127:90 | SSA definition | SSA def(ret) | ssa_stress.c:127:93:127:95 | ret | +| ssa_stress.c:127:93:127:100 | SSA definition | SSA def(ret) | ssa_stress.c:128:3:128:5 | ret | +| ssa_stress.c:128:3:128:10 | SSA definition | SSA def(ret) | ssa_stress.c:128:13:128:15 | ret | +| ssa_stress.c:128:13:128:20 | SSA definition | SSA def(ret) | ssa_stress.c:128:23:128:25 | ret | +| ssa_stress.c:128:23:128:30 | SSA definition | SSA def(ret) | ssa_stress.c:128:33:128:35 | ret | +| ssa_stress.c:128:33:128:40 | SSA definition | SSA def(ret) | ssa_stress.c:128:43:128:45 | ret | +| ssa_stress.c:128:43:128:50 | SSA definition | SSA def(ret) | ssa_stress.c:128:53:128:55 | ret | +| ssa_stress.c:128:53:128:60 | SSA definition | SSA def(ret) | ssa_stress.c:128:63:128:65 | ret | +| ssa_stress.c:128:63:128:70 | SSA definition | SSA def(ret) | ssa_stress.c:128:73:128:75 | ret | +| ssa_stress.c:128:73:128:80 | SSA definition | SSA def(ret) | ssa_stress.c:128:83:128:85 | ret | +| ssa_stress.c:128:83:128:90 | SSA definition | SSA def(ret) | ssa_stress.c:128:93:128:95 | ret | +| ssa_stress.c:128:93:128:100 | SSA definition | SSA def(ret) | ssa_stress.c:129:3:129:5 | ret | +| ssa_stress.c:129:3:129:10 | SSA definition | SSA def(ret) | ssa_stress.c:129:13:129:15 | ret | +| ssa_stress.c:129:13:129:20 | SSA definition | SSA def(ret) | ssa_stress.c:129:23:129:25 | ret | +| ssa_stress.c:129:23:129:30 | SSA definition | SSA def(ret) | ssa_stress.c:129:33:129:35 | ret | +| ssa_stress.c:129:33:129:40 | SSA definition | SSA def(ret) | ssa_stress.c:129:43:129:45 | ret | +| ssa_stress.c:129:43:129:50 | SSA definition | SSA def(ret) | ssa_stress.c:129:53:129:55 | ret | +| ssa_stress.c:129:53:129:60 | SSA definition | SSA def(ret) | ssa_stress.c:129:63:129:65 | ret | +| ssa_stress.c:129:63:129:70 | SSA definition | SSA def(ret) | ssa_stress.c:129:73:129:75 | ret | +| ssa_stress.c:129:73:129:80 | SSA definition | SSA def(ret) | ssa_stress.c:129:83:129:85 | ret | +| ssa_stress.c:129:83:129:90 | SSA definition | SSA def(ret) | ssa_stress.c:129:93:129:95 | ret | +| ssa_stress.c:129:93:129:100 | SSA definition | SSA def(ret) | ssa_stress.c:130:3:130:5 | ret | +| ssa_stress.c:130:3:130:10 | SSA definition | SSA def(ret) | ssa_stress.c:130:13:130:15 | ret | +| ssa_stress.c:130:13:130:20 | SSA definition | SSA def(ret) | ssa_stress.c:130:23:130:25 | ret | +| ssa_stress.c:130:23:130:30 | SSA definition | SSA def(ret) | ssa_stress.c:130:33:130:35 | ret | +| ssa_stress.c:130:33:130:40 | SSA definition | SSA def(ret) | ssa_stress.c:130:43:130:45 | ret | +| ssa_stress.c:130:43:130:50 | SSA definition | SSA def(ret) | ssa_stress.c:130:53:130:55 | ret | +| ssa_stress.c:130:53:130:60 | SSA definition | SSA def(ret) | ssa_stress.c:130:63:130:65 | ret | +| ssa_stress.c:130:63:130:70 | SSA definition | SSA def(ret) | ssa_stress.c:130:73:130:75 | ret | +| ssa_stress.c:130:73:130:80 | SSA definition | SSA def(ret) | ssa_stress.c:130:83:130:85 | ret | +| ssa_stress.c:130:83:130:90 | SSA definition | SSA def(ret) | ssa_stress.c:130:93:130:95 | ret | +| ssa_stress.c:130:93:130:100 | SSA definition | SSA def(ret) | ssa_stress.c:131:3:131:5 | ret | +| ssa_stress.c:131:3:131:10 | SSA definition | SSA def(ret) | ssa_stress.c:131:13:131:15 | ret | +| ssa_stress.c:131:13:131:20 | SSA definition | SSA def(ret) | ssa_stress.c:131:23:131:25 | ret | +| ssa_stress.c:131:23:131:30 | SSA definition | SSA def(ret) | ssa_stress.c:131:33:131:35 | ret | +| ssa_stress.c:131:33:131:40 | SSA definition | SSA def(ret) | ssa_stress.c:131:43:131:45 | ret | +| ssa_stress.c:131:43:131:50 | SSA definition | SSA def(ret) | ssa_stress.c:131:53:131:55 | ret | +| ssa_stress.c:131:53:131:60 | SSA definition | SSA def(ret) | ssa_stress.c:131:63:131:65 | ret | +| ssa_stress.c:131:63:131:70 | SSA definition | SSA def(ret) | ssa_stress.c:131:73:131:75 | ret | +| ssa_stress.c:131:73:131:80 | SSA definition | SSA def(ret) | ssa_stress.c:131:83:131:85 | ret | +| ssa_stress.c:131:83:131:90 | SSA definition | SSA def(ret) | ssa_stress.c:131:93:131:95 | ret | +| ssa_stress.c:131:93:131:100 | SSA definition | SSA def(ret) | ssa_stress.c:132:3:132:5 | ret | +| ssa_stress.c:132:3:132:10 | SSA definition | SSA def(ret) | ssa_stress.c:132:13:132:15 | ret | +| ssa_stress.c:132:13:132:20 | SSA definition | SSA def(ret) | ssa_stress.c:132:23:132:25 | ret | +| ssa_stress.c:132:23:132:30 | SSA definition | SSA def(ret) | ssa_stress.c:132:33:132:35 | ret | +| ssa_stress.c:132:33:132:40 | SSA definition | SSA def(ret) | ssa_stress.c:132:43:132:45 | ret | +| ssa_stress.c:132:43:132:50 | SSA definition | SSA def(ret) | ssa_stress.c:132:53:132:55 | ret | +| ssa_stress.c:132:53:132:60 | SSA definition | SSA def(ret) | ssa_stress.c:132:63:132:65 | ret | +| ssa_stress.c:132:63:132:70 | SSA definition | SSA def(ret) | ssa_stress.c:132:73:132:75 | ret | +| ssa_stress.c:132:73:132:80 | SSA definition | SSA def(ret) | ssa_stress.c:132:83:132:85 | ret | +| ssa_stress.c:132:83:132:90 | SSA definition | SSA def(ret) | ssa_stress.c:132:93:132:95 | ret | +| ssa_stress.c:132:93:132:100 | SSA definition | SSA def(ret) | ssa_stress.c:133:3:133:5 | ret | +| ssa_stress.c:133:3:133:10 | SSA definition | SSA def(ret) | ssa_stress.c:133:13:133:15 | ret | +| ssa_stress.c:133:13:133:20 | SSA definition | SSA def(ret) | ssa_stress.c:133:23:133:25 | ret | +| ssa_stress.c:133:23:133:30 | SSA definition | SSA def(ret) | ssa_stress.c:133:33:133:35 | ret | +| ssa_stress.c:133:33:133:40 | SSA definition | SSA def(ret) | ssa_stress.c:133:43:133:45 | ret | +| ssa_stress.c:133:43:133:50 | SSA definition | SSA def(ret) | ssa_stress.c:133:53:133:55 | ret | +| ssa_stress.c:133:53:133:60 | SSA definition | SSA def(ret) | ssa_stress.c:133:63:133:65 | ret | +| ssa_stress.c:133:63:133:70 | SSA definition | SSA def(ret) | ssa_stress.c:133:73:133:75 | ret | +| ssa_stress.c:133:73:133:80 | SSA definition | SSA def(ret) | ssa_stress.c:133:83:133:85 | ret | +| ssa_stress.c:133:83:133:90 | SSA definition | SSA def(ret) | ssa_stress.c:133:93:133:95 | ret | +| ssa_stress.c:133:93:133:100 | SSA definition | SSA def(ret) | ssa_stress.c:134:3:134:5 | ret | +| ssa_stress.c:134:3:134:10 | SSA definition | SSA def(ret) | ssa_stress.c:134:13:134:15 | ret | +| ssa_stress.c:134:13:134:20 | SSA definition | SSA def(ret) | ssa_stress.c:134:23:134:25 | ret | +| ssa_stress.c:134:23:134:30 | SSA definition | SSA def(ret) | ssa_stress.c:134:33:134:35 | ret | +| ssa_stress.c:134:33:134:40 | SSA definition | SSA def(ret) | ssa_stress.c:134:43:134:45 | ret | +| ssa_stress.c:134:43:134:50 | SSA definition | SSA def(ret) | ssa_stress.c:134:53:134:55 | ret | +| ssa_stress.c:134:53:134:60 | SSA definition | SSA def(ret) | ssa_stress.c:134:63:134:65 | ret | +| ssa_stress.c:134:63:134:70 | SSA definition | SSA def(ret) | ssa_stress.c:134:73:134:75 | ret | +| ssa_stress.c:134:73:134:80 | SSA definition | SSA def(ret) | ssa_stress.c:134:83:134:85 | ret | +| ssa_stress.c:134:83:134:90 | SSA definition | SSA def(ret) | ssa_stress.c:134:93:134:95 | ret | +| ssa_stress.c:134:93:134:100 | SSA definition | SSA def(ret) | ssa_stress.c:137:3:137:5 | ret | +| ssa_stress.c:137:3:137:10 | SSA definition | SSA def(ret) | ssa_stress.c:137:13:137:15 | ret | +| ssa_stress.c:137:13:137:20 | SSA definition | SSA def(ret) | ssa_stress.c:137:23:137:25 | ret | +| ssa_stress.c:137:23:137:30 | SSA definition | SSA def(ret) | ssa_stress.c:137:33:137:35 | ret | +| ssa_stress.c:137:33:137:40 | SSA definition | SSA def(ret) | ssa_stress.c:137:43:137:45 | ret | +| ssa_stress.c:137:43:137:50 | SSA definition | SSA def(ret) | ssa_stress.c:137:53:137:55 | ret | +| ssa_stress.c:137:53:137:60 | SSA definition | SSA def(ret) | ssa_stress.c:137:63:137:65 | ret | +| ssa_stress.c:137:63:137:70 | SSA definition | SSA def(ret) | ssa_stress.c:137:73:137:75 | ret | +| ssa_stress.c:137:73:137:80 | SSA definition | SSA def(ret) | ssa_stress.c:137:83:137:85 | ret | +| ssa_stress.c:137:83:137:90 | SSA definition | SSA def(ret) | ssa_stress.c:137:93:137:95 | ret | +| ssa_stress.c:137:93:137:100 | SSA definition | SSA def(ret) | ssa_stress.c:138:3:138:5 | ret | +| ssa_stress.c:138:3:138:10 | SSA definition | SSA def(ret) | ssa_stress.c:138:13:138:15 | ret | +| ssa_stress.c:138:13:138:20 | SSA definition | SSA def(ret) | ssa_stress.c:138:23:138:25 | ret | +| ssa_stress.c:138:23:138:30 | SSA definition | SSA def(ret) | ssa_stress.c:138:33:138:35 | ret | +| ssa_stress.c:138:33:138:40 | SSA definition | SSA def(ret) | ssa_stress.c:138:43:138:45 | ret | +| ssa_stress.c:138:43:138:50 | SSA definition | SSA def(ret) | ssa_stress.c:138:53:138:55 | ret | +| ssa_stress.c:138:53:138:60 | SSA definition | SSA def(ret) | ssa_stress.c:138:63:138:65 | ret | +| ssa_stress.c:138:63:138:70 | SSA definition | SSA def(ret) | ssa_stress.c:138:73:138:75 | ret | +| ssa_stress.c:138:73:138:80 | SSA definition | SSA def(ret) | ssa_stress.c:138:83:138:85 | ret | +| ssa_stress.c:138:83:138:90 | SSA definition | SSA def(ret) | ssa_stress.c:138:93:138:95 | ret | +| ssa_stress.c:138:93:138:100 | SSA definition | SSA def(ret) | ssa_stress.c:139:3:139:5 | ret | +| ssa_stress.c:139:3:139:10 | SSA definition | SSA def(ret) | ssa_stress.c:139:13:139:15 | ret | +| ssa_stress.c:139:13:139:20 | SSA definition | SSA def(ret) | ssa_stress.c:139:23:139:25 | ret | +| ssa_stress.c:139:23:139:30 | SSA definition | SSA def(ret) | ssa_stress.c:139:33:139:35 | ret | +| ssa_stress.c:139:33:139:40 | SSA definition | SSA def(ret) | ssa_stress.c:139:43:139:45 | ret | +| ssa_stress.c:139:43:139:50 | SSA definition | SSA def(ret) | ssa_stress.c:139:53:139:55 | ret | +| ssa_stress.c:139:53:139:60 | SSA definition | SSA def(ret) | ssa_stress.c:139:63:139:65 | ret | +| ssa_stress.c:139:63:139:70 | SSA definition | SSA def(ret) | ssa_stress.c:139:73:139:75 | ret | +| ssa_stress.c:139:73:139:80 | SSA definition | SSA def(ret) | ssa_stress.c:139:83:139:85 | ret | +| ssa_stress.c:139:83:139:90 | SSA definition | SSA def(ret) | ssa_stress.c:139:93:139:95 | ret | +| ssa_stress.c:139:93:139:100 | SSA definition | SSA def(ret) | ssa_stress.c:140:3:140:5 | ret | +| ssa_stress.c:140:3:140:10 | SSA definition | SSA def(ret) | ssa_stress.c:140:13:140:15 | ret | +| ssa_stress.c:140:13:140:20 | SSA definition | SSA def(ret) | ssa_stress.c:140:23:140:25 | ret | +| ssa_stress.c:140:23:140:30 | SSA definition | SSA def(ret) | ssa_stress.c:140:33:140:35 | ret | +| ssa_stress.c:140:33:140:40 | SSA definition | SSA def(ret) | ssa_stress.c:140:43:140:45 | ret | +| ssa_stress.c:140:43:140:50 | SSA definition | SSA def(ret) | ssa_stress.c:140:53:140:55 | ret | +| ssa_stress.c:140:53:140:60 | SSA definition | SSA def(ret) | ssa_stress.c:140:63:140:65 | ret | +| ssa_stress.c:140:63:140:70 | SSA definition | SSA def(ret) | ssa_stress.c:140:73:140:75 | ret | +| ssa_stress.c:140:73:140:80 | SSA definition | SSA def(ret) | ssa_stress.c:140:83:140:85 | ret | +| ssa_stress.c:140:83:140:90 | SSA definition | SSA def(ret) | ssa_stress.c:140:93:140:95 | ret | +| ssa_stress.c:140:93:140:100 | SSA definition | SSA def(ret) | ssa_stress.c:141:3:141:5 | ret | +| ssa_stress.c:141:3:141:10 | SSA definition | SSA def(ret) | ssa_stress.c:141:13:141:15 | ret | +| ssa_stress.c:141:13:141:20 | SSA definition | SSA def(ret) | ssa_stress.c:141:23:141:25 | ret | +| ssa_stress.c:141:23:141:30 | SSA definition | SSA def(ret) | ssa_stress.c:141:33:141:35 | ret | +| ssa_stress.c:141:33:141:40 | SSA definition | SSA def(ret) | ssa_stress.c:141:43:141:45 | ret | +| ssa_stress.c:141:43:141:50 | SSA definition | SSA def(ret) | ssa_stress.c:141:53:141:55 | ret | +| ssa_stress.c:141:53:141:60 | SSA definition | SSA def(ret) | ssa_stress.c:141:63:141:65 | ret | +| ssa_stress.c:141:63:141:70 | SSA definition | SSA def(ret) | ssa_stress.c:141:73:141:75 | ret | +| ssa_stress.c:141:73:141:80 | SSA definition | SSA def(ret) | ssa_stress.c:141:83:141:85 | ret | +| ssa_stress.c:141:83:141:90 | SSA definition | SSA def(ret) | ssa_stress.c:141:93:141:95 | ret | +| ssa_stress.c:141:93:141:100 | SSA definition | SSA def(ret) | ssa_stress.c:142:3:142:5 | ret | +| ssa_stress.c:142:3:142:10 | SSA definition | SSA def(ret) | ssa_stress.c:142:13:142:15 | ret | +| ssa_stress.c:142:13:142:20 | SSA definition | SSA def(ret) | ssa_stress.c:142:23:142:25 | ret | +| ssa_stress.c:142:23:142:30 | SSA definition | SSA def(ret) | ssa_stress.c:142:33:142:35 | ret | +| ssa_stress.c:142:33:142:40 | SSA definition | SSA def(ret) | ssa_stress.c:142:43:142:45 | ret | +| ssa_stress.c:142:43:142:50 | SSA definition | SSA def(ret) | ssa_stress.c:142:53:142:55 | ret | +| ssa_stress.c:142:53:142:60 | SSA definition | SSA def(ret) | ssa_stress.c:142:63:142:65 | ret | +| ssa_stress.c:142:63:142:70 | SSA definition | SSA def(ret) | ssa_stress.c:142:73:142:75 | ret | +| ssa_stress.c:142:73:142:80 | SSA definition | SSA def(ret) | ssa_stress.c:142:83:142:85 | ret | +| ssa_stress.c:142:83:142:90 | SSA definition | SSA def(ret) | ssa_stress.c:142:93:142:95 | ret | +| ssa_stress.c:142:93:142:100 | SSA definition | SSA def(ret) | ssa_stress.c:143:3:143:5 | ret | +| ssa_stress.c:143:3:143:10 | SSA definition | SSA def(ret) | ssa_stress.c:143:13:143:15 | ret | +| ssa_stress.c:143:13:143:20 | SSA definition | SSA def(ret) | ssa_stress.c:143:23:143:25 | ret | +| ssa_stress.c:143:23:143:30 | SSA definition | SSA def(ret) | ssa_stress.c:143:33:143:35 | ret | +| ssa_stress.c:143:33:143:40 | SSA definition | SSA def(ret) | ssa_stress.c:143:43:143:45 | ret | +| ssa_stress.c:143:43:143:50 | SSA definition | SSA def(ret) | ssa_stress.c:143:53:143:55 | ret | +| ssa_stress.c:143:53:143:60 | SSA definition | SSA def(ret) | ssa_stress.c:143:63:143:65 | ret | +| ssa_stress.c:143:63:143:70 | SSA definition | SSA def(ret) | ssa_stress.c:143:73:143:75 | ret | +| ssa_stress.c:143:73:143:80 | SSA definition | SSA def(ret) | ssa_stress.c:143:83:143:85 | ret | +| ssa_stress.c:143:83:143:90 | SSA definition | SSA def(ret) | ssa_stress.c:143:93:143:95 | ret | +| ssa_stress.c:143:93:143:100 | SSA definition | SSA def(ret) | ssa_stress.c:144:3:144:5 | ret | +| ssa_stress.c:144:3:144:10 | SSA definition | SSA def(ret) | ssa_stress.c:144:13:144:15 | ret | +| ssa_stress.c:144:13:144:20 | SSA definition | SSA def(ret) | ssa_stress.c:144:23:144:25 | ret | +| ssa_stress.c:144:23:144:30 | SSA definition | SSA def(ret) | ssa_stress.c:144:33:144:35 | ret | +| ssa_stress.c:144:33:144:40 | SSA definition | SSA def(ret) | ssa_stress.c:144:43:144:45 | ret | +| ssa_stress.c:144:43:144:50 | SSA definition | SSA def(ret) | ssa_stress.c:144:53:144:55 | ret | +| ssa_stress.c:144:53:144:60 | SSA definition | SSA def(ret) | ssa_stress.c:144:63:144:65 | ret | +| ssa_stress.c:144:63:144:70 | SSA definition | SSA def(ret) | ssa_stress.c:144:73:144:75 | ret | +| ssa_stress.c:144:73:144:80 | SSA definition | SSA def(ret) | ssa_stress.c:144:83:144:85 | ret | +| ssa_stress.c:144:83:144:90 | SSA definition | SSA def(ret) | ssa_stress.c:144:93:144:95 | ret | +| ssa_stress.c:144:93:144:100 | SSA definition | SSA def(ret) | ssa_stress.c:145:3:145:5 | ret | +| ssa_stress.c:145:3:145:10 | SSA definition | SSA def(ret) | ssa_stress.c:145:13:145:15 | ret | +| ssa_stress.c:145:13:145:20 | SSA definition | SSA def(ret) | ssa_stress.c:145:23:145:25 | ret | +| ssa_stress.c:145:23:145:30 | SSA definition | SSA def(ret) | ssa_stress.c:145:33:145:35 | ret | +| ssa_stress.c:145:33:145:40 | SSA definition | SSA def(ret) | ssa_stress.c:145:43:145:45 | ret | +| ssa_stress.c:145:43:145:50 | SSA definition | SSA def(ret) | ssa_stress.c:145:53:145:55 | ret | +| ssa_stress.c:145:53:145:60 | SSA definition | SSA def(ret) | ssa_stress.c:145:63:145:65 | ret | +| ssa_stress.c:145:63:145:70 | SSA definition | SSA def(ret) | ssa_stress.c:145:73:145:75 | ret | +| ssa_stress.c:145:73:145:80 | SSA definition | SSA def(ret) | ssa_stress.c:145:83:145:85 | ret | +| ssa_stress.c:145:83:145:90 | SSA definition | SSA def(ret) | ssa_stress.c:145:93:145:95 | ret | +| ssa_stress.c:145:93:145:100 | SSA definition | SSA def(ret) | ssa_stress.c:146:3:146:5 | ret | +| ssa_stress.c:146:3:146:10 | SSA definition | SSA def(ret) | ssa_stress.c:146:13:146:15 | ret | +| ssa_stress.c:146:13:146:20 | SSA definition | SSA def(ret) | ssa_stress.c:146:23:146:25 | ret | +| ssa_stress.c:146:23:146:30 | SSA definition | SSA def(ret) | ssa_stress.c:146:33:146:35 | ret | +| ssa_stress.c:146:33:146:40 | SSA definition | SSA def(ret) | ssa_stress.c:146:43:146:45 | ret | +| ssa_stress.c:146:43:146:50 | SSA definition | SSA def(ret) | ssa_stress.c:146:53:146:55 | ret | +| ssa_stress.c:146:53:146:60 | SSA definition | SSA def(ret) | ssa_stress.c:146:63:146:65 | ret | +| ssa_stress.c:146:63:146:70 | SSA definition | SSA def(ret) | ssa_stress.c:146:73:146:75 | ret | +| ssa_stress.c:146:73:146:80 | SSA definition | SSA def(ret) | ssa_stress.c:146:83:146:85 | ret | +| ssa_stress.c:146:83:146:90 | SSA definition | SSA def(ret) | ssa_stress.c:146:93:146:95 | ret | +| ssa_stress.c:146:93:146:100 | SSA definition | SSA def(ret) | ssa_stress.c:149:3:149:5 | ret | +| ssa_stress.c:149:3:149:10 | SSA definition | SSA def(ret) | ssa_stress.c:149:13:149:15 | ret | +| ssa_stress.c:149:13:149:20 | SSA definition | SSA def(ret) | ssa_stress.c:149:23:149:25 | ret | +| ssa_stress.c:149:23:149:30 | SSA definition | SSA def(ret) | ssa_stress.c:149:33:149:35 | ret | +| ssa_stress.c:149:33:149:40 | SSA definition | SSA def(ret) | ssa_stress.c:149:43:149:45 | ret | +| ssa_stress.c:149:43:149:50 | SSA definition | SSA def(ret) | ssa_stress.c:149:53:149:55 | ret | +| ssa_stress.c:149:53:149:60 | SSA definition | SSA def(ret) | ssa_stress.c:149:63:149:65 | ret | +| ssa_stress.c:149:63:149:70 | SSA definition | SSA def(ret) | ssa_stress.c:149:73:149:75 | ret | +| ssa_stress.c:149:73:149:80 | SSA definition | SSA def(ret) | ssa_stress.c:149:83:149:85 | ret | +| ssa_stress.c:149:83:149:90 | SSA definition | SSA def(ret) | ssa_stress.c:149:93:149:95 | ret | +| ssa_stress.c:149:93:149:100 | SSA definition | SSA def(ret) | ssa_stress.c:150:3:150:5 | ret | +| ssa_stress.c:150:3:150:10 | SSA definition | SSA def(ret) | ssa_stress.c:150:13:150:15 | ret | +| ssa_stress.c:150:13:150:20 | SSA definition | SSA def(ret) | ssa_stress.c:150:23:150:25 | ret | +| ssa_stress.c:150:23:150:30 | SSA definition | SSA def(ret) | ssa_stress.c:150:33:150:35 | ret | +| ssa_stress.c:150:33:150:40 | SSA definition | SSA def(ret) | ssa_stress.c:150:43:150:45 | ret | +| ssa_stress.c:150:43:150:50 | SSA definition | SSA def(ret) | ssa_stress.c:150:53:150:55 | ret | +| ssa_stress.c:150:53:150:60 | SSA definition | SSA def(ret) | ssa_stress.c:150:63:150:65 | ret | +| ssa_stress.c:150:63:150:70 | SSA definition | SSA def(ret) | ssa_stress.c:150:73:150:75 | ret | +| ssa_stress.c:150:73:150:80 | SSA definition | SSA def(ret) | ssa_stress.c:150:83:150:85 | ret | +| ssa_stress.c:150:83:150:90 | SSA definition | SSA def(ret) | ssa_stress.c:150:93:150:95 | ret | +| ssa_stress.c:150:93:150:100 | SSA definition | SSA def(ret) | ssa_stress.c:151:3:151:5 | ret | +| ssa_stress.c:151:3:151:10 | SSA definition | SSA def(ret) | ssa_stress.c:151:13:151:15 | ret | +| ssa_stress.c:151:13:151:20 | SSA definition | SSA def(ret) | ssa_stress.c:151:23:151:25 | ret | +| ssa_stress.c:151:23:151:30 | SSA definition | SSA def(ret) | ssa_stress.c:151:33:151:35 | ret | +| ssa_stress.c:151:33:151:40 | SSA definition | SSA def(ret) | ssa_stress.c:151:43:151:45 | ret | +| ssa_stress.c:151:43:151:50 | SSA definition | SSA def(ret) | ssa_stress.c:151:53:151:55 | ret | +| ssa_stress.c:151:53:151:60 | SSA definition | SSA def(ret) | ssa_stress.c:151:63:151:65 | ret | +| ssa_stress.c:151:63:151:70 | SSA definition | SSA def(ret) | ssa_stress.c:151:73:151:75 | ret | +| ssa_stress.c:151:73:151:80 | SSA definition | SSA def(ret) | ssa_stress.c:151:83:151:85 | ret | +| ssa_stress.c:151:83:151:90 | SSA definition | SSA def(ret) | ssa_stress.c:151:93:151:95 | ret | +| ssa_stress.c:151:93:151:100 | SSA definition | SSA def(ret) | ssa_stress.c:152:3:152:5 | ret | +| ssa_stress.c:152:3:152:10 | SSA definition | SSA def(ret) | ssa_stress.c:152:13:152:15 | ret | +| ssa_stress.c:152:13:152:20 | SSA definition | SSA def(ret) | ssa_stress.c:152:23:152:25 | ret | +| ssa_stress.c:152:23:152:30 | SSA definition | SSA def(ret) | ssa_stress.c:152:33:152:35 | ret | +| ssa_stress.c:152:33:152:40 | SSA definition | SSA def(ret) | ssa_stress.c:152:43:152:45 | ret | +| ssa_stress.c:152:43:152:50 | SSA definition | SSA def(ret) | ssa_stress.c:152:53:152:55 | ret | +| ssa_stress.c:152:53:152:60 | SSA definition | SSA def(ret) | ssa_stress.c:152:63:152:65 | ret | +| ssa_stress.c:152:63:152:70 | SSA definition | SSA def(ret) | ssa_stress.c:152:73:152:75 | ret | +| ssa_stress.c:152:73:152:80 | SSA definition | SSA def(ret) | ssa_stress.c:152:83:152:85 | ret | +| ssa_stress.c:152:83:152:90 | SSA definition | SSA def(ret) | ssa_stress.c:152:93:152:95 | ret | +| ssa_stress.c:152:93:152:100 | SSA definition | SSA def(ret) | ssa_stress.c:153:3:153:5 | ret | +| ssa_stress.c:153:3:153:10 | SSA definition | SSA def(ret) | ssa_stress.c:153:13:153:15 | ret | +| ssa_stress.c:153:13:153:20 | SSA definition | SSA def(ret) | ssa_stress.c:153:23:153:25 | ret | +| ssa_stress.c:153:23:153:30 | SSA definition | SSA def(ret) | ssa_stress.c:153:33:153:35 | ret | +| ssa_stress.c:153:33:153:40 | SSA definition | SSA def(ret) | ssa_stress.c:153:43:153:45 | ret | +| ssa_stress.c:153:43:153:50 | SSA definition | SSA def(ret) | ssa_stress.c:153:53:153:55 | ret | +| ssa_stress.c:153:53:153:60 | SSA definition | SSA def(ret) | ssa_stress.c:153:63:153:65 | ret | +| ssa_stress.c:153:63:153:70 | SSA definition | SSA def(ret) | ssa_stress.c:153:73:153:75 | ret | +| ssa_stress.c:153:73:153:80 | SSA definition | SSA def(ret) | ssa_stress.c:153:83:153:85 | ret | +| ssa_stress.c:153:83:153:90 | SSA definition | SSA def(ret) | ssa_stress.c:153:93:153:95 | ret | +| ssa_stress.c:153:93:153:100 | SSA definition | SSA def(ret) | ssa_stress.c:154:3:154:5 | ret | +| ssa_stress.c:154:3:154:10 | SSA definition | SSA def(ret) | ssa_stress.c:154:13:154:15 | ret | +| ssa_stress.c:154:13:154:20 | SSA definition | SSA def(ret) | ssa_stress.c:154:23:154:25 | ret | +| ssa_stress.c:154:23:154:30 | SSA definition | SSA def(ret) | ssa_stress.c:154:33:154:35 | ret | +| ssa_stress.c:154:33:154:40 | SSA definition | SSA def(ret) | ssa_stress.c:154:43:154:45 | ret | +| ssa_stress.c:154:43:154:50 | SSA definition | SSA def(ret) | ssa_stress.c:154:53:154:55 | ret | +| ssa_stress.c:154:53:154:60 | SSA definition | SSA def(ret) | ssa_stress.c:154:63:154:65 | ret | +| ssa_stress.c:154:63:154:70 | SSA definition | SSA def(ret) | ssa_stress.c:154:73:154:75 | ret | +| ssa_stress.c:154:73:154:80 | SSA definition | SSA def(ret) | ssa_stress.c:154:83:154:85 | ret | +| ssa_stress.c:154:83:154:90 | SSA definition | SSA def(ret) | ssa_stress.c:154:93:154:95 | ret | +| ssa_stress.c:154:93:154:100 | SSA definition | SSA def(ret) | ssa_stress.c:155:3:155:5 | ret | +| ssa_stress.c:155:3:155:10 | SSA definition | SSA def(ret) | ssa_stress.c:155:13:155:15 | ret | +| ssa_stress.c:155:13:155:20 | SSA definition | SSA def(ret) | ssa_stress.c:155:23:155:25 | ret | +| ssa_stress.c:155:23:155:30 | SSA definition | SSA def(ret) | ssa_stress.c:155:33:155:35 | ret | +| ssa_stress.c:155:33:155:40 | SSA definition | SSA def(ret) | ssa_stress.c:155:43:155:45 | ret | +| ssa_stress.c:155:43:155:50 | SSA definition | SSA def(ret) | ssa_stress.c:155:53:155:55 | ret | +| ssa_stress.c:155:53:155:60 | SSA definition | SSA def(ret) | ssa_stress.c:155:63:155:65 | ret | +| ssa_stress.c:155:63:155:70 | SSA definition | SSA def(ret) | ssa_stress.c:155:73:155:75 | ret | +| ssa_stress.c:155:73:155:80 | SSA definition | SSA def(ret) | ssa_stress.c:155:83:155:85 | ret | +| ssa_stress.c:155:83:155:90 | SSA definition | SSA def(ret) | ssa_stress.c:155:93:155:95 | ret | +| ssa_stress.c:155:93:155:100 | SSA definition | SSA def(ret) | ssa_stress.c:156:3:156:5 | ret | +| ssa_stress.c:156:3:156:10 | SSA definition | SSA def(ret) | ssa_stress.c:156:13:156:15 | ret | +| ssa_stress.c:156:13:156:20 | SSA definition | SSA def(ret) | ssa_stress.c:156:23:156:25 | ret | +| ssa_stress.c:156:23:156:30 | SSA definition | SSA def(ret) | ssa_stress.c:156:33:156:35 | ret | +| ssa_stress.c:156:33:156:40 | SSA definition | SSA def(ret) | ssa_stress.c:156:43:156:45 | ret | +| ssa_stress.c:156:43:156:50 | SSA definition | SSA def(ret) | ssa_stress.c:156:53:156:55 | ret | +| ssa_stress.c:156:53:156:60 | SSA definition | SSA def(ret) | ssa_stress.c:156:63:156:65 | ret | +| ssa_stress.c:156:63:156:70 | SSA definition | SSA def(ret) | ssa_stress.c:156:73:156:75 | ret | +| ssa_stress.c:156:73:156:80 | SSA definition | SSA def(ret) | ssa_stress.c:156:83:156:85 | ret | +| ssa_stress.c:156:83:156:90 | SSA definition | SSA def(ret) | ssa_stress.c:156:93:156:95 | ret | +| ssa_stress.c:156:93:156:100 | SSA definition | SSA def(ret) | ssa_stress.c:157:3:157:5 | ret | +| ssa_stress.c:157:3:157:10 | SSA definition | SSA def(ret) | ssa_stress.c:157:13:157:15 | ret | +| ssa_stress.c:157:13:157:20 | SSA definition | SSA def(ret) | ssa_stress.c:157:23:157:25 | ret | +| ssa_stress.c:157:23:157:30 | SSA definition | SSA def(ret) | ssa_stress.c:157:33:157:35 | ret | +| ssa_stress.c:157:33:157:40 | SSA definition | SSA def(ret) | ssa_stress.c:157:43:157:45 | ret | +| ssa_stress.c:157:43:157:50 | SSA definition | SSA def(ret) | ssa_stress.c:157:53:157:55 | ret | +| ssa_stress.c:157:53:157:60 | SSA definition | SSA def(ret) | ssa_stress.c:157:63:157:65 | ret | +| ssa_stress.c:157:63:157:70 | SSA definition | SSA def(ret) | ssa_stress.c:157:73:157:75 | ret | +| ssa_stress.c:157:73:157:80 | SSA definition | SSA def(ret) | ssa_stress.c:157:83:157:85 | ret | +| ssa_stress.c:157:83:157:90 | SSA definition | SSA def(ret) | ssa_stress.c:157:93:157:95 | ret | +| ssa_stress.c:157:93:157:100 | SSA definition | SSA def(ret) | ssa_stress.c:158:3:158:5 | ret | +| ssa_stress.c:158:3:158:10 | SSA definition | SSA def(ret) | ssa_stress.c:158:13:158:15 | ret | +| ssa_stress.c:158:13:158:20 | SSA definition | SSA def(ret) | ssa_stress.c:158:23:158:25 | ret | +| ssa_stress.c:158:23:158:30 | SSA definition | SSA def(ret) | ssa_stress.c:158:33:158:35 | ret | +| ssa_stress.c:158:33:158:40 | SSA definition | SSA def(ret) | ssa_stress.c:158:43:158:45 | ret | +| ssa_stress.c:158:43:158:50 | SSA definition | SSA def(ret) | ssa_stress.c:158:53:158:55 | ret | +| ssa_stress.c:158:53:158:60 | SSA definition | SSA def(ret) | ssa_stress.c:158:63:158:65 | ret | +| ssa_stress.c:158:63:158:70 | SSA definition | SSA def(ret) | ssa_stress.c:158:73:158:75 | ret | +| ssa_stress.c:158:73:158:80 | SSA definition | SSA def(ret) | ssa_stress.c:158:83:158:85 | ret | +| ssa_stress.c:158:83:158:90 | SSA definition | SSA def(ret) | ssa_stress.c:158:93:158:95 | ret | +| ssa_stress.c:158:93:158:100 | SSA definition | SSA def(ret) | ssa_stress.c:161:3:161:5 | ret | +| ssa_stress.c:161:3:161:10 | SSA definition | SSA def(ret) | ssa_stress.c:161:13:161:15 | ret | +| ssa_stress.c:161:13:161:20 | SSA definition | SSA def(ret) | ssa_stress.c:161:23:161:25 | ret | +| ssa_stress.c:161:23:161:30 | SSA definition | SSA def(ret) | ssa_stress.c:161:33:161:35 | ret | +| ssa_stress.c:161:33:161:40 | SSA definition | SSA def(ret) | ssa_stress.c:161:43:161:45 | ret | +| ssa_stress.c:161:43:161:50 | SSA definition | SSA def(ret) | ssa_stress.c:161:53:161:55 | ret | +| ssa_stress.c:161:53:161:60 | SSA definition | SSA def(ret) | ssa_stress.c:161:63:161:65 | ret | +| ssa_stress.c:161:63:161:70 | SSA definition | SSA def(ret) | ssa_stress.c:161:73:161:75 | ret | +| ssa_stress.c:161:73:161:80 | SSA definition | SSA def(ret) | ssa_stress.c:161:83:161:85 | ret | +| ssa_stress.c:161:83:161:90 | SSA definition | SSA def(ret) | ssa_stress.c:161:93:161:95 | ret | +| ssa_stress.c:161:93:161:100 | SSA definition | SSA def(ret) | ssa_stress.c:162:3:162:5 | ret | +| ssa_stress.c:162:3:162:10 | SSA definition | SSA def(ret) | ssa_stress.c:162:13:162:15 | ret | +| ssa_stress.c:162:13:162:20 | SSA definition | SSA def(ret) | ssa_stress.c:162:23:162:25 | ret | +| ssa_stress.c:162:23:162:30 | SSA definition | SSA def(ret) | ssa_stress.c:162:33:162:35 | ret | +| ssa_stress.c:162:33:162:40 | SSA definition | SSA def(ret) | ssa_stress.c:162:43:162:45 | ret | +| ssa_stress.c:162:43:162:50 | SSA definition | SSA def(ret) | ssa_stress.c:162:53:162:55 | ret | +| ssa_stress.c:162:53:162:60 | SSA definition | SSA def(ret) | ssa_stress.c:162:63:162:65 | ret | +| ssa_stress.c:162:63:162:70 | SSA definition | SSA def(ret) | ssa_stress.c:162:73:162:75 | ret | +| ssa_stress.c:162:73:162:80 | SSA definition | SSA def(ret) | ssa_stress.c:162:83:162:85 | ret | +| ssa_stress.c:162:83:162:90 | SSA definition | SSA def(ret) | ssa_stress.c:162:93:162:95 | ret | +| ssa_stress.c:162:93:162:100 | SSA definition | SSA def(ret) | ssa_stress.c:163:3:163:5 | ret | +| ssa_stress.c:163:3:163:10 | SSA definition | SSA def(ret) | ssa_stress.c:163:13:163:15 | ret | +| ssa_stress.c:163:13:163:20 | SSA definition | SSA def(ret) | ssa_stress.c:163:23:163:25 | ret | +| ssa_stress.c:163:23:163:30 | SSA definition | SSA def(ret) | ssa_stress.c:163:33:163:35 | ret | +| ssa_stress.c:163:33:163:40 | SSA definition | SSA def(ret) | ssa_stress.c:163:43:163:45 | ret | +| ssa_stress.c:163:43:163:50 | SSA definition | SSA def(ret) | ssa_stress.c:163:53:163:55 | ret | +| ssa_stress.c:163:53:163:60 | SSA definition | SSA def(ret) | ssa_stress.c:163:63:163:65 | ret | +| ssa_stress.c:163:63:163:70 | SSA definition | SSA def(ret) | ssa_stress.c:163:73:163:75 | ret | +| ssa_stress.c:163:73:163:80 | SSA definition | SSA def(ret) | ssa_stress.c:163:83:163:85 | ret | +| ssa_stress.c:163:83:163:90 | SSA definition | SSA def(ret) | ssa_stress.c:163:93:163:95 | ret | +| ssa_stress.c:163:93:163:100 | SSA definition | SSA def(ret) | ssa_stress.c:164:3:164:5 | ret | +| ssa_stress.c:164:3:164:10 | SSA definition | SSA def(ret) | ssa_stress.c:164:13:164:15 | ret | +| ssa_stress.c:164:13:164:20 | SSA definition | SSA def(ret) | ssa_stress.c:164:23:164:25 | ret | +| ssa_stress.c:164:23:164:30 | SSA definition | SSA def(ret) | ssa_stress.c:164:33:164:35 | ret | +| ssa_stress.c:164:33:164:40 | SSA definition | SSA def(ret) | ssa_stress.c:164:43:164:45 | ret | +| ssa_stress.c:164:43:164:50 | SSA definition | SSA def(ret) | ssa_stress.c:164:53:164:55 | ret | +| ssa_stress.c:164:53:164:60 | SSA definition | SSA def(ret) | ssa_stress.c:164:63:164:65 | ret | +| ssa_stress.c:164:63:164:70 | SSA definition | SSA def(ret) | ssa_stress.c:164:73:164:75 | ret | +| ssa_stress.c:164:73:164:80 | SSA definition | SSA def(ret) | ssa_stress.c:164:83:164:85 | ret | +| ssa_stress.c:164:83:164:90 | SSA definition | SSA def(ret) | ssa_stress.c:164:93:164:95 | ret | +| ssa_stress.c:164:93:164:100 | SSA definition | SSA def(ret) | ssa_stress.c:165:3:165:5 | ret | +| ssa_stress.c:165:3:165:10 | SSA definition | SSA def(ret) | ssa_stress.c:165:13:165:15 | ret | +| ssa_stress.c:165:13:165:20 | SSA definition | SSA def(ret) | ssa_stress.c:165:23:165:25 | ret | +| ssa_stress.c:165:23:165:30 | SSA definition | SSA def(ret) | ssa_stress.c:165:33:165:35 | ret | +| ssa_stress.c:165:33:165:40 | SSA definition | SSA def(ret) | ssa_stress.c:165:43:165:45 | ret | +| ssa_stress.c:165:43:165:50 | SSA definition | SSA def(ret) | ssa_stress.c:165:53:165:55 | ret | +| ssa_stress.c:165:53:165:60 | SSA definition | SSA def(ret) | ssa_stress.c:165:63:165:65 | ret | +| ssa_stress.c:165:63:165:70 | SSA definition | SSA def(ret) | ssa_stress.c:165:73:165:75 | ret | +| ssa_stress.c:165:73:165:80 | SSA definition | SSA def(ret) | ssa_stress.c:165:83:165:85 | ret | +| ssa_stress.c:165:83:165:90 | SSA definition | SSA def(ret) | ssa_stress.c:165:93:165:95 | ret | +| ssa_stress.c:165:93:165:100 | SSA definition | SSA def(ret) | ssa_stress.c:166:3:166:5 | ret | +| ssa_stress.c:166:3:166:10 | SSA definition | SSA def(ret) | ssa_stress.c:166:13:166:15 | ret | +| ssa_stress.c:166:13:166:20 | SSA definition | SSA def(ret) | ssa_stress.c:166:23:166:25 | ret | +| ssa_stress.c:166:23:166:30 | SSA definition | SSA def(ret) | ssa_stress.c:166:33:166:35 | ret | +| ssa_stress.c:166:33:166:40 | SSA definition | SSA def(ret) | ssa_stress.c:166:43:166:45 | ret | +| ssa_stress.c:166:43:166:50 | SSA definition | SSA def(ret) | ssa_stress.c:166:53:166:55 | ret | +| ssa_stress.c:166:53:166:60 | SSA definition | SSA def(ret) | ssa_stress.c:166:63:166:65 | ret | +| ssa_stress.c:166:63:166:70 | SSA definition | SSA def(ret) | ssa_stress.c:166:73:166:75 | ret | +| ssa_stress.c:166:73:166:80 | SSA definition | SSA def(ret) | ssa_stress.c:166:83:166:85 | ret | +| ssa_stress.c:166:83:166:90 | SSA definition | SSA def(ret) | ssa_stress.c:166:93:166:95 | ret | +| ssa_stress.c:166:93:166:100 | SSA definition | SSA def(ret) | ssa_stress.c:167:3:167:5 | ret | +| ssa_stress.c:167:3:167:10 | SSA definition | SSA def(ret) | ssa_stress.c:167:13:167:15 | ret | +| ssa_stress.c:167:13:167:20 | SSA definition | SSA def(ret) | ssa_stress.c:167:23:167:25 | ret | +| ssa_stress.c:167:23:167:30 | SSA definition | SSA def(ret) | ssa_stress.c:167:33:167:35 | ret | +| ssa_stress.c:167:33:167:40 | SSA definition | SSA def(ret) | ssa_stress.c:167:43:167:45 | ret | +| ssa_stress.c:167:43:167:50 | SSA definition | SSA def(ret) | ssa_stress.c:167:53:167:55 | ret | +| ssa_stress.c:167:53:167:60 | SSA definition | SSA def(ret) | ssa_stress.c:167:63:167:65 | ret | +| ssa_stress.c:167:63:167:70 | SSA definition | SSA def(ret) | ssa_stress.c:167:73:167:75 | ret | +| ssa_stress.c:167:73:167:80 | SSA definition | SSA def(ret) | ssa_stress.c:167:83:167:85 | ret | +| ssa_stress.c:167:83:167:90 | SSA definition | SSA def(ret) | ssa_stress.c:167:93:167:95 | ret | +| ssa_stress.c:167:93:167:100 | SSA definition | SSA def(ret) | ssa_stress.c:168:3:168:5 | ret | +| ssa_stress.c:168:3:168:10 | SSA definition | SSA def(ret) | ssa_stress.c:168:13:168:15 | ret | +| ssa_stress.c:168:13:168:20 | SSA definition | SSA def(ret) | ssa_stress.c:168:23:168:25 | ret | +| ssa_stress.c:168:23:168:30 | SSA definition | SSA def(ret) | ssa_stress.c:168:33:168:35 | ret | +| ssa_stress.c:168:33:168:40 | SSA definition | SSA def(ret) | ssa_stress.c:168:43:168:45 | ret | +| ssa_stress.c:168:43:168:50 | SSA definition | SSA def(ret) | ssa_stress.c:168:53:168:55 | ret | +| ssa_stress.c:168:53:168:60 | SSA definition | SSA def(ret) | ssa_stress.c:168:63:168:65 | ret | +| ssa_stress.c:168:63:168:70 | SSA definition | SSA def(ret) | ssa_stress.c:168:73:168:75 | ret | +| ssa_stress.c:168:73:168:80 | SSA definition | SSA def(ret) | ssa_stress.c:168:83:168:85 | ret | +| ssa_stress.c:168:83:168:90 | SSA definition | SSA def(ret) | ssa_stress.c:168:93:168:95 | ret | +| ssa_stress.c:168:93:168:100 | SSA definition | SSA def(ret) | ssa_stress.c:169:3:169:5 | ret | +| ssa_stress.c:169:3:169:10 | SSA definition | SSA def(ret) | ssa_stress.c:169:13:169:15 | ret | +| ssa_stress.c:169:13:169:20 | SSA definition | SSA def(ret) | ssa_stress.c:169:23:169:25 | ret | +| ssa_stress.c:169:23:169:30 | SSA definition | SSA def(ret) | ssa_stress.c:169:33:169:35 | ret | +| ssa_stress.c:169:33:169:40 | SSA definition | SSA def(ret) | ssa_stress.c:169:43:169:45 | ret | +| ssa_stress.c:169:43:169:50 | SSA definition | SSA def(ret) | ssa_stress.c:169:53:169:55 | ret | +| ssa_stress.c:169:53:169:60 | SSA definition | SSA def(ret) | ssa_stress.c:169:63:169:65 | ret | +| ssa_stress.c:169:63:169:70 | SSA definition | SSA def(ret) | ssa_stress.c:169:73:169:75 | ret | +| ssa_stress.c:169:73:169:80 | SSA definition | SSA def(ret) | ssa_stress.c:169:83:169:85 | ret | +| ssa_stress.c:169:83:169:90 | SSA definition | SSA def(ret) | ssa_stress.c:169:93:169:95 | ret | +| ssa_stress.c:169:93:169:100 | SSA definition | SSA def(ret) | ssa_stress.c:170:3:170:5 | ret | +| ssa_stress.c:170:3:170:10 | SSA definition | SSA def(ret) | ssa_stress.c:170:13:170:15 | ret | +| ssa_stress.c:170:13:170:20 | SSA definition | SSA def(ret) | ssa_stress.c:170:23:170:25 | ret | +| ssa_stress.c:170:23:170:30 | SSA definition | SSA def(ret) | ssa_stress.c:170:33:170:35 | ret | +| ssa_stress.c:170:33:170:40 | SSA definition | SSA def(ret) | ssa_stress.c:170:43:170:45 | ret | +| ssa_stress.c:170:43:170:50 | SSA definition | SSA def(ret) | ssa_stress.c:170:53:170:55 | ret | +| ssa_stress.c:170:53:170:60 | SSA definition | SSA def(ret) | ssa_stress.c:170:63:170:65 | ret | +| ssa_stress.c:170:63:170:70 | SSA definition | SSA def(ret) | ssa_stress.c:170:73:170:75 | ret | +| ssa_stress.c:170:73:170:80 | SSA definition | SSA def(ret) | ssa_stress.c:170:83:170:85 | ret | +| ssa_stress.c:170:83:170:90 | SSA definition | SSA def(ret) | ssa_stress.c:170:93:170:95 | ret | +| ssa_stress.c:170:93:170:100 | SSA definition | SSA def(ret) | ssa_stress.c:173:3:173:5 | ret | +| ssa_stress.c:173:3:173:10 | SSA definition | SSA def(ret) | ssa_stress.c:173:13:173:15 | ret | +| ssa_stress.c:173:13:173:20 | SSA definition | SSA def(ret) | ssa_stress.c:173:23:173:25 | ret | +| ssa_stress.c:173:23:173:30 | SSA definition | SSA def(ret) | ssa_stress.c:173:33:173:35 | ret | +| ssa_stress.c:173:33:173:40 | SSA definition | SSA def(ret) | ssa_stress.c:173:43:173:45 | ret | +| ssa_stress.c:173:43:173:50 | SSA definition | SSA def(ret) | ssa_stress.c:173:53:173:55 | ret | +| ssa_stress.c:173:53:173:60 | SSA definition | SSA def(ret) | ssa_stress.c:173:63:173:65 | ret | +| ssa_stress.c:173:63:173:70 | SSA definition | SSA def(ret) | ssa_stress.c:173:73:173:75 | ret | +| ssa_stress.c:173:73:173:80 | SSA definition | SSA def(ret) | ssa_stress.c:173:83:173:85 | ret | +| ssa_stress.c:173:83:173:90 | SSA definition | SSA def(ret) | ssa_stress.c:173:93:173:95 | ret | +| ssa_stress.c:173:93:173:100 | SSA definition | SSA def(ret) | ssa_stress.c:174:3:174:5 | ret | +| ssa_stress.c:174:3:174:10 | SSA definition | SSA def(ret) | ssa_stress.c:174:13:174:15 | ret | +| ssa_stress.c:174:13:174:20 | SSA definition | SSA def(ret) | ssa_stress.c:174:23:174:25 | ret | +| ssa_stress.c:174:23:174:30 | SSA definition | SSA def(ret) | ssa_stress.c:174:33:174:35 | ret | +| ssa_stress.c:174:33:174:40 | SSA definition | SSA def(ret) | ssa_stress.c:174:43:174:45 | ret | +| ssa_stress.c:174:43:174:50 | SSA definition | SSA def(ret) | ssa_stress.c:174:53:174:55 | ret | +| ssa_stress.c:174:53:174:60 | SSA definition | SSA def(ret) | ssa_stress.c:174:63:174:65 | ret | +| ssa_stress.c:174:63:174:70 | SSA definition | SSA def(ret) | ssa_stress.c:174:73:174:75 | ret | +| ssa_stress.c:174:73:174:80 | SSA definition | SSA def(ret) | ssa_stress.c:174:83:174:85 | ret | +| ssa_stress.c:174:83:174:90 | SSA definition | SSA def(ret) | ssa_stress.c:174:93:174:95 | ret | +| ssa_stress.c:174:93:174:100 | SSA definition | SSA def(ret) | ssa_stress.c:175:3:175:5 | ret | +| ssa_stress.c:175:3:175:10 | SSA definition | SSA def(ret) | ssa_stress.c:175:13:175:15 | ret | +| ssa_stress.c:175:13:175:20 | SSA definition | SSA def(ret) | ssa_stress.c:175:23:175:25 | ret | +| ssa_stress.c:175:23:175:30 | SSA definition | SSA def(ret) | ssa_stress.c:175:33:175:35 | ret | +| ssa_stress.c:175:33:175:40 | SSA definition | SSA def(ret) | ssa_stress.c:175:43:175:45 | ret | +| ssa_stress.c:175:43:175:50 | SSA definition | SSA def(ret) | ssa_stress.c:175:53:175:55 | ret | +| ssa_stress.c:175:53:175:60 | SSA definition | SSA def(ret) | ssa_stress.c:175:63:175:65 | ret | +| ssa_stress.c:175:63:175:70 | SSA definition | SSA def(ret) | ssa_stress.c:175:73:175:75 | ret | +| ssa_stress.c:175:73:175:80 | SSA definition | SSA def(ret) | ssa_stress.c:175:83:175:85 | ret | +| ssa_stress.c:175:83:175:90 | SSA definition | SSA def(ret) | ssa_stress.c:175:93:175:95 | ret | +| ssa_stress.c:175:93:175:100 | SSA definition | SSA def(ret) | ssa_stress.c:176:3:176:5 | ret | +| ssa_stress.c:176:3:176:10 | SSA definition | SSA def(ret) | ssa_stress.c:176:13:176:15 | ret | +| ssa_stress.c:176:13:176:20 | SSA definition | SSA def(ret) | ssa_stress.c:176:23:176:25 | ret | +| ssa_stress.c:176:23:176:30 | SSA definition | SSA def(ret) | ssa_stress.c:176:33:176:35 | ret | +| ssa_stress.c:176:33:176:40 | SSA definition | SSA def(ret) | ssa_stress.c:176:43:176:45 | ret | +| ssa_stress.c:176:43:176:50 | SSA definition | SSA def(ret) | ssa_stress.c:176:53:176:55 | ret | +| ssa_stress.c:176:53:176:60 | SSA definition | SSA def(ret) | ssa_stress.c:176:63:176:65 | ret | +| ssa_stress.c:176:63:176:70 | SSA definition | SSA def(ret) | ssa_stress.c:176:73:176:75 | ret | +| ssa_stress.c:176:73:176:80 | SSA definition | SSA def(ret) | ssa_stress.c:176:83:176:85 | ret | +| ssa_stress.c:176:83:176:90 | SSA definition | SSA def(ret) | ssa_stress.c:176:93:176:95 | ret | +| ssa_stress.c:176:93:176:100 | SSA definition | SSA def(ret) | ssa_stress.c:177:3:177:5 | ret | +| ssa_stress.c:177:3:177:10 | SSA definition | SSA def(ret) | ssa_stress.c:177:13:177:15 | ret | +| ssa_stress.c:177:13:177:20 | SSA definition | SSA def(ret) | ssa_stress.c:177:23:177:25 | ret | +| ssa_stress.c:177:23:177:30 | SSA definition | SSA def(ret) | ssa_stress.c:177:33:177:35 | ret | +| ssa_stress.c:177:33:177:40 | SSA definition | SSA def(ret) | ssa_stress.c:177:43:177:45 | ret | +| ssa_stress.c:177:43:177:50 | SSA definition | SSA def(ret) | ssa_stress.c:177:53:177:55 | ret | +| ssa_stress.c:177:53:177:60 | SSA definition | SSA def(ret) | ssa_stress.c:177:63:177:65 | ret | +| ssa_stress.c:177:63:177:70 | SSA definition | SSA def(ret) | ssa_stress.c:177:73:177:75 | ret | +| ssa_stress.c:177:73:177:80 | SSA definition | SSA def(ret) | ssa_stress.c:177:83:177:85 | ret | +| ssa_stress.c:177:83:177:90 | SSA definition | SSA def(ret) | ssa_stress.c:177:93:177:95 | ret | +| ssa_stress.c:177:93:177:100 | SSA definition | SSA def(ret) | ssa_stress.c:178:3:178:5 | ret | +| ssa_stress.c:178:3:178:10 | SSA definition | SSA def(ret) | ssa_stress.c:178:13:178:15 | ret | +| ssa_stress.c:178:13:178:20 | SSA definition | SSA def(ret) | ssa_stress.c:178:23:178:25 | ret | +| ssa_stress.c:178:23:178:30 | SSA definition | SSA def(ret) | ssa_stress.c:178:33:178:35 | ret | +| ssa_stress.c:178:33:178:40 | SSA definition | SSA def(ret) | ssa_stress.c:178:43:178:45 | ret | +| ssa_stress.c:178:43:178:50 | SSA definition | SSA def(ret) | ssa_stress.c:178:53:178:55 | ret | +| ssa_stress.c:178:53:178:60 | SSA definition | SSA def(ret) | ssa_stress.c:178:63:178:65 | ret | +| ssa_stress.c:178:63:178:70 | SSA definition | SSA def(ret) | ssa_stress.c:178:73:178:75 | ret | +| ssa_stress.c:178:73:178:80 | SSA definition | SSA def(ret) | ssa_stress.c:178:83:178:85 | ret | +| ssa_stress.c:178:83:178:90 | SSA definition | SSA def(ret) | ssa_stress.c:178:93:178:95 | ret | +| ssa_stress.c:178:93:178:100 | SSA definition | SSA def(ret) | ssa_stress.c:179:3:179:5 | ret | +| ssa_stress.c:179:3:179:10 | SSA definition | SSA def(ret) | ssa_stress.c:179:13:179:15 | ret | +| ssa_stress.c:179:13:179:20 | SSA definition | SSA def(ret) | ssa_stress.c:179:23:179:25 | ret | +| ssa_stress.c:179:23:179:30 | SSA definition | SSA def(ret) | ssa_stress.c:179:33:179:35 | ret | +| ssa_stress.c:179:33:179:40 | SSA definition | SSA def(ret) | ssa_stress.c:179:43:179:45 | ret | +| ssa_stress.c:179:43:179:50 | SSA definition | SSA def(ret) | ssa_stress.c:179:53:179:55 | ret | +| ssa_stress.c:179:53:179:60 | SSA definition | SSA def(ret) | ssa_stress.c:179:63:179:65 | ret | +| ssa_stress.c:179:63:179:70 | SSA definition | SSA def(ret) | ssa_stress.c:179:73:179:75 | ret | +| ssa_stress.c:179:73:179:80 | SSA definition | SSA def(ret) | ssa_stress.c:179:83:179:85 | ret | +| ssa_stress.c:179:83:179:90 | SSA definition | SSA def(ret) | ssa_stress.c:179:93:179:95 | ret | +| ssa_stress.c:179:93:179:100 | SSA definition | SSA def(ret) | ssa_stress.c:180:3:180:5 | ret | +| ssa_stress.c:180:3:180:10 | SSA definition | SSA def(ret) | ssa_stress.c:180:13:180:15 | ret | +| ssa_stress.c:180:13:180:20 | SSA definition | SSA def(ret) | ssa_stress.c:180:23:180:25 | ret | +| ssa_stress.c:180:23:180:30 | SSA definition | SSA def(ret) | ssa_stress.c:180:33:180:35 | ret | +| ssa_stress.c:180:33:180:40 | SSA definition | SSA def(ret) | ssa_stress.c:180:43:180:45 | ret | +| ssa_stress.c:180:43:180:50 | SSA definition | SSA def(ret) | ssa_stress.c:180:53:180:55 | ret | +| ssa_stress.c:180:53:180:60 | SSA definition | SSA def(ret) | ssa_stress.c:180:63:180:65 | ret | +| ssa_stress.c:180:63:180:70 | SSA definition | SSA def(ret) | ssa_stress.c:180:73:180:75 | ret | +| ssa_stress.c:180:73:180:80 | SSA definition | SSA def(ret) | ssa_stress.c:180:83:180:85 | ret | +| ssa_stress.c:180:83:180:90 | SSA definition | SSA def(ret) | ssa_stress.c:180:93:180:95 | ret | +| ssa_stress.c:180:93:180:100 | SSA definition | SSA def(ret) | ssa_stress.c:181:3:181:5 | ret | +| ssa_stress.c:181:3:181:10 | SSA definition | SSA def(ret) | ssa_stress.c:181:13:181:15 | ret | +| ssa_stress.c:181:13:181:20 | SSA definition | SSA def(ret) | ssa_stress.c:181:23:181:25 | ret | +| ssa_stress.c:181:23:181:30 | SSA definition | SSA def(ret) | ssa_stress.c:181:33:181:35 | ret | +| ssa_stress.c:181:33:181:40 | SSA definition | SSA def(ret) | ssa_stress.c:181:43:181:45 | ret | +| ssa_stress.c:181:43:181:50 | SSA definition | SSA def(ret) | ssa_stress.c:181:53:181:55 | ret | +| ssa_stress.c:181:53:181:60 | SSA definition | SSA def(ret) | ssa_stress.c:181:63:181:65 | ret | +| ssa_stress.c:181:63:181:70 | SSA definition | SSA def(ret) | ssa_stress.c:181:73:181:75 | ret | +| ssa_stress.c:181:73:181:80 | SSA definition | SSA def(ret) | ssa_stress.c:181:83:181:85 | ret | +| ssa_stress.c:181:83:181:90 | SSA definition | SSA def(ret) | ssa_stress.c:181:93:181:95 | ret | +| ssa_stress.c:181:93:181:100 | SSA definition | SSA def(ret) | ssa_stress.c:182:3:182:5 | ret | +| ssa_stress.c:182:3:182:10 | SSA definition | SSA def(ret) | ssa_stress.c:182:13:182:15 | ret | +| ssa_stress.c:182:13:182:20 | SSA definition | SSA def(ret) | ssa_stress.c:182:23:182:25 | ret | +| ssa_stress.c:182:23:182:30 | SSA definition | SSA def(ret) | ssa_stress.c:182:33:182:35 | ret | +| ssa_stress.c:182:33:182:40 | SSA definition | SSA def(ret) | ssa_stress.c:182:43:182:45 | ret | +| ssa_stress.c:182:43:182:50 | SSA definition | SSA def(ret) | ssa_stress.c:182:53:182:55 | ret | +| ssa_stress.c:182:53:182:60 | SSA definition | SSA def(ret) | ssa_stress.c:182:63:182:65 | ret | +| ssa_stress.c:182:63:182:70 | SSA definition | SSA def(ret) | ssa_stress.c:182:73:182:75 | ret | +| ssa_stress.c:182:73:182:80 | SSA definition | SSA def(ret) | ssa_stress.c:182:83:182:85 | ret | +| ssa_stress.c:182:83:182:90 | SSA definition | SSA def(ret) | ssa_stress.c:182:93:182:95 | ret | +| ssa_stress.c:182:93:182:100 | SSA definition | SSA def(ret) | ssa_stress.c:185:3:185:5 | ret | +| ssa_stress.c:185:3:185:10 | SSA definition | SSA def(ret) | ssa_stress.c:185:13:185:15 | ret | +| ssa_stress.c:185:13:185:20 | SSA definition | SSA def(ret) | ssa_stress.c:185:23:185:25 | ret | +| ssa_stress.c:185:23:185:30 | SSA definition | SSA def(ret) | ssa_stress.c:185:33:185:35 | ret | +| ssa_stress.c:185:33:185:40 | SSA definition | SSA def(ret) | ssa_stress.c:185:43:185:45 | ret | +| ssa_stress.c:185:43:185:50 | SSA definition | SSA def(ret) | ssa_stress.c:185:53:185:55 | ret | +| ssa_stress.c:185:53:185:60 | SSA definition | SSA def(ret) | ssa_stress.c:185:63:185:65 | ret | +| ssa_stress.c:185:63:185:70 | SSA definition | SSA def(ret) | ssa_stress.c:185:73:185:75 | ret | +| ssa_stress.c:185:73:185:80 | SSA definition | SSA def(ret) | ssa_stress.c:185:83:185:85 | ret | +| ssa_stress.c:185:83:185:90 | SSA definition | SSA def(ret) | ssa_stress.c:185:93:185:95 | ret | +| ssa_stress.c:185:93:185:100 | SSA definition | SSA def(ret) | ssa_stress.c:186:3:186:5 | ret | +| ssa_stress.c:186:3:186:10 | SSA definition | SSA def(ret) | ssa_stress.c:186:13:186:15 | ret | +| ssa_stress.c:186:13:186:20 | SSA definition | SSA def(ret) | ssa_stress.c:186:23:186:25 | ret | +| ssa_stress.c:186:23:186:30 | SSA definition | SSA def(ret) | ssa_stress.c:186:33:186:35 | ret | +| ssa_stress.c:186:33:186:40 | SSA definition | SSA def(ret) | ssa_stress.c:186:43:186:45 | ret | +| ssa_stress.c:186:43:186:50 | SSA definition | SSA def(ret) | ssa_stress.c:186:53:186:55 | ret | +| ssa_stress.c:186:53:186:60 | SSA definition | SSA def(ret) | ssa_stress.c:186:63:186:65 | ret | +| ssa_stress.c:186:63:186:70 | SSA definition | SSA def(ret) | ssa_stress.c:186:73:186:75 | ret | +| ssa_stress.c:186:73:186:80 | SSA definition | SSA def(ret) | ssa_stress.c:186:83:186:85 | ret | +| ssa_stress.c:186:83:186:90 | SSA definition | SSA def(ret) | ssa_stress.c:186:93:186:95 | ret | +| ssa_stress.c:186:93:186:100 | SSA definition | SSA def(ret) | ssa_stress.c:187:3:187:5 | ret | +| ssa_stress.c:187:3:187:10 | SSA definition | SSA def(ret) | ssa_stress.c:187:13:187:15 | ret | +| ssa_stress.c:187:13:187:20 | SSA definition | SSA def(ret) | ssa_stress.c:187:23:187:25 | ret | +| ssa_stress.c:187:23:187:30 | SSA definition | SSA def(ret) | ssa_stress.c:187:33:187:35 | ret | +| ssa_stress.c:187:33:187:40 | SSA definition | SSA def(ret) | ssa_stress.c:187:43:187:45 | ret | +| ssa_stress.c:187:43:187:50 | SSA definition | SSA def(ret) | ssa_stress.c:187:53:187:55 | ret | +| ssa_stress.c:187:53:187:60 | SSA definition | SSA def(ret) | ssa_stress.c:187:63:187:65 | ret | +| ssa_stress.c:187:63:187:70 | SSA definition | SSA def(ret) | ssa_stress.c:187:73:187:75 | ret | +| ssa_stress.c:187:73:187:80 | SSA definition | SSA def(ret) | ssa_stress.c:187:83:187:85 | ret | +| ssa_stress.c:187:83:187:90 | SSA definition | SSA def(ret) | ssa_stress.c:187:93:187:95 | ret | +| ssa_stress.c:187:93:187:100 | SSA definition | SSA def(ret) | ssa_stress.c:188:3:188:5 | ret | +| ssa_stress.c:188:3:188:10 | SSA definition | SSA def(ret) | ssa_stress.c:188:13:188:15 | ret | +| ssa_stress.c:188:13:188:20 | SSA definition | SSA def(ret) | ssa_stress.c:188:23:188:25 | ret | +| ssa_stress.c:188:23:188:30 | SSA definition | SSA def(ret) | ssa_stress.c:188:33:188:35 | ret | +| ssa_stress.c:188:33:188:40 | SSA definition | SSA def(ret) | ssa_stress.c:188:43:188:45 | ret | +| ssa_stress.c:188:43:188:50 | SSA definition | SSA def(ret) | ssa_stress.c:188:53:188:55 | ret | +| ssa_stress.c:188:53:188:60 | SSA definition | SSA def(ret) | ssa_stress.c:188:63:188:65 | ret | +| ssa_stress.c:188:63:188:70 | SSA definition | SSA def(ret) | ssa_stress.c:188:73:188:75 | ret | +| ssa_stress.c:188:73:188:80 | SSA definition | SSA def(ret) | ssa_stress.c:188:83:188:85 | ret | +| ssa_stress.c:188:83:188:90 | SSA definition | SSA def(ret) | ssa_stress.c:188:93:188:95 | ret | +| ssa_stress.c:188:93:188:100 | SSA definition | SSA def(ret) | ssa_stress.c:189:3:189:5 | ret | +| ssa_stress.c:189:3:189:10 | SSA definition | SSA def(ret) | ssa_stress.c:189:13:189:15 | ret | +| ssa_stress.c:189:13:189:20 | SSA definition | SSA def(ret) | ssa_stress.c:189:23:189:25 | ret | +| ssa_stress.c:189:23:189:30 | SSA definition | SSA def(ret) | ssa_stress.c:189:33:189:35 | ret | +| ssa_stress.c:189:33:189:40 | SSA definition | SSA def(ret) | ssa_stress.c:189:43:189:45 | ret | +| ssa_stress.c:189:43:189:50 | SSA definition | SSA def(ret) | ssa_stress.c:189:53:189:55 | ret | +| ssa_stress.c:189:53:189:60 | SSA definition | SSA def(ret) | ssa_stress.c:189:63:189:65 | ret | +| ssa_stress.c:189:63:189:70 | SSA definition | SSA def(ret) | ssa_stress.c:189:73:189:75 | ret | +| ssa_stress.c:189:73:189:80 | SSA definition | SSA def(ret) | ssa_stress.c:189:83:189:85 | ret | +| ssa_stress.c:189:83:189:90 | SSA definition | SSA def(ret) | ssa_stress.c:189:93:189:95 | ret | +| ssa_stress.c:189:93:189:100 | SSA definition | SSA def(ret) | ssa_stress.c:190:3:190:5 | ret | +| ssa_stress.c:190:3:190:10 | SSA definition | SSA def(ret) | ssa_stress.c:190:13:190:15 | ret | +| ssa_stress.c:190:13:190:20 | SSA definition | SSA def(ret) | ssa_stress.c:190:23:190:25 | ret | +| ssa_stress.c:190:23:190:30 | SSA definition | SSA def(ret) | ssa_stress.c:190:33:190:35 | ret | +| ssa_stress.c:190:33:190:40 | SSA definition | SSA def(ret) | ssa_stress.c:190:43:190:45 | ret | +| ssa_stress.c:190:43:190:50 | SSA definition | SSA def(ret) | ssa_stress.c:190:53:190:55 | ret | +| ssa_stress.c:190:53:190:60 | SSA definition | SSA def(ret) | ssa_stress.c:190:63:190:65 | ret | +| ssa_stress.c:190:63:190:70 | SSA definition | SSA def(ret) | ssa_stress.c:190:73:190:75 | ret | +| ssa_stress.c:190:73:190:80 | SSA definition | SSA def(ret) | ssa_stress.c:190:83:190:85 | ret | +| ssa_stress.c:190:83:190:90 | SSA definition | SSA def(ret) | ssa_stress.c:190:93:190:95 | ret | +| ssa_stress.c:190:93:190:100 | SSA definition | SSA def(ret) | ssa_stress.c:191:3:191:5 | ret | +| ssa_stress.c:191:3:191:10 | SSA definition | SSA def(ret) | ssa_stress.c:191:13:191:15 | ret | +| ssa_stress.c:191:13:191:20 | SSA definition | SSA def(ret) | ssa_stress.c:191:23:191:25 | ret | +| ssa_stress.c:191:23:191:30 | SSA definition | SSA def(ret) | ssa_stress.c:191:33:191:35 | ret | +| ssa_stress.c:191:33:191:40 | SSA definition | SSA def(ret) | ssa_stress.c:191:43:191:45 | ret | +| ssa_stress.c:191:43:191:50 | SSA definition | SSA def(ret) | ssa_stress.c:191:53:191:55 | ret | +| ssa_stress.c:191:53:191:60 | SSA definition | SSA def(ret) | ssa_stress.c:191:63:191:65 | ret | +| ssa_stress.c:191:63:191:70 | SSA definition | SSA def(ret) | ssa_stress.c:191:73:191:75 | ret | +| ssa_stress.c:191:73:191:80 | SSA definition | SSA def(ret) | ssa_stress.c:191:83:191:85 | ret | +| ssa_stress.c:191:83:191:90 | SSA definition | SSA def(ret) | ssa_stress.c:191:93:191:95 | ret | +| ssa_stress.c:191:93:191:100 | SSA definition | SSA def(ret) | ssa_stress.c:192:3:192:5 | ret | +| ssa_stress.c:192:3:192:10 | SSA definition | SSA def(ret) | ssa_stress.c:192:13:192:15 | ret | +| ssa_stress.c:192:13:192:20 | SSA definition | SSA def(ret) | ssa_stress.c:192:23:192:25 | ret | +| ssa_stress.c:192:23:192:30 | SSA definition | SSA def(ret) | ssa_stress.c:192:33:192:35 | ret | +| ssa_stress.c:192:33:192:40 | SSA definition | SSA def(ret) | ssa_stress.c:192:43:192:45 | ret | +| ssa_stress.c:192:43:192:50 | SSA definition | SSA def(ret) | ssa_stress.c:192:53:192:55 | ret | +| ssa_stress.c:192:53:192:60 | SSA definition | SSA def(ret) | ssa_stress.c:192:63:192:65 | ret | +| ssa_stress.c:192:63:192:70 | SSA definition | SSA def(ret) | ssa_stress.c:192:73:192:75 | ret | +| ssa_stress.c:192:73:192:80 | SSA definition | SSA def(ret) | ssa_stress.c:192:83:192:85 | ret | +| ssa_stress.c:192:83:192:90 | SSA definition | SSA def(ret) | ssa_stress.c:192:93:192:95 | ret | +| ssa_stress.c:192:93:192:100 | SSA definition | SSA def(ret) | ssa_stress.c:193:3:193:5 | ret | +| ssa_stress.c:193:3:193:10 | SSA definition | SSA def(ret) | ssa_stress.c:193:13:193:15 | ret | +| ssa_stress.c:193:13:193:20 | SSA definition | SSA def(ret) | ssa_stress.c:193:23:193:25 | ret | +| ssa_stress.c:193:23:193:30 | SSA definition | SSA def(ret) | ssa_stress.c:193:33:193:35 | ret | +| ssa_stress.c:193:33:193:40 | SSA definition | SSA def(ret) | ssa_stress.c:193:43:193:45 | ret | +| ssa_stress.c:193:43:193:50 | SSA definition | SSA def(ret) | ssa_stress.c:193:53:193:55 | ret | +| ssa_stress.c:193:53:193:60 | SSA definition | SSA def(ret) | ssa_stress.c:193:63:193:65 | ret | +| ssa_stress.c:193:63:193:70 | SSA definition | SSA def(ret) | ssa_stress.c:193:73:193:75 | ret | +| ssa_stress.c:193:73:193:80 | SSA definition | SSA def(ret) | ssa_stress.c:193:83:193:85 | ret | +| ssa_stress.c:193:83:193:90 | SSA definition | SSA def(ret) | ssa_stress.c:193:93:193:95 | ret | +| ssa_stress.c:193:93:193:100 | SSA definition | SSA def(ret) | ssa_stress.c:194:3:194:5 | ret | +| ssa_stress.c:194:3:194:10 | SSA definition | SSA def(ret) | ssa_stress.c:194:13:194:15 | ret | +| ssa_stress.c:194:13:194:20 | SSA definition | SSA def(ret) | ssa_stress.c:194:23:194:25 | ret | +| ssa_stress.c:194:23:194:30 | SSA definition | SSA def(ret) | ssa_stress.c:194:33:194:35 | ret | +| ssa_stress.c:194:33:194:40 | SSA definition | SSA def(ret) | ssa_stress.c:194:43:194:45 | ret | +| ssa_stress.c:194:43:194:50 | SSA definition | SSA def(ret) | ssa_stress.c:194:53:194:55 | ret | +| ssa_stress.c:194:53:194:60 | SSA definition | SSA def(ret) | ssa_stress.c:194:63:194:65 | ret | +| ssa_stress.c:194:63:194:70 | SSA definition | SSA def(ret) | ssa_stress.c:194:73:194:75 | ret | +| ssa_stress.c:194:73:194:80 | SSA definition | SSA def(ret) | ssa_stress.c:194:83:194:85 | ret | +| ssa_stress.c:194:83:194:90 | SSA definition | SSA def(ret) | ssa_stress.c:194:93:194:95 | ret | +| ssa_stress.c:194:93:194:100 | SSA definition | SSA def(ret) | ssa_stress.c:197:3:197:5 | ret | +| ssa_stress.c:197:3:197:10 | SSA definition | SSA def(ret) | ssa_stress.c:197:13:197:15 | ret | +| ssa_stress.c:197:13:197:20 | SSA definition | SSA def(ret) | ssa_stress.c:197:23:197:25 | ret | +| ssa_stress.c:197:23:197:30 | SSA definition | SSA def(ret) | ssa_stress.c:197:33:197:35 | ret | +| ssa_stress.c:197:33:197:40 | SSA definition | SSA def(ret) | ssa_stress.c:197:43:197:45 | ret | +| ssa_stress.c:197:43:197:50 | SSA definition | SSA def(ret) | ssa_stress.c:197:53:197:55 | ret | +| ssa_stress.c:197:53:197:60 | SSA definition | SSA def(ret) | ssa_stress.c:197:63:197:65 | ret | +| ssa_stress.c:197:63:197:70 | SSA definition | SSA def(ret) | ssa_stress.c:197:73:197:75 | ret | +| ssa_stress.c:197:73:197:80 | SSA definition | SSA def(ret) | ssa_stress.c:197:83:197:85 | ret | +| ssa_stress.c:197:83:197:90 | SSA definition | SSA def(ret) | ssa_stress.c:197:93:197:95 | ret | +| ssa_stress.c:197:93:197:100 | SSA definition | SSA def(ret) | ssa_stress.c:198:3:198:5 | ret | +| ssa_stress.c:198:3:198:10 | SSA definition | SSA def(ret) | ssa_stress.c:198:13:198:15 | ret | +| ssa_stress.c:198:13:198:20 | SSA definition | SSA def(ret) | ssa_stress.c:198:23:198:25 | ret | +| ssa_stress.c:198:23:198:30 | SSA definition | SSA def(ret) | ssa_stress.c:198:33:198:35 | ret | +| ssa_stress.c:198:33:198:40 | SSA definition | SSA def(ret) | ssa_stress.c:198:43:198:45 | ret | +| ssa_stress.c:198:43:198:50 | SSA definition | SSA def(ret) | ssa_stress.c:198:53:198:55 | ret | +| ssa_stress.c:198:53:198:60 | SSA definition | SSA def(ret) | ssa_stress.c:198:63:198:65 | ret | +| ssa_stress.c:198:63:198:70 | SSA definition | SSA def(ret) | ssa_stress.c:198:73:198:75 | ret | +| ssa_stress.c:198:73:198:80 | SSA definition | SSA def(ret) | ssa_stress.c:198:83:198:85 | ret | +| ssa_stress.c:198:83:198:90 | SSA definition | SSA def(ret) | ssa_stress.c:198:93:198:95 | ret | +| ssa_stress.c:198:93:198:100 | SSA definition | SSA def(ret) | ssa_stress.c:199:3:199:5 | ret | +| ssa_stress.c:199:3:199:10 | SSA definition | SSA def(ret) | ssa_stress.c:199:13:199:15 | ret | +| ssa_stress.c:199:13:199:20 | SSA definition | SSA def(ret) | ssa_stress.c:199:23:199:25 | ret | +| ssa_stress.c:199:23:199:30 | SSA definition | SSA def(ret) | ssa_stress.c:199:33:199:35 | ret | +| ssa_stress.c:199:33:199:40 | SSA definition | SSA def(ret) | ssa_stress.c:199:43:199:45 | ret | +| ssa_stress.c:199:43:199:50 | SSA definition | SSA def(ret) | ssa_stress.c:199:53:199:55 | ret | +| ssa_stress.c:199:53:199:60 | SSA definition | SSA def(ret) | ssa_stress.c:199:63:199:65 | ret | +| ssa_stress.c:199:63:199:70 | SSA definition | SSA def(ret) | ssa_stress.c:199:73:199:75 | ret | +| ssa_stress.c:199:73:199:80 | SSA definition | SSA def(ret) | ssa_stress.c:199:83:199:85 | ret | +| ssa_stress.c:199:83:199:90 | SSA definition | SSA def(ret) | ssa_stress.c:199:93:199:95 | ret | +| ssa_stress.c:199:93:199:100 | SSA definition | SSA def(ret) | ssa_stress.c:200:3:200:5 | ret | +| ssa_stress.c:200:3:200:10 | SSA definition | SSA def(ret) | ssa_stress.c:200:13:200:15 | ret | +| ssa_stress.c:200:13:200:20 | SSA definition | SSA def(ret) | ssa_stress.c:200:23:200:25 | ret | +| ssa_stress.c:200:23:200:30 | SSA definition | SSA def(ret) | ssa_stress.c:200:33:200:35 | ret | +| ssa_stress.c:200:33:200:40 | SSA definition | SSA def(ret) | ssa_stress.c:200:43:200:45 | ret | +| ssa_stress.c:200:43:200:50 | SSA definition | SSA def(ret) | ssa_stress.c:200:53:200:55 | ret | +| ssa_stress.c:200:53:200:60 | SSA definition | SSA def(ret) | ssa_stress.c:200:63:200:65 | ret | +| ssa_stress.c:200:63:200:70 | SSA definition | SSA def(ret) | ssa_stress.c:200:73:200:75 | ret | +| ssa_stress.c:200:73:200:80 | SSA definition | SSA def(ret) | ssa_stress.c:200:83:200:85 | ret | +| ssa_stress.c:200:83:200:90 | SSA definition | SSA def(ret) | ssa_stress.c:200:93:200:95 | ret | +| ssa_stress.c:200:93:200:100 | SSA definition | SSA def(ret) | ssa_stress.c:201:3:201:5 | ret | +| ssa_stress.c:201:3:201:10 | SSA definition | SSA def(ret) | ssa_stress.c:201:13:201:15 | ret | +| ssa_stress.c:201:13:201:20 | SSA definition | SSA def(ret) | ssa_stress.c:201:23:201:25 | ret | +| ssa_stress.c:201:23:201:30 | SSA definition | SSA def(ret) | ssa_stress.c:201:33:201:35 | ret | +| ssa_stress.c:201:33:201:40 | SSA definition | SSA def(ret) | ssa_stress.c:201:43:201:45 | ret | +| ssa_stress.c:201:43:201:50 | SSA definition | SSA def(ret) | ssa_stress.c:201:53:201:55 | ret | +| ssa_stress.c:201:53:201:60 | SSA definition | SSA def(ret) | ssa_stress.c:201:63:201:65 | ret | +| ssa_stress.c:201:63:201:70 | SSA definition | SSA def(ret) | ssa_stress.c:201:73:201:75 | ret | +| ssa_stress.c:201:73:201:80 | SSA definition | SSA def(ret) | ssa_stress.c:201:83:201:85 | ret | +| ssa_stress.c:201:83:201:90 | SSA definition | SSA def(ret) | ssa_stress.c:201:93:201:95 | ret | +| ssa_stress.c:201:93:201:100 | SSA definition | SSA def(ret) | ssa_stress.c:202:3:202:5 | ret | +| ssa_stress.c:202:3:202:10 | SSA definition | SSA def(ret) | ssa_stress.c:202:13:202:15 | ret | +| ssa_stress.c:202:13:202:20 | SSA definition | SSA def(ret) | ssa_stress.c:202:23:202:25 | ret | +| ssa_stress.c:202:23:202:30 | SSA definition | SSA def(ret) | ssa_stress.c:202:33:202:35 | ret | +| ssa_stress.c:202:33:202:40 | SSA definition | SSA def(ret) | ssa_stress.c:202:43:202:45 | ret | +| ssa_stress.c:202:43:202:50 | SSA definition | SSA def(ret) | ssa_stress.c:202:53:202:55 | ret | +| ssa_stress.c:202:53:202:60 | SSA definition | SSA def(ret) | ssa_stress.c:202:63:202:65 | ret | +| ssa_stress.c:202:63:202:70 | SSA definition | SSA def(ret) | ssa_stress.c:202:73:202:75 | ret | +| ssa_stress.c:202:73:202:80 | SSA definition | SSA def(ret) | ssa_stress.c:202:83:202:85 | ret | +| ssa_stress.c:202:83:202:90 | SSA definition | SSA def(ret) | ssa_stress.c:202:93:202:95 | ret | +| ssa_stress.c:202:93:202:100 | SSA definition | SSA def(ret) | ssa_stress.c:203:3:203:5 | ret | +| ssa_stress.c:203:3:203:10 | SSA definition | SSA def(ret) | ssa_stress.c:203:13:203:15 | ret | +| ssa_stress.c:203:13:203:20 | SSA definition | SSA def(ret) | ssa_stress.c:203:23:203:25 | ret | +| ssa_stress.c:203:23:203:30 | SSA definition | SSA def(ret) | ssa_stress.c:203:33:203:35 | ret | +| ssa_stress.c:203:33:203:40 | SSA definition | SSA def(ret) | ssa_stress.c:203:43:203:45 | ret | +| ssa_stress.c:203:43:203:50 | SSA definition | SSA def(ret) | ssa_stress.c:203:53:203:55 | ret | +| ssa_stress.c:203:53:203:60 | SSA definition | SSA def(ret) | ssa_stress.c:203:63:203:65 | ret | +| ssa_stress.c:203:63:203:70 | SSA definition | SSA def(ret) | ssa_stress.c:203:73:203:75 | ret | +| ssa_stress.c:203:73:203:80 | SSA definition | SSA def(ret) | ssa_stress.c:203:83:203:85 | ret | +| ssa_stress.c:203:83:203:90 | SSA definition | SSA def(ret) | ssa_stress.c:203:93:203:95 | ret | +| ssa_stress.c:203:93:203:100 | SSA definition | SSA def(ret) | ssa_stress.c:204:3:204:5 | ret | +| ssa_stress.c:204:3:204:10 | SSA definition | SSA def(ret) | ssa_stress.c:204:13:204:15 | ret | +| ssa_stress.c:204:13:204:20 | SSA definition | SSA def(ret) | ssa_stress.c:204:23:204:25 | ret | +| ssa_stress.c:204:23:204:30 | SSA definition | SSA def(ret) | ssa_stress.c:204:33:204:35 | ret | +| ssa_stress.c:204:33:204:40 | SSA definition | SSA def(ret) | ssa_stress.c:204:43:204:45 | ret | +| ssa_stress.c:204:43:204:50 | SSA definition | SSA def(ret) | ssa_stress.c:204:53:204:55 | ret | +| ssa_stress.c:204:53:204:60 | SSA definition | SSA def(ret) | ssa_stress.c:204:63:204:65 | ret | +| ssa_stress.c:204:63:204:70 | SSA definition | SSA def(ret) | ssa_stress.c:204:73:204:75 | ret | +| ssa_stress.c:204:73:204:80 | SSA definition | SSA def(ret) | ssa_stress.c:204:83:204:85 | ret | +| ssa_stress.c:204:83:204:90 | SSA definition | SSA def(ret) | ssa_stress.c:204:93:204:95 | ret | +| ssa_stress.c:204:93:204:100 | SSA definition | SSA def(ret) | ssa_stress.c:205:3:205:5 | ret | +| ssa_stress.c:205:3:205:10 | SSA definition | SSA def(ret) | ssa_stress.c:205:13:205:15 | ret | +| ssa_stress.c:205:13:205:20 | SSA definition | SSA def(ret) | ssa_stress.c:205:23:205:25 | ret | +| ssa_stress.c:205:23:205:30 | SSA definition | SSA def(ret) | ssa_stress.c:205:33:205:35 | ret | +| ssa_stress.c:205:33:205:40 | SSA definition | SSA def(ret) | ssa_stress.c:205:43:205:45 | ret | +| ssa_stress.c:205:43:205:50 | SSA definition | SSA def(ret) | ssa_stress.c:205:53:205:55 | ret | +| ssa_stress.c:205:53:205:60 | SSA definition | SSA def(ret) | ssa_stress.c:205:63:205:65 | ret | +| ssa_stress.c:205:63:205:70 | SSA definition | SSA def(ret) | ssa_stress.c:205:73:205:75 | ret | +| ssa_stress.c:205:73:205:80 | SSA definition | SSA def(ret) | ssa_stress.c:205:83:205:85 | ret | +| ssa_stress.c:205:83:205:90 | SSA definition | SSA def(ret) | ssa_stress.c:205:93:205:95 | ret | +| ssa_stress.c:205:93:205:100 | SSA definition | SSA def(ret) | ssa_stress.c:206:3:206:5 | ret | +| ssa_stress.c:206:3:206:10 | SSA definition | SSA def(ret) | ssa_stress.c:206:13:206:15 | ret | +| ssa_stress.c:206:13:206:20 | SSA definition | SSA def(ret) | ssa_stress.c:206:23:206:25 | ret | +| ssa_stress.c:206:23:206:30 | SSA definition | SSA def(ret) | ssa_stress.c:206:33:206:35 | ret | +| ssa_stress.c:206:33:206:40 | SSA definition | SSA def(ret) | ssa_stress.c:206:43:206:45 | ret | +| ssa_stress.c:206:43:206:50 | SSA definition | SSA def(ret) | ssa_stress.c:206:53:206:55 | ret | +| ssa_stress.c:206:53:206:60 | SSA definition | SSA def(ret) | ssa_stress.c:206:63:206:65 | ret | +| ssa_stress.c:206:63:206:70 | SSA definition | SSA def(ret) | ssa_stress.c:206:73:206:75 | ret | +| ssa_stress.c:206:73:206:80 | SSA definition | SSA def(ret) | ssa_stress.c:206:83:206:85 | ret | +| ssa_stress.c:206:83:206:90 | SSA definition | SSA def(ret) | ssa_stress.c:206:93:206:95 | ret | +| ssa_stress.c:206:93:206:100 | SSA definition | SSA def(ret) | ssa_stress.c:209:3:209:5 | ret | +| ssa_stress.c:209:3:209:10 | SSA definition | SSA def(ret) | ssa_stress.c:209:13:209:15 | ret | +| ssa_stress.c:209:13:209:20 | SSA definition | SSA def(ret) | ssa_stress.c:209:23:209:25 | ret | +| ssa_stress.c:209:23:209:30 | SSA definition | SSA def(ret) | ssa_stress.c:209:33:209:35 | ret | +| ssa_stress.c:209:33:209:40 | SSA definition | SSA def(ret) | ssa_stress.c:209:43:209:45 | ret | +| ssa_stress.c:209:43:209:50 | SSA definition | SSA def(ret) | ssa_stress.c:209:53:209:55 | ret | +| ssa_stress.c:209:53:209:60 | SSA definition | SSA def(ret) | ssa_stress.c:209:63:209:65 | ret | +| ssa_stress.c:209:63:209:70 | SSA definition | SSA def(ret) | ssa_stress.c:209:73:209:75 | ret | +| ssa_stress.c:209:73:209:80 | SSA definition | SSA def(ret) | ssa_stress.c:209:83:209:85 | ret | +| ssa_stress.c:209:83:209:90 | SSA definition | SSA def(ret) | ssa_stress.c:209:93:209:95 | ret | +| ssa_stress.c:209:93:209:100 | SSA definition | SSA def(ret) | ssa_stress.c:210:3:210:5 | ret | +| ssa_stress.c:210:3:210:10 | SSA definition | SSA def(ret) | ssa_stress.c:210:13:210:15 | ret | +| ssa_stress.c:210:13:210:20 | SSA definition | SSA def(ret) | ssa_stress.c:210:23:210:25 | ret | +| ssa_stress.c:210:23:210:30 | SSA definition | SSA def(ret) | ssa_stress.c:210:33:210:35 | ret | +| ssa_stress.c:210:33:210:40 | SSA definition | SSA def(ret) | ssa_stress.c:210:43:210:45 | ret | +| ssa_stress.c:210:43:210:50 | SSA definition | SSA def(ret) | ssa_stress.c:210:53:210:55 | ret | +| ssa_stress.c:210:53:210:60 | SSA definition | SSA def(ret) | ssa_stress.c:210:63:210:65 | ret | +| ssa_stress.c:210:63:210:70 | SSA definition | SSA def(ret) | ssa_stress.c:210:73:210:75 | ret | +| ssa_stress.c:210:73:210:80 | SSA definition | SSA def(ret) | ssa_stress.c:210:83:210:85 | ret | +| ssa_stress.c:210:83:210:90 | SSA definition | SSA def(ret) | ssa_stress.c:210:93:210:95 | ret | +| ssa_stress.c:210:93:210:100 | SSA definition | SSA def(ret) | ssa_stress.c:211:3:211:5 | ret | +| ssa_stress.c:211:3:211:10 | SSA definition | SSA def(ret) | ssa_stress.c:211:13:211:15 | ret | +| ssa_stress.c:211:13:211:20 | SSA definition | SSA def(ret) | ssa_stress.c:211:23:211:25 | ret | +| ssa_stress.c:211:23:211:30 | SSA definition | SSA def(ret) | ssa_stress.c:211:33:211:35 | ret | +| ssa_stress.c:211:33:211:40 | SSA definition | SSA def(ret) | ssa_stress.c:211:43:211:45 | ret | +| ssa_stress.c:211:43:211:50 | SSA definition | SSA def(ret) | ssa_stress.c:211:53:211:55 | ret | +| ssa_stress.c:211:53:211:60 | SSA definition | SSA def(ret) | ssa_stress.c:211:63:211:65 | ret | +| ssa_stress.c:211:63:211:70 | SSA definition | SSA def(ret) | ssa_stress.c:211:73:211:75 | ret | +| ssa_stress.c:211:73:211:80 | SSA definition | SSA def(ret) | ssa_stress.c:211:83:211:85 | ret | +| ssa_stress.c:211:83:211:90 | SSA definition | SSA def(ret) | ssa_stress.c:211:93:211:95 | ret | +| ssa_stress.c:211:93:211:100 | SSA definition | SSA def(ret) | ssa_stress.c:212:3:212:5 | ret | +| ssa_stress.c:212:3:212:10 | SSA definition | SSA def(ret) | ssa_stress.c:212:13:212:15 | ret | +| ssa_stress.c:212:13:212:20 | SSA definition | SSA def(ret) | ssa_stress.c:212:23:212:25 | ret | +| ssa_stress.c:212:23:212:30 | SSA definition | SSA def(ret) | ssa_stress.c:212:33:212:35 | ret | +| ssa_stress.c:212:33:212:40 | SSA definition | SSA def(ret) | ssa_stress.c:212:43:212:45 | ret | +| ssa_stress.c:212:43:212:50 | SSA definition | SSA def(ret) | ssa_stress.c:212:53:212:55 | ret | +| ssa_stress.c:212:53:212:60 | SSA definition | SSA def(ret) | ssa_stress.c:212:63:212:65 | ret | +| ssa_stress.c:212:63:212:70 | SSA definition | SSA def(ret) | ssa_stress.c:212:73:212:75 | ret | +| ssa_stress.c:212:73:212:80 | SSA definition | SSA def(ret) | ssa_stress.c:212:83:212:85 | ret | +| ssa_stress.c:212:83:212:90 | SSA definition | SSA def(ret) | ssa_stress.c:212:93:212:95 | ret | +| ssa_stress.c:212:93:212:100 | SSA definition | SSA def(ret) | ssa_stress.c:213:3:213:5 | ret | +| ssa_stress.c:213:3:213:10 | SSA definition | SSA def(ret) | ssa_stress.c:213:13:213:15 | ret | +| ssa_stress.c:213:13:213:20 | SSA definition | SSA def(ret) | ssa_stress.c:213:23:213:25 | ret | +| ssa_stress.c:213:23:213:30 | SSA definition | SSA def(ret) | ssa_stress.c:213:33:213:35 | ret | +| ssa_stress.c:213:33:213:40 | SSA definition | SSA def(ret) | ssa_stress.c:213:43:213:45 | ret | +| ssa_stress.c:213:43:213:50 | SSA definition | SSA def(ret) | ssa_stress.c:213:53:213:55 | ret | +| ssa_stress.c:213:53:213:60 | SSA definition | SSA def(ret) | ssa_stress.c:213:63:213:65 | ret | +| ssa_stress.c:213:63:213:70 | SSA definition | SSA def(ret) | ssa_stress.c:213:73:213:75 | ret | +| ssa_stress.c:213:73:213:80 | SSA definition | SSA def(ret) | ssa_stress.c:213:83:213:85 | ret | +| ssa_stress.c:213:83:213:90 | SSA definition | SSA def(ret) | ssa_stress.c:213:93:213:95 | ret | +| ssa_stress.c:213:93:213:100 | SSA definition | SSA def(ret) | ssa_stress.c:214:3:214:5 | ret | +| ssa_stress.c:214:3:214:10 | SSA definition | SSA def(ret) | ssa_stress.c:214:13:214:15 | ret | +| ssa_stress.c:214:13:214:20 | SSA definition | SSA def(ret) | ssa_stress.c:214:23:214:25 | ret | +| ssa_stress.c:214:23:214:30 | SSA definition | SSA def(ret) | ssa_stress.c:214:33:214:35 | ret | +| ssa_stress.c:214:33:214:40 | SSA definition | SSA def(ret) | ssa_stress.c:214:43:214:45 | ret | +| ssa_stress.c:214:43:214:50 | SSA definition | SSA def(ret) | ssa_stress.c:214:53:214:55 | ret | +| ssa_stress.c:214:53:214:60 | SSA definition | SSA def(ret) | ssa_stress.c:214:63:214:65 | ret | +| ssa_stress.c:214:63:214:70 | SSA definition | SSA def(ret) | ssa_stress.c:214:73:214:75 | ret | +| ssa_stress.c:214:73:214:80 | SSA definition | SSA def(ret) | ssa_stress.c:214:83:214:85 | ret | +| ssa_stress.c:214:83:214:90 | SSA definition | SSA def(ret) | ssa_stress.c:214:93:214:95 | ret | +| ssa_stress.c:214:93:214:100 | SSA definition | SSA def(ret) | ssa_stress.c:215:3:215:5 | ret | +| ssa_stress.c:215:3:215:10 | SSA definition | SSA def(ret) | ssa_stress.c:215:13:215:15 | ret | +| ssa_stress.c:215:13:215:20 | SSA definition | SSA def(ret) | ssa_stress.c:215:23:215:25 | ret | +| ssa_stress.c:215:23:215:30 | SSA definition | SSA def(ret) | ssa_stress.c:215:33:215:35 | ret | +| ssa_stress.c:215:33:215:40 | SSA definition | SSA def(ret) | ssa_stress.c:215:43:215:45 | ret | +| ssa_stress.c:215:43:215:50 | SSA definition | SSA def(ret) | ssa_stress.c:215:53:215:55 | ret | +| ssa_stress.c:215:53:215:60 | SSA definition | SSA def(ret) | ssa_stress.c:215:63:215:65 | ret | +| ssa_stress.c:215:63:215:70 | SSA definition | SSA def(ret) | ssa_stress.c:215:73:215:75 | ret | +| ssa_stress.c:215:73:215:80 | SSA definition | SSA def(ret) | ssa_stress.c:215:83:215:85 | ret | +| ssa_stress.c:215:83:215:90 | SSA definition | SSA def(ret) | ssa_stress.c:215:93:215:95 | ret | +| ssa_stress.c:215:93:215:100 | SSA definition | SSA def(ret) | ssa_stress.c:216:3:216:5 | ret | +| ssa_stress.c:216:3:216:10 | SSA definition | SSA def(ret) | ssa_stress.c:216:13:216:15 | ret | +| ssa_stress.c:216:13:216:20 | SSA definition | SSA def(ret) | ssa_stress.c:216:23:216:25 | ret | +| ssa_stress.c:216:23:216:30 | SSA definition | SSA def(ret) | ssa_stress.c:216:33:216:35 | ret | +| ssa_stress.c:216:33:216:40 | SSA definition | SSA def(ret) | ssa_stress.c:216:43:216:45 | ret | +| ssa_stress.c:216:43:216:50 | SSA definition | SSA def(ret) | ssa_stress.c:216:53:216:55 | ret | +| ssa_stress.c:216:53:216:60 | SSA definition | SSA def(ret) | ssa_stress.c:216:63:216:65 | ret | +| ssa_stress.c:216:63:216:70 | SSA definition | SSA def(ret) | ssa_stress.c:216:73:216:75 | ret | +| ssa_stress.c:216:73:216:80 | SSA definition | SSA def(ret) | ssa_stress.c:216:83:216:85 | ret | +| ssa_stress.c:216:83:216:90 | SSA definition | SSA def(ret) | ssa_stress.c:216:93:216:95 | ret | +| ssa_stress.c:216:93:216:100 | SSA definition | SSA def(ret) | ssa_stress.c:217:3:217:5 | ret | +| ssa_stress.c:217:3:217:10 | SSA definition | SSA def(ret) | ssa_stress.c:217:13:217:15 | ret | +| ssa_stress.c:217:13:217:20 | SSA definition | SSA def(ret) | ssa_stress.c:217:23:217:25 | ret | +| ssa_stress.c:217:23:217:30 | SSA definition | SSA def(ret) | ssa_stress.c:217:33:217:35 | ret | +| ssa_stress.c:217:33:217:40 | SSA definition | SSA def(ret) | ssa_stress.c:217:43:217:45 | ret | +| ssa_stress.c:217:43:217:50 | SSA definition | SSA def(ret) | ssa_stress.c:217:53:217:55 | ret | +| ssa_stress.c:217:53:217:60 | SSA definition | SSA def(ret) | ssa_stress.c:217:63:217:65 | ret | +| ssa_stress.c:217:63:217:70 | SSA definition | SSA def(ret) | ssa_stress.c:217:73:217:75 | ret | +| ssa_stress.c:217:73:217:80 | SSA definition | SSA def(ret) | ssa_stress.c:217:83:217:85 | ret | +| ssa_stress.c:217:83:217:90 | SSA definition | SSA def(ret) | ssa_stress.c:217:93:217:95 | ret | +| ssa_stress.c:217:93:217:100 | SSA definition | SSA def(ret) | ssa_stress.c:218:3:218:5 | ret | +| ssa_stress.c:218:3:218:10 | SSA definition | SSA def(ret) | ssa_stress.c:218:13:218:15 | ret | +| ssa_stress.c:218:13:218:20 | SSA definition | SSA def(ret) | ssa_stress.c:218:23:218:25 | ret | +| ssa_stress.c:218:23:218:30 | SSA definition | SSA def(ret) | ssa_stress.c:218:33:218:35 | ret | +| ssa_stress.c:218:33:218:40 | SSA definition | SSA def(ret) | ssa_stress.c:218:43:218:45 | ret | +| ssa_stress.c:218:43:218:50 | SSA definition | SSA def(ret) | ssa_stress.c:218:53:218:55 | ret | +| ssa_stress.c:218:53:218:60 | SSA definition | SSA def(ret) | ssa_stress.c:218:63:218:65 | ret | +| ssa_stress.c:218:63:218:70 | SSA definition | SSA def(ret) | ssa_stress.c:218:73:218:75 | ret | +| ssa_stress.c:218:73:218:80 | SSA definition | SSA def(ret) | ssa_stress.c:218:83:218:85 | ret | +| ssa_stress.c:218:83:218:90 | SSA definition | SSA def(ret) | ssa_stress.c:218:93:218:95 | ret | +| ssa_stress.c:218:93:218:100 | SSA definition | SSA def(ret) | ssa_stress.c:221:3:221:5 | ret | +| ssa_stress.c:221:3:221:10 | SSA definition | SSA def(ret) | ssa_stress.c:221:13:221:15 | ret | +| ssa_stress.c:221:13:221:20 | SSA definition | SSA def(ret) | ssa_stress.c:221:23:221:25 | ret | +| ssa_stress.c:221:23:221:30 | SSA definition | SSA def(ret) | ssa_stress.c:221:33:221:35 | ret | +| ssa_stress.c:221:33:221:40 | SSA definition | SSA def(ret) | ssa_stress.c:221:43:221:45 | ret | +| ssa_stress.c:221:43:221:50 | SSA definition | SSA def(ret) | ssa_stress.c:221:53:221:55 | ret | +| ssa_stress.c:221:53:221:60 | SSA definition | SSA def(ret) | ssa_stress.c:221:63:221:65 | ret | +| ssa_stress.c:221:63:221:70 | SSA definition | SSA def(ret) | ssa_stress.c:221:73:221:75 | ret | +| ssa_stress.c:221:73:221:80 | SSA definition | SSA def(ret) | ssa_stress.c:221:83:221:85 | ret | +| ssa_stress.c:221:83:221:90 | SSA definition | SSA def(ret) | ssa_stress.c:221:93:221:95 | ret | +| ssa_stress.c:221:93:221:100 | SSA definition | SSA def(ret) | ssa_stress.c:222:3:222:5 | ret | +| ssa_stress.c:222:3:222:10 | SSA definition | SSA def(ret) | ssa_stress.c:222:13:222:15 | ret | +| ssa_stress.c:222:13:222:20 | SSA definition | SSA def(ret) | ssa_stress.c:222:23:222:25 | ret | +| ssa_stress.c:222:23:222:30 | SSA definition | SSA def(ret) | ssa_stress.c:222:33:222:35 | ret | +| ssa_stress.c:222:33:222:40 | SSA definition | SSA def(ret) | ssa_stress.c:222:43:222:45 | ret | +| ssa_stress.c:222:43:222:50 | SSA definition | SSA def(ret) | ssa_stress.c:222:53:222:55 | ret | +| ssa_stress.c:222:53:222:60 | SSA definition | SSA def(ret) | ssa_stress.c:222:63:222:65 | ret | +| ssa_stress.c:222:63:222:70 | SSA definition | SSA def(ret) | ssa_stress.c:222:73:222:75 | ret | +| ssa_stress.c:222:73:222:80 | SSA definition | SSA def(ret) | ssa_stress.c:222:83:222:85 | ret | +| ssa_stress.c:222:83:222:90 | SSA definition | SSA def(ret) | ssa_stress.c:222:93:222:95 | ret | +| ssa_stress.c:222:93:222:100 | SSA definition | SSA def(ret) | ssa_stress.c:223:3:223:5 | ret | +| ssa_stress.c:223:3:223:10 | SSA definition | SSA def(ret) | ssa_stress.c:223:13:223:15 | ret | +| ssa_stress.c:223:13:223:20 | SSA definition | SSA def(ret) | ssa_stress.c:223:23:223:25 | ret | +| ssa_stress.c:223:23:223:30 | SSA definition | SSA def(ret) | ssa_stress.c:223:33:223:35 | ret | +| ssa_stress.c:223:33:223:40 | SSA definition | SSA def(ret) | ssa_stress.c:223:43:223:45 | ret | +| ssa_stress.c:223:43:223:50 | SSA definition | SSA def(ret) | ssa_stress.c:223:53:223:55 | ret | +| ssa_stress.c:223:53:223:60 | SSA definition | SSA def(ret) | ssa_stress.c:223:63:223:65 | ret | +| ssa_stress.c:223:63:223:70 | SSA definition | SSA def(ret) | ssa_stress.c:223:73:223:75 | ret | +| ssa_stress.c:223:73:223:80 | SSA definition | SSA def(ret) | ssa_stress.c:223:83:223:85 | ret | +| ssa_stress.c:223:83:223:90 | SSA definition | SSA def(ret) | ssa_stress.c:223:93:223:95 | ret | +| ssa_stress.c:223:93:223:100 | SSA definition | SSA def(ret) | ssa_stress.c:224:3:224:5 | ret | +| ssa_stress.c:224:3:224:10 | SSA definition | SSA def(ret) | ssa_stress.c:224:13:224:15 | ret | +| ssa_stress.c:224:13:224:20 | SSA definition | SSA def(ret) | ssa_stress.c:224:23:224:25 | ret | +| ssa_stress.c:224:23:224:30 | SSA definition | SSA def(ret) | ssa_stress.c:224:33:224:35 | ret | +| ssa_stress.c:224:33:224:40 | SSA definition | SSA def(ret) | ssa_stress.c:224:43:224:45 | ret | +| ssa_stress.c:224:43:224:50 | SSA definition | SSA def(ret) | ssa_stress.c:224:53:224:55 | ret | +| ssa_stress.c:224:53:224:60 | SSA definition | SSA def(ret) | ssa_stress.c:224:63:224:65 | ret | +| ssa_stress.c:224:63:224:70 | SSA definition | SSA def(ret) | ssa_stress.c:224:73:224:75 | ret | +| ssa_stress.c:224:73:224:80 | SSA definition | SSA def(ret) | ssa_stress.c:224:83:224:85 | ret | +| ssa_stress.c:224:83:224:90 | SSA definition | SSA def(ret) | ssa_stress.c:224:93:224:95 | ret | +| ssa_stress.c:224:93:224:100 | SSA definition | SSA def(ret) | ssa_stress.c:225:3:225:5 | ret | +| ssa_stress.c:225:3:225:10 | SSA definition | SSA def(ret) | ssa_stress.c:225:13:225:15 | ret | +| ssa_stress.c:225:13:225:20 | SSA definition | SSA def(ret) | ssa_stress.c:225:23:225:25 | ret | +| ssa_stress.c:225:23:225:30 | SSA definition | SSA def(ret) | ssa_stress.c:225:33:225:35 | ret | +| ssa_stress.c:225:33:225:40 | SSA definition | SSA def(ret) | ssa_stress.c:225:43:225:45 | ret | +| ssa_stress.c:225:43:225:50 | SSA definition | SSA def(ret) | ssa_stress.c:225:53:225:55 | ret | +| ssa_stress.c:225:53:225:60 | SSA definition | SSA def(ret) | ssa_stress.c:225:63:225:65 | ret | +| ssa_stress.c:225:63:225:70 | SSA definition | SSA def(ret) | ssa_stress.c:225:73:225:75 | ret | +| ssa_stress.c:225:73:225:80 | SSA definition | SSA def(ret) | ssa_stress.c:225:83:225:85 | ret | +| ssa_stress.c:225:83:225:90 | SSA definition | SSA def(ret) | ssa_stress.c:225:93:225:95 | ret | +| ssa_stress.c:225:93:225:100 | SSA definition | SSA def(ret) | ssa_stress.c:226:3:226:5 | ret | +| ssa_stress.c:226:3:226:10 | SSA definition | SSA def(ret) | ssa_stress.c:226:13:226:15 | ret | +| ssa_stress.c:226:13:226:20 | SSA definition | SSA def(ret) | ssa_stress.c:226:23:226:25 | ret | +| ssa_stress.c:226:23:226:30 | SSA definition | SSA def(ret) | ssa_stress.c:226:33:226:35 | ret | +| ssa_stress.c:226:33:226:40 | SSA definition | SSA def(ret) | ssa_stress.c:226:43:226:45 | ret | +| ssa_stress.c:226:43:226:50 | SSA definition | SSA def(ret) | ssa_stress.c:226:53:226:55 | ret | +| ssa_stress.c:226:53:226:60 | SSA definition | SSA def(ret) | ssa_stress.c:226:63:226:65 | ret | +| ssa_stress.c:226:63:226:70 | SSA definition | SSA def(ret) | ssa_stress.c:226:73:226:75 | ret | +| ssa_stress.c:226:73:226:80 | SSA definition | SSA def(ret) | ssa_stress.c:226:83:226:85 | ret | +| ssa_stress.c:226:83:226:90 | SSA definition | SSA def(ret) | ssa_stress.c:226:93:226:95 | ret | +| ssa_stress.c:226:93:226:100 | SSA definition | SSA def(ret) | ssa_stress.c:227:3:227:5 | ret | +| ssa_stress.c:227:3:227:10 | SSA definition | SSA def(ret) | ssa_stress.c:227:13:227:15 | ret | +| ssa_stress.c:227:13:227:20 | SSA definition | SSA def(ret) | ssa_stress.c:227:23:227:25 | ret | +| ssa_stress.c:227:23:227:30 | SSA definition | SSA def(ret) | ssa_stress.c:227:33:227:35 | ret | +| ssa_stress.c:227:33:227:40 | SSA definition | SSA def(ret) | ssa_stress.c:227:43:227:45 | ret | +| ssa_stress.c:227:43:227:50 | SSA definition | SSA def(ret) | ssa_stress.c:227:53:227:55 | ret | +| ssa_stress.c:227:53:227:60 | SSA definition | SSA def(ret) | ssa_stress.c:227:63:227:65 | ret | +| ssa_stress.c:227:63:227:70 | SSA definition | SSA def(ret) | ssa_stress.c:227:73:227:75 | ret | +| ssa_stress.c:227:73:227:80 | SSA definition | SSA def(ret) | ssa_stress.c:227:83:227:85 | ret | +| ssa_stress.c:227:83:227:90 | SSA definition | SSA def(ret) | ssa_stress.c:227:93:227:95 | ret | +| ssa_stress.c:227:93:227:100 | SSA definition | SSA def(ret) | ssa_stress.c:228:3:228:5 | ret | +| ssa_stress.c:228:3:228:10 | SSA definition | SSA def(ret) | ssa_stress.c:228:13:228:15 | ret | +| ssa_stress.c:228:13:228:20 | SSA definition | SSA def(ret) | ssa_stress.c:228:23:228:25 | ret | +| ssa_stress.c:228:23:228:30 | SSA definition | SSA def(ret) | ssa_stress.c:228:33:228:35 | ret | +| ssa_stress.c:228:33:228:40 | SSA definition | SSA def(ret) | ssa_stress.c:228:43:228:45 | ret | +| ssa_stress.c:228:43:228:50 | SSA definition | SSA def(ret) | ssa_stress.c:228:53:228:55 | ret | +| ssa_stress.c:228:53:228:60 | SSA definition | SSA def(ret) | ssa_stress.c:228:63:228:65 | ret | +| ssa_stress.c:228:63:228:70 | SSA definition | SSA def(ret) | ssa_stress.c:228:73:228:75 | ret | +| ssa_stress.c:228:73:228:80 | SSA definition | SSA def(ret) | ssa_stress.c:228:83:228:85 | ret | +| ssa_stress.c:228:83:228:90 | SSA definition | SSA def(ret) | ssa_stress.c:228:93:228:95 | ret | +| ssa_stress.c:228:93:228:100 | SSA definition | SSA def(ret) | ssa_stress.c:229:3:229:5 | ret | +| ssa_stress.c:229:3:229:10 | SSA definition | SSA def(ret) | ssa_stress.c:229:13:229:15 | ret | +| ssa_stress.c:229:13:229:20 | SSA definition | SSA def(ret) | ssa_stress.c:229:23:229:25 | ret | +| ssa_stress.c:229:23:229:30 | SSA definition | SSA def(ret) | ssa_stress.c:229:33:229:35 | ret | +| ssa_stress.c:229:33:229:40 | SSA definition | SSA def(ret) | ssa_stress.c:229:43:229:45 | ret | +| ssa_stress.c:229:43:229:50 | SSA definition | SSA def(ret) | ssa_stress.c:229:53:229:55 | ret | +| ssa_stress.c:229:53:229:60 | SSA definition | SSA def(ret) | ssa_stress.c:229:63:229:65 | ret | +| ssa_stress.c:229:63:229:70 | SSA definition | SSA def(ret) | ssa_stress.c:229:73:229:75 | ret | +| ssa_stress.c:229:73:229:80 | SSA definition | SSA def(ret) | ssa_stress.c:229:83:229:85 | ret | +| ssa_stress.c:229:83:229:90 | SSA definition | SSA def(ret) | ssa_stress.c:229:93:229:95 | ret | +| ssa_stress.c:229:93:229:100 | SSA definition | SSA def(ret) | ssa_stress.c:230:3:230:5 | ret | +| ssa_stress.c:230:3:230:10 | SSA definition | SSA def(ret) | ssa_stress.c:230:13:230:15 | ret | +| ssa_stress.c:230:13:230:20 | SSA definition | SSA def(ret) | ssa_stress.c:230:23:230:25 | ret | +| ssa_stress.c:230:23:230:30 | SSA definition | SSA def(ret) | ssa_stress.c:230:33:230:35 | ret | +| ssa_stress.c:230:33:230:40 | SSA definition | SSA def(ret) | ssa_stress.c:230:43:230:45 | ret | +| ssa_stress.c:230:43:230:50 | SSA definition | SSA def(ret) | ssa_stress.c:230:53:230:55 | ret | +| ssa_stress.c:230:53:230:60 | SSA definition | SSA def(ret) | ssa_stress.c:230:63:230:65 | ret | +| ssa_stress.c:230:63:230:70 | SSA definition | SSA def(ret) | ssa_stress.c:230:73:230:75 | ret | +| ssa_stress.c:230:73:230:80 | SSA definition | SSA def(ret) | ssa_stress.c:230:83:230:85 | ret | +| ssa_stress.c:230:83:230:90 | SSA definition | SSA def(ret) | ssa_stress.c:230:93:230:95 | ret | +| ssa_stress.c:230:93:230:100 | SSA definition | SSA def(ret) | ssa_stress.c:233:3:233:5 | ret | +| ssa_stress.c:233:3:233:10 | SSA definition | SSA def(ret) | ssa_stress.c:233:13:233:15 | ret | +| ssa_stress.c:233:13:233:20 | SSA definition | SSA def(ret) | ssa_stress.c:233:23:233:25 | ret | +| ssa_stress.c:233:23:233:30 | SSA definition | SSA def(ret) | ssa_stress.c:233:33:233:35 | ret | +| ssa_stress.c:233:33:233:40 | SSA definition | SSA def(ret) | ssa_stress.c:233:43:233:45 | ret | +| ssa_stress.c:233:43:233:50 | SSA definition | SSA def(ret) | ssa_stress.c:233:53:233:55 | ret | +| ssa_stress.c:233:53:233:60 | SSA definition | SSA def(ret) | ssa_stress.c:233:63:233:65 | ret | +| ssa_stress.c:233:63:233:70 | SSA definition | SSA def(ret) | ssa_stress.c:233:73:233:75 | ret | +| ssa_stress.c:233:73:233:80 | SSA definition | SSA def(ret) | ssa_stress.c:233:83:233:85 | ret | +| ssa_stress.c:233:83:233:90 | SSA definition | SSA def(ret) | ssa_stress.c:233:93:233:95 | ret | +| ssa_stress.c:233:93:233:100 | SSA definition | SSA def(ret) | ssa_stress.c:234:3:234:5 | ret | +| ssa_stress.c:234:3:234:10 | SSA definition | SSA def(ret) | ssa_stress.c:234:13:234:15 | ret | +| ssa_stress.c:234:13:234:20 | SSA definition | SSA def(ret) | ssa_stress.c:234:23:234:25 | ret | +| ssa_stress.c:234:23:234:30 | SSA definition | SSA def(ret) | ssa_stress.c:234:33:234:35 | ret | +| ssa_stress.c:234:33:234:40 | SSA definition | SSA def(ret) | ssa_stress.c:234:43:234:45 | ret | +| ssa_stress.c:234:43:234:50 | SSA definition | SSA def(ret) | ssa_stress.c:234:53:234:55 | ret | +| ssa_stress.c:234:53:234:60 | SSA definition | SSA def(ret) | ssa_stress.c:234:63:234:65 | ret | +| ssa_stress.c:234:63:234:70 | SSA definition | SSA def(ret) | ssa_stress.c:234:73:234:75 | ret | +| ssa_stress.c:234:73:234:80 | SSA definition | SSA def(ret) | ssa_stress.c:234:83:234:85 | ret | +| ssa_stress.c:234:83:234:90 | SSA definition | SSA def(ret) | ssa_stress.c:234:93:234:95 | ret | +| ssa_stress.c:234:93:234:100 | SSA definition | SSA def(ret) | ssa_stress.c:235:3:235:5 | ret | +| ssa_stress.c:235:3:235:10 | SSA definition | SSA def(ret) | ssa_stress.c:235:13:235:15 | ret | +| ssa_stress.c:235:13:235:20 | SSA definition | SSA def(ret) | ssa_stress.c:235:23:235:25 | ret | +| ssa_stress.c:235:23:235:30 | SSA definition | SSA def(ret) | ssa_stress.c:235:33:235:35 | ret | +| ssa_stress.c:235:33:235:40 | SSA definition | SSA def(ret) | ssa_stress.c:235:43:235:45 | ret | +| ssa_stress.c:235:43:235:50 | SSA definition | SSA def(ret) | ssa_stress.c:235:53:235:55 | ret | +| ssa_stress.c:235:53:235:60 | SSA definition | SSA def(ret) | ssa_stress.c:235:63:235:65 | ret | +| ssa_stress.c:235:63:235:70 | SSA definition | SSA def(ret) | ssa_stress.c:235:73:235:75 | ret | +| ssa_stress.c:235:73:235:80 | SSA definition | SSA def(ret) | ssa_stress.c:235:83:235:85 | ret | +| ssa_stress.c:235:83:235:90 | SSA definition | SSA def(ret) | ssa_stress.c:235:93:235:95 | ret | +| ssa_stress.c:235:93:235:100 | SSA definition | SSA def(ret) | ssa_stress.c:236:3:236:5 | ret | +| ssa_stress.c:236:3:236:10 | SSA definition | SSA def(ret) | ssa_stress.c:236:13:236:15 | ret | +| ssa_stress.c:236:13:236:20 | SSA definition | SSA def(ret) | ssa_stress.c:236:23:236:25 | ret | +| ssa_stress.c:236:23:236:30 | SSA definition | SSA def(ret) | ssa_stress.c:236:33:236:35 | ret | +| ssa_stress.c:236:33:236:40 | SSA definition | SSA def(ret) | ssa_stress.c:236:43:236:45 | ret | +| ssa_stress.c:236:43:236:50 | SSA definition | SSA def(ret) | ssa_stress.c:236:53:236:55 | ret | +| ssa_stress.c:236:53:236:60 | SSA definition | SSA def(ret) | ssa_stress.c:236:63:236:65 | ret | +| ssa_stress.c:236:63:236:70 | SSA definition | SSA def(ret) | ssa_stress.c:236:73:236:75 | ret | +| ssa_stress.c:236:73:236:80 | SSA definition | SSA def(ret) | ssa_stress.c:236:83:236:85 | ret | +| ssa_stress.c:236:83:236:90 | SSA definition | SSA def(ret) | ssa_stress.c:236:93:236:95 | ret | +| ssa_stress.c:236:93:236:100 | SSA definition | SSA def(ret) | ssa_stress.c:237:3:237:5 | ret | +| ssa_stress.c:237:3:237:10 | SSA definition | SSA def(ret) | ssa_stress.c:237:13:237:15 | ret | +| ssa_stress.c:237:13:237:20 | SSA definition | SSA def(ret) | ssa_stress.c:237:23:237:25 | ret | +| ssa_stress.c:237:23:237:30 | SSA definition | SSA def(ret) | ssa_stress.c:237:33:237:35 | ret | +| ssa_stress.c:237:33:237:40 | SSA definition | SSA def(ret) | ssa_stress.c:237:43:237:45 | ret | +| ssa_stress.c:237:43:237:50 | SSA definition | SSA def(ret) | ssa_stress.c:237:53:237:55 | ret | +| ssa_stress.c:237:53:237:60 | SSA definition | SSA def(ret) | ssa_stress.c:237:63:237:65 | ret | +| ssa_stress.c:237:63:237:70 | SSA definition | SSA def(ret) | ssa_stress.c:237:73:237:75 | ret | +| ssa_stress.c:237:73:237:80 | SSA definition | SSA def(ret) | ssa_stress.c:237:83:237:85 | ret | +| ssa_stress.c:237:83:237:90 | SSA definition | SSA def(ret) | ssa_stress.c:237:93:237:95 | ret | +| ssa_stress.c:237:93:237:100 | SSA definition | SSA def(ret) | ssa_stress.c:238:3:238:5 | ret | +| ssa_stress.c:238:3:238:10 | SSA definition | SSA def(ret) | ssa_stress.c:238:13:238:15 | ret | +| ssa_stress.c:238:13:238:20 | SSA definition | SSA def(ret) | ssa_stress.c:238:23:238:25 | ret | +| ssa_stress.c:238:23:238:30 | SSA definition | SSA def(ret) | ssa_stress.c:238:33:238:35 | ret | +| ssa_stress.c:238:33:238:40 | SSA definition | SSA def(ret) | ssa_stress.c:238:43:238:45 | ret | +| ssa_stress.c:238:43:238:50 | SSA definition | SSA def(ret) | ssa_stress.c:238:53:238:55 | ret | +| ssa_stress.c:238:53:238:60 | SSA definition | SSA def(ret) | ssa_stress.c:238:63:238:65 | ret | +| ssa_stress.c:238:63:238:70 | SSA definition | SSA def(ret) | ssa_stress.c:238:73:238:75 | ret | +| ssa_stress.c:238:73:238:80 | SSA definition | SSA def(ret) | ssa_stress.c:238:83:238:85 | ret | +| ssa_stress.c:238:83:238:90 | SSA definition | SSA def(ret) | ssa_stress.c:238:93:238:95 | ret | +| ssa_stress.c:238:93:238:100 | SSA definition | SSA def(ret) | ssa_stress.c:239:3:239:5 | ret | +| ssa_stress.c:239:3:239:10 | SSA definition | SSA def(ret) | ssa_stress.c:239:13:239:15 | ret | +| ssa_stress.c:239:13:239:20 | SSA definition | SSA def(ret) | ssa_stress.c:239:23:239:25 | ret | +| ssa_stress.c:239:23:239:30 | SSA definition | SSA def(ret) | ssa_stress.c:239:33:239:35 | ret | +| ssa_stress.c:239:33:239:40 | SSA definition | SSA def(ret) | ssa_stress.c:239:43:239:45 | ret | +| ssa_stress.c:239:43:239:50 | SSA definition | SSA def(ret) | ssa_stress.c:239:53:239:55 | ret | +| ssa_stress.c:239:53:239:60 | SSA definition | SSA def(ret) | ssa_stress.c:239:63:239:65 | ret | +| ssa_stress.c:239:63:239:70 | SSA definition | SSA def(ret) | ssa_stress.c:239:73:239:75 | ret | +| ssa_stress.c:239:73:239:80 | SSA definition | SSA def(ret) | ssa_stress.c:239:83:239:85 | ret | +| ssa_stress.c:239:83:239:90 | SSA definition | SSA def(ret) | ssa_stress.c:239:93:239:95 | ret | +| ssa_stress.c:239:93:239:100 | SSA definition | SSA def(ret) | ssa_stress.c:240:3:240:5 | ret | +| ssa_stress.c:240:3:240:10 | SSA definition | SSA def(ret) | ssa_stress.c:240:13:240:15 | ret | +| ssa_stress.c:240:13:240:20 | SSA definition | SSA def(ret) | ssa_stress.c:240:23:240:25 | ret | +| ssa_stress.c:240:23:240:30 | SSA definition | SSA def(ret) | ssa_stress.c:240:33:240:35 | ret | +| ssa_stress.c:240:33:240:40 | SSA definition | SSA def(ret) | ssa_stress.c:240:43:240:45 | ret | +| ssa_stress.c:240:43:240:50 | SSA definition | SSA def(ret) | ssa_stress.c:240:53:240:55 | ret | +| ssa_stress.c:240:53:240:60 | SSA definition | SSA def(ret) | ssa_stress.c:240:63:240:65 | ret | +| ssa_stress.c:240:63:240:70 | SSA definition | SSA def(ret) | ssa_stress.c:240:73:240:75 | ret | +| ssa_stress.c:240:73:240:80 | SSA definition | SSA def(ret) | ssa_stress.c:240:83:240:85 | ret | +| ssa_stress.c:240:83:240:90 | SSA definition | SSA def(ret) | ssa_stress.c:240:93:240:95 | ret | +| ssa_stress.c:240:93:240:100 | SSA definition | SSA def(ret) | ssa_stress.c:241:3:241:5 | ret | +| ssa_stress.c:241:3:241:10 | SSA definition | SSA def(ret) | ssa_stress.c:241:13:241:15 | ret | +| ssa_stress.c:241:13:241:20 | SSA definition | SSA def(ret) | ssa_stress.c:241:23:241:25 | ret | +| ssa_stress.c:241:23:241:30 | SSA definition | SSA def(ret) | ssa_stress.c:241:33:241:35 | ret | +| ssa_stress.c:241:33:241:40 | SSA definition | SSA def(ret) | ssa_stress.c:241:43:241:45 | ret | +| ssa_stress.c:241:43:241:50 | SSA definition | SSA def(ret) | ssa_stress.c:241:53:241:55 | ret | +| ssa_stress.c:241:53:241:60 | SSA definition | SSA def(ret) | ssa_stress.c:241:63:241:65 | ret | +| ssa_stress.c:241:63:241:70 | SSA definition | SSA def(ret) | ssa_stress.c:241:73:241:75 | ret | +| ssa_stress.c:241:73:241:80 | SSA definition | SSA def(ret) | ssa_stress.c:241:83:241:85 | ret | +| ssa_stress.c:241:83:241:90 | SSA definition | SSA def(ret) | ssa_stress.c:241:93:241:95 | ret | +| ssa_stress.c:241:93:241:100 | SSA definition | SSA def(ret) | ssa_stress.c:242:3:242:5 | ret | +| ssa_stress.c:242:3:242:10 | SSA definition | SSA def(ret) | ssa_stress.c:242:13:242:15 | ret | +| ssa_stress.c:242:13:242:20 | SSA definition | SSA def(ret) | ssa_stress.c:242:23:242:25 | ret | +| ssa_stress.c:242:23:242:30 | SSA definition | SSA def(ret) | ssa_stress.c:242:33:242:35 | ret | +| ssa_stress.c:242:33:242:40 | SSA definition | SSA def(ret) | ssa_stress.c:242:43:242:45 | ret | +| ssa_stress.c:242:43:242:50 | SSA definition | SSA def(ret) | ssa_stress.c:242:53:242:55 | ret | +| ssa_stress.c:242:53:242:60 | SSA definition | SSA def(ret) | ssa_stress.c:242:63:242:65 | ret | +| ssa_stress.c:242:63:242:70 | SSA definition | SSA def(ret) | ssa_stress.c:242:73:242:75 | ret | +| ssa_stress.c:242:73:242:80 | SSA definition | SSA def(ret) | ssa_stress.c:242:83:242:85 | ret | +| ssa_stress.c:242:83:242:90 | SSA definition | SSA def(ret) | ssa_stress.c:242:93:242:95 | ret | +| ssa_stress.c:242:93:242:100 | SSA definition | SSA def(ret) | ssa_stress.c:245:3:245:5 | ret | +| ssa_stress.c:245:3:245:10 | SSA definition | SSA def(ret) | ssa_stress.c:245:13:245:15 | ret | +| ssa_stress.c:245:13:245:20 | SSA definition | SSA def(ret) | ssa_stress.c:245:23:245:25 | ret | +| ssa_stress.c:245:23:245:30 | SSA definition | SSA def(ret) | ssa_stress.c:245:33:245:35 | ret | +| ssa_stress.c:245:33:245:40 | SSA definition | SSA def(ret) | ssa_stress.c:245:43:245:45 | ret | +| ssa_stress.c:245:43:245:50 | SSA definition | SSA def(ret) | ssa_stress.c:245:53:245:55 | ret | +| ssa_stress.c:245:53:245:60 | SSA definition | SSA def(ret) | ssa_stress.c:245:63:245:65 | ret | +| ssa_stress.c:245:63:245:70 | SSA definition | SSA def(ret) | ssa_stress.c:245:73:245:75 | ret | +| ssa_stress.c:245:73:245:80 | SSA definition | SSA def(ret) | ssa_stress.c:245:83:245:85 | ret | +| ssa_stress.c:245:83:245:90 | SSA definition | SSA def(ret) | ssa_stress.c:245:93:245:95 | ret | +| ssa_stress.c:245:93:245:100 | SSA definition | SSA def(ret) | ssa_stress.c:246:3:246:5 | ret | +| ssa_stress.c:246:3:246:10 | SSA definition | SSA def(ret) | ssa_stress.c:246:13:246:15 | ret | +| ssa_stress.c:246:13:246:20 | SSA definition | SSA def(ret) | ssa_stress.c:246:23:246:25 | ret | +| ssa_stress.c:246:23:246:30 | SSA definition | SSA def(ret) | ssa_stress.c:246:33:246:35 | ret | +| ssa_stress.c:246:33:246:40 | SSA definition | SSA def(ret) | ssa_stress.c:246:43:246:45 | ret | +| ssa_stress.c:246:43:246:50 | SSA definition | SSA def(ret) | ssa_stress.c:246:53:246:55 | ret | +| ssa_stress.c:246:53:246:60 | SSA definition | SSA def(ret) | ssa_stress.c:246:63:246:65 | ret | +| ssa_stress.c:246:63:246:70 | SSA definition | SSA def(ret) | ssa_stress.c:246:73:246:75 | ret | +| ssa_stress.c:246:73:246:80 | SSA definition | SSA def(ret) | ssa_stress.c:246:83:246:85 | ret | +| ssa_stress.c:246:83:246:90 | SSA definition | SSA def(ret) | ssa_stress.c:246:93:246:95 | ret | +| ssa_stress.c:246:93:246:100 | SSA definition | SSA def(ret) | ssa_stress.c:247:3:247:5 | ret | +| ssa_stress.c:247:3:247:10 | SSA definition | SSA def(ret) | ssa_stress.c:247:13:247:15 | ret | +| ssa_stress.c:247:13:247:20 | SSA definition | SSA def(ret) | ssa_stress.c:247:23:247:25 | ret | +| ssa_stress.c:247:23:247:30 | SSA definition | SSA def(ret) | ssa_stress.c:247:33:247:35 | ret | +| ssa_stress.c:247:33:247:40 | SSA definition | SSA def(ret) | ssa_stress.c:247:43:247:45 | ret | +| ssa_stress.c:247:43:247:50 | SSA definition | SSA def(ret) | ssa_stress.c:247:53:247:55 | ret | +| ssa_stress.c:247:53:247:60 | SSA definition | SSA def(ret) | ssa_stress.c:247:63:247:65 | ret | +| ssa_stress.c:247:63:247:70 | SSA definition | SSA def(ret) | ssa_stress.c:247:73:247:75 | ret | +| ssa_stress.c:247:73:247:80 | SSA definition | SSA def(ret) | ssa_stress.c:247:83:247:85 | ret | +| ssa_stress.c:247:83:247:90 | SSA definition | SSA def(ret) | ssa_stress.c:247:93:247:95 | ret | +| ssa_stress.c:247:93:247:100 | SSA definition | SSA def(ret) | ssa_stress.c:248:3:248:5 | ret | +| ssa_stress.c:248:3:248:10 | SSA definition | SSA def(ret) | ssa_stress.c:248:13:248:15 | ret | +| ssa_stress.c:248:13:248:20 | SSA definition | SSA def(ret) | ssa_stress.c:248:23:248:25 | ret | +| ssa_stress.c:248:23:248:30 | SSA definition | SSA def(ret) | ssa_stress.c:248:33:248:35 | ret | +| ssa_stress.c:248:33:248:40 | SSA definition | SSA def(ret) | ssa_stress.c:248:43:248:45 | ret | +| ssa_stress.c:248:43:248:50 | SSA definition | SSA def(ret) | ssa_stress.c:248:53:248:55 | ret | +| ssa_stress.c:248:53:248:60 | SSA definition | SSA def(ret) | ssa_stress.c:248:63:248:65 | ret | +| ssa_stress.c:248:63:248:70 | SSA definition | SSA def(ret) | ssa_stress.c:248:73:248:75 | ret | +| ssa_stress.c:248:73:248:80 | SSA definition | SSA def(ret) | ssa_stress.c:248:83:248:85 | ret | +| ssa_stress.c:248:83:248:90 | SSA definition | SSA def(ret) | ssa_stress.c:248:93:248:95 | ret | +| ssa_stress.c:248:93:248:100 | SSA definition | SSA def(ret) | ssa_stress.c:249:3:249:5 | ret | +| ssa_stress.c:249:3:249:10 | SSA definition | SSA def(ret) | ssa_stress.c:249:13:249:15 | ret | +| ssa_stress.c:249:13:249:20 | SSA definition | SSA def(ret) | ssa_stress.c:249:23:249:25 | ret | +| ssa_stress.c:249:23:249:30 | SSA definition | SSA def(ret) | ssa_stress.c:249:33:249:35 | ret | +| ssa_stress.c:249:33:249:40 | SSA definition | SSA def(ret) | ssa_stress.c:249:43:249:45 | ret | +| ssa_stress.c:249:43:249:50 | SSA definition | SSA def(ret) | ssa_stress.c:249:53:249:55 | ret | +| ssa_stress.c:249:53:249:60 | SSA definition | SSA def(ret) | ssa_stress.c:249:63:249:65 | ret | +| ssa_stress.c:249:63:249:70 | SSA definition | SSA def(ret) | ssa_stress.c:249:73:249:75 | ret | +| ssa_stress.c:249:73:249:80 | SSA definition | SSA def(ret) | ssa_stress.c:249:83:249:85 | ret | +| ssa_stress.c:249:83:249:90 | SSA definition | SSA def(ret) | ssa_stress.c:249:93:249:95 | ret | +| ssa_stress.c:249:93:249:100 | SSA definition | SSA def(ret) | ssa_stress.c:250:3:250:5 | ret | +| ssa_stress.c:250:3:250:10 | SSA definition | SSA def(ret) | ssa_stress.c:250:13:250:15 | ret | +| ssa_stress.c:250:13:250:20 | SSA definition | SSA def(ret) | ssa_stress.c:250:23:250:25 | ret | +| ssa_stress.c:250:23:250:30 | SSA definition | SSA def(ret) | ssa_stress.c:250:33:250:35 | ret | +| ssa_stress.c:250:33:250:40 | SSA definition | SSA def(ret) | ssa_stress.c:250:43:250:45 | ret | +| ssa_stress.c:250:43:250:50 | SSA definition | SSA def(ret) | ssa_stress.c:250:53:250:55 | ret | +| ssa_stress.c:250:53:250:60 | SSA definition | SSA def(ret) | ssa_stress.c:250:63:250:65 | ret | +| ssa_stress.c:250:63:250:70 | SSA definition | SSA def(ret) | ssa_stress.c:250:73:250:75 | ret | +| ssa_stress.c:250:73:250:80 | SSA definition | SSA def(ret) | ssa_stress.c:250:83:250:85 | ret | +| ssa_stress.c:250:83:250:90 | SSA definition | SSA def(ret) | ssa_stress.c:250:93:250:95 | ret | +| ssa_stress.c:250:93:250:100 | SSA definition | SSA def(ret) | ssa_stress.c:251:3:251:5 | ret | +| ssa_stress.c:251:3:251:10 | SSA definition | SSA def(ret) | ssa_stress.c:251:13:251:15 | ret | +| ssa_stress.c:251:13:251:20 | SSA definition | SSA def(ret) | ssa_stress.c:251:23:251:25 | ret | +| ssa_stress.c:251:23:251:30 | SSA definition | SSA def(ret) | ssa_stress.c:251:33:251:35 | ret | +| ssa_stress.c:251:33:251:40 | SSA definition | SSA def(ret) | ssa_stress.c:251:43:251:45 | ret | +| ssa_stress.c:251:43:251:50 | SSA definition | SSA def(ret) | ssa_stress.c:251:53:251:55 | ret | +| ssa_stress.c:251:53:251:60 | SSA definition | SSA def(ret) | ssa_stress.c:251:63:251:65 | ret | +| ssa_stress.c:251:63:251:70 | SSA definition | SSA def(ret) | ssa_stress.c:251:73:251:75 | ret | +| ssa_stress.c:251:73:251:80 | SSA definition | SSA def(ret) | ssa_stress.c:251:83:251:85 | ret | +| ssa_stress.c:251:83:251:90 | SSA definition | SSA def(ret) | ssa_stress.c:251:93:251:95 | ret | +| ssa_stress.c:251:93:251:100 | SSA definition | SSA def(ret) | ssa_stress.c:252:3:252:5 | ret | +| ssa_stress.c:252:3:252:10 | SSA definition | SSA def(ret) | ssa_stress.c:252:13:252:15 | ret | +| ssa_stress.c:252:13:252:20 | SSA definition | SSA def(ret) | ssa_stress.c:252:23:252:25 | ret | +| ssa_stress.c:252:23:252:30 | SSA definition | SSA def(ret) | ssa_stress.c:252:33:252:35 | ret | +| ssa_stress.c:252:33:252:40 | SSA definition | SSA def(ret) | ssa_stress.c:252:43:252:45 | ret | +| ssa_stress.c:252:43:252:50 | SSA definition | SSA def(ret) | ssa_stress.c:252:53:252:55 | ret | +| ssa_stress.c:252:53:252:60 | SSA definition | SSA def(ret) | ssa_stress.c:252:63:252:65 | ret | +| ssa_stress.c:252:63:252:70 | SSA definition | SSA def(ret) | ssa_stress.c:252:73:252:75 | ret | +| ssa_stress.c:252:73:252:80 | SSA definition | SSA def(ret) | ssa_stress.c:252:83:252:85 | ret | +| ssa_stress.c:252:83:252:90 | SSA definition | SSA def(ret) | ssa_stress.c:252:93:252:95 | ret | +| ssa_stress.c:252:93:252:100 | SSA definition | SSA def(ret) | ssa_stress.c:253:3:253:5 | ret | +| ssa_stress.c:253:3:253:10 | SSA definition | SSA def(ret) | ssa_stress.c:253:13:253:15 | ret | +| ssa_stress.c:253:13:253:20 | SSA definition | SSA def(ret) | ssa_stress.c:253:23:253:25 | ret | +| ssa_stress.c:253:23:253:30 | SSA definition | SSA def(ret) | ssa_stress.c:253:33:253:35 | ret | +| ssa_stress.c:253:33:253:40 | SSA definition | SSA def(ret) | ssa_stress.c:253:43:253:45 | ret | +| ssa_stress.c:253:43:253:50 | SSA definition | SSA def(ret) | ssa_stress.c:253:53:253:55 | ret | +| ssa_stress.c:253:53:253:60 | SSA definition | SSA def(ret) | ssa_stress.c:253:63:253:65 | ret | +| ssa_stress.c:253:63:253:70 | SSA definition | SSA def(ret) | ssa_stress.c:253:73:253:75 | ret | +| ssa_stress.c:253:73:253:80 | SSA definition | SSA def(ret) | ssa_stress.c:253:83:253:85 | ret | +| ssa_stress.c:253:83:253:90 | SSA definition | SSA def(ret) | ssa_stress.c:253:93:253:95 | ret | +| ssa_stress.c:253:93:253:100 | SSA definition | SSA def(ret) | ssa_stress.c:254:3:254:5 | ret | +| ssa_stress.c:254:3:254:10 | SSA definition | SSA def(ret) | ssa_stress.c:254:13:254:15 | ret | +| ssa_stress.c:254:13:254:20 | SSA definition | SSA def(ret) | ssa_stress.c:254:23:254:25 | ret | +| ssa_stress.c:254:23:254:30 | SSA definition | SSA def(ret) | ssa_stress.c:254:33:254:35 | ret | +| ssa_stress.c:254:33:254:40 | SSA definition | SSA def(ret) | ssa_stress.c:254:43:254:45 | ret | +| ssa_stress.c:254:43:254:50 | SSA definition | SSA def(ret) | ssa_stress.c:254:53:254:55 | ret | +| ssa_stress.c:254:53:254:60 | SSA definition | SSA def(ret) | ssa_stress.c:254:63:254:65 | ret | +| ssa_stress.c:254:63:254:70 | SSA definition | SSA def(ret) | ssa_stress.c:254:73:254:75 | ret | +| ssa_stress.c:254:73:254:80 | SSA definition | SSA def(ret) | ssa_stress.c:254:83:254:85 | ret | +| ssa_stress.c:254:83:254:90 | SSA definition | SSA def(ret) | ssa_stress.c:254:93:254:95 | ret | +| ssa_stress.c:254:93:254:100 | SSA definition | SSA def(ret) | ssa_stress.c:257:3:257:5 | ret | +| ssa_stress.c:257:3:257:10 | SSA definition | SSA def(ret) | ssa_stress.c:257:13:257:15 | ret | +| ssa_stress.c:257:13:257:20 | SSA definition | SSA def(ret) | ssa_stress.c:257:23:257:25 | ret | +| ssa_stress.c:257:23:257:30 | SSA definition | SSA def(ret) | ssa_stress.c:257:33:257:35 | ret | +| ssa_stress.c:257:33:257:40 | SSA definition | SSA def(ret) | ssa_stress.c:257:43:257:45 | ret | +| ssa_stress.c:257:43:257:50 | SSA definition | SSA def(ret) | ssa_stress.c:257:53:257:55 | ret | +| ssa_stress.c:257:53:257:60 | SSA definition | SSA def(ret) | ssa_stress.c:257:63:257:65 | ret | +| ssa_stress.c:257:63:257:70 | SSA definition | SSA def(ret) | ssa_stress.c:257:73:257:75 | ret | +| ssa_stress.c:257:73:257:80 | SSA definition | SSA def(ret) | ssa_stress.c:257:83:257:85 | ret | +| ssa_stress.c:257:83:257:90 | SSA definition | SSA def(ret) | ssa_stress.c:257:93:257:95 | ret | +| ssa_stress.c:257:93:257:100 | SSA definition | SSA def(ret) | ssa_stress.c:258:3:258:5 | ret | +| ssa_stress.c:258:3:258:10 | SSA definition | SSA def(ret) | ssa_stress.c:258:13:258:15 | ret | +| ssa_stress.c:258:13:258:20 | SSA definition | SSA def(ret) | ssa_stress.c:258:23:258:25 | ret | +| ssa_stress.c:258:23:258:30 | SSA definition | SSA def(ret) | ssa_stress.c:258:33:258:35 | ret | +| ssa_stress.c:258:33:258:40 | SSA definition | SSA def(ret) | ssa_stress.c:258:43:258:45 | ret | +| ssa_stress.c:258:43:258:50 | SSA definition | SSA def(ret) | ssa_stress.c:258:53:258:55 | ret | +| ssa_stress.c:258:53:258:60 | SSA definition | SSA def(ret) | ssa_stress.c:258:63:258:65 | ret | +| ssa_stress.c:258:63:258:70 | SSA definition | SSA def(ret) | ssa_stress.c:258:73:258:75 | ret | +| ssa_stress.c:258:73:258:80 | SSA definition | SSA def(ret) | ssa_stress.c:258:83:258:85 | ret | +| ssa_stress.c:258:83:258:90 | SSA definition | SSA def(ret) | ssa_stress.c:258:93:258:95 | ret | +| ssa_stress.c:258:93:258:100 | SSA definition | SSA def(ret) | ssa_stress.c:259:3:259:5 | ret | +| ssa_stress.c:259:3:259:10 | SSA definition | SSA def(ret) | ssa_stress.c:259:13:259:15 | ret | +| ssa_stress.c:259:13:259:20 | SSA definition | SSA def(ret) | ssa_stress.c:259:23:259:25 | ret | +| ssa_stress.c:259:23:259:30 | SSA definition | SSA def(ret) | ssa_stress.c:259:33:259:35 | ret | +| ssa_stress.c:259:33:259:40 | SSA definition | SSA def(ret) | ssa_stress.c:259:43:259:45 | ret | +| ssa_stress.c:259:43:259:50 | SSA definition | SSA def(ret) | ssa_stress.c:259:53:259:55 | ret | +| ssa_stress.c:259:53:259:60 | SSA definition | SSA def(ret) | ssa_stress.c:259:63:259:65 | ret | +| ssa_stress.c:259:63:259:70 | SSA definition | SSA def(ret) | ssa_stress.c:259:73:259:75 | ret | +| ssa_stress.c:259:73:259:80 | SSA definition | SSA def(ret) | ssa_stress.c:259:83:259:85 | ret | +| ssa_stress.c:259:83:259:90 | SSA definition | SSA def(ret) | ssa_stress.c:259:93:259:95 | ret | +| ssa_stress.c:259:93:259:100 | SSA definition | SSA def(ret) | ssa_stress.c:260:3:260:5 | ret | +| ssa_stress.c:260:3:260:10 | SSA definition | SSA def(ret) | ssa_stress.c:260:13:260:15 | ret | +| ssa_stress.c:260:13:260:20 | SSA definition | SSA def(ret) | ssa_stress.c:260:23:260:25 | ret | +| ssa_stress.c:260:23:260:30 | SSA definition | SSA def(ret) | ssa_stress.c:260:33:260:35 | ret | +| ssa_stress.c:260:33:260:40 | SSA definition | SSA def(ret) | ssa_stress.c:260:43:260:45 | ret | +| ssa_stress.c:260:43:260:50 | SSA definition | SSA def(ret) | ssa_stress.c:260:53:260:55 | ret | +| ssa_stress.c:260:53:260:60 | SSA definition | SSA def(ret) | ssa_stress.c:260:63:260:65 | ret | +| ssa_stress.c:260:63:260:70 | SSA definition | SSA def(ret) | ssa_stress.c:260:73:260:75 | ret | +| ssa_stress.c:260:73:260:80 | SSA definition | SSA def(ret) | ssa_stress.c:260:83:260:85 | ret | +| ssa_stress.c:260:83:260:90 | SSA definition | SSA def(ret) | ssa_stress.c:260:93:260:95 | ret | +| ssa_stress.c:260:93:260:100 | SSA definition | SSA def(ret) | ssa_stress.c:261:3:261:5 | ret | +| ssa_stress.c:261:3:261:10 | SSA definition | SSA def(ret) | ssa_stress.c:261:13:261:15 | ret | +| ssa_stress.c:261:13:261:20 | SSA definition | SSA def(ret) | ssa_stress.c:261:23:261:25 | ret | +| ssa_stress.c:261:23:261:30 | SSA definition | SSA def(ret) | ssa_stress.c:261:33:261:35 | ret | +| ssa_stress.c:261:33:261:40 | SSA definition | SSA def(ret) | ssa_stress.c:261:43:261:45 | ret | +| ssa_stress.c:261:43:261:50 | SSA definition | SSA def(ret) | ssa_stress.c:261:53:261:55 | ret | +| ssa_stress.c:261:53:261:60 | SSA definition | SSA def(ret) | ssa_stress.c:261:63:261:65 | ret | +| ssa_stress.c:261:63:261:70 | SSA definition | SSA def(ret) | ssa_stress.c:261:73:261:75 | ret | +| ssa_stress.c:261:73:261:80 | SSA definition | SSA def(ret) | ssa_stress.c:261:83:261:85 | ret | +| ssa_stress.c:261:83:261:90 | SSA definition | SSA def(ret) | ssa_stress.c:261:93:261:95 | ret | +| ssa_stress.c:261:93:261:100 | SSA definition | SSA def(ret) | ssa_stress.c:262:3:262:5 | ret | +| ssa_stress.c:262:3:262:10 | SSA definition | SSA def(ret) | ssa_stress.c:262:13:262:15 | ret | +| ssa_stress.c:262:13:262:20 | SSA definition | SSA def(ret) | ssa_stress.c:262:23:262:25 | ret | +| ssa_stress.c:262:23:262:30 | SSA definition | SSA def(ret) | ssa_stress.c:262:33:262:35 | ret | +| ssa_stress.c:262:33:262:40 | SSA definition | SSA def(ret) | ssa_stress.c:262:43:262:45 | ret | +| ssa_stress.c:262:43:262:50 | SSA definition | SSA def(ret) | ssa_stress.c:262:53:262:55 | ret | +| ssa_stress.c:262:53:262:60 | SSA definition | SSA def(ret) | ssa_stress.c:262:63:262:65 | ret | +| ssa_stress.c:262:63:262:70 | SSA definition | SSA def(ret) | ssa_stress.c:262:73:262:75 | ret | +| ssa_stress.c:262:73:262:80 | SSA definition | SSA def(ret) | ssa_stress.c:262:83:262:85 | ret | +| ssa_stress.c:262:83:262:90 | SSA definition | SSA def(ret) | ssa_stress.c:262:93:262:95 | ret | +| ssa_stress.c:262:93:262:100 | SSA definition | SSA def(ret) | ssa_stress.c:263:3:263:5 | ret | +| ssa_stress.c:263:3:263:10 | SSA definition | SSA def(ret) | ssa_stress.c:263:13:263:15 | ret | +| ssa_stress.c:263:13:263:20 | SSA definition | SSA def(ret) | ssa_stress.c:263:23:263:25 | ret | +| ssa_stress.c:263:23:263:30 | SSA definition | SSA def(ret) | ssa_stress.c:263:33:263:35 | ret | +| ssa_stress.c:263:33:263:40 | SSA definition | SSA def(ret) | ssa_stress.c:263:43:263:45 | ret | +| ssa_stress.c:263:43:263:50 | SSA definition | SSA def(ret) | ssa_stress.c:263:53:263:55 | ret | +| ssa_stress.c:263:53:263:60 | SSA definition | SSA def(ret) | ssa_stress.c:263:63:263:65 | ret | +| ssa_stress.c:263:63:263:70 | SSA definition | SSA def(ret) | ssa_stress.c:263:73:263:75 | ret | +| ssa_stress.c:263:73:263:80 | SSA definition | SSA def(ret) | ssa_stress.c:263:83:263:85 | ret | +| ssa_stress.c:263:83:263:90 | SSA definition | SSA def(ret) | ssa_stress.c:263:93:263:95 | ret | +| ssa_stress.c:263:93:263:100 | SSA definition | SSA def(ret) | ssa_stress.c:264:3:264:5 | ret | +| ssa_stress.c:264:3:264:10 | SSA definition | SSA def(ret) | ssa_stress.c:264:13:264:15 | ret | +| ssa_stress.c:264:13:264:20 | SSA definition | SSA def(ret) | ssa_stress.c:264:23:264:25 | ret | +| ssa_stress.c:264:23:264:30 | SSA definition | SSA def(ret) | ssa_stress.c:264:33:264:35 | ret | +| ssa_stress.c:264:33:264:40 | SSA definition | SSA def(ret) | ssa_stress.c:264:43:264:45 | ret | +| ssa_stress.c:264:43:264:50 | SSA definition | SSA def(ret) | ssa_stress.c:264:53:264:55 | ret | +| ssa_stress.c:264:53:264:60 | SSA definition | SSA def(ret) | ssa_stress.c:264:63:264:65 | ret | +| ssa_stress.c:264:63:264:70 | SSA definition | SSA def(ret) | ssa_stress.c:264:73:264:75 | ret | +| ssa_stress.c:264:73:264:80 | SSA definition | SSA def(ret) | ssa_stress.c:264:83:264:85 | ret | +| ssa_stress.c:264:83:264:90 | SSA definition | SSA def(ret) | ssa_stress.c:264:93:264:95 | ret | +| ssa_stress.c:264:93:264:100 | SSA definition | SSA def(ret) | ssa_stress.c:265:3:265:5 | ret | +| ssa_stress.c:265:3:265:10 | SSA definition | SSA def(ret) | ssa_stress.c:265:13:265:15 | ret | +| ssa_stress.c:265:13:265:20 | SSA definition | SSA def(ret) | ssa_stress.c:265:23:265:25 | ret | +| ssa_stress.c:265:23:265:30 | SSA definition | SSA def(ret) | ssa_stress.c:265:33:265:35 | ret | +| ssa_stress.c:265:33:265:40 | SSA definition | SSA def(ret) | ssa_stress.c:265:43:265:45 | ret | +| ssa_stress.c:265:43:265:50 | SSA definition | SSA def(ret) | ssa_stress.c:265:53:265:55 | ret | +| ssa_stress.c:265:53:265:60 | SSA definition | SSA def(ret) | ssa_stress.c:265:63:265:65 | ret | +| ssa_stress.c:265:63:265:70 | SSA definition | SSA def(ret) | ssa_stress.c:265:73:265:75 | ret | +| ssa_stress.c:265:73:265:80 | SSA definition | SSA def(ret) | ssa_stress.c:265:83:265:85 | ret | +| ssa_stress.c:265:83:265:90 | SSA definition | SSA def(ret) | ssa_stress.c:265:93:265:95 | ret | +| ssa_stress.c:265:93:265:100 | SSA definition | SSA def(ret) | ssa_stress.c:266:3:266:5 | ret | +| ssa_stress.c:266:3:266:10 | SSA definition | SSA def(ret) | ssa_stress.c:266:13:266:15 | ret | +| ssa_stress.c:266:13:266:20 | SSA definition | SSA def(ret) | ssa_stress.c:266:23:266:25 | ret | +| ssa_stress.c:266:23:266:30 | SSA definition | SSA def(ret) | ssa_stress.c:266:33:266:35 | ret | +| ssa_stress.c:266:33:266:40 | SSA definition | SSA def(ret) | ssa_stress.c:266:43:266:45 | ret | +| ssa_stress.c:266:43:266:50 | SSA definition | SSA def(ret) | ssa_stress.c:266:53:266:55 | ret | +| ssa_stress.c:266:53:266:60 | SSA definition | SSA def(ret) | ssa_stress.c:266:63:266:65 | ret | +| ssa_stress.c:266:63:266:70 | SSA definition | SSA def(ret) | ssa_stress.c:266:73:266:75 | ret | +| ssa_stress.c:266:73:266:80 | SSA definition | SSA def(ret) | ssa_stress.c:266:83:266:85 | ret | +| ssa_stress.c:266:83:266:90 | SSA definition | SSA def(ret) | ssa_stress.c:266:93:266:95 | ret | +| ssa_stress.c:266:93:266:100 | SSA definition | SSA def(ret) | ssa_stress.c:269:3:269:5 | ret | +| ssa_stress.c:269:3:269:10 | SSA definition | SSA def(ret) | ssa_stress.c:269:13:269:15 | ret | +| ssa_stress.c:269:13:269:20 | SSA definition | SSA def(ret) | ssa_stress.c:269:23:269:25 | ret | +| ssa_stress.c:269:23:269:30 | SSA definition | SSA def(ret) | ssa_stress.c:269:33:269:35 | ret | +| ssa_stress.c:269:33:269:40 | SSA definition | SSA def(ret) | ssa_stress.c:269:43:269:45 | ret | +| ssa_stress.c:269:43:269:50 | SSA definition | SSA def(ret) | ssa_stress.c:269:53:269:55 | ret | +| ssa_stress.c:269:53:269:60 | SSA definition | SSA def(ret) | ssa_stress.c:269:63:269:65 | ret | +| ssa_stress.c:269:63:269:70 | SSA definition | SSA def(ret) | ssa_stress.c:269:73:269:75 | ret | +| ssa_stress.c:269:73:269:80 | SSA definition | SSA def(ret) | ssa_stress.c:269:83:269:85 | ret | +| ssa_stress.c:269:83:269:90 | SSA definition | SSA def(ret) | ssa_stress.c:269:93:269:95 | ret | +| ssa_stress.c:269:93:269:100 | SSA definition | SSA def(ret) | ssa_stress.c:270:3:270:5 | ret | +| ssa_stress.c:270:3:270:10 | SSA definition | SSA def(ret) | ssa_stress.c:270:13:270:15 | ret | +| ssa_stress.c:270:13:270:20 | SSA definition | SSA def(ret) | ssa_stress.c:270:23:270:25 | ret | +| ssa_stress.c:270:23:270:30 | SSA definition | SSA def(ret) | ssa_stress.c:270:33:270:35 | ret | +| ssa_stress.c:270:33:270:40 | SSA definition | SSA def(ret) | ssa_stress.c:270:43:270:45 | ret | +| ssa_stress.c:270:43:270:50 | SSA definition | SSA def(ret) | ssa_stress.c:270:53:270:55 | ret | +| ssa_stress.c:270:53:270:60 | SSA definition | SSA def(ret) | ssa_stress.c:270:63:270:65 | ret | +| ssa_stress.c:270:63:270:70 | SSA definition | SSA def(ret) | ssa_stress.c:270:73:270:75 | ret | +| ssa_stress.c:270:73:270:80 | SSA definition | SSA def(ret) | ssa_stress.c:270:83:270:85 | ret | +| ssa_stress.c:270:83:270:90 | SSA definition | SSA def(ret) | ssa_stress.c:270:93:270:95 | ret | +| ssa_stress.c:270:93:270:100 | SSA definition | SSA def(ret) | ssa_stress.c:271:3:271:5 | ret | +| ssa_stress.c:271:3:271:10 | SSA definition | SSA def(ret) | ssa_stress.c:271:13:271:15 | ret | +| ssa_stress.c:271:13:271:20 | SSA definition | SSA def(ret) | ssa_stress.c:271:23:271:25 | ret | +| ssa_stress.c:271:23:271:30 | SSA definition | SSA def(ret) | ssa_stress.c:271:33:271:35 | ret | +| ssa_stress.c:271:33:271:40 | SSA definition | SSA def(ret) | ssa_stress.c:271:43:271:45 | ret | +| ssa_stress.c:271:43:271:50 | SSA definition | SSA def(ret) | ssa_stress.c:271:53:271:55 | ret | +| ssa_stress.c:271:53:271:60 | SSA definition | SSA def(ret) | ssa_stress.c:271:63:271:65 | ret | +| ssa_stress.c:271:63:271:70 | SSA definition | SSA def(ret) | ssa_stress.c:271:73:271:75 | ret | +| ssa_stress.c:271:73:271:80 | SSA definition | SSA def(ret) | ssa_stress.c:271:83:271:85 | ret | +| ssa_stress.c:271:83:271:90 | SSA definition | SSA def(ret) | ssa_stress.c:271:93:271:95 | ret | +| ssa_stress.c:271:93:271:100 | SSA definition | SSA def(ret) | ssa_stress.c:272:3:272:5 | ret | +| ssa_stress.c:272:3:272:10 | SSA definition | SSA def(ret) | ssa_stress.c:272:13:272:15 | ret | +| ssa_stress.c:272:13:272:20 | SSA definition | SSA def(ret) | ssa_stress.c:272:23:272:25 | ret | +| ssa_stress.c:272:23:272:30 | SSA definition | SSA def(ret) | ssa_stress.c:272:33:272:35 | ret | +| ssa_stress.c:272:33:272:40 | SSA definition | SSA def(ret) | ssa_stress.c:272:43:272:45 | ret | +| ssa_stress.c:272:43:272:50 | SSA definition | SSA def(ret) | ssa_stress.c:272:53:272:55 | ret | +| ssa_stress.c:272:53:272:60 | SSA definition | SSA def(ret) | ssa_stress.c:272:63:272:65 | ret | +| ssa_stress.c:272:63:272:70 | SSA definition | SSA def(ret) | ssa_stress.c:272:73:272:75 | ret | +| ssa_stress.c:272:73:272:80 | SSA definition | SSA def(ret) | ssa_stress.c:272:83:272:85 | ret | +| ssa_stress.c:272:83:272:90 | SSA definition | SSA def(ret) | ssa_stress.c:272:93:272:95 | ret | +| ssa_stress.c:272:93:272:100 | SSA definition | SSA def(ret) | ssa_stress.c:273:3:273:5 | ret | +| ssa_stress.c:273:3:273:10 | SSA definition | SSA def(ret) | ssa_stress.c:273:13:273:15 | ret | +| ssa_stress.c:273:13:273:20 | SSA definition | SSA def(ret) | ssa_stress.c:273:23:273:25 | ret | +| ssa_stress.c:273:23:273:30 | SSA definition | SSA def(ret) | ssa_stress.c:273:33:273:35 | ret | +| ssa_stress.c:273:33:273:40 | SSA definition | SSA def(ret) | ssa_stress.c:273:43:273:45 | ret | +| ssa_stress.c:273:43:273:50 | SSA definition | SSA def(ret) | ssa_stress.c:273:53:273:55 | ret | +| ssa_stress.c:273:53:273:60 | SSA definition | SSA def(ret) | ssa_stress.c:273:63:273:65 | ret | +| ssa_stress.c:273:63:273:70 | SSA definition | SSA def(ret) | ssa_stress.c:273:73:273:75 | ret | +| ssa_stress.c:273:73:273:80 | SSA definition | SSA def(ret) | ssa_stress.c:273:83:273:85 | ret | +| ssa_stress.c:273:83:273:90 | SSA definition | SSA def(ret) | ssa_stress.c:273:93:273:95 | ret | +| ssa_stress.c:273:93:273:100 | SSA definition | SSA def(ret) | ssa_stress.c:274:3:274:5 | ret | +| ssa_stress.c:274:3:274:10 | SSA definition | SSA def(ret) | ssa_stress.c:274:13:274:15 | ret | +| ssa_stress.c:274:13:274:20 | SSA definition | SSA def(ret) | ssa_stress.c:274:23:274:25 | ret | +| ssa_stress.c:274:23:274:30 | SSA definition | SSA def(ret) | ssa_stress.c:274:33:274:35 | ret | +| ssa_stress.c:274:33:274:40 | SSA definition | SSA def(ret) | ssa_stress.c:274:43:274:45 | ret | +| ssa_stress.c:274:43:274:50 | SSA definition | SSA def(ret) | ssa_stress.c:274:53:274:55 | ret | +| ssa_stress.c:274:53:274:60 | SSA definition | SSA def(ret) | ssa_stress.c:274:63:274:65 | ret | +| ssa_stress.c:274:63:274:70 | SSA definition | SSA def(ret) | ssa_stress.c:274:73:274:75 | ret | +| ssa_stress.c:274:73:274:80 | SSA definition | SSA def(ret) | ssa_stress.c:274:83:274:85 | ret | +| ssa_stress.c:274:83:274:90 | SSA definition | SSA def(ret) | ssa_stress.c:274:93:274:95 | ret | +| ssa_stress.c:274:93:274:100 | SSA definition | SSA def(ret) | ssa_stress.c:275:3:275:5 | ret | +| ssa_stress.c:275:3:275:10 | SSA definition | SSA def(ret) | ssa_stress.c:275:13:275:15 | ret | +| ssa_stress.c:275:13:275:20 | SSA definition | SSA def(ret) | ssa_stress.c:275:23:275:25 | ret | +| ssa_stress.c:275:23:275:30 | SSA definition | SSA def(ret) | ssa_stress.c:275:33:275:35 | ret | +| ssa_stress.c:275:33:275:40 | SSA definition | SSA def(ret) | ssa_stress.c:275:43:275:45 | ret | +| ssa_stress.c:275:43:275:50 | SSA definition | SSA def(ret) | ssa_stress.c:275:53:275:55 | ret | +| ssa_stress.c:275:53:275:60 | SSA definition | SSA def(ret) | ssa_stress.c:275:63:275:65 | ret | +| ssa_stress.c:275:63:275:70 | SSA definition | SSA def(ret) | ssa_stress.c:275:73:275:75 | ret | +| ssa_stress.c:275:73:275:80 | SSA definition | SSA def(ret) | ssa_stress.c:275:83:275:85 | ret | +| ssa_stress.c:275:83:275:90 | SSA definition | SSA def(ret) | ssa_stress.c:275:93:275:95 | ret | +| ssa_stress.c:275:93:275:100 | SSA definition | SSA def(ret) | ssa_stress.c:276:3:276:5 | ret | +| ssa_stress.c:276:3:276:10 | SSA definition | SSA def(ret) | ssa_stress.c:276:13:276:15 | ret | +| ssa_stress.c:276:13:276:20 | SSA definition | SSA def(ret) | ssa_stress.c:276:23:276:25 | ret | +| ssa_stress.c:276:23:276:30 | SSA definition | SSA def(ret) | ssa_stress.c:276:33:276:35 | ret | +| ssa_stress.c:276:33:276:40 | SSA definition | SSA def(ret) | ssa_stress.c:276:43:276:45 | ret | +| ssa_stress.c:276:43:276:50 | SSA definition | SSA def(ret) | ssa_stress.c:276:53:276:55 | ret | +| ssa_stress.c:276:53:276:60 | SSA definition | SSA def(ret) | ssa_stress.c:276:63:276:65 | ret | +| ssa_stress.c:276:63:276:70 | SSA definition | SSA def(ret) | ssa_stress.c:276:73:276:75 | ret | +| ssa_stress.c:276:73:276:80 | SSA definition | SSA def(ret) | ssa_stress.c:276:83:276:85 | ret | +| ssa_stress.c:276:83:276:90 | SSA definition | SSA def(ret) | ssa_stress.c:276:93:276:95 | ret | +| ssa_stress.c:276:93:276:100 | SSA definition | SSA def(ret) | ssa_stress.c:277:3:277:5 | ret | +| ssa_stress.c:277:3:277:10 | SSA definition | SSA def(ret) | ssa_stress.c:277:13:277:15 | ret | +| ssa_stress.c:277:13:277:20 | SSA definition | SSA def(ret) | ssa_stress.c:277:23:277:25 | ret | +| ssa_stress.c:277:23:277:30 | SSA definition | SSA def(ret) | ssa_stress.c:277:33:277:35 | ret | +| ssa_stress.c:277:33:277:40 | SSA definition | SSA def(ret) | ssa_stress.c:277:43:277:45 | ret | +| ssa_stress.c:277:43:277:50 | SSA definition | SSA def(ret) | ssa_stress.c:277:53:277:55 | ret | +| ssa_stress.c:277:53:277:60 | SSA definition | SSA def(ret) | ssa_stress.c:277:63:277:65 | ret | +| ssa_stress.c:277:63:277:70 | SSA definition | SSA def(ret) | ssa_stress.c:277:73:277:75 | ret | +| ssa_stress.c:277:73:277:80 | SSA definition | SSA def(ret) | ssa_stress.c:277:83:277:85 | ret | +| ssa_stress.c:277:83:277:90 | SSA definition | SSA def(ret) | ssa_stress.c:277:93:277:95 | ret | +| ssa_stress.c:277:93:277:100 | SSA definition | SSA def(ret) | ssa_stress.c:278:3:278:5 | ret | +| ssa_stress.c:278:3:278:10 | SSA definition | SSA def(ret) | ssa_stress.c:278:13:278:15 | ret | +| ssa_stress.c:278:13:278:20 | SSA definition | SSA def(ret) | ssa_stress.c:278:23:278:25 | ret | +| ssa_stress.c:278:23:278:30 | SSA definition | SSA def(ret) | ssa_stress.c:278:33:278:35 | ret | +| ssa_stress.c:278:33:278:40 | SSA definition | SSA def(ret) | ssa_stress.c:278:43:278:45 | ret | +| ssa_stress.c:278:43:278:50 | SSA definition | SSA def(ret) | ssa_stress.c:278:53:278:55 | ret | +| ssa_stress.c:278:53:278:60 | SSA definition | SSA def(ret) | ssa_stress.c:278:63:278:65 | ret | +| ssa_stress.c:278:63:278:70 | SSA definition | SSA def(ret) | ssa_stress.c:278:73:278:75 | ret | +| ssa_stress.c:278:73:278:80 | SSA definition | SSA def(ret) | ssa_stress.c:278:83:278:85 | ret | +| ssa_stress.c:278:83:278:90 | SSA definition | SSA def(ret) | ssa_stress.c:278:93:278:95 | ret | +| ssa_stress.c:278:93:278:100 | SSA definition | SSA def(ret) | ssa_stress.c:281:3:281:5 | ret | +| ssa_stress.c:281:3:281:10 | SSA definition | SSA def(ret) | ssa_stress.c:281:13:281:15 | ret | +| ssa_stress.c:281:13:281:20 | SSA definition | SSA def(ret) | ssa_stress.c:281:23:281:25 | ret | +| ssa_stress.c:281:23:281:30 | SSA definition | SSA def(ret) | ssa_stress.c:281:33:281:35 | ret | +| ssa_stress.c:281:33:281:40 | SSA definition | SSA def(ret) | ssa_stress.c:281:43:281:45 | ret | +| ssa_stress.c:281:43:281:50 | SSA definition | SSA def(ret) | ssa_stress.c:281:53:281:55 | ret | +| ssa_stress.c:281:53:281:60 | SSA definition | SSA def(ret) | ssa_stress.c:281:63:281:65 | ret | +| ssa_stress.c:281:63:281:70 | SSA definition | SSA def(ret) | ssa_stress.c:281:73:281:75 | ret | +| ssa_stress.c:281:73:281:80 | SSA definition | SSA def(ret) | ssa_stress.c:281:83:281:85 | ret | +| ssa_stress.c:281:83:281:90 | SSA definition | SSA def(ret) | ssa_stress.c:281:93:281:95 | ret | +| ssa_stress.c:281:93:281:100 | SSA definition | SSA def(ret) | ssa_stress.c:282:3:282:5 | ret | +| ssa_stress.c:282:3:282:10 | SSA definition | SSA def(ret) | ssa_stress.c:282:13:282:15 | ret | +| ssa_stress.c:282:13:282:20 | SSA definition | SSA def(ret) | ssa_stress.c:282:23:282:25 | ret | +| ssa_stress.c:282:23:282:30 | SSA definition | SSA def(ret) | ssa_stress.c:282:33:282:35 | ret | +| ssa_stress.c:282:33:282:40 | SSA definition | SSA def(ret) | ssa_stress.c:282:43:282:45 | ret | +| ssa_stress.c:282:43:282:50 | SSA definition | SSA def(ret) | ssa_stress.c:282:53:282:55 | ret | +| ssa_stress.c:282:53:282:60 | SSA definition | SSA def(ret) | ssa_stress.c:282:63:282:65 | ret | +| ssa_stress.c:282:63:282:70 | SSA definition | SSA def(ret) | ssa_stress.c:282:73:282:75 | ret | +| ssa_stress.c:282:73:282:80 | SSA definition | SSA def(ret) | ssa_stress.c:282:83:282:85 | ret | +| ssa_stress.c:282:83:282:90 | SSA definition | SSA def(ret) | ssa_stress.c:282:93:282:95 | ret | +| ssa_stress.c:282:93:282:100 | SSA definition | SSA def(ret) | ssa_stress.c:283:3:283:5 | ret | +| ssa_stress.c:283:3:283:10 | SSA definition | SSA def(ret) | ssa_stress.c:283:13:283:15 | ret | +| ssa_stress.c:283:13:283:20 | SSA definition | SSA def(ret) | ssa_stress.c:283:23:283:25 | ret | +| ssa_stress.c:283:23:283:30 | SSA definition | SSA def(ret) | ssa_stress.c:283:33:283:35 | ret | +| ssa_stress.c:283:33:283:40 | SSA definition | SSA def(ret) | ssa_stress.c:283:43:283:45 | ret | +| ssa_stress.c:283:43:283:50 | SSA definition | SSA def(ret) | ssa_stress.c:283:53:283:55 | ret | +| ssa_stress.c:283:53:283:60 | SSA definition | SSA def(ret) | ssa_stress.c:283:63:283:65 | ret | +| ssa_stress.c:283:63:283:70 | SSA definition | SSA def(ret) | ssa_stress.c:283:73:283:75 | ret | +| ssa_stress.c:283:73:283:80 | SSA definition | SSA def(ret) | ssa_stress.c:283:83:283:85 | ret | +| ssa_stress.c:283:83:283:90 | SSA definition | SSA def(ret) | ssa_stress.c:283:93:283:95 | ret | +| ssa_stress.c:283:93:283:100 | SSA definition | SSA def(ret) | ssa_stress.c:284:3:284:5 | ret | +| ssa_stress.c:284:3:284:10 | SSA definition | SSA def(ret) | ssa_stress.c:284:13:284:15 | ret | +| ssa_stress.c:284:13:284:20 | SSA definition | SSA def(ret) | ssa_stress.c:284:23:284:25 | ret | +| ssa_stress.c:284:23:284:30 | SSA definition | SSA def(ret) | ssa_stress.c:284:33:284:35 | ret | +| ssa_stress.c:284:33:284:40 | SSA definition | SSA def(ret) | ssa_stress.c:284:43:284:45 | ret | +| ssa_stress.c:284:43:284:50 | SSA definition | SSA def(ret) | ssa_stress.c:284:53:284:55 | ret | +| ssa_stress.c:284:53:284:60 | SSA definition | SSA def(ret) | ssa_stress.c:284:63:284:65 | ret | +| ssa_stress.c:284:63:284:70 | SSA definition | SSA def(ret) | ssa_stress.c:284:73:284:75 | ret | +| ssa_stress.c:284:73:284:80 | SSA definition | SSA def(ret) | ssa_stress.c:284:83:284:85 | ret | +| ssa_stress.c:284:83:284:90 | SSA definition | SSA def(ret) | ssa_stress.c:284:93:284:95 | ret | +| ssa_stress.c:284:93:284:100 | SSA definition | SSA def(ret) | ssa_stress.c:285:3:285:5 | ret | +| ssa_stress.c:285:3:285:10 | SSA definition | SSA def(ret) | ssa_stress.c:285:13:285:15 | ret | +| ssa_stress.c:285:13:285:20 | SSA definition | SSA def(ret) | ssa_stress.c:285:23:285:25 | ret | +| ssa_stress.c:285:23:285:30 | SSA definition | SSA def(ret) | ssa_stress.c:285:33:285:35 | ret | +| ssa_stress.c:285:33:285:40 | SSA definition | SSA def(ret) | ssa_stress.c:285:43:285:45 | ret | +| ssa_stress.c:285:43:285:50 | SSA definition | SSA def(ret) | ssa_stress.c:285:53:285:55 | ret | +| ssa_stress.c:285:53:285:60 | SSA definition | SSA def(ret) | ssa_stress.c:285:63:285:65 | ret | +| ssa_stress.c:285:63:285:70 | SSA definition | SSA def(ret) | ssa_stress.c:285:73:285:75 | ret | +| ssa_stress.c:285:73:285:80 | SSA definition | SSA def(ret) | ssa_stress.c:285:83:285:85 | ret | +| ssa_stress.c:285:83:285:90 | SSA definition | SSA def(ret) | ssa_stress.c:285:93:285:95 | ret | +| ssa_stress.c:285:93:285:100 | SSA definition | SSA def(ret) | ssa_stress.c:286:3:286:5 | ret | +| ssa_stress.c:286:3:286:10 | SSA definition | SSA def(ret) | ssa_stress.c:286:13:286:15 | ret | +| ssa_stress.c:286:13:286:20 | SSA definition | SSA def(ret) | ssa_stress.c:286:23:286:25 | ret | +| ssa_stress.c:286:23:286:30 | SSA definition | SSA def(ret) | ssa_stress.c:286:33:286:35 | ret | +| ssa_stress.c:286:33:286:40 | SSA definition | SSA def(ret) | ssa_stress.c:286:43:286:45 | ret | +| ssa_stress.c:286:43:286:50 | SSA definition | SSA def(ret) | ssa_stress.c:286:53:286:55 | ret | +| ssa_stress.c:286:53:286:60 | SSA definition | SSA def(ret) | ssa_stress.c:286:63:286:65 | ret | +| ssa_stress.c:286:63:286:70 | SSA definition | SSA def(ret) | ssa_stress.c:286:73:286:75 | ret | +| ssa_stress.c:286:73:286:80 | SSA definition | SSA def(ret) | ssa_stress.c:286:83:286:85 | ret | +| ssa_stress.c:286:83:286:90 | SSA definition | SSA def(ret) | ssa_stress.c:286:93:286:95 | ret | +| ssa_stress.c:286:93:286:100 | SSA definition | SSA def(ret) | ssa_stress.c:287:3:287:5 | ret | +| ssa_stress.c:287:3:287:10 | SSA definition | SSA def(ret) | ssa_stress.c:287:13:287:15 | ret | +| ssa_stress.c:287:13:287:20 | SSA definition | SSA def(ret) | ssa_stress.c:287:23:287:25 | ret | +| ssa_stress.c:287:23:287:30 | SSA definition | SSA def(ret) | ssa_stress.c:287:33:287:35 | ret | +| ssa_stress.c:287:33:287:40 | SSA definition | SSA def(ret) | ssa_stress.c:287:43:287:45 | ret | +| ssa_stress.c:287:43:287:50 | SSA definition | SSA def(ret) | ssa_stress.c:287:53:287:55 | ret | +| ssa_stress.c:287:53:287:60 | SSA definition | SSA def(ret) | ssa_stress.c:287:63:287:65 | ret | +| ssa_stress.c:287:63:287:70 | SSA definition | SSA def(ret) | ssa_stress.c:287:73:287:75 | ret | +| ssa_stress.c:287:73:287:80 | SSA definition | SSA def(ret) | ssa_stress.c:287:83:287:85 | ret | +| ssa_stress.c:287:83:287:90 | SSA definition | SSA def(ret) | ssa_stress.c:287:93:287:95 | ret | +| ssa_stress.c:287:93:287:100 | SSA definition | SSA def(ret) | ssa_stress.c:288:3:288:5 | ret | +| ssa_stress.c:288:3:288:10 | SSA definition | SSA def(ret) | ssa_stress.c:288:13:288:15 | ret | +| ssa_stress.c:288:13:288:20 | SSA definition | SSA def(ret) | ssa_stress.c:288:23:288:25 | ret | +| ssa_stress.c:288:23:288:30 | SSA definition | SSA def(ret) | ssa_stress.c:288:33:288:35 | ret | +| ssa_stress.c:288:33:288:40 | SSA definition | SSA def(ret) | ssa_stress.c:288:43:288:45 | ret | +| ssa_stress.c:288:43:288:50 | SSA definition | SSA def(ret) | ssa_stress.c:288:53:288:55 | ret | +| ssa_stress.c:288:53:288:60 | SSA definition | SSA def(ret) | ssa_stress.c:288:63:288:65 | ret | +| ssa_stress.c:288:63:288:70 | SSA definition | SSA def(ret) | ssa_stress.c:288:73:288:75 | ret | +| ssa_stress.c:288:73:288:80 | SSA definition | SSA def(ret) | ssa_stress.c:288:83:288:85 | ret | +| ssa_stress.c:288:83:288:90 | SSA definition | SSA def(ret) | ssa_stress.c:288:93:288:95 | ret | +| ssa_stress.c:288:93:288:100 | SSA definition | SSA def(ret) | ssa_stress.c:289:3:289:5 | ret | +| ssa_stress.c:289:3:289:10 | SSA definition | SSA def(ret) | ssa_stress.c:289:13:289:15 | ret | +| ssa_stress.c:289:13:289:20 | SSA definition | SSA def(ret) | ssa_stress.c:289:23:289:25 | ret | +| ssa_stress.c:289:23:289:30 | SSA definition | SSA def(ret) | ssa_stress.c:289:33:289:35 | ret | +| ssa_stress.c:289:33:289:40 | SSA definition | SSA def(ret) | ssa_stress.c:289:43:289:45 | ret | +| ssa_stress.c:289:43:289:50 | SSA definition | SSA def(ret) | ssa_stress.c:289:53:289:55 | ret | +| ssa_stress.c:289:53:289:60 | SSA definition | SSA def(ret) | ssa_stress.c:289:63:289:65 | ret | +| ssa_stress.c:289:63:289:70 | SSA definition | SSA def(ret) | ssa_stress.c:289:73:289:75 | ret | +| ssa_stress.c:289:73:289:80 | SSA definition | SSA def(ret) | ssa_stress.c:289:83:289:85 | ret | +| ssa_stress.c:289:83:289:90 | SSA definition | SSA def(ret) | ssa_stress.c:289:93:289:95 | ret | +| ssa_stress.c:289:93:289:100 | SSA definition | SSA def(ret) | ssa_stress.c:290:3:290:5 | ret | +| ssa_stress.c:290:3:290:10 | SSA definition | SSA def(ret) | ssa_stress.c:290:13:290:15 | ret | +| ssa_stress.c:290:13:290:20 | SSA definition | SSA def(ret) | ssa_stress.c:290:23:290:25 | ret | +| ssa_stress.c:290:23:290:30 | SSA definition | SSA def(ret) | ssa_stress.c:290:33:290:35 | ret | +| ssa_stress.c:290:33:290:40 | SSA definition | SSA def(ret) | ssa_stress.c:290:43:290:45 | ret | +| ssa_stress.c:290:43:290:50 | SSA definition | SSA def(ret) | ssa_stress.c:290:53:290:55 | ret | +| ssa_stress.c:290:53:290:60 | SSA definition | SSA def(ret) | ssa_stress.c:290:63:290:65 | ret | +| ssa_stress.c:290:63:290:70 | SSA definition | SSA def(ret) | ssa_stress.c:290:73:290:75 | ret | +| ssa_stress.c:290:73:290:80 | SSA definition | SSA def(ret) | ssa_stress.c:290:83:290:85 | ret | +| ssa_stress.c:290:83:290:90 | SSA definition | SSA def(ret) | ssa_stress.c:290:93:290:95 | ret | +| ssa_stress.c:290:93:290:100 | SSA definition | SSA def(ret) | ssa_stress.c:293:3:293:5 | ret | +| ssa_stress.c:293:3:293:10 | SSA definition | SSA def(ret) | ssa_stress.c:293:13:293:15 | ret | +| ssa_stress.c:293:13:293:20 | SSA definition | SSA def(ret) | ssa_stress.c:293:23:293:25 | ret | +| ssa_stress.c:293:23:293:30 | SSA definition | SSA def(ret) | ssa_stress.c:293:33:293:35 | ret | +| ssa_stress.c:293:33:293:40 | SSA definition | SSA def(ret) | ssa_stress.c:293:43:293:45 | ret | +| ssa_stress.c:293:43:293:50 | SSA definition | SSA def(ret) | ssa_stress.c:293:53:293:55 | ret | +| ssa_stress.c:293:53:293:60 | SSA definition | SSA def(ret) | ssa_stress.c:293:63:293:65 | ret | +| ssa_stress.c:293:63:293:70 | SSA definition | SSA def(ret) | ssa_stress.c:293:73:293:75 | ret | +| ssa_stress.c:293:73:293:80 | SSA definition | SSA def(ret) | ssa_stress.c:293:83:293:85 | ret | +| ssa_stress.c:293:83:293:90 | SSA definition | SSA def(ret) | ssa_stress.c:293:93:293:95 | ret | +| ssa_stress.c:293:93:293:100 | SSA definition | SSA def(ret) | ssa_stress.c:294:3:294:5 | ret | +| ssa_stress.c:294:3:294:10 | SSA definition | SSA def(ret) | ssa_stress.c:294:13:294:15 | ret | +| ssa_stress.c:294:13:294:20 | SSA definition | SSA def(ret) | ssa_stress.c:294:23:294:25 | ret | +| ssa_stress.c:294:23:294:30 | SSA definition | SSA def(ret) | ssa_stress.c:294:33:294:35 | ret | +| ssa_stress.c:294:33:294:40 | SSA definition | SSA def(ret) | ssa_stress.c:294:43:294:45 | ret | +| ssa_stress.c:294:43:294:50 | SSA definition | SSA def(ret) | ssa_stress.c:294:53:294:55 | ret | +| ssa_stress.c:294:53:294:60 | SSA definition | SSA def(ret) | ssa_stress.c:294:63:294:65 | ret | +| ssa_stress.c:294:63:294:70 | SSA definition | SSA def(ret) | ssa_stress.c:294:73:294:75 | ret | +| ssa_stress.c:294:73:294:80 | SSA definition | SSA def(ret) | ssa_stress.c:294:83:294:85 | ret | +| ssa_stress.c:294:83:294:90 | SSA definition | SSA def(ret) | ssa_stress.c:294:93:294:95 | ret | +| ssa_stress.c:294:93:294:100 | SSA definition | SSA def(ret) | ssa_stress.c:295:3:295:5 | ret | +| ssa_stress.c:295:3:295:10 | SSA definition | SSA def(ret) | ssa_stress.c:295:13:295:15 | ret | +| ssa_stress.c:295:13:295:20 | SSA definition | SSA def(ret) | ssa_stress.c:295:23:295:25 | ret | +| ssa_stress.c:295:23:295:30 | SSA definition | SSA def(ret) | ssa_stress.c:295:33:295:35 | ret | +| ssa_stress.c:295:33:295:40 | SSA definition | SSA def(ret) | ssa_stress.c:295:43:295:45 | ret | +| ssa_stress.c:295:43:295:50 | SSA definition | SSA def(ret) | ssa_stress.c:295:53:295:55 | ret | +| ssa_stress.c:295:53:295:60 | SSA definition | SSA def(ret) | ssa_stress.c:295:63:295:65 | ret | +| ssa_stress.c:295:63:295:70 | SSA definition | SSA def(ret) | ssa_stress.c:295:73:295:75 | ret | +| ssa_stress.c:295:73:295:80 | SSA definition | SSA def(ret) | ssa_stress.c:295:83:295:85 | ret | +| ssa_stress.c:295:83:295:90 | SSA definition | SSA def(ret) | ssa_stress.c:295:93:295:95 | ret | +| ssa_stress.c:295:93:295:100 | SSA definition | SSA def(ret) | ssa_stress.c:296:3:296:5 | ret | +| ssa_stress.c:296:3:296:10 | SSA definition | SSA def(ret) | ssa_stress.c:296:13:296:15 | ret | +| ssa_stress.c:296:13:296:20 | SSA definition | SSA def(ret) | ssa_stress.c:296:23:296:25 | ret | +| ssa_stress.c:296:23:296:30 | SSA definition | SSA def(ret) | ssa_stress.c:296:33:296:35 | ret | +| ssa_stress.c:296:33:296:40 | SSA definition | SSA def(ret) | ssa_stress.c:296:43:296:45 | ret | +| ssa_stress.c:296:43:296:50 | SSA definition | SSA def(ret) | ssa_stress.c:296:53:296:55 | ret | +| ssa_stress.c:296:53:296:60 | SSA definition | SSA def(ret) | ssa_stress.c:296:63:296:65 | ret | +| ssa_stress.c:296:63:296:70 | SSA definition | SSA def(ret) | ssa_stress.c:296:73:296:75 | ret | +| ssa_stress.c:296:73:296:80 | SSA definition | SSA def(ret) | ssa_stress.c:296:83:296:85 | ret | +| ssa_stress.c:296:83:296:90 | SSA definition | SSA def(ret) | ssa_stress.c:296:93:296:95 | ret | +| ssa_stress.c:296:93:296:100 | SSA definition | SSA def(ret) | ssa_stress.c:297:3:297:5 | ret | +| ssa_stress.c:297:3:297:10 | SSA definition | SSA def(ret) | ssa_stress.c:297:13:297:15 | ret | +| ssa_stress.c:297:13:297:20 | SSA definition | SSA def(ret) | ssa_stress.c:297:23:297:25 | ret | +| ssa_stress.c:297:23:297:30 | SSA definition | SSA def(ret) | ssa_stress.c:297:33:297:35 | ret | +| ssa_stress.c:297:33:297:40 | SSA definition | SSA def(ret) | ssa_stress.c:297:43:297:45 | ret | +| ssa_stress.c:297:43:297:50 | SSA definition | SSA def(ret) | ssa_stress.c:297:53:297:55 | ret | +| ssa_stress.c:297:53:297:60 | SSA definition | SSA def(ret) | ssa_stress.c:297:63:297:65 | ret | +| ssa_stress.c:297:63:297:70 | SSA definition | SSA def(ret) | ssa_stress.c:297:73:297:75 | ret | +| ssa_stress.c:297:73:297:80 | SSA definition | SSA def(ret) | ssa_stress.c:297:83:297:85 | ret | +| ssa_stress.c:297:83:297:90 | SSA definition | SSA def(ret) | ssa_stress.c:297:93:297:95 | ret | +| ssa_stress.c:297:93:297:100 | SSA definition | SSA def(ret) | ssa_stress.c:298:3:298:5 | ret | +| ssa_stress.c:298:3:298:10 | SSA definition | SSA def(ret) | ssa_stress.c:298:13:298:15 | ret | +| ssa_stress.c:298:13:298:20 | SSA definition | SSA def(ret) | ssa_stress.c:298:23:298:25 | ret | +| ssa_stress.c:298:23:298:30 | SSA definition | SSA def(ret) | ssa_stress.c:298:33:298:35 | ret | +| ssa_stress.c:298:33:298:40 | SSA definition | SSA def(ret) | ssa_stress.c:298:43:298:45 | ret | +| ssa_stress.c:298:43:298:50 | SSA definition | SSA def(ret) | ssa_stress.c:298:53:298:55 | ret | +| ssa_stress.c:298:53:298:60 | SSA definition | SSA def(ret) | ssa_stress.c:298:63:298:65 | ret | +| ssa_stress.c:298:63:298:70 | SSA definition | SSA def(ret) | ssa_stress.c:298:73:298:75 | ret | +| ssa_stress.c:298:73:298:80 | SSA definition | SSA def(ret) | ssa_stress.c:298:83:298:85 | ret | +| ssa_stress.c:298:83:298:90 | SSA definition | SSA def(ret) | ssa_stress.c:298:93:298:95 | ret | +| ssa_stress.c:298:93:298:100 | SSA definition | SSA def(ret) | ssa_stress.c:299:3:299:5 | ret | +| ssa_stress.c:299:3:299:10 | SSA definition | SSA def(ret) | ssa_stress.c:299:13:299:15 | ret | +| ssa_stress.c:299:13:299:20 | SSA definition | SSA def(ret) | ssa_stress.c:299:23:299:25 | ret | +| ssa_stress.c:299:23:299:30 | SSA definition | SSA def(ret) | ssa_stress.c:299:33:299:35 | ret | +| ssa_stress.c:299:33:299:40 | SSA definition | SSA def(ret) | ssa_stress.c:299:43:299:45 | ret | +| ssa_stress.c:299:43:299:50 | SSA definition | SSA def(ret) | ssa_stress.c:299:53:299:55 | ret | +| ssa_stress.c:299:53:299:60 | SSA definition | SSA def(ret) | ssa_stress.c:299:63:299:65 | ret | +| ssa_stress.c:299:63:299:70 | SSA definition | SSA def(ret) | ssa_stress.c:299:73:299:75 | ret | +| ssa_stress.c:299:73:299:80 | SSA definition | SSA def(ret) | ssa_stress.c:299:83:299:85 | ret | +| ssa_stress.c:299:83:299:90 | SSA definition | SSA def(ret) | ssa_stress.c:299:93:299:95 | ret | +| ssa_stress.c:299:93:299:100 | SSA definition | SSA def(ret) | ssa_stress.c:300:3:300:5 | ret | +| ssa_stress.c:300:3:300:10 | SSA definition | SSA def(ret) | ssa_stress.c:300:13:300:15 | ret | +| ssa_stress.c:300:13:300:20 | SSA definition | SSA def(ret) | ssa_stress.c:300:23:300:25 | ret | +| ssa_stress.c:300:23:300:30 | SSA definition | SSA def(ret) | ssa_stress.c:300:33:300:35 | ret | +| ssa_stress.c:300:33:300:40 | SSA definition | SSA def(ret) | ssa_stress.c:300:43:300:45 | ret | +| ssa_stress.c:300:43:300:50 | SSA definition | SSA def(ret) | ssa_stress.c:300:53:300:55 | ret | +| ssa_stress.c:300:53:300:60 | SSA definition | SSA def(ret) | ssa_stress.c:300:63:300:65 | ret | +| ssa_stress.c:300:63:300:70 | SSA definition | SSA def(ret) | ssa_stress.c:300:73:300:75 | ret | +| ssa_stress.c:300:73:300:80 | SSA definition | SSA def(ret) | ssa_stress.c:300:83:300:85 | ret | +| ssa_stress.c:300:83:300:90 | SSA definition | SSA def(ret) | ssa_stress.c:300:93:300:95 | ret | +| ssa_stress.c:300:93:300:100 | SSA definition | SSA def(ret) | ssa_stress.c:301:3:301:5 | ret | +| ssa_stress.c:301:3:301:10 | SSA definition | SSA def(ret) | ssa_stress.c:301:13:301:15 | ret | +| ssa_stress.c:301:13:301:20 | SSA definition | SSA def(ret) | ssa_stress.c:301:23:301:25 | ret | +| ssa_stress.c:301:23:301:30 | SSA definition | SSA def(ret) | ssa_stress.c:301:33:301:35 | ret | +| ssa_stress.c:301:33:301:40 | SSA definition | SSA def(ret) | ssa_stress.c:301:43:301:45 | ret | +| ssa_stress.c:301:43:301:50 | SSA definition | SSA def(ret) | ssa_stress.c:301:53:301:55 | ret | +| ssa_stress.c:301:53:301:60 | SSA definition | SSA def(ret) | ssa_stress.c:301:63:301:65 | ret | +| ssa_stress.c:301:63:301:70 | SSA definition | SSA def(ret) | ssa_stress.c:301:73:301:75 | ret | +| ssa_stress.c:301:73:301:80 | SSA definition | SSA def(ret) | ssa_stress.c:301:83:301:85 | ret | +| ssa_stress.c:301:83:301:90 | SSA definition | SSA def(ret) | ssa_stress.c:301:93:301:95 | ret | +| ssa_stress.c:301:93:301:100 | SSA definition | SSA def(ret) | ssa_stress.c:302:3:302:5 | ret | +| ssa_stress.c:302:3:302:10 | SSA definition | SSA def(ret) | ssa_stress.c:302:13:302:15 | ret | +| ssa_stress.c:302:13:302:20 | SSA definition | SSA def(ret) | ssa_stress.c:302:23:302:25 | ret | +| ssa_stress.c:302:23:302:30 | SSA definition | SSA def(ret) | ssa_stress.c:302:33:302:35 | ret | +| ssa_stress.c:302:33:302:40 | SSA definition | SSA def(ret) | ssa_stress.c:302:43:302:45 | ret | +| ssa_stress.c:302:43:302:50 | SSA definition | SSA def(ret) | ssa_stress.c:302:53:302:55 | ret | +| ssa_stress.c:302:53:302:60 | SSA definition | SSA def(ret) | ssa_stress.c:302:63:302:65 | ret | +| ssa_stress.c:302:63:302:70 | SSA definition | SSA def(ret) | ssa_stress.c:302:73:302:75 | ret | +| ssa_stress.c:302:73:302:80 | SSA definition | SSA def(ret) | ssa_stress.c:302:83:302:85 | ret | +| ssa_stress.c:302:83:302:90 | SSA definition | SSA def(ret) | ssa_stress.c:302:93:302:95 | ret | +| ssa_stress.c:302:93:302:100 | SSA definition | SSA def(ret) | ssa_stress.c:305:10:305:12 | ret | diff --git a/cpp/ql/test/library-tests/controlflow_stresstest/SsaDefUsePairs.ql b/cpp/ql/test/library-tests/controlflow_stresstest/SsaDefUsePairs.ql new file mode 100644 index 000000000000..2328645e098c --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow_stresstest/SsaDefUsePairs.ql @@ -0,0 +1,12 @@ +/** + * @name Ssa def-use pairs test + * @description List all the uses for each SsaDefinition + * @kind test + */ + +import cpp +import semmle.code.cpp.controlflow.SSA + +from SsaDefinition def, LocalScopeVariable var, Expr use +where def.getAUse(var) = use +select def, def.toString(var), use diff --git a/cpp/ql/test/library-tests/controlflow_stresstest/ssa_stress.c b/cpp/ql/test/library-tests/controlflow_stresstest/ssa_stress.c new file mode 100644 index 000000000000..b145b4f96b1c --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow_stresstest/ssa_stress.c @@ -0,0 +1,306 @@ +// This is a stress test to make sure that SSA can handle large basic blocks (see ODASA-5774). +int ssa_stress() { + int ret = 0; + + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + // 0100 + + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + // 0200 + + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + // 0300 + + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + // 0400 + + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + // 0500 + + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + // 0600 + + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + // 0700 + + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + // 0800 + + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + // 0900 + + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + // 1000 + + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + // 1100 + + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + // 1200 + + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + // 1300 + + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + // 1400 + + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + // 1500 + + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + // 1600 + + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + // 1700 + + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + // 1800 + + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + // 1900 + + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + // 2000 + + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + // 2100 + + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + // 2200 + + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + // 2300 + + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + // 2400 + + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + ret += 0; ret += 1; ret += 2; ret += 3; ret += 4; ret += 5; ret += 6; ret += 7; ret += 8; ret += 9; + // 2500 + + return ret; +} diff --git a/cpp/ql/test/library-tests/conversions/complex.c b/cpp/ql/test/library-tests/conversions/complex.c new file mode 100644 index 000000000000..066eb1211eed --- /dev/null +++ b/cpp/ql/test/library-tests/conversions/complex.c @@ -0,0 +1,9 @@ +void ComplexNumbers() { + double r = 0.0; + _Imaginary double i = 1.0; + _Complex double c = 0.0; + r = c; + c = i; + c = r; +} +// semmle-extractor-options: --microsoft --edg --c99 diff --git a/cpp/ql/test/library-tests/conversions/conversions.cpp b/cpp/ql/test/library-tests/conversions/conversions.cpp new file mode 100644 index 000000000000..5409a4ad572f --- /dev/null +++ b/cpp/ql/test/library-tests/conversions/conversions.cpp @@ -0,0 +1,237 @@ +typedef unsigned short ushort; + +enum E { + E0, + E1 +}; + +enum class EC : int { + EC0, + EC1 +}; + +void ArithmeticConversions() { + char c = 0; + unsigned char uc = 0; + short s = 0; + unsigned short us = 0; + int i = 0; + unsigned int ui = 0; + long l = 0; + unsigned long ul = 0; + long long ll = 0; + unsigned long long ull = 0; + float f = 0; + double d = 0; + wchar_t wc = 0; + E e{}; + EC ec{}; + + c = uc; + c = (char)uc; + c = char(uc); + c = static_cast(uc); + i = s; + i = (int)s; + i = int(s); + i = static_cast(s); + us = i; + us = (unsigned short)i; + us = ushort(i); + us = static_cast(i); + + i = d; + i = (int)d; + i = int(d); + i = static_cast(d); + + f = c; + f = (float)c; + f = float(c); + f = static_cast(c); + + f = d; + f = (float)d; + f = float(d); + f = static_cast(d); + + d = f; + d = (double)f; + d = double(f); + d = static_cast(f); + + i = E0; + i = e; + i = static_cast(EC::EC0); + i = static_cast(ec); + e = static_cast(i); + ec = static_cast(i); +} + +struct S { + int x; + double y; +}; + +void ConversionsToBool() { + bool b = 0; + int i = 0; + double d = 0; + void* p = nullptr; + int S::* pmd = nullptr; + + if (b) { + } + else if ((bool)b) { + } + else if (i) { + } + else if (d) { + } + else if (p) { + } + else if (pmd) { + } +} + +struct Base { + int b1; + void BaseMethod(); +}; + +struct Middle : Base { + int m1; + void MiddleMethod(); +}; + +struct Derived : Middle { + int d1; + void DerivedMethod(); +}; + +void HierarchyCasts() { + Base b; + Middle m; + Derived d; + + Base* pb = &b; + Middle* pm = &m; + Derived* pd = &d; + + b = m; + b = (Base)m; + b = static_cast(m); + pb = pm; + pb = (Base*)pm; + pb = static_cast(pm); + pb = reinterpret_cast(pm); + + m = (Middle&)b; + m = static_cast(b); + pm = (Middle*)pb; + pm = static_cast(pb); + pm = reinterpret_cast(pb); + + b = d; + b = (Base)d; + b = static_cast(d); + pb = pd; + pb = (Base*)pd; + pb = static_cast(pd); + pb = reinterpret_cast(pd); + + d = (Derived&)b; + d = static_cast(b); + pd = (Derived*)pb; + pd = static_cast(pb); + pd = reinterpret_cast(pb); +} + +void PTMCasts() { + int Base::* pb = &Base::b1; + void (Base::* pmfb)() = &Base::BaseMethod; + int Middle::* pm = &Middle::m1; + void (Middle::* pmfm)() = &Middle::MiddleMethod; + int Derived::* pd = &Derived::d1; + void (Derived::* pmfd)() = &Derived::DerivedMethod; + + pb = (int Base::*)pm; + pmfb = (void (Base::*)())pmfm; + pb = static_cast(pm); + pmfb = static_cast(pmfm); + + pm = pb; + pmfm = pmfb; + pm = (int Middle::*)pb; + pmfm = (void (Middle::*)())pmfb; + pm = static_cast(pb); + pmfm = static_cast(pmfb); + + pb = (int Base::*)pd; + pmfb = (void (Base::*)())pmfd; + pb = static_cast(pd); + pmfb = static_cast(pmfd); + + pd = pb; + pmfd = pmfb; + pd = (int Derived::*)pb; + pmfd = (void (Derived::*)())pmfb; + pd = static_cast(pb); + pmfd = static_cast(pmfb); +} + +struct String { + String(); + String(const String&); + ~String(); +}; + +void Adjust() { + const String& s1 = String(); // prvalue adjustment + Base b; + Derived d; + const Base& rb = true ? b : d; // glvalue adjustment + const Base& r = (Base&)s1; +} + +void QualificationConversions() { + const int* pc = nullptr; + const volatile int* pcv = nullptr; + pcv = pc; + pc = const_cast(pcv); +} + +void PointerIntegralConversions() { + void* p = nullptr; + long n = (long)p; + n = reinterpret_cast(p); + p = (void*)n; + p = reinterpret_cast(n); +} + +struct PolymorphicBase { + virtual ~PolymorphicBase(); +}; + +struct PolymorphicDerived : PolymorphicBase { +}; + +void DynamicCast() { + PolymorphicBase b; + PolymorphicDerived d; + + PolymorphicBase* pb = &b; + PolymorphicDerived* pd = &d; + + // These two casts are represented as BaseClassCasts because they can be resolved at compile time. + pb = dynamic_cast(pd); + PolymorphicBase& rb = dynamic_cast(d); + + pd = dynamic_cast(pb); + PolymorphicDerived& rd = dynamic_cast(b); +} + +void FuncPtrConversions(int(*pfn)(int), void* p) { + p = (void*)pfn; + pfn = (int(*)(int))p; +} diff --git a/cpp/ql/test/library-tests/conversions/conversions.expected b/cpp/ql/test/library-tests/conversions/conversions.expected new file mode 100644 index 000000000000..348f7636f55e --- /dev/null +++ b/cpp/ql/test/library-tests/conversions/conversions.expected @@ -0,0 +1,145 @@ +| complex.c:3:25:3:27 | (_Imaginary double)... | floating point conversion | prval | _Imaginary double | double | +| complex.c:4:23:4:25 | (_Complex double)... | floating point conversion | prval | _Complex double | double | +| complex.c:5:7:5:7 | (double)... | floating point conversion | prval | double | _Complex double | +| complex.c:6:7:6:7 | (_Complex double)... | floating point conversion | prval | _Complex double | _Imaginary double | +| complex.c:7:7:7:7 | (_Complex double)... | floating point conversion | prval | _Complex double | double | +| conversions.cpp:14:12:14:12 | (char)... | integral conversion | prval | char | int | +| conversions.cpp:15:22:15:22 | (unsigned char)... | integral conversion | prval | unsigned char | int | +| conversions.cpp:16:13:16:13 | (short)... | integral conversion | prval | short | int | +| conversions.cpp:17:23:17:23 | (unsigned short)... | integral conversion | prval | unsigned short | int | +| conversions.cpp:19:21:19:21 | (unsigned int)... | integral conversion | prval | unsigned int | int | +| conversions.cpp:20:12:20:12 | (long)... | integral conversion | prval | long | int | +| conversions.cpp:21:22:21:22 | (unsigned long)... | integral conversion | prval | unsigned long | int | +| conversions.cpp:22:18:22:18 | (long long)... | integral conversion | prval | long long | int | +| conversions.cpp:23:28:23:28 | (unsigned long long)... | integral conversion | prval | unsigned long long | int | +| conversions.cpp:24:13:24:13 | (float)... | integral to floating point conversion | prval | float | int | +| conversions.cpp:25:14:25:14 | (double)... | integral to floating point conversion | prval | double | int | +| conversions.cpp:26:16:26:16 | (wchar_t)... | integral conversion | prval | wchar_t | int | +| conversions.cpp:30:7:30:8 | (char)... | integral conversion | prval | char | unsigned char | +| conversions.cpp:31:7:31:14 | (char)... | integral conversion | prval | char | unsigned char | +| conversions.cpp:32:7:32:14 | (char)... | integral conversion | prval | char | unsigned char | +| conversions.cpp:33:7:33:27 | static_cast... | integral conversion | prval | char | unsigned char | +| conversions.cpp:34:7:34:7 | (int)... | integral conversion | prval | int | short | +| conversions.cpp:35:7:35:12 | (int)... | integral conversion | prval | int | short | +| conversions.cpp:36:7:36:12 | (int)... | integral conversion | prval | int | short | +| conversions.cpp:37:7:37:25 | static_cast... | integral conversion | prval | int | short | +| conversions.cpp:38:8:38:8 | (unsigned short)... | integral conversion | prval | unsigned short | int | +| conversions.cpp:39:8:39:24 | (unsigned short)... | integral conversion | prval | unsigned short | int | +| conversions.cpp:40:8:40:16 | (ushort)... | integral conversion | prval | ushort | int | +| conversions.cpp:41:8:41:37 | static_cast... | integral conversion | prval | unsigned short | int | +| conversions.cpp:43:7:43:7 | (int)... | floating point to integral conversion | prval | int | double | +| conversions.cpp:44:7:44:12 | (int)... | floating point to integral conversion | prval | int | double | +| conversions.cpp:45:7:45:12 | (int)... | floating point to integral conversion | prval | int | double | +| conversions.cpp:46:7:46:25 | static_cast... | floating point to integral conversion | prval | int | double | +| conversions.cpp:48:7:48:7 | (float)... | integral to floating point conversion | prval | float | char | +| conversions.cpp:49:7:49:14 | (float)... | integral to floating point conversion | prval | float | char | +| conversions.cpp:50:7:50:14 | (float)... | integral to floating point conversion | prval | float | char | +| conversions.cpp:51:7:51:27 | static_cast... | integral to floating point conversion | prval | float | char | +| conversions.cpp:53:7:53:7 | (float)... | floating point conversion | prval | float | double | +| conversions.cpp:54:7:54:14 | (float)... | floating point conversion | prval | float | double | +| conversions.cpp:55:7:55:14 | (float)... | floating point conversion | prval | float | double | +| conversions.cpp:56:7:56:27 | static_cast... | floating point conversion | prval | float | double | +| conversions.cpp:58:7:58:7 | (double)... | floating point conversion | prval | double | float | +| conversions.cpp:59:7:59:15 | (double)... | floating point conversion | prval | double | float | +| conversions.cpp:60:7:60:15 | (double)... | floating point conversion | prval | double | float | +| conversions.cpp:61:7:61:28 | static_cast... | floating point conversion | prval | double | float | +| conversions.cpp:63:7:63:8 | (int)... | integral conversion | prval | int | E | +| conversions.cpp:64:7:64:7 | (int)... | integral conversion | prval | int | E | +| conversions.cpp:65:7:65:31 | static_cast... | integral conversion | prval | int | EC | +| conversions.cpp:66:7:66:26 | static_cast... | integral conversion | prval | int | EC | +| conversions.cpp:67:7:67:23 | static_cast... | integral conversion | prval | E | int | +| conversions.cpp:68:8:68:25 | static_cast... | integral conversion | prval | EC | int | +| conversions.cpp:77:12:77:12 | (bool)... | conversion to bool | prval | bool | int | +| conversions.cpp:79:14:79:14 | (double)... | integral to floating point conversion | prval | double | int | +| conversions.cpp:80:13:80:19 | (void *)... | pointer conversion | prval | void * | decltype(nullptr) | +| conversions.cpp:81:18:81:24 | (..:: *)... | pointer-to-member conversion | prval | ..:: * | decltype(nullptr) | +| conversions.cpp:85:12:85:18 | (bool)... | conversion to bool | prval | bool | bool | +| conversions.cpp:87:12:87:12 | (bool)... | conversion to bool | prval | bool | int | +| conversions.cpp:89:12:89:12 | (bool)... | conversion to bool | prval | bool | double | +| conversions.cpp:91:12:91:12 | (bool)... | conversion to bool | prval | bool | void * | +| conversions.cpp:93:12:93:14 | (bool)... | conversion to bool | prval | bool | ..:: * | +| conversions.cpp:121:7:121:7 | (Base)... | base class conversion | prval(load) | Base | Middle | +| conversions.cpp:122:13:122:13 | (Base)... | base class conversion | prval(load) | Base | Middle | +| conversions.cpp:123:25:123:25 | (Base)... | base class conversion | prval(load) | Base | Middle | +| conversions.cpp:124:8:124:9 | (Base *)... | base class conversion | prval | Base * | Middle * | +| conversions.cpp:125:8:125:16 | (Base *)... | base class conversion | prval | Base * | Middle * | +| conversions.cpp:126:8:126:29 | static_cast... | base class conversion | prval | Base * | Middle * | +| conversions.cpp:127:8:127:34 | reinterpret_cast... | pointer conversion | prval | Base * | Middle * | +| conversions.cpp:129:7:129:16 | (Middle)... | derived class conversion | prval(load) | Middle | Base | +| conversions.cpp:130:7:130:29 | static_cast... | derived class conversion | prval(load) | Middle | Base | +| conversions.cpp:131:8:131:18 | (Middle *)... | derived class conversion | prval | Middle * | Base * | +| conversions.cpp:132:8:132:31 | static_cast... | derived class conversion | prval | Middle * | Base * | +| conversions.cpp:133:8:133:36 | reinterpret_cast... | pointer conversion | prval | Middle * | Base * | +| conversions.cpp:135:7:135:7 | (Base)... | base class conversion | prval(load) | Base | Middle | +| conversions.cpp:135:7:135:7 | (Middle)... | base class conversion | lval | Middle | Derived | +| conversions.cpp:136:13:136:13 | (Base)... | base class conversion | prval(load) | Base | Middle | +| conversions.cpp:136:13:136:13 | (Middle)... | base class conversion | lval | Middle | Derived | +| conversions.cpp:137:25:137:25 | (Base)... | base class conversion | prval(load) | Base | Middle | +| conversions.cpp:137:25:137:25 | (Middle)... | base class conversion | lval | Middle | Derived | +| conversions.cpp:138:8:138:9 | (Base *)... | base class conversion | prval | Base * | Middle * | +| conversions.cpp:138:8:138:9 | (Middle *)... | base class conversion | prval | Middle * | Derived * | +| conversions.cpp:139:8:139:16 | (Base *)... | base class conversion | prval | Base * | Middle * | +| conversions.cpp:139:15:139:16 | (Middle *)... | base class conversion | prval | Middle * | Derived * | +| conversions.cpp:140:8:140:29 | static_cast... | base class conversion | prval | Base * | Middle * | +| conversions.cpp:140:27:140:28 | (Middle *)... | base class conversion | prval | Middle * | Derived * | +| conversions.cpp:141:8:141:34 | reinterpret_cast... | pointer conversion | prval | Base * | Derived * | +| conversions.cpp:143:7:143:17 | (Derived)... | derived class conversion | prval(load) | Derived | Middle | +| conversions.cpp:143:17:143:17 | (Middle)... | derived class conversion | lval | Middle | Base | +| conversions.cpp:144:7:144:30 | static_cast... | derived class conversion | prval(load) | Derived | Middle | +| conversions.cpp:144:29:144:29 | (Middle)... | derived class conversion | lval | Middle | Base | +| conversions.cpp:145:8:145:19 | (Derived *)... | derived class conversion | prval | Derived * | Middle * | +| conversions.cpp:145:18:145:19 | (Middle *)... | derived class conversion | prval | Middle * | Base * | +| conversions.cpp:146:8:146:32 | static_cast... | derived class conversion | prval | Derived * | Middle * | +| conversions.cpp:146:30:146:31 | (Middle *)... | derived class conversion | prval | Middle * | Base * | +| conversions.cpp:147:8:147:37 | reinterpret_cast... | pointer conversion | prval | Derived * | Base * | +| conversions.cpp:158:8:158:22 | (..:: *)... | pointer-to-member base class conversion | prval | ..:: * | ..:: * | +| conversions.cpp:159:10:159:31 | (..:: *)... | pointer-to-member base class conversion | prval | ..:: * | ..:: * | +| conversions.cpp:160:8:160:35 | static_cast<..:: *>... | pointer-to-member base class conversion | prval | ..:: * | ..:: * | +| conversions.cpp:161:10:161:44 | static_cast<..:: *>... | pointer-to-member base class conversion | prval | ..:: * | ..:: * | +| conversions.cpp:163:8:163:9 | (..:: *)... | pointer-to-member derived class conversion | prval | ..:: * | ..:: * | +| conversions.cpp:164:10:164:13 | (..:: *)... | pointer-to-member derived class conversion | prval | ..:: * | ..:: * | +| conversions.cpp:165:8:165:24 | (..:: *)... | pointer-to-member derived class conversion | prval | ..:: * | ..:: * | +| conversions.cpp:166:10:166:33 | (..:: *)... | pointer-to-member derived class conversion | prval | ..:: * | ..:: * | +| conversions.cpp:167:8:167:37 | static_cast<..:: *>... | pointer-to-member derived class conversion | prval | ..:: * | ..:: * | +| conversions.cpp:168:10:168:46 | static_cast<..:: *>... | pointer-to-member derived class conversion | prval | ..:: * | ..:: * | +| conversions.cpp:170:8:170:22 | (..:: *)... | pointer-to-member base class conversion | prval | ..:: * | ..:: * | +| conversions.cpp:170:21:170:22 | (..:: *)... | pointer-to-member base class conversion | prval | ..:: * | ..:: * | +| conversions.cpp:171:10:171:31 | (..:: *)... | pointer-to-member base class conversion | prval | ..:: * | ..:: * | +| conversions.cpp:171:28:171:31 | (..:: *)... | pointer-to-member base class conversion | prval | ..:: * | ..:: * | +| conversions.cpp:172:8:172:35 | static_cast<..:: *>... | pointer-to-member base class conversion | prval | ..:: * | ..:: * | +| conversions.cpp:172:33:172:34 | (..:: *)... | pointer-to-member base class conversion | prval | ..:: * | ..:: * | +| conversions.cpp:173:10:173:44 | static_cast<..:: *>... | pointer-to-member base class conversion | prval | ..:: * | ..:: * | +| conversions.cpp:173:40:173:43 | (..:: *)... | pointer-to-member base class conversion | prval | ..:: * | ..:: * | +| conversions.cpp:175:8:175:9 | (..:: *)... | pointer-to-member derived class conversion | prval | ..:: * | ..:: * | +| conversions.cpp:175:8:175:9 | (..:: *)... | pointer-to-member derived class conversion | prval | ..:: * | ..:: * | +| conversions.cpp:176:10:176:13 | (..:: *)... | pointer-to-member derived class conversion | prval | ..:: * | ..:: * | +| conversions.cpp:176:10:176:13 | (..:: *)... | pointer-to-member derived class conversion | prval | ..:: * | ..:: * | +| conversions.cpp:177:8:177:25 | (..:: *)... | pointer-to-member derived class conversion | prval | ..:: * | ..:: * | +| conversions.cpp:177:24:177:25 | (..:: *)... | pointer-to-member derived class conversion | prval | ..:: * | ..:: * | +| conversions.cpp:178:10:178:34 | (..:: *)... | pointer-to-member derived class conversion | prval | ..:: * | ..:: * | +| conversions.cpp:178:31:178:34 | (..:: *)... | pointer-to-member derived class conversion | prval | ..:: * | ..:: * | +| conversions.cpp:179:8:179:38 | static_cast<..:: *>... | pointer-to-member derived class conversion | prval | ..:: * | ..:: * | +| conversions.cpp:179:36:179:37 | (..:: *)... | pointer-to-member derived class conversion | prval | ..:: * | ..:: * | +| conversions.cpp:180:10:180:47 | static_cast<..:: *>... | pointer-to-member derived class conversion | prval | ..:: * | ..:: * | +| conversions.cpp:180:43:180:46 | (..:: *)... | pointer-to-member derived class conversion | prval | ..:: * | ..:: * | +| conversions.cpp:190:22:190:29 | (const String)... | prvalue adjustment conversion | prval | const String | void | +| conversions.cpp:193:20:193:31 | (const Base)... | glvalue conversion | lval | const Base | Base | +| conversions.cpp:193:31:193:31 | (Base)... | base class conversion | lval | Base | Middle | +| conversions.cpp:193:31:193:31 | (Middle)... | base class conversion | lval | Middle | Derived | +| conversions.cpp:194:19:194:27 | (Base)... | glvalue conversion | lval | Base | const String | +| conversions.cpp:194:19:194:27 | (const Base)... | glvalue conversion | lval | const Base | Base | +| conversions.cpp:198:19:198:25 | (const int *)... | pointer conversion | prval | const int * | decltype(nullptr) | +| conversions.cpp:199:29:199:35 | (const volatile int *)... | pointer conversion | prval | const volatile int * | decltype(nullptr) | +| conversions.cpp:200:9:200:10 | (const volatile int *)... | pointer conversion | prval | const volatile int * | const int * | +| conversions.cpp:201:8:201:34 | const_cast... | pointer conversion | prval | const int * | const volatile int * | +| conversions.cpp:205:13:205:19 | (void *)... | pointer conversion | prval | void * | decltype(nullptr) | +| conversions.cpp:206:12:206:18 | (long)... | pointer to integral conversion | prval | long | void * | +| conversions.cpp:207:7:207:31 | reinterpret_cast... | pointer to integral conversion | prval | long | void * | +| conversions.cpp:208:7:208:14 | (void *)... | integral to pointer conversion | prval | void * | long | +| conversions.cpp:209:7:209:32 | reinterpret_cast... | integral to pointer conversion | prval | void * | long | +| conversions.cpp:227:8:227:41 | (PolymorphicBase *)... | base class conversion | prval | PolymorphicBase * | PolymorphicDerived * | +| conversions.cpp:228:25:228:57 | (PolymorphicBase)... | base class conversion | lval | PolymorphicBase | PolymorphicDerived | +| conversions.cpp:230:8:230:44 | dynamic_cast... | dynamic_cast | prval | PolymorphicDerived * | PolymorphicBase * | +| conversions.cpp:231:28:231:63 | dynamic_cast... | dynamic_cast | lval | PolymorphicDerived | PolymorphicBase | +| conversions.cpp:235:7:235:16 | (void *)... | pointer conversion | prval | void * | ..(*)(..) | +| conversions.cpp:236:9:236:22 | (..(*)(..))... | pointer conversion | prval | ..(*)(..) | void * | diff --git a/cpp/ql/test/library-tests/conversions/conversions.ql b/cpp/ql/test/library-tests/conversions/conversions.ql new file mode 100644 index 000000000000..78af6ed17f02 --- /dev/null +++ b/cpp/ql/test/library-tests/conversions/conversions.ql @@ -0,0 +1,15 @@ +import default + +string getValueCategoryString(Expr expr) { + if expr.isLValueCategory() then + result = "lval" + else if expr.isXValueCategory() then + result = "xval" + else if expr.hasLValueToRValueConversion() then + result = "prval(load)" + else + result = "prval" +} + +from Cast cast +select cast, cast.getSemanticConversionString(), getValueCategoryString(cast), cast.getType().toString(), cast.getExpr().getType().toString() diff --git a/cpp/ql/test/library-tests/conversions/sanity.expected b/cpp/ql/test/library-tests/conversions/sanity.expected new file mode 100644 index 000000000000..cf988ff5eb3f --- /dev/null +++ b/cpp/ql/test/library-tests/conversions/sanity.expected @@ -0,0 +1,3 @@ +multipleSemanticConversionStrings +missingSemanticConversionString +unknownSemanticConversionString diff --git a/cpp/ql/test/library-tests/conversions/sanity.qlref b/cpp/ql/test/library-tests/conversions/sanity.qlref new file mode 100644 index 000000000000..fb9c931e2241 --- /dev/null +++ b/cpp/ql/test/library-tests/conversions/sanity.qlref @@ -0,0 +1 @@ +semmle/code/cpp/ASTSanity.ql diff --git a/cpp/ql/test/library-tests/cpp11_g/cfg.expected b/cpp/ql/test/library-tests/cpp11_g/cfg.expected new file mode 100644 index 000000000000..1f07b3035cc7 --- /dev/null +++ b/cpp/ql/test/library-tests/cpp11_g/cfg.expected @@ -0,0 +1,77 @@ +| choose_second | 24 | 1 | { ... } | 24 | return ... | +| choose_second | 24 | 2 | return ... | 24 | choose_second | +| choose_second | 27 | 1 | { ... } | 28 | return ... | +| choose_second | 28 | 2 | return ... | 25 | choose_second | +| count_args_1 prototype instantiation | 11 | 1 | { ... } | 12 | return ... | +| count_args_1 prototype instantiation | 12 | 2 | return ... | 12 | sizeof...(...) | +| count_args_1 prototype instantiation | 12 | 3 | sizeof...(...) | 11 | count_args_1 | +| count_args_1<> | 11 | 1 | { ... } | 12 | return ... | +| count_args_1<> | 12 | 2 | return ... | 12 | 0 | +| count_args_1<> | 12 | 3 | 0 | 11 | count_args_1 | +| count_args_2 prototype instantiation | 15 | 1 | { ... } | 16 | return ... | +| count_args_2 prototype instantiation | 16 | 2 | return ... | 16 | a | +| count_args_2 prototype instantiation | 16 | 3 | a | 16 | sizeof...(...) | +| count_args_2 prototype instantiation | 16 | 4 | sizeof...(...) | 15 | count_args_2 | +| count_args_2 | 15 | 1 | { ... } | 16 | return ... | +| count_args_2 | 16 | 2 | return ... | 16 | 2 | +| count_args_2 | 16 | 3 | 2 | 15 | count_args_2 | +| make_val_from_one prototype instantiation | 1 | 1 | { ... } | 2 | declaration | +| make_val_from_one prototype instantiation | 2 | 2 | declaration | 2 | initializer for val | +| make_val_from_one prototype instantiation | 2 | 3 | initializer for val | 2 | 1 | +| make_val_from_one prototype instantiation | 2 | 4 | 1 | 2 | ({...}) | +| make_val_from_one prototype instantiation | 2 | 5 | ({...}) | 2 | call to unknown function | +| make_val_from_one prototype instantiation | 2 | 6 | call to unknown function | 3 | return ... | +| make_val_from_one prototype instantiation | 3 | 7 | return ... | 3 | val | +| make_val_from_one prototype instantiation | 3 | 8 | val | 1 | make_val_from_one | +| make_val_from_one | 1 | 1 | { ... } | 2 | declaration | +| make_val_from_one | 2 | 2 | declaration | 2 | initializer for val | +| make_val_from_one | 2 | 3 | initializer for val | 2 | 1 | +| make_val_from_one | 2 | 4 | 1 | 3 | return ... | +| make_val_from_one | 3 | 5 | return ... | 3 | val | +| make_val_from_one | 3 | 6 | val | 1 | make_val_from_one | +| make_val_from_three_and_four prototype instantiation | 6 | 1 | { ... } | 7 | declaration | +| make_val_from_three_and_four prototype instantiation | 7 | 2 | declaration | 7 | initializer for val | +| make_val_from_three_and_four prototype instantiation | 7 | 3 | initializer for val | 7 | 3 | +| make_val_from_three_and_four prototype instantiation | 7 | 4 | 3 | 7 | 4 | +| make_val_from_three_and_four prototype instantiation | 7 | 5 | 4 | 7 | ({...}) | +| make_val_from_three_and_four prototype instantiation | 7 | 6 | ({...}) | 7 | call to unknown function | +| make_val_from_three_and_four prototype instantiation | 7 | 7 | call to unknown function | 8 | return ... | +| make_val_from_three_and_four prototype instantiation | 8 | 8 | return ... | 8 | val | +| make_val_from_three_and_four prototype instantiation | 8 | 9 | val | 6 | make_val_from_three_and_four | +| make_val_from_three_and_four | 6 | 1 | { ... } | 7 | declaration | +| make_val_from_three_and_four | 7 | 2 | declaration | 7 | initializer for val | +| make_val_from_three_and_four | 7 | 3 | initializer for val | 7 | 3 | +| make_val_from_three_and_four | 7 | 4 | 3 | 7 | 4 | +| make_val_from_three_and_four | 7 | 5 | 4 | 7 | call to choose_second | +| make_val_from_three_and_four | 7 | 6 | call to choose_second | 8 | return ... | +| make_val_from_three_and_four | 8 | 7 | return ... | 8 | val | +| make_val_from_three_and_four | 8 | 8 | val | 6 | make_val_from_three_and_four | +| make_val_from_three_and_four | 6 | 1 | { ... } | 7 | declaration | +| make_val_from_three_and_four | 7 | 2 | declaration | 7 | initializer for val | +| make_val_from_three_and_four | 7 | 3 | initializer for val | 7 | 3 | +| make_val_from_three_and_four | 7 | 4 | 3 | 7 | 4 | +| make_val_from_three_and_four | 7 | 5 | 4 | 7 | {...} | +| make_val_from_three_and_four | 7 | 6 | {...} | 8 | return ... | +| make_val_from_three_and_four | 8 | 7 | return ... | 8 | val | +| make_val_from_three_and_four | 8 | 8 | val | 6 | make_val_from_three_and_four | +| numbers | 33 | 1 | { ... } | 34 | declaration | +| numbers | 34 | 2 | declaration | 34 | initializer for zero | +| numbers | 34 | 3 | initializer for zero | 34 | call to count_args_1 | +| numbers | 34 | 4 | call to count_args_1 | 35 | declaration | +| numbers | 35 | 5 | declaration | 35 | initializer for one | +| numbers | 35 | 6 | initializer for one | 35 | call to make_val_from_one | +| numbers | 35 | 7 | call to make_val_from_one | 36 | declaration | +| numbers | 36 | 8 | declaration | 36 | initializer for two | +| numbers | 36 | 9 | initializer for two | 36 | 1 | +| numbers | 36 | 10 | 1 | 36 | 1.0 | +| numbers | 36 | 11 | 1.0 | 36 | call to count_args_2 | +| numbers | 36 | 12 | call to count_args_2 | 37 | declaration | +| numbers | 37 | 13 | declaration | 37 | initializer for three | +| numbers | 37 | 14 | initializer for three | 37 | call to make_val_from_three_and_four | +| numbers | 37 | 15 | call to make_val_from_three_and_four | 37 | first | +| numbers | 37 | 16 | first | 38 | declaration | +| numbers | 38 | 17 | declaration | 38 | initializer for four | +| numbers | 38 | 18 | initializer for four | 38 | call to make_val_from_three_and_four | +| numbers | 38 | 19 | call to make_val_from_three_and_four | 38 | value | +| numbers | 38 | 20 | value | 39 | return ... | +| numbers | 39 | 21 | return ... | 33 | numbers | diff --git a/cpp/ql/test/library-tests/cpp11_g/cfg.ql b/cpp/ql/test/library-tests/cpp11_g/cfg.ql new file mode 100644 index 000000000000..bbfe53684138 --- /dev/null +++ b/cpp/ql/test/library-tests/cpp11_g/cfg.ql @@ -0,0 +1,26 @@ +import cpp +import semmle.code.cpp.exprs.ObjectiveC + +string arguments(Function f, int i) { + (result = "," and i = -1) or + (result = "" and i = min(int j | function_template_argument(f, j, _)) - 1) or + (result = arguments(f, i - 1) + "," + f.getTemplateArgument(i).toString()) +} + +string name(Function f) { + if f.isConstructedFrom(_) then + result = f.toString() + "<" + max(arguments(f, _)).suffix(1) + ">" + else if f instanceof TemplateFunction then + result = f.toString() + " prototype instantiation" + else + result = f.toString() +} + +from ControlFlowNode x, ControlFlowNode y +where y = x.getASuccessor() +select name(x.getControlFlowScope()), + x.getLocation().getStartLine(), + count(x.getAPredecessor*()), // This helps order things sensibly + x.toString(), + y.getLocation().getStartLine(), + y.toString() diff --git a/cpp/ql/test/library-tests/cpp11_g/cpp11_g.cpp b/cpp/ql/test/library-tests/cpp11_g/cpp11_g.cpp new file mode 100644 index 000000000000..d6b780fa99ce --- /dev/null +++ b/cpp/ql/test/library-tests/cpp11_g/cpp11_g.cpp @@ -0,0 +1,39 @@ +template T make_val_from_one() { + T val ({1}); + return val; +} + +template T make_val_from_three_and_four() { + T val ({3,4}); + return val; +} + +template int count_args_1() { + return sizeof...(Args); +} + +template int count_args_2(Args... a...) { + return sizeof...(a); +} + +struct int_pair { + int first; + int second; +}; + +struct choose_second { + choose_second(int first, int second) + : value(second) + { + } + + int value; +}; + +void numbers() { + int zero = count_args_1<>(); + int one = make_val_from_one(); + int two = count_args_2(1, 1.); + int three = make_val_from_three_and_four().first; + int four = make_val_from_three_and_four().value; +} diff --git a/cpp/ql/test/library-tests/cpp_builtin_types/bool/Variables.expected b/cpp/ql/test/library-tests/cpp_builtin_types/bool/Variables.expected new file mode 100644 index 000000000000..130e5f396ac9 --- /dev/null +++ b/cpp/ql/test/library-tests/cpp_builtin_types/bool/Variables.expected @@ -0,0 +1,6 @@ +| a-source.cpp:2:6:2:7 | b1 | file://:0:0:0:0 | bool | +| c-source.c:4:6:4:7 | b2 | file://:0:0:0:0 | unsigned char | +| file://:0:0:0:0 | fp_offset | file://:0:0:0:0 | unsigned int | +| file://:0:0:0:0 | gp_offset | file://:0:0:0:0 | unsigned int | +| file://:0:0:0:0 | overflow_arg_area | file://:0:0:0:0 | void * | +| file://:0:0:0:0 | reg_save_area | file://:0:0:0:0 | void * | diff --git a/cpp/ql/test/library-tests/cpp_builtin_types/bool/Variables.ql b/cpp/ql/test/library-tests/cpp_builtin_types/bool/Variables.ql new file mode 100644 index 000000000000..1227fd1484ba --- /dev/null +++ b/cpp/ql/test/library-tests/cpp_builtin_types/bool/Variables.ql @@ -0,0 +1,4 @@ +import cpp + +from Variable v +select v, v.getType().getUnspecifiedType() diff --git a/cpp/ql/test/library-tests/cpp_builtin_types/bool/a-source.cpp b/cpp/ql/test/library-tests/cpp_builtin_types/bool/a-source.cpp new file mode 100644 index 000000000000..0f1de8179e74 --- /dev/null +++ b/cpp/ql/test/library-tests/cpp_builtin_types/bool/a-source.cpp @@ -0,0 +1,2 @@ +//semmle-extractor-options: --edg --target --edg linux_x86_64 -std=c++11 +bool b1; diff --git a/cpp/ql/test/library-tests/cpp_builtin_types/bool/c-source.c b/cpp/ql/test/library-tests/cpp_builtin_types/bool/c-source.c new file mode 100644 index 000000000000..27a246a5d9cd --- /dev/null +++ b/cpp/ql/test/library-tests/cpp_builtin_types/bool/c-source.c @@ -0,0 +1,4 @@ +//semmle-extractor-options: --edg --target --edg linux_x86_64 -std=c99 +typedef unsigned char bool; + +bool b2; diff --git a/cpp/ql/test/library-tests/cpp_builtin_types/wchar_t/Variables.expected b/cpp/ql/test/library-tests/cpp_builtin_types/wchar_t/Variables.expected new file mode 100644 index 000000000000..8afb9b307a69 --- /dev/null +++ b/cpp/ql/test/library-tests/cpp_builtin_types/wchar_t/Variables.expected @@ -0,0 +1,5 @@ +| a-source.cpp:2:9:2:9 | s | +| file://:0:0:0:0 | fp_offset | +| file://:0:0:0:0 | gp_offset | +| file://:0:0:0:0 | overflow_arg_area | +| file://:0:0:0:0 | reg_save_area | diff --git a/cpp/ql/test/library-tests/cpp_builtin_types/wchar_t/Variables.ql b/cpp/ql/test/library-tests/cpp_builtin_types/wchar_t/Variables.ql new file mode 100644 index 000000000000..d00bf7511451 --- /dev/null +++ b/cpp/ql/test/library-tests/cpp_builtin_types/wchar_t/Variables.ql @@ -0,0 +1,4 @@ +import cpp + +from Variable v +select v diff --git a/cpp/ql/test/library-tests/cpp_builtin_types/wchar_t/a-source.cpp b/cpp/ql/test/library-tests/cpp_builtin_types/wchar_t/a-source.cpp new file mode 100644 index 000000000000..cea68cd7d15b --- /dev/null +++ b/cpp/ql/test/library-tests/cpp_builtin_types/wchar_t/a-source.cpp @@ -0,0 +1,2 @@ +//semmle-extractor-options: --edg --target --edg linux_x86_64 -std=c++11 +wchar_t s; diff --git a/cpp/ql/test/library-tests/cpp_builtin_types/wchar_t/c-source.c b/cpp/ql/test/library-tests/cpp_builtin_types/wchar_t/c-source.c new file mode 100644 index 000000000000..3171e6ac9837 --- /dev/null +++ b/cpp/ql/test/library-tests/cpp_builtin_types/wchar_t/c-source.c @@ -0,0 +1 @@ +//semmle-extractor-options: --edg --target --edg linux_x86_64 -std=c99 diff --git a/cpp/ql/test/library-tests/ctorinits/calls.expected b/cpp/ql/test/library-tests/ctorinits/calls.expected new file mode 100644 index 000000000000..91011797ecde --- /dev/null +++ b/cpp/ql/test/library-tests/ctorinits/calls.expected @@ -0,0 +1,32 @@ +| ctorinits.cpp:5:46:5:51 | call to printf | +| ctorinits.cpp:6:17:6:22 | call to printf | +| ctorinits.cpp:11:8:11:8 | call to ~NoisyInt | +| ctorinits.cpp:11:8:11:8 | call to ~NoisyInt | +| ctorinits.cpp:14:7:14:16 | call to NoisyInt | +| ctorinits.cpp:15:7:15:16 | call to NoisyInt | +| ctorinits.cpp:16:17:16:31 | call to NoisyPair | +| ctorinits.cpp:21:8:21:8 | call to NoisyInt | +| ctorinits.cpp:21:8:21:8 | call to NoisyPair | +| ctorinits.cpp:21:8:21:8 | call to ~NoisyInt | +| ctorinits.cpp:21:8:21:8 | call to ~NoisyPair | +| ctorinits.cpp:28:13:28:13 | call to NoisyInt | +| ctorinits.cpp:29:15:29:15 | call to ~NoisyInt | +| ctorinits.cpp:36:15:36:15 | call to NoisyTriple | +| ctorinits.cpp:37:1:37:1 | call to ~NoisyTriple | +| ctorinits.cpp:67:5:67:8 | call to B | +| ctorinits.cpp:69:5:69:8 | call to A | +| ctorinits.cpp:70:5:70:8 | call to C | +| ctorinits.cpp:81:8:81:8 | call to VB | +| ctorinits.cpp:81:8:81:8 | call to ~VB | +| ctorinits.cpp:85:26:85:26 | call to VB | +| ctorinits.cpp:85:26:85:26 | call to VB | +| ctorinits.cpp:85:26:85:26 | call to VD | +| ctorinits.cpp:88:3:88:3 | call to ~VB | +| ctorinits.cpp:88:3:88:3 | call to ~VB | +| ctorinits.cpp:88:3:88:3 | call to ~VD | +| ctorinits.cpp:93:5:93:9 | call to VB | +| ctorinits.cpp:93:11:93:11 | call to VD | +| ctorinits.cpp:93:11:93:11 | call to VirtualAndNonVirtual | +| ctorinits.cpp:96:3:96:3 | call to ~VB | +| ctorinits.cpp:96:3:96:3 | call to ~VD | +| ctorinits.cpp:96:3:96:3 | call to ~VirtualAndNonVirtual | diff --git a/cpp/ql/test/library-tests/ctorinits/calls.ql b/cpp/ql/test/library-tests/ctorinits/calls.ql new file mode 100644 index 000000000000..c22d8d273141 --- /dev/null +++ b/cpp/ql/test/library-tests/ctorinits/calls.ql @@ -0,0 +1,4 @@ +import cpp + +from FunctionCall fc +select fc diff --git a/cpp/ql/test/library-tests/ctorinits/ctorinits.cpp b/cpp/ql/test/library-tests/ctorinits/ctorinits.cpp new file mode 100644 index 000000000000..6d39d6a393cb --- /dev/null +++ b/cpp/ql/test/library-tests/ctorinits/ctorinits.cpp @@ -0,0 +1,97 @@ +int printf(const char*, ...); + +struct NoisyInt +{ + NoisyInt(int value = 0) : m_value(value) { printf("constructor %d\n", m_value); } + ~NoisyInt() { printf("destructor %d\n", m_value); } + + int m_value; +}; + +struct NoisyPair +{ + NoisyPair(int fst, int snd) + : m_fst(fst) + , m_snd(snd) {} + NoisyPair() : NoisyPair(0, 0) {} + + NoisyInt m_fst, m_snd; +}; + +struct NoisyTriple : NoisyPair +{ + NoisyInt m_third; +}; + +class ArrayInt +{ + ArrayInt() {} + ~ArrayInt() {} + + NoisyInt m_array[10]; +}; + +int main() +{ + NoisyTriple t; +} + +class ArrayMemberInit +{ +public: + ArrayMemberInit() : xs{0,1,2,3} {} // initializer + +private: + int xs[4]; +}; + +struct A { + A(int); +}; + +struct B { + B(int); +}; + +struct C { + C(int); +}; + +struct MultipleBases : A, B, C { + int x; + int y; + int z; + + MultipleBases() : + z(5), + B(1), + x(3), + A(0), + C(2), + y(4) { + } +}; + +struct VB { + VB(); + VB(int); + ~VB(); +}; + +struct VD : virtual VB { +}; + +struct VirtualAndNonVirtual : VD, VB { + VirtualAndNonVirtual() { + } + ~VirtualAndNonVirtual() { + } +}; + +struct AllYourVirtualBaseAreBelongToUs : VD, VirtualAndNonVirtual, virtual VB { + AllYourVirtualBaseAreBelongToUs() : + VB(5) { + } + ~AllYourVirtualBaseAreBelongToUs() { + } +}; \ No newline at end of file diff --git a/cpp/ql/test/library-tests/ctorinits/ctors.expected b/cpp/ql/test/library-tests/ctorinits/ctors.expected new file mode 100644 index 000000000000..8a14ee6001ae --- /dev/null +++ b/cpp/ql/test/library-tests/ctorinits/ctors.expected @@ -0,0 +1,21 @@ +| ctorinits.cpp:5:3:5:10 | NoisyInt | 0 | ConstructorFieldInit | ctorinits.cpp:5:29:5:42 | constructor init of field m_value | 1 | 0 | +| ctorinits.cpp:13:3:13:11 | NoisyPair | 0 | ConstructorFieldInit | ctorinits.cpp:14:7:14:16 | constructor init of field m_fst | 1 | 0 | +| ctorinits.cpp:13:3:13:11 | NoisyPair | 1 | ConstructorFieldInit | ctorinits.cpp:15:7:15:16 | constructor init of field m_snd | 1 | 0 | +| ctorinits.cpp:16:3:16:11 | NoisyPair | 0 | ConstructorDelegationInit | ctorinits.cpp:16:17:16:31 | call to NoisyPair | 2 | 2 | +| ctorinits.cpp:21:8:21:8 | NoisyTriple | 0 | ConstructorDirectInit | ctorinits.cpp:21:8:21:8 | call to NoisyPair | 0 | 0 | +| ctorinits.cpp:21:8:21:8 | NoisyTriple | 1 | ConstructorFieldInit | ctorinits.cpp:21:8:21:8 | constructor init of field m_third | 1 | 0 | +| ctorinits.cpp:28:2:28:9 | ArrayInt | 0 | ConstructorFieldInit | ctorinits.cpp:28:13:28:13 | constructor init of field m_array | 1 | 0 | +| ctorinits.cpp:42:2:42:16 | ArrayMemberInit | 0 | ConstructorFieldInit | ctorinits.cpp:42:22:42:32 | constructor init of field xs | 1 | 4 | +| ctorinits.cpp:65:3:65:15 | MultipleBases | 0 | ConstructorDirectInit | ctorinits.cpp:69:5:69:8 | call to A | 1 | 1 | +| ctorinits.cpp:65:3:65:15 | MultipleBases | 1 | ConstructorDirectInit | ctorinits.cpp:67:5:67:8 | call to B | 1 | 1 | +| ctorinits.cpp:65:3:65:15 | MultipleBases | 2 | ConstructorDirectInit | ctorinits.cpp:70:5:70:8 | call to C | 1 | 1 | +| ctorinits.cpp:65:3:65:15 | MultipleBases | 3 | ConstructorFieldInit | ctorinits.cpp:68:5:68:8 | constructor init of field x | 1 | 1 | +| ctorinits.cpp:65:3:65:15 | MultipleBases | 4 | ConstructorFieldInit | ctorinits.cpp:71:5:71:8 | constructor init of field y | 1 | 1 | +| ctorinits.cpp:65:3:65:15 | MultipleBases | 5 | ConstructorFieldInit | ctorinits.cpp:66:5:66:8 | constructor init of field z | 1 | 1 | +| ctorinits.cpp:81:8:81:8 | VD | 0 | ConstructorVirtualInit | ctorinits.cpp:81:8:81:8 | call to VB | 0 | 0 | +| ctorinits.cpp:85:3:85:22 | VirtualAndNonVirtual | 0 | ConstructorVirtualInit | ctorinits.cpp:85:26:85:26 | call to VB | 0 | 0 | +| ctorinits.cpp:85:3:85:22 | VirtualAndNonVirtual | 1 | ConstructorDirectInit | ctorinits.cpp:85:26:85:26 | call to VD | 0 | 0 | +| ctorinits.cpp:85:3:85:22 | VirtualAndNonVirtual | 2 | ConstructorDirectInit | ctorinits.cpp:85:26:85:26 | call to VB | 0 | 0 | +| ctorinits.cpp:92:3:92:33 | AllYourVirtualBaseAreBelongToUs | 0 | ConstructorVirtualInit | ctorinits.cpp:93:5:93:9 | call to VB | 1 | 1 | +| ctorinits.cpp:92:3:92:33 | AllYourVirtualBaseAreBelongToUs | 1 | ConstructorDirectInit | ctorinits.cpp:93:11:93:11 | call to VD | 0 | 0 | +| ctorinits.cpp:92:3:92:33 | AllYourVirtualBaseAreBelongToUs | 2 | ConstructorDirectInit | ctorinits.cpp:93:11:93:11 | call to VirtualAndNonVirtual | 0 | 0 | diff --git a/cpp/ql/test/library-tests/ctorinits/ctors.ql b/cpp/ql/test/library-tests/ctorinits/ctors.ql new file mode 100644 index 000000000000..9f528854dcf3 --- /dev/null +++ b/cpp/ql/test/library-tests/ctorinits/ctors.ql @@ -0,0 +1,7 @@ +import cpp + +from Constructor c, int i, Expr e, string what +where e = c.getInitializer(i) + and what = e.getAQlClass() + and what.matches("Constructor%Init") +select c, i, what, e, count(e.getAChild()), count(e.getAChild*().(Literal)) diff --git a/cpp/ql/test/library-tests/ctorinits/dtors.expected b/cpp/ql/test/library-tests/ctorinits/dtors.expected new file mode 100644 index 000000000000..9924ea90011b --- /dev/null +++ b/cpp/ql/test/library-tests/ctorinits/dtors.expected @@ -0,0 +1,12 @@ +| ctorinits.cpp:11:8:11:8 | ~NoisyPair | 0 | DestructorFieldDestruction | ctorinits.cpp:11:8:11:8 | destructor field destruction of m_snd | 1 | +| ctorinits.cpp:11:8:11:8 | ~NoisyPair | 1 | DestructorFieldDestruction | ctorinits.cpp:11:8:11:8 | destructor field destruction of m_fst | 1 | +| ctorinits.cpp:21:8:21:8 | ~NoisyTriple | 0 | DestructorFieldDestruction | ctorinits.cpp:21:8:21:8 | destructor field destruction of m_third | 1 | +| ctorinits.cpp:21:8:21:8 | ~NoisyTriple | 1 | DestructorDirectDestruction | ctorinits.cpp:21:8:21:8 | call to ~NoisyPair | 0 | +| ctorinits.cpp:29:2:29:10 | ~ArrayInt | 0 | DestructorFieldDestruction | ctorinits.cpp:29:15:29:15 | destructor field destruction of m_array | 1 | +| ctorinits.cpp:81:8:81:8 | ~VD | 0 | DestructorVirtualDestruction | ctorinits.cpp:81:8:81:8 | call to ~VB | 0 | +| ctorinits.cpp:87:3:87:23 | ~VirtualAndNonVirtual | 0 | DestructorDirectDestruction | ctorinits.cpp:88:3:88:3 | call to ~VB | 0 | +| ctorinits.cpp:87:3:87:23 | ~VirtualAndNonVirtual | 1 | DestructorDirectDestruction | ctorinits.cpp:88:3:88:3 | call to ~VD | 0 | +| ctorinits.cpp:87:3:87:23 | ~VirtualAndNonVirtual | 2 | DestructorVirtualDestruction | ctorinits.cpp:88:3:88:3 | call to ~VB | 0 | +| ctorinits.cpp:95:3:95:34 | ~AllYourVirtualBaseAreBelongToUs | 0 | DestructorDirectDestruction | ctorinits.cpp:96:3:96:3 | call to ~VirtualAndNonVirtual | 0 | +| ctorinits.cpp:95:3:95:34 | ~AllYourVirtualBaseAreBelongToUs | 1 | DestructorDirectDestruction | ctorinits.cpp:96:3:96:3 | call to ~VD | 0 | +| ctorinits.cpp:95:3:95:34 | ~AllYourVirtualBaseAreBelongToUs | 2 | DestructorVirtualDestruction | ctorinits.cpp:96:3:96:3 | call to ~VB | 0 | diff --git a/cpp/ql/test/library-tests/ctorinits/dtors.ql b/cpp/ql/test/library-tests/ctorinits/dtors.ql new file mode 100644 index 000000000000..cd649a6bc962 --- /dev/null +++ b/cpp/ql/test/library-tests/ctorinits/dtors.ql @@ -0,0 +1,7 @@ +import cpp + +from Destructor d, int i, Expr e, string what +where e = d.getDestruction(i) + and what = e.getAQlClass() + and what.matches("Destructor%Destruction") +select d, i, what, e, count(e.getAChild()) diff --git a/cpp/ql/test/library-tests/dataflow/additional-flow-to-parameter/standardFlow.expected b/cpp/ql/test/library-tests/dataflow/additional-flow-to-parameter/standardFlow.expected new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/cpp/ql/test/library-tests/dataflow/additional-flow-to-parameter/standardFlow.ql b/cpp/ql/test/library-tests/dataflow/additional-flow-to-parameter/standardFlow.ql new file mode 100644 index 000000000000..362bd8cd6b79 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/additional-flow-to-parameter/standardFlow.ql @@ -0,0 +1,23 @@ +import cpp +import semmle.code.cpp.dataflow.DataFlow + +class TestConfig extends DataFlow::Configuration { + TestConfig() { + this = "TestConfig" + } + + override predicate isSource(DataFlow::Node source) { + source.asExpr().(FunctionCall).getTarget().getName() = "source" + } + + override predicate isSink(DataFlow::Node sink) { + exists(FunctionCall call | + call.getTarget().getName() = "sink" and + sink.asExpr() = call.getAnArgument() + ) + } +} + +from DataFlow::Node sink, DataFlow::Node source, TestConfig cfg +where cfg.hasFlow(source, sink) +select sink, source diff --git a/cpp/ql/test/library-tests/dataflow/additional-flow-to-parameter/test.cpp b/cpp/ql/test/library-tests/dataflow/additional-flow-to-parameter/test.cpp new file mode 100644 index 000000000000..73dee2211779 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/additional-flow-to-parameter/test.cpp @@ -0,0 +1,20 @@ +// This test confirms that `DataFlow::Config.isAdditionalFlowStep` can be +// overridden to cause flow from an expression to a parameter. + +int source(); +void sink(...); + +// In this test, a function with this name becomes the target of all calls to +// function pointers. +void targetOfAllFunctionPointerCalls(int i1, int i2, int i3) { + sink(i1); + sink(i2); +} + +typedef void (*ftype)(int, int, int); + +void test1(ftype callback) { + callback(source(), 0, 0); + callback(0, source(), 0); + callback(0, 0, source()); +} diff --git a/cpp/ql/test/library-tests/dataflow/additional-flow-to-parameter/withAdditionalFlow.expected b/cpp/ql/test/library-tests/dataflow/additional-flow-to-parameter/withAdditionalFlow.expected new file mode 100644 index 000000000000..cd30867d4356 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/additional-flow-to-parameter/withAdditionalFlow.expected @@ -0,0 +1,2 @@ +| test.cpp:10:10:10:11 | i1 | test.cpp:17:14:17:19 | call to source | +| test.cpp:11:10:11:11 | i2 | test.cpp:18:17:18:22 | call to source | diff --git a/cpp/ql/test/library-tests/dataflow/additional-flow-to-parameter/withAdditionalFlow.ql b/cpp/ql/test/library-tests/dataflow/additional-flow-to-parameter/withAdditionalFlow.ql new file mode 100644 index 000000000000..3e3d23e65f92 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/additional-flow-to-parameter/withAdditionalFlow.ql @@ -0,0 +1,34 @@ +import cpp +import semmle.code.cpp.dataflow.DataFlow + +class TestConfig extends DataFlow::Configuration { + TestConfig() { + this = "TestConfig" + } + + override predicate isSource(DataFlow::Node source) { + source.asExpr().(FunctionCall).getTarget().getName() = "source" + } + + override predicate isSink(DataFlow::Node sink) { + exists(FunctionCall call | + call.getTarget().getName() = "sink" and + sink.asExpr() = call.getAnArgument() + ) + } + + override predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) { + // Send all arguments of function-pointer-calls to a function with a + // special name + exists(Call call, Function target, int i | + not call instanceof FunctionCall and + call.getArgument(i) = n1.asExpr() and + target.getParameter(i) = n2.asParameter() and + target.getName() = "targetOfAllFunctionPointerCalls" + ) + } +} + +from DataFlow::Node sink, DataFlow::Node source, TestConfig cfg +where cfg.hasFlow(source, sink) +select sink, source diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-edge-tests/additionalEdges.expected b/cpp/ql/test/library-tests/dataflow/dataflow-edge-tests/additionalEdges.expected new file mode 100644 index 000000000000..2047554655c7 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/dataflow-edge-tests/additionalEdges.expected @@ -0,0 +1,4 @@ +| tryExcept.c:7:7:7:7 | x | tryExcept.c:14:10:14:10 | x | +| tryExcept.c:7:13:7:14 | 0 | tryExcept.c:10:9:10:9 | y | +| tryExcept.c:10:9:10:9 | y | tryExcept.c:10:5:10:9 | ... = ... | +| tryExcept.c:10:9:10:9 | y | tryExcept.c:14:10:14:10 | x | diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-edge-tests/additionalEdges.ql b/cpp/ql/test/library-tests/dataflow/dataflow-edge-tests/additionalEdges.ql new file mode 100644 index 000000000000..b76898e3e4ff --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/dataflow-edge-tests/additionalEdges.ql @@ -0,0 +1,34 @@ +import cpp +import semmle.code.cpp.dataflow.DataFlow + +class EdgeToExcept extends AdditionalControlFlowEdge { + EdgeToExcept() { + this instanceof Call and + exists(getNearestTryExceptParent(this)) + } + + override ControlFlowNode getAnEdgeTarget() { + result = getNearestTryExceptParent(this).getExcept() + } +} + +MicrosoftTryExceptStmt getNearestTryExceptParent(Expr e) { + result = getANearParent(e) +} + +private Element getANearParent(Expr e) { + result = e.getParent() + or + exists(Element prev | + result = prev.(Expr).getParent() + or + result = prev.(Stmt).getParent() + | // do not recurse past __try + not prev instanceof MicrosoftTryStmt and + prev = getANearParent(e) + ) +} + +from DataFlow::Node nodeFrom, DataFlow::Node nodeTo +where DataFlow::localFlowStep(nodeFrom, nodeTo) +select nodeFrom, nodeTo \ No newline at end of file diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-edge-tests/standardEdges.expected b/cpp/ql/test/library-tests/dataflow/dataflow-edge-tests/standardEdges.expected new file mode 100644 index 000000000000..580392512034 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/dataflow-edge-tests/standardEdges.expected @@ -0,0 +1,2 @@ +| tryExcept.c:7:13:7:14 | 0 | tryExcept.c:10:9:10:9 | y | +| tryExcept.c:10:9:10:9 | y | tryExcept.c:10:5:10:9 | ... = ... | diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-edge-tests/standardEdges.ql b/cpp/ql/test/library-tests/dataflow/dataflow-edge-tests/standardEdges.ql new file mode 100644 index 000000000000..41f59b074872 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/dataflow-edge-tests/standardEdges.ql @@ -0,0 +1,6 @@ +import cpp +import semmle.code.cpp.dataflow.DataFlow + +from DataFlow::Node nodeFrom, DataFlow::Node nodeTo +where DataFlow::localFlowStep(nodeFrom, nodeTo) +select nodeFrom, nodeTo \ No newline at end of file diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-edge-tests/tryExcept.c b/cpp/ql/test/library-tests/dataflow/dataflow-edge-tests/tryExcept.c new file mode 100644 index 000000000000..3761ade4e5d5 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/dataflow-edge-tests/tryExcept.c @@ -0,0 +1,16 @@ +// semmle-extractor-options: --microsoft + +void ProbeFunction(); +void sink(); + +void f() { + int x, y = 0; + __try { + ProbeFunction(0); + x = y; + ProbeFunction(0); + } + __except (0) { + sink(x); + } +} diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/DataflowTestCommon.qll b/cpp/ql/test/library-tests/dataflow/dataflow-tests/DataflowTestCommon.qll new file mode 100644 index 000000000000..2f9c932f2187 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/DataflowTestCommon.qll @@ -0,0 +1,29 @@ +import cpp +import semmle.code.cpp.dataflow.DataFlow + +/** Common data flow configuration to be used by tests. */ +class TestAllocationConfig extends DataFlow::Configuration { + TestAllocationConfig() { + this = "TestAllocationConfig" + } + + override predicate isSource(DataFlow::Node source) { + source.asExpr().(FunctionCall).getTarget().getName() = "source" + or + source.asParameter().getName().matches("source%") + or + // Track uninitialized variables + exists(source.asUninitialized()) + } + + override predicate isSink(DataFlow::Node sink) { + exists(FunctionCall call | + call.getTarget().getName() = "sink" and + sink.asExpr() = call.getAnArgument() + ) + } + + override predicate isBarrier(DataFlow::Node barrier) { + barrier.asExpr().(VariableAccess).getTarget().hasName("barrier") + } +} diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/example.c b/cpp/ql/test/library-tests/dataflow/dataflow-tests/example.c new file mode 100644 index 000000000000..40f62783ea93 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/example.c @@ -0,0 +1,29 @@ + +typedef struct +{ + int x, y; +} MyCoords; + +int getX(MyCoords *coords); +void doSomething(void *something); + +typedef struct +{ + char isTrue; +} MyBool; + +void myTest_with_local_flow(MyBool *b, int pos) +{ + MyCoords coords = {0}; + + if (b->isTrue) { + goto next; + } + +next: + coords.x = coords.y = pos + 1; + // There is flow from `{0}` to `coords` in `&coords` on the next line. + coords.x = getX(&coords); + + doSomething((void *)&pos); +} diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/localFlow.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/localFlow.expected new file mode 100644 index 000000000000..5bd5c9e8925a --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/localFlow.expected @@ -0,0 +1,30 @@ +| example.c:15:37:15:37 | b | example.c:19:6:19:6 | b | +| example.c:15:44:15:46 | pos | example.c:24:24:24:26 | pos | +| example.c:15:44:15:46 | pos | example.c:28:23:28:25 | pos | +| example.c:17:19:17:22 | {...} | example.c:24:13:24:18 | coords | +| example.c:24:13:24:30 | ... = ... | example.c:24:2:24:30 | ... = ... | +| example.c:24:24:24:30 | ... + ... | example.c:24:13:24:30 | ... = ... | +| example.c:26:13:26:16 | call to getX | example.c:26:2:26:25 | ... = ... | +| test.cpp:6:12:6:17 | call to source | test.cpp:7:8:7:9 | t1 | +| test.cpp:6:12:6:17 | call to source | test.cpp:8:8:8:9 | t1 | +| test.cpp:6:12:6:17 | call to source | test.cpp:9:8:9:9 | t1 | +| test.cpp:6:12:6:17 | call to source | test.cpp:11:7:11:8 | t1 | +| test.cpp:8:8:8:9 | t1 | test.cpp:8:3:8:9 | ... = ... | +| test.cpp:8:8:8:9 | t1 | test.cpp:10:8:10:9 | t2 | +| test.cpp:8:8:8:9 | t1 | test.cpp:15:8:15:9 | t2 | +| test.cpp:8:8:8:9 | t1 | test.cpp:24:10:24:11 | t2 | +| test.cpp:12:10:12:10 | 0 | test.cpp:12:5:12:10 | ... = ... | +| test.cpp:12:10:12:10 | 0 | test.cpp:13:10:13:11 | t2 | +| test.cpp:12:10:12:10 | 0 | test.cpp:15:8:15:9 | t2 | +| test.cpp:12:10:12:10 | 0 | test.cpp:24:10:24:11 | t2 | +| test.cpp:17:8:17:8 | 0 | test.cpp:17:3:17:8 | ... = ... | +| test.cpp:17:8:17:8 | 0 | test.cpp:21:8:21:9 | t1 | +| test.cpp:17:8:17:8 | 0 | test.cpp:23:23:23:24 | t1 | +| test.cpp:17:8:17:8 | 0 | test.cpp:26:8:26:9 | t1 | +| test.cpp:19:10:19:11 | t2 | test.cpp:19:5:19:11 | ... = ... | +| test.cpp:23:15:23:16 | 0 | test.cpp:23:19:23:19 | i | +| test.cpp:23:15:23:16 | 0 | test.cpp:23:27:23:27 | i | +| test.cpp:23:27:23:27 | i | test.cpp:23:27:23:29 | ... ++ | +| test.cpp:24:10:24:11 | t2 | test.cpp:23:23:23:24 | t1 | +| test.cpp:24:10:24:11 | t2 | test.cpp:24:5:24:11 | ... = ... | +| test.cpp:24:10:24:11 | t2 | test.cpp:26:8:26:9 | t1 | diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/localFlow.ql b/cpp/ql/test/library-tests/dataflow/dataflow-tests/localFlow.ql new file mode 100644 index 000000000000..4a8bddc8f576 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/localFlow.ql @@ -0,0 +1,7 @@ +import cpp +import semmle.code.cpp.dataflow.DataFlow + +from DataFlow::Node nodeFrom, DataFlow::Node nodeTo +where DataFlow::localFlowStep(nodeFrom, nodeTo) + and nodeFrom.getFunction().getName().matches("%\\_with\\_local\\_flow") +select nodeFrom, nodeTo diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp new file mode 100644 index 000000000000..5cd5f9e3989f --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp @@ -0,0 +1,425 @@ +int source(); +void sink(...); + +void intraprocedural_with_local_flow() { + int t2; + int t1 = source(); + sink(t1); // tainted + t2 = t1; + sink(t1); // tainted + sink(t2); // tainted + if (t1) { + t2 = 0; + sink(t2); // clean + } + sink(t2); // tainted + + t1 = 0; + while (false) { + t1 = t2; + } + sink(t1); // clean (because loop condition is `false`) + + for (int i = 0; i < t1; i++) { + t1 = t2; + } + sink(t1); // tainted +} + +static void callee(int t, int c) { + sink(t); // tainted (from first call in caller() function) + sink(c); // tainted (from second call in caller() function) +} + +void caller() { + callee(source(), 0); + callee(1, source()); +} + +void branching(bool b) { + { + int t1 = 1, t2 = 2; + int t = source(); + sink(t ? t1 : t2); // clean + + t = b ? t1 : t2; + sink(t); // clean + } + + { + int t1 = source(), t = 0; + if (b) { + t = t1; + } else { + t = 1; + } + + + sink(t); // tainted + } +} + +namespace std { + template T&& move(T& t) noexcept; // simplified signature +} + +void identityOperations(int* source1) { + const int *x1 = std::move(source1); + int* x2 = const_cast(x1); + int* x3 = (x2); + const int* x4 = (const int *)x3; + sink(x4); +} + +void trackUninitialized() { + int u1; + sink(u1); // tainted + u1 = 2; + sink(u1); // clean + + int i1 = 1; + sink(i1); // clean + + int u2; + sink(i1 ? u2 : 1); // tainted + i1 = u2; + sink(i1); // tainted +} + +void local_references(int &source1, int clean1) { + sink(source1); // tainted + source1 = clean1; + sink(source1); // clean + + // The next two test cases show that the analysis does not understand the "&" + // on the type at all. It does not understand that the initialization creates + // an alias, so it does not understand when two variables change on one + // assignment. This leads to both overapproximation and underapproximation of + // flows. + { + int t = source(); + int &ref = t; + t = clean1; + sink(ref); // clean (FALSE POSITIVE) + } + + { + int t = clean1; + int &ref = t; + t = source(); + sink(ref); // tainted (FALSE NEGATIVE) + } +} + +struct twoIntFields { + int m1, m2; + int getFirst() { return m1; } +}; + +void following_pointers( + int sourceArray1[], + int cleanArray1[], + twoIntFields sourceStruct1, + twoIntFields *sourceStruct1_ptr, + int (*sourceFunctionPointer)()) +{ + sink(sourceArray1); // flow + + sink(sourceArray1[0]); // no flow + sink(*sourceArray1); // no flow + sink(&sourceArray1); // no flow (since sourceArray1 is really a pointer) + + sink(sourceStruct1.m1); // no flow + sink(sourceStruct1_ptr->m1); // no flow + sink(sourceStruct1_ptr->getFirst()); // no flow + + sourceStruct1_ptr->m1 = source(); + sink(sourceStruct1_ptr->m1); // flow + sink(sourceStruct1_ptr->getFirst()); // no flow (due to limitations of the analysis) + sink(sourceStruct1_ptr->m2); // no flow + sink(sourceStruct1.m1); // flow (due to lack of no-alias tracking) + + twoIntFields s = { source(), source() }; + // TODO: fix this by distinguishing between an AggregateLiteral that + // initializes an array and one that initializes a struct. + sink(s.m2); // no flow (due to limitations of the analysis) + + twoIntFields sArray[1] = { { source(), source() } }; + // TODO: fix this like above + sink(sArray[0].m2); // no flow (due to limitations of the analysis) + + twoIntFields sSwapped = { .m2 = source(), .m1 = 0 }; + // TODO: fix this like above + sink(sSwapped.m2); // no flow (due to limitations of the analysis) + + sink(sourceFunctionPointer()); // no flow + + int stackArray[2] = { source(), source() }; + stackArray[0] = source(); + sink(stackArray); // no flow +} + +int alwaysAssignSource(int *out) { + *out = source(); + return 0; +} + +int alwaysAssign0(int *out) { + *out = 0; + return 0; +} + +int alwaysAssignInput(int *out, int in) { + *out = in; + return 0; +} +// TODO: call the above + +// Tests for flow through functions that return a parameter, or a value obtained from a parameter +// These also test some cases for tracking non-parameter sources returned to a function call + +int returnParameter(int p) { + return p; // considered clean unless the caller passes taint into p, which the analysis will handle separately +} + +void callReturnParameter() { + int x = returnParameter(source()); + int y = x; + sink(y); // tainted due to above source +} + +int returnSourceParameter(int s) { + sink(s); // tainted + return s; // considered clean unless the caller passes taint into the parameter source +} + +void callReturnSourceParameter() { + int x = returnSourceParameter(0); + sink(x); // clean + int y = returnSourceParameter(source()); + sink(y); // tainted +} + +int returnSourceParameter2(int s) { + int x = s; + sink(x); // tainted + return x; // considered clean unless the caller passes taint into the parameter source +} + +void callReturnSourceParameter2() { + int x = returnSourceParameter2(0); + sink(x); // clean + int y = returnSourceParameter2(source()); + sink(y); // tainted +} + +// Tests for non-parameter sources returned to a function call + +int returnSource() { + int x = source(); // taints the return value + return x; +} + +void callReturnSource() { + int x = returnSource(); + int y = x; + sink(y); // tainted +} + +// TESTS WITH BARRIERS: none of these should have results + +void barrier(...); + +class BarrierTests { + // Tests for flow through functions that return a parameter, or a value obtained from a parameter + // These also test some cases for tracking non-parameter sources returned to a function call + + int returnParameter(int p) { + return p; // considered clean unless the caller passes taint into p, which the analysis will handle separately + } + + void callReturnParameter() { + int x = returnParameter(source()); + int barrier = x; + int y = barrier; + sink(y); // no longer tainted + } + + int returnSourceParameter(int source) { + int barrier = source; + sink(barrier); // no longer tainted + return barrier; // clean + } + + void callReturnSourceParameter() { + int x = returnSourceParameter(0); + sink(x); // clean + int y = returnSourceParameter(source()); + sink(y); // no longer tainted + } + + int returnSourceParameter2(int source) { + int barrier = source; + int x = barrier; + sink(x); // no longer tainted + return x; // clean + } + + void callReturnSourceParameter2() { + int x = returnSourceParameter2(0); + sink(x); // clean + int y = returnSourceParameter2(source()); + sink(y); // no longer tainted + } + + // Tests for non-parameter sources returned to a function call + + int returnSource() { + int x = source(); + int barrier = x; + return barrier; + } + + void callReturnSource() { + int x = returnSource(); + int y = x; + sink(y); // no longer tainted + } +}; +// Tests for interprocedural flow (as above) involving nested function calls +namespace NestedTests { + class FlowIntoParameter { + void level0() { + level1(source()); + safelevel1(source()); + } + + void level1(int x) { + int y = x; + level2(y); + } + + void safelevel1(int x) { + int barrier = x; + level2(barrier); + } + + void level2(int x) { + sink(x); // tainted from call to level1() but not from call to safelevel1() + } + }; + class FlowThroughFunctionReturn { + void level0() { + int x = level1(source()); + sink(x); // tainted + x = safelevel1(source()); + sink(x); // no longer tainted + } + + int level1(int x) { + int y = x; + return level2(y); + } + + int safelevel1(int x) { + int barrier = x; + return level2(barrier); + } + + int level2(int x) { + int y = x; + return y; + } + }; + class FlowOutOfFunction { + void level0() { + int x = level1(); + sink(x); // tainted + x = safelevel1(); + sink(x); // no longer tainted + } + + int level1() { + int y = level2(); + return y; + } + + int safelevel1() { + int barrier = level2(); + return barrier; + } + + int level2() { + int y = source(); + return y; + } + + // the next three functions describe a case that should not be picked up + // by the flow-out-of-function case, but only by the flow-through-function case + // a poor heuristic to prevent that will lead to the clean sink being flagged + + void f() { + g(source()); + } + void g(int p) { + int x = h(p); + sink(x); // tainted from f + int y = h(0); + sink(y); // clean + f(); + } + int h(int p) { + return p; + } + }; +} + +namespace FlowThroughGlobals { + int globalVar; + + int taintGlobal() { + globalVar = source(); + } + + int f() { + sink(globalVar); // tainted or clean? Not sure. + taintGlobal(); + sink(globalVar); // tainted (FALSE NEGATIVE) + } + + int calledAfterTaint() { + sink(globalVar); // tainted (FALSE NEGATIVE) + } + + int taintAndCall() { + globalVar = source(); + calledAfterTaint(); + sink(globalVar); // tainted + } +} + +// This is similar to FlowThroughGlobals, only with a non-static data member +// instead of a global. +class FlowThroughFields { + int field = 0; + + int taintField() { + field = source(); + } + + int f() { + sink(field); // tainted or clean? Not sure. + taintField(); + sink(field); // tainted (FALSE NEGATIVE) + } + + int calledAfterTaint() { + sink(field); // tainted (FALSE NEGATIVE) + } + + int taintAndCall() { + field = source(); + calledAfterTaint(); + sink(field); // tainted + } +}; diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.expected new file mode 100644 index 000000000000..b2519f4805b9 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.expected @@ -0,0 +1,36 @@ +| test.cpp:7:8:7:9 | t1 | test.cpp:6:12:6:17 | call to source | +| test.cpp:9:8:9:9 | t1 | test.cpp:6:12:6:17 | call to source | +| test.cpp:10:8:10:9 | t2 | test.cpp:6:12:6:17 | call to source | +| test.cpp:15:8:15:9 | t2 | test.cpp:6:12:6:17 | call to source | +| test.cpp:26:8:26:9 | t1 | test.cpp:6:12:6:17 | call to source | +| test.cpp:30:8:30:8 | t | test.cpp:35:10:35:15 | call to source | +| test.cpp:31:8:31:8 | c | test.cpp:36:13:36:18 | call to source | +| test.cpp:58:10:58:10 | t | test.cpp:50:14:50:19 | call to source | +| test.cpp:71:8:71:9 | x4 | test.cpp:66:30:66:36 | source1 | +| test.cpp:76:8:76:9 | u1 | test.cpp:75:7:75:8 | u1 | +| test.cpp:84:8:84:18 | ... ? ... : ... | test.cpp:83:7:83:8 | u2 | +| test.cpp:86:8:86:9 | i1 | test.cpp:83:7:83:8 | u2 | +| test.cpp:90:8:90:14 | source1 | test.cpp:89:28:89:34 | source1 | +| test.cpp:103:10:103:12 | ref | test.cpp:100:13:100:18 | call to source | +| test.cpp:126:8:126:19 | sourceArray1 | test.cpp:120:9:120:20 | sourceArray1 | +| test.cpp:137:27:137:28 | m1 | test.cpp:136:27:136:32 | call to source | +| test.cpp:140:22:140:23 | m1 | test.cpp:136:27:136:32 | call to source | +| test.cpp:188:8:188:8 | y | test.cpp:186:27:186:32 | call to source | +| test.cpp:192:8:192:8 | s | test.cpp:199:33:199:38 | call to source | +| test.cpp:200:8:200:8 | y | test.cpp:199:33:199:38 | call to source | +| test.cpp:205:8:205:8 | x | test.cpp:212:34:212:39 | call to source | +| test.cpp:213:8:213:8 | y | test.cpp:212:34:212:39 | call to source | +| test.cpp:226:8:226:8 | y | test.cpp:219:11:219:16 | call to source | +| test.cpp:308:12:308:12 | x | test.cpp:293:14:293:19 | call to source | +| test.cpp:314:12:314:12 | x | test.cpp:313:22:313:27 | call to source | +| test.cpp:337:14:337:14 | x | test.cpp:353:17:353:22 | call to source | +| test.cpp:366:7:366:7 | x | test.cpp:362:4:362:9 | call to source | +| test.cpp:397:10:397:18 | globalVar | test.cpp:395:17:395:22 | call to source | +| test.cpp:423:10:423:14 | field | test.cpp:421:13:421:18 | 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 | +| true_upon_entry.cpp:49:8:49:8 | x | true_upon_entry.cpp:43:11:43:16 | call to source | +| true_upon_entry.cpp:57:8:57:8 | x | true_upon_entry.cpp:54:11:54:16 | call to source | +| true_upon_entry.cpp:78:8:78:8 | x | true_upon_entry.cpp:70:11:70:16 | call to source | +| true_upon_entry.cpp:86:8:86:8 | x | true_upon_entry.cpp:83:11:83:16 | call to source | diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.ql b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.ql new file mode 100644 index 000000000000..58af834de1f9 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.ql @@ -0,0 +1,5 @@ +import DataflowTestCommon + +from DataFlow::Node sink, DataFlow::Node source, TestAllocationConfig cfg +where cfg.hasFlow(source, sink) +select sink, source diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/true_upon_entry.cpp b/cpp/ql/test/library-tests/dataflow/dataflow-tests/true_upon_entry.cpp new file mode 100644 index 000000000000..435fafa037c5 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/true_upon_entry.cpp @@ -0,0 +1,106 @@ +// This file tests that data flow cannot survive in a variable past a loop that +// always assigns to this variable. + +int source(); +void sink(...); +bool random(); + +int test1() { + int x = source(); + for (int i = 0; i < 10; i++) { + x = 0; + } + sink(x); // GOOD (x always overwritten) +} + +int test2(int iterations) { + int x = source(); + for (int i = 0; i < iterations; i++) { + x = 0; + } + sink(x); // BAD +} + +int test3() { + int x = 0; + for (int i = 0; i < 10; i++) { + x = source(); + } + sink(x); // BAD +} + +int test4() { + int x = source(); + for (int i = 0; i < 10; i++) { + if (random()) + break; + x = 0; + } + sink(x); // BAD +} + +int test5() { + int x = source(); + for (int i = 0; i < 10; i++) { + if (random()) + continue; + x = 0; + } + sink(x); // BAD +} + +int test6() { + int y; + int x = source(); + for (int i = 0; i < 10 && (y = 1); i++) { + } + sink(x); // BAD +} + +int test7() { + int y; + int x = source(); + for (int i = 0; i < 10 && (y = 1); i++) { + x = 0; + } + sink(x); // GOOD +} + +int test8() { + int x = source(); + // It appears to the analysis that the condition can exit after `i < 10` + // without having assigned to `x`. That is an effect of how the + // true-upon-entry analysis considers the entire loop condition, while the + // analysis of where we might jump out of the loop condition considers every + // jump out of the condition, not just the last one. + for (int i = 0; i < 10 && (x = 1); i++) { + } + sink(x); // GOOD [false positive] +} + +int test9() { + int y; + int x = source(); + for (int i = 0; (y = 1) && i < 10; i++) { + } + sink(x); // BAD +} + +int test10() { + int x = source(); + for (int i = 0; (x = 1) && i < 10; i++) { + } + sink(x); // GOOD +} + +int test10(int b, int d) { + int i = 0; + int x = source(); + if (b) + goto L; + for (; i < 10; i += d) { + x = 0; + L: + } + sink(x); // BAD +} diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/uninitialized.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/uninitialized.expected new file mode 100644 index 000000000000..4067f9d3971c --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/uninitialized.expected @@ -0,0 +1,3 @@ +| test.cpp:75:7:75:8 | u1 | test.cpp:76:8:76:9 | u1 | +| test.cpp:83:7:83:8 | u2 | test.cpp:84:13:84:14 | u2 | +| test.cpp:83:7:83:8 | u2 | test.cpp:85:8:85:9 | u2 | diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/uninitialized.ql b/cpp/ql/test/library-tests/dataflow/dataflow-tests/uninitialized.ql new file mode 100644 index 000000000000..8be3004152e6 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/uninitialized.ql @@ -0,0 +1,5 @@ +import semmle.code.cpp.dataflow.internal.FlowVar + +from LocalScopeVariable var, VariableAccess va +where FlowVar_internal::mayBeUsedUninitialized(var, va) +select var, va diff --git a/cpp/ql/test/library-tests/dataflow/recursion/chained_use.expected b/cpp/ql/test/library-tests/dataflow/recursion/chained_use.expected new file mode 100644 index 000000000000..2a4f078a25fc --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/recursion/chained_use.expected @@ -0,0 +1 @@ +| 1 | diff --git a/cpp/ql/test/library-tests/dataflow/recursion/chained_use.ql b/cpp/ql/test/library-tests/dataflow/recursion/chained_use.ql new file mode 100644 index 000000000000..aeed7825a375 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/recursion/chained_use.ql @@ -0,0 +1,73 @@ +import cpp +import semmle.code.cpp.dataflow.DataFlow +import semmle.code.cpp.dataflow.DataFlow2 +import semmle.code.cpp.dataflow.DataFlow3 +import semmle.code.cpp.dataflow.DataFlow4 +import semmle.code.cpp.dataflow.RecursionPrevention + +class TestConf1 extends DataFlow::Configuration { + TestConf1() { this = "TestConf1" } + override + predicate isSource(DataFlow::Node source) { any() } + override + predicate isSink(DataFlow::Node sink) { any() } +} + +class TestConf2 extends DataFlow2::Configuration { + TestConf2() { this = "TestConf2" } + override + predicate isSource(DataFlow::Node source) { + exists(TestConf1 conf1 | conf1.hasFlowTo(source)) + } + override + predicate isSink(DataFlow::Node sink) { + exists(TestConf1 conf1 | conf1.hasFlowTo(sink)) + } + override + predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) { + exists(TestConf1 conf1 | + conf1.hasFlowTo(n1) and + conf1.hasFlowTo(n2) + ) + } +} + +class TestConf3 extends DataFlow3::Configuration { + TestConf3() { this = "TestConf3" } + override + predicate isSource(DataFlow::Node source) { + exists(TestConf2 conf2 | conf2.hasFlowTo(source)) + } + override + predicate isSink(DataFlow::Node sink) { + exists(TestConf2 conf2 | conf2.hasFlowTo(sink)) + } + override + predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) { + exists(TestConf2 conf2 | + conf2.hasFlowTo(n1) and + conf2.hasFlowTo(n2) + ) + } +} + +class TestConf4 extends DataFlow4::Configuration { + TestConf4() { this = "TestConf4" } + override + predicate isSource(DataFlow::Node source) { + exists(TestConf3 conf3 | conf3.hasFlowTo(source)) + } + override + predicate isSink(DataFlow::Node sink) { + exists(TestConf3 conf3 | conf3.hasFlowTo(sink)) + } + override + predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) { + exists(TestConf3 conf3 | + conf3.hasFlowTo(n1) and + conf3.hasFlowTo(n2) + ) + } +} + +select 1 diff --git a/cpp/ql/test/library-tests/dataflow/recursion/recursion.c b/cpp/ql/test/library-tests/dataflow/recursion/recursion.c new file mode 100644 index 000000000000..109733d75277 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/recursion/recursion.c @@ -0,0 +1,2 @@ +void f() { +} diff --git a/cpp/ql/test/library-tests/dataflow/stackaddress/StackPointerFlowsToUse.expected b/cpp/ql/test/library-tests/dataflow/stackaddress/StackPointerFlowsToUse.expected new file mode 100644 index 000000000000..dd9d02c85516 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/stackaddress/StackPointerFlowsToUse.expected @@ -0,0 +1,29 @@ +| test.cpp:6:10:6:11 | & ... | file://:0:0:0:0 | int | test.cpp:6:11:6:11 | x | true | +| test.cpp:7:10:7:11 | & ... | file://:0:0:0:0 | int | test.cpp:7:11:7:11 | x | true | +| test.cpp:13:10:13:10 | p | file://:0:0:0:0 | int | test.cpp:12:13:12:13 | x | true | +| test.cpp:14:10:14:10 | p | file://:0:0:0:0 | int | test.cpp:12:13:12:13 | x | true | +| test.cpp:19:10:19:10 | q | file://:0:0:0:0 | int | test.cpp:18:12:18:12 | p | false | +| test.cpp:20:10:20:10 | q | file://:0:0:0:0 | int | test.cpp:18:12:18:12 | p | false | +| test.cpp:26:18:26:18 | p | file://:0:0:0:0 | int | test.cpp:25:13:25:13 | x | true | +| test.cpp:31:10:31:10 | x | file://:0:0:0:0 | int | test.cpp:31:10:31:10 | x | true | +| test.cpp:32:10:32:12 | ... + ... | file://:0:0:0:0 | int | test.cpp:32:10:32:10 | x | true | +| test.cpp:33:10:33:10 | x | file://:0:0:0:0 | int | test.cpp:33:10:33:10 | x | true | +| test.cpp:39:10:39:10 | p | file://:0:0:0:0 | int | test.cpp:38:12:38:12 | x | true | +| test.cpp:40:10:40:12 | ... - ... | file://:0:0:0:0 | int | test.cpp:38:12:38:12 | x | true | +| test.cpp:41:10:41:10 | p | file://:0:0:0:0 | int | test.cpp:38:12:38:12 | x | true | +| test.cpp:47:10:47:10 | p | file://:0:0:0:0 | int | test.cpp:46:13:46:13 | x | true | +| test.cpp:48:10:48:10 | p | file://:0:0:0:0 | int | test.cpp:46:13:46:13 | x | true | +| test.cpp:54:10:54:10 | p | file://:0:0:0:0 | int | test.cpp:53:13:53:13 | x | true | +| test.cpp:59:10:59:10 | q | file://:0:0:0:0 | int | test.cpp:58:12:58:12 | p | false | +| test.cpp:60:10:60:12 | ... + ... | file://:0:0:0:0 | int | test.cpp:58:12:58:12 | p | false | +| test.cpp:61:10:61:10 | q | file://:0:0:0:0 | int | test.cpp:58:12:58:12 | p | false | +| test.cpp:66:18:66:18 | x | file://:0:0:0:0 | int | test.cpp:66:18:66:18 | x | true | +| test.cpp:67:18:67:20 | ... - ... | file://:0:0:0:0 | int | test.cpp:67:18:67:18 | x | true | +| test.cpp:78:10:78:20 | & ... | file://:0:0:0:0 | int | test.cpp:78:11:78:11 | x | true | +| test.cpp:79:10:79:20 | & ... | file://:0:0:0:0 | int | test.cpp:79:11:79:11 | x | true | +| test.cpp:82:10:82:25 | & ... | file://:0:0:0:0 | int | test.cpp:81:24:81:24 | x | true | +| test.cpp:83:10:83:25 | & ... | file://:0:0:0:0 | int | test.cpp:81:24:81:24 | x | true | +| test.cpp:87:10:87:17 | & ... | file://:0:0:0:0 | int | test.cpp:87:11:87:11 | x | false | +| test.cpp:88:10:88:17 | & ... | file://:0:0:0:0 | int | test.cpp:88:11:88:11 | x | false | +| test.cpp:93:18:93:21 | access to array | file://:0:0:0:0 | int[5] | test.cpp:93:18:93:18 | x | true | +| test.cpp:98:10:98:10 | x | file://:0:0:0:0 | int[4][5] | test.cpp:98:10:98:10 | x | true | diff --git a/cpp/ql/test/library-tests/dataflow/stackaddress/StackPointerFlowsToUse.ql b/cpp/ql/test/library-tests/dataflow/stackaddress/StackPointerFlowsToUse.ql new file mode 100644 index 000000000000..bc47c9537d31 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/stackaddress/StackPointerFlowsToUse.ql @@ -0,0 +1,8 @@ +import cpp +import semmle.code.cpp.dataflow.StackAddress + +from FunctionCall call, Expr use, Type useType, Expr source, boolean isLocal +where + use = call.getAnArgument() and + stackPointerFlowsToUse(use, useType, source, isLocal) +select use, useType, source, isLocal diff --git a/cpp/ql/test/library-tests/dataflow/stackaddress/test.cpp b/cpp/ql/test/library-tests/dataflow/stackaddress/test.cpp new file mode 100644 index 000000000000..883898b76102 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/stackaddress/test.cpp @@ -0,0 +1,99 @@ +void sink_i(int*); +void sink_v(void*); + +void test000() { + int x; + sink_i(&x); + sink_v(&x); +} + +void test001() { + int x; + int *p = &x; + sink_i(p); + sink_v(p); +} + +void test002_helper(int *p) { + int *q = p; + sink_i(q); + sink_v(q); +} + +void test002() { + int x; + int *p = &x; + test002_helper(p); +} + +void test100() { + int x[10]; + sink_i(x); + sink_i(x+2); + sink_v(x); +} + +void test101() { + int x[10]; + int *p = x; + sink_i(p); + sink_i(p-2); + sink_v(p); +} + +void test102() { + int x[10]; + int *p = &x[2]; + sink_i(p); + sink_v(p); +} + +void test103() { + int x[10]; + void *p = x; + sink_v(p); +} + +void test104_helper(int *p) { + int *q = p; + sink_i(q); + sink_i(q+2); + sink_v(q); +} + +void test104() { + int x[10]; + test104_helper(x); + test104_helper(x-2); +} + +void test200() { + int* p = new int[10]; + sink_i(p); + sink_v(p); +} + +void test300() { + int x[3][4][5]; + sink_i(&x[0][0][0]); + sink_v(&x[0][0][0]); + + int (*p)[3][4][5] = &x; + sink_i(&((*p)[0][0][0])); + sink_v(&((*p)[0][0][0])); +} + +void test301_helper(int x[4][5]) { + sink_i(&x[0][0]); + sink_v(&x[0][0]); +} + +void test301() { + int x[3][4][5]; + test301_helper(x[0]); +} + +void test302() { + int x[3][4][5]; + sink_v(x); +} diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected new file mode 100644 index 000000000000..0ed943ffa23e --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected @@ -0,0 +1,135 @@ +| file://:0:0:0:0 | this | taint.cpp:72:3:72:3 | c | TAINT | +| file://:0:0:0:0 | this | taint.cpp:73:3:73:3 | d | TAINT | +| file://:0:0:0:0 | this | taint.cpp:77:3:77:3 | d | TAINT | +| 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 | | +| taint.cpp:6:3:6:8 | clean1 | taint.cpp:6:3:6:19 | ... += ... | TAINT | +| taint.cpp:6:3:6:19 | ... += ... | taint.cpp:7:3:7:8 | clean1 | | +| taint.cpp:6:13:6:19 | source1 | taint.cpp:6:3:6:19 | ... += ... | TAINT | +| 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: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: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 | ... = ... | | +| taint.cpp:13:3:13:9 | source1 | taint.cpp:13:3:13:11 | ... ++ | | +| taint.cpp:13:3:13:11 | ... ++ | taint.cpp:14:5:14:11 | source1 | TAINT | +| taint.cpp:14:3:14:11 | ++ ... | taint.cpp:15:3:15:9 | source1 | | +| taint.cpp:14:5:14:11 | source1 | taint.cpp:14:3:14:11 | ++ ... | TAINT | +| taint.cpp:15:3:15:9 | source1 | taint.cpp:15:3:15:14 | ... += ... | TAINT | +| 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: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: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: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: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: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: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:14:71:17 | 0 | taint.cpp:71:14:71:17 | constructor init of field a | TAINT | +| taint.cpp:71:22:71:27 | call to source | taint.cpp:71:20:71:30 | constructor init of field b | TAINT | +| taint.cpp:72:7:72:12 | call to source | taint.cpp:72:3:72:14 | ... = ... | | +| taint.cpp:73:7:73:7 | 0 | taint.cpp:73:3:73:7 | ... = ... | | +| 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 | | +| taint.cpp:84:10:84:12 | call to MyClass | taint.cpp:89:7:89:9 | mc1 | | +| taint.cpp:84:10:84:12 | call to MyClass | taint.cpp:90:7:90:9 | mc1 | | +| taint.cpp:84:10:84:12 | call to MyClass | taint.cpp:91:7:91:9 | mc1 | | +| taint.cpp:84:15:84:17 | call to MyClass | taint.cpp:92:7:92:9 | mc2 | | +| taint.cpp:84:15:84:17 | call to MyClass | taint.cpp:93:7:93:9 | mc2 | | +| taint.cpp:84:15:84:17 | call to MyClass | taint.cpp:94:7:94:9 | mc2 | | +| taint.cpp:84:15:84:17 | call to MyClass | taint.cpp:95:7:95:9 | mc2 | | +| taint.cpp:88:7:88:9 | mc1 | taint.cpp:88:11:88:11 | a | TAINT | +| taint.cpp:89:7:89:9 | mc1 | taint.cpp:89:11:89:11 | b | TAINT | +| taint.cpp:90:7:90:9 | mc1 | taint.cpp:90:11:90:11 | c | TAINT | +| taint.cpp:91:7:91:9 | mc1 | taint.cpp:91:11:91:11 | d | TAINT | +| taint.cpp:92:7:92:9 | mc2 | taint.cpp:92:11:92:11 | a | TAINT | +| taint.cpp:93:7:93:9 | mc2 | taint.cpp:93:11:93:11 | b | TAINT | +| taint.cpp:94:7:94:9 | mc2 | taint.cpp:94:11:94:11 | c | TAINT | +| taint.cpp:95:7:95:9 | mc2 | taint.cpp:95:11:95:11 | d | TAINT | +| taint.cpp:100:21:100:21 | i | taint.cpp:106:7:106:7 | i | | +| taint.cpp:100:21:100:21 | i | taint.cpp:110:12:110:12 | i | | +| 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: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: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: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: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: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: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: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: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: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: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: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: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 | | +| taint.cpp:121:10:121:11 | 1 | taint.cpp:124:13:124:14 | t2 | | +| taint.cpp:122:10:122:11 | 1 | taint.cpp:125:13:125:14 | t3 | | +| taint.cpp:123:12:123:14 | & ... | taint.cpp:129:8:129:9 | p1 | | +| taint.cpp:123:13:123:14 | t1 | taint.cpp:123:12:123:14 | & ... | TAINT | +| taint.cpp:124:12:124:14 | & ... | taint.cpp:127:3:127:4 | p2 | | +| taint.cpp:124:12:124:14 | & ... | taint.cpp:130:8:130:9 | p2 | | +| taint.cpp:124:13:124:14 | t2 | taint.cpp:124:12:124:14 | & ... | TAINT | +| 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 | +| 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 | +| taint.cpp:130:8:130:9 | p2 | taint.cpp:130:7:130:9 | * ... | TAINT | +| taint.cpp:131:8:131:9 | p3 | taint.cpp:131:7:131:9 | * ... | TAINT | +| taint.cpp:133:7:133:9 | & ... | taint.cpp:133:2:133:9 | ... = ... | | +| taint.cpp:133:7:133:9 | & ... | taint.cpp:134:8:134:9 | p3 | | +| taint.cpp:133:7:133:9 | & ... | taint.cpp:136:3:136:4 | p3 | | +| 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 | +| taint.cpp:134:8:134:9 | p3 | taint.cpp:134:7:134:9 | * ... | TAINT | +| 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 | +| taint.cpp:142:16:142:16 | i | taint.cpp:143:6:143:6 | i | | +| taint.cpp:142:23:142:23 | a | taint.cpp:144:10:144:10 | a | | +| taint.cpp:142:30:142:30 | b | taint.cpp:146:10:146:10 | b | | +| taint.cpp:150:18:150:18 | i | taint.cpp:151:14:151:14 | i | | +| taint.cpp:164:19:164:24 | call to source | taint.cpp:168:8:168:14 | tainted | | +| taint.cpp:164:19:164:24 | call to source | taint.cpp:172:18:172:24 | tainted | | +| taint.cpp:165:22:165:25 | {...} | taint.cpp:170:10:170:15 | buffer | | +| taint.cpp:165:24:165:24 | 0 | taint.cpp:165:22:165:25 | {...} | TAINT | +| taint.cpp:180:19:180:19 | p | taint.cpp:181:9:181:9 | p | | +| taint.cpp:181:9:181:9 | p | taint.cpp:181:8:181:9 | * ... | TAINT | +| taint.cpp:185:11:185:16 | call to source | taint.cpp:186:11:186:11 | x | | +| taint.cpp:186:11:186:11 | x | taint.cpp:186:10:186:11 | & ... | TAINT | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.ql b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.ql new file mode 100644 index 000000000000..b49cd46d6a10 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.ql @@ -0,0 +1,9 @@ +import cpp +import semmle.code.cpp.dataflow.TaintTracking + +from DataFlow::Node nodeFrom, DataFlow::Node nodeTo, string msg +where TaintTracking::localTaintStep(nodeFrom, nodeTo) + and if DataFlow::localFlowStep(nodeFrom, nodeTo) + then msg = "" + else msg = "TAINT" +select nodeFrom, nodeTo, msg diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp new file mode 100644 index 000000000000..d30514eedd4c --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp @@ -0,0 +1,188 @@ +int source(); +void sink(...); + +void arithAssignments(int source1, int clean1) { + sink(clean1); // clean + clean1 += source1; + clean1 += 1; + sink(clean1); // tainted + + clean1 = source1 = 1; + sink(clean1); // clean + source1 = clean1 = source(); + source1++; + ++source1; + source1 += 1; + sink(source1); // tainted + sink(++source1); // tainted +} + +// --- globals --- + +int increment(int x) {return x + 1;} +int zero(int x) {return 0;} + +int global1 = 0; +int global2 = source(); +int global3 = global2 + 1; +int global4 = increment(source()); +int global5 = zero(source()); +int global6, global7, global8, global9, global10; + +void do_source() +{ + global6 = 0; + global7 = source(); + global8 = global7 + 1; + global9 = increment(source()); + global10 = zero(source()); + + sink(global6); + sink(global7); // tainted + sink(global8); // tainted + sink(global9); // tainted + sink(global10); +} + +void do_sink() +{ + sink(global1); + sink(global2); // tainted [NOT DETECTED] + sink(global3); // tainted [NOT DETECTED] + sink(global4); // tainted [NOT DETECTED] + sink(global5); + sink(global6); + sink(global7); // tainted [NOT DETECTED] + sink(global8); // tainted [NOT DETECTED] + sink(global9); // tainted [NOT DETECTED] + sink(global10); +} + +void global_test() +{ + do_source(); + do_sink(); +} + +// --- class fields --- + +class MyClass { +public: + MyClass() : a(0), b(source()) { + c = source(); + d = 0; + } + + void myMethod() { + d = source(); + } + + int a, b, c, d; +}; + +void class_field_test() { + MyClass mc1, mc2; + + mc1.myMethod(); + + sink(mc1.a); + sink(mc1.b); // tainted [NOT DETECTED] + sink(mc1.c); // tainted [NOT DETECTED] + sink(mc1.d); // tainted [NOT DETECTED] + sink(mc2.a); + sink(mc2.b); // tainted [NOT DETECTED] + sink(mc2.c); // tainted [NOT DETECTED] + sink(mc2.d); +} + +// --- arrays --- + +void array_test(int i) { + int arr1[10] = {0}; + int arr2[10] = {0}; + int arr3[10] = {0}; + + arr1[5] = source(); + arr2[i] = source(); + arr3[5] = 0; + + sink(arr1[5]); // tainted [NOT DETECTED] + sink(arr1[i]); // tainted [NOT DETECTED] + sink(arr2[5]); // tainted [NOT DETECTED] + sink(arr2[i]); // tainted [NOT DETECTED] + sink(arr3[5]); + sink(arr3[i]); +} + +// --- pointers --- + +void pointer_test() { + int t1 = source(); + int t2 = 1; + int t3 = 1; + int *p1 = &t1; + int *p2 = &t2; + int *p3 = &t3; + + *p2 = source(); + + sink(*p1); // tainted + sink(*p2); // tainted [NOT DETECTED] + sink(*p3); + + p3 = &t1; + sink(*p3); // tainted + + *p3 = 0; + sink(*p3); // [FALSE POSITIVE] +} + +// --- return values --- + +int select(int i, int a, int b) { + if (i == 1) { + return a; + } else { + return b; + } +} + +void fn_test(int i) { + sink(select(i, 1, source())); // tainted +} + +// --- strings --- + +char *strcpy(char *destination, const char *source); +char *strcat(char *destination, const char *source); + +namespace strings +{ + char *source(); // char* source + + void strings_test1() { + char *tainted = source(); + char buffer[1024] = {0}; + + sink(source()); // tainted + sink(tainted); // tainted + + strcpy(buffer, "Hello, "); + sink(buffer); + strcat(buffer, tainted); + sink(buffer); // tainted [NOT DETECTED] + } +} + +// --- pass by reference --- + +namespace refs { + void callee(int *p) { + sink(*p); // tainted + } + + void caller() { + int x = source(); + callee(&x); + } +} diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected new file mode 100644 index 000000000000..5b3972062c95 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected @@ -0,0 +1,13 @@ +| 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:41:7:41:13 | global7 | taint.cpp:35:12:35:17 | call to source | +| taint.cpp:42:7:42:13 | global8 | taint.cpp:35:12:35:17 | call to source | +| taint.cpp:43:7:43:13 | global9 | taint.cpp:37:22:37:27 | call to source | +| taint.cpp:129:7:129:9 | * ... | taint.cpp:120:11:120:16 | 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 | +| 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:181:8:181:9 | * ... | taint.cpp:185:11:185:16 | call to source | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.ql b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.ql new file mode 100644 index 000000000000..c35ddab6e1ec --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.ql @@ -0,0 +1,33 @@ +import cpp +import semmle.code.cpp.dataflow.TaintTracking + +/** Common data flow configuration to be used by tests. */ +class TestAllocationConfig extends TaintTracking::Configuration { + TestAllocationConfig() { + this = "TestAllocationConfig" + } + + override predicate isSource(DataFlow::Node source) { + source.asExpr().(FunctionCall).getTarget().getName() = "source" + or + source.asParameter().getName().matches("source%") + or + // Track uninitialized variables + exists(source.asUninitialized()) + } + + override predicate isSink(DataFlow::Node sink) { + exists(FunctionCall call | + call.getTarget().getName() = "sink" and + sink.asExpr() = call.getAnArgument() + ) + } + + override predicate isSanitizer(DataFlow::Node barrier) { + barrier.asExpr().(VariableAccess).getTarget().hasName("sanitizer") + } +} + +from DataFlow::Node sink, DataFlow::Node source, TestAllocationConfig cfg +where cfg.hasFlow(source, sink) +select sink, source diff --git a/cpp/ql/test/library-tests/dataflow/variable/noInit.c b/cpp/ql/test/library-tests/dataflow/variable/noInit.c new file mode 100644 index 000000000000..06ad434c9d34 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/variable/noInit.c @@ -0,0 +1,47 @@ +void f(int); + +int t1(int b) { + int x; + if (b) + x = 1; + f(x); // BAD + x = 1; + return x; +} + +int t2(int b) { + int x; + if (b) + x = 1; + else + x = 2; + f(x); + x = 1; + return x; +} + +int t3(int b) { + int x; + f(x); // BAD + x++; // BAD + x += 1; // BAD + return x; // BAD +} + +int t4(int b) { + int x = 1; + f(x); + return x; +} + +int t5(int b) { + int x; + x = 1; + f(x); + return x; +} + +int t6() { + int x; + f(x); // BAD +} diff --git a/cpp/ql/test/library-tests/dataflow/variable/noInit.expected b/cpp/ql/test/library-tests/dataflow/variable/noInit.expected new file mode 100644 index 000000000000..01a507b28b22 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/variable/noInit.expected @@ -0,0 +1,6 @@ +| noInit.c:4:9:4:9 | x | noInit.c:7:7:7:7 | x | +| noInit.c:24:9:24:9 | x | noInit.c:25:7:25:7 | x | +| noInit.c:24:9:24:9 | x | noInit.c:26:5:26:5 | x | +| noInit.c:24:9:24:9 | x | noInit.c:27:5:27:5 | x | +| noInit.c:24:9:24:9 | x | noInit.c:28:12:28:12 | x | +| noInit.c:45:7:45:7 | x | noInit.c:46:5:46:5 | x | diff --git a/cpp/ql/test/library-tests/dataflow/variable/noInit.ql b/cpp/ql/test/library-tests/dataflow/variable/noInit.ql new file mode 100644 index 000000000000..8be3004152e6 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/variable/noInit.ql @@ -0,0 +1,5 @@ +import semmle.code.cpp.dataflow.internal.FlowVar + +from LocalScopeVariable var, VariableAccess va +where FlowVar_internal::mayBeUsedUninitialized(var, va) +select var, va diff --git a/cpp/ql/test/library-tests/declaration/IsMember.expected b/cpp/ql/test/library-tests/declaration/IsMember.expected new file mode 100644 index 000000000000..174b8745c854 --- /dev/null +++ b/cpp/ql/test/library-tests/declaration/IsMember.expected @@ -0,0 +1,49 @@ +| declaration.cpp:32:7:32:7 | MyClass0 | +| declaration.cpp:32:7:32:7 | MyClass0 | +| 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 | operator= | +| declaration.cpp:36:9:36:9 | operator= | +| declaration.cpp:36:9:36:21 | MyNestedClass | +| declaration.cpp:37:9:37:21 | myNestedField | +| declaration.cpp:40:3:40:10 | MyClass0 | +| declaration.cpp:42:7:42:17 | getMyField0 | +| declaration.cpp:46:8:46:8 | operator= | +| declaration.cpp:46:8:46:8 | operator= | +| declaration.cpp:47:7:47:14 | myField0 | +| declaration.cpp:51:7:51:7 | operator= | +| declaration.cpp:51:7:51:7 | operator= | +| declaration.cpp:52:7:52:14 | myField0 | +| declaration.cpp:53:9:53:17 | myPointer | +| declaration.cpp:62:7:62:7 | operator= | +| declaration.cpp:62:7:62:7 | operator= | +| declaration.cpp:62:7:62:7 | operator= | +| declaration.cpp:62:7:62:7 | operator= | +| declaration.cpp:65:5:65:20 | myMemberVariable | +| declaration.cpp:65:5:65:20 | myMemberVariable | +| declaration.cpp:65:5:65:20 | myMemberVariable | +| declaration.cpp:100:7:100:7 | MyClass1 | +| declaration.cpp:100:7:100:7 | MyClass1 | +| 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 | operator= | +| declaration.cpp:104:9:104:9 | operator= | +| declaration.cpp:104:9:104:21 | MyNestedClass | +| declaration.cpp:105:9:105:21 | myNestedField | +| declaration.cpp:108:3:108:10 | MyClass1 | +| declaration.cpp:110:7:110:17 | getMyField1 | +| declaration.cpp:119:7:119:7 | operator= | +| declaration.cpp:119:7:119:7 | operator= | +| declaration.cpp:119:7:119:7 | operator= | +| declaration.cpp:119:7:119:7 | operator= | +| declaration.cpp:122:5:122:20 | myMemberVariable | +| declaration.cpp:122:5:122:20 | myMemberVariable | +| declaration.cpp:122:5:122:20 | myMemberVariable | +| file://:0:0:0:0 | fp_offset | +| file://:0:0:0:0 | gp_offset | +| file://:0:0:0:0 | operator= | +| file://:0:0:0:0 | operator= | +| file://:0:0:0:0 | overflow_arg_area | +| file://:0:0:0:0 | reg_save_area | diff --git a/cpp/ql/test/library-tests/declaration/IsMember.ql b/cpp/ql/test/library-tests/declaration/IsMember.ql new file mode 100644 index 000000000000..a78bc0028799 --- /dev/null +++ b/cpp/ql/test/library-tests/declaration/IsMember.ql @@ -0,0 +1,5 @@ +import cpp + +from Declaration decl +where decl.isMember() +select decl diff --git a/cpp/ql/test/library-tests/declaration/declaration.cpp b/cpp/ql/test/library-tests/declaration/declaration.cpp new file mode 100644 index 000000000000..0601f5c2d6ac --- /dev/null +++ b/cpp/ql/test/library-tests/declaration/declaration.cpp @@ -0,0 +1,133 @@ +// Variable + +extern int myVariable0; + +int myVariable0 = 10; + +// Function, Parameter + +void myFunction0(int myParameter); + +void myFunction0(int myParameter) +{ + // ... +} + +// Enum (UserType) + +enum myEnum0 : short; + +enum myEnum0 : short +{ + myEnumConst0 +}; + +enum class MyEnumClass { + RED, + GREEN, + BLUE +}; + +// Class +class MyClass0 { +public: + int myField0; + + class MyNestedClass { + int myNestedField; + }; + + MyClass0() : myField0(0) { myField0 = 1; } + + int getMyField0() { return myField0; } +}; + +// Struct +struct MyStruct0 { + int myField0; +}; + +// Union +union MyUnion0 { + int myField0; + void* myPointer; +}; + +// Typedef +typedef MyClass0 *MyClassPtr; + +// TemplateClass, TemplateParameter (UserType) + +template +class myTemplateClass +{ +public: + T myMemberVariable; +}; + +myTemplateClass mtc_int; +myTemplateClass mtc_short; + + +// Everything inside a namespace. +namespace MyNamespace { + +// Variable + +extern int myVariable1; + +int myVariable1 = 10; + +// Function, Parameter + +void myFunction1(int myParameter); + +void myFunction1(int myParameter) +{ + // ... +} + +// Enum (UserType) + +enum myEnum1 : short; + +enum myEnum1 : short +{ + myEnumConst1 +}; + +// Class +class MyClass1 { +public: + int myField1; + + class MyNestedClass { + int myNestedField; + }; + + MyClass1() : myField1(0) { myField1 = 1; } + + int getMyField1() { return myField1; } +}; + +// Typedef +typedef MyClass1 *MyClassPtr; + +// TemplateClass, TemplateParameter (UserType) + +template +class myTemplateClass +{ +public: + T myMemberVariable; +}; + +myTemplateClass mtc_int; +myTemplateClass mtc_short; + +// Nested namespace. +namespace NestedNamespace { +int myVariable2 = 10; +} + +} diff --git a/cpp/ql/test/library-tests/declarationEntry/declarationEntry/declarationEntry.c b/cpp/ql/test/library-tests/declarationEntry/declarationEntry/declarationEntry.c new file mode 100644 index 000000000000..71df6b1bcd31 --- /dev/null +++ b/cpp/ql/test/library-tests/declarationEntry/declarationEntry/declarationEntry.c @@ -0,0 +1,17 @@ + +void myFirstFunction(); + +void mySecondFunction() +{ +} + +void myThirdFunction() +{ + myFirstFunction(); + mySecondFunction(); + myThirdFunction(); + myFourthFunction(); // implicit FDE + myFifthFunction(); // implicit FDE +} + +void myFourthFunction(); diff --git a/cpp/ql/test/library-tests/declarationEntry/declarationEntry/declarationEntry.cpp b/cpp/ql/test/library-tests/declarationEntry/declarationEntry/declarationEntry.cpp new file mode 100644 index 000000000000..3bbfc05ec65c --- /dev/null +++ b/cpp/ql/test/library-tests/declarationEntry/declarationEntry/declarationEntry.cpp @@ -0,0 +1,35 @@ +// Variable + +extern int myVariable; + +int myVariable = 10; + +// Function, Parameter + +void myFunction(int myParameter); + +void myFunction(int myParameter) +{ + // ... +} + +// Enum (UserType) + +enum myEnum : short; + +enum myEnum : short +{ + myEnumConst +}; + +// TemplateClass, TemplateParameter (UserType) + +template +class myTemplateClass +{ +public: + T myMemberVariable; +}; + +myTemplateClass mtc_int; +myTemplateClass mtc_short; diff --git a/cpp/ql/test/library-tests/declarationEntry/declarationEntry/declarationEntry.expected b/cpp/ql/test/library-tests/declarationEntry/declarationEntry/declarationEntry.expected new file mode 100644 index 000000000000..912fa5bf1b3d --- /dev/null +++ b/cpp/ql/test/library-tests/declarationEntry/declarationEntry/declarationEntry.expected @@ -0,0 +1,29 @@ +| declarationEntry.c:2:6:2:20 | myFirstFunction | declarationEntry.c:2:6:2:20 | declaration of myFirstFunction | 1 | 1 | +| declarationEntry.c:4:6:4:21 | mySecondFunction | declarationEntry.c:4:6:4:21 | definition of mySecondFunction | 1 | 1 | +| declarationEntry.c:8:6:8:20 | myThirdFunction | declarationEntry.c:8:6:8:20 | definition of myThirdFunction | 1 | 1 | +| declarationEntry.c:13:2:13:2 | myFourthFunction | declarationEntry.c:13:2:13:2 | declaration of myFourthFunction | 1 | 1 | +| declarationEntry.c:13:2:13:2 | myFourthFunction | declarationEntry.c:17:6:17:21 | declaration of myFourthFunction | 1 | 1 | +| declarationEntry.c:14:2:14:2 | myFifthFunction | declarationEntry.c:14:2:14:2 | declaration of myFifthFunction | 1 | 1 | +| declarationEntry.c:17:6:17:21 | myFourthFunction | declarationEntry.c:13:2:13:2 | declaration of myFourthFunction | 1 | 1 | +| declarationEntry.c:17:6:17:21 | myFourthFunction | declarationEntry.c:17:6:17:21 | declaration of myFourthFunction | 1 | 1 | +| declarationEntry.cpp:5:5:5:14 | myVariable | declarationEntry.cpp:3:12:3:21 | declaration of myVariable | 1 | 1 | +| declarationEntry.cpp:5:5:5:14 | myVariable | declarationEntry.cpp:5:5:5:14 | definition of myVariable | 1 | 1 | +| declarationEntry.cpp:11:6:11:15 | myFunction | declarationEntry.cpp:9:6:9:15 | declaration of myFunction | 1 | 1 | +| declarationEntry.cpp:11:6:11:15 | myFunction | declarationEntry.cpp:11:6:11:15 | definition of myFunction | 1 | 1 | +| declarationEntry.cpp:11:21:11:31 | myParameter | declarationEntry.cpp:9:21:9:31 | declaration of myParameter | 1 | 1 | +| declarationEntry.cpp:11:21:11:31 | myParameter | declarationEntry.cpp:11:21:11:31 | definition of myParameter | 1 | 1 | +| declarationEntry.cpp:20:6:20:11 | myEnum | declarationEntry.cpp:18:6:18:11 | declaration of myEnum | 1 | 1 | +| declarationEntry.cpp:20:6:20:11 | myEnum | declarationEntry.cpp:20:6:20:11 | definition of myEnum | 1 | 1 | +| declarationEntry.cpp:27:20:27:20 | T | declarationEntry.cpp:27:20:27:20 | definition of T | 1 | 1 | +| declarationEntry.cpp:28:7:28:7 | operator= | declarationEntry.cpp:28:7:28:7 | declaration of operator= | 1 | 1 | +| declarationEntry.cpp:28:7:28:7 | operator= | declarationEntry.cpp:28:7:28:7 | declaration of operator= | 1 | 1 | +| declarationEntry.cpp:28:7:28:7 | operator= | declarationEntry.cpp:28:7:28:7 | declaration of operator= | 1 | 1 | +| declarationEntry.cpp:28:7:28:7 | operator= | declarationEntry.cpp:28:7:28:7 | declaration of operator= | 1 | 1 | +| declarationEntry.cpp:28:7:28:21 | myTemplateClass | declarationEntry.cpp:28:7:28:21 | definition of myTemplateClass | 1 | 1 | +| declarationEntry.cpp:28:7:28:21 | myTemplateClass | declarationEntry.cpp:28:7:28:21 | definition of myTemplateClass | 1 | 0 | +| declarationEntry.cpp:28:7:28:21 | myTemplateClass | declarationEntry.cpp:28:7:28:21 | definition of myTemplateClass | 1 | 0 | +| declarationEntry.cpp:31:4:31:19 | myMemberVariable | declarationEntry.cpp:31:4:31:19 | definition of myMemberVariable | 1 | 1 | +| declarationEntry.cpp:31:4:31:19 | myMemberVariable | declarationEntry.cpp:31:4:31:19 | definition of myMemberVariable | 1 | 1 | +| declarationEntry.cpp:31:4:31:19 | myMemberVariable | declarationEntry.cpp:31:4:31:19 | definition of myMemberVariable | 1 | 1 | +| declarationEntry.cpp:34:22:34:28 | mtc_int | declarationEntry.cpp:34:22:34:28 | definition of mtc_int | 1 | 1 | +| declarationEntry.cpp:35:24:35:32 | mtc_short | declarationEntry.cpp:35:24:35:32 | definition of mtc_short | 1 | 1 | diff --git a/cpp/ql/test/library-tests/declarationEntry/declarationEntry/declarationEntry.ql b/cpp/ql/test/library-tests/declarationEntry/declarationEntry/declarationEntry.ql new file mode 100644 index 000000000000..4debf88fe5be --- /dev/null +++ b/cpp/ql/test/library-tests/declarationEntry/declarationEntry/declarationEntry.ql @@ -0,0 +1,8 @@ +import cpp + +from Declaration d, DeclarationEntry de, int i, int j +where (d.getADeclarationEntry() = de or de.getDeclaration() = d) +and if d.getADeclarationEntry() = de then i = 1 else i = 0 +and if de.getDeclaration() = d then j = 1 else j = 0 +and d.getLocation().getStartLine() != 0 +select d, de, i as getADeclarationEntry, j as getDeclaration diff --git a/cpp/ql/test/library-tests/declarationEntry/declarationEntry/fde.expected b/cpp/ql/test/library-tests/declarationEntry/declarationEntry/fde.expected new file mode 100644 index 000000000000..6a452473c720 --- /dev/null +++ b/cpp/ql/test/library-tests/declarationEntry/declarationEntry/fde.expected @@ -0,0 +1,12 @@ +| declarationEntry.c:2:6:2:20 | declaration of myFirstFunction | | +| declarationEntry.c:4:6:4:21 | definition of mySecondFunction | | +| declarationEntry.c:8:6:8:20 | definition of myThirdFunction | | +| declarationEntry.c:13:2:13:2 | declaration of myFourthFunction | isImplicit | +| declarationEntry.c:14:2:14:2 | declaration of myFifthFunction | isImplicit | +| declarationEntry.c:17:6:17:21 | declaration of myFourthFunction | | +| declarationEntry.cpp:9:6:9:15 | declaration of myFunction | | +| declarationEntry.cpp:11:6:11:15 | definition of myFunction | | +| declarationEntry.cpp:28:7:28:7 | declaration of operator= | | +| declarationEntry.cpp:28:7:28:7 | declaration of operator= | | +| declarationEntry.cpp:28:7:28:7 | declaration of operator= | | +| declarationEntry.cpp:28:7:28:7 | declaration of operator= | | diff --git a/cpp/ql/test/library-tests/declarationEntry/declarationEntry/fde.ql b/cpp/ql/test/library-tests/declarationEntry/declarationEntry/fde.ql new file mode 100644 index 000000000000..292403b67697 --- /dev/null +++ b/cpp/ql/test/library-tests/declarationEntry/declarationEntry/fde.ql @@ -0,0 +1,6 @@ +import cpp + +from FunctionDeclarationEntry fde, string imp +where + if fde.isImplicit() then imp = "isImplicit" else imp = "" +select fde, imp diff --git a/cpp/ql/test/library-tests/declarationEntry/local/declarationEntry.expected b/cpp/ql/test/library-tests/declarationEntry/local/declarationEntry.expected new file mode 100644 index 000000000000..c17c1f8f02d9 --- /dev/null +++ b/cpp/ql/test/library-tests/declarationEntry/local/declarationEntry.expected @@ -0,0 +1,72 @@ +| test.cpp:3:8:3:16 | declaration of myStruct1 | TopLevel | +| test.cpp:3:8:3:16 | myStruct1 | | +| test.cpp:4:19:4:28 | declaration of myTypedef1 | TopLevel | +| test.cpp:4:19:4:28 | myTypedef1 | | +| test.cpp:6:16:6:24 | declaration of myStruct2 | | +| test.cpp:6:16:6:24 | myStruct2 | | +| test.cpp:6:26:6:35 | declaration of myTypedef2 | TopLevel | +| test.cpp:6:26:6:35 | myTypedef2 | | +| test.cpp:8:8:8:16 | declaration of myStruct3 | TopLevel | +| test.cpp:8:8:8:16 | myStruct3 | | +| test.cpp:9:6:9:12 | declaration of myFunc3 | | +| test.cpp:9:6:9:12 | myFunc3 | | +| test.cpp:9:25:9:29 | declaration of param | | +| test.cpp:9:25:9:29 | param | | +| test.cpp:11:6:11:12 | declaration of myFunc4 | | +| test.cpp:11:6:11:12 | myFunc4 | | +| test.cpp:11:21:11:29 | declaration of myStruct4 | | +| test.cpp:11:21:11:29 | myStruct4 | | +| test.cpp:11:32:11:36 | declaration of param | | +| test.cpp:11:32:11:36 | param | | +| test.cpp:13:6:13:12 | definition of myFunc5 | | +| test.cpp:13:6:13:12 | myFunc5 | | +| test.cpp:13:21:13:29 | declaration of myStruct5 | | +| test.cpp:13:21:13:29 | myStruct5 | | +| test.cpp:13:32:13:36 | definition of param | | +| test.cpp:13:32:13:36 | param | | +| test.cpp:16:8:16:16 | declaration of myStruct6 | TopLevel | +| test.cpp:16:8:16:16 | myStruct6 | | +| test.cpp:17:6:17:12 | declaration of myFunc6 | | +| test.cpp:17:6:17:12 | myFunc6 | | +| test.cpp:17:32:17:36 | declaration of param | | +| test.cpp:17:32:17:36 | param | | +| test.cpp:19:8:19:16 | declaration of myStruct7 | | +| test.cpp:19:8:19:16 | myStruct7 | | +| test.cpp:19:19:19:25 | declaration of myFunc7 | | +| test.cpp:19:19:19:25 | myFunc7 | | +| test.cpp:21:6:21:13 | declaration of myFunc8a | | +| test.cpp:21:6:21:13 | myFunc8a | | +| test.cpp:21:22:21:30 | declaration of myStruct8 | | +| test.cpp:21:22:21:30 | myStruct8 | | +| test.cpp:21:33:21:37 | declaration of param | | +| test.cpp:21:33:21:37 | param | | +| test.cpp:22:6:22:13 | declaration of myFunc8b | | +| test.cpp:22:6:22:13 | myFunc8b | | +| test.cpp:22:33:22:37 | declaration of param | | +| test.cpp:22:33:22:37 | param | | +| test.cpp:24:6:24:12 | definition of myFunc9 | | +| test.cpp:24:6:24:12 | myFunc9 | | +| test.cpp:25:9:25:17 | declaration of myStruct9 | | +| test.cpp:25:9:25:17 | myStruct9 | | +| test.cpp:28:7:28:7 | declaration of operator= | | +| test.cpp:28:7:28:7 | declaration of operator= | | +| test.cpp:28:7:28:7 | operator= | | +| test.cpp:28:7:28:7 | operator= | | +| test.cpp:28:7:28:14 | definition of myClass1 | TopLevel | +| test.cpp:28:7:28:14 | myClass1 | | +| test.cpp:30:9:30:18 | declaration of myStruct10 | | +| test.cpp:30:9:30:18 | myStruct10 | | +| test.cpp:33:17:33:17 | T | | +| test.cpp:33:17:33:17 | definition of T | | +| test.cpp:33:26:33:41 | declaration of myTemplateClass1 | TopLevel | +| test.cpp:33:26:33:41 | myTemplateClass1 | | +| test.cpp:35:7:35:7 | declaration of operator= | | +| test.cpp:35:7:35:7 | declaration of operator= | | +| test.cpp:35:7:35:7 | operator= | | +| test.cpp:35:7:35:7 | operator= | | +| test.cpp:35:7:35:14 | definition of myClass2 | TopLevel | +| test.cpp:35:7:35:14 | myClass2 | | +| test.cpp:37:18:37:18 | T | | +| test.cpp:37:18:37:18 | definition of T | | +| test.cpp:37:27:37:42 | declaration of myTemplateClass2 | | +| test.cpp:37:27:37:42 | myTemplateClass2 | | diff --git a/cpp/ql/test/library-tests/declarationEntry/local/declarationEntry.ql b/cpp/ql/test/library-tests/declarationEntry/local/declarationEntry.ql new file mode 100644 index 000000000000..9ba717edaa56 --- /dev/null +++ b/cpp/ql/test/library-tests/declarationEntry/local/declarationEntry.ql @@ -0,0 +1,17 @@ +import cpp + +from Element e, string s +where + ( + e instanceof DeclarationEntry or + e instanceof Declaration + ) and ( + e.getFile().toString() != "" + ) and ( + if e.(TypeDeclarationEntry).isTopLevel() then ( + s = "TopLevel" + ) else ( + s = "" + ) + ) +select e, s diff --git a/cpp/ql/test/library-tests/declarationEntry/local/test.cpp b/cpp/ql/test/library-tests/declarationEntry/local/test.cpp new file mode 100644 index 000000000000..903fbcdcad0e --- /dev/null +++ b/cpp/ql/test/library-tests/declarationEntry/local/test.cpp @@ -0,0 +1,38 @@ +// test.cpp + +struct myStruct1; +typedef myStruct1 myTypedef1; + +typedef struct myStruct2 myTypedef2; + +struct myStruct3; +void myFunc3(myStruct3 *param); + +void myFunc4(struct myStruct4 *param); + +void myFunc5(struct myStruct5 *param) { +} + +struct myStruct6; +void myFunc6(struct myStruct6 *param); + +struct myStruct7 *myFunc7(); + +void myFunc8a(struct myStruct8 *param); +void myFunc8b(struct myStruct8 *param); + +void myFunc9() { + struct myStruct9; +} + +class myClass1 { +public: + struct myStruct10; +}; + +template class myTemplateClass1; + +class myClass2 { +public: + template class myTemplateClass2; +}; diff --git a/cpp/ql/test/library-tests/declarationEntry/more/declarationEntry.expected b/cpp/ql/test/library-tests/declarationEntry/more/declarationEntry.expected new file mode 100644 index 000000000000..4f89ef48c486 --- /dev/null +++ b/cpp/ql/test/library-tests/declarationEntry/more/declarationEntry.expected @@ -0,0 +1,83 @@ +| file://:0:0:0:0 | declaration of 1st parameter | +| file://:0:0:0:0 | declaration of 1st parameter | +| file://:0:0:0:0 | declaration of 1st parameter | +| file://:0:0:0:0 | declaration of 1st parameter | +| file://:0:0:0:0 | declaration of 1st parameter | +| file://:0:0:0:0 | declaration of 1st parameter | +| file://:0:0:0:0 | declaration of 1st parameter | +| file://:0:0:0:0 | declaration of 1st parameter | +| file://:0:0:0:0 | definition of __va_list_tag | +| file://:0:0:0:0 | definition of fp_offset | +| file://:0:0:0:0 | definition of gp_offset | +| file://:0:0:0:0 | definition of overflow_arg_area | +| file://:0:0:0:0 | definition of reg_save_area | +| test.cpp:2:5:2:12 | definition of variable | +| test.cpp:4:6:4:14 | declaration of protoOnly | +| test.cpp:6:6:6:12 | definition of defOnly | +| test.cpp:8:6:8:16 | declaration of protoAndDef | +| test.cpp:9:6:9:16 | definition of protoAndDef | +| test.cpp:11:19:11:19 | definition of T | +| test.cpp:12:6:12:18 | declaration of tmplProtoOnly | +| test.cpp:12:22:12:22 | declaration of t | +| test.cpp:14:19:14:19 | definition of T | +| test.cpp:15:6:15:16 | definition of tmplDefOnly | +| test.cpp:15:20:15:20 | definition of t | +| test.cpp:17:19:17:19 | definition of T | +| test.cpp:18:6:18:20 | declaration of tmplProtoAndDef | +| test.cpp:19:19:19:19 | definition of T | +| 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 operator= | +| test.cpp:22:7:22:7 | declaration of operator= | +| test.cpp:22:7:22:8 | definition of Cl | +| test.cpp:23:9:23:13 | definition of clVar | +| test.cpp:25:10:25:20 | declaration of clProtoOnly | +| test.cpp:27:10:27:18 | definition of clDefOnly | +| test.cpp:29:10:29:22 | declaration of clProtoAndDef | +| test.cpp:31:23:31:23 | definition of T | +| test.cpp:32:10:32:24 | declaration of clTmplProtoOnly | +| test.cpp:32:28:32:28 | declaration of t | +| test.cpp:34:23:34:23 | definition of T | +| test.cpp:35:10:35:22 | definition of clTmplDefOnly | +| test.cpp:35:26:35:26 | definition of t | +| test.cpp:37:23:37:23 | definition of T | +| test.cpp:38:10:38:26 | declaration of clTmplProtoAndDef | +| test.cpp:41:6:41:22 | definition of clProtoAndDef | +| test.cpp:43:19:43:19 | definition of T | +| test.cpp:44:6:44:26 | definition of clTmplProtoAndDef | +| test.cpp:44:30:44:30 | declaration of t | +| test.cpp:44:30:44:30 | definition of t | +| test.cpp:46:7:46:20 | declaration of classProtoOnly | +| test.cpp:48:7:48:22 | declaration of classProtoAndDef | +| test.cpp:49:7:49:7 | declaration of operator= | +| test.cpp:49:7:49:7 | declaration of operator= | +| test.cpp:49:7:49:22 | definition of classProtoAndDef | +| test.cpp:51:19:51:19 | definition of T | +| test.cpp:52:7:52:24 | declaration of tmplClassProtoOnly | +| test.cpp:54:19:54:19 | definition of T | +| test.cpp:55:7:55:26 | declaration of tmplClassProtoAndDef | +| test.cpp:56:19:56:19 | definition of T | +| test.cpp:57:7:57:26 | definition of tmplClassProtoAndDef | +| test.cpp:59:19:59:19 | definition of T | +| test.cpp:60:6:60:29 | declaration of tmplInstantiatedFunction | +| test.cpp:61:19:61:19 | definition of T | +| test.cpp:62:6:62:29 | definition of tmplInstantiatedFunction | +| test.cpp:62:33:62:33 | declaration of t | +| test.cpp:62:33:62:33 | definition of t | +| test.cpp:64:19:64:19 | definition of T | +| test.cpp:65:7:65:27 | declaration of tmplInstantiatedClass | +| test.cpp:66:19:66:19 | definition of T | +| 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 | declaration of operator= | +| 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 | +| test.cpp:68:7:68:7 | definition of t | +| test.cpp:71:6:71:6 | definition of f | +| test.cpp:72:32:72:35 | definition of tici | +| test.cpp:73:35:73:38 | definition of ticd | +| test.cpp:74:9:74:9 | definition of i | +| test.cpp:75:12:75:12 | definition of d | diff --git a/cpp/ql/test/library-tests/declarationEntry/more/declarationEntry.ql b/cpp/ql/test/library-tests/declarationEntry/more/declarationEntry.ql new file mode 100644 index 000000000000..99e71a8d4baa --- /dev/null +++ b/cpp/ql/test/library-tests/declarationEntry/more/declarationEntry.ql @@ -0,0 +1,5 @@ +import cpp + +from DeclarationEntry de +select de + diff --git a/cpp/ql/test/library-tests/declarationEntry/more/test.cpp b/cpp/ql/test/library-tests/declarationEntry/more/test.cpp new file mode 100644 index 000000000000..7f1e3e2145fe --- /dev/null +++ b/cpp/ql/test/library-tests/declarationEntry/more/test.cpp @@ -0,0 +1,79 @@ + +int variable; + +void protoOnly(void); + +void defOnly(void) { } + +void protoAndDef(void); +void protoAndDef(void) { } + +template +void tmplProtoOnly(T t); + +template +void tmplDefOnly(T t) {} + +template +void tmplProtoAndDef(T t); +template +void tmplProtoAndDef(T t) {} + +class Cl { + int clVar; + + void clProtoOnly(void); + + void clDefOnly(void) { } + + void clProtoAndDef(void); + + template + void clTmplProtoOnly(T t); + + template + void clTmplDefOnly(T t) {} + + template + void clTmplProtoAndDef(T t); +}; + +void Cl::clProtoAndDef(void) { } + +template +void Cl::clTmplProtoAndDef(T t) {} + +class classProtoOnly; + +class classProtoAndDef; +class classProtoAndDef { }; + +template +class tmplClassProtoOnly; + +template +class tmplClassProtoAndDef; +template +class tmplClassProtoAndDef { }; + +template +void tmplInstantiatedFunction(T t); +template +void tmplInstantiatedFunction(T t) {} + +template +class tmplInstantiatedClass; +template +class tmplInstantiatedClass { + T t; +}; + +void f(void) { + tmplInstantiatedClass tici; + tmplInstantiatedClass ticd; + int i; + double d; + tmplInstantiatedFunction(i); + tmplInstantiatedFunction(d); +} + diff --git a/cpp/ql/test/library-tests/declarationEntry/template/declarationEntry.expected b/cpp/ql/test/library-tests/declarationEntry/template/declarationEntry.expected new file mode 100644 index 000000000000..9273ca6a569f --- /dev/null +++ b/cpp/ql/test/library-tests/declarationEntry/template/declarationEntry.expected @@ -0,0 +1,17 @@ +| src1.cpp:3:16:3:16 | definition of A | +| src1.cpp:4:7:4:20 | declaration of template_class | +| src1.cpp:6:23:6:23 | definition of x | +| src2.cpp:3:16:3:16 | definition of A | +| src2.cpp:4:7:4:20 | declaration of template_class | +| src2.cpp:6:23:6:23 | definition of y | +| src3.cpp:3:16:3:16 | definition of B | +| src3.cpp:4:7:4:20 | declaration of template_class | +| src3.cpp:6:23:6:23 | definition of z | +| src4.cpp:4:7:4:20 | declaration of template_class<> | +| src4.cpp:6:20:6:21 | definition of zz | +| src5.cpp:5:16:5:19 | definition of elem | +| src5.cpp:6:8:6:17 | definition of my_istream | +| src5.cpp:10:8:10:17 | declaration of my_istream | +| src5.fwd.hpp:6:17:6:20 | definition of elem | +| src5.fwd.hpp:6:29:6:38 | declaration of my_istream | +| src6.cpp:5:19:5:23 | definition of mis_c | diff --git a/cpp/ql/test/library-tests/declarationEntry/template/declarationEntry.ql b/cpp/ql/test/library-tests/declarationEntry/template/declarationEntry.ql new file mode 100644 index 000000000000..3dffe31503e1 --- /dev/null +++ b/cpp/ql/test/library-tests/declarationEntry/template/declarationEntry.ql @@ -0,0 +1,5 @@ +import cpp + +from DeclarationEntry de +where de.getFile().toString() != "" +select de diff --git a/cpp/ql/test/library-tests/declarationEntry/template/src1.cpp b/cpp/ql/test/library-tests/declarationEntry/template/src1.cpp new file mode 100644 index 000000000000..2ea7f4f5b704 --- /dev/null +++ b/cpp/ql/test/library-tests/declarationEntry/template/src1.cpp @@ -0,0 +1,6 @@ +// src1.cpp + +template +class template_class; + +template_class *x; diff --git a/cpp/ql/test/library-tests/declarationEntry/template/src2.cpp b/cpp/ql/test/library-tests/declarationEntry/template/src2.cpp new file mode 100644 index 000000000000..2a88be6b2cb5 --- /dev/null +++ b/cpp/ql/test/library-tests/declarationEntry/template/src2.cpp @@ -0,0 +1,23 @@ +// src2.cpp + +template +class template_class; + +template_class *y; + +/* +This used to cause a DBCheck issue along the lines of: + +[INVALID_KEY] Relation class_instantiation((unique @usertype to, @usertype from)): Value 134 of key field to occurs in several tuples. Two such tuples are: (134,129) and (134,150) + Relevant element: Full ID for 134: @"type_decl_template_class[class]<(24)>". The ID may expand to @"type_decl_template_class[class]<{@"predefined_type;char;1,-1"}>" + Relevant element: Full ID for 129: @"type_decl_template_class[class]<(127)>". The ID may expand to @"type_decl_template_class[class]<{@"template_parameter_A:3:16:{@"C:/semmle/code/semmlecode-cpp-tests/library-tests/declarationEntry/template/src1.cpp;sourcefile"}"}>" + Relevant element: Full ID for 150: @"type_decl_template_class[class]<(148)>". The ID may expand to @"type_decl_template_class[class]<{@"template_parameter_A:3:16:{@"C:/semmle/code/semmlecode-cpp-tests/library-tests/declarationEntry/template/src2.cpp;sourcefile"}"}>" + +i.e. that the type 'template_class' is an instantiation of two distinct templates, +template_class of src1.cpp and template_class of src2.cpp (and template_class of +src3.cpp). After some discussion we've decided that this is correct, since: + * template_class (x2) and template_class are not real classes, but prototypes local to their own source files. They do not make it through to the linking process or the final compiled program, thus they remain as three distinct entities. + * template_class on the other hand is a real class, merged from the three sources at link time. + (it is a - not necessarily enforced - requirement of C++ that the three template_class have equivalent definitions; if they don't, it can easily lead to bugs at runtime but that's another story) + * thus, template_class is legitimately derived from multiple templates. +*/ diff --git a/cpp/ql/test/library-tests/declarationEntry/template/src3.cpp b/cpp/ql/test/library-tests/declarationEntry/template/src3.cpp new file mode 100644 index 000000000000..0dc5f5b9b802 --- /dev/null +++ b/cpp/ql/test/library-tests/declarationEntry/template/src3.cpp @@ -0,0 +1,6 @@ +// src3.cpp + +template +class template_class; + +template_class *z; diff --git a/cpp/ql/test/library-tests/declarationEntry/template/src4.cpp b/cpp/ql/test/library-tests/declarationEntry/template/src4.cpp new file mode 100644 index 000000000000..2ed3a08f3e3d --- /dev/null +++ b/cpp/ql/test/library-tests/declarationEntry/template/src4.cpp @@ -0,0 +1,6 @@ +// src4.cpp + +template +class template_class; + +template_class<3> *zz; diff --git a/cpp/ql/test/library-tests/declarationEntry/template/src5.cpp b/cpp/ql/test/library-tests/declarationEntry/template/src5.cpp new file mode 100644 index 000000000000..8fc3e31ad72d --- /dev/null +++ b/cpp/ql/test/library-tests/declarationEntry/template/src5.cpp @@ -0,0 +1,10 @@ +// src5.cpp + +#include "src5.fwd.hpp" + +template + class my_istream { + }; + +template <> + class my_istream; diff --git a/cpp/ql/test/library-tests/declarationEntry/template/src5.fwd.hpp b/cpp/ql/test/library-tests/declarationEntry/template/src5.fwd.hpp new file mode 100644 index 000000000000..770189f7266c --- /dev/null +++ b/cpp/ql/test/library-tests/declarationEntry/template/src5.fwd.hpp @@ -0,0 +1,8 @@ +// src5.fwd.hpp + +#ifndef SRC5_FWD_HPP +#define SRC5_FWD_HPP + + template class my_istream; + +#endif // SRC5_FWD_HPP diff --git a/cpp/ql/test/library-tests/declarationEntry/template/src6.cpp b/cpp/ql/test/library-tests/declarationEntry/template/src6.cpp new file mode 100644 index 000000000000..729a25f50494 --- /dev/null +++ b/cpp/ql/test/library-tests/declarationEntry/template/src6.cpp @@ -0,0 +1,5 @@ +// src6.cpp + +#include "src5.fwd.hpp" + +my_istream *mis_c; diff --git a/cpp/ql/test/library-tests/declarationEntry/template/usertype.expected b/cpp/ql/test/library-tests/declarationEntry/template/usertype.expected new file mode 100644 index 000000000000..30490ce36575 --- /dev/null +++ b/cpp/ql/test/library-tests/declarationEntry/template/usertype.expected @@ -0,0 +1,15 @@ +| src1.cpp:3:16:3:16 | A | 1 | +| src1.cpp:4:7:4:20 | template_class | 1 | +| src1.cpp:4:7:4:20 | template_class | 3 | +| src2.cpp:3:16:3:16 | A | 1 | +| src2.cpp:4:7:4:20 | template_class | 1 | +| src2.cpp:4:7:4:20 | template_class | 3 | +| src3.cpp:3:16:3:16 | B | 1 | +| src3.cpp:4:7:4:20 | template_class | 1 | +| src3.cpp:4:7:4:20 | template_class | 3 | +| src4.cpp:4:7:4:20 | template_class<3> | 1 | +| src4.cpp:4:7:4:20 | template_class<> | 1 | +| src5.cpp:5:16:5:19 | elem | 1 | +| src5.cpp:6:8:6:17 | my_istream | 1 | +| src5.cpp:10:8:10:17 | my_istream | 1 | +| src5.fwd.hpp:6:17:6:20 | elem | 1 | diff --git a/cpp/ql/test/library-tests/declarationEntry/template/usertype.ql b/cpp/ql/test/library-tests/declarationEntry/template/usertype.ql new file mode 100644 index 000000000000..f5200a30a4b8 --- /dev/null +++ b/cpp/ql/test/library-tests/declarationEntry/template/usertype.ql @@ -0,0 +1,5 @@ +import cpp + +from UserType t +where t.getFile().toString() != "" +select t, count(t.getLocation()) diff --git a/cpp/ql/test/library-tests/declstmt/cpp.cpp b/cpp/ql/test/library-tests/declstmt/cpp.cpp new file mode 100644 index 000000000000..eee9d6271ec7 --- /dev/null +++ b/cpp/ql/test/library-tests/declstmt/cpp.cpp @@ -0,0 +1,7 @@ + +void cpp_fun() { + typeof(({ int twisty(); twisty(); })) twisty(); + + decltype( ({using namespace std; using t = int; 1; }) ) i; +} + diff --git a/cpp/ql/test/library-tests/declstmt/declstmt.c b/cpp/ql/test/library-tests/declstmt/declstmt.c new file mode 100644 index 000000000000..f7367ae10f86 --- /dev/null +++ b/cpp/ql/test/library-tests/declstmt/declstmt.c @@ -0,0 +1,30 @@ + +int topLevel1, topLevel2; + +int x = 1; + +void fun(void) { + int fun1, fun2; + + int y = 2; + extern int x; + void some_fun(); + typedef int my_int; + int z; + + int i1a[sizeof(struct S1 { int i; })] = { ({ int x = 3; int y = 4; 5; }) }, i1b; + extern int i2a[sizeof(struct S2 { int i; })], i2b; + + int nested_x = ({ extern int nested_y; 1; }), nested_y; + + extern int repeated_var; + extern int repeated_var; + void repeated_fun(); + void repeated_fun(); +} + +void another_fun(void) { + extern int repeated_var; + void repeated_fun(); +} + diff --git a/cpp/ql/test/library-tests/declstmt/getDeclaration.expected b/cpp/ql/test/library-tests/declstmt/getDeclaration.expected new file mode 100644 index 000000000000..995816ef6775 --- /dev/null +++ b/cpp/ql/test/library-tests/declstmt/getDeclaration.expected @@ -0,0 +1,40 @@ +| cpp.cpp:3:5:3:51 | declaration | 0 | cpp.cpp:3:19:3:24 | twisty | +| cpp.cpp:3:5:3:51 | declaration | 0 | cpp.cpp:3:43:3:48 | twisty | +| cpp.cpp:5:5:5:62 | declaration | 0 | cpp.cpp:5:61:5:61 | i | +| cpp.cpp:5:38:5:51 | declaration | 0 | cpp.cpp:5:44:5:44 | t | +| declstmt.c:7:5:7:19 | declaration | 0 | declstmt.c:7:9:7:12 | fun1 | +| declstmt.c:7:5:7:19 | declaration | 1 | declstmt.c:7:15:7:18 | fun2 | +| declstmt.c:9:5:9:14 | declaration | 0 | declstmt.c:9:9:9:9 | y | +| declstmt.c:10:5:10:17 | declaration | 0 | declstmt.c:4:5:4:5 | x | +| declstmt.c:11:5:11:20 | declaration | 0 | declstmt.c:11:10:11:17 | some_fun | +| declstmt.c:12:5:12:23 | declaration | 0 | declstmt.c:12:17:12:22 | my_int | +| declstmt.c:13:5:13:10 | declaration | 0 | declstmt.c:13:9:13:9 | z | +| declstmt.c:15:5:15:84 | declaration | 0 | declstmt.c:15:9:15:11 | i1a | +| declstmt.c:15:5:15:84 | declaration | 1 | declstmt.c:15:27:15:28 | S1 | +| declstmt.c:15:5:15:84 | declaration | 2 | declstmt.c:15:81:15:83 | i1b | +| declstmt.c:15:50:15:59 | declaration | 0 | declstmt.c:15:54:15:54 | x | +| declstmt.c:15:61:15:70 | declaration | 0 | declstmt.c:15:65:15:65 | y | +| declstmt.c:16:5:16:54 | declaration | 0 | declstmt.c:16:16:16:18 | i2a | +| declstmt.c:16:5:16:54 | declaration | 1 | declstmt.c:16:34:16:35 | S2 | +| declstmt.c:16:5:16:54 | declaration | 2 | declstmt.c:16:51:16:53 | i2b | +| declstmt.c:18:5:18:59 | declaration | 0 | declstmt.c:18:9:18:16 | nested_x | +| declstmt.c:18:5:18:59 | declaration | 1 | declstmt.c:18:51:18:58 | nested_y | +| declstmt.c:18:23:18:42 | declaration | 0 | declstmt.c:18:34:18:41 | nested_y | +| declstmt.c:20:5:20:28 | declaration | 0 | declstmt.c:20:16:20:27 | repeated_var | +| declstmt.c:20:5:20:28 | declaration | 0 | declstmt.c:21:16:21:27 | repeated_var | +| declstmt.c:20:5:20:28 | declaration | 0 | declstmt.c:27:16:27:27 | repeated_var | +| declstmt.c:21:5:21:28 | declaration | 0 | declstmt.c:20:16:20:27 | repeated_var | +| declstmt.c:21:5:21:28 | declaration | 0 | declstmt.c:21:16:21:27 | repeated_var | +| declstmt.c:21:5:21:28 | declaration | 0 | declstmt.c:27:16:27:27 | repeated_var | +| declstmt.c:22:5:22:24 | declaration | 0 | declstmt.c:22:10:22:21 | repeated_fun | +| declstmt.c:22:5:22:24 | declaration | 0 | declstmt.c:23:10:23:21 | repeated_fun | +| declstmt.c:22:5:22:24 | declaration | 0 | declstmt.c:28:10:28:21 | repeated_fun | +| declstmt.c:23:5:23:24 | declaration | 0 | declstmt.c:22:10:22:21 | repeated_fun | +| declstmt.c:23:5:23:24 | declaration | 0 | declstmt.c:23:10:23:21 | repeated_fun | +| declstmt.c:23:5:23:24 | declaration | 0 | declstmt.c:28:10:28:21 | repeated_fun | +| declstmt.c:27:5:27:28 | declaration | 0 | declstmt.c:20:16:20:27 | repeated_var | +| declstmt.c:27:5:27:28 | declaration | 0 | declstmt.c:21:16:21:27 | repeated_var | +| declstmt.c:27:5:27:28 | declaration | 0 | declstmt.c:27:16:27:27 | repeated_var | +| declstmt.c:28:5:28:24 | declaration | 0 | declstmt.c:22:10:22:21 | repeated_fun | +| declstmt.c:28:5:28:24 | declaration | 0 | declstmt.c:23:10:23:21 | repeated_fun | +| declstmt.c:28:5:28:24 | declaration | 0 | declstmt.c:28:10:28:21 | repeated_fun | diff --git a/cpp/ql/test/library-tests/declstmt/getDeclaration.ql b/cpp/ql/test/library-tests/declstmt/getDeclaration.ql new file mode 100644 index 000000000000..87711f2503d1 --- /dev/null +++ b/cpp/ql/test/library-tests/declstmt/getDeclaration.ql @@ -0,0 +1,5 @@ +import cpp + +from DeclStmt ds, int i +select ds, i, ds.getDeclaration(i) + diff --git a/cpp/ql/test/library-tests/declstmt/getDeclarationEntry.expected b/cpp/ql/test/library-tests/declstmt/getDeclarationEntry.expected new file mode 100644 index 000000000000..198e8ccf9fe4 --- /dev/null +++ b/cpp/ql/test/library-tests/declstmt/getDeclarationEntry.expected @@ -0,0 +1,27 @@ +| cpp.cpp:3:5:3:51 | declaration | 0 | cpp.cpp:3:43:3:48 | declaration of twisty | +| cpp.cpp:5:5:5:62 | declaration | 0 | cpp.cpp:5:61:5:61 | definition of i | +| cpp.cpp:5:38:5:51 | declaration | 0 | cpp.cpp:5:44:5:44 | declaration of t | +| declstmt.c:7:5:7:19 | declaration | 0 | declstmt.c:7:9:7:12 | definition of fun1 | +| declstmt.c:7:5:7:19 | declaration | 1 | declstmt.c:7:15:7:18 | definition of fun2 | +| declstmt.c:9:5:9:14 | declaration | 0 | declstmt.c:9:9:9:9 | definition of y | +| declstmt.c:10:5:10:17 | declaration | 0 | declstmt.c:10:16:10:16 | declaration of x | +| declstmt.c:11:5:11:20 | declaration | 0 | declstmt.c:11:10:11:17 | declaration of some_fun | +| declstmt.c:12:5:12:23 | declaration | 0 | declstmt.c:12:17:12:22 | declaration of my_int | +| declstmt.c:13:5:13:10 | declaration | 0 | declstmt.c:13:9:13:9 | definition of z | +| declstmt.c:15:5:15:84 | declaration | 0 | declstmt.c:15:9:15:11 | definition of i1a | +| declstmt.c:15:5:15:84 | declaration | 1 | declstmt.c:15:27:15:28 | definition of S1 | +| declstmt.c:15:5:15:84 | declaration | 2 | declstmt.c:15:81:15:83 | definition of i1b | +| declstmt.c:15:50:15:59 | declaration | 0 | declstmt.c:15:54:15:54 | definition of x | +| declstmt.c:15:61:15:70 | declaration | 0 | declstmt.c:15:65:15:65 | definition of y | +| declstmt.c:16:5:16:54 | declaration | 0 | declstmt.c:16:16:16:18 | declaration of i2a | +| declstmt.c:16:5:16:54 | declaration | 1 | declstmt.c:16:34:16:35 | definition of S2 | +| declstmt.c:16:5:16:54 | declaration | 2 | declstmt.c:16:51:16:53 | declaration of i2b | +| declstmt.c:18:5:18:59 | declaration | 0 | declstmt.c:18:9:18:16 | definition of nested_x | +| declstmt.c:18:5:18:59 | declaration | 1 | declstmt.c:18:51:18:58 | definition of nested_y | +| declstmt.c:18:23:18:42 | declaration | 0 | declstmt.c:18:34:18:41 | declaration of nested_y | +| declstmt.c:20:5:20:28 | declaration | 0 | declstmt.c:20:16:20:27 | declaration of repeated_var | +| declstmt.c:21:5:21:28 | declaration | 0 | declstmt.c:21:16:21:27 | declaration of repeated_var | +| declstmt.c:22:5:22:24 | declaration | 0 | declstmt.c:22:10:22:21 | declaration of repeated_fun | +| declstmt.c:23:5:23:24 | declaration | 0 | declstmt.c:23:10:23:21 | declaration of repeated_fun | +| declstmt.c:27:5:27:28 | declaration | 0 | declstmt.c:27:16:27:27 | declaration of repeated_var | +| declstmt.c:28:5:28:24 | declaration | 0 | declstmt.c:28:10:28:21 | declaration of repeated_fun | diff --git a/cpp/ql/test/library-tests/declstmt/getDeclarationEntry.ql b/cpp/ql/test/library-tests/declstmt/getDeclarationEntry.ql new file mode 100644 index 000000000000..42416c0f3aba --- /dev/null +++ b/cpp/ql/test/library-tests/declstmt/getDeclarationEntry.ql @@ -0,0 +1,5 @@ +import cpp + +from DeclStmt ds, int i +select ds, i, ds.getDeclarationEntry(i) + diff --git a/cpp/ql/test/library-tests/default_parameters/exprs.expected b/cpp/ql/test/library-tests/default_parameters/exprs.expected new file mode 100644 index 000000000000..a1830aff1fe5 --- /dev/null +++ b/cpp/ql/test/library-tests/default_parameters/exprs.expected @@ -0,0 +1,7 @@ +| test.cpp:2:25:2:26 | 10 | +| test.cpp:3:5:3:5 | x | +| test.cpp:3:5:3:9 | ... = ... | +| test.cpp:3:9:3:9 | 3 | +| test.cpp:7:5:7:14 | call to myFunction | +| test.cpp:7:16:7:16 | 9 | +| test.cpp:8:5:8:14 | call to myFunction | diff --git a/cpp/ql/test/library-tests/default_parameters/exprs.ql b/cpp/ql/test/library-tests/default_parameters/exprs.ql new file mode 100644 index 000000000000..2f525817cb22 --- /dev/null +++ b/cpp/ql/test/library-tests/default_parameters/exprs.ql @@ -0,0 +1,4 @@ +import cpp + +from Expr e +select e diff --git a/cpp/ql/test/library-tests/default_parameters/test.cpp b/cpp/ql/test/library-tests/default_parameters/test.cpp new file mode 100644 index 000000000000..f7fe7577e793 --- /dev/null +++ b/cpp/ql/test/library-tests/default_parameters/test.cpp @@ -0,0 +1,10 @@ + +void myFunction(int x = 10) { + x = 3; +} + +void f(void) { + myFunction(9); + myFunction(); +} + diff --git a/cpp/ql/test/library-tests/default_parameters/variables.expected b/cpp/ql/test/library-tests/default_parameters/variables.expected new file mode 100644 index 000000000000..3dcb5d017f99 --- /dev/null +++ b/cpp/ql/test/library-tests/default_parameters/variables.expected @@ -0,0 +1,6 @@ +| file://:0:0:0:0 | fp_offset | 1 | | 1 | | +| file://:0:0:0:0 | gp_offset | 1 | | 1 | | +| file://:0:0:0:0 | overflow_arg_area | 1 | | 1 | | +| file://:0:0:0:0 | reg_save_area | 1 | | 1 | | +| test.cpp:2:21:2:21 | x | 1 | file://:0:0:0:0 initializer for x | 2 | test.cpp:2:25:2:26 10 | +| test.cpp:2:21:2:21 | x | 1 | file://:0:0:0:0 initializer for x | 2 | test.cpp:3:9:3:9 3 | diff --git a/cpp/ql/test/library-tests/default_parameters/variables.ql b/cpp/ql/test/library-tests/default_parameters/variables.ql new file mode 100644 index 000000000000..42b144e69172 --- /dev/null +++ b/cpp/ql/test/library-tests/default_parameters/variables.ql @@ -0,0 +1,43 @@ +import cpp + +string istr(Initializer i) { + if exists(i.toString()) + then result = i.toString() + else result = "" +} + +string iloc(Initializer i) { + if exists(i.getLocation().toString()) + then result = i.getLocation().toString() + else result = "" +} + +string init(Variable v) { + if v.hasInitializer() + then exists(Initializer i | i = v.getInitializer() and + result = iloc(i) + " " + istr(i)) + else result = "" +} + +string estr(Expr e) { + if exists(e.toString()) + then result = e.toString() + else result = "" +} + +string eloc(Expr e) { + if exists(e.getLocation().toString()) + then result = e.getLocation().toString() + else result = "" +} + +string assigned(Variable v) { + if exists(v.getAnAssignedValue()) + then exists(Expr e | e = v.getAnAssignedValue() and + result = eloc(e) + " " + estr(e)) + else result = "" +} + +from Variable v +select v, count(init(v)), init(v), count(assigned(v)), assigned(v) + diff --git a/cpp/ql/test/library-tests/defuse/addressOf.cpp b/cpp/ql/test/library-tests/defuse/addressOf.cpp new file mode 100644 index 000000000000..eaf3065475d5 --- /dev/null +++ b/cpp/ql/test/library-tests/defuse/addressOf.cpp @@ -0,0 +1,65 @@ +namespace std { + template T&& move(T& t) noexcept; // simplified signature +} + +struct Base { int x; }; +struct Derived : Base { int y; }; + +int accept_base(Base &base); + +struct ContainsDerived { + Derived d_; + + int f() { + return accept_base(d_); + } +}; + +void accept_int_rvref(int &&rvref); +void accept_intptr_const_lvref(int *const &rvref); +void accept_intptr_rvref(int *&&rvref); + +void call_with_int_rvref() { + int i; + accept_int_rvref(std::move(i)); + accept_intptr_rvref(&i); + accept_intptr_const_lvref(&i); +} + +void accept_address(int *ptr); + +void pass_address(int i) { + accept_address(&i); + + accept_address(i ? &i : nullptr); + accept_address(&*&*&i); + accept_address(&(++i)); + accept_address(&(i |= 1)); + accept_address(&(i += 1) + 1); + + int &iref = i; + // This takes the address of `i`, not `iref`. + accept_address(&iref); +} + + +int lambdas(int captured) { + auto f1 = [&] { captured++; }; // capture has location "file://:0:0:0:0" + f1(); + auto f2 = [&captured] { captured++; }; + f1(); + return captured; +} + + +void arrays(int i) { + int a[8] = { i, i+1, i+2 }; + accept_address(&a[0] + sizeof(a)/sizeof(*a)); + accept_address(a); +} + +void nonexamples(int *ptr, int &ref) { + if (--(*ptr) == 0) { + nonexamples(&*ptr, ref); + } +} diff --git a/cpp/ql/test/library-tests/defuse/definition.expected b/cpp/ql/test/library-tests/defuse/definition.expected new file mode 100644 index 000000000000..a744109dc2a2 --- /dev/null +++ b/cpp/ql/test/library-tests/defuse/definition.expected @@ -0,0 +1,129 @@ +| addressOf.cpp:23:9:23:9 | i | addressOf.cpp:24:22:24:30 | call to move | +| addressOf.cpp:23:9:23:9 | i | addressOf.cpp:25:25:25:26 | & ... | +| addressOf.cpp:31:23:31:23 | i | addressOf.cpp:32:18:32:19 | & ... | +| addressOf.cpp:31:23:31:23 | i | addressOf.cpp:34:18:34:33 | ... ? ... : ... | +| addressOf.cpp:31:23:31:23 | i | addressOf.cpp:35:18:35:23 | & ... | +| addressOf.cpp:31:23:31:23 | i | addressOf.cpp:36:18:36:23 | & ... | +| addressOf.cpp:31:23:31:23 | i | addressOf.cpp:36:20:36:22 | ++ ... | +| addressOf.cpp:31:23:31:23 | i | addressOf.cpp:37:18:37:26 | & ... | +| addressOf.cpp:31:23:31:23 | i | addressOf.cpp:37:20:37:25 | ... \|= ... | +| addressOf.cpp:31:23:31:23 | i | addressOf.cpp:38:18:38:30 | ... + ... | +| addressOf.cpp:31:23:31:23 | i | addressOf.cpp:38:20:38:25 | ... += ... | +| addressOf.cpp:40:8:40:11 | iref | addressOf.cpp:40:15:40:15 | i | +| addressOf.cpp:40:8:40:11 | iref | addressOf.cpp:42:18:42:22 | & ... | +| addressOf.cpp:47:8:47:9 | f1 | addressOf.cpp:47:13:47:31 | [...](...){...} | +| addressOf.cpp:49:8:49:9 | f2 | addressOf.cpp:49:13:49:39 | [...](...){...} | +| addressOf.cpp:56:7:56:7 | a | addressOf.cpp:56:13:56:28 | {...} | +| addressOf.cpp:56:7:56:7 | a | addressOf.cpp:57:18:57:45 | ... + ... | +| addressOf.cpp:56:7:56:7 | a | addressOf.cpp:58:18:58:18 | a | +| addressOf.cpp:61:33:61:35 | ref | addressOf.cpp:63:24:63:26 | ref | +| indirect_use.cpp:20:10:20:10 | p | indirect_use.cpp:20:14:20:15 | ip | +| indirect_use.cpp:25:10:25:10 | p | indirect_use.cpp:25:14:25:19 | ... + ... | +| indirect_use.cpp:25:10:25:10 | p | indirect_use.cpp:26:5:26:7 | ... -- | +| indirect_use.cpp:35:10:35:10 | p | indirect_use.cpp:35:14:35:15 | ip | +| indirect_use.cpp:36:11:36:12 | pp | indirect_use.cpp:36:16:36:17 | & ... | +| indirect_use.cpp:36:11:36:12 | pp | indirect_use.cpp:37:13:37:15 | & ... | +| indirect_use.cpp:52:14:52:14 | i | indirect_use.cpp:52:17:52:18 | 0 | +| indirect_use.cpp:52:14:52:14 | i | indirect_use.cpp:52:35:52:37 | ... ++ | +| indirect_use.cpp:53:14:53:14 | p | indirect_use.cpp:53:18:53:43 | __builtin_va_arg | +| indirect_use.cpp:60:10:60:10 | p | indirect_use.cpp:60:14:60:19 | ... + ... | +| indirect_use.cpp:60:10:60:10 | p | indirect_use.cpp:61:5:61:7 | ... -- | +| indirect_use.cpp:65:27:65:27 | i | indirect_use.cpp:67:9:67:13 | ... = ... | +| indirect_use.cpp:72:27:72:27 | i | indirect_use.cpp:73:14:73:14 | i | +| indirect_use.cpp:111:28:111:28 | i | indirect_use.cpp:112:5:112:9 | ... = ... | +| indirect_use.cpp:116:32:116:33 | ip | indirect_use.cpp:118:5:118:8 | ... ++ | +| indirect_use.cpp:116:32:116:33 | ip | indirect_use.cpp:119:5:119:8 | ... -- | +| indirect_use.cpp:150:29:150:30 | ns | indirect_use.cpp:151:5:151:26 | ... = ... | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:156:18:156:19 | & ... | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:157:18:157:19 | & ... | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:158:18:158:19 | & ... | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:159:18:159:19 | & ... | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:160:18:160:19 | & ... | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:161:27:161:28 | & ... | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:162:27:162:28 | & ... | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:163:14:163:14 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:164:14:164:14 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:165:14:165:14 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:166:18:166:19 | & ... | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:167:28:167:29 | & ... | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:169:20:169:21 | & ... | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:170:19:170:20 | & ... | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:171:19:171:20 | & ... | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:172:19:172:20 | & ... | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:173:19:173:20 | & ... | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:174:15:174:15 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:176:21:176:22 | & ... | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:177:23:177:24 | & ... | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:178:30:178:31 | & ... | +| indirect_use.cpp:180:18:180:19 | ns | indirect_use.cpp:181:13:181:15 | & ... | +| indirect_use.cpp:180:18:180:19 | ns | indirect_use.cpp:182:13:182:15 | & ... | +| indirect_use.cpp:180:18:180:19 | ns | indirect_use.cpp:183:14:183:16 | & ... | +| indirect_use.cpp:180:18:180:19 | ns | indirect_use.cpp:184:14:184:15 | ns | +| pass_by_ref.cpp:10:7:10:24 | uninitializedArray | pass_by_ref.cpp:12:18:12:35 | uninitializedArray | +| pass_by_ref.cpp:10:7:10:24 | uninitializedArray | pass_by_ref.cpp:13:20:13:37 | uninitializedArray | +| pass_by_ref.cpp:18:7:18:8 | i1 | pass_by_ref.cpp:18:12:18:12 | n | +| pass_by_ref.cpp:18:7:18:8 | i1 | pass_by_ref.cpp:19:22:19:23 | i1 | +| pass_by_ref.cpp:21:7:21:8 | i2 | pass_by_ref.cpp:21:12:21:12 | n | +| pass_by_ref.cpp:21:7:21:8 | i2 | pass_by_ref.cpp:22:20:22:22 | & ... | +| pass_by_ref.cpp:30:15:30:15 | n | pass_by_ref.cpp:32:10:32:12 | -- ... | +| pass_by_ref.cpp:31:7:31:9 | arr | pass_by_ref.cpp:31:15:31:18 | {...} | +| pass_by_ref.cpp:31:7:31:9 | arr | pass_by_ref.cpp:33:20:33:22 | arr | +| pass_by_ref.cpp:36:16:36:16 | n | pass_by_ref.cpp:39:10:39:12 | -- ... | +| pass_by_ref.cpp:37:7:37:9 | arr | pass_by_ref.cpp:37:15:37:18 | {...} | +| pass_by_ref.cpp:37:7:37:9 | arr | pass_by_ref.cpp:40:22:40:24 | arr | +| pass_by_ref.cpp:37:7:37:9 | arr | pass_by_ref.cpp:41:20:41:22 | arr | +| pass_by_ref.cpp:46:7:46:7 | i | pass_by_ref.cpp:46:11:46:11 | n | +| pass_by_ref.cpp:46:7:46:7 | i | pass_by_ref.cpp:49:5:49:7 | ... ++ | +| pass_by_ref.cpp:46:7:46:7 | i | pass_by_ref.cpp:53:22:53:22 | i | +| test.cpp:4:7:4:7 | a | test.cpp:5:3:5:8 | ... = ... | +| test.cpp:4:7:4:7 | a | test.cpp:13:3:13:7 | ... = ... | +| test.cpp:4:7:4:7 | a | test.cpp:19:5:19:9 | ... = ... | +| test.cpp:4:10:4:10 | b | test.cpp:6:3:6:8 | ... = ... | +| test.cpp:4:10:4:10 | b | test.cpp:21:5:21:9 | ... = ... | +| test.cpp:4:13:4:13 | c | test.cpp:7:3:7:8 | ... = ... | +| test.cpp:28:7:28:7 | d | test.cpp:28:11:28:11 | a | +| test.cpp:28:7:28:7 | d | test.cpp:31:11:31:13 | ... ++ | +| test.cpp:28:7:28:7 | d | test.cpp:32:7:32:9 | ... ++ | +| test.cpp:31:7:31:7 | e | test.cpp:31:11:31:13 | ... ++ | +| test.cpp:31:7:31:7 | e | test.cpp:32:3:32:9 | ... = ... | +| test.cpp:39:20:39:20 | x | test.cpp:40:3:40:8 | ... = ... | +| test.cpp:44:8:44:8 | y | test.cpp:44:12:44:12 | x | +| test.cpp:52:7:52:7 | x | test.cpp:52:10:52:11 | 0 | +| test.cpp:52:7:52:7 | x | test.cpp:53:12:53:12 | x | +| test.cpp:58:7:58:7 | x | test.cpp:58:10:58:11 | 0 | +| test.cpp:58:7:58:7 | x | test.cpp:59:12:59:12 | x | +| test.cpp:64:7:64:7 | x | test.cpp:64:10:64:11 | 0 | +| test.cpp:64:7:64:7 | x | test.cpp:65:12:65:13 | & ... | +| test.cpp:70:7:70:7 | x | test.cpp:70:10:70:11 | 0 | +| test.cpp:70:7:70:7 | x | test.cpp:71:12:71:13 | & ... | +| test.cpp:80:7:80:7 | x | test.cpp:80:10:80:11 | 0 | +| test.cpp:86:7:86:7 | x | test.cpp:86:10:86:11 | 0 | +| test.cpp:92:7:92:7 | x | test.cpp:92:10:92:11 | 0 | +| test.cpp:92:7:92:7 | x | test.cpp:95:5:95:9 | ... = ... | +| test.cpp:93:12:93:12 | i | test.cpp:93:15:93:16 | 0 | +| test.cpp:93:12:93:12 | i | test.cpp:93:26:93:28 | ... ++ | +| test.cpp:101:7:101:7 | x | test.cpp:101:10:101:11 | 0 | +| test.cpp:101:7:101:7 | x | test.cpp:105:5:105:9 | ... = ... | +| test.cpp:102:8:102:11 | done | test.cpp:102:14:102:19 | 0 | +| test.cpp:102:8:102:11 | done | test.cpp:106:5:106:15 | ... = ... | +| test.cpp:112:7:112:7 | x | test.cpp:112:10:112:11 | 0 | +| test.cpp:112:7:112:7 | x | test.cpp:115:5:115:9 | ... = ... | +| test.cpp:112:7:112:7 | x | test.cpp:121:5:121:9 | ... = ... | +| test.cpp:113:12:113:12 | i | test.cpp:113:15:113:16 | 0 | +| test.cpp:113:12:113:12 | i | test.cpp:113:26:113:28 | ... ++ | +| test.cpp:118:8:118:11 | done | test.cpp:118:14:118:19 | 0 | +| test.cpp:118:8:118:11 | done | test.cpp:122:5:122:15 | ... = ... | +| test.cpp:128:7:128:7 | x | test.cpp:128:10:128:11 | 0 | +| test.cpp:128:7:128:7 | x | test.cpp:131:5:131:9 | ... = ... | +| test.cpp:128:7:128:7 | x | test.cpp:135:7:135:11 | ... = ... | +| test.cpp:129:12:129:12 | i | test.cpp:129:15:129:16 | 0 | +| test.cpp:129:12:129:12 | i | test.cpp:129:26:129:28 | ... ++ | +| test.cpp:132:10:132:13 | done | test.cpp:132:16:132:21 | 0 | +| test.cpp:132:10:132:13 | done | test.cpp:136:7:136:17 | ... = ... | +| test.cpp:144:7:144:7 | x | test.cpp:144:10:144:11 | 0 | +| test.cpp:145:8:145:8 | y | test.cpp:145:12:145:13 | & ... | +| test.cpp:151:7:151:7 | x | test.cpp:151:10:151:11 | 0 | +| test.cpp:152:8:152:8 | y | test.cpp:152:12:152:12 | x | +| test.cpp:152:8:152:8 | y | test.cpp:154:3:154:7 | ... = ... | +| test.cpp:158:17:158:17 | x | test.cpp:160:3:160:8 | ... = ... | +| test.cpp:190:5:190:5 | s | test.cpp:190:8:190:24 | {...} | diff --git a/cpp/ql/test/library-tests/defuse/definition.ql b/cpp/ql/test/library-tests/defuse/definition.ql new file mode 100644 index 000000000000..adcf9e68ffad --- /dev/null +++ b/cpp/ql/test/library-tests/defuse/definition.ql @@ -0,0 +1,5 @@ +import cpp + +from LocalScopeVariable v, ControlFlowNode d +where definition(v, d) +select v, d diff --git a/cpp/ql/test/library-tests/defuse/definitionUsePair.expected b/cpp/ql/test/library-tests/defuse/definitionUsePair.expected new file mode 100644 index 000000000000..2abfc279e585 --- /dev/null +++ b/cpp/ql/test/library-tests/defuse/definitionUsePair.expected @@ -0,0 +1,150 @@ +| addressOf.cpp:23:9:23:9 | i | addressOf.cpp:24:22:24:30 | call to move | addressOf.cpp:25:26:25:26 | i | +| addressOf.cpp:23:9:23:9 | i | addressOf.cpp:25:25:25:26 | & ... | addressOf.cpp:26:32:26:32 | i | +| addressOf.cpp:31:23:31:23 | i | addressOf.cpp:34:18:34:33 | ... ? ... : ... | addressOf.cpp:34:18:34:18 | i | +| addressOf.cpp:31:23:31:23 | i | addressOf.cpp:34:18:34:33 | ... ? ... : ... | addressOf.cpp:34:23:34:23 | i | +| addressOf.cpp:31:23:31:23 | i | addressOf.cpp:34:18:34:33 | ... ? ... : ... | addressOf.cpp:35:23:35:23 | i | +| addressOf.cpp:31:23:31:23 | i | addressOf.cpp:35:18:35:23 | & ... | addressOf.cpp:36:22:36:22 | i | +| addressOf.cpp:31:23:31:23 | i | addressOf.cpp:36:18:36:23 | & ... | addressOf.cpp:37:20:37:20 | i | +| addressOf.cpp:31:23:31:23 | i | addressOf.cpp:37:18:37:26 | & ... | addressOf.cpp:38:20:38:20 | i | +| addressOf.cpp:31:23:31:23 | i | addressOf.cpp:38:18:38:30 | ... + ... | addressOf.cpp:40:15:40:15 | i | +| addressOf.cpp:40:8:40:11 | iref | addressOf.cpp:40:15:40:15 | i | addressOf.cpp:42:19:42:22 | iref | +| addressOf.cpp:47:8:47:9 | f1 | addressOf.cpp:47:13:47:31 | [...](...){...} | addressOf.cpp:48:3:48:4 | f1 | +| addressOf.cpp:47:8:47:9 | f1 | addressOf.cpp:47:13:47:31 | [...](...){...} | addressOf.cpp:50:3:50:4 | f1 | +| addressOf.cpp:56:7:56:7 | a | addressOf.cpp:56:13:56:28 | {...} | addressOf.cpp:57:19:57:19 | a | +| addressOf.cpp:56:7:56:7 | a | addressOf.cpp:57:18:57:45 | ... + ... | addressOf.cpp:58:18:58:18 | a | +| indirect_use.cpp:20:10:20:10 | p | indirect_use.cpp:20:14:20:15 | ip | indirect_use.cpp:21:17:21:17 | p | +| indirect_use.cpp:25:10:25:10 | p | indirect_use.cpp:25:14:25:19 | ... + ... | indirect_use.cpp:26:5:26:5 | p | +| indirect_use.cpp:25:10:25:10 | p | indirect_use.cpp:26:5:26:7 | ... -- | indirect_use.cpp:27:17:27:17 | p | +| indirect_use.cpp:35:10:35:10 | p | indirect_use.cpp:35:14:35:15 | ip | indirect_use.cpp:36:17:36:17 | p | +| indirect_use.cpp:36:11:36:12 | pp | indirect_use.cpp:36:16:36:17 | & ... | indirect_use.cpp:37:14:37:15 | pp | +| indirect_use.cpp:52:14:52:14 | i | indirect_use.cpp:52:17:52:18 | 0 | indirect_use.cpp:52:21:52:21 | i | +| indirect_use.cpp:52:14:52:14 | i | indirect_use.cpp:52:17:52:18 | 0 | indirect_use.cpp:52:35:52:35 | i | +| indirect_use.cpp:52:14:52:14 | i | indirect_use.cpp:52:35:52:37 | ... ++ | indirect_use.cpp:52:21:52:21 | i | +| indirect_use.cpp:52:14:52:14 | i | indirect_use.cpp:52:35:52:37 | ... ++ | indirect_use.cpp:52:35:52:35 | i | +| indirect_use.cpp:53:14:53:14 | p | indirect_use.cpp:53:18:53:43 | __builtin_va_arg | indirect_use.cpp:54:23:54:23 | p | +| indirect_use.cpp:60:10:60:10 | p | indirect_use.cpp:60:14:60:19 | ... + ... | indirect_use.cpp:61:5:61:5 | p | +| indirect_use.cpp:60:10:60:10 | p | indirect_use.cpp:61:5:61:7 | ... -- | indirect_use.cpp:62:17:62:17 | p | +| indirect_use.cpp:65:27:65:27 | i | indirect_use.cpp:67:9:67:13 | ... = ... | indirect_use.cpp:69:16:69:16 | i | +| indirect_use.cpp:111:28:111:28 | i | indirect_use.cpp:112:5:112:9 | ... = ... | indirect_use.cpp:113:16:113:16 | i | +| indirect_use.cpp:116:32:116:33 | ip | indirect_use.cpp:118:5:118:8 | ... ++ | indirect_use.cpp:119:5:119:6 | ip | +| indirect_use.cpp:116:32:116:33 | ip | indirect_use.cpp:119:5:119:8 | ... -- | indirect_use.cpp:120:17:120:18 | ip | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:156:18:156:19 | & ... | indirect_use.cpp:157:19:157:19 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:157:18:157:19 | & ... | indirect_use.cpp:158:19:158:19 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:158:18:158:19 | & ... | indirect_use.cpp:159:19:159:19 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:159:18:159:19 | & ... | indirect_use.cpp:160:19:160:19 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:160:18:160:19 | & ... | indirect_use.cpp:161:28:161:28 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:161:27:161:28 | & ... | indirect_use.cpp:162:28:162:28 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:162:27:162:28 | & ... | indirect_use.cpp:163:14:163:14 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:163:14:163:14 | i | indirect_use.cpp:164:14:164:14 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:164:14:164:14 | i | indirect_use.cpp:165:14:165:14 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:165:14:165:14 | i | indirect_use.cpp:166:19:166:19 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:166:18:166:19 | & ... | indirect_use.cpp:167:29:167:29 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:167:28:167:29 | & ... | indirect_use.cpp:168:21:168:21 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:167:28:167:29 | & ... | indirect_use.cpp:169:21:169:21 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:169:20:169:21 | & ... | indirect_use.cpp:170:20:170:20 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:170:19:170:20 | & ... | indirect_use.cpp:171:20:171:20 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:171:19:171:20 | & ... | indirect_use.cpp:172:20:172:20 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:172:19:172:20 | & ... | indirect_use.cpp:173:20:173:20 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:173:19:173:20 | & ... | indirect_use.cpp:174:15:174:15 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:174:15:174:15 | i | indirect_use.cpp:175:15:175:15 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:174:15:174:15 | i | indirect_use.cpp:176:22:176:22 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:176:21:176:22 | & ... | indirect_use.cpp:177:24:177:24 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:177:23:177:24 | & ... | indirect_use.cpp:178:31:178:31 | i | +| indirect_use.cpp:180:18:180:19 | ns | indirect_use.cpp:181:13:181:15 | & ... | indirect_use.cpp:182:14:182:15 | ns | +| indirect_use.cpp:180:18:180:19 | ns | indirect_use.cpp:182:13:182:15 | & ... | indirect_use.cpp:183:15:183:16 | ns | +| indirect_use.cpp:180:18:180:19 | ns | indirect_use.cpp:183:14:183:16 | & ... | indirect_use.cpp:184:14:184:15 | ns | +| pass_by_ref.cpp:10:7:10:24 | uninitializedArray | pass_by_ref.cpp:12:18:12:35 | uninitializedArray | pass_by_ref.cpp:13:20:13:37 | uninitializedArray | +| pass_by_ref.cpp:10:7:10:24 | uninitializedArray | pass_by_ref.cpp:13:20:13:37 | uninitializedArray | pass_by_ref.cpp:15:23:15:40 | uninitializedArray | +| pass_by_ref.cpp:10:7:10:24 | uninitializedArray | pass_by_ref.cpp:13:20:13:37 | uninitializedArray | pass_by_ref.cpp:16:25:16:42 | uninitializedArray | +| pass_by_ref.cpp:10:7:10:24 | uninitializedArray | pass_by_ref.cpp:13:20:13:37 | uninitializedArray | pass_by_ref.cpp:27:10:27:27 | uninitializedArray | +| pass_by_ref.cpp:18:7:18:8 | i1 | pass_by_ref.cpp:18:12:18:12 | n | pass_by_ref.cpp:19:22:19:23 | i1 | +| pass_by_ref.cpp:18:7:18:8 | i1 | pass_by_ref.cpp:19:22:19:23 | i1 | pass_by_ref.cpp:27:34:27:35 | i1 | +| pass_by_ref.cpp:21:7:21:8 | i2 | pass_by_ref.cpp:21:12:21:12 | n | pass_by_ref.cpp:22:21:22:22 | i2 | +| pass_by_ref.cpp:21:7:21:8 | i2 | pass_by_ref.cpp:22:20:22:22 | & ... | pass_by_ref.cpp:24:26:24:27 | i2 | +| pass_by_ref.cpp:21:7:21:8 | i2 | pass_by_ref.cpp:22:20:22:22 | & ... | pass_by_ref.cpp:25:27:25:28 | i2 | +| pass_by_ref.cpp:21:7:21:8 | i2 | pass_by_ref.cpp:22:20:22:22 | & ... | pass_by_ref.cpp:27:39:27:40 | i2 | +| pass_by_ref.cpp:30:15:30:15 | n | pass_by_ref.cpp:32:10:32:12 | -- ... | pass_by_ref.cpp:32:12:32:12 | n | +| pass_by_ref.cpp:31:7:31:9 | arr | pass_by_ref.cpp:31:15:31:18 | {...} | pass_by_ref.cpp:33:20:33:22 | arr | +| pass_by_ref.cpp:31:7:31:9 | arr | pass_by_ref.cpp:33:20:33:22 | arr | pass_by_ref.cpp:33:20:33:22 | arr | +| pass_by_ref.cpp:36:16:36:16 | n | pass_by_ref.cpp:39:10:39:12 | -- ... | pass_by_ref.cpp:39:12:39:12 | n | +| pass_by_ref.cpp:37:7:37:9 | arr | pass_by_ref.cpp:37:15:37:18 | {...} | pass_by_ref.cpp:40:22:40:24 | arr | +| pass_by_ref.cpp:37:7:37:9 | arr | pass_by_ref.cpp:40:22:40:24 | arr | pass_by_ref.cpp:41:20:41:22 | arr | +| pass_by_ref.cpp:37:7:37:9 | arr | pass_by_ref.cpp:41:20:41:22 | arr | pass_by_ref.cpp:40:22:40:24 | arr | +| pass_by_ref.cpp:46:7:46:7 | i | pass_by_ref.cpp:46:11:46:11 | n | pass_by_ref.cpp:49:5:49:5 | i | +| pass_by_ref.cpp:46:7:46:7 | i | pass_by_ref.cpp:46:11:46:11 | n | pass_by_ref.cpp:53:22:53:22 | i | +| pass_by_ref.cpp:46:7:46:7 | i | pass_by_ref.cpp:49:5:49:7 | ... ++ | pass_by_ref.cpp:53:22:53:22 | i | +| pass_by_ref.cpp:46:7:46:7 | i | pass_by_ref.cpp:53:22:53:22 | i | pass_by_ref.cpp:54:10:54:10 | i | +| test.cpp:4:7:4:7 | a | test.cpp:5:3:5:8 | ... = ... | test.cpp:9:7:9:7 | a | +| test.cpp:4:7:4:7 | a | test.cpp:13:3:13:7 | ... = ... | test.cpp:14:7:14:7 | a | +| test.cpp:4:7:4:7 | a | test.cpp:13:3:13:7 | ... = ... | test.cpp:18:7:18:7 | a | +| test.cpp:4:7:4:7 | a | test.cpp:13:3:13:7 | ... = ... | test.cpp:24:7:24:7 | a | +| test.cpp:4:7:4:7 | a | test.cpp:13:3:13:7 | ... = ... | test.cpp:28:11:28:11 | a | +| test.cpp:4:7:4:7 | a | test.cpp:19:5:19:9 | ... = ... | test.cpp:24:7:24:7 | a | +| test.cpp:4:7:4:7 | a | test.cpp:19:5:19:9 | ... = ... | test.cpp:28:11:28:11 | a | +| test.cpp:4:10:4:10 | b | test.cpp:6:3:6:8 | ... = ... | test.cpp:10:7:10:7 | b | +| test.cpp:4:10:4:10 | b | test.cpp:6:3:6:8 | ... = ... | test.cpp:13:7:13:7 | b | +| test.cpp:4:10:4:10 | b | test.cpp:6:3:6:8 | ... = ... | test.cpp:15:7:15:7 | b | +| test.cpp:4:10:4:10 | b | test.cpp:6:3:6:8 | ... = ... | test.cpp:25:7:25:7 | b | +| test.cpp:4:10:4:10 | b | test.cpp:21:5:21:9 | ... = ... | test.cpp:25:7:25:7 | b | +| test.cpp:4:13:4:13 | c | test.cpp:7:3:7:8 | ... = ... | test.cpp:11:7:11:7 | c | +| test.cpp:4:13:4:13 | c | test.cpp:7:3:7:8 | ... = ... | test.cpp:16:7:16:7 | c | +| test.cpp:4:13:4:13 | c | test.cpp:7:3:7:8 | ... = ... | test.cpp:26:7:26:7 | c | +| test.cpp:28:7:28:7 | d | test.cpp:28:11:28:11 | a | test.cpp:29:7:29:7 | d | +| test.cpp:28:7:28:7 | d | test.cpp:28:11:28:11 | a | test.cpp:31:11:31:11 | d | +| test.cpp:28:7:28:7 | d | test.cpp:31:11:31:13 | ... ++ | test.cpp:32:7:32:7 | d | +| test.cpp:28:7:28:7 | d | test.cpp:32:7:32:9 | ... ++ | test.cpp:33:7:33:7 | d | +| test.cpp:31:7:31:7 | e | test.cpp:32:3:32:9 | ... = ... | test.cpp:34:7:34:7 | e | +| test.cpp:44:8:44:8 | y | test.cpp:44:12:44:12 | x | test.cpp:44:16:44:16 | y | +| test.cpp:52:7:52:7 | x | test.cpp:52:10:52:11 | 0 | test.cpp:53:12:53:12 | x | +| test.cpp:52:7:52:7 | x | test.cpp:53:12:53:12 | x | test.cpp:54:7:54:7 | x | +| test.cpp:58:7:58:7 | x | test.cpp:58:10:58:11 | 0 | test.cpp:59:12:59:12 | x | +| test.cpp:58:7:58:7 | x | test.cpp:59:12:59:12 | x | test.cpp:60:7:60:7 | x | +| test.cpp:64:7:64:7 | x | test.cpp:64:10:64:11 | 0 | test.cpp:65:13:65:13 | x | +| test.cpp:64:7:64:7 | x | test.cpp:65:12:65:13 | & ... | test.cpp:66:7:66:7 | x | +| test.cpp:70:7:70:7 | x | test.cpp:70:10:70:11 | 0 | test.cpp:71:13:71:13 | x | +| test.cpp:70:7:70:7 | x | test.cpp:71:12:71:13 | & ... | test.cpp:72:7:72:7 | x | +| test.cpp:80:7:80:7 | x | test.cpp:80:10:80:11 | 0 | test.cpp:81:15:81:15 | x | +| test.cpp:80:7:80:7 | x | test.cpp:80:10:80:11 | 0 | test.cpp:82:7:82:7 | x | +| test.cpp:86:7:86:7 | x | test.cpp:86:10:86:11 | 0 | test.cpp:87:16:87:16 | x | +| test.cpp:86:7:86:7 | x | test.cpp:86:10:86:11 | 0 | test.cpp:88:7:88:7 | x | +| test.cpp:92:7:92:7 | x | test.cpp:92:10:92:11 | 0 | test.cpp:94:9:94:9 | x | +| test.cpp:92:7:92:7 | x | test.cpp:95:5:95:9 | ... = ... | test.cpp:94:9:94:9 | x | +| test.cpp:92:7:92:7 | x | test.cpp:95:5:95:9 | ... = ... | test.cpp:97:7:97:7 | x | +| test.cpp:93:12:93:12 | i | test.cpp:93:15:93:16 | 0 | test.cpp:93:19:93:19 | i | +| test.cpp:93:12:93:12 | i | test.cpp:93:15:93:16 | 0 | test.cpp:93:26:93:26 | i | +| test.cpp:93:12:93:12 | i | test.cpp:93:26:93:28 | ... ++ | test.cpp:93:19:93:19 | i | +| test.cpp:93:12:93:12 | i | test.cpp:93:26:93:28 | ... ++ | test.cpp:93:26:93:26 | i | +| test.cpp:101:7:101:7 | x | test.cpp:101:10:101:11 | 0 | test.cpp:104:9:104:9 | x | +| test.cpp:101:7:101:7 | x | test.cpp:105:5:105:9 | ... = ... | test.cpp:104:9:104:9 | x | +| test.cpp:101:7:101:7 | x | test.cpp:105:5:105:9 | ... = ... | test.cpp:108:7:108:7 | x | +| test.cpp:102:8:102:11 | done | test.cpp:102:14:102:19 | 0 | test.cpp:103:11:103:14 | done | +| test.cpp:102:8:102:11 | done | test.cpp:106:5:106:15 | ... = ... | test.cpp:103:11:103:14 | done | +| test.cpp:112:7:112:7 | x | test.cpp:112:10:112:11 | 0 | test.cpp:114:9:114:9 | x | +| test.cpp:112:7:112:7 | x | test.cpp:115:5:115:9 | ... = ... | test.cpp:114:9:114:9 | x | +| test.cpp:112:7:112:7 | x | test.cpp:115:5:115:9 | ... = ... | test.cpp:117:7:117:7 | x | +| test.cpp:112:7:112:7 | x | test.cpp:115:5:115:9 | ... = ... | test.cpp:120:9:120:9 | x | +| test.cpp:112:7:112:7 | x | test.cpp:121:5:121:9 | ... = ... | test.cpp:120:9:120:9 | x | +| test.cpp:112:7:112:7 | x | test.cpp:121:5:121:9 | ... = ... | test.cpp:124:7:124:7 | x | +| test.cpp:113:12:113:12 | i | test.cpp:113:15:113:16 | 0 | test.cpp:113:19:113:19 | i | +| test.cpp:113:12:113:12 | i | test.cpp:113:15:113:16 | 0 | test.cpp:113:26:113:26 | i | +| test.cpp:113:12:113:12 | i | test.cpp:113:26:113:28 | ... ++ | test.cpp:113:19:113:19 | i | +| test.cpp:113:12:113:12 | i | test.cpp:113:26:113:28 | ... ++ | test.cpp:113:26:113:26 | i | +| test.cpp:118:8:118:11 | done | test.cpp:118:14:118:19 | 0 | test.cpp:119:11:119:14 | done | +| test.cpp:118:8:118:11 | done | test.cpp:122:5:122:15 | ... = ... | test.cpp:119:11:119:14 | done | +| test.cpp:128:7:128:7 | x | test.cpp:128:10:128:11 | 0 | test.cpp:130:9:130:9 | x | +| test.cpp:128:7:128:7 | x | test.cpp:131:5:131:9 | ... = ... | test.cpp:134:11:134:11 | x | +| test.cpp:128:7:128:7 | x | test.cpp:135:7:135:11 | ... = ... | test.cpp:130:9:130:9 | x | +| test.cpp:128:7:128:7 | x | test.cpp:135:7:135:11 | ... = ... | test.cpp:134:11:134:11 | x | +| test.cpp:128:7:128:7 | x | test.cpp:135:7:135:11 | ... = ... | test.cpp:138:9:138:9 | x | +| test.cpp:128:7:128:7 | x | test.cpp:135:7:135:11 | ... = ... | test.cpp:140:7:140:7 | x | +| test.cpp:129:12:129:12 | i | test.cpp:129:15:129:16 | 0 | test.cpp:129:19:129:19 | i | +| test.cpp:129:12:129:12 | i | test.cpp:129:15:129:16 | 0 | test.cpp:129:26:129:26 | i | +| test.cpp:129:12:129:12 | i | test.cpp:129:26:129:28 | ... ++ | test.cpp:129:19:129:19 | i | +| test.cpp:129:12:129:12 | i | test.cpp:129:26:129:28 | ... ++ | test.cpp:129:26:129:26 | i | +| test.cpp:132:10:132:13 | done | test.cpp:132:16:132:21 | 0 | test.cpp:133:13:133:16 | done | +| test.cpp:132:10:132:13 | done | test.cpp:136:7:136:17 | ... = ... | test.cpp:133:13:133:16 | done | +| test.cpp:144:7:144:7 | x | test.cpp:144:10:144:11 | 0 | test.cpp:145:13:145:13 | x | +| test.cpp:145:8:145:8 | y | test.cpp:145:12:145:13 | & ... | test.cpp:146:4:146:4 | y | +| test.cpp:151:7:151:7 | x | test.cpp:151:10:151:11 | 0 | test.cpp:152:12:152:12 | x | +| test.cpp:158:17:158:17 | x | test.cpp:160:3:160:8 | ... = ... | test.cpp:161:7:161:7 | x | +| test.cpp:190:5:190:5 | s | test.cpp:190:8:190:24 | {...} | test.cpp:191:10:191:10 | s | diff --git a/cpp/ql/test/library-tests/defuse/definitionUsePair.ql b/cpp/ql/test/library-tests/defuse/definitionUsePair.ql new file mode 100644 index 000000000000..7856dc06c52a --- /dev/null +++ b/cpp/ql/test/library-tests/defuse/definitionUsePair.ql @@ -0,0 +1,5 @@ +import cpp + +from Variable v, Expr first, Expr second +where definitionUsePair(v, first, second) +select v, first, second diff --git a/cpp/ql/test/library-tests/defuse/definitionUsePairEquivalence.expected b/cpp/ql/test/library-tests/defuse/definitionUsePairEquivalence.expected new file mode 100644 index 000000000000..f082a67fcf66 --- /dev/null +++ b/cpp/ql/test/library-tests/defuse/definitionUsePairEquivalence.expected @@ -0,0 +1 @@ +| 0 | diff --git a/cpp/ql/test/library-tests/defuse/definitionUsePairEquivalence.ql b/cpp/ql/test/library-tests/defuse/definitionUsePairEquivalence.ql new file mode 100644 index 000000000000..077c4f267497 --- /dev/null +++ b/cpp/ql/test/library-tests/defuse/definitionUsePairEquivalence.ql @@ -0,0 +1,35 @@ +import cpp +import semmle.code.cpp.controlflow.LocalScopeVariableReachability + +// Test that def/use algorithm is an instance of LocalScopeVariableReachability +class MyDefOrUse extends LocalScopeVariableReachability { + MyDefOrUse() { this = "MyDefUse" } + + override predicate isSource(ControlFlowNode node, LocalScopeVariable v) { + definition(v, node) + } + + override predicate isSink(ControlFlowNode node, LocalScopeVariable v) { + useOfVar(v, node) + } + + override predicate isBarrier(ControlFlowNode node, LocalScopeVariable v) { + definitionBarrier(v, node) + } +} + +predicate equivalence() { + forall(LocalScopeVariable v, Expr first, Expr second | + definitionUsePair(v, first, second) | + exists(MyDefOrUse x | x.reaches(first, v, second)) + ) + and + forall(LocalScopeVariable v, Expr first, Expr second | + exists(MyDefOrUse x | x.reaches(first, v, second)) | + definitionUsePair(v, first, second) + ) +} + +from int i +where if equivalence() then i = 0 else i = 1 +select i diff --git a/cpp/ql/test/library-tests/defuse/exprDefinition.expected b/cpp/ql/test/library-tests/defuse/exprDefinition.expected new file mode 100644 index 000000000000..f5922a12c9d2 --- /dev/null +++ b/cpp/ql/test/library-tests/defuse/exprDefinition.expected @@ -0,0 +1,59 @@ +| addressOf.cpp:47:8:47:9 | f1 | addressOf.cpp:47:13:47:31 | [...](...){...} | addressOf.cpp:47:13:47:31 | [...](...){...} | +| addressOf.cpp:49:8:49:9 | f2 | addressOf.cpp:49:13:49:39 | [...](...){...} | addressOf.cpp:49:13:49:39 | [...](...){...} | +| addressOf.cpp:56:7:56:7 | a | addressOf.cpp:56:13:56:28 | {...} | addressOf.cpp:56:13:56:28 | {...} | +| indirect_use.cpp:20:10:20:10 | p | indirect_use.cpp:20:14:20:15 | ip | indirect_use.cpp:20:14:20:15 | ip | +| indirect_use.cpp:25:10:25:10 | p | indirect_use.cpp:25:14:25:19 | ... + ... | indirect_use.cpp:25:14:25:19 | ... + ... | +| indirect_use.cpp:35:10:35:10 | p | indirect_use.cpp:35:14:35:15 | ip | indirect_use.cpp:35:14:35:15 | ip | +| indirect_use.cpp:36:11:36:12 | pp | indirect_use.cpp:36:16:36:17 | & ... | indirect_use.cpp:36:16:36:17 | & ... | +| indirect_use.cpp:52:14:52:14 | i | indirect_use.cpp:52:17:52:18 | 0 | indirect_use.cpp:52:17:52:18 | 0 | +| indirect_use.cpp:53:14:53:14 | p | indirect_use.cpp:53:18:53:43 | __builtin_va_arg | indirect_use.cpp:53:18:53:43 | __builtin_va_arg | +| indirect_use.cpp:60:10:60:10 | p | indirect_use.cpp:60:14:60:19 | ... + ... | indirect_use.cpp:60:14:60:19 | ... + ... | +| indirect_use.cpp:65:27:65:27 | i | indirect_use.cpp:67:9:67:13 | ... = ... | indirect_use.cpp:67:13:67:13 | 0 | +| indirect_use.cpp:111:28:111:28 | i | indirect_use.cpp:112:5:112:9 | ... = ... | indirect_use.cpp:112:9:112:9 | 1 | +| pass_by_ref.cpp:18:7:18:8 | i1 | pass_by_ref.cpp:18:12:18:12 | n | pass_by_ref.cpp:18:12:18:12 | n | +| pass_by_ref.cpp:21:7:21:8 | i2 | pass_by_ref.cpp:21:12:21:12 | n | pass_by_ref.cpp:21:12:21:12 | n | +| pass_by_ref.cpp:31:7:31:9 | arr | pass_by_ref.cpp:31:15:31:18 | {...} | pass_by_ref.cpp:31:15:31:18 | {...} | +| pass_by_ref.cpp:37:7:37:9 | arr | pass_by_ref.cpp:37:15:37:18 | {...} | pass_by_ref.cpp:37:15:37:18 | {...} | +| pass_by_ref.cpp:46:7:46:7 | i | pass_by_ref.cpp:46:11:46:11 | n | pass_by_ref.cpp:46:11:46:11 | n | +| test.cpp:4:7:4:7 | a | test.cpp:5:3:5:8 | ... = ... | test.cpp:5:7:5:8 | a0 | +| test.cpp:4:7:4:7 | a | test.cpp:13:3:13:7 | ... = ... | test.cpp:13:7:13:7 | b | +| test.cpp:4:7:4:7 | a | test.cpp:19:5:19:9 | ... = ... | test.cpp:19:9:19:9 | 1 | +| test.cpp:4:10:4:10 | b | test.cpp:6:3:6:8 | ... = ... | test.cpp:6:7:6:8 | b0 | +| test.cpp:4:10:4:10 | b | test.cpp:21:5:21:9 | ... = ... | test.cpp:21:9:21:9 | 1 | +| test.cpp:4:13:4:13 | c | test.cpp:7:3:7:8 | ... = ... | test.cpp:7:7:7:8 | c0 | +| test.cpp:28:7:28:7 | d | test.cpp:28:11:28:11 | a | test.cpp:28:11:28:11 | a | +| test.cpp:31:7:31:7 | e | test.cpp:31:11:31:13 | ... ++ | test.cpp:31:11:31:13 | ... ++ | +| test.cpp:31:7:31:7 | e | test.cpp:32:3:32:9 | ... = ... | test.cpp:32:7:32:9 | ... ++ | +| test.cpp:39:20:39:20 | x | test.cpp:40:3:40:8 | ... = ... | test.cpp:40:7:40:8 | 42 | +| test.cpp:44:8:44:8 | y | test.cpp:44:12:44:12 | x | test.cpp:44:12:44:12 | x | +| test.cpp:52:7:52:7 | x | test.cpp:52:10:52:11 | 0 | test.cpp:52:10:52:11 | 0 | +| test.cpp:58:7:58:7 | x | test.cpp:58:10:58:11 | 0 | test.cpp:58:10:58:11 | 0 | +| test.cpp:64:7:64:7 | x | test.cpp:64:10:64:11 | 0 | test.cpp:64:10:64:11 | 0 | +| test.cpp:70:7:70:7 | x | test.cpp:70:10:70:11 | 0 | test.cpp:70:10:70:11 | 0 | +| test.cpp:80:7:80:7 | x | test.cpp:80:10:80:11 | 0 | test.cpp:80:10:80:11 | 0 | +| test.cpp:86:7:86:7 | x | test.cpp:86:10:86:11 | 0 | test.cpp:86:10:86:11 | 0 | +| test.cpp:92:7:92:7 | x | test.cpp:92:10:92:11 | 0 | test.cpp:92:10:92:11 | 0 | +| test.cpp:92:7:92:7 | x | test.cpp:95:5:95:9 | ... = ... | test.cpp:95:9:95:9 | 3 | +| test.cpp:93:12:93:12 | i | test.cpp:93:15:93:16 | 0 | test.cpp:93:15:93:16 | 0 | +| test.cpp:101:7:101:7 | x | test.cpp:101:10:101:11 | 0 | test.cpp:101:10:101:11 | 0 | +| test.cpp:101:7:101:7 | x | test.cpp:105:5:105:9 | ... = ... | test.cpp:105:9:105:9 | 3 | +| test.cpp:102:8:102:11 | done | test.cpp:102:14:102:19 | 0 | test.cpp:102:14:102:19 | 0 | +| test.cpp:102:8:102:11 | done | test.cpp:106:5:106:15 | ... = ... | test.cpp:106:12:106:15 | 1 | +| test.cpp:112:7:112:7 | x | test.cpp:112:10:112:11 | 0 | test.cpp:112:10:112:11 | 0 | +| test.cpp:112:7:112:7 | x | test.cpp:115:5:115:9 | ... = ... | test.cpp:115:9:115:9 | 3 | +| test.cpp:112:7:112:7 | x | test.cpp:121:5:121:9 | ... = ... | test.cpp:121:9:121:9 | 3 | +| test.cpp:113:12:113:12 | i | test.cpp:113:15:113:16 | 0 | test.cpp:113:15:113:16 | 0 | +| test.cpp:118:8:118:11 | done | test.cpp:118:14:118:19 | 0 | test.cpp:118:14:118:19 | 0 | +| test.cpp:118:8:118:11 | done | test.cpp:122:5:122:15 | ... = ... | test.cpp:122:12:122:15 | 1 | +| test.cpp:128:7:128:7 | x | test.cpp:128:10:128:11 | 0 | test.cpp:128:10:128:11 | 0 | +| test.cpp:128:7:128:7 | x | test.cpp:131:5:131:9 | ... = ... | test.cpp:131:9:131:9 | 3 | +| test.cpp:128:7:128:7 | x | test.cpp:135:7:135:11 | ... = ... | test.cpp:135:11:135:11 | 3 | +| test.cpp:129:12:129:12 | i | test.cpp:129:15:129:16 | 0 | test.cpp:129:15:129:16 | 0 | +| test.cpp:132:10:132:13 | done | test.cpp:132:16:132:21 | 0 | test.cpp:132:16:132:21 | 0 | +| test.cpp:132:10:132:13 | done | test.cpp:136:7:136:17 | ... = ... | test.cpp:136:14:136:17 | 1 | +| test.cpp:144:7:144:7 | x | test.cpp:144:10:144:11 | 0 | test.cpp:144:10:144:11 | 0 | +| test.cpp:145:8:145:8 | y | test.cpp:145:12:145:13 | & ... | test.cpp:145:12:145:13 | & ... | +| test.cpp:151:7:151:7 | x | test.cpp:151:10:151:11 | 0 | test.cpp:151:10:151:11 | 0 | +| test.cpp:152:8:152:8 | y | test.cpp:154:3:154:7 | ... = ... | test.cpp:154:7:154:7 | 1 | +| test.cpp:158:17:158:17 | x | test.cpp:160:3:160:8 | ... = ... | test.cpp:160:7:160:8 | 42 | +| test.cpp:190:5:190:5 | s | test.cpp:190:8:190:24 | {...} | test.cpp:190:8:190:24 | {...} | diff --git a/cpp/ql/test/library-tests/defuse/exprDefinition.ql b/cpp/ql/test/library-tests/defuse/exprDefinition.ql new file mode 100644 index 000000000000..4bf86e1ae176 --- /dev/null +++ b/cpp/ql/test/library-tests/defuse/exprDefinition.ql @@ -0,0 +1,5 @@ +import cpp + +from LocalScopeVariable v, ControlFlowNode def, Expr e +where exprDefinition(v, def, e) +select v, def, e diff --git a/cpp/ql/test/library-tests/defuse/indirect_use.cpp b/cpp/ql/test/library-tests/defuse/indirect_use.cpp new file mode 100644 index 000000000000..5ac1ca1146ad --- /dev/null +++ b/cpp/ql/test/library-tests/defuse/indirect_use.cpp @@ -0,0 +1,188 @@ +extern int externInt; // used as a source of non-determinism + +void receiveInt(int); +void receiveIntPtr(int *p); +void receiveConstIntPtr(const int *p); + +// Pointers and references to functions +void (*functionPointer)(int *p); +extern void (&functionReference)(int *p); +void (&referenceToreceiveIntPtr)(int *p) = receiveIntPtr; + +struct IntPointer { + int *p; +}; + +IntPointer globalIntPtr; +IntPointer *globalIntPtrPtr = &globalIntPtr; + +static void readPointer1(int *ip) { + int *p = ip; + receiveInt(*p); +} + +static void readPointer2(int *ip) { + int *p = ip + 1; + p--; + receiveInt(*p); +} + +static void readPPP(int ***ppp) { + receiveInt(***ppp); +} + +static void readPointer3(int *ip) { + int *p = ip; + int **pp = &p; + readPPP(&pp); +} + +void readPointer4(int *ip) { + readPointer3(ip); +} + +void readPointer5(int *ip) { + receiveInt(ip[0]); +} + +void readVarArgsByCount(int argCount, ...) { + __builtin_va_list ap; + + __builtin_va_start(ap, argCount); + for (int i = 0; i < argCount; i++) { + int *p = __builtin_va_arg(ap, int*); + receiveIntPtr(p); + } + __builtin_va_end(ap); +} + +static void readRef1(int &i) { + int *p = &i + 1; + p--; + receiveInt(*p); +} + +static void readRef2(int &i) { + if (externInt) { + i = 0; + } + receiveInt(i); +} + +static void readRef3(int &i) { + readRef2(i); +} + +static void leakPointer1(int *ip) { + globalIntPtrPtr->p = ip; +} + +static void readPointerConditional(int *ip) { + if (externInt) { + receiveInt(*ip); + } +} + +static void ignorePointer1(int *ip) { +} + +// recursive +static void ignorePointer2(int *ip) { + if (externInt > 0) { + externInt--; + ignorePointer1(ip); + ignorePointer2(ip); + } +} + +static void writePointer1(int *ip) { + *ip = 1; +} + +static void writePointer2(int *ip) { + ip[0] = 1; +} + +static void writePointer3(int *ip) { + *ip = 1; + receiveInt(*ip); +} + +static void writeRef3(int &i) { + i = 1; + receiveInt(i); +} + +static void writePointer4(int *ip) { + ip[0] = 1; + ip++; + ip--; + receiveInt(*ip); +} + +struct C { + virtual void f(int &i) { + // This function does not access i, but overrides might. + } +}; + +struct NestedStruct { + struct { + struct { + double d; + int i; + } level2; + } level1; +}; + +void readNS1(NestedStruct *ns) { + readPointer1(&ns->level1.level2.i); +} + +void readNS2(NestedStruct *ns) { + receiveInt(ns->level1.level2.i); +} + +void writeNS1(NestedStruct *ns) { + ns->level1.level2.i = 0; +} + +void writeNS2(NestedStruct &ns) { + ns.level1.level2.i = 0; +} + +void caller(C *object) { + int i; + readPointer1(&i); + readPointer2(&i); + readPointer3(&i); + readPointer4(&i); + readPointer5(&i); + readVarArgsByCount(0, &i); // does not actually read + readVarArgsByCount(1, &i); + readRef1(i); + readRef2(i); + readRef3(i); + leakPointer1(&i); + readPointerConditional(&i); + ignorePointer1(&i); + ignorePointer2(&i); + writePointer1(&i); + writePointer2(&i); + writePointer3(&i); + writePointer4(&i); + writeRef3(i); + object->f(i); + functionPointer(&i); + functionReference(&i); + referenceToreceiveIntPtr(&i); + + NestedStruct ns; + readNS1(&ns); + readNS2(&ns); + writeNS1(&ns); + writeNS2(ns); + + receiveInt(*globalIntPtrPtr->p); + functionPointer = receiveIntPtr; +} diff --git a/cpp/ql/test/library-tests/defuse/isAddressOfAccess.expected b/cpp/ql/test/library-tests/defuse/isAddressOfAccess.expected new file mode 100644 index 000000000000..7b6bd8d4fac5 --- /dev/null +++ b/cpp/ql/test/library-tests/defuse/isAddressOfAccess.expected @@ -0,0 +1,245 @@ +| addressOf.cpp:14:28:14:29 | d_ | non-const address | +| addressOf.cpp:24:32:24:32 | i | non-const address | +| addressOf.cpp:25:26:25:26 | i | non-const address | +| addressOf.cpp:26:32:26:32 | i | const address | +| addressOf.cpp:32:19:32:19 | i | non-const address | +| addressOf.cpp:34:18:34:18 | i | | +| addressOf.cpp:34:23:34:23 | i | non-const address | +| addressOf.cpp:35:23:35:23 | i | non-const address | +| addressOf.cpp:36:22:36:22 | i | non-const address | +| addressOf.cpp:37:20:37:20 | i | non-const address | +| addressOf.cpp:38:20:38:20 | i | non-const address | +| addressOf.cpp:40:15:40:15 | i | non-const address | +| addressOf.cpp:42:19:42:22 | iref | non-const address | +| addressOf.cpp:48:3:48:4 | f1 | | +| addressOf.cpp:49:15:49:22 | captured | non-const address | +| addressOf.cpp:50:3:50:4 | f1 | | +| addressOf.cpp:51:10:51:17 | captured | | +| addressOf.cpp:56:16:56:16 | i | | +| addressOf.cpp:56:19:56:19 | i | | +| addressOf.cpp:56:24:56:24 | i | | +| addressOf.cpp:57:19:57:19 | a | non-const address | +| addressOf.cpp:57:33:57:33 | a | | +| addressOf.cpp:57:44:57:44 | a | | +| addressOf.cpp:58:18:58:18 | a | non-const address | +| addressOf.cpp:62:11:62:13 | ptr | | +| addressOf.cpp:63:19:63:21 | ptr | | +| addressOf.cpp:63:24:63:26 | ref | non-const address | +| file://:0:0:0:0 | captured | | +| file://:0:0:0:0 | captured | | +| file://:0:0:0:0 | captured | non-const address | +| indirect_use.cpp:17:32:17:43 | globalIntPtr | non-const address | +| indirect_use.cpp:20:14:20:15 | ip | | +| indirect_use.cpp:21:17:21:17 | p | | +| indirect_use.cpp:25:14:25:15 | ip | | +| indirect_use.cpp:26:5:26:5 | p | | +| indirect_use.cpp:27:17:27:17 | p | | +| indirect_use.cpp:31:19:31:21 | ppp | | +| indirect_use.cpp:35:14:35:15 | ip | | +| indirect_use.cpp:36:17:36:17 | p | non-const address | +| indirect_use.cpp:37:14:37:15 | pp | non-const address | +| indirect_use.cpp:41:18:41:19 | ip | | +| indirect_use.cpp:45:16:45:17 | ip | | +| indirect_use.cpp:51:24:51:25 | ap | | +| indirect_use.cpp:51:28:51:35 | argCount | | +| indirect_use.cpp:52:21:52:21 | i | | +| indirect_use.cpp:52:25:52:32 | argCount | | +| indirect_use.cpp:52:35:52:35 | i | | +| indirect_use.cpp:53:35:53:36 | ap | | +| indirect_use.cpp:54:23:54:23 | p | | +| indirect_use.cpp:56:22:56:23 | ap | | +| indirect_use.cpp:60:15:60:15 | i | non-const address | +| indirect_use.cpp:61:5:61:5 | p | | +| indirect_use.cpp:62:17:62:17 | p | | +| indirect_use.cpp:66:9:66:17 | externInt | | +| indirect_use.cpp:67:9:67:9 | i | | +| indirect_use.cpp:69:16:69:16 | i | | +| indirect_use.cpp:73:14:73:14 | i | non-const address | +| indirect_use.cpp:77:5:77:19 | globalIntPtrPtr | | +| indirect_use.cpp:77:22:77:22 | p | | +| indirect_use.cpp:77:26:77:27 | ip | | +| indirect_use.cpp:81:9:81:17 | externInt | | +| indirect_use.cpp:82:21:82:22 | ip | | +| indirect_use.cpp:91:9:91:17 | externInt | | +| indirect_use.cpp:92:9:92:17 | externInt | | +| indirect_use.cpp:93:24:93:25 | ip | | +| indirect_use.cpp:94:24:94:25 | ip | | +| indirect_use.cpp:99:6:99:7 | ip | | +| indirect_use.cpp:103:5:103:6 | ip | | +| indirect_use.cpp:107:6:107:7 | ip | | +| indirect_use.cpp:108:17:108:18 | ip | | +| indirect_use.cpp:112:5:112:5 | i | | +| indirect_use.cpp:113:16:113:16 | i | | +| indirect_use.cpp:117:5:117:6 | ip | | +| indirect_use.cpp:118:5:118:6 | ip | | +| indirect_use.cpp:119:5:119:6 | ip | | +| indirect_use.cpp:120:17:120:18 | ip | | +| indirect_use.cpp:139:19:139:20 | ns | | +| indirect_use.cpp:139:23:139:28 | level1 | non-const address | +| indirect_use.cpp:139:30:139:35 | level2 | non-const address | +| indirect_use.cpp:139:37:139:37 | i | non-const address | +| indirect_use.cpp:143:16:143:17 | ns | | +| indirect_use.cpp:143:20:143:25 | level1 | | +| indirect_use.cpp:143:27:143:32 | level2 | | +| indirect_use.cpp:143:34:143:34 | i | | +| indirect_use.cpp:147:5:147:6 | ns | | +| indirect_use.cpp:147:9:147:14 | level1 | | +| indirect_use.cpp:147:16:147:21 | level2 | | +| indirect_use.cpp:147:23:147:23 | i | | +| indirect_use.cpp:151:5:151:6 | ns | | +| indirect_use.cpp:151:8:151:13 | level1 | | +| indirect_use.cpp:151:15:151:20 | level2 | | +| indirect_use.cpp:151:22:151:22 | i | | +| indirect_use.cpp:156:19:156:19 | i | non-const address | +| indirect_use.cpp:157:19:157:19 | i | non-const address | +| indirect_use.cpp:158:19:158:19 | i | non-const address | +| indirect_use.cpp:159:19:159:19 | i | non-const address | +| indirect_use.cpp:160:19:160:19 | i | non-const address | +| indirect_use.cpp:161:28:161:28 | i | non-const address | +| indirect_use.cpp:162:28:162:28 | i | non-const address | +| indirect_use.cpp:163:14:163:14 | i | non-const address | +| indirect_use.cpp:164:14:164:14 | i | non-const address | +| indirect_use.cpp:165:14:165:14 | i | non-const address | +| indirect_use.cpp:166:19:166:19 | i | non-const address | +| indirect_use.cpp:167:29:167:29 | i | non-const address | +| indirect_use.cpp:168:21:168:21 | i | non-const address | +| indirect_use.cpp:169:21:169:21 | i | non-const address | +| indirect_use.cpp:170:20:170:20 | i | non-const address | +| indirect_use.cpp:171:20:171:20 | i | non-const address | +| indirect_use.cpp:172:20:172:20 | i | non-const address | +| indirect_use.cpp:173:20:173:20 | i | non-const address | +| indirect_use.cpp:174:15:174:15 | i | non-const address | +| indirect_use.cpp:175:5:175:10 | object | | +| indirect_use.cpp:175:15:175:15 | i | non-const address | +| indirect_use.cpp:176:5:176:19 | functionPointer | | +| indirect_use.cpp:176:22:176:22 | i | non-const address | +| indirect_use.cpp:177:5:177:21 | functionReference | | +| indirect_use.cpp:177:24:177:24 | i | non-const address | +| indirect_use.cpp:178:5:178:28 | referenceToreceiveIntPtr | | +| indirect_use.cpp:178:31:178:31 | i | non-const address | +| indirect_use.cpp:181:14:181:15 | ns | non-const address | +| indirect_use.cpp:182:14:182:15 | ns | non-const address | +| indirect_use.cpp:183:15:183:16 | ns | non-const address | +| indirect_use.cpp:184:14:184:15 | ns | non-const address | +| indirect_use.cpp:186:17:186:31 | globalIntPtrPtr | | +| indirect_use.cpp:186:34:186:34 | p | | +| indirect_use.cpp:187:5:187:19 | functionPointer | | +| pass_by_ref.cpp:12:18:12:35 | uninitializedArray | non-const address | +| pass_by_ref.cpp:13:20:13:37 | uninitializedArray | non-const address | +| pass_by_ref.cpp:15:23:15:40 | uninitializedArray | const address | +| pass_by_ref.cpp:16:25:16:42 | uninitializedArray | const address | +| pass_by_ref.cpp:18:12:18:12 | n | | +| pass_by_ref.cpp:19:22:19:23 | i1 | non-const address | +| pass_by_ref.cpp:21:12:21:12 | n | | +| pass_by_ref.cpp:22:21:22:22 | i2 | non-const address | +| pass_by_ref.cpp:24:26:24:27 | i2 | const address | +| pass_by_ref.cpp:25:27:25:28 | i2 | const address | +| pass_by_ref.cpp:27:10:27:27 | uninitializedArray | | +| pass_by_ref.cpp:27:34:27:35 | i1 | | +| pass_by_ref.cpp:27:39:27:40 | i2 | | +| pass_by_ref.cpp:32:12:32:12 | n | | +| pass_by_ref.cpp:33:20:33:22 | arr | non-const address | +| pass_by_ref.cpp:39:12:39:12 | n | | +| pass_by_ref.cpp:40:22:40:24 | arr | non-const address | +| pass_by_ref.cpp:41:20:41:22 | arr | non-const address | +| pass_by_ref.cpp:46:11:46:11 | n | | +| pass_by_ref.cpp:48:7:48:7 | n | | +| pass_by_ref.cpp:49:5:49:5 | i | | +| pass_by_ref.cpp:53:22:53:22 | i | non-const address | +| pass_by_ref.cpp:54:10:54:10 | i | | +| test.cpp:5:3:5:3 | a | | +| test.cpp:5:7:5:8 | a0 | | +| test.cpp:6:3:6:3 | b | | +| test.cpp:6:7:6:8 | b0 | | +| test.cpp:7:3:7:3 | c | | +| test.cpp:7:7:7:8 | c0 | | +| test.cpp:9:7:9:7 | a | | +| test.cpp:10:7:10:7 | b | | +| test.cpp:11:7:11:7 | c | | +| test.cpp:13:3:13:3 | a | | +| test.cpp:13:7:13:7 | b | | +| test.cpp:14:7:14:7 | a | | +| test.cpp:15:7:15:7 | b | | +| test.cpp:16:7:16:7 | c | | +| test.cpp:18:7:18:7 | a | | +| test.cpp:19:5:19:5 | a | | +| test.cpp:21:5:21:5 | b | | +| test.cpp:24:7:24:7 | a | | +| test.cpp:25:7:25:7 | b | | +| test.cpp:26:7:26:7 | c | | +| test.cpp:28:11:28:11 | a | | +| test.cpp:29:7:29:7 | d | | +| test.cpp:31:11:31:11 | d | | +| test.cpp:32:3:32:3 | e | | +| test.cpp:32:7:32:7 | d | | +| test.cpp:33:7:33:7 | d | | +| test.cpp:34:7:34:7 | e | | +| test.cpp:40:3:40:3 | x | | +| test.cpp:44:12:44:12 | x | | +| test.cpp:44:16:44:16 | y | | +| test.cpp:48:13:48:13 | x | | +| test.cpp:53:12:53:12 | x | non-const address | +| test.cpp:54:7:54:7 | x | | +| test.cpp:59:12:59:12 | x | non-const address | +| test.cpp:60:7:60:7 | x | | +| test.cpp:65:13:65:13 | x | non-const address | +| test.cpp:66:7:66:7 | x | | +| test.cpp:71:13:71:13 | x | non-const address | +| test.cpp:72:7:72:7 | x | | +| test.cpp:81:15:81:15 | x | non-const address | +| test.cpp:82:7:82:7 | x | | +| test.cpp:87:16:87:16 | x | non-const address | +| test.cpp:88:7:88:7 | x | | +| test.cpp:93:19:93:19 | i | | +| test.cpp:93:26:93:26 | i | | +| test.cpp:94:9:94:9 | x | | +| test.cpp:95:5:95:5 | x | | +| test.cpp:97:7:97:7 | x | | +| test.cpp:103:11:103:14 | done | | +| test.cpp:104:9:104:9 | x | | +| test.cpp:105:5:105:5 | x | | +| test.cpp:106:5:106:8 | done | | +| test.cpp:108:7:108:7 | x | | +| test.cpp:113:19:113:19 | i | | +| test.cpp:113:26:113:26 | i | | +| test.cpp:114:9:114:9 | x | | +| test.cpp:115:5:115:5 | x | | +| test.cpp:117:7:117:7 | x | | +| test.cpp:119:11:119:14 | done | | +| test.cpp:120:9:120:9 | x | | +| test.cpp:121:5:121:5 | x | | +| test.cpp:122:5:122:8 | done | | +| test.cpp:124:7:124:7 | x | | +| test.cpp:129:19:129:19 | i | | +| test.cpp:129:26:129:26 | i | | +| test.cpp:130:9:130:9 | x | | +| test.cpp:131:5:131:5 | x | | +| test.cpp:133:13:133:16 | done | | +| test.cpp:134:11:134:11 | x | | +| test.cpp:135:7:135:7 | x | | +| test.cpp:136:7:136:10 | done | | +| test.cpp:138:9:138:9 | x | | +| test.cpp:140:7:140:7 | x | | +| test.cpp:145:13:145:13 | x | non-const address | +| test.cpp:146:4:146:4 | y | | +| test.cpp:147:7:147:7 | x | | +| test.cpp:152:12:152:12 | x | non-const address | +| test.cpp:153:7:153:7 | x | | +| test.cpp:154:3:154:3 | y | | +| test.cpp:155:7:155:7 | x | | +| test.cpp:159:7:159:7 | x | | +| test.cpp:160:3:160:3 | x | | +| test.cpp:161:7:161:7 | x | | +| test.cpp:165:7:165:7 | x | | +| test.cpp:169:8:169:8 | x | | +| test.cpp:173:19:173:19 | x | const address | +| test.cpp:174:20:174:20 | x | const address | +| test.cpp:175:7:175:7 | x | | +| test.cpp:183:38:183:41 | yptr | | +| test.cpp:183:48:183:48 | z | | +| test.cpp:184:28:184:35 | static_y | non-const address | +| test.cpp:184:42:184:42 | z | | +| test.cpp:190:11:190:11 | x | | +| test.cpp:190:17:190:17 | y | non-const address | +| test.cpp:190:20:190:20 | z | | +| test.cpp:191:10:191:10 | s | | diff --git a/cpp/ql/test/library-tests/defuse/isAddressOfAccess.ql b/cpp/ql/test/library-tests/defuse/isAddressOfAccess.ql new file mode 100644 index 000000000000..4fef4fbf0a2b --- /dev/null +++ b/cpp/ql/test/library-tests/defuse/isAddressOfAccess.ql @@ -0,0 +1,11 @@ +import cpp + +from VariableAccess va, string description +where + if va.isAddressOfAccessNonConst() then + description = "non-const address" + else if va.isAddressOfAccess() then + description = "const address" + else + description = "" +select va, description diff --git a/cpp/ql/test/library-tests/defuse/parameterUsePair.expected b/cpp/ql/test/library-tests/defuse/parameterUsePair.expected new file mode 100644 index 000000000000..c2eff5fac121 --- /dev/null +++ b/cpp/ql/test/library-tests/defuse/parameterUsePair.expected @@ -0,0 +1,56 @@ +| addressOf.cpp:31:23:31:23 | i | addressOf.cpp:32:19:32:19 | i | +| addressOf.cpp:46:17:46:24 | captured | file://:0:0:0:0 | captured | +| addressOf.cpp:55:17:55:17 | i | addressOf.cpp:56:16:56:16 | i | +| addressOf.cpp:55:17:55:17 | i | addressOf.cpp:56:19:56:19 | i | +| addressOf.cpp:55:17:55:17 | i | addressOf.cpp:56:24:56:24 | i | +| addressOf.cpp:61:23:61:25 | ptr | addressOf.cpp:62:11:62:13 | ptr | +| addressOf.cpp:61:23:61:25 | ptr | addressOf.cpp:63:19:63:21 | ptr | +| addressOf.cpp:61:33:61:35 | ref | addressOf.cpp:63:24:63:26 | ref | +| indirect_use.cpp:19:31:19:32 | ip | indirect_use.cpp:20:14:20:15 | ip | +| indirect_use.cpp:24:31:24:32 | ip | indirect_use.cpp:25:14:25:15 | ip | +| indirect_use.cpp:30:28:30:30 | ppp | indirect_use.cpp:31:19:31:21 | ppp | +| indirect_use.cpp:34:31:34:32 | ip | indirect_use.cpp:35:14:35:15 | ip | +| indirect_use.cpp:40:24:40:25 | ip | indirect_use.cpp:41:18:41:19 | ip | +| indirect_use.cpp:44:24:44:25 | ip | indirect_use.cpp:45:16:45:17 | ip | +| indirect_use.cpp:48:29:48:36 | argCount | indirect_use.cpp:51:28:51:35 | argCount | +| indirect_use.cpp:48:29:48:36 | argCount | indirect_use.cpp:52:25:52:32 | argCount | +| indirect_use.cpp:59:27:59:27 | i | indirect_use.cpp:60:15:60:15 | i | +| indirect_use.cpp:65:27:65:27 | i | indirect_use.cpp:69:16:69:16 | i | +| indirect_use.cpp:72:27:72:27 | i | indirect_use.cpp:73:14:73:14 | i | +| indirect_use.cpp:76:31:76:32 | ip | indirect_use.cpp:77:26:77:27 | ip | +| indirect_use.cpp:80:41:80:42 | ip | indirect_use.cpp:82:21:82:22 | ip | +| indirect_use.cpp:90:33:90:34 | ip | indirect_use.cpp:93:24:93:25 | ip | +| indirect_use.cpp:90:33:90:34 | ip | indirect_use.cpp:94:24:94:25 | ip | +| indirect_use.cpp:98:32:98:33 | ip | indirect_use.cpp:99:6:99:7 | ip | +| indirect_use.cpp:102:32:102:33 | ip | indirect_use.cpp:103:5:103:6 | ip | +| indirect_use.cpp:106:32:106:33 | ip | indirect_use.cpp:107:6:107:7 | ip | +| indirect_use.cpp:106:32:106:33 | ip | indirect_use.cpp:108:17:108:18 | ip | +| indirect_use.cpp:116:32:116:33 | ip | indirect_use.cpp:117:5:117:6 | ip | +| indirect_use.cpp:116:32:116:33 | ip | indirect_use.cpp:118:5:118:6 | ip | +| indirect_use.cpp:138:28:138:29 | ns | indirect_use.cpp:139:19:139:20 | ns | +| indirect_use.cpp:142:28:142:29 | ns | indirect_use.cpp:143:16:143:17 | ns | +| indirect_use.cpp:146:29:146:30 | ns | indirect_use.cpp:147:5:147:6 | ns | +| indirect_use.cpp:150:29:150:30 | ns | indirect_use.cpp:151:5:151:6 | ns | +| indirect_use.cpp:154:16:154:21 | object | indirect_use.cpp:175:5:175:10 | object | +| pass_by_ref.cpp:9:16:9:16 | n | pass_by_ref.cpp:18:12:18:12 | n | +| pass_by_ref.cpp:9:16:9:16 | n | pass_by_ref.cpp:21:12:21:12 | n | +| pass_by_ref.cpp:30:15:30:15 | n | pass_by_ref.cpp:32:12:32:12 | n | +| pass_by_ref.cpp:36:16:36:16 | n | pass_by_ref.cpp:39:12:39:12 | n | +| pass_by_ref.cpp:45:17:45:17 | n | pass_by_ref.cpp:46:11:46:11 | n | +| pass_by_ref.cpp:45:17:45:17 | n | pass_by_ref.cpp:48:7:48:7 | n | +| test.cpp:3:16:3:17 | a0 | test.cpp:5:7:5:8 | a0 | +| test.cpp:3:24:3:25 | b0 | test.cpp:6:7:6:8 | b0 | +| test.cpp:3:32:3:33 | c0 | test.cpp:7:7:7:8 | c0 | +| test.cpp:43:20:43:20 | x | test.cpp:44:12:44:12 | x | +| test.cpp:47:20:47:20 | x | test.cpp:48:13:48:13 | x | +| test.cpp:158:17:158:17 | x | test.cpp:159:7:159:7 | x | +| test.cpp:164:33:164:33 | x | test.cpp:165:7:165:7 | x | +| test.cpp:168:33:168:33 | x | test.cpp:169:8:169:8 | x | +| test.cpp:172:17:172:17 | x | test.cpp:173:19:173:19 | x | +| test.cpp:172:17:172:17 | x | test.cpp:174:20:174:20 | x | +| test.cpp:172:17:172:17 | x | test.cpp:175:7:175:7 | x | +| test.cpp:183:17:183:20 | yptr | test.cpp:183:38:183:41 | yptr | +| test.cpp:183:27:183:27 | z | test.cpp:183:48:183:48 | z | +| test.cpp:184:16:184:16 | z | test.cpp:184:42:184:42 | z | +| test.cpp:188:9:188:9 | x | test.cpp:190:11:190:11 | x | +| test.cpp:188:16:188:16 | z | test.cpp:190:20:190:20 | z | diff --git a/cpp/ql/test/library-tests/defuse/parameterUsePair.ql b/cpp/ql/test/library-tests/defuse/parameterUsePair.ql new file mode 100644 index 000000000000..8d76ae7f0244 --- /dev/null +++ b/cpp/ql/test/library-tests/defuse/parameterUsePair.ql @@ -0,0 +1,5 @@ +import cpp + +from Parameter p, VariableAccess va +where parameterUsePair(p, va) +select p, va \ No newline at end of file diff --git a/cpp/ql/test/library-tests/defuse/pass_by_ref.cpp b/cpp/ql/test/library-tests/defuse/pass_by_ref.cpp new file mode 100644 index 000000000000..2633786d2434 --- /dev/null +++ b/cpp/ql/test/library-tests/defuse/pass_by_ref.cpp @@ -0,0 +1,55 @@ +void arrayParameter(int a[4]); +void pointerParameter(int *a); +void referenceParameter(int &n); + +void constArrayParameter(const int a[4]); +void constPointerParameter(const int *a); +void constReferenceParameter(const int &n); + +int caller(int n) { + int uninitializedArray[4]; + + arrayParameter(uninitializedArray); + pointerParameter(uninitializedArray); + + constArrayParameter(uninitializedArray); + constPointerParameter(uninitializedArray); + + int i1 = n; + referenceParameter(i1); + + int i2 = n; + pointerParameter(&i2); + + constPointerParameter(&i2); + constReferenceParameter(i2); + + return uninitializedArray[0] + i1 + i2; +} + +void loop(int n) { + int arr[4] = {0}; + while (--n) + arrayParameter(arr); +} + +void loop2(int n) { + int arr[4] = {0}; + + while (--n) { + pointerParameter(arr); + arrayParameter(arr); + } +} + +int afterIf(int n) { + int i = n; + + if (n) { + i++; + } + // The following access to `i` should be the first control-flow node in its + // basic block. + referenceParameter(i); + return i; +} diff --git a/cpp/ql/test/library-tests/defuse/test.cpp b/cpp/ql/test/library-tests/defuse/test.cpp new file mode 100644 index 000000000000..b62dc6fd23c8 --- /dev/null +++ b/cpp/ql/test/library-tests/defuse/test.cpp @@ -0,0 +1,192 @@ +void use(int x); +int puts(const char* str); +void test1(int a0, int b0, int c0) { + int a, b, c; + a = a0; + b = b0; + c = c0; + + use(a); + use(b); + use(c); + + a = b; + use(a); + use(b); + use(c); + + if (a < 0) { + a = 1; + } else { + b = 1; + } + + use(a); + use(b); + use(c); + + int d = a; // `a` is both a use of `a` and a def of `d` + use(d); + + int e = d++; // `d++` is both a def of `d` and a def of `e` + e = d++; + use(d); + use(e); +} + +void assigns0(int& x); + +void assigns1(int& x) { + x = 42; +} + +void assigns2(int* x) { + int *y = x; *y = 42; +} + +void assigns3(int* x) { + assigns0(*x); +} + +void test2() { + int x = 0; + assigns0(x); + use(x); +} + +void test3() { + int x = 0; + assigns1(x); + use(x); +} + +void test4() { + int x = 0; + assigns2(&x); + use(x); +} + +void test5() { + int x = 0; + assigns3(&x); + use(x); +} + +void nonAssigns0(int& x) { } + +void nonAssigns1(int* x) { } + +void test6() { + int x = 0; + nonAssigns0(x); + use(x); +} + +void test7() { + int x = 0; + nonAssigns1(&x); + use(x); +} + +void test8() { + int x = 0; + for (int i = 0; i < 2; i++) { + use(x); + x = 3; + } + use(x); +} + +void test9() { + int x = 0; + bool done = false; + while (!done) { + use(x); + x = 3; + done = true; + } + use(x); +} + +void test10() { + int x = 0; + for (int i = 0; i < 2; i++) { + use(x); + x = 3; + } + use(x); + bool done = false; + while (!done) { + use(x); + x = 3; + done = true; + } + use(x); +} + +void test11() { + int x = 0; + for (int i = 0; i < 2; i++) { + use(x); + x = 3; + bool done = false; + while (!done) { + use(x); + x = 3; + done = true; + } + use(x); + } + use(x); +} + +void test12() { + int x = 0; + int* y = &x; + *y = 1; + use(x); +} + +void test13() { + int x = 0; + int& y = x; + use(x); + y = 1; + use(x); +} + +void test14(int x) { + use(x); + x = 42; + use(x); +} + +void reads_const_ref(const int &x) { + use(x); +} + +void reads_const_ptr(const int *x) { + use(*x); +} + +void test15(int x) { + reads_const_ref(x); + reads_const_ptr(&x); + use(x); +} + +struct S { + int x_; + struct Nested { + int *yptr_, z_; + static int static_y; + Nested(int *yptr, int z) : yptr_(yptr), z_(z) {} + Nested(int z) : yptr_(&static_y), z_(z) {} + } nested; +}; + +S f(int x, int z) { + static int y; + S s = { x, { &y, z } }; + return s; +} diff --git a/cpp/ql/test/library-tests/defuse/useOfVar.expected b/cpp/ql/test/library-tests/defuse/useOfVar.expected new file mode 100644 index 000000000000..6d86bd3af25a --- /dev/null +++ b/cpp/ql/test/library-tests/defuse/useOfVar.expected @@ -0,0 +1,192 @@ +| addressOf.cpp:23:9:23:9 | i | addressOf.cpp:24:32:24:32 | i | +| addressOf.cpp:23:9:23:9 | i | addressOf.cpp:25:26:25:26 | i | +| addressOf.cpp:23:9:23:9 | i | addressOf.cpp:26:32:26:32 | i | +| addressOf.cpp:31:23:31:23 | i | addressOf.cpp:32:19:32:19 | i | +| addressOf.cpp:31:23:31:23 | i | addressOf.cpp:34:18:34:18 | i | +| addressOf.cpp:31:23:31:23 | i | addressOf.cpp:34:23:34:23 | i | +| addressOf.cpp:31:23:31:23 | i | addressOf.cpp:35:23:35:23 | i | +| addressOf.cpp:31:23:31:23 | i | addressOf.cpp:36:22:36:22 | i | +| addressOf.cpp:31:23:31:23 | i | addressOf.cpp:37:20:37:20 | i | +| addressOf.cpp:31:23:31:23 | i | addressOf.cpp:38:20:38:20 | i | +| addressOf.cpp:31:23:31:23 | i | addressOf.cpp:40:15:40:15 | i | +| addressOf.cpp:40:8:40:11 | iref | addressOf.cpp:42:19:42:22 | iref | +| addressOf.cpp:46:17:46:24 | captured | addressOf.cpp:49:15:49:22 | captured | +| addressOf.cpp:46:17:46:24 | captured | addressOf.cpp:51:10:51:17 | captured | +| addressOf.cpp:46:17:46:24 | captured | file://:0:0:0:0 | captured | +| addressOf.cpp:47:8:47:9 | f1 | addressOf.cpp:48:3:48:4 | f1 | +| addressOf.cpp:47:8:47:9 | f1 | addressOf.cpp:50:3:50:4 | f1 | +| addressOf.cpp:55:17:55:17 | i | addressOf.cpp:56:16:56:16 | i | +| addressOf.cpp:55:17:55:17 | i | addressOf.cpp:56:19:56:19 | i | +| addressOf.cpp:55:17:55:17 | i | addressOf.cpp:56:24:56:24 | i | +| addressOf.cpp:56:7:56:7 | a | addressOf.cpp:57:19:57:19 | a | +| addressOf.cpp:56:7:56:7 | a | addressOf.cpp:58:18:58:18 | a | +| addressOf.cpp:61:23:61:25 | ptr | addressOf.cpp:62:11:62:13 | ptr | +| addressOf.cpp:61:23:61:25 | ptr | addressOf.cpp:63:19:63:21 | ptr | +| addressOf.cpp:61:33:61:35 | ref | addressOf.cpp:63:24:63:26 | ref | +| indirect_use.cpp:19:31:19:32 | ip | indirect_use.cpp:20:14:20:15 | ip | +| indirect_use.cpp:20:10:20:10 | p | indirect_use.cpp:21:17:21:17 | p | +| indirect_use.cpp:24:31:24:32 | ip | indirect_use.cpp:25:14:25:15 | ip | +| indirect_use.cpp:25:10:25:10 | p | indirect_use.cpp:26:5:26:5 | p | +| indirect_use.cpp:25:10:25:10 | p | indirect_use.cpp:27:17:27:17 | p | +| indirect_use.cpp:30:28:30:30 | ppp | indirect_use.cpp:31:19:31:21 | ppp | +| indirect_use.cpp:34:31:34:32 | ip | indirect_use.cpp:35:14:35:15 | ip | +| indirect_use.cpp:35:10:35:10 | p | indirect_use.cpp:36:17:36:17 | p | +| indirect_use.cpp:36:11:36:12 | pp | indirect_use.cpp:37:14:37:15 | pp | +| indirect_use.cpp:40:24:40:25 | ip | indirect_use.cpp:41:18:41:19 | ip | +| indirect_use.cpp:44:24:44:25 | ip | indirect_use.cpp:45:16:45:17 | ip | +| indirect_use.cpp:48:29:48:36 | argCount | indirect_use.cpp:51:28:51:35 | argCount | +| indirect_use.cpp:48:29:48:36 | argCount | indirect_use.cpp:52:25:52:32 | argCount | +| indirect_use.cpp:49:23:49:24 | ap | indirect_use.cpp:51:24:51:25 | ap | +| indirect_use.cpp:49:23:49:24 | ap | indirect_use.cpp:53:35:53:36 | ap | +| indirect_use.cpp:49:23:49:24 | ap | indirect_use.cpp:56:22:56:23 | ap | +| indirect_use.cpp:52:14:52:14 | i | indirect_use.cpp:52:21:52:21 | i | +| indirect_use.cpp:52:14:52:14 | i | indirect_use.cpp:52:35:52:35 | i | +| indirect_use.cpp:53:14:53:14 | p | indirect_use.cpp:54:23:54:23 | p | +| indirect_use.cpp:59:27:59:27 | i | indirect_use.cpp:60:15:60:15 | i | +| indirect_use.cpp:60:10:60:10 | p | indirect_use.cpp:61:5:61:5 | p | +| indirect_use.cpp:60:10:60:10 | p | indirect_use.cpp:62:17:62:17 | p | +| indirect_use.cpp:65:27:65:27 | i | indirect_use.cpp:69:16:69:16 | i | +| indirect_use.cpp:72:27:72:27 | i | indirect_use.cpp:73:14:73:14 | i | +| indirect_use.cpp:76:31:76:32 | ip | indirect_use.cpp:77:26:77:27 | ip | +| indirect_use.cpp:80:41:80:42 | ip | indirect_use.cpp:82:21:82:22 | ip | +| indirect_use.cpp:90:33:90:34 | ip | indirect_use.cpp:93:24:93:25 | ip | +| indirect_use.cpp:90:33:90:34 | ip | indirect_use.cpp:94:24:94:25 | ip | +| indirect_use.cpp:98:32:98:33 | ip | indirect_use.cpp:99:6:99:7 | ip | +| indirect_use.cpp:102:32:102:33 | ip | indirect_use.cpp:103:5:103:6 | ip | +| indirect_use.cpp:106:32:106:33 | ip | indirect_use.cpp:107:6:107:7 | ip | +| indirect_use.cpp:106:32:106:33 | ip | indirect_use.cpp:108:17:108:18 | ip | +| indirect_use.cpp:111:28:111:28 | i | indirect_use.cpp:113:16:113:16 | i | +| indirect_use.cpp:116:32:116:33 | ip | indirect_use.cpp:117:5:117:6 | ip | +| indirect_use.cpp:116:32:116:33 | ip | indirect_use.cpp:118:5:118:6 | ip | +| indirect_use.cpp:116:32:116:33 | ip | indirect_use.cpp:119:5:119:6 | ip | +| indirect_use.cpp:116:32:116:33 | ip | indirect_use.cpp:120:17:120:18 | ip | +| indirect_use.cpp:138:28:138:29 | ns | indirect_use.cpp:139:19:139:20 | ns | +| indirect_use.cpp:142:28:142:29 | ns | indirect_use.cpp:143:16:143:17 | ns | +| indirect_use.cpp:146:29:146:30 | ns | indirect_use.cpp:147:5:147:6 | ns | +| indirect_use.cpp:150:29:150:30 | ns | indirect_use.cpp:151:5:151:6 | ns | +| indirect_use.cpp:154:16:154:21 | object | indirect_use.cpp:175:5:175:10 | object | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:156:19:156:19 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:157:19:157:19 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:158:19:158:19 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:159:19:159:19 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:160:19:160:19 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:161:28:161:28 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:162:28:162:28 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:163:14:163:14 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:164:14:164:14 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:165:14:165:14 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:166:19:166:19 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:167:29:167:29 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:168:21:168:21 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:169:21:169:21 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:170:20:170:20 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:171:20:171:20 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:172:20:172:20 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:173:20:173:20 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:174:15:174:15 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:175:15:175:15 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:176:22:176:22 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:177:24:177:24 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:178:31:178:31 | i | +| indirect_use.cpp:180:18:180:19 | ns | indirect_use.cpp:181:14:181:15 | ns | +| indirect_use.cpp:180:18:180:19 | ns | indirect_use.cpp:182:14:182:15 | ns | +| indirect_use.cpp:180:18:180:19 | ns | indirect_use.cpp:183:15:183:16 | ns | +| indirect_use.cpp:180:18:180:19 | ns | indirect_use.cpp:184:14:184:15 | ns | +| pass_by_ref.cpp:9:16:9:16 | n | pass_by_ref.cpp:18:12:18:12 | n | +| pass_by_ref.cpp:9:16:9:16 | n | pass_by_ref.cpp:21:12:21:12 | n | +| pass_by_ref.cpp:10:7:10:24 | uninitializedArray | pass_by_ref.cpp:12:18:12:35 | uninitializedArray | +| pass_by_ref.cpp:10:7:10:24 | uninitializedArray | pass_by_ref.cpp:13:20:13:37 | uninitializedArray | +| pass_by_ref.cpp:10:7:10:24 | uninitializedArray | pass_by_ref.cpp:15:23:15:40 | uninitializedArray | +| pass_by_ref.cpp:10:7:10:24 | uninitializedArray | pass_by_ref.cpp:16:25:16:42 | uninitializedArray | +| pass_by_ref.cpp:10:7:10:24 | uninitializedArray | pass_by_ref.cpp:27:10:27:27 | uninitializedArray | +| pass_by_ref.cpp:18:7:18:8 | i1 | pass_by_ref.cpp:19:22:19:23 | i1 | +| pass_by_ref.cpp:18:7:18:8 | i1 | pass_by_ref.cpp:27:34:27:35 | i1 | +| pass_by_ref.cpp:21:7:21:8 | i2 | pass_by_ref.cpp:22:21:22:22 | i2 | +| pass_by_ref.cpp:21:7:21:8 | i2 | pass_by_ref.cpp:24:26:24:27 | i2 | +| pass_by_ref.cpp:21:7:21:8 | i2 | pass_by_ref.cpp:25:27:25:28 | i2 | +| pass_by_ref.cpp:21:7:21:8 | i2 | pass_by_ref.cpp:27:39:27:40 | i2 | +| pass_by_ref.cpp:30:15:30:15 | n | pass_by_ref.cpp:32:12:32:12 | n | +| pass_by_ref.cpp:31:7:31:9 | arr | pass_by_ref.cpp:33:20:33:22 | arr | +| pass_by_ref.cpp:36:16:36:16 | n | pass_by_ref.cpp:39:12:39:12 | n | +| pass_by_ref.cpp:37:7:37:9 | arr | pass_by_ref.cpp:40:22:40:24 | arr | +| pass_by_ref.cpp:37:7:37:9 | arr | pass_by_ref.cpp:41:20:41:22 | arr | +| pass_by_ref.cpp:45:17:45:17 | n | pass_by_ref.cpp:46:11:46:11 | n | +| pass_by_ref.cpp:45:17:45:17 | n | pass_by_ref.cpp:48:7:48:7 | n | +| pass_by_ref.cpp:46:7:46:7 | i | pass_by_ref.cpp:49:5:49:5 | i | +| pass_by_ref.cpp:46:7:46:7 | i | pass_by_ref.cpp:53:22:53:22 | i | +| pass_by_ref.cpp:46:7:46:7 | i | pass_by_ref.cpp:54:10:54:10 | i | +| test.cpp:3:16:3:17 | a0 | test.cpp:5:7:5:8 | a0 | +| test.cpp:3:24:3:25 | b0 | test.cpp:6:7:6:8 | b0 | +| test.cpp:3:32:3:33 | c0 | test.cpp:7:7:7:8 | c0 | +| test.cpp:4:7:4:7 | a | test.cpp:9:7:9:7 | a | +| test.cpp:4:7:4:7 | a | test.cpp:14:7:14:7 | a | +| test.cpp:4:7:4:7 | a | test.cpp:18:7:18:7 | a | +| test.cpp:4:7:4:7 | a | test.cpp:24:7:24:7 | a | +| test.cpp:4:7:4:7 | a | test.cpp:28:11:28:11 | a | +| test.cpp:4:10:4:10 | b | test.cpp:10:7:10:7 | b | +| test.cpp:4:10:4:10 | b | test.cpp:13:7:13:7 | b | +| test.cpp:4:10:4:10 | b | test.cpp:15:7:15:7 | b | +| test.cpp:4:10:4:10 | b | test.cpp:25:7:25:7 | b | +| test.cpp:4:13:4:13 | c | test.cpp:11:7:11:7 | c | +| test.cpp:4:13:4:13 | c | test.cpp:16:7:16:7 | c | +| test.cpp:4:13:4:13 | c | test.cpp:26:7:26:7 | c | +| test.cpp:28:7:28:7 | d | test.cpp:29:7:29:7 | d | +| test.cpp:28:7:28:7 | d | test.cpp:31:11:31:11 | d | +| test.cpp:28:7:28:7 | d | test.cpp:32:7:32:7 | d | +| test.cpp:28:7:28:7 | d | test.cpp:33:7:33:7 | d | +| test.cpp:31:7:31:7 | e | test.cpp:34:7:34:7 | e | +| test.cpp:43:20:43:20 | x | test.cpp:44:12:44:12 | x | +| test.cpp:44:8:44:8 | y | test.cpp:44:16:44:16 | y | +| test.cpp:47:20:47:20 | x | test.cpp:48:13:48:13 | x | +| test.cpp:52:7:52:7 | x | test.cpp:53:12:53:12 | x | +| test.cpp:52:7:52:7 | x | test.cpp:54:7:54:7 | x | +| test.cpp:58:7:58:7 | x | test.cpp:59:12:59:12 | x | +| test.cpp:58:7:58:7 | x | test.cpp:60:7:60:7 | x | +| test.cpp:64:7:64:7 | x | test.cpp:65:13:65:13 | x | +| test.cpp:64:7:64:7 | x | test.cpp:66:7:66:7 | x | +| test.cpp:70:7:70:7 | x | test.cpp:71:13:71:13 | x | +| test.cpp:70:7:70:7 | x | test.cpp:72:7:72:7 | x | +| test.cpp:80:7:80:7 | x | test.cpp:81:15:81:15 | x | +| test.cpp:80:7:80:7 | x | test.cpp:82:7:82:7 | x | +| test.cpp:86:7:86:7 | x | test.cpp:87:16:87:16 | x | +| test.cpp:86:7:86:7 | x | test.cpp:88:7:88:7 | x | +| test.cpp:92:7:92:7 | x | test.cpp:94:9:94:9 | x | +| test.cpp:92:7:92:7 | x | test.cpp:97:7:97:7 | x | +| test.cpp:93:12:93:12 | i | test.cpp:93:19:93:19 | i | +| test.cpp:93:12:93:12 | i | test.cpp:93:26:93:26 | i | +| test.cpp:101:7:101:7 | x | test.cpp:104:9:104:9 | x | +| test.cpp:101:7:101:7 | x | test.cpp:108:7:108:7 | x | +| test.cpp:102:8:102:11 | done | test.cpp:103:11:103:14 | done | +| test.cpp:112:7:112:7 | x | test.cpp:114:9:114:9 | x | +| test.cpp:112:7:112:7 | x | test.cpp:117:7:117:7 | x | +| test.cpp:112:7:112:7 | x | test.cpp:120:9:120:9 | x | +| test.cpp:112:7:112:7 | x | test.cpp:124:7:124:7 | x | +| test.cpp:113:12:113:12 | i | test.cpp:113:19:113:19 | i | +| test.cpp:113:12:113:12 | i | test.cpp:113:26:113:26 | i | +| test.cpp:118:8:118:11 | done | test.cpp:119:11:119:14 | done | +| test.cpp:128:7:128:7 | x | test.cpp:130:9:130:9 | x | +| test.cpp:128:7:128:7 | x | test.cpp:134:11:134:11 | x | +| test.cpp:128:7:128:7 | x | test.cpp:138:9:138:9 | x | +| test.cpp:128:7:128:7 | x | test.cpp:140:7:140:7 | x | +| test.cpp:129:12:129:12 | i | test.cpp:129:19:129:19 | i | +| test.cpp:129:12:129:12 | i | test.cpp:129:26:129:26 | i | +| test.cpp:132:10:132:13 | done | test.cpp:133:13:133:16 | done | +| test.cpp:144:7:144:7 | x | test.cpp:145:13:145:13 | x | +| test.cpp:144:7:144:7 | x | test.cpp:147:7:147:7 | x | +| test.cpp:145:8:145:8 | y | test.cpp:146:4:146:4 | y | +| test.cpp:151:7:151:7 | x | test.cpp:152:12:152:12 | x | +| test.cpp:151:7:151:7 | x | test.cpp:153:7:153:7 | x | +| test.cpp:151:7:151:7 | x | test.cpp:155:7:155:7 | x | +| test.cpp:158:17:158:17 | x | test.cpp:159:7:159:7 | x | +| test.cpp:158:17:158:17 | x | test.cpp:161:7:161:7 | x | +| test.cpp:164:33:164:33 | x | test.cpp:165:7:165:7 | x | +| test.cpp:168:33:168:33 | x | test.cpp:169:8:169:8 | x | +| test.cpp:172:17:172:17 | x | test.cpp:173:19:173:19 | x | +| test.cpp:172:17:172:17 | x | test.cpp:174:20:174:20 | x | +| test.cpp:172:17:172:17 | x | test.cpp:175:7:175:7 | x | +| test.cpp:183:17:183:20 | yptr | test.cpp:183:38:183:41 | yptr | +| test.cpp:183:27:183:27 | z | test.cpp:183:48:183:48 | z | +| test.cpp:184:16:184:16 | z | test.cpp:184:42:184:42 | z | +| test.cpp:188:9:188:9 | x | test.cpp:190:11:190:11 | x | +| test.cpp:188:16:188:16 | z | test.cpp:190:20:190:20 | z | +| test.cpp:190:5:190:5 | s | test.cpp:191:10:191:10 | s | diff --git a/cpp/ql/test/library-tests/defuse/useOfVar.ql b/cpp/ql/test/library-tests/defuse/useOfVar.ql new file mode 100644 index 000000000000..c68a1d6d19a2 --- /dev/null +++ b/cpp/ql/test/library-tests/defuse/useOfVar.ql @@ -0,0 +1,5 @@ +import cpp + +from LocalScopeVariable v, VariableAccess use +where useOfVar(v, use) +select v, use diff --git a/cpp/ql/test/library-tests/defuse/useOfVarActual.expected b/cpp/ql/test/library-tests/defuse/useOfVarActual.expected new file mode 100644 index 000000000000..3f7921a57232 --- /dev/null +++ b/cpp/ql/test/library-tests/defuse/useOfVarActual.expected @@ -0,0 +1,121 @@ +| addressOf.cpp:31:23:31:23 | i | addressOf.cpp:34:18:34:18 | i | +| addressOf.cpp:31:23:31:23 | i | addressOf.cpp:36:22:36:22 | i | +| addressOf.cpp:31:23:31:23 | i | addressOf.cpp:37:20:37:20 | i | +| addressOf.cpp:31:23:31:23 | i | addressOf.cpp:38:20:38:20 | i | +| addressOf.cpp:46:17:46:24 | captured | addressOf.cpp:51:10:51:17 | captured | +| addressOf.cpp:47:8:47:9 | f1 | addressOf.cpp:48:3:48:4 | f1 | +| addressOf.cpp:47:8:47:9 | f1 | addressOf.cpp:50:3:50:4 | f1 | +| addressOf.cpp:55:17:55:17 | i | addressOf.cpp:56:16:56:16 | i | +| addressOf.cpp:55:17:55:17 | i | addressOf.cpp:56:19:56:19 | i | +| addressOf.cpp:55:17:55:17 | i | addressOf.cpp:56:24:56:24 | i | +| addressOf.cpp:61:23:61:25 | ptr | addressOf.cpp:62:11:62:13 | ptr | +| addressOf.cpp:61:23:61:25 | ptr | addressOf.cpp:63:19:63:21 | ptr | +| indirect_use.cpp:19:31:19:32 | ip | indirect_use.cpp:20:14:20:15 | ip | +| indirect_use.cpp:20:10:20:10 | p | indirect_use.cpp:21:17:21:17 | p | +| indirect_use.cpp:24:31:24:32 | ip | indirect_use.cpp:25:14:25:15 | ip | +| indirect_use.cpp:25:10:25:10 | p | indirect_use.cpp:26:5:26:5 | p | +| indirect_use.cpp:25:10:25:10 | p | indirect_use.cpp:27:17:27:17 | p | +| indirect_use.cpp:30:28:30:30 | ppp | indirect_use.cpp:31:19:31:21 | ppp | +| indirect_use.cpp:34:31:34:32 | ip | indirect_use.cpp:35:14:35:15 | ip | +| indirect_use.cpp:40:24:40:25 | ip | indirect_use.cpp:41:18:41:19 | ip | +| indirect_use.cpp:44:24:44:25 | ip | indirect_use.cpp:45:16:45:17 | ip | +| indirect_use.cpp:48:29:48:36 | argCount | indirect_use.cpp:51:28:51:35 | argCount | +| indirect_use.cpp:48:29:48:36 | argCount | indirect_use.cpp:52:25:52:32 | argCount | +| indirect_use.cpp:52:14:52:14 | i | indirect_use.cpp:52:21:52:21 | i | +| indirect_use.cpp:52:14:52:14 | i | indirect_use.cpp:52:35:52:35 | i | +| indirect_use.cpp:53:14:53:14 | p | indirect_use.cpp:54:23:54:23 | p | +| indirect_use.cpp:60:10:60:10 | p | indirect_use.cpp:61:5:61:5 | p | +| indirect_use.cpp:60:10:60:10 | p | indirect_use.cpp:62:17:62:17 | p | +| indirect_use.cpp:65:27:65:27 | i | indirect_use.cpp:69:16:69:16 | i | +| indirect_use.cpp:76:31:76:32 | ip | indirect_use.cpp:77:26:77:27 | ip | +| indirect_use.cpp:80:41:80:42 | ip | indirect_use.cpp:82:21:82:22 | ip | +| indirect_use.cpp:90:33:90:34 | ip | indirect_use.cpp:94:24:94:25 | ip | +| indirect_use.cpp:98:32:98:33 | ip | indirect_use.cpp:99:6:99:7 | ip | +| indirect_use.cpp:102:32:102:33 | ip | indirect_use.cpp:103:5:103:6 | ip | +| indirect_use.cpp:106:32:106:33 | ip | indirect_use.cpp:107:6:107:7 | ip | +| indirect_use.cpp:106:32:106:33 | ip | indirect_use.cpp:108:17:108:18 | ip | +| indirect_use.cpp:111:28:111:28 | i | indirect_use.cpp:113:16:113:16 | i | +| indirect_use.cpp:116:32:116:33 | ip | indirect_use.cpp:117:5:117:6 | ip | +| indirect_use.cpp:116:32:116:33 | ip | indirect_use.cpp:118:5:118:6 | ip | +| indirect_use.cpp:116:32:116:33 | ip | indirect_use.cpp:119:5:119:6 | ip | +| indirect_use.cpp:116:32:116:33 | ip | indirect_use.cpp:120:17:120:18 | ip | +| indirect_use.cpp:138:28:138:29 | ns | indirect_use.cpp:139:19:139:20 | ns | +| indirect_use.cpp:142:28:142:29 | ns | indirect_use.cpp:143:16:143:17 | ns | +| indirect_use.cpp:146:29:146:30 | ns | indirect_use.cpp:147:5:147:6 | ns | +| indirect_use.cpp:154:16:154:21 | object | indirect_use.cpp:175:5:175:10 | object | +| pass_by_ref.cpp:9:16:9:16 | n | pass_by_ref.cpp:18:12:18:12 | n | +| pass_by_ref.cpp:9:16:9:16 | n | pass_by_ref.cpp:21:12:21:12 | n | +| pass_by_ref.cpp:10:7:10:24 | uninitializedArray | pass_by_ref.cpp:27:10:27:27 | uninitializedArray | +| pass_by_ref.cpp:18:7:18:8 | i1 | pass_by_ref.cpp:27:34:27:35 | i1 | +| pass_by_ref.cpp:21:7:21:8 | i2 | pass_by_ref.cpp:27:39:27:40 | i2 | +| pass_by_ref.cpp:30:15:30:15 | n | pass_by_ref.cpp:32:12:32:12 | n | +| pass_by_ref.cpp:36:16:36:16 | n | pass_by_ref.cpp:39:12:39:12 | n | +| pass_by_ref.cpp:45:17:45:17 | n | pass_by_ref.cpp:46:11:46:11 | n | +| pass_by_ref.cpp:45:17:45:17 | n | pass_by_ref.cpp:48:7:48:7 | n | +| pass_by_ref.cpp:46:7:46:7 | i | pass_by_ref.cpp:49:5:49:5 | i | +| pass_by_ref.cpp:46:7:46:7 | i | pass_by_ref.cpp:54:10:54:10 | i | +| test.cpp:3:16:3:17 | a0 | test.cpp:5:7:5:8 | a0 | +| test.cpp:3:24:3:25 | b0 | test.cpp:6:7:6:8 | b0 | +| test.cpp:3:32:3:33 | c0 | test.cpp:7:7:7:8 | c0 | +| test.cpp:4:7:4:7 | a | test.cpp:9:7:9:7 | a | +| test.cpp:4:7:4:7 | a | test.cpp:14:7:14:7 | a | +| test.cpp:4:7:4:7 | a | test.cpp:18:7:18:7 | a | +| test.cpp:4:7:4:7 | a | test.cpp:24:7:24:7 | a | +| test.cpp:4:7:4:7 | a | test.cpp:28:11:28:11 | a | +| test.cpp:4:10:4:10 | b | test.cpp:10:7:10:7 | b | +| test.cpp:4:10:4:10 | b | test.cpp:13:7:13:7 | b | +| test.cpp:4:10:4:10 | b | test.cpp:15:7:15:7 | b | +| test.cpp:4:10:4:10 | b | test.cpp:25:7:25:7 | b | +| test.cpp:4:13:4:13 | c | test.cpp:11:7:11:7 | c | +| test.cpp:4:13:4:13 | c | test.cpp:16:7:16:7 | c | +| test.cpp:4:13:4:13 | c | test.cpp:26:7:26:7 | c | +| test.cpp:28:7:28:7 | d | test.cpp:29:7:29:7 | d | +| test.cpp:28:7:28:7 | d | test.cpp:31:11:31:11 | d | +| test.cpp:28:7:28:7 | d | test.cpp:32:7:32:7 | d | +| test.cpp:28:7:28:7 | d | test.cpp:33:7:33:7 | d | +| test.cpp:31:7:31:7 | e | test.cpp:34:7:34:7 | e | +| test.cpp:43:20:43:20 | x | test.cpp:44:12:44:12 | x | +| test.cpp:44:8:44:8 | y | test.cpp:44:16:44:16 | y | +| test.cpp:47:20:47:20 | x | test.cpp:48:13:48:13 | x | +| test.cpp:52:7:52:7 | x | test.cpp:54:7:54:7 | x | +| test.cpp:58:7:58:7 | x | test.cpp:60:7:60:7 | x | +| test.cpp:64:7:64:7 | x | test.cpp:66:7:66:7 | x | +| test.cpp:70:7:70:7 | x | test.cpp:72:7:72:7 | x | +| test.cpp:80:7:80:7 | x | test.cpp:82:7:82:7 | x | +| test.cpp:86:7:86:7 | x | test.cpp:88:7:88:7 | x | +| test.cpp:92:7:92:7 | x | test.cpp:94:9:94:9 | x | +| test.cpp:92:7:92:7 | x | test.cpp:97:7:97:7 | x | +| test.cpp:93:12:93:12 | i | test.cpp:93:19:93:19 | i | +| test.cpp:93:12:93:12 | i | test.cpp:93:26:93:26 | i | +| test.cpp:101:7:101:7 | x | test.cpp:104:9:104:9 | x | +| test.cpp:101:7:101:7 | x | test.cpp:108:7:108:7 | x | +| test.cpp:102:8:102:11 | done | test.cpp:103:11:103:14 | done | +| test.cpp:112:7:112:7 | x | test.cpp:114:9:114:9 | x | +| test.cpp:112:7:112:7 | x | test.cpp:117:7:117:7 | x | +| test.cpp:112:7:112:7 | x | test.cpp:120:9:120:9 | x | +| test.cpp:112:7:112:7 | x | test.cpp:124:7:124:7 | x | +| test.cpp:113:12:113:12 | i | test.cpp:113:19:113:19 | i | +| test.cpp:113:12:113:12 | i | test.cpp:113:26:113:26 | i | +| test.cpp:118:8:118:11 | done | test.cpp:119:11:119:14 | done | +| test.cpp:128:7:128:7 | x | test.cpp:130:9:130:9 | x | +| test.cpp:128:7:128:7 | x | test.cpp:134:11:134:11 | x | +| test.cpp:128:7:128:7 | x | test.cpp:138:9:138:9 | x | +| test.cpp:128:7:128:7 | x | test.cpp:140:7:140:7 | x | +| test.cpp:129:12:129:12 | i | test.cpp:129:19:129:19 | i | +| test.cpp:129:12:129:12 | i | test.cpp:129:26:129:26 | i | +| test.cpp:132:10:132:13 | done | test.cpp:133:13:133:16 | done | +| test.cpp:144:7:144:7 | x | test.cpp:147:7:147:7 | x | +| test.cpp:145:8:145:8 | y | test.cpp:146:4:146:4 | y | +| test.cpp:151:7:151:7 | x | test.cpp:153:7:153:7 | x | +| test.cpp:151:7:151:7 | x | test.cpp:155:7:155:7 | x | +| test.cpp:158:17:158:17 | x | test.cpp:159:7:159:7 | x | +| test.cpp:158:17:158:17 | x | test.cpp:161:7:161:7 | x | +| test.cpp:164:33:164:33 | x | test.cpp:165:7:165:7 | x | +| test.cpp:168:33:168:33 | x | test.cpp:169:8:169:8 | x | +| test.cpp:172:17:172:17 | x | test.cpp:175:7:175:7 | x | +| test.cpp:183:17:183:20 | yptr | test.cpp:183:38:183:41 | yptr | +| test.cpp:183:27:183:27 | z | test.cpp:183:48:183:48 | z | +| test.cpp:184:16:184:16 | z | test.cpp:184:42:184:42 | z | +| test.cpp:188:9:188:9 | x | test.cpp:190:11:190:11 | x | +| test.cpp:188:16:188:16 | z | test.cpp:190:20:190:20 | z | +| test.cpp:190:5:190:5 | s | test.cpp:191:10:191:10 | s | diff --git a/cpp/ql/test/library-tests/defuse/useOfVarActual.ql b/cpp/ql/test/library-tests/defuse/useOfVarActual.ql new file mode 100644 index 000000000000..ea99f0f5639c --- /dev/null +++ b/cpp/ql/test/library-tests/defuse/useOfVarActual.ql @@ -0,0 +1,8 @@ +import cpp + +from LocalScopeVariable v, VariableAccess use +where useOfVarActual(v, use) +// Also check that `useOfVarActual` is a subset of `useOfVar`; if not +// the query will not return any results +and forall(LocalScopeVariable v0, VariableAccess use0 | useOfVarActual(v0, use0) | useOfVar(v0, use0)) +select v, use \ No newline at end of file diff --git a/cpp/ql/test/library-tests/defuse/useUsePair.expected b/cpp/ql/test/library-tests/defuse/useUsePair.expected new file mode 100644 index 000000000000..2c0bbd44a39d --- /dev/null +++ b/cpp/ql/test/library-tests/defuse/useUsePair.expected @@ -0,0 +1,55 @@ +| addressOf.cpp:31:23:31:23 | i | addressOf.cpp:34:18:34:18 | i | addressOf.cpp:34:23:34:23 | i | +| addressOf.cpp:31:23:31:23 | i | addressOf.cpp:34:18:34:18 | i | addressOf.cpp:35:23:35:23 | i | +| addressOf.cpp:31:23:31:23 | i | addressOf.cpp:34:23:34:23 | i | addressOf.cpp:35:23:35:23 | i | +| addressOf.cpp:47:8:47:9 | f1 | addressOf.cpp:48:3:48:4 | f1 | addressOf.cpp:50:3:50:4 | f1 | +| addressOf.cpp:55:17:55:17 | i | addressOf.cpp:56:16:56:16 | i | addressOf.cpp:56:19:56:19 | i | +| addressOf.cpp:55:17:55:17 | i | addressOf.cpp:56:16:56:16 | i | addressOf.cpp:56:24:56:24 | i | +| addressOf.cpp:55:17:55:17 | i | addressOf.cpp:56:19:56:19 | i | addressOf.cpp:56:24:56:24 | i | +| addressOf.cpp:61:23:61:25 | ptr | addressOf.cpp:62:11:62:13 | ptr | addressOf.cpp:63:19:63:21 | ptr | +| indirect_use.cpp:48:29:48:36 | argCount | indirect_use.cpp:51:28:51:35 | argCount | indirect_use.cpp:52:25:52:32 | argCount | +| indirect_use.cpp:48:29:48:36 | argCount | indirect_use.cpp:52:25:52:32 | argCount | indirect_use.cpp:52:25:52:32 | argCount | +| indirect_use.cpp:49:23:49:24 | ap | indirect_use.cpp:51:24:51:25 | ap | indirect_use.cpp:53:35:53:36 | ap | +| indirect_use.cpp:49:23:49:24 | ap | indirect_use.cpp:51:24:51:25 | ap | indirect_use.cpp:56:22:56:23 | ap | +| indirect_use.cpp:49:23:49:24 | ap | indirect_use.cpp:53:35:53:36 | ap | indirect_use.cpp:53:35:53:36 | ap | +| indirect_use.cpp:49:23:49:24 | ap | indirect_use.cpp:53:35:53:36 | ap | indirect_use.cpp:56:22:56:23 | ap | +| indirect_use.cpp:52:14:52:14 | i | indirect_use.cpp:52:21:52:21 | i | indirect_use.cpp:52:35:52:35 | i | +| indirect_use.cpp:90:33:90:34 | ip | indirect_use.cpp:93:24:93:25 | ip | indirect_use.cpp:94:24:94:25 | ip | +| indirect_use.cpp:106:32:106:33 | ip | indirect_use.cpp:107:6:107:7 | ip | indirect_use.cpp:108:17:108:18 | ip | +| indirect_use.cpp:116:32:116:33 | ip | indirect_use.cpp:117:5:117:6 | ip | indirect_use.cpp:118:5:118:6 | ip | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:168:21:168:21 | i | indirect_use.cpp:169:21:169:21 | i | +| indirect_use.cpp:155:9:155:9 | i | indirect_use.cpp:175:15:175:15 | i | indirect_use.cpp:176:22:176:22 | i | +| pass_by_ref.cpp:9:16:9:16 | n | pass_by_ref.cpp:18:12:18:12 | n | pass_by_ref.cpp:21:12:21:12 | n | +| pass_by_ref.cpp:10:7:10:24 | uninitializedArray | pass_by_ref.cpp:15:23:15:40 | uninitializedArray | pass_by_ref.cpp:16:25:16:42 | uninitializedArray | +| pass_by_ref.cpp:10:7:10:24 | uninitializedArray | pass_by_ref.cpp:15:23:15:40 | uninitializedArray | pass_by_ref.cpp:27:10:27:27 | uninitializedArray | +| pass_by_ref.cpp:10:7:10:24 | uninitializedArray | pass_by_ref.cpp:16:25:16:42 | uninitializedArray | pass_by_ref.cpp:27:10:27:27 | uninitializedArray | +| pass_by_ref.cpp:21:7:21:8 | i2 | pass_by_ref.cpp:24:26:24:27 | i2 | pass_by_ref.cpp:25:27:25:28 | i2 | +| pass_by_ref.cpp:21:7:21:8 | i2 | pass_by_ref.cpp:24:26:24:27 | i2 | pass_by_ref.cpp:27:39:27:40 | i2 | +| pass_by_ref.cpp:21:7:21:8 | i2 | pass_by_ref.cpp:25:27:25:28 | i2 | pass_by_ref.cpp:27:39:27:40 | i2 | +| pass_by_ref.cpp:45:17:45:17 | n | pass_by_ref.cpp:46:11:46:11 | n | pass_by_ref.cpp:48:7:48:7 | n | +| test.cpp:4:7:4:7 | a | test.cpp:14:7:14:7 | a | test.cpp:18:7:18:7 | a | +| test.cpp:4:7:4:7 | a | test.cpp:14:7:14:7 | a | test.cpp:24:7:24:7 | a | +| test.cpp:4:7:4:7 | a | test.cpp:14:7:14:7 | a | test.cpp:28:11:28:11 | a | +| test.cpp:4:7:4:7 | a | test.cpp:18:7:18:7 | a | test.cpp:24:7:24:7 | a | +| test.cpp:4:7:4:7 | a | test.cpp:18:7:18:7 | a | test.cpp:28:11:28:11 | a | +| test.cpp:4:7:4:7 | a | test.cpp:24:7:24:7 | a | test.cpp:28:11:28:11 | a | +| test.cpp:4:10:4:10 | b | test.cpp:10:7:10:7 | b | test.cpp:13:7:13:7 | b | +| test.cpp:4:10:4:10 | b | test.cpp:10:7:10:7 | b | test.cpp:15:7:15:7 | b | +| test.cpp:4:10:4:10 | b | test.cpp:10:7:10:7 | b | test.cpp:25:7:25:7 | b | +| test.cpp:4:10:4:10 | b | test.cpp:13:7:13:7 | b | test.cpp:15:7:15:7 | b | +| test.cpp:4:10:4:10 | b | test.cpp:13:7:13:7 | b | test.cpp:25:7:25:7 | b | +| test.cpp:4:10:4:10 | b | test.cpp:15:7:15:7 | b | test.cpp:25:7:25:7 | b | +| test.cpp:4:13:4:13 | c | test.cpp:11:7:11:7 | c | test.cpp:16:7:16:7 | c | +| test.cpp:4:13:4:13 | c | test.cpp:11:7:11:7 | c | test.cpp:26:7:26:7 | c | +| test.cpp:4:13:4:13 | c | test.cpp:16:7:16:7 | c | test.cpp:26:7:26:7 | c | +| test.cpp:28:7:28:7 | d | test.cpp:29:7:29:7 | d | test.cpp:31:11:31:11 | d | +| test.cpp:80:7:80:7 | x | test.cpp:81:15:81:15 | x | test.cpp:82:7:82:7 | x | +| test.cpp:86:7:86:7 | x | test.cpp:87:16:87:16 | x | test.cpp:88:7:88:7 | x | +| test.cpp:93:12:93:12 | i | test.cpp:93:19:93:19 | i | test.cpp:93:26:93:26 | i | +| test.cpp:112:7:112:7 | x | test.cpp:117:7:117:7 | x | test.cpp:120:9:120:9 | x | +| test.cpp:113:12:113:12 | i | test.cpp:113:19:113:19 | i | test.cpp:113:26:113:26 | i | +| test.cpp:128:7:128:7 | x | test.cpp:138:9:138:9 | x | test.cpp:130:9:130:9 | x | +| test.cpp:128:7:128:7 | x | test.cpp:138:9:138:9 | x | test.cpp:140:7:140:7 | x | +| test.cpp:129:12:129:12 | i | test.cpp:129:19:129:19 | i | test.cpp:129:26:129:26 | i | +| test.cpp:172:17:172:17 | x | test.cpp:173:19:173:19 | x | test.cpp:174:20:174:20 | x | +| test.cpp:172:17:172:17 | x | test.cpp:173:19:173:19 | x | test.cpp:175:7:175:7 | x | +| test.cpp:172:17:172:17 | x | test.cpp:174:20:174:20 | x | test.cpp:175:7:175:7 | x | diff --git a/cpp/ql/test/library-tests/defuse/useUsePair.ql b/cpp/ql/test/library-tests/defuse/useUsePair.ql new file mode 100644 index 000000000000..3aab1d29cd72 --- /dev/null +++ b/cpp/ql/test/library-tests/defuse/useUsePair.ql @@ -0,0 +1,5 @@ +import cpp + +from Variable v, Expr first, Expr second +where useUsePair(v, first, second) +select v, first, second diff --git a/cpp/ql/test/library-tests/depends_addressable/DependsAddressable1.expected b/cpp/ql/test/library-tests/depends_addressable/DependsAddressable1.expected new file mode 100644 index 000000000000..2600ceae4e9d --- /dev/null +++ b/cpp/ql/test/library-tests/depends_addressable/DependsAddressable1.expected @@ -0,0 +1,7 @@ +| addressable.cpp:20:3:20:5 | ptr | addressable.cpp:18:9:18:11 | ptr | +| addressable.cpp:20:10:20:10 | a | addressable.cpp:6:3:6:3 | a | +| addressable.cpp:21:3:21:5 | ptr | addressable.cpp:18:9:18:11 | ptr | +| addressable.cpp:21:10:21:11 | aa | addressable.cpp:11:4:11:5 | aa | +| addressable.cpp:23:3:23:6 | fptr | addressable.cpp:16:10:16:13 | fptr | +| addressable.cpp:24:3:24:6 | fptr | addressable.cpp:16:10:16:13 | fptr | +| addressable.cpp:25:3:25:7 | mfptr | addressable.cpp:17:16:17:20 | mfptr | diff --git a/cpp/ql/test/library-tests/depends_addressable/DependsAddressable1.ql b/cpp/ql/test/library-tests/depends_addressable/DependsAddressable1.ql new file mode 100644 index 000000000000..79cc0671d50a --- /dev/null +++ b/cpp/ql/test/library-tests/depends_addressable/DependsAddressable1.ql @@ -0,0 +1,5 @@ +import cpp + +from Expr e, Variable v +where varbind(e, v) +select e, v diff --git a/cpp/ql/test/library-tests/depends_addressable/addressable.cpp b/cpp/ql/test/library-tests/depends_addressable/addressable.cpp new file mode 100644 index 000000000000..40de1850c7ed --- /dev/null +++ b/cpp/ql/test/library-tests/depends_addressable/addressable.cpp @@ -0,0 +1,28 @@ +class A { +public: + A() {} +}; + +A a; +void f() {} + + +class Test { + A aa; + + void fa() {} + + void test() { + void (*fptr)(); + void (Test::*mfptr)(); + void *ptr; + + ptr = &a; + ptr = &aa; + + fptr = f; // same as below + fptr = &f; // same as above + mfptr = &Test::fa; + + } +}; diff --git a/cpp/ql/test/library-tests/depends_friends/Friend1.expected b/cpp/ql/test/library-tests/depends_friends/Friend1.expected new file mode 100644 index 000000000000..9099cc51c7a9 --- /dev/null +++ b/cpp/ql/test/library-tests/depends_friends/Friend1.expected @@ -0,0 +1,5 @@ +| file://:0:0:0:0 | __va_list_tag | file://:0:0:0:0 | __va_list_tag | +| friends.cpp:1:7:1:13 | Friend1 | friends.cpp:1:7:1:13 | Friend1 | +| friends.cpp:10:7:10:13 | Friend2 | friends.cpp:10:7:10:13 | Friend2 | +| friends.cpp:24:7:24:7 | C | friends.cpp:1:7:1:13 | Friend1 | +| friends.cpp:24:7:24:7 | C | friends.cpp:24:7:24:7 | C | diff --git a/cpp/ql/test/library-tests/depends_friends/Friend1.ql b/cpp/ql/test/library-tests/depends_friends/Friend1.ql new file mode 100644 index 000000000000..0841a75eb798 --- /dev/null +++ b/cpp/ql/test/library-tests/depends_friends/Friend1.ql @@ -0,0 +1,9 @@ +/** + * @name Friends1 + * @description Check that a class depends on its friend class. + */ +import cpp + +from MetricClass c, Class f1 +where c.getAClassDependency() = f1 +select c, f1 diff --git a/cpp/ql/test/library-tests/depends_friends/friends.cpp b/cpp/ql/test/library-tests/depends_friends/friends.cpp new file mode 100644 index 000000000000..1acdd763a90f --- /dev/null +++ b/cpp/ql/test/library-tests/depends_friends/friends.cpp @@ -0,0 +1,28 @@ +class Friend1 { +public: + void f(); +protected: + void g(); +private: + void h(); +}; + +class Friend2 { +public: + void f(); +protected: + void g(); +private: + void h(); +}; + +void Friend2::f() { +} + +void friendFunc() {} + +class C { + friend class Friend1; + friend void Friend2::f(); + friend void friendFunc(); +}; diff --git a/cpp/ql/test/library-tests/depends_initializers/InitializerAccesses.expected b/cpp/ql/test/library-tests/depends_initializers/InitializerAccesses.expected new file mode 100644 index 000000000000..71e0949fa52f --- /dev/null +++ b/cpp/ql/test/library-tests/depends_initializers/InitializerAccesses.expected @@ -0,0 +1,45 @@ +| initializers.cpp:18:8:18:15 | initializer for y | initializers.cpp:18:9:18:9 | x | +| initializers.cpp:19:8:19:21 | initializer for z | initializers.cpp:19:9:19:9 | x | +| initializers.cpp:23:8:23:9 | initializer for j | initializers.cpp:23:9:23:9 | i | +| initializers.cpp:27:8:27:10 | initializer for aax | initializers.cpp:27:9:27:10 | ax | +| initializers.cpp:30:20:30:35 | initializer for myIntArray | initializers.cpp:30:22:30:22 | i | +| template_static.cpp:9:24:9:24 | initializer for static_c | template_static.cpp:9:24:9:24 | c | +| template_static.cpp:10:22:10:24 | initializer for static_v | template_static.cpp:10:24:10:24 | v | +| template_static.cpp:13:15:13:16 | initializer for local_c | template_static.cpp:13:16:13:16 | c | +| template_static.cpp:14:15:14:16 | initializer for local_v | template_static.cpp:14:16:14:16 | v | +| template_static_instantiated.cpp:9:24:9:24 | initializer for static_c | template_static_instantiated.cpp:9:24:9:24 | c | +| template_static_instantiated.cpp:10:22:10:24 | initializer for static_v | template_static_instantiated.cpp:10:24:10:24 | v | +| template_static_instantiated.cpp:13:15:13:16 | initializer for local_c | template_static_instantiated.cpp:13:16:13:16 | c | +| template_static_instantiated.cpp:14:15:14:16 | initializer for local_v | template_static_instantiated.cpp:14:16:14:16 | v | +| template_static_instantiated.cpp:21:28:21:28 | initializer for static_int_c | template_static_instantiated.cpp:21:28:21:28 | c | +| template_static_instantiated.cpp:21:28:21:28 | initializer for static_int_c | template_static_instantiated.cpp:21:28:21:28 | c | +| template_static_instantiated.cpp:22:26:22:28 | initializer for static_int_v | template_static_instantiated.cpp:22:28:22:28 | v | +| template_static_instantiated.cpp:22:26:22:28 | initializer for static_int_v | template_static_instantiated.cpp:22:28:22:28 | v | +| template_static_instantiated.cpp:25:22:25:24 | initializer for static_t_c | template_static_instantiated.cpp:25:24:25:24 | c | +| template_static_instantiated.cpp:25:24:25:24 | initializer for static_t_c | template_static_instantiated.cpp:25:24:25:24 | c | +| template_static_instantiated.cpp:26:22:26:24 | initializer for static_t_v | template_static_instantiated.cpp:26:24:26:24 | v | +| template_static_instantiated.cpp:26:22:26:24 | initializer for static_t_v | template_static_instantiated.cpp:26:24:26:24 | v | +| template_static_instantiated.cpp:30:19:30:20 | initializer for local_int_c | template_static_instantiated.cpp:30:20:30:20 | c | +| template_static_instantiated.cpp:30:19:30:20 | initializer for local_int_c | template_static_instantiated.cpp:30:20:30:20 | c | +| template_static_instantiated.cpp:31:19:31:20 | initializer for local_int_v | template_static_instantiated.cpp:31:20:31:20 | v | +| template_static_instantiated.cpp:31:19:31:20 | initializer for local_int_v | template_static_instantiated.cpp:31:20:31:20 | v | +| template_static_instantiated.cpp:34:15:34:16 | initializer for local_t_c | template_static_instantiated.cpp:34:16:34:16 | c | +| template_static_instantiated.cpp:34:15:34:16 | initializer for local_t_c | template_static_instantiated.cpp:34:16:34:16 | c | +| template_static_instantiated.cpp:35:15:35:16 | initializer for local_t_v | template_static_instantiated.cpp:35:16:35:16 | v | +| template_static_instantiated.cpp:35:15:35:16 | initializer for local_t_v | template_static_instantiated.cpp:35:16:35:16 | v | +| template_static_instantiated.cpp:45:29:45:29 | initializer for static_int_c | template_static_instantiated.cpp:45:29:45:29 | c | +| template_static_instantiated.cpp:45:29:45:29 | initializer for static_int_c | template_static_instantiated.cpp:45:29:45:29 | c | +| template_static_instantiated.cpp:46:27:46:29 | initializer for static_int_v | template_static_instantiated.cpp:46:29:46:29 | v | +| template_static_instantiated.cpp:46:27:46:29 | initializer for static_int_v | template_static_instantiated.cpp:46:29:46:29 | v | +| template_static_instantiated.cpp:49:23:49:25 | initializer for static_t_c | template_static_instantiated.cpp:49:25:49:25 | c | +| template_static_instantiated.cpp:49:25:49:25 | initializer for static_t_c | template_static_instantiated.cpp:49:25:49:25 | c | +| template_static_instantiated.cpp:50:23:50:25 | initializer for static_t_v | template_static_instantiated.cpp:50:25:50:25 | v | +| template_static_instantiated.cpp:50:23:50:25 | initializer for static_t_v | template_static_instantiated.cpp:50:25:50:25 | v | +| template_static_instantiated.cpp:54:20:54:21 | initializer for local_int_c | template_static_instantiated.cpp:54:21:54:21 | c | +| template_static_instantiated.cpp:54:20:54:21 | initializer for local_int_c | template_static_instantiated.cpp:54:21:54:21 | c | +| template_static_instantiated.cpp:55:20:55:21 | initializer for local_int_v | template_static_instantiated.cpp:55:21:55:21 | v | +| template_static_instantiated.cpp:55:20:55:21 | initializer for local_int_v | template_static_instantiated.cpp:55:21:55:21 | v | +| template_static_instantiated.cpp:58:16:58:17 | initializer for local_t_c | template_static_instantiated.cpp:58:17:58:17 | c | +| template_static_instantiated.cpp:58:16:58:17 | initializer for local_t_c | template_static_instantiated.cpp:58:17:58:17 | c | +| template_static_instantiated.cpp:59:16:59:17 | initializer for local_t_v | template_static_instantiated.cpp:59:17:59:17 | v | +| template_static_instantiated.cpp:59:16:59:17 | initializer for local_t_v | template_static_instantiated.cpp:59:17:59:17 | v | diff --git a/cpp/ql/test/library-tests/depends_initializers/InitializerAccesses.ql b/cpp/ql/test/library-tests/depends_initializers/InitializerAccesses.ql new file mode 100644 index 000000000000..e085d37c7a2d --- /dev/null +++ b/cpp/ql/test/library-tests/depends_initializers/InitializerAccesses.ql @@ -0,0 +1,8 @@ +/** + * @name InitializerAccesses + */ +import cpp + +from Initializer i, VariableAccess va +where i.getExpr().getAChild*() = va +select i, va diff --git a/cpp/ql/test/library-tests/depends_initializers/InitializerCFG.expected b/cpp/ql/test/library-tests/depends_initializers/InitializerCFG.expected new file mode 100644 index 000000000000..da871c2fbdaf --- /dev/null +++ b/cpp/ql/test/library-tests/depends_initializers/InitializerCFG.expected @@ -0,0 +1,97 @@ +| file://:0:0:0:0 | initializer for MYENUM_CONST | | +| initializers.cpp:8:15:8:22 | initializer for a_ptr | f | +| initializers.cpp:9:7:9:10 | initializer for a | f | +| initializers.cpp:17:8:17:11 | initializer for x | | +| initializers.cpp:18:8:18:15 | initializer for y | | +| initializers.cpp:19:8:19:21 | initializer for z | | +| initializers.cpp:22:9:22:9 | initializer for i | | +| initializers.cpp:23:8:23:9 | initializer for j | | +| initializers.cpp:25:3:25:3 | initializer for a | | +| initializers.cpp:26:7:26:10 | initializer for ax | | +| initializers.cpp:27:8:27:10 | initializer for aax | | +| initializers.cpp:30:20:30:35 | initializer for myIntArray | | +| initializers.cpp:31:3:31:15 | initializer for myObjectArray | | +| template_static.cpp:2:15:2:15 | initializer for c | | +| template_static.cpp:3:9:3:9 | initializer for v | | +| template_static.cpp:8:24:8:24 | initializer for static_1 | | +| template_static.cpp:9:24:9:24 | initializer for static_c | | +| template_static.cpp:10:22:10:24 | initializer for static_v | myNormalFunction | +| template_static.cpp:11:24:11:30 | initializer for static_one | myNormalFunction | +| template_static.cpp:12:15:12:16 | initializer for local_1 | myNormalFunction | +| template_static.cpp:13:15:13:16 | initializer for local_c | myNormalFunction | +| template_static.cpp:14:15:14:16 | initializer for local_v | myNormalFunction | +| template_static.cpp:15:17:15:22 | initializer for local_one | myNormalFunction | +| template_static_instantiated.cpp:2:15:2:15 | initializer for c | | +| template_static_instantiated.cpp:3:9:3:9 | initializer for v | | +| template_static_instantiated.cpp:8:24:8:24 | initializer for static_1 | | +| template_static_instantiated.cpp:9:24:9:24 | initializer for static_c | | +| template_static_instantiated.cpp:10:22:10:24 | initializer for static_v | myNormalFunction | +| template_static_instantiated.cpp:11:24:11:30 | initializer for static_one | myNormalFunction | +| template_static_instantiated.cpp:12:15:12:16 | initializer for local_1 | myNormalFunction | +| template_static_instantiated.cpp:13:15:13:16 | initializer for local_c | myNormalFunction | +| template_static_instantiated.cpp:14:15:14:16 | initializer for local_v | myNormalFunction | +| template_static_instantiated.cpp:15:17:15:22 | initializer for local_one | myNormalFunction | +| template_static_instantiated.cpp:20:28:20:28 | initializer for static_int_1 | | +| template_static_instantiated.cpp:20:28:20:28 | initializer for static_int_1 | | +| template_static_instantiated.cpp:21:28:21:28 | initializer for static_int_c | | +| template_static_instantiated.cpp:21:28:21:28 | initializer for static_int_c | | +| template_static_instantiated.cpp:22:26:22:28 | initializer for static_int_v | myTemplateFunction | +| template_static_instantiated.cpp:22:26:22:28 | initializer for static_int_v | myTemplateFunction | +| template_static_instantiated.cpp:23:28:23:34 | initializer for static_int_one | myTemplateFunction | +| template_static_instantiated.cpp:23:28:23:34 | initializer for static_int_one | myTemplateFunction | +| template_static_instantiated.cpp:24:24:24:24 | initializer for static_t_1 | | +| template_static_instantiated.cpp:24:24:24:24 | initializer for static_t_1 | | +| template_static_instantiated.cpp:25:22:25:24 | initializer for static_t_c | myTemplateFunction | +| template_static_instantiated.cpp:25:24:25:24 | initializer for static_t_c | | +| template_static_instantiated.cpp:26:22:26:24 | initializer for static_t_v | myTemplateFunction | +| template_static_instantiated.cpp:26:22:26:24 | initializer for static_t_v | myTemplateFunction | +| template_static_instantiated.cpp:27:24:27:30 | initializer for static_t_one | myTemplateFunction | +| template_static_instantiated.cpp:27:24:27:30 | initializer for static_t_one | myTemplateFunction | +| template_static_instantiated.cpp:29:19:29:20 | initializer for local_int_1 | myTemplateFunction | +| template_static_instantiated.cpp:29:19:29:20 | initializer for local_int_1 | myTemplateFunction | +| template_static_instantiated.cpp:30:19:30:20 | initializer for local_int_c | myTemplateFunction | +| template_static_instantiated.cpp:30:19:30:20 | initializer for local_int_c | myTemplateFunction | +| template_static_instantiated.cpp:31:19:31:20 | initializer for local_int_v | myTemplateFunction | +| template_static_instantiated.cpp:31:19:31:20 | initializer for local_int_v | myTemplateFunction | +| template_static_instantiated.cpp:32:21:32:26 | initializer for local_int_one | myTemplateFunction | +| template_static_instantiated.cpp:32:21:32:26 | initializer for local_int_one | myTemplateFunction | +| template_static_instantiated.cpp:33:15:33:16 | initializer for local_t_1 | myTemplateFunction | +| template_static_instantiated.cpp:33:15:33:16 | initializer for local_t_1 | myTemplateFunction | +| template_static_instantiated.cpp:34:15:34:16 | initializer for local_t_c | myTemplateFunction | +| template_static_instantiated.cpp:34:15:34:16 | initializer for local_t_c | myTemplateFunction | +| template_static_instantiated.cpp:35:15:35:16 | initializer for local_t_v | myTemplateFunction | +| template_static_instantiated.cpp:35:15:35:16 | initializer for local_t_v | myTemplateFunction | +| template_static_instantiated.cpp:36:17:36:22 | initializer for local_t_one | myTemplateFunction | +| template_static_instantiated.cpp:36:17:36:22 | initializer for local_t_one | myTemplateFunction | +| template_static_instantiated.cpp:44:29:44:29 | initializer for static_int_1 | | +| template_static_instantiated.cpp:44:29:44:29 | initializer for static_int_1 | | +| template_static_instantiated.cpp:45:29:45:29 | initializer for static_int_c | | +| template_static_instantiated.cpp:45:29:45:29 | initializer for static_int_c | | +| template_static_instantiated.cpp:46:27:46:29 | initializer for static_int_v | myMethod | +| template_static_instantiated.cpp:46:27:46:29 | initializer for static_int_v | myMethod | +| template_static_instantiated.cpp:47:29:47:35 | initializer for static_int_one | myMethod | +| template_static_instantiated.cpp:47:29:47:35 | initializer for static_int_one | myMethod | +| template_static_instantiated.cpp:48:25:48:25 | initializer for static_t_1 | | +| template_static_instantiated.cpp:48:25:48:25 | initializer for static_t_1 | | +| template_static_instantiated.cpp:49:23:49:25 | initializer for static_t_c | myMethod | +| template_static_instantiated.cpp:49:25:49:25 | initializer for static_t_c | | +| template_static_instantiated.cpp:50:23:50:25 | initializer for static_t_v | myMethod | +| template_static_instantiated.cpp:50:23:50:25 | initializer for static_t_v | myMethod | +| template_static_instantiated.cpp:51:25:51:31 | initializer for static_t_one | myMethod | +| template_static_instantiated.cpp:51:25:51:31 | initializer for static_t_one | myMethod | +| template_static_instantiated.cpp:53:20:53:21 | initializer for local_int_1 | myMethod | +| template_static_instantiated.cpp:53:20:53:21 | initializer for local_int_1 | myMethod | +| template_static_instantiated.cpp:54:20:54:21 | initializer for local_int_c | myMethod | +| template_static_instantiated.cpp:54:20:54:21 | initializer for local_int_c | myMethod | +| template_static_instantiated.cpp:55:20:55:21 | initializer for local_int_v | myMethod | +| template_static_instantiated.cpp:55:20:55:21 | initializer for local_int_v | myMethod | +| template_static_instantiated.cpp:56:22:56:27 | initializer for local_int_one | myMethod | +| template_static_instantiated.cpp:56:22:56:27 | initializer for local_int_one | myMethod | +| template_static_instantiated.cpp:57:16:57:17 | initializer for local_t_1 | myMethod | +| template_static_instantiated.cpp:57:16:57:17 | initializer for local_t_1 | myMethod | +| template_static_instantiated.cpp:58:16:58:17 | initializer for local_t_c | myMethod | +| template_static_instantiated.cpp:58:16:58:17 | initializer for local_t_c | myMethod | +| template_static_instantiated.cpp:59:16:59:17 | initializer for local_t_v | myMethod | +| template_static_instantiated.cpp:59:16:59:17 | initializer for local_t_v | myMethod | +| template_static_instantiated.cpp:60:18:60:23 | initializer for local_t_one | myMethod | +| template_static_instantiated.cpp:60:18:60:23 | initializer for local_t_one | myMethod | diff --git a/cpp/ql/test/library-tests/depends_initializers/InitializerCFG.ql b/cpp/ql/test/library-tests/depends_initializers/InitializerCFG.ql new file mode 100644 index 000000000000..44444c628217 --- /dev/null +++ b/cpp/ql/test/library-tests/depends_initializers/InitializerCFG.ql @@ -0,0 +1,14 @@ +import cpp + +Function getCFGFunction(Initializer i) { + result = i.getASuccessor*() +} + +from Initializer i, string f +where + if exists(getCFGFunction(i)) then ( + f = getCFGFunction(i).toString() + ) else ( + f = "" + ) +select i, f diff --git a/cpp/ql/test/library-tests/depends_initializers/InitializerCalls.expected b/cpp/ql/test/library-tests/depends_initializers/InitializerCalls.expected new file mode 100644 index 000000000000..3ac6b15451ba --- /dev/null +++ b/cpp/ql/test/library-tests/depends_initializers/InitializerCalls.expected @@ -0,0 +1,29 @@ +| initializers.cpp:8:15:8:22 | initializer for a_ptr | initializers.cpp:8:16:8:22 | call to A | +| initializers.cpp:9:7:9:10 | initializer for a | initializers.cpp:9:7:9:10 | call to A | +| initializers.cpp:17:8:17:11 | initializer for x | initializers.cpp:17:9:17:9 | call to g | +| initializers.cpp:18:8:18:15 | initializer for y | initializers.cpp:18:13:18:13 | call to g | +| initializers.cpp:19:8:19:21 | initializer for z | initializers.cpp:19:13:19:13 | call to g | +| initializers.cpp:19:8:19:21 | initializer for z | initializers.cpp:19:19:19:19 | call to h | +| initializers.cpp:25:3:25:3 | initializer for a | initializers.cpp:25:3:25:3 | call to A | +| initializers.cpp:26:7:26:10 | initializer for ax | initializers.cpp:26:7:26:10 | call to A | +| initializers.cpp:31:3:31:15 | initializer for myObjectArray | initializers.cpp:31:3:31:15 | call to A | +| template_static.cpp:11:24:11:30 | initializer for static_one | template_static.cpp:11:26:11:28 | call to one | +| template_static.cpp:15:17:15:22 | initializer for local_one | template_static.cpp:15:18:15:20 | call to one | +| template_static_instantiated.cpp:11:24:11:30 | initializer for static_one | template_static_instantiated.cpp:11:26:11:28 | call to one | +| template_static_instantiated.cpp:15:17:15:22 | initializer for local_one | template_static_instantiated.cpp:15:18:15:20 | call to one | +| template_static_instantiated.cpp:23:28:23:34 | initializer for static_int_one | template_static_instantiated.cpp:23:30:23:32 | call to one | +| template_static_instantiated.cpp:23:28:23:34 | initializer for static_int_one | template_static_instantiated.cpp:23:30:23:32 | call to one | +| template_static_instantiated.cpp:27:24:27:30 | initializer for static_t_one | template_static_instantiated.cpp:27:26:27:28 | call to one | +| template_static_instantiated.cpp:27:24:27:30 | initializer for static_t_one | template_static_instantiated.cpp:27:26:27:28 | call to one | +| template_static_instantiated.cpp:32:21:32:26 | initializer for local_int_one | template_static_instantiated.cpp:32:22:32:24 | call to one | +| template_static_instantiated.cpp:32:21:32:26 | initializer for local_int_one | template_static_instantiated.cpp:32:22:32:24 | call to one | +| template_static_instantiated.cpp:36:17:36:22 | initializer for local_t_one | template_static_instantiated.cpp:36:18:36:20 | call to one | +| template_static_instantiated.cpp:36:17:36:22 | initializer for local_t_one | template_static_instantiated.cpp:36:18:36:20 | call to one | +| template_static_instantiated.cpp:47:29:47:35 | initializer for static_int_one | template_static_instantiated.cpp:47:31:47:33 | call to one | +| template_static_instantiated.cpp:47:29:47:35 | initializer for static_int_one | template_static_instantiated.cpp:47:31:47:33 | call to one | +| template_static_instantiated.cpp:51:25:51:31 | initializer for static_t_one | template_static_instantiated.cpp:51:27:51:29 | call to one | +| template_static_instantiated.cpp:51:25:51:31 | initializer for static_t_one | template_static_instantiated.cpp:51:27:51:29 | call to one | +| template_static_instantiated.cpp:56:22:56:27 | initializer for local_int_one | template_static_instantiated.cpp:56:23:56:25 | call to one | +| template_static_instantiated.cpp:56:22:56:27 | initializer for local_int_one | template_static_instantiated.cpp:56:23:56:25 | call to one | +| template_static_instantiated.cpp:60:18:60:23 | initializer for local_t_one | template_static_instantiated.cpp:60:19:60:21 | call to one | +| template_static_instantiated.cpp:60:18:60:23 | initializer for local_t_one | template_static_instantiated.cpp:60:19:60:21 | call to one | diff --git a/cpp/ql/test/library-tests/depends_initializers/InitializerCalls.ql b/cpp/ql/test/library-tests/depends_initializers/InitializerCalls.ql new file mode 100644 index 000000000000..f37d8faca127 --- /dev/null +++ b/cpp/ql/test/library-tests/depends_initializers/InitializerCalls.ql @@ -0,0 +1,8 @@ +/** + * @name InitializerCalls + */ +import cpp + +from Initializer i, FunctionCall fc +where i.getExpr().getAChild*() = fc +select i, fc diff --git a/cpp/ql/test/library-tests/depends_initializers/VariableInitializers.expected b/cpp/ql/test/library-tests/depends_initializers/VariableInitializers.expected new file mode 100644 index 000000000000..a7698cee885b --- /dev/null +++ b/cpp/ql/test/library-tests/depends_initializers/VariableInitializers.expected @@ -0,0 +1,111 @@ +| file://:0:0:0:0 | fp_offset | 0 | 0 | +| file://:0:0:0:0 | gp_offset | 0 | 0 | +| file://:0:0:0:0 | overflow_arg_area | 0 | 0 | +| file://:0:0:0:0 | p#0 | 0 | 0 | +| file://:0:0:0:0 | p#0 | 0 | 0 | +| file://:0:0:0:0 | p#0 | 0 | 0 | +| file://:0:0:0:0 | p#0 | 0 | 0 | +| file://:0:0:0:0 | p#0 | 0 | 0 | +| file://:0:0:0:0 | p#0 | 0 | 0 | +| file://:0:0:0:0 | p#0 | 0 | 0 | +| file://:0:0:0:0 | p#0 | 0 | 0 | +| file://:0:0:0:0 | reg_save_area | 0 | 0 | +| initializers.cpp:8:8:8:12 | a_ptr | 1 | 1 | +| initializers.cpp:9:4:9:4 | a | 1 | 1 | +| initializers.cpp:17:5:17:5 | x | 1 | 1 | +| initializers.cpp:18:5:18:5 | y | 1 | 1 | +| initializers.cpp:19:5:19:5 | z | 1 | 1 | +| initializers.cpp:22:5:22:5 | i | 1 | 1 | +| initializers.cpp:23:5:23:5 | j | 1 | 1 | +| initializers.cpp:25:3:25:3 | a | 1 | 1 | +| initializers.cpp:26:3:26:4 | ax | 1 | 1 | +| initializers.cpp:27:3:27:5 | aax | 1 | 1 | +| initializers.cpp:30:5:30:14 | myIntArray | 1 | 1 | +| initializers.cpp:31:3:31:15 | myObjectArray | 1 | 1 | +| template_static.cpp:2:11:2:11 | c | 1 | 1 | +| template_static.cpp:3:5:3:5 | v | 1 | 1 | +| template_static.cpp:8:13:8:20 | static_1 | 1 | 1 | +| template_static.cpp:9:13:9:20 | static_c | 1 | 1 | +| template_static.cpp:10:13:10:20 | static_v | 1 | 1 | +| template_static.cpp:11:13:11:22 | static_one | 1 | 1 | +| template_static.cpp:12:6:12:12 | local_1 | 1 | 1 | +| template_static.cpp:13:6:13:12 | local_c | 1 | 1 | +| template_static.cpp:14:6:14:12 | local_v | 1 | 1 | +| template_static.cpp:15:6:15:14 | local_one | 1 | 1 | +| template_static.cpp:69:48:69:48 | a | 0 | 0 | +| template_static.cpp:69:57:69:57 | b | 0 | 0 | +| template_static_instantiated.cpp:2:11:2:11 | c | 1 | 1 | +| template_static_instantiated.cpp:3:5:3:5 | v | 1 | 1 | +| template_static_instantiated.cpp:8:13:8:20 | static_1 | 1 | 1 | +| template_static_instantiated.cpp:9:13:9:20 | static_c | 1 | 1 | +| template_static_instantiated.cpp:10:13:10:20 | static_v | 1 | 1 | +| template_static_instantiated.cpp:11:13:11:22 | static_one | 1 | 1 | +| template_static_instantiated.cpp:12:6:12:12 | local_1 | 1 | 1 | +| template_static_instantiated.cpp:13:6:13:12 | local_c | 1 | 1 | +| template_static_instantiated.cpp:14:6:14:12 | local_v | 1 | 1 | +| template_static_instantiated.cpp:15:6:15:14 | local_one | 1 | 1 | +| template_static_instantiated.cpp:20:13:20:24 | static_int_1 | 1 | 1 | +| template_static_instantiated.cpp:20:13:20:24 | static_int_1 | 1 | 1 | +| template_static_instantiated.cpp:21:13:21:24 | static_int_c | 1 | 1 | +| template_static_instantiated.cpp:21:13:21:24 | static_int_c | 1 | 1 | +| template_static_instantiated.cpp:22:13:22:24 | static_int_v | 1 | 1 | +| template_static_instantiated.cpp:22:13:22:24 | static_int_v | 1 | 1 | +| template_static_instantiated.cpp:23:13:23:26 | static_int_one | 1 | 1 | +| template_static_instantiated.cpp:23:13:23:26 | static_int_one | 1 | 1 | +| template_static_instantiated.cpp:24:11:24:20 | static_t_1 | 1 | 1 | +| template_static_instantiated.cpp:24:11:24:20 | static_t_1 | 1 | 1 | +| template_static_instantiated.cpp:25:11:25:20 | static_t_c | 1 | 1 | +| template_static_instantiated.cpp:25:11:25:20 | static_t_c | 1 | 1 | +| template_static_instantiated.cpp:26:11:26:20 | static_t_v | 1 | 1 | +| template_static_instantiated.cpp:26:11:26:20 | static_t_v | 1 | 1 | +| template_static_instantiated.cpp:27:11:27:22 | static_t_one | 1 | 1 | +| template_static_instantiated.cpp:27:11:27:22 | static_t_one | 1 | 1 | +| template_static_instantiated.cpp:29:6:29:16 | local_int_1 | 1 | 1 | +| template_static_instantiated.cpp:29:6:29:16 | local_int_1 | 1 | 1 | +| template_static_instantiated.cpp:30:6:30:16 | local_int_c | 1 | 1 | +| template_static_instantiated.cpp:30:6:30:16 | local_int_c | 1 | 1 | +| template_static_instantiated.cpp:31:6:31:16 | local_int_v | 1 | 1 | +| template_static_instantiated.cpp:31:6:31:16 | local_int_v | 1 | 1 | +| template_static_instantiated.cpp:32:6:32:18 | local_int_one | 1 | 1 | +| template_static_instantiated.cpp:32:6:32:18 | local_int_one | 1 | 1 | +| template_static_instantiated.cpp:33:4:33:12 | local_t_1 | 1 | 1 | +| template_static_instantiated.cpp:33:4:33:12 | local_t_1 | 1 | 1 | +| template_static_instantiated.cpp:34:4:34:12 | local_t_c | 1 | 1 | +| template_static_instantiated.cpp:34:4:34:12 | local_t_c | 1 | 1 | +| template_static_instantiated.cpp:35:4:35:12 | local_t_v | 1 | 1 | +| template_static_instantiated.cpp:35:4:35:12 | local_t_v | 1 | 1 | +| template_static_instantiated.cpp:36:4:36:14 | local_t_one | 1 | 1 | +| template_static_instantiated.cpp:36:4:36:14 | local_t_one | 1 | 1 | +| template_static_instantiated.cpp:44:14:44:25 | static_int_1 | 1 | 1 | +| template_static_instantiated.cpp:44:14:44:25 | static_int_1 | 1 | 1 | +| template_static_instantiated.cpp:45:14:45:25 | static_int_c | 1 | 1 | +| template_static_instantiated.cpp:45:14:45:25 | static_int_c | 1 | 1 | +| template_static_instantiated.cpp:46:14:46:25 | static_int_v | 1 | 1 | +| template_static_instantiated.cpp:46:14:46:25 | static_int_v | 1 | 1 | +| template_static_instantiated.cpp:47:14:47:27 | static_int_one | 1 | 1 | +| template_static_instantiated.cpp:47:14:47:27 | static_int_one | 1 | 1 | +| template_static_instantiated.cpp:48:12:48:21 | static_t_1 | 1 | 1 | +| template_static_instantiated.cpp:48:12:48:21 | static_t_1 | 1 | 1 | +| template_static_instantiated.cpp:49:12:49:21 | static_t_c | 1 | 1 | +| template_static_instantiated.cpp:49:12:49:21 | static_t_c | 1 | 1 | +| template_static_instantiated.cpp:50:12:50:21 | static_t_v | 1 | 1 | +| template_static_instantiated.cpp:50:12:50:21 | static_t_v | 1 | 1 | +| template_static_instantiated.cpp:51:12:51:23 | static_t_one | 1 | 1 | +| template_static_instantiated.cpp:51:12:51:23 | static_t_one | 1 | 1 | +| template_static_instantiated.cpp:53:7:53:17 | local_int_1 | 1 | 1 | +| template_static_instantiated.cpp:53:7:53:17 | local_int_1 | 1 | 1 | +| template_static_instantiated.cpp:54:7:54:17 | local_int_c | 1 | 1 | +| template_static_instantiated.cpp:54:7:54:17 | local_int_c | 1 | 1 | +| template_static_instantiated.cpp:55:7:55:17 | local_int_v | 1 | 1 | +| template_static_instantiated.cpp:55:7:55:17 | local_int_v | 1 | 1 | +| template_static_instantiated.cpp:56:7:56:19 | local_int_one | 1 | 1 | +| template_static_instantiated.cpp:56:7:56:19 | local_int_one | 1 | 1 | +| template_static_instantiated.cpp:57:5:57:13 | local_t_1 | 1 | 1 | +| template_static_instantiated.cpp:57:5:57:13 | local_t_1 | 1 | 1 | +| template_static_instantiated.cpp:58:5:58:13 | local_t_c | 1 | 1 | +| template_static_instantiated.cpp:58:5:58:13 | local_t_c | 1 | 1 | +| template_static_instantiated.cpp:59:5:59:13 | local_t_v | 1 | 1 | +| template_static_instantiated.cpp:59:5:59:13 | local_t_v | 1 | 1 | +| template_static_instantiated.cpp:60:5:60:15 | local_t_one | 1 | 1 | +| template_static_instantiated.cpp:60:5:60:15 | local_t_one | 1 | 1 | +| template_static_instantiated.cpp:70:24:70:26 | mtc | 0 | 0 | diff --git a/cpp/ql/test/library-tests/depends_initializers/VariableInitializers.ql b/cpp/ql/test/library-tests/depends_initializers/VariableInitializers.ql new file mode 100644 index 000000000000..a39ecd1ac18f --- /dev/null +++ b/cpp/ql/test/library-tests/depends_initializers/VariableInitializers.ql @@ -0,0 +1,4 @@ +import cpp + +from Variable v +select v, count(v.getInitializer()), count(v.getInitializer().getExpr()) diff --git a/cpp/ql/test/library-tests/depends_initializers/initializers.cpp b/cpp/ql/test/library-tests/depends_initializers/initializers.cpp new file mode 100644 index 000000000000..079398e20bc3 --- /dev/null +++ b/cpp/ql/test/library-tests/depends_initializers/initializers.cpp @@ -0,0 +1,31 @@ +//references(UserType) +class A { +public: + A() {} +}; + +int f() { + void *a_ptr = new A(); //match (1 call) + A a = A(); // match (1 call) + return 1; +} + +//calls(Function) +int g() {return 0;} +extern int h(); + +int x = g(); //match +int y = x + g(); //match (1 call, 1 access) +int z = x + g() + h(); //match(2 calls, 1 access) + +//accesses(Variable) +int i = 1; +int j = i; //match (1 access) + +A a; //match(1 call) +A ax = A(); //match (1 call) +A aax = ax; //match (1 access) + +//array initialization +int myIntArray[5] = {i, 0, 0, 0, 0}; //match(1 access) +A myObjectArray[3]; //match(1 call) diff --git a/cpp/ql/test/library-tests/depends_initializers/template_static.cpp b/cpp/ql/test/library-tests/depends_initializers/template_static.cpp new file mode 100644 index 000000000000..956cd767d633 --- /dev/null +++ b/cpp/ql/test/library-tests/depends_initializers/template_static.cpp @@ -0,0 +1,89 @@ + +const int c = 1; +int v = 1; +int one() {return 1;} + +void myNormalFunction() +{ + static int static_1 = 1; + static int static_c = c; + static int static_v = v; + static int static_one = one(); + int local_1 = 1; + int local_c = c; + int local_v = v; + int local_one = one(); +} + +template void myTemplateFunction() +{ + static int static_int_1 = 1; + static int static_int_c = c; // [initializer is not populated] + static int static_int_v = v; // [initializer is not populated] + static int static_int_one = one(); // [initializer is not populated] + static T static_t_1 = 1; // [initializer is not populated] + static T static_t_c = c; // [initializer is not populated] + static T static_t_v = v; // [initializer is not populated] + static T static_t_one = one(); // [initializer is not populated] + + int local_int_1 = 1; + int local_int_c = c; + int local_int_v = v; + int local_int_one = one(); + T local_t_1 = 1; + T local_t_c = c; + T local_t_v = v; + T local_t_one = one(); +} + +template class myTemplateClass +{ +public: + void myMethod() + { + static int static_int_1 = 1; + static int static_int_c = c; // [initializer is not populated] + static int static_int_v = v; // [initializer is not populated] + static int static_int_one = one(); // [initializer is not populated] + static T static_t_1 = 1; // [initializer is not populated] + static T static_t_c = c; // [initializer is not populated] + static T static_t_v = v; // [initializer is not populated] + static T static_t_one = one(); // [initializer is not populated] + + int local_int_1 = 1; + int local_int_c = c; + int local_int_v = v; + int local_int_one = one(); + T local_t_1 = 1; + T local_t_c = c; + T local_t_v = v; + T local_t_one = one(); + } +}; + +enum myEnum +{ + MYENUM_CONST +}; + +template void myTemplateFunction2(int a = 1, T b = 2) +{ + static int static_int_zero = 0; + static int static_int_ec = MYENUM_CONST; + static int static_int_expr = v + 1; + static int *static_int_addr = &v; + static int static_int_sizeof_v = sizeof(v); + static int static_int_sizeof_t = sizeof(T); + static T static_t_zero = 0; + static T static_t_ec = MYENUM_CONST; + static T static_t_expr = v + 1; + static T *static_t_addr = &v; + static T static_t_sizeof_v = sizeof(v); + static T static_t_sizeof_t = sizeof(T); + + static int static_int_c1 = c; + static int static_int_c2=c; + { + static int static_int_v2 = v; + } +} diff --git a/cpp/ql/test/library-tests/depends_initializers/template_static_instantiated.cpp b/cpp/ql/test/library-tests/depends_initializers/template_static_instantiated.cpp new file mode 100644 index 000000000000..06d5ae355fd7 --- /dev/null +++ b/cpp/ql/test/library-tests/depends_initializers/template_static_instantiated.cpp @@ -0,0 +1,74 @@ +namespace ns2 { +const int c = 1; +int v = 1; +int one() {return 1;} + +void myNormalFunction() +{ + static int static_1 = 1; + static int static_c = c; + static int static_v = v; + static int static_one = one(); + int local_1 = 1; + int local_c = c; + int local_v = v; + int local_one = one(); +} + +template void myTemplateFunction() +{ + static int static_int_1 = 1; + static int static_int_c = c; // [initializer is not populated] + static int static_int_v = v; // [initializer is not populated] + static int static_int_one = one(); // [initializer is not populated] + static T static_t_1 = 1; // [initializer is not populated] + static T static_t_c = c; // [initializer is not populated] + static T static_t_v = v; // [initializer is not populated] + static T static_t_one = one(); // [initializer is not populated] + + int local_int_1 = 1; + int local_int_c = c; + int local_int_v = v; + int local_int_one = one(); + T local_t_1 = 1; + T local_t_c = c; + T local_t_v = v; + T local_t_one = one(); +} + +template class myTemplateClass +{ +public: + void myMethod() + { + static int static_int_1 = 1; + static int static_int_c = c; // [initializer is not populated] + static int static_int_v = v; // [initializer is not populated] + static int static_int_one = one(); // [initializer is not populated] + static T static_t_1 = 1; // [initializer is not populated] + static T static_t_c = c; // [initializer is not populated] + static T static_t_v = v; // [initializer is not populated] + static T static_t_one = one(); // [initializer is not populated] + + int local_int_1 = 1; + int local_int_c = c; + int local_int_v = v; + int local_int_one = one(); + T local_t_1 = 1; + T local_t_c = c; + T local_t_v = v; + T local_t_one = one(); + } +}; + +void testFunc() +{ + // instantiate the templates + myTemplateFunction(); + + { + myTemplateClass mtc; + mtc.myMethod(); + } +} +} diff --git a/cpp/ql/test/library-tests/deprecated/stackVariableReachability.c b/cpp/ql/test/library-tests/deprecated/stackVariableReachability.c new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/cpp/ql/test/library-tests/deprecated/stackVariableReachability.expected b/cpp/ql/test/library-tests/deprecated/stackVariableReachability.expected new file mode 100644 index 000000000000..12f63b493a67 --- /dev/null +++ b/cpp/ql/test/library-tests/deprecated/stackVariableReachability.expected @@ -0,0 +1 @@ +| Test for deprecated library StackVariableReachability. | diff --git a/cpp/ql/test/library-tests/deprecated/stackVariableReachability.ql b/cpp/ql/test/library-tests/deprecated/stackVariableReachability.ql new file mode 100644 index 000000000000..63691e7c3edd --- /dev/null +++ b/cpp/ql/test/library-tests/deprecated/stackVariableReachability.ql @@ -0,0 +1,4 @@ +import cpp +import semmle.code.cpp.controlflow.StackVariableReachability + +select "Test for deprecated library StackVariableReachability." diff --git a/cpp/ql/test/library-tests/derived_types/DerivedTypesBaseType.expected b/cpp/ql/test/library-tests/derived_types/DerivedTypesBaseType.expected new file mode 100644 index 000000000000..68e2d7e841c2 --- /dev/null +++ b/cpp/ql/test/library-tests/derived_types/DerivedTypesBaseType.expected @@ -0,0 +1,10 @@ +| file://:0:0:0:0 | C & | derivedtype.cpp:1:7:1:7 | C | +| file://:0:0:0:0 | C && | derivedtype.cpp:1:7:1:7 | C | +| file://:0:0:0:0 | C * | derivedtype.cpp:1:7:1:7 | C | +| file://:0:0:0:0 | C ** | file://:0:0:0:0 | C * | +| file://:0:0:0:0 | CC * | derivedtype.cpp:5:11:5:12 | CC | +| file://:0:0:0:0 | CC ** | file://:0:0:0:0 | CC * | +| file://:0:0:0:0 | __va_list_tag & | file://:0:0:0:0 | __va_list_tag | +| file://:0:0:0:0 | const C | derivedtype.cpp:1:7:1:7 | C | +| file://:0:0:0:0 | const C & | file://:0:0:0:0 | const C | +| file://:0:0:0:0 | void * | file://:0:0:0:0 | void | diff --git a/cpp/ql/test/library-tests/derived_types/DerivedTypesBaseType.ql b/cpp/ql/test/library-tests/derived_types/DerivedTypesBaseType.ql new file mode 100644 index 000000000000..bcc7ae9fdc6b --- /dev/null +++ b/cpp/ql/test/library-tests/derived_types/DerivedTypesBaseType.ql @@ -0,0 +1,6 @@ +import cpp + +//this query should find the baseType of CC* to be CC, not C. +from DerivedType t, Type baseType +where t.getBaseType() = baseType +select t, baseType diff --git a/cpp/ql/test/library-tests/derived_types/derivedtype.cpp b/cpp/ql/test/library-tests/derived_types/derivedtype.cpp new file mode 100644 index 000000000000..496901e6e091 --- /dev/null +++ b/cpp/ql/test/library-tests/derived_types/derivedtype.cpp @@ -0,0 +1,9 @@ +class C { + C() {} +}; + +typedef C CC; + +CC** f() { + return 0; +} diff --git a/cpp/ql/test/library-tests/destructors/cfg.expected b/cpp/ql/test/library-tests/destructors/cfg.expected new file mode 100644 index 000000000000..cf730468263e --- /dev/null +++ b/cpp/ql/test/library-tests/destructors/cfg.expected @@ -0,0 +1,255 @@ +| C::C | false | 135 | 135 | C | +| C::C | false | 138 | 138 | C | +| C::operator= | false | 133 | 133 | operator= | +| C::~C | false | 137 | 137 | ~C | +| Class2::Class2 | false | 382 | 382 | Class2 | +| Class2::Class2 | false | 388 | 388 | return ... | +| Class2::Class2 | false | 390 | 390 | { ... } | +| Class2::Class2 | false | 395 | 395 | Class2 | +| Class2::Class2 | true | 388 | 382 | | +| Class2::Class2 | true | 390 | 388 | | +| Class2::operator= | false | 392 | 392 | operator= | +| Class2::~Class2 | false | 394 | 394 | ~Class2 | +| Outer::Inner::Inner | false | 429 | 429 | Inner | +| Outer::Inner::Inner | false | 434 | 434 | return ... | +| Outer::Inner::Inner | false | 436 | 436 | { ... } | +| Outer::Inner::Inner | false | 498 | 498 | Inner | +| Outer::Inner::Inner | true | 434 | 429 | | +| Outer::Inner::Inner | true | 436 | 434 | | +| Outer::Inner::operator= | false | 496 | 496 | operator= | +| Outer::Inner::~Inner | false | 472 | 472 | ~Inner | +| Outer::Inner::~Inner | false | 476 | 476 | return ... | +| Outer::Inner::~Inner | false | 478 | 478 | { ... } | +| Outer::Inner::~Inner | true | 476 | 472 | | +| Outer::Inner::~Inner | true | 478 | 476 | | +| Outer::f2 | false | 411 | 411 | f2 | +| Outer::f2 | false | 417 | 417 | call to getClass2 | +| Outer::f2 | false | 419 | 419 | initializer for c | +| Outer::f2 | false | 424 | 424 | call to Inner | +| Outer::f2 | false | 438 | 438 | c | +| Outer::f2 | false | 440 | 440 | (const Class2)... | +| Outer::f2 | false | 442 | 442 | (reference to) | +| Outer::f2 | false | 444 | 444 | initializer for inner | +| Outer::f2 | false | 448 | 448 | declaration | +| Outer::f2 | false | 450 | 450 | i | +| Outer::f2 | false | 452 | 452 | (bool)... | +| Outer::f2 | false | 454 | 454 | return ... | +| Outer::f2 | false | 456 | 456 | { ... } | +| Outer::f2 | false | 458 | 458 | if (...) ... | +| Outer::f2 | false | 460 | 460 | declaration | +| Outer::f2 | false | 462 | 462 | return ... | +| Outer::f2 | false | 464 | 464 | { ... } | +| Outer::f2 | false | 466 | 466 | c | +| Outer::f2 | false | 468 | 468 | call to c.~Class2 | +| Outer::f2 | false | 469 | 469 | inner | +| Outer::f2 | false | 470 | 470 | call to inner.~Inner | +| Outer::f2 | true | 417 | 458 | | +| Outer::f2 | true | 419 | 417 | | +| Outer::f2 | true | 424 | 462 | | +| Outer::f2 | true | 438 | 424 | | +| Outer::f2 | true | 444 | 438 | | +| Outer::f2 | true | 448 | 419 | | +| Outer::f2 | true | 450 | 456 | T | +| Outer::f2 | true | 450 | 460 | F | +| Outer::f2 | true | 454 | 466 | | +| Outer::f2 | true | 456 | 454 | | +| Outer::f2 | true | 458 | 450 | | +| Outer::f2 | true | 460 | 444 | | +| Outer::f2 | true | 462 | 469 | | +| Outer::f2 | true | 464 | 448 | | +| Outer::f2 | true | 466 | 468 | | +| Outer::f2 | true | 468 | 411 | | +| Outer::f2 | true | 469 | 470 | | +| Outer::f2 | true | 470 | 466 | | +| Outer::operator= | false | 481 | 481 | operator= | +| Outer::operator= | false | 485 | 485 | operator= | +| __va_list_tag::operator= | false | 64 | 64 | operator= | +| __va_list_tag::operator= | false | 68 | 68 | operator= | +| f | false | 152 | 152 | f | +| f | false | 158 | 158 | call to C | +| f | false | 162 | 162 | 110 | +| f | false | 163 | 163 | initializer for c10 | +| f | false | 168 | 168 | call to C | +| f | false | 172 | 172 | 111 | +| f | false | 173 | 173 | initializer for c11 | +| f | false | 178 | 178 | call to C | +| f | false | 182 | 182 | 120 | +| f | false | 183 | 183 | initializer for c20 | +| f | false | 188 | 188 | call to C | +| f | false | 192 | 192 | 121 | +| f | false | 193 | 193 | initializer for c21 | +| f | false | 198 | 198 | call to C | +| f | false | 202 | 202 | 130 | +| f | false | 203 | 203 | initializer for c30 | +| f | false | 208 | 208 | call to C | +| f | false | 212 | 212 | 131 | +| f | false | 213 | 213 | initializer for c31 | +| f | false | 218 | 218 | call to C | +| f | false | 222 | 222 | 132 | +| f | false | 223 | 223 | initializer for c32 | +| f | false | 228 | 228 | call to C | +| f | false | 232 | 232 | 133 | +| f | false | 233 | 233 | initializer for c33 | +| f | false | 238 | 238 | call to C | +| f | false | 242 | 242 | 134 | +| f | false | 243 | 243 | initializer for c34 | +| f | false | 248 | 248 | call to C | +| f | false | 252 | 252 | 122 | +| f | false | 253 | 253 | initializer for c22 | +| f | false | 258 | 258 | call to C | +| f | false | 262 | 262 | 123 | +| f | false | 263 | 263 | initializer for c23 | +| f | false | 267 | 267 | declaration | +| f | false | 269 | 269 | declaration | +| f | false | 271 | 271 | declaration | +| f | false | 273 | 273 | { ... } | +| f | false | 275 | 275 | declaration | +| f | false | 277 | 277 | b1 | +| f | false | 279 | 279 | (bool)... | +| f | false | 281 | 281 | goto ... | +| f | false | 283 | 283 | if (...) ... | +| f | false | 285 | 285 | declaration | +| f | false | 287 | 287 | b2 | +| f | false | 289 | 289 | (bool)... | +| f | false | 291 | 291 | return ... | +| f | false | 293 | 293 | if (...) ... | +| f | false | 295 | 295 | declaration | +| f | false | 297 | 297 | { ... } | +| f | false | 299 | 299 | declaration | +| f | false | 301 | 301 | { ... } | +| f | false | 303 | 303 | declaration | +| f | false | 305 | 305 | { ... } | +| f | false | 307 | 307 | declaration | +| f | false | 309 | 309 | { ... } | +| f | false | 311 | 311 | label ...: | +| f | false | 313 | 313 | declaration | +| f | false | 315 | 315 | { ... } | +| f | false | 317 | 317 | declaration | +| f | false | 319 | 319 | return ... | +| f | false | 321 | 321 | { ... } | +| f | false | 323 | 323 | c10 | +| f | false | 325 | 325 | call to c10.~C | +| f | false | 326 | 326 | c11 | +| f | false | 327 | 327 | call to c11.~C | +| f | false | 328 | 328 | c23 | +| f | false | 330 | 330 | call to c23.~C | +| f | false | 331 | 331 | c22 | +| f | false | 333 | 333 | call to c22.~C | +| f | false | 334 | 334 | c20 | +| f | false | 336 | 336 | call to c20.~C | +| f | false | 337 | 337 | c21 | +| f | false | 338 | 338 | call to c21.~C | +| f | false | 339 | 339 | c34 | +| f | false | 341 | 341 | call to c34.~C | +| f | false | 342 | 342 | c31 | +| f | false | 344 | 344 | call to c31.~C | +| f | false | 345 | 345 | c32 | +| f | false | 346 | 346 | call to c32.~C | +| f | false | 347 | 347 | c33 | +| f | false | 348 | 348 | call to c33.~C | +| f | false | 349 | 349 | c20 | +| f | false | 350 | 350 | call to c20.~C | +| f | false | 351 | 351 | c31 | +| f | false | 352 | 352 | call to c31.~C | +| f | false | 353 | 353 | c32 | +| f | false | 354 | 354 | call to c32.~C | +| f | false | 355 | 355 | c20 | +| f | false | 356 | 356 | call to c20.~C | +| f | false | 357 | 357 | c31 | +| f | false | 358 | 358 | call to c31.~C | +| f | false | 359 | 359 | c30 | +| f | false | 361 | 361 | call to c30.~C | +| f | true | 158 | 305 | | +| f | true | 162 | 158 | | +| f | true | 163 | 162 | | +| f | true | 168 | 319 | | +| f | true | 172 | 168 | | +| f | true | 173 | 172 | | +| f | true | 178 | 273 | | +| f | true | 182 | 178 | | +| f | true | 183 | 182 | | +| f | true | 188 | 337 | | +| f | true | 192 | 188 | | +| f | true | 193 | 192 | | +| f | true | 198 | 359 | | +| f | true | 202 | 198 | | +| f | true | 203 | 202 | | +| f | true | 208 | 283 | | +| f | true | 212 | 208 | | +| f | true | 213 | 212 | | +| f | true | 218 | 293 | | +| f | true | 222 | 218 | | +| f | true | 223 | 222 | | +| f | true | 228 | 347 | | +| f | true | 232 | 228 | | +| f | true | 233 | 232 | | +| f | true | 238 | 339 | | +| f | true | 242 | 238 | | +| f | true | 243 | 242 | | +| f | true | 248 | 331 | | +| f | true | 252 | 248 | | +| f | true | 253 | 252 | | +| f | true | 258 | 328 | | +| f | true | 262 | 258 | | +| f | true | 263 | 262 | | +| f | true | 267 | 163 | | +| f | true | 269 | 183 | | +| f | true | 271 | 203 | | +| f | true | 273 | 271 | | +| f | true | 275 | 213 | | +| f | true | 277 | 281 | T | +| f | true | 277 | 285 | F | +| f | true | 281 | 357 | | +| f | true | 283 | 277 | | +| f | true | 285 | 223 | | +| f | true | 287 | 291 | T | +| f | true | 287 | 295 | F | +| f | true | 291 | 353 | | +| f | true | 293 | 287 | | +| f | true | 295 | 233 | | +| f | true | 297 | 275 | | +| f | true | 299 | 243 | | +| f | true | 301 | 299 | | +| f | true | 303 | 193 | | +| f | true | 305 | 269 | | +| f | true | 307 | 253 | | +| f | true | 309 | 307 | | +| f | true | 311 | 313 | | +| f | true | 313 | 263 | | +| f | true | 315 | 311 | | +| f | true | 317 | 173 | | +| f | true | 319 | 326 | | +| f | true | 321 | 267 | | +| f | true | 323 | 325 | | +| f | true | 325 | 152 | | +| f | true | 326 | 327 | | +| f | true | 327 | 323 | | +| f | true | 328 | 330 | | +| f | true | 330 | 317 | | +| f | true | 331 | 333 | | +| f | true | 333 | 315 | | +| f | true | 334 | 336 | | +| f | true | 336 | 309 | | +| f | true | 337 | 338 | | +| f | true | 338 | 334 | | +| f | true | 339 | 341 | | +| f | true | 341 | 303 | | +| f | true | 342 | 344 | | +| f | true | 344 | 301 | | +| f | true | 345 | 346 | | +| f | true | 346 | 342 | | +| f | true | 347 | 348 | | +| f | true | 348 | 345 | | +| f | true | 349 | 350 | | +| f | true | 350 | 323 | | +| f | true | 351 | 352 | | +| f | true | 352 | 349 | | +| f | true | 353 | 354 | | +| f | true | 354 | 351 | | +| f | true | 355 | 356 | | +| f | true | 356 | 311 | | +| f | true | 357 | 358 | | +| f | true | 358 | 355 | | +| f | true | 359 | 361 | | +| f | true | 361 | 297 | | +| getClass2 | false | 405 | 405 | getClass2 | diff --git a/cpp/ql/test/library-tests/destructors/cfg.ql b/cpp/ql/test/library-tests/destructors/cfg.ql new file mode 100644 index 000000000000..4c3d73d46eea --- /dev/null +++ b/cpp/ql/test/library-tests/destructors/cfg.ql @@ -0,0 +1,34 @@ +// query-type: graph +import cpp + +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() + } +} + +string scope(ControlFlowNode x) { + if exists(x.getControlFlowScope().getQualifiedName()) + then result = x.getControlFlowScope().getQualifiedName() + else result = "" +} + +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 x.getASuccessor() = y + and if x.getATrueSuccessor() = y then truelabel = "T" else truelabel = "" + and if x.getAFalseSuccessor() = y then falselabel = "F" else falselabel = "" + and label = truelabel + falselabel) +} + +from boolean isEdge, ControlFlowNode x, ControlFlowNode y, string label +where isNode(isEdge, x, y, label) or isSuccessor(isEdge, x, y, label) +select scope(x), isEdge, x, y, label + diff --git a/cpp/ql/test/library-tests/destructors/destructors.cpp b/cpp/ql/test/library-tests/destructors/destructors.cpp new file mode 100644 index 000000000000..10e0925af910 --- /dev/null +++ b/cpp/ql/test/library-tests/destructors/destructors.cpp @@ -0,0 +1,36 @@ + +class C { + public: + C(int x); + ~C(); +}; + +void f(int b1, int b2) { + C c10(110); + { + C c20(120); + { + C c30(130); + } + { + C c31(131); + if (b1) goto out; + C c32(132); + if (b2) return; + C c33(133); + } + { + C c34(134); + } + C c21(121); + } + { + C c22(122); + } + { +out: + C c23(123); + } + C c11(111); +} + diff --git a/cpp/ql/test/library-tests/destructors/destructors2.cpp b/cpp/ql/test/library-tests/destructors/destructors2.cpp new file mode 100644 index 000000000000..7a599a9ff811 --- /dev/null +++ b/cpp/ql/test/library-tests/destructors/destructors2.cpp @@ -0,0 +1,29 @@ + +// This tickled a bug where the destructor call for 'c' in 'f' could be +// followed by leaving the '~Inner' function, rather than leaving 'f'. + +class Class2 { +public: + Class2(); + ~Class2(); +}; + +Class2 getClass2(); + +class Outer { +public: + class Inner { + public: + Inner(const Class2 &c) { } + ~Inner() { } + }; + + void f2(int i) { + Class2 c = getClass2(); + if(i) { + return; + } + Inner inner(c); + } +}; + diff --git a/cpp/ql/test/library-tests/destructors/jump_destructs.expected b/cpp/ql/test/library-tests/destructors/jump_destructs.expected new file mode 100644 index 000000000000..a049189efa64 --- /dev/null +++ b/cpp/ql/test/library-tests/destructors/jump_destructs.expected @@ -0,0 +1,29 @@ +| destructors2.cpp:5:7:5:7 | Class2 | 5 | return ... | 3 | 5 | Class2 | +| destructors2.cpp:17:9:17:13 | Inner | 17 | return ... | 3 | 17 | Inner | +| destructors2.cpp:18:9:18:14 | ~Inner | 18 | return ... | 3 | 18 | ~Inner | +| destructors2.cpp:21:10:21:11 | f2 | 24 | return ... | 16 | 27 | c | +| destructors2.cpp:21:10:21:11 | f2 | 24 | return ... | 17 | 27 | call to ~Class2 | +| destructors2.cpp:21:10:21:11 | f2 | 24 | return ... | 18 | 21 | f2 | +| destructors2.cpp:21:10:21:11 | f2 | 27 | return ... | 12 | 27 | inner | +| destructors2.cpp:21:10:21:11 | f2 | 27 | return ... | 13 | 27 | call to ~Inner | +| destructors2.cpp:21:10:21:11 | f2 | 27 | return ... | 16 | 27 | c | +| destructors2.cpp:21:10:21:11 | f2 | 27 | return ... | 17 | 27 | call to ~Class2 | +| destructors2.cpp:21:10:21:11 | f2 | 27 | return ... | 18 | 21 | f2 | +| destructors.cpp:8:6:8:6 | f | 17 | goto ... | 26 | 21 | c31 | +| destructors.cpp:8:6:8:6 | f | 17 | goto ... | 27 | 21 | call to ~C | +| destructors.cpp:8:6:8:6 | f | 17 | goto ... | 28 | 26 | c20 | +| destructors.cpp:8:6:8:6 | f | 17 | goto ... | 29 | 26 | call to ~C | +| destructors.cpp:8:6:8:6 | f | 19 | return ... | 32 | 21 | c32 | +| destructors.cpp:8:6:8:6 | f | 19 | return ... | 33 | 21 | call to ~C | +| destructors.cpp:8:6:8:6 | f | 19 | return ... | 34 | 21 | c31 | +| destructors.cpp:8:6:8:6 | f | 19 | return ... | 35 | 21 | call to ~C | +| destructors.cpp:8:6:8:6 | f | 19 | return ... | 36 | 26 | c20 | +| destructors.cpp:8:6:8:6 | f | 19 | return ... | 37 | 26 | call to ~C | +| destructors.cpp:8:6:8:6 | f | 19 | return ... | 90 | 35 | c10 | +| destructors.cpp:8:6:8:6 | f | 19 | return ... | 91 | 35 | call to ~C | +| destructors.cpp:8:6:8:6 | f | 19 | return ... | 92 | 8 | f | +| destructors.cpp:8:6:8:6 | f | 35 | return ... | 81 | 35 | c11 | +| destructors.cpp:8:6:8:6 | f | 35 | return ... | 82 | 35 | call to ~C | +| destructors.cpp:8:6:8:6 | f | 35 | return ... | 90 | 35 | c10 | +| destructors.cpp:8:6:8:6 | f | 35 | return ... | 91 | 35 | call to ~C | +| destructors.cpp:8:6:8:6 | f | 35 | return ... | 92 | 8 | f | diff --git a/cpp/ql/test/library-tests/destructors/jump_destructs.ql b/cpp/ql/test/library-tests/destructors/jump_destructs.ql new file mode 100644 index 000000000000..a805162955f1 --- /dev/null +++ b/cpp/ql/test/library-tests/destructors/jump_destructs.ql @@ -0,0 +1,22 @@ +import cpp + +ControlFlowNode getANonLabelSuccessor(ControlFlowNode n) { + n.getASuccessor() = result and not result instanceof LabelStmt +} + +class Jump extends Locatable { + Jump() { + this instanceof GotoStmt or + this instanceof ReturnStmt + } +} + +from Jump j, ControlFlowNode n +where getANonLabelSuccessor+(j) = n +select j.(ControlFlowNode).getControlFlowScope(), + j.getLocation().getStartLine(), + j.toString(), + count(n.getAPredecessor*()), // Improves the output's sort order + n.getLocation().getStartLine(), + n.toString() + diff --git a/cpp/ql/test/library-tests/destructors/mkdot.pl b/cpp/ql/test/library-tests/destructors/mkdot.pl new file mode 100755 index 000000000000..e228be564c62 --- /dev/null +++ b/cpp/ql/test/library-tests/destructors/mkdot.pl @@ -0,0 +1,54 @@ +#!/usr/bin/perl -w + +# This currently won't parse the test output, as showIds isn't enabled. + +use strict; + +my %info; + +sub add_info { + my $id = shift; + my $str = shift; + + if (defined($info{$id})) { + if ($info{$id} ne $str) { + die "Mismatch"; + } + } else { + $info{$id} = $str; + } +} + +open OUT, "> cfg.dot" or die "open failed: $!"; + +print OUT "digraph G {\n"; + +open IN, "< cfg.expected" or die "open failed: $!"; +while () { + if (/^\| [0-9]+ \| [0-9]+ \| ([0-9]+) \| ([^|]+) \| ([0-9]+) \| ([^|]+) \|$/) { + my $srcid = $1; + my $srcstr = $2; + my $dstid = $3; + my $dststr = $4; + &add_info($srcid, $srcstr); + &add_info($dstid, $dststr); + print OUT "n$srcid -> n$dstid;\n"; + } elsif (/^$/) { + # Nothing + } else { + die "Bad line: $_"; + } +} +close IN; + +for my $id (keys %info) { + my $str = $info{$id}; + print OUT qq(n$id [label="$str"];\n); +} + +print OUT "}\n"; +close OUT; + +system ("dot", "-Tpng", "cfg.dot", "-o", "cfg.png") == 0 + or die "dot failed: $?"; + diff --git a/cpp/ql/test/library-tests/diagnostics/diagnostics.c b/cpp/ql/test/library-tests/diagnostics/diagnostics.c new file mode 100644 index 000000000000..dd1fda6b6c17 --- /dev/null +++ b/cpp/ql/test/library-tests/diagnostics/diagnostics.c @@ -0,0 +1,3 @@ + +int int int i; + diff --git a/cpp/ql/test/library-tests/diagnostics/diags.expected b/cpp/ql/test/library-tests/diagnostics/diags.expected new file mode 100644 index 000000000000..1a0b8fc7e309 --- /dev/null +++ b/cpp/ql/test/library-tests/diagnostics/diags.expected @@ -0,0 +1,3 @@ +| diagnostics.c:2:5:2:5 | invalid combination of type specifiers | 4 | bad_combination_of_type_specifiers | "diagnostics.c", line 2: error: invalid combination of type specifiers\n int int int i;\n ^\n\n | +| diagnostics.c:2:9:2:9 | invalid combination of type specifiers | 4 | bad_combination_of_type_specifiers | "diagnostics.c", line 2: error: invalid combination of type specifiers\n int int int i;\n ^\n\n | +| file://:0:0:0:0 | There was an error during this compilation | 4 | Error occurred | There was an error during this compilation | diff --git a/cpp/ql/test/library-tests/diagnostics/diags.ql b/cpp/ql/test/library-tests/diagnostics/diags.ql new file mode 100644 index 000000000000..8c255348ea7a --- /dev/null +++ b/cpp/ql/test/library-tests/diagnostics/diags.ql @@ -0,0 +1,4 @@ +import cpp + +from Diagnostic d +select d, d.getSeverity(), d.getTag(), d.getFullMessage() diff --git a/cpp/ql/test/library-tests/diagnostics/options b/cpp/ql/test/library-tests/diagnostics/options new file mode 100644 index 000000000000..4181beff3c28 --- /dev/null +++ b/cpp/ql/test/library-tests/diagnostics/options @@ -0,0 +1 @@ +extractor_flags: --expect_errors diff --git a/cpp/ql/test/library-tests/digraphs/digraphs.cpp b/cpp/ql/test/library-tests/digraphs/digraphs.cpp new file mode 100644 index 000000000000..d0b82e6ddceb --- /dev/null +++ b/cpp/ql/test/library-tests/digraphs/digraphs.cpp @@ -0,0 +1,7 @@ +static const int x = 1; + +int main() <% /* <% is { */ + return "ab"<: /* <: is [ */ + 0<::x /* <: is not { here; the < is a <, and the :: is a name qualifier for x */ + :>; /* :> is ] */ +%> /* %> is } */ diff --git a/cpp/ql/test/library-tests/digraphs/digraphs.expected b/cpp/ql/test/library-tests/digraphs/digraphs.expected new file mode 100644 index 000000000000..09e8a6249266 --- /dev/null +++ b/cpp/ql/test/library-tests/digraphs/digraphs.expected @@ -0,0 +1 @@ +| digraphs.cpp:5:11:5:15 | ... < ... | digraphs.cpp:3:5:3:8 | main | diff --git a/cpp/ql/test/library-tests/digraphs/digraphs.ql b/cpp/ql/test/library-tests/digraphs/digraphs.ql new file mode 100644 index 000000000000..b4a4cf52801e --- /dev/null +++ b/cpp/ql/test/library-tests/digraphs/digraphs.ql @@ -0,0 +1,4 @@ +import cpp + +from LTExpr lt +select lt, lt.getEnclosingFunction() diff --git a/cpp/ql/test/library-tests/digraphs/options b/cpp/ql/test/library-tests/digraphs/options new file mode 100644 index 000000000000..2f87d3828f86 --- /dev/null +++ b/cpp/ql/test/library-tests/digraphs/options @@ -0,0 +1 @@ +extractor_flags: --gnu_version 40801 diff --git a/cpp/ql/test/library-tests/enums/enums/Enums1.expected b/cpp/ql/test/library-tests/enums/enums/Enums1.expected new file mode 100644 index 000000000000..ed3517be2e47 --- /dev/null +++ b/cpp/ql/test/library-tests/enums/enums/Enums1.expected @@ -0,0 +1,24 @@ +| enums.cpp:3:12:3:14 | sun | sun | 1 | Day | 1 | 1 | 1 | +| enums.cpp:3:17:3:19 | mon | mon | 1 | Day | 1 | 1 | 1 | +| enums.cpp:3:22:3:24 | tue | tue | 1 | Day | 1 | 1 | 1 | +| enums.cpp:3:27:3:29 | wed | wed | 1 | Day | 1 | 1 | 1 | +| enums.cpp:3:32:3:34 | thu | thu | 1 | Day | 1 | 1 | 1 | +| enums.cpp:3:37:3:39 | fri | fri | 1 | Day | 1 | 1 | 1 | +| enums.cpp:3:42:3:44 | sat | sat | 1 | Day | 1 | 1 | 1 | +| enums.cpp:4:13:4:16 | sun2 | sun2 | 1 | Day2 | 1 | 1 | 1 | +| enums.cpp:4:23:4:26 | mon2 | mon2 | 1 | Day2 | 1 | 1 | 1 | +| enums.cpp:4:29:4:32 | tue2 | tue2 | 1 | Day2 | 1 | 1 | 1 | +| enums.cpp:5:13:5:13 | b | b | 1 | Flag | 1 | 1 | 1 | +| enums.cpp:5:22:5:22 | c | c | 1 | Flag | 1 | 1 | 1 | +| enums.cpp:5:31:5:31 | d | d | 1 | Flag | 1 | 1 | 1 | +| enums.ms.c:2:3:2:6 | zero | zero | 1 | numbers | 1 | 1 | 1 | +| enums.ms.c:2:9:2:11 | one | one | 1 | numbers | 1 | 1 | 1 | +| scoped.cpp:3:5:3:5 | X | X | 1 | E1 | 1 | 1 | 1 | +| scoped.cpp:3:8:3:8 | Y | Y | 1 | E1 | 1 | 1 | 1 | +| scoped.cpp:3:11:3:11 | Z | Z | 1 | E1 | 1 | 1 | 1 | +| scoped.cpp:7:5:7:5 | X | X | 1 | E2 | 1 | 1 | 1 | +| scoped.cpp:7:8:7:8 | A | A | 1 | E2 | 1 | 1 | 1 | +| scoped.cpp:7:11:7:11 | B | B | 1 | E2 | 1 | 1 | 1 | +| scoped.cpp:11:5:11:5 | X | X | 1 | E3 | 1 | 1 | 1 | +| scoped.cpp:11:8:11:8 | C | C | 1 | E3 | 1 | 1 | 1 | +| scoped.cpp:11:11:11:11 | D | D | 1 | E3 | 1 | 1 | 1 | diff --git a/cpp/ql/test/library-tests/enums/enums/Enums1.ql b/cpp/ql/test/library-tests/enums/enums/Enums1.ql new file mode 100644 index 000000000000..af544276768e --- /dev/null +++ b/cpp/ql/test/library-tests/enums/enums/Enums1.ql @@ -0,0 +1,11 @@ +import cpp + +string declaringEnum(EnumConstant c) { + if exists(c.getDeclaringEnum().toString()) + then result = c.getDeclaringEnum().toString() + else result = "" +} + +from EnumConstant c, string cName +where c.hasName(cName) +select c, cName, count(c.getDeclaringEnum()), declaringEnum(c), count(c.getLocation()), count(c.getDefinitionLocation()), count(c.getADeclarationLocation()) diff --git a/cpp/ql/test/library-tests/enums/enums/Enums2.expected b/cpp/ql/test/library-tests/enums/enums/Enums2.expected new file mode 100644 index 000000000000..b975f2eae9ef --- /dev/null +++ b/cpp/ql/test/library-tests/enums/enums/Enums2.expected @@ -0,0 +1,5 @@ +| enums.cpp:14:14:14:16 | sat | Day | +| enums.cpp:14:24:14:26 | sun | Day | +| enums.ms.c:7:10:7:12 | one | numbers | +| enums.ms.c:7:22:7:24 | one | numbers | +| enums.ms.c:8:21:8:24 | zero | numbers | diff --git a/cpp/ql/test/library-tests/enums/enums/Enums2.ql b/cpp/ql/test/library-tests/enums/enums/Enums2.ql new file mode 100644 index 000000000000..d970520119ae --- /dev/null +++ b/cpp/ql/test/library-tests/enums/enums/Enums2.ql @@ -0,0 +1,9 @@ +/** + * @name Enums2 + * @kind table + */ +import cpp + +from EnumConstantAccess a, string name +where a.getTarget().getDeclaringEnum().hasName(name) +select a, name diff --git a/cpp/ql/test/library-tests/enums/enums/Enums3.expected b/cpp/ql/test/library-tests/enums/enums/Enums3.expected new file mode 100644 index 000000000000..33cba74bc57b --- /dev/null +++ b/cpp/ql/test/library-tests/enums/enums/Enums3.expected @@ -0,0 +1,8 @@ +| enums.cpp:3:6:3:8 | Day | false | +| enums.cpp:4:6:4:9 | Day2 | false | +| enums.cpp:5:6:5:9 | Flag | false | +| enums.ms.c:1:6:1:12 | numbers | false | +| scoped.cpp:2:12:2:13 | E1 | true | +| scoped.cpp:6:12:6:13 | E2 | true | +| scoped.cpp:10:13:10:14 | E3 | true | +| scoped.cpp:16:14:16:18 | State | true | diff --git a/cpp/ql/test/library-tests/enums/enums/Enums3.ql b/cpp/ql/test/library-tests/enums/enums/Enums3.ql new file mode 100644 index 000000000000..33e87f2e09ba --- /dev/null +++ b/cpp/ql/test/library-tests/enums/enums/Enums3.ql @@ -0,0 +1,6 @@ +import cpp + +from Enum e, boolean isScoped +where if e instanceof ScopedEnum then isScoped = true else isScoped = false +select e, isScoped + diff --git a/cpp/ql/test/library-tests/enums/enums/enums.cpp b/cpp/ql/test/library-tests/enums/enums/enums.cpp new file mode 100644 index 000000000000..1fb523eca805 --- /dev/null +++ b/cpp/ql/test/library-tests/enums/enums/enums.cpp @@ -0,0 +1,15 @@ +const int j = 0; + +enum Day { sun, mon, tue, wed, thu, fri, sat }; +enum Day2 { sun2 = j, mon2, tue2 }; +enum Flag { b = 'a', c = 'b', d = 'd' }; + +Day& operator++(Day& d) +{ + int i = d; + Flag f = Flag(7); + Flag g = Flag(8); + //const int *p = &sat; + Day2 d2 = (Day2)d; + return d = (sat==d) ? sun: Day(d+1); +} diff --git a/cpp/ql/test/library-tests/enums/enums/enums.ms.c b/cpp/ql/test/library-tests/enums/enums/enums.ms.c new file mode 100644 index 000000000000..af1d9dbf5279 --- /dev/null +++ b/cpp/ql/test/library-tests/enums/enums/enums.ms.c @@ -0,0 +1,11 @@ +enum numbers { + zero, one +}; + +static int is_one(int x) { + switch(x) { + case one: return one; + default: return zero; + } +} +// semmle-extractor-options: --microsoft diff --git a/cpp/ql/test/library-tests/enums/enums/scoped.cpp b/cpp/ql/test/library-tests/enums/enums/scoped.cpp new file mode 100644 index 000000000000..51454077887d --- /dev/null +++ b/cpp/ql/test/library-tests/enums/enums/scoped.cpp @@ -0,0 +1,19 @@ + +enum class E1 { + X, Y, Z +}; + +enum class E2 { + X, A, B +}; + +enum struct E3 { + X, C, D +}; + +class E4 { + private: + enum class State : int; +}; + + diff --git a/cpp/ql/test/library-tests/enums/multi_file/a.c b/cpp/ql/test/library-tests/enums/multi_file/a.c new file mode 100644 index 000000000000..c4452d40dcca --- /dev/null +++ b/cpp/ql/test/library-tests/enums/multi_file/a.c @@ -0,0 +1,5 @@ + +enum foo { + bar = 23 +}; + diff --git a/cpp/ql/test/library-tests/enums/multi_file/a.h b/cpp/ql/test/library-tests/enums/multi_file/a.h new file mode 100644 index 000000000000..c4452d40dcca --- /dev/null +++ b/cpp/ql/test/library-tests/enums/multi_file/a.h @@ -0,0 +1,5 @@ + +enum foo { + bar = 23 +}; + diff --git a/cpp/ql/test/library-tests/enums/multi_file/b.c b/cpp/ql/test/library-tests/enums/multi_file/b.c new file mode 100644 index 000000000000..789a1a3c89bf --- /dev/null +++ b/cpp/ql/test/library-tests/enums/multi_file/b.c @@ -0,0 +1,6 @@ + +#include "a.h" + +void b() { + int xs[bar]; +} diff --git a/cpp/ql/test/library-tests/enums/multi_file/eca.expected b/cpp/ql/test/library-tests/enums/multi_file/eca.expected new file mode 100644 index 000000000000..af7f1bae3f3b --- /dev/null +++ b/cpp/ql/test/library-tests/enums/multi_file/eca.expected @@ -0,0 +1 @@ +| b.c:5:10:5:12 | bar | diff --git a/cpp/ql/test/library-tests/enums/multi_file/eca.ql b/cpp/ql/test/library-tests/enums/multi_file/eca.ql new file mode 100644 index 000000000000..78b76f2274bc --- /dev/null +++ b/cpp/ql/test/library-tests/enums/multi_file/eca.ql @@ -0,0 +1,4 @@ +import cpp + +from EnumConstantAccess eca +select eca diff --git a/cpp/ql/test/library-tests/enums/multi_file/enumConstant.expected b/cpp/ql/test/library-tests/enums/multi_file/enumConstant.expected new file mode 100644 index 000000000000..ed50720eeb72 --- /dev/null +++ b/cpp/ql/test/library-tests/enums/multi_file/enumConstant.expected @@ -0,0 +1,2 @@ +| a.c:3:5:3:7 | bar | +| a.h:3:5:3:7 | bar | diff --git a/cpp/ql/test/library-tests/enums/multi_file/enumConstant.ql b/cpp/ql/test/library-tests/enums/multi_file/enumConstant.ql new file mode 100644 index 000000000000..c065bbf05b35 --- /dev/null +++ b/cpp/ql/test/library-tests/enums/multi_file/enumConstant.ql @@ -0,0 +1,5 @@ +import cpp + +from EnumConstant e +select e + diff --git a/cpp/ql/test/library-tests/enums/typedefs/exprs.expected b/cpp/ql/test/library-tests/enums/typedefs/exprs.expected new file mode 100644 index 000000000000..7a9ce45b403e --- /dev/null +++ b/cpp/ql/test/library-tests/enums/typedefs/exprs.expected @@ -0,0 +1,2 @@ +| file://:0:0:0:0 | 0 | file://:0:0:0:0 | int | +| test.c:7:20:7:21 | E | file://:0:0:0:0 | int | diff --git a/cpp/ql/test/library-tests/enums/typedefs/exprs.ql b/cpp/ql/test/library-tests/enums/typedefs/exprs.ql new file mode 100644 index 000000000000..530939c66c18 --- /dev/null +++ b/cpp/ql/test/library-tests/enums/typedefs/exprs.ql @@ -0,0 +1,4 @@ +import cpp + +from Expr e +select e, e.getType() diff --git a/cpp/ql/test/library-tests/enums/typedefs/test.c b/cpp/ql/test/library-tests/enums/typedefs/test.c new file mode 100644 index 000000000000..4fb9c180382f --- /dev/null +++ b/cpp/ql/test/library-tests/enums/typedefs/test.c @@ -0,0 +1,9 @@ + +enum { + E +}; + +f(void) { + __typeof__(E) j = E; +} + diff --git a/cpp/ql/test/library-tests/environment/environmentRead.expected b/cpp/ql/test/library-tests/environment/environmentRead.expected new file mode 100644 index 000000000000..4bd74f5dd6a7 --- /dev/null +++ b/cpp/ql/test/library-tests/environment/environmentRead.expected @@ -0,0 +1,2 @@ +| test.c:4:5:4:10 | call to getenv | HOME | getenv | +| test.c:6:5:6:17 | call to secure_getenv | QUERY_STRING | secure_getenv | diff --git a/cpp/ql/test/library-tests/environment/environmentRead.ql b/cpp/ql/test/library-tests/environment/environmentRead.ql new file mode 100644 index 000000000000..b32fdb2b5c15 --- /dev/null +++ b/cpp/ql/test/library-tests/environment/environmentRead.ql @@ -0,0 +1,4 @@ +import semmle.code.cpp.commons.Environment + +from EnvironmentRead read +select read, read.getEnvironmentVariable(), read.getSourceDescription() diff --git a/cpp/ql/test/library-tests/environment/stdlib.h b/cpp/ql/test/library-tests/environment/stdlib.h new file mode 100644 index 000000000000..cc9131004a18 --- /dev/null +++ b/cpp/ql/test/library-tests/environment/stdlib.h @@ -0,0 +1,2 @@ +char *getenv(const char *name); +char *secure_getenv(const char *name); diff --git a/cpp/ql/test/library-tests/environment/test.c b/cpp/ql/test/library-tests/environment/test.c new file mode 100644 index 000000000000..5c49d09f818a --- /dev/null +++ b/cpp/ql/test/library-tests/environment/test.c @@ -0,0 +1,7 @@ +#include "stdlib.h" + +void test(char *name) { + getenv("HOME"); + getenv(name); + secure_getenv("QUERY_STRING"); +} diff --git a/cpp/ql/test/library-tests/exprs/comparison_operation/comparison_operation.expected b/cpp/ql/test/library-tests/exprs/comparison_operation/comparison_operation.expected new file mode 100644 index 000000000000..ad9bfc8cd527 --- /dev/null +++ b/cpp/ql/test/library-tests/exprs/comparison_operation/comparison_operation.expected @@ -0,0 +1,20 @@ +| test.cpp:6:6:6:11 | ... == ... | EQExpr | +| test.cpp:6:6:6:11 | ... == ... | EqualityOperation | +| test.cpp:7:6:7:11 | ... != ... | EqualityOperation | +| test.cpp:7:6:7:11 | ... != ... | NEExpr | +| test.cpp:8:6:8:10 | ... < ... | LTExpr | +| test.cpp:8:6:8:10 | ... < ... | RelationalOperation | +| test.cpp:8:6:8:10 | ... < ... | getGreaterOperand() = 1 | +| test.cpp:8:6:8:10 | ... < ... | getLesserOperand() = x | +| test.cpp:9:6:9:10 | ... > ... | GTExpr | +| test.cpp:9:6:9:10 | ... > ... | RelationalOperation | +| test.cpp:9:6:9:10 | ... > ... | getGreaterOperand() = x | +| test.cpp:9:6:9:10 | ... > ... | getLesserOperand() = 1 | +| test.cpp:10:6:10:11 | ... <= ... | LEExpr | +| test.cpp:10:6:10:11 | ... <= ... | RelationalOperation | +| test.cpp:10:6:10:11 | ... <= ... | getGreaterOperand() = 1 | +| test.cpp:10:6:10:11 | ... <= ... | getLesserOperand() = x | +| test.cpp:11:6:11:11 | ... >= ... | GEExpr | +| test.cpp:11:6:11:11 | ... >= ... | RelationalOperation | +| test.cpp:11:6:11:11 | ... >= ... | getGreaterOperand() = x | +| test.cpp:11:6:11:11 | ... >= ... | getLesserOperand() = 1 | diff --git a/cpp/ql/test/library-tests/exprs/comparison_operation/comparison_operation.ql b/cpp/ql/test/library-tests/exprs/comparison_operation/comparison_operation.ql new file mode 100644 index 000000000000..0039f8da903f --- /dev/null +++ b/cpp/ql/test/library-tests/exprs/comparison_operation/comparison_operation.ql @@ -0,0 +1,26 @@ +import cpp + +from ComparisonOperation co, string s +where + ( + co instanceof EqualityOperation and s = "EqualityOperation" + ) or ( + co instanceof EQExpr and s = "EQExpr" + ) or ( + co instanceof NEExpr and s = "NEExpr" + ) or ( + co instanceof RelationalOperation and s = "RelationalOperation" + ) or ( + s = "getGreaterOperand() = " + co.(RelationalOperation).getGreaterOperand().toString() + ) or ( + s = "getLesserOperand() = " + co.(RelationalOperation).getLesserOperand().toString() + ) or ( + co instanceof GTExpr and s = "GTExpr" + ) or ( + co instanceof LTExpr and s = "LTExpr" + ) or ( + co instanceof GEExpr and s = "GEExpr" + ) or ( + co instanceof LEExpr and s = "LEExpr" + ) +select co, s diff --git a/cpp/ql/test/library-tests/exprs/comparison_operation/test.cpp b/cpp/ql/test/library-tests/exprs/comparison_operation/test.cpp new file mode 100644 index 000000000000..0735583e171e --- /dev/null +++ b/cpp/ql/test/library-tests/exprs/comparison_operation/test.cpp @@ -0,0 +1,14 @@ + +int main() +{ + int x; + + if (x == 1) {} + if (x != 1) {} + if (x < 1) {} + if (x > 1) {} + if (x <= 1) {} + if (x >= 1) {} + + return 0; +} diff --git a/cpp/ql/test/library-tests/exprs/min_max/expr.expected b/cpp/ql/test/library-tests/exprs/min_max/expr.expected new file mode 100644 index 000000000000..ebd03f8b6990 --- /dev/null +++ b/cpp/ql/test/library-tests/exprs/min_max/expr.expected @@ -0,0 +1,6 @@ +| test.cpp:3:13:3:13 | i | +| test.cpp:3:13:3:18 | ... ? ... | +| test.cpp:4:18:4:18 | j | diff --git a/cpp/ql/test/library-tests/exprs/min_max/expr.ql b/cpp/ql/test/library-tests/exprs/min_max/expr.ql new file mode 100644 index 000000000000..3c2a3269e72e --- /dev/null +++ b/cpp/ql/test/library-tests/exprs/min_max/expr.ql @@ -0,0 +1,5 @@ +import cpp + +from Expr e +select e + diff --git a/cpp/ql/test/library-tests/exprs/min_max/test.cpp b/cpp/ql/test/library-tests/exprs/min_max/test.cpp new file mode 100644 index 000000000000..d8be21af82aa --- /dev/null +++ b/cpp/ql/test/library-tests/exprs/min_max/test.cpp @@ -0,0 +1,6 @@ + +void f(int i, int j) { + int k = i ? j; +} + diff --git a/cpp/ql/test/library-tests/exprs/unary_operation/test.cpp b/cpp/ql/test/library-tests/exprs/unary_operation/test.cpp new file mode 100644 index 000000000000..8b33083b13f4 --- /dev/null +++ b/cpp/ql/test/library-tests/exprs/unary_operation/test.cpp @@ -0,0 +1,15 @@ + +int main() +{ + int i; + int *ip; + + i = +(-1); + i++; + ip = &i; + *ip--; + ++i; + --i; + + return 0; +} diff --git a/cpp/ql/test/library-tests/exprs/unary_operation/unary_operation.expected b/cpp/ql/test/library-tests/exprs/unary_operation/unary_operation.expected new file mode 100644 index 000000000000..366c71ea6682 --- /dev/null +++ b/cpp/ql/test/library-tests/exprs/unary_operation/unary_operation.expected @@ -0,0 +1,8 @@ +| test.cpp:7:6:7:10 | + ... | + | UnaryArithmeticOperation, UnaryPlusExpr | +| test.cpp:7:8:7:9 | - ... | - | UnaryArithmeticOperation, UnaryMinusExpr | +| test.cpp:8:2:8:4 | ... ++ | ++ | CrementOperation, IncrementOperation, PostfixCrementOperation, UnaryArithmeticOperation | +| test.cpp:9:7:9:8 | & ... | & | AddressOfExpr, getAddressable() = i | +| test.cpp:10:2:10:6 | * ... | * | PointerDereferenceExpr | +| test.cpp:10:3:10:6 | ... -- | -- | CrementOperation, DecrementOperation, PostfixCrementOperation, UnaryArithmeticOperation | +| test.cpp:11:2:11:4 | ++ ... | ++ | CrementOperation, IncrementOperation, PrefixCrementOperation, UnaryArithmeticOperation | +| test.cpp:12:2:12:4 | -- ... | -- | CrementOperation, DecrementOperation, PrefixCrementOperation, UnaryArithmeticOperation | diff --git a/cpp/ql/test/library-tests/exprs/unary_operation/unary_operation.ql b/cpp/ql/test/library-tests/exprs/unary_operation/unary_operation.ql new file mode 100644 index 000000000000..5ffeebd7155d --- /dev/null +++ b/cpp/ql/test/library-tests/exprs/unary_operation/unary_operation.ql @@ -0,0 +1,37 @@ +import cpp + +predicate describe(UnaryOperation uo, string s) +{ + ( + uo instanceof UnaryArithmeticOperation and s = "UnaryArithmeticOperation" + ) or ( + uo instanceof UnaryMinusExpr and s = "UnaryMinusExpr" + ) or ( + uo instanceof UnaryPlusExpr and s = "UnaryPlusExpr" + ) or ( + uo instanceof ConjugationExpr and s = "ConjugationExpr" + ) or ( + uo instanceof CrementOperation and s = "CrementOperation" + ) or ( + uo instanceof IncrementOperation and s = "IncrementOperation" + ) or ( + uo instanceof DecrementOperation and s = "DecrementOperation" + ) or ( + uo instanceof PrefixCrementOperation and s = "PrefixCrementOperation" + ) or ( + uo instanceof PostfixCrementOperation and s = "PostfixCrementOperation" + ) or ( + uo instanceof AddressOfExpr and s = "AddressOfExpr" + ) or ( + s = "getAddressable() = " + uo.(AddressOfExpr).getAddressable().toString() + ) or ( + uo instanceof PointerDereferenceExpr and s = "PointerDereferenceExpr" + ) or ( + uo instanceof UnaryLogicalOperation and s = "UnaryLogicalOperation" + ) or ( + uo instanceof NotExpr and s = "NotExpr" + ) +} + +from UnaryOperation uo +select uo, uo.getOperator(), concat(string s | describe(uo, s) | s, ", ") diff --git a/cpp/ql/test/library-tests/exprs/unevaluated/test.cpp b/cpp/ql/test/library-tests/exprs/unevaluated/test.cpp new file mode 100644 index 000000000000..85465fe29f96 --- /dev/null +++ b/cpp/ql/test/library-tests/exprs/unevaluated/test.cpp @@ -0,0 +1,64 @@ +#include "typeinfo.h" + +int test1() { + int x; + return sizeof(x); // unevaluated +} + +int test2() { + int x; + return sizeof(x + 1); // unevaluated +} + +class MyClass { +public: + int field; +}; + +void test3() { + MyClass my_class; + int x; + decltype(my_class.field) y = x; // unevaluated +} + +void test4() { + int x; + noexcept(x); // unevaluated +} + +void test5() { + int x; + typeid(x); // unevaluated +} + + +class NoVirtual { +public: + int x; +}; + +void test6() { + NoVirtual x; + typeid(*&x); // unevaluated +} + +class Virtual { +public: + int x; + virtual int GetX(); +}; + +void test7() { + Virtual x; + typeid(x); // evaluated +} + +void test8() { + typeid(4); // unevaluated +} + +Virtual getAVirtual(); + +void test9() { + typeid(getAVirtual()); // unevaluated +} diff --git a/cpp/ql/test/library-tests/exprs/unevaluated/typeinfo.h b/cpp/ql/test/library-tests/exprs/unevaluated/typeinfo.h new file mode 100644 index 000000000000..d91536479b45 --- /dev/null +++ b/cpp/ql/test/library-tests/exprs/unevaluated/typeinfo.h @@ -0,0 +1,35 @@ +namespace std { +class exception { +}; + +class type_info +{ +public: + virtual ~type_info(); + bool operator==(const type_info& rhs) const noexcept; + bool operator!=(const type_info& rhs) const noexcept; + bool before(const type_info& rhs) const noexcept; + int hash_code() const noexcept; + const char* name() const noexcept; + type_info(const type_info& rhs) = delete; + type_info& operator=(const type_info& rhs) = delete; +}; +class bad_cast + : public exception +{ +public: + bad_cast() noexcept; + bad_cast(const bad_cast&) noexcept; + bad_cast& operator=(const bad_cast&) noexcept; + virtual const char* what() const noexcept; +}; +class bad_typeid + : public exception +{ +public: + bad_typeid() noexcept; + bad_typeid(const bad_typeid&) noexcept; + bad_typeid& operator=(const bad_typeid&) noexcept; + virtual const char* what() const noexcept; +}; +} diff --git a/cpp/ql/test/library-tests/exprs/unevaluated/unevaluated.expected b/cpp/ql/test/library-tests/exprs/unevaluated/unevaluated.expected new file mode 100644 index 000000000000..6015065b4f70 --- /dev/null +++ b/cpp/ql/test/library-tests/exprs/unevaluated/unevaluated.expected @@ -0,0 +1,15 @@ +| test.cpp:5:16:5:16 | x | +| test.cpp:5:16:5:17 | (...) | +| test.cpp:10:16:10:16 | x | +| test.cpp:10:16:10:20 | ... + ... | +| test.cpp:10:16:10:21 | (...) | +| test.cpp:10:20:10:20 | 1 | +| test.cpp:21:11:21:18 | my_class | +| test.cpp:21:20:21:24 | field | +| test.cpp:26:11:26:11 | x | +| test.cpp:31:9:31:9 | x | +| test.cpp:42:9:42:11 | * ... | +| test.cpp:42:10:42:11 | & ... | +| test.cpp:42:11:42:11 | x | +| test.cpp:57:9:57:9 | 4 | +| test.cpp:63:9:63:19 | call to getAVirtual | diff --git a/cpp/ql/test/library-tests/exprs/unevaluated/unevaluated.ql b/cpp/ql/test/library-tests/exprs/unevaluated/unevaluated.ql new file mode 100644 index 000000000000..914707401a1a --- /dev/null +++ b/cpp/ql/test/library-tests/exprs/unevaluated/unevaluated.ql @@ -0,0 +1,5 @@ +import cpp + +from Expr e +where e.isUnevaluated() +select e diff --git a/cpp/ql/test/library-tests/exprs/value_categories/loads.expected b/cpp/ql/test/library-tests/exprs/value_categories/loads.expected new file mode 100644 index 000000000000..b2d0bde8a4e8 --- /dev/null +++ b/cpp/ql/test/library-tests/exprs/value_categories/loads.expected @@ -0,0 +1,17 @@ +| value_categories.cpp:6:9:6:9 | x | +| value_categories.cpp:9:12:9:12 | y | +| value_categories.cpp:16:6:16:6 | p | +| value_categories.cpp:16:10:16:11 | * ... | +| value_categories.cpp:16:11:16:11 | q | +| value_categories.cpp:22:12:22:14 | * ... | +| value_categories.cpp:29:13:29:13 | (reference dereference) | +| value_categories.cpp:29:13:29:13 | r | +| value_categories.cpp:33:15:33:15 | r | +| value_categories.cpp:38:5:38:5 | r | +| value_categories.cpp:42:12:42:12 | r | +| value_categories.cpp:57:5:57:6 | rr | +| value_categories.cpp:62:31:62:32 | rr | +| value_categories.cpp:74:12:74:12 | s | +| value_categories.cpp:81:18:81:21 | Func | +| value_categories.cpp:98:15:98:15 | x | +| value_categories.cpp:108:14:108:24 | access to array | diff --git a/cpp/ql/test/library-tests/exprs/value_categories/loads.ql b/cpp/ql/test/library-tests/exprs/value_categories/loads.ql new file mode 100644 index 000000000000..dcc4f4b4c2eb --- /dev/null +++ b/cpp/ql/test/library-tests/exprs/value_categories/loads.ql @@ -0,0 +1,5 @@ +import cpp + +from Expr e +where e.hasLValueToRValueConversion() +select e diff --git a/cpp/ql/test/library-tests/exprs/value_categories/value_categories.cpp b/cpp/ql/test/library-tests/exprs/value_categories/value_categories.cpp new file mode 100644 index 000000000000..05cd6c16583f --- /dev/null +++ b/cpp/ql/test/library-tests/exprs/value_categories/value_categories.cpp @@ -0,0 +1,155 @@ +int ParamsAndLocals(int x) +{ + int y; + + // y is an lvalue, as is the result of the assignment. x is a load. + y = x + 1; + + // y is a load. + return y; +} + +int Dereference(int* p, int *q) +{ + // *p is an lvalue, as is the result of the assignment. + // p, q, and *q are loads. + *p = *q; + + int x = 5; + + // x is an lvalue. + // *&x is a load. + return *&x; +} + +int& References(int& r) +{ + // The reference r is a load, as is the result of dereferencing the + // reference r. + int x = r; + + // The result of dereferencing the reference r is an lvalue. + // The reference r is a load. + int* p = &r; + + // The result of deferencing the reference r is an lvalue, as is the result + // of the assignment. + // The reference r is a load. + r = 5; + + // The result of dereferencing the reference r is an lvalue. + // The reference r is a load. + return r; +} + +int&& GetRValueRef(); +void CallRValueRef(int&& rr); + +int&& RValueReferences(int&& rr) +{ + // The result of dereferencing the reference returned by GetRValueRef() is + // an xvalue. + CallRValueRef(GetRValueRef()); + + // The result of dereferencing the reference rr is an lvalue, as is the + // result of the assignment. + // The reference rr is a load. + rr = 5; + + // The result of the static cast is an xvalue. The result of dereferencing + // the reference rr is an lvalue. + // The reference rr is a load. + return static_cast(rr); +} + +struct S +{ + int MemberFunction(); +}; + +int CallMemberFunction(S& s) +{ + // The result of dereferencing the reference s is an lvalue. + // The reference s is a load. + return s.MemberFunction(); +} + +int Func(); +void AddressOfFunc() +{ + // Func is a load due to the function-to-function-pointer conversions. + int (*p)() = Func; +} + +struct T +{ + int x; + int y; + int MemberFunc(float); +}; + +void FieldAccesses() +{ + T t; + // t, t.x, and the assignment are all lvalues. + t.x = 0; + // t is an lvalue. + // t.x is a load. + int a = t.x; +} + +void StringLiterals() +{ + // All string literals are lvalues + "String"; + const char* p = "String"; + const char (&a)[7] = "String"; + // The array access is a load + char c = "String"[1]; +} + +void Crement() +{ + int x = 0; + // x is an lvalue. + x++; + // x is an lvalue. + x--; + // x is an lvalue, as is the result of ++x. + ++x; + // x is an lvalue, as is the result of --x. + --x; +} + +void CompoundAssignment() +{ + int x = 0; + + // x is an lvalue, as is the result of x += 1 + x += 1; + // x is an lvalue, as is the result of x -= 1 + x -= 1; + // x is an lvalue, as is the result of x *= 1 + x *= 1; + // x is an lvalue, as is the result of x /= 1 + x /= 1; + // x is an lvalue, as is the result of x %= 1 + x %= 1; + // x is an lvalue, as is the result of x <<= 1 + x <<= 1; + // x is an lvalue, as is the result of x >>= 1 + x >>= 1; + // x is an lvalue, as is the result of x |= 1 + x |= 1; + // x is an lvalue, as is the result of x &= 1 + x &= 1; + // x is an lvalue, as is the result of x ^= 1 + x ^= 1; +} + +void PointerToMemberLiteral() +{ + // All pointer-to-member literals are prvalues + int T::* pmd = &T::x; + int (T::* pmf)(float) = &T::MemberFunc; +} diff --git a/cpp/ql/test/library-tests/exprs/value_categories/value_categories.expected b/cpp/ql/test/library-tests/exprs/value_categories/value_categories.expected new file mode 100644 index 000000000000..631f1b336ca2 --- /dev/null +++ b/cpp/ql/test/library-tests/exprs/value_categories/value_categories.expected @@ -0,0 +1,49 @@ +| value_categories.cpp:6:5:6:5 | y | int | lvalue | +| value_categories.cpp:6:5:6:13 | ... = ... | int | lvalue | +| value_categories.cpp:16:5:16:6 | * ... | int | lvalue | +| value_categories.cpp:16:5:16:11 | ... = ... | int | lvalue | +| value_categories.cpp:22:14:22:14 | x | int | lvalue | +| value_categories.cpp:33:15:33:15 | (reference dereference) | int | lvalue | +| value_categories.cpp:38:5:38:5 | (reference dereference) | int | lvalue | +| value_categories.cpp:38:5:38:9 | ... = ... | int | lvalue | +| value_categories.cpp:42:12:42:12 | (reference dereference) | int | lvalue | +| value_categories.cpp:52:19:52:33 | (reference dereference) | int | xvalue | +| value_categories.cpp:57:5:57:6 | (reference dereference) | int | lvalue | +| value_categories.cpp:57:5:57:10 | ... = ... | int | lvalue | +| value_categories.cpp:62:12:62:33 | static_cast... | int | xvalue | +| value_categories.cpp:62:31:62:32 | (reference dereference) | int | lvalue | +| value_categories.cpp:74:12:74:12 | (reference dereference) | S | lvalue | +| value_categories.cpp:95:5:95:5 | t | T | lvalue | +| value_categories.cpp:95:5:95:11 | ... = ... | int | lvalue | +| value_categories.cpp:95:7:95:7 | x | int | lvalue | +| value_categories.cpp:98:13:98:13 | t | T | lvalue | +| value_categories.cpp:104:5:104:12 | String | const char[7] | lvalue | +| value_categories.cpp:105:21:105:28 | String | const char[7] | lvalue | +| value_categories.cpp:106:26:106:33 | String | const char[7] | lvalue | +| value_categories.cpp:108:14:108:21 | String | const char[7] | lvalue | +| value_categories.cpp:115:5:115:5 | x | int | lvalue | +| value_categories.cpp:117:5:117:5 | x | int | lvalue | +| value_categories.cpp:119:5:119:7 | ++ ... | int | lvalue | +| value_categories.cpp:119:7:119:7 | x | int | lvalue | +| value_categories.cpp:121:5:121:7 | -- ... | int | lvalue | +| value_categories.cpp:121:7:121:7 | x | int | lvalue | +| value_categories.cpp:129:5:129:5 | x | int | lvalue | +| value_categories.cpp:129:5:129:10 | ... += ... | int | lvalue | +| value_categories.cpp:131:5:131:5 | x | int | lvalue | +| value_categories.cpp:131:5:131:10 | ... -= ... | int | lvalue | +| value_categories.cpp:133:5:133:5 | x | int | lvalue | +| value_categories.cpp:133:5:133:10 | ... *= ... | int | lvalue | +| value_categories.cpp:135:5:135:5 | x | int | lvalue | +| value_categories.cpp:135:5:135:10 | ... /= ... | int | lvalue | +| value_categories.cpp:137:5:137:5 | x | int | lvalue | +| value_categories.cpp:137:5:137:10 | ... %= ... | int | lvalue | +| value_categories.cpp:139:5:139:5 | x | int | lvalue | +| value_categories.cpp:139:5:139:11 | ... <<= ... | int | lvalue | +| value_categories.cpp:141:5:141:5 | x | int | lvalue | +| value_categories.cpp:141:5:141:11 | ... >>= ... | int | lvalue | +| value_categories.cpp:143:5:143:5 | x | int | lvalue | +| value_categories.cpp:143:5:143:10 | ... \|= ... | int | lvalue | +| value_categories.cpp:145:5:145:5 | x | int | lvalue | +| value_categories.cpp:145:5:145:10 | ... &= ... | int | lvalue | +| value_categories.cpp:147:5:147:5 | x | int | lvalue | +| value_categories.cpp:147:5:147:10 | ... ^= ... | int | lvalue | diff --git a/cpp/ql/test/library-tests/exprs/value_categories/value_categories.ql b/cpp/ql/test/library-tests/exprs/value_categories/value_categories.ql new file mode 100644 index 000000000000..12a1048263a7 --- /dev/null +++ b/cpp/ql/test/library-tests/exprs/value_categories/value_categories.ql @@ -0,0 +1,7 @@ +import cpp + +from Expr e, string valcat +where + e.isLValueCategory() and valcat = "lvalue" or + e.isXValueCategory() and valcat = "xvalue" +select e, e.getType().toString(), valcat diff --git a/cpp/ql/test/library-tests/exprs_basic/ExprsBasic1.expected b/cpp/ql/test/library-tests/exprs_basic/ExprsBasic1.expected new file mode 100644 index 000000000000..9821feb7a46a --- /dev/null +++ b/cpp/ql/test/library-tests/exprs_basic/ExprsBasic1.expected @@ -0,0 +1,7 @@ +| exprs_basic1.cpp:3:8:3:8 | operator= | 0 | 0 | +| exprs_basic1.cpp:3:8:3:8 | operator= | 0 | 0 | +| exprs_basic1.cpp:12:6:12:15 | create_foo | 9 | 4 | +| exprs_basic1.cpp:21:6:21:14 | print_foo | 0 | 0 | +| exprs_basic1.cpp:28:5:28:19 | set_current_foo | 0 | 0 | +| file://:0:0:0:0 | operator= | 0 | 0 | +| file://:0:0:0:0 | operator= | 0 | 0 | diff --git a/cpp/ql/test/library-tests/exprs_basic/ExprsBasic1.ql b/cpp/ql/test/library-tests/exprs_basic/ExprsBasic1.ql new file mode 100644 index 000000000000..7f40302b3241 --- /dev/null +++ b/cpp/ql/test/library-tests/exprs_basic/ExprsBasic1.ql @@ -0,0 +1,7 @@ +import cpp + +from Function f +select f, + count(Literal l | l.getEnclosingFunction() = f), + count(ArrayToPointerConversion c | c.getEnclosingFunction() = f) + diff --git a/cpp/ql/test/library-tests/exprs_basic/ExprsBasic3.expected b/cpp/ql/test/library-tests/exprs_basic/ExprsBasic3.expected new file mode 100644 index 000000000000..98984766e0b8 --- /dev/null +++ b/cpp/ql/test/library-tests/exprs_basic/ExprsBasic3.expected @@ -0,0 +1 @@ +| exprs_basic1.cpp:12:6:12:15 | create_foo | exprs_basic1.cpp:16:5:16:8 | name | exprs_basic1.cpp:4:8:4:11 | name | diff --git a/cpp/ql/test/library-tests/exprs_basic/ExprsBasic3.ql b/cpp/ql/test/library-tests/exprs_basic/ExprsBasic3.ql new file mode 100644 index 000000000000..80f40af2ba18 --- /dev/null +++ b/cpp/ql/test/library-tests/exprs_basic/ExprsBasic3.ql @@ -0,0 +1,13 @@ +/** + * @name ExprsBasic3 + * @kind table + */ +import cpp + +from Function f, FieldAccess fa +where f.hasName("create_foo") + and fa.getEnclosingFunction() = f + and fa.getTarget().getDeclaringType().hasName("Foo") + and fa.getTarget().hasName("name") +select f, fa, fa.getTarget() + diff --git a/cpp/ql/test/library-tests/exprs_basic/ExprsBasic4.expected b/cpp/ql/test/library-tests/exprs_basic/ExprsBasic4.expected new file mode 100644 index 000000000000..87db41a91011 --- /dev/null +++ b/cpp/ql/test/library-tests/exprs_basic/ExprsBasic4.expected @@ -0,0 +1,2 @@ +| exprs_basic1.cpp:4:8:4:11 | name | name | exprs_basic1.cpp:3:8:3:10 | Foo | Foo | exprs_basic1.cpp:16:5:16:8 | name | +| exprs_basic1.cpp:5:6:5:10 | count | count | exprs_basic1.cpp:3:8:3:10 | Foo | Foo | exprs_basic1.cpp:17:5:17:9 | count | diff --git a/cpp/ql/test/library-tests/exprs_basic/ExprsBasic4.ql b/cpp/ql/test/library-tests/exprs_basic/ExprsBasic4.ql new file mode 100644 index 000000000000..ea892a768d14 --- /dev/null +++ b/cpp/ql/test/library-tests/exprs_basic/ExprsBasic4.ql @@ -0,0 +1,15 @@ +/** + * @name ExprsBasic4 + * @kind table + */ +import cpp + +from Field f, string fname, string ftype +where f.hasName(fname) + and f.getDeclaringType().hasName(ftype) + and exists(f.getAnAccess()) +select f, fname, f.getDeclaringType(), ftype, f.getAnAccess() + + + + diff --git a/cpp/ql/test/library-tests/exprs_basic/ExprsBasic5.expected b/cpp/ql/test/library-tests/exprs_basic/ExprsBasic5.expected new file mode 100644 index 000000000000..706b5ab7a863 --- /dev/null +++ b/cpp/ql/test/library-tests/exprs_basic/ExprsBasic5.expected @@ -0,0 +1,2 @@ +| exprs_basic1.cpp:30:13:30:23 | current_foo | +| exprs_basic1.cpp:31:2:31:12 | current_foo | diff --git a/cpp/ql/test/library-tests/exprs_basic/ExprsBasic5.ql b/cpp/ql/test/library-tests/exprs_basic/ExprsBasic5.ql new file mode 100644 index 000000000000..d212c67121b0 --- /dev/null +++ b/cpp/ql/test/library-tests/exprs_basic/ExprsBasic5.ql @@ -0,0 +1,15 @@ +/** + * @name ExprsBasic5 + * @kind table + */ +import cpp + +from Function f, VariableAccess va, GlobalVariable v +where f.hasName("set_current_foo") + and va.getEnclosingFunction() = f + and v.hasName("current_foo") + and va.getTarget() = v +select va + + + diff --git a/cpp/ql/test/library-tests/exprs_basic/ExprsBasic7.expected b/cpp/ql/test/library-tests/exprs_basic/ExprsBasic7.expected new file mode 100644 index 000000000000..175794932a32 --- /dev/null +++ b/cpp/ql/test/library-tests/exprs_basic/ExprsBasic7.expected @@ -0,0 +1,2 @@ +| exprs_basic1.cpp:16:2:16:15 | ... = ... | exprs_basic1.cpp:16:5:16:8 | name | +| exprs_basic1.cpp:17:2:17:15 | ... = ... | exprs_basic1.cpp:17:5:17:9 | count | diff --git a/cpp/ql/test/library-tests/exprs_basic/ExprsBasic7.ql b/cpp/ql/test/library-tests/exprs_basic/ExprsBasic7.ql new file mode 100644 index 000000000000..ecfc166233e4 --- /dev/null +++ b/cpp/ql/test/library-tests/exprs_basic/ExprsBasic7.ql @@ -0,0 +1,13 @@ +/** + * @name ExprsBasic7 + * @kind table + */ +import cpp + +from AssignExpr e +where e.getEnclosingFunction().hasName("create_foo") + and e.getLValue() instanceof FieldAccess +select e, e.getLValue() + + + diff --git a/cpp/ql/test/library-tests/exprs_basic/ExprsBasic8.expected b/cpp/ql/test/library-tests/exprs_basic/ExprsBasic8.expected new file mode 100644 index 000000000000..d4b113a53f76 --- /dev/null +++ b/cpp/ql/test/library-tests/exprs_basic/ExprsBasic8.expected @@ -0,0 +1 @@ +| exprs_basic1.cpp:17:2:17:15 | ... = ... | exprs_basic1.cpp:17:13:17:15 | 123 | diff --git a/cpp/ql/test/library-tests/exprs_basic/ExprsBasic8.ql b/cpp/ql/test/library-tests/exprs_basic/ExprsBasic8.ql new file mode 100644 index 000000000000..8b8a0c51e902 --- /dev/null +++ b/cpp/ql/test/library-tests/exprs_basic/ExprsBasic8.ql @@ -0,0 +1,13 @@ +/** + * @name ExprsBasic8 + * @kind table + */ +import cpp + +from AssignExpr e +where e.getEnclosingFunction().hasName("create_foo") + and e.getRValue() instanceof Literal +select e, e.getRValue() + + + diff --git a/cpp/ql/test/library-tests/exprs_basic/ExprsBasic9.expected b/cpp/ql/test/library-tests/exprs_basic/ExprsBasic9.expected new file mode 100644 index 000000000000..7fb15b3c920a --- /dev/null +++ b/cpp/ql/test/library-tests/exprs_basic/ExprsBasic9.expected @@ -0,0 +1,6 @@ +| exprs_basic1.cpp:16:2:16:15 | ... = ... | 0 | +| exprs_basic1.cpp:16:2:16:15 | ... = ... | 1 | +| exprs_basic1.cpp:17:2:17:15 | ... = ... | 0 | +| exprs_basic1.cpp:17:2:17:15 | ... = ... | 1 | +| exprs_basic1.cpp:31:2:31:19 | ... = ... | 0 | +| exprs_basic1.cpp:31:2:31:19 | ... = ... | 1 | diff --git a/cpp/ql/test/library-tests/exprs_basic/ExprsBasic9.ql b/cpp/ql/test/library-tests/exprs_basic/ExprsBasic9.ql new file mode 100644 index 000000000000..9fc39a36d459 --- /dev/null +++ b/cpp/ql/test/library-tests/exprs_basic/ExprsBasic9.ql @@ -0,0 +1,6 @@ +import cpp + +from AssignExpr e, int i +where exists(e.getChild(i)) +select e, i + diff --git a/cpp/ql/test/library-tests/exprs_basic/exprs_basic1.cpp b/cpp/ql/test/library-tests/exprs_basic/exprs_basic1.cpp new file mode 100644 index 000000000000..a1190b7955d4 --- /dev/null +++ b/cpp/ql/test/library-tests/exprs_basic/exprs_basic1.cpp @@ -0,0 +1,33 @@ +enum Type { S, I }; + +struct Foo { + char* name; + int count; + char* another_name; + char* yet_another_name; + char initials[2]; + long very_long; +}; + +void create_foo() +{ + Foo xx; + char name[] = "Foo McFoo"; + xx.name = name; + xx.count = 123; + Foo yy = { "Barry McBar", 42, "Baz", "Basildon", { 'B', 'X' }, 5678 }; +} + +void print_foo(Foo* p) +{ + +} + +Foo current_foo; + +Foo set_current_foo(Foo next) +{ + Foo prev = current_foo; + current_foo = next; + return prev; +} diff --git a/cpp/ql/test/library-tests/exprs_cast/ExprsCast1.expected b/cpp/ql/test/library-tests/exprs_cast/ExprsCast1.expected new file mode 100644 index 000000000000..b39658e6259f --- /dev/null +++ b/cpp/ql/test/library-tests/exprs_cast/ExprsCast1.expected @@ -0,0 +1,9 @@ +| exprs_cast.cpp:8:16:8:16 | (unsigned long long)... | 1 | +| exprs_cast.cpp:21:12:21:30 | (char *)... | 1 | +| exprs_cast.cpp:22:13:22:21 | (long)... | 1 | +| exprs_cast.cpp:23:14:23:26 | (char *)... | 1 | +| exprs_cast.cpp:23:29:23:30 | (long)... | 1 | +| exprs_cast.cpp:23:33:23:37 | (char *)... | 1 | +| exprs_cast.cpp:23:40:23:49 | (char *)... | 1 | +| exprs_cast.cpp:23:66:23:69 | (long)... | 1 | +| exprs_cast.cpp:36:22:36:25 | (Foo)... | 1 | diff --git a/cpp/ql/test/library-tests/exprs_cast/ExprsCast1.ql b/cpp/ql/test/library-tests/exprs_cast/ExprsCast1.ql new file mode 100644 index 000000000000..e0781299754f --- /dev/null +++ b/cpp/ql/test/library-tests/exprs_cast/ExprsCast1.ql @@ -0,0 +1,8 @@ +/** + * @name ExprsCast1 + * @kind table + */ +import cpp + +from Cast c +select c, count(c.getExpr()) diff --git a/cpp/ql/test/library-tests/exprs_cast/ExprsCast2.expected b/cpp/ql/test/library-tests/exprs_cast/ExprsCast2.expected new file mode 100644 index 000000000000..7c82891dd248 --- /dev/null +++ b/cpp/ql/test/library-tests/exprs_cast/ExprsCast2.expected @@ -0,0 +1,9 @@ +| exprs_cast.cpp:8:16:8:16 | (unsigned long long)... | 2 | int | unsigned long long | | +| exprs_cast.cpp:21:12:21:30 | (char *)... | Foo McFoo | const char * | char * | | +| exprs_cast.cpp:22:13:22:21 | (long)... | 38 | int | long | | +| exprs_cast.cpp:23:14:23:26 | (char *)... | Barry McBar | const char * | char * | | +| exprs_cast.cpp:23:29:23:30 | (long)... | 99 | int | long | | +| exprs_cast.cpp:23:33:23:37 | (char *)... | Baz | const char * | char * | | +| exprs_cast.cpp:23:40:23:49 | (char *)... | Basildon | const char * | char * | | +| exprs_cast.cpp:23:66:23:69 | (long)... | 5678 | int | long | | +| exprs_cast.cpp:36:22:36:25 | (Foo)... | next | ExtendedFoo | Foo | struct | diff --git a/cpp/ql/test/library-tests/exprs_cast/ExprsCast2.ql b/cpp/ql/test/library-tests/exprs_cast/ExprsCast2.ql new file mode 100644 index 000000000000..2e55e2681039 --- /dev/null +++ b/cpp/ql/test/library-tests/exprs_cast/ExprsCast2.ql @@ -0,0 +1,19 @@ +/** + * @name ExprsCast2 + * @kind table + */ +import cpp + +string exprString(Expr e) { + if (e instanceof ArrayToPointerConversion) then ( + result = e.(ArrayToPointerConversion).getExpr().(Literal).getValue() + ) else ( + result = e.toString() + ) +} + +from Cast c, Type cType, string cTypeName, string toStruct +where cType = c.getType() + and cType.hasName(cTypeName) + and if cType instanceof Struct then toStruct = "struct" else toStruct = "" +select c, exprString(c.getExpr()), c.getExpr().getType().toString(), cTypeName, toStruct diff --git a/cpp/ql/test/library-tests/exprs_cast/exprs_cast.cpp b/cpp/ql/test/library-tests/exprs_cast/exprs_cast.cpp new file mode 100644 index 000000000000..31b8eab5fc1c --- /dev/null +++ b/cpp/ql/test/library-tests/exprs_cast/exprs_cast.cpp @@ -0,0 +1,38 @@ +enum Type { S, I }; + +struct Foo { + char* name; + long int count; + char* another_name; + char* yet_another_name; + char initials[2]; + long very_long; +}; + +struct ExtendedFoo : Foo +{ + char* more_details; +}; + + +void create_foo() +{ + Foo xx; + xx.name = (char*) "Foo McFoo"; + xx.count = (long) 38; + Foo xx2 = { "Barry McBar", 99, "Baz", "Basildon", { 'B', 'X' }, 5678 }; +} + +void print_foo(Foo* p) +{ + +} + +Foo current_foo; + +Foo set_current_foo(ExtendedFoo next) +{ + Foo prev = current_foo; + current_foo = (Foo) next; + return prev; +} diff --git a/cpp/ql/test/library-tests/exprs_cast/options b/cpp/ql/test/library-tests/exprs_cast/options new file mode 100644 index 000000000000..68a7c411dc5d --- /dev/null +++ b/cpp/ql/test/library-tests/exprs_cast/options @@ -0,0 +1 @@ +extractor_flags: --edg --target --edg win64 diff --git a/cpp/ql/test/library-tests/fields/fields/EnumConst.expected b/cpp/ql/test/library-tests/fields/fields/EnumConst.expected new file mode 100644 index 000000000000..e0f3ec8d5ebc --- /dev/null +++ b/cpp/ql/test/library-tests/fields/fields/EnumConst.expected @@ -0,0 +1,4 @@ +| fields.cpp:1:6:1:9 | Type | fields.cpp:1:13:1:13 | S | getDeclaringEnum() | +| fields.cpp:1:6:1:9 | Type | fields.cpp:1:13:1:13 | S | getType() | +| fields.cpp:1:6:1:9 | Type | fields.cpp:1:16:1:16 | I | getDeclaringEnum() | +| fields.cpp:1:6:1:9 | Type | fields.cpp:1:16:1:16 | I | getType() | diff --git a/cpp/ql/test/library-tests/fields/fields/EnumConst.ql b/cpp/ql/test/library-tests/fields/fields/EnumConst.ql new file mode 100644 index 000000000000..9d4e56c00934 --- /dev/null +++ b/cpp/ql/test/library-tests/fields/fields/EnumConst.ql @@ -0,0 +1,11 @@ +/** + * @name EnumConst + * @kind table + */ +import cpp + +from Enum e, Declaration c, string reason +where (c.(EnumConstant).getDeclaringEnum() = e and reason = "getDeclaringEnum()") or + (c.(EnumConstant).getType() = e and reason = "getType()") or + (c.(Field).getDeclaringType() = e and reason = "getDeclaringType()") +select e, c, reason diff --git a/cpp/ql/test/library-tests/fields/fields/Fields.expected b/cpp/ql/test/library-tests/fields/fields/Fields.expected new file mode 100644 index 000000000000..6ef4a17ec508 --- /dev/null +++ b/cpp/ql/test/library-tests/fields/fields/Fields.expected @@ -0,0 +1,13 @@ +| fields.cpp:3:8:3:12 | Entry | fields.cpp:4:9:4:12 | name | public | CharPointerType | char | +| fields.cpp:3:8:3:12 | Entry | fields.cpp:5:8:5:8 | t | public | Enum | | +| fields.cpp:3:8:3:12 | Entry | fields.cpp:6:9:6:9 | s | public | CharPointerType | char | +| fields.cpp:3:8:3:12 | Entry | fields.cpp:7:7:7:7 | i | public | IntType | | +| fields.cpp:3:8:3:12 | Entry | fields.cpp:7:7:7:7 | i | public | MicrosoftInt32Type | | +| fields.cpp:3:8:3:12 | Entry | fields.cpp:9:7:9:14 | internal | private | IntType | | +| fields.cpp:3:8:3:12 | Entry | fields.cpp:9:7:9:14 | internal | private | MicrosoftInt32Type | | +| fields.cpp:12:7:12:10 | Name | fields.cpp:13:15:13:15 | s | private | PointerType | const char | +| fields.cpp:16:7:16:11 | Table | fields.cpp:17:9:17:9 | p | private | PointerType | Name | +| fields.cpp:16:7:16:11 | Table | fields.cpp:18:7:18:8 | sz | private | IntType | | +| fields.cpp:16:7:16:11 | Table | fields.cpp:18:7:18:8 | sz | private | MicrosoftInt32Type | | +| fields.cpp:26:7:26:10 | Date | fields.cpp:28:16:28:26 | cache_valid | private | BoolType | | +| fields.cpp:26:7:26:10 | Date | fields.cpp:30:17:30:21 | cache | public | CharPointerType | char | diff --git a/cpp/ql/test/library-tests/fields/fields/Fields.ql b/cpp/ql/test/library-tests/fields/fields/Fields.ql new file mode 100644 index 000000000000..c64409d55fb4 --- /dev/null +++ b/cpp/ql/test/library-tests/fields/fields/Fields.ql @@ -0,0 +1,40 @@ +/** + * @name Fields + * @kind table + */ +import cpp + +predicate nameCheck(Declaration d) { + count(d.toString()) = 1 and + count(string s | d.hasName(s)) = 1 and + d.hasName(d.toString()) +} + +string accessType(Field f) { + (f.isPublic() and result = "public") or + (f.isProtected() and result = "protected") or + (f.isPrivate() and result = "private") +} + +string fieldType(Field f) { + result = f.getType().getAQlClass() and + ( + result.matches("%Type") or + result = "Enum" + ) +} + +string pointedType(Field f) { + if f.getType() instanceof PointerType then ( + result = f.getType().(PointerType).getBaseType().toString() + ) else ( + result = "" + ) +} + +from Class c, Field f +where f.getDeclaringType() = c and + c.getAField() = f and + nameCheck(c) and + nameCheck(f) +select c, f, accessType(f), fieldType(f), pointedType(f) diff --git a/cpp/ql/test/library-tests/fields/fields/MemberVariable.expected b/cpp/ql/test/library-tests/fields/fields/MemberVariable.expected new file mode 100644 index 000000000000..c886686d41f8 --- /dev/null +++ b/cpp/ql/test/library-tests/fields/fields/MemberVariable.expected @@ -0,0 +1,13 @@ +| fields.cpp:4:9:4:12 | name | | public | +| fields.cpp:5:8:5:8 | t | | public | +| fields.cpp:6:9:6:9 | s | | public | +| fields.cpp:7:7:7:7 | i | | public | +| fields.cpp:9:7:9:14 | internal | | private | +| fields.cpp:13:15:13:15 | s | | private | +| fields.cpp:17:9:17:9 | p | | private | +| fields.cpp:18:7:18:8 | sz | | private | +| fields.cpp:27:16:27:21 | memtbl | static | extern | +| fields.cpp:27:16:27:21 | memtbl | static | private | +| fields.cpp:27:16:27:21 | memtbl | static | static | +| fields.cpp:28:16:28:26 | cache_valid | | private | +| fields.cpp:30:17:30:21 | cache | | public | diff --git a/cpp/ql/test/library-tests/fields/fields/MemberVariable.ql b/cpp/ql/test/library-tests/fields/fields/MemberVariable.ql new file mode 100644 index 000000000000..f6e6c513996a --- /dev/null +++ b/cpp/ql/test/library-tests/fields/fields/MemberVariable.ql @@ -0,0 +1,9 @@ +/** + * @name MemberVariable + * @kind table + */ +import cpp + +from MemberVariable m, string static +where if m.isStatic() then static = "static" else static = "" +select m, static, m.getASpecifier().toString() diff --git a/cpp/ql/test/library-tests/fields/fields/fields.cpp b/cpp/ql/test/library-tests/fields/fields/fields.cpp new file mode 100644 index 000000000000..c4e26495b4c7 --- /dev/null +++ b/cpp/ql/test/library-tests/fields/fields/fields.cpp @@ -0,0 +1,33 @@ +enum Type { S, I }; + +struct Entry { + char* name; + Type t; + char* s; + int i; +private: + int internal; +}; + +class Name { + const char* s; +}; + +class Table { + Name* p; + int sz; +public: + Table(int s=15) { p = new Name[sz=s]; } // constructor + ~Table() { delete[] p; } + Name* lookup (const char*); + bool insert(Name*); +}; + +class Date { + static Table memtbl; + mutable bool cache_valid; +public: + mutable char* cache; + void compute_cache_value() const; +}; + diff --git a/cpp/ql/test/library-tests/fields/segfault/exprs.expected b/cpp/ql/test/library-tests/fields/segfault/exprs.expected new file mode 100644 index 000000000000..71eb7b7756ee --- /dev/null +++ b/cpp/ql/test/library-tests/fields/segfault/exprs.expected @@ -0,0 +1,3 @@ +| segfault.cpp:7:16:7:16 | 0 | +| segfault.cpp:7:16:7:16 | (Zero)... | +| segfault.cpp:11:7:11:12 | call to C | diff --git a/cpp/ql/test/library-tests/fields/segfault/exprs.ql b/cpp/ql/test/library-tests/fields/segfault/exprs.ql new file mode 100644 index 000000000000..3c2a3269e72e --- /dev/null +++ b/cpp/ql/test/library-tests/fields/segfault/exprs.ql @@ -0,0 +1,5 @@ +import cpp + +from Expr e +select e + diff --git a/cpp/ql/test/library-tests/fields/segfault/segfault.cpp b/cpp/ql/test/library-tests/fields/segfault/segfault.cpp new file mode 100644 index 000000000000..5149ece46890 --- /dev/null +++ b/cpp/ql/test/library-tests/fields/segfault/segfault.cpp @@ -0,0 +1,13 @@ + +struct Private; +typedef int (Private::*Zero); + +class C { +public: + C(Zero x = 0) {} +}; + +void f() { + C result; +} + diff --git a/cpp/ql/test/library-tests/files/FilePath.expected b/cpp/ql/test/library-tests/files/FilePath.expected new file mode 100644 index 000000000000..8e5bb3727920 --- /dev/null +++ b/cpp/ql/test/library-tests/files/FilePath.expected @@ -0,0 +1,4 @@ +| c.c | c.c | +| files1.cpp | files1.cpp | +| files1.h | files1.h | +| files2.cpp | files2.cpp | diff --git a/cpp/ql/test/library-tests/files/FilePath.ql b/cpp/ql/test/library-tests/files/FilePath.ql new file mode 100644 index 000000000000..724c4b45ae79 --- /dev/null +++ b/cpp/ql/test/library-tests/files/FilePath.ql @@ -0,0 +1,5 @@ +import cpp + +from File f +where f.toString() != "" +select f.toString(), f.getRelativePath() diff --git a/cpp/ql/test/library-tests/files/Files1.expected b/cpp/ql/test/library-tests/files/Files1.expected new file mode 100644 index 000000000000..ef11fb83e9be --- /dev/null +++ b/cpp/ql/test/library-tests/files/Files1.expected @@ -0,0 +1 @@ +| files1.h:0:0:0:0 | files1.h | files1.cpp:4:6:4:9 | swap | diff --git a/cpp/ql/test/library-tests/files/Files1.ql b/cpp/ql/test/library-tests/files/Files1.ql new file mode 100644 index 000000000000..7757119cb106 --- /dev/null +++ b/cpp/ql/test/library-tests/files/Files1.ql @@ -0,0 +1,5 @@ +import cpp + +from HeaderFile f +select f, f.getATopLevelDeclaration() + diff --git a/cpp/ql/test/library-tests/files/Files2.expected b/cpp/ql/test/library-tests/files/Files2.expected new file mode 100644 index 000000000000..7a2649c09e4a --- /dev/null +++ b/cpp/ql/test/library-tests/files/Files2.expected @@ -0,0 +1,8 @@ +| CFile | C | --- | c.c | +| CppFile | - | C++ | files1.cpp | +| CppFile | - | C++ | files2.cpp | +| HeaderFile | - | --- | files1.h | +| MetricFile | - | --- | files1.h | +| MetricFile | - | C++ | files1.cpp | +| MetricFile | - | C++ | files2.cpp | +| MetricFile | C | --- | c.c | diff --git a/cpp/ql/test/library-tests/files/Files2.ql b/cpp/ql/test/library-tests/files/Files2.ql new file mode 100644 index 000000000000..30bd78672240 --- /dev/null +++ b/cpp/ql/test/library-tests/files/Files2.ql @@ -0,0 +1,18 @@ +import cpp + +string isCompiledAsC(File f) { + if f.compiledAsC() then result = "C" else result = "-" +} + +string isCompiledAsCpp(File f) { + if f.compiledAsCpp() then result = "C++" else result = "---" +} + +from File f +// On 64bit Linux, __va_list_tag is in the unknown file (""). Ignore it. +where f.getAbsolutePath() != "" +select (f.getAQlClass().toString() + " ").prefix(10), + isCompiledAsC(f), + isCompiledAsCpp(f), + f.toString() + diff --git a/cpp/ql/test/library-tests/files/Files3.expected b/cpp/ql/test/library-tests/files/Files3.expected new file mode 100644 index 000000000000..f17f4192871c --- /dev/null +++ b/cpp/ql/test/library-tests/files/Files3.expected @@ -0,0 +1,3 @@ +| files1.cpp | files1.cpp:4:6:4:9 | swap | +| files1.h | files1.cpp:4:6:4:9 | swap | +| files2.cpp | files2.cpp:3:6:3:6 | g | diff --git a/cpp/ql/test/library-tests/files/Files3.ql b/cpp/ql/test/library-tests/files/Files3.ql new file mode 100644 index 000000000000..f51f85b6dbbe --- /dev/null +++ b/cpp/ql/test/library-tests/files/Files3.ql @@ -0,0 +1,6 @@ +import cpp + +from File f, Declaration d +where d = f.getATopLevelDeclaration() + and d.getName() != "__va_list_tag" +select f.toString(), d diff --git a/cpp/ql/test/library-tests/files/Files4.expected b/cpp/ql/test/library-tests/files/Files4.expected new file mode 100644 index 000000000000..188b7161d991 --- /dev/null +++ b/cpp/ql/test/library-tests/files/Files4.expected @@ -0,0 +1,3 @@ +| files1.cpp | files1.cpp:6:6:6:6 | t | +| files2.cpp | files2.cpp:4:6:4:6 | x | +| files2.cpp | files2.cpp:5:6:5:6 | y | diff --git a/cpp/ql/test/library-tests/files/Files4.ql b/cpp/ql/test/library-tests/files/Files4.ql new file mode 100644 index 000000000000..104029795415 --- /dev/null +++ b/cpp/ql/test/library-tests/files/Files4.ql @@ -0,0 +1,5 @@ +import cpp + +from File f, LocalVariable v +where f.getADeclaration() = v +select f.toString(), v diff --git a/cpp/ql/test/library-tests/files/c.c b/cpp/ql/test/library-tests/files/c.c new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/cpp/ql/test/library-tests/files/files1.cpp b/cpp/ql/test/library-tests/files/files1.cpp new file mode 100644 index 000000000000..cd30b0b78ba9 --- /dev/null +++ b/cpp/ql/test/library-tests/files/files1.cpp @@ -0,0 +1,9 @@ +#include "files1.h" + + +void swap(int* p, int* q) +{ + int t = *p; + *p = *q; + *q = t; +} diff --git a/cpp/ql/test/library-tests/files/files1.h b/cpp/ql/test/library-tests/files/files1.h new file mode 100644 index 000000000000..90b150ae9d68 --- /dev/null +++ b/cpp/ql/test/library-tests/files/files1.h @@ -0,0 +1,2 @@ + +extern void swap(int*, int*); diff --git a/cpp/ql/test/library-tests/files/files2.cpp b/cpp/ql/test/library-tests/files/files2.cpp new file mode 100644 index 000000000000..301b082a5345 --- /dev/null +++ b/cpp/ql/test/library-tests/files/files2.cpp @@ -0,0 +1,7 @@ +#include "files1.h" + +void g() { + int x = 2; + int y = 4; + swap(&x,&y); +} diff --git a/cpp/ql/test/library-tests/floats/float128/errors.expected b/cpp/ql/test/library-tests/floats/float128/errors.expected new file mode 100644 index 000000000000..2f163e66cb05 --- /dev/null +++ b/cpp/ql/test/library-tests/floats/float128/errors.expected @@ -0,0 +1,3 @@ +| file://:0:0:0:0 | There was an error during this compilation | +| float128.cpp:1:39:1:39 | 128-bit floating-point types are not supported in this configuration | +| float128.cpp:2:30:2:30 | 128-bit floating-point types are not supported in this configuration | diff --git a/cpp/ql/test/library-tests/floats/float128/errors.ql b/cpp/ql/test/library-tests/floats/float128/errors.ql new file mode 100644 index 000000000000..3fa864748e16 --- /dev/null +++ b/cpp/ql/test/library-tests/floats/float128/errors.ql @@ -0,0 +1,4 @@ +import cpp + +from Diagnostic d +select d diff --git a/cpp/ql/test/library-tests/floats/float128/float128.cpp b/cpp/ql/test/library-tests/floats/float128/float128.cpp new file mode 100644 index 000000000000..7c57a0be72a8 --- /dev/null +++ b/cpp/ql/test/library-tests/floats/float128/float128.cpp @@ -0,0 +1,28 @@ +typedef _Complex float __attribute__((mode(TC))) _Complex128; // [COMPILER ERROR AND ERROR-TYPE DUE TO __float128 BEING DISABLED] +typedef float __attribute__((mode(TF))) _Float128; // [COMPILER ERROR AND ERROR-TYPE DUE TO __float128 BEING DISABLED] + +int main() { + __float128 f = 1.0f; + __float128 g = 2.0f; + float h = f + g; +} + +struct false_type {enum {value = 0};}; +struct true_type {enum {value = 1};}; + +template struct __is_floating_point_helper : public false_type { }; +template<> struct __is_floating_point_helper : public true_type { }; +template<> struct __is_floating_point_helper : public true_type { }; +template<> struct __is_floating_point_helper : public true_type { }; +template<> struct __is_floating_point_helper<__float128> : public true_type { }; + +long double id(long double d) +{ + return d; +} + +__float128 id(__float128 q) +{ + return q; +} +// semmle-extractor-options: --expect_errors diff --git a/cpp/ql/test/library-tests/floats/float128/functions.expected b/cpp/ql/test/library-tests/floats/float128/functions.expected new file mode 100644 index 000000000000..75ed9f32d96f --- /dev/null +++ b/cpp/ql/test/library-tests/floats/float128/functions.expected @@ -0,0 +1,14 @@ +| id | file://:0:0:0:0 | __float128 | file://:0:0:0:0 | __float128 | +| id | file://:0:0:0:0 | long double | file://:0:0:0:0 | long double | +| operator= | file://:0:0:0:0 | __is_floating_point_helper<__float128> & | file://:0:0:0:0 | __is_floating_point_helper<__float128> && | +| operator= | file://:0:0:0:0 | __is_floating_point_helper<__float128> & | file://:0:0:0:0 | const __is_floating_point_helper<__float128> & | +| operator= | file://:0:0:0:0 | __is_floating_point_helper & | file://:0:0:0:0 | __is_floating_point_helper && | +| operator= | file://:0:0:0:0 | __is_floating_point_helper & | file://:0:0:0:0 | const __is_floating_point_helper & | +| operator= | file://:0:0:0:0 | __is_floating_point_helper & | file://:0:0:0:0 | __is_floating_point_helper && | +| operator= | file://:0:0:0:0 | __is_floating_point_helper & | file://:0:0:0:0 | const __is_floating_point_helper & | +| operator= | file://:0:0:0:0 | __is_floating_point_helper & | file://:0:0:0:0 | __is_floating_point_helper && | +| operator= | file://:0:0:0:0 | __is_floating_point_helper & | file://:0:0:0:0 | const __is_floating_point_helper & | +| operator= | file://:0:0:0:0 | false_type & | file://:0:0:0:0 | const false_type & | +| operator= | file://:0:0:0:0 | false_type & | file://:0:0:0:0 | false_type && | +| operator= | file://:0:0:0:0 | true_type & | file://:0:0:0:0 | const true_type & | +| operator= | file://:0:0:0:0 | true_type & | file://:0:0:0:0 | true_type && | diff --git a/cpp/ql/test/library-tests/floats/float128/functions.ql b/cpp/ql/test/library-tests/floats/float128/functions.ql new file mode 100644 index 000000000000..716f77e8cad9 --- /dev/null +++ b/cpp/ql/test/library-tests/floats/float128/functions.ql @@ -0,0 +1,4 @@ +import cpp + +from Function f +select f.getName(), f.getType(), f.getParameter(0).getType() diff --git a/cpp/ql/test/library-tests/floats/float128/usertypes.expected b/cpp/ql/test/library-tests/floats/float128/usertypes.expected new file mode 100644 index 000000000000..42b6612aff9e --- /dev/null +++ b/cpp/ql/test/library-tests/floats/float128/usertypes.expected @@ -0,0 +1,7 @@ +| float128.cpp:1:50:1:60 | _Complex128 | file://:0:0:0:0 | | +| float128.cpp:2:41:2:49 | _Float128 | file://:0:0:0:0 | | +| float128.cpp:13:29:13:54 | __is_floating_point_helper | float128.cpp:10:8:10:17 | false_type | +| float128.cpp:14:19:14:51 | __is_floating_point_helper | float128.cpp:11:8:11:16 | true_type | +| float128.cpp:15:19:15:52 | __is_floating_point_helper | float128.cpp:11:8:11:16 | true_type | +| float128.cpp:16:19:16:57 | __is_floating_point_helper | float128.cpp:11:8:11:16 | true_type | +| float128.cpp:17:19:17:56 | __is_floating_point_helper<__float128> | float128.cpp:11:8:11:16 | true_type | diff --git a/cpp/ql/test/library-tests/floats/float128/usertypes.ql b/cpp/ql/test/library-tests/floats/float128/usertypes.ql new file mode 100644 index 000000000000..81584d7b2d85 --- /dev/null +++ b/cpp/ql/test/library-tests/floats/float128/usertypes.ql @@ -0,0 +1,6 @@ +import cpp + +from UserType t, Type related +where related = ((Class)t).getABaseClass() + or related = ((TypedefType)t).getUnderlyingType() +select t, related diff --git a/cpp/ql/test/library-tests/floats/floats/floats.c b/cpp/ql/test/library-tests/floats/floats/floats.c new file mode 100644 index 000000000000..0cbaf6b3cd50 --- /dev/null +++ b/cpp/ql/test/library-tests/floats/floats/floats.c @@ -0,0 +1,11 @@ + +float f0 = 0; +float f1 = 0.5; +float f2 = 2.5e-10; +float f3 = 2.5e10; +float f4 = -2.5; +float f5 = -2.5e-10; +float f6 = 2.4674957095607889277282112901141e-37; +float f7 = 2.4674957095607889277282112901141e-77; +float f8 = -2.4674957095607889277282112901141e-37; + diff --git a/cpp/ql/test/library-tests/floats/floats/floats.expected b/cpp/ql/test/library-tests/floats/floats/floats.expected new file mode 100644 index 000000000000..d046e3a09f34 --- /dev/null +++ b/cpp/ql/test/library-tests/floats/floats/floats.expected @@ -0,0 +1,9 @@ +| floats.c:2:7:2:8 | f0 | 0 | +| floats.c:3:7:3:8 | f1 | 0.5 | +| floats.c:4:7:4:8 | f2 | src: 2.5e-10 | +| floats.c:5:7:5:8 | f3 | src: 2.5e10 | +| floats.c:6:7:6:8 | f4 | -2.5 | +| floats.c:7:7:7:8 | f5 | src: -2.5e-10 | +| floats.c:8:7:8:8 | f6 | src: 2.4674957095607889277282112901141e-37 | +| floats.c:9:7:9:8 | f7 | src: 2.4674957095607889277282112901141e-77 | +| floats.c:10:7:10:8 | f8 | src: -2.4674957095607889277282112901141e-37 | diff --git a/cpp/ql/test/library-tests/floats/floats/floats.ql b/cpp/ql/test/library-tests/floats/floats/floats.ql new file mode 100644 index 000000000000..0381fa4af8c4 --- /dev/null +++ b/cpp/ql/test/library-tests/floats/floats/floats.ql @@ -0,0 +1,23 @@ +import cpp + +string displayExpr(Expr e) { + if e instanceof UnaryMinusExpr + then result = "-" + displayExpr(((UnaryMinusExpr)e).getOperand()) + else result = e.toString() +} + +string displayPortableExpr(Expr e) { + exists(string displayed | displayed = displayExpr(e) | + if displayed.length() < 5 + then result = displayed + else // If the calculated value is long, then it's probably got + // lots of precision, and the least significant digits may + // vary depending on the machine. So just show the original + // source value, as well as an indication that that's what + // we're doing. + result = "src: " + e.getValueText()) +} + +from Variable v +select v, displayPortableExpr(v.getInitializer().getExpr()) + diff --git a/cpp/ql/test/library-tests/friends/friends/friends.cpp b/cpp/ql/test/library-tests/friends/friends/friends.cpp new file mode 100644 index 000000000000..6034efca74c8 --- /dev/null +++ b/cpp/ql/test/library-tests/friends/friends/friends.cpp @@ -0,0 +1,29 @@ +template +class SelfFriendlyTemplate +{ + friend class SelfFriendlyTemplate; +}; + +class OuterC +{ + class InnerC + { + friend class Foo; + }; + friend class Foo; +}; + +class OuterF +{ + friend void foo(); + class InnerF + { + friend void foo(); + }; +}; + +int main() +{ + SelfFriendlyTemplate i; + SelfFriendlyTemplate f; +} diff --git a/cpp/ql/test/library-tests/friends/friends/instantiated_template_class.expected b/cpp/ql/test/library-tests/friends/friends/instantiated_template_class.expected new file mode 100644 index 000000000000..42df9cd48473 --- /dev/null +++ b/cpp/ql/test/library-tests/friends/friends/instantiated_template_class.expected @@ -0,0 +1,2 @@ +| SelfFriendlyTemplate | SelfFriendlyTemplate | +| SelfFriendlyTemplate | SelfFriendlyTemplate | diff --git a/cpp/ql/test/library-tests/friends/friends/instantiated_template_class.ql b/cpp/ql/test/library-tests/friends/friends/instantiated_template_class.ql new file mode 100644 index 000000000000..204a2493b19c --- /dev/null +++ b/cpp/ql/test/library-tests/friends/friends/instantiated_template_class.ql @@ -0,0 +1,6 @@ +import cpp + +from Class c +where c.getName().matches("SelfFriendlyTemplate<__%>") +select c.toString() as parent, c.getAFriendDecl().getFriend().toString() as friend +order by parent, friend diff --git a/cpp/ql/test/library-tests/friends/friends/nesting.expected b/cpp/ql/test/library-tests/friends/friends/nesting.expected new file mode 100644 index 000000000000..0d1f0f6f7250 --- /dev/null +++ b/cpp/ql/test/library-tests/friends/friends/nesting.expected @@ -0,0 +1,2 @@ +| OuterC | Foo | 6 | +| OuterF | foo | 2 | diff --git a/cpp/ql/test/library-tests/friends/friends/nesting.ql b/cpp/ql/test/library-tests/friends/friends/nesting.ql new file mode 100644 index 000000000000..320e37c18098 --- /dev/null +++ b/cpp/ql/test/library-tests/friends/friends/nesting.ql @@ -0,0 +1,10 @@ +import cpp + +int relativeLine(Locatable first, Locatable second) { + result = second.getLocation().getStartLine() - first.getLocation().getStartLine() +} + +from Class c, FriendDecl d +where c.getName().matches("Outer%") + and d = c.getAFriendDecl() +select c.toString(), d.getFriend().toString(), relativeLine(c, d) diff --git a/cpp/ql/test/library-tests/friends/friends/prototype_template_class.expected b/cpp/ql/test/library-tests/friends/friends/prototype_template_class.expected new file mode 100644 index 000000000000..211cc49b36cf --- /dev/null +++ b/cpp/ql/test/library-tests/friends/friends/prototype_template_class.expected @@ -0,0 +1 @@ +| friends.cpp:2:7:2:26 | SelfFriendlyTemplate | friends.cpp:2:7:2:26 | SelfFriendlyTemplate | diff --git a/cpp/ql/test/library-tests/friends/friends/prototype_template_class.ql b/cpp/ql/test/library-tests/friends/friends/prototype_template_class.ql new file mode 100644 index 000000000000..d6ed4305e4e7 --- /dev/null +++ b/cpp/ql/test/library-tests/friends/friends/prototype_template_class.ql @@ -0,0 +1,5 @@ +import cpp + +from TemplateClass tc, Declaration f +where f = tc.getAFriendDecl().getFriend() +select tc, f diff --git a/cpp/ql/test/library-tests/friends/loop/friends.expected b/cpp/ql/test/library-tests/friends/loop/friends.expected new file mode 100644 index 000000000000..0fa38a8b8546 --- /dev/null +++ b/cpp/ql/test/library-tests/friends/loop/friends.expected @@ -0,0 +1,9 @@ +| file://:0:0:0:0 | E's friend | loop.cpp:5:26:5:26 | E | +| file://:0:0:0:0 | E's friend | loop.cpp:10:26:10:26 | F | +| file://:0:0:0:0 | E's friend | loop.cpp:5:26:5:26 | E | +| file://:0:0:0:0 | E's friend | loop.cpp:10:26:10:26 | F | +| file://:0:0:0:0 | F's friend | loop.cpp:5:26:5:26 | E | +| file://:0:0:0:0 | F's friend | loop.cpp:5:26:5:26 | E | +| loop.cpp:6:5:6:5 | E's friend | loop.cpp:5:26:5:26 | E | +| loop.cpp:7:5:7:5 | E's friend | loop.cpp:7:36:7:36 | F | +| loop.cpp:11:5:11:5 | F's friend | loop.cpp:5:26:5:26 | E | diff --git a/cpp/ql/test/library-tests/friends/loop/friends.ql b/cpp/ql/test/library-tests/friends/loop/friends.ql new file mode 100644 index 000000000000..914a716ac11d --- /dev/null +++ b/cpp/ql/test/library-tests/friends/loop/friends.ql @@ -0,0 +1,4 @@ +import cpp + +from FriendDecl f +select f, f.getFriend() diff --git a/cpp/ql/test/library-tests/friends/loop/loop.cpp b/cpp/ql/test/library-tests/friends/loop/loop.cpp new file mode 100644 index 000000000000..dceabeba5a86 --- /dev/null +++ b/cpp/ql/test/library-tests/friends/loop/loop.cpp @@ -0,0 +1,18 @@ + +class C; +class D; + +template class E { + template friend class E; + template friend class F; +}; + +template class F : public E { + template friend class E; +}; + +void f(void) { + E ec; + F fd; +} + diff --git a/cpp/ql/test/library-tests/funcdname/funcdname.cpp b/cpp/ql/test/library-tests/funcdname/funcdname.cpp new file mode 100644 index 000000000000..a954257a75f1 --- /dev/null +++ b/cpp/ql/test/library-tests/funcdname/funcdname.cpp @@ -0,0 +1,36 @@ +extern "C" int printf(const char*, ...); + +namespace Foo +{ + namespace + { + const char* A() + { + return __FUNCDNAME__; + } + } + const char* a() + { + return A(); + } +} + +namespace Bar +{ + namespace + { + const char* B() + { + return __FUNCTION__; + } + } + const char* b() + { + return B(); + } +} + +int main() +{ + return printf("%s\n%s\n", Foo::a(), Bar::b()); +} diff --git a/cpp/ql/test/library-tests/funcdname/funcdname.expected b/cpp/ql/test/library-tests/funcdname/funcdname.expected new file mode 100644 index 000000000000..7564b54bff1e --- /dev/null +++ b/cpp/ql/test/library-tests/funcdname/funcdname.expected @@ -0,0 +1,2 @@ +| Bar::(unnamed namespace)::B | Bar::::B | +| Foo::(unnamed namespace)::A | _ZN3Foo37_GLOBAL__N__13_funcdname_cpp_?AEv | diff --git a/cpp/ql/test/library-tests/funcdname/funcdname.ql b/cpp/ql/test/library-tests/funcdname/funcdname.ql new file mode 100644 index 000000000000..08026d75e597 --- /dev/null +++ b/cpp/ql/test/library-tests/funcdname/funcdname.ql @@ -0,0 +1,5 @@ +import cpp + +from Function f, ReturnStmt r +where r.getEnclosingFunction() = f +select f.getQualifiedName(), r.getExpr().getValue().regexpReplaceAll("_[0-9a-f]+AEv$", "_?AEv") diff --git a/cpp/ql/test/library-tests/funcdname/options b/cpp/ql/test/library-tests/funcdname/options new file mode 100644 index 000000000000..91adeb70204c --- /dev/null +++ b/cpp/ql/test/library-tests/funcdname/options @@ -0,0 +1 @@ +extractor_flags: --microsoft diff --git a/cpp/ql/test/library-tests/function_try_stmt/function_try_stmt.cpp b/cpp/ql/test/library-tests/function_try_stmt/function_try_stmt.cpp new file mode 100644 index 000000000000..cdf405b32320 --- /dev/null +++ b/cpp/ql/test/library-tests/function_try_stmt/function_try_stmt.cpp @@ -0,0 +1,16 @@ +int foo() try { + return 0; +} +catch(...) { + throw; +} + +class Bar +{ + Bar() try { + return; + } + catch(...) { + throw; + } +}; diff --git a/cpp/ql/test/library-tests/function_try_stmt/function_try_stmt.expected b/cpp/ql/test/library-tests/function_try_stmt/function_try_stmt.expected new file mode 100644 index 000000000000..af94288b5db5 --- /dev/null +++ b/cpp/ql/test/library-tests/function_try_stmt/function_try_stmt.expected @@ -0,0 +1,2 @@ +| function_try_stmt.cpp:1:11:3:1 | try { ... } | function_try_stmt.cpp:1:5:1:7 | foo | function_try_stmt.cpp:2:3:2:11 | return ... | +| function_try_stmt.cpp:10:9:12:3 | try { ... } | function_try_stmt.cpp:10:3:10:5 | Bar | function_try_stmt.cpp:11:5:11:11 | return ... | diff --git a/cpp/ql/test/library-tests/function_try_stmt/function_try_stmt.ql b/cpp/ql/test/library-tests/function_try_stmt/function_try_stmt.ql new file mode 100644 index 000000000000..664d8c6e5da6 --- /dev/null +++ b/cpp/ql/test/library-tests/function_try_stmt/function_try_stmt.ql @@ -0,0 +1,6 @@ +import cpp + +from FunctionTryStmt fts +select fts, + fts.getEnclosingFunction() as f, + f.getBlock().getAStmt() diff --git a/cpp/ql/test/library-tests/functionpointerish/functionpointerish.cpp b/cpp/ql/test/library-tests/functionpointerish/functionpointerish.cpp new file mode 100644 index 000000000000..a738bd9769df --- /dev/null +++ b/cpp/ql/test/library-tests/functionpointerish/functionpointerish.cpp @@ -0,0 +1,6 @@ + +int fun(int x) {}; +int (*funptr)(int x); +int (&funref)(int x) = fun; +auto blockPtr = ^ int (int x, int y) {return x + y;}; + diff --git a/cpp/ql/test/library-tests/functionpointerish/functionpointerish.expected b/cpp/ql/test/library-tests/functionpointerish/functionpointerish.expected new file mode 100644 index 000000000000..633644463aed --- /dev/null +++ b/cpp/ql/test/library-tests/functionpointerish/functionpointerish.expected @@ -0,0 +1,3 @@ +| ..(&)(..) | int | 1 | int | +| ..(*)(..) | int | 1 | int | +| ..(^)(..) | int | 2 | int | diff --git a/cpp/ql/test/library-tests/functionpointerish/functionpointerish.ql b/cpp/ql/test/library-tests/functionpointerish/functionpointerish.ql new file mode 100644 index 000000000000..b662884ea56c --- /dev/null +++ b/cpp/ql/test/library-tests/functionpointerish/functionpointerish.ql @@ -0,0 +1,8 @@ +import cpp + +from FunctionPointerIshType t +select t.toString(), + t.getReturnType().toString(), + t.getNumberOfParameters(), + t.getAParameterType().toString() + diff --git a/cpp/ql/test/library-tests/functionpointerish/options b/cpp/ql/test/library-tests/functionpointerish/options new file mode 100644 index 000000000000..81574938c6ae --- /dev/null +++ b/cpp/ql/test/library-tests/functionpointerish/options @@ -0,0 +1 @@ +extractor_flags: -fblocks diff --git a/cpp/ql/test/library-tests/functions/arguments/arguments.expected b/cpp/ql/test/library-tests/functions/arguments/arguments.expected new file mode 100644 index 000000000000..3d12ea884309 --- /dev/null +++ b/cpp/ql/test/library-tests/functions/arguments/arguments.expected @@ -0,0 +1,4 @@ +| test.c:5:5:5:5 | call to f | 0 | test.c:5:8:5:9 | 11 | +| test.c:5:5:5:5 | call to f | 1 | test.c:5:12:5:13 | 22 | +| test.c:6:4:6:4 | call to f | 0 | test.c:6:6:6:7 | 33 | +| test.c:6:4:6:4 | call to f | 1 | test.c:6:10:6:11 | 44 | diff --git a/cpp/ql/test/library-tests/functions/arguments/arguments.ql b/cpp/ql/test/library-tests/functions/arguments/arguments.ql new file mode 100644 index 000000000000..f1cd4869724f --- /dev/null +++ b/cpp/ql/test/library-tests/functions/arguments/arguments.ql @@ -0,0 +1,5 @@ +import cpp + +from FunctionCall fc, int i +select fc, i, fc.getArgument(i) + diff --git a/cpp/ql/test/library-tests/functions/arguments/num_arguments.expected b/cpp/ql/test/library-tests/functions/arguments/num_arguments.expected new file mode 100644 index 000000000000..7caebedacf0b --- /dev/null +++ b/cpp/ql/test/library-tests/functions/arguments/num_arguments.expected @@ -0,0 +1,2 @@ +| test.c:5:5:5:5 | call to f | 2 | +| test.c:6:4:6:4 | call to f | 2 | diff --git a/cpp/ql/test/library-tests/functions/arguments/num_arguments.ql b/cpp/ql/test/library-tests/functions/arguments/num_arguments.ql new file mode 100644 index 000000000000..caedc1b5bc0e --- /dev/null +++ b/cpp/ql/test/library-tests/functions/arguments/num_arguments.ql @@ -0,0 +1,5 @@ +import cpp + +from FunctionCall fc +select fc, fc.getNumberOfArguments() + diff --git a/cpp/ql/test/library-tests/functions/arguments/test.c b/cpp/ql/test/library-tests/functions/arguments/test.c new file mode 100644 index 000000000000..5b082315299a --- /dev/null +++ b/cpp/ql/test/library-tests/functions/arguments/test.c @@ -0,0 +1,8 @@ + +extern void f (int x, int y); + +void g(void) { + (f)(11, 22); + f(33, 44); +} + diff --git a/cpp/ql/test/library-tests/functions/fde_get_block/fde_get_block.c b/cpp/ql/test/library-tests/functions/fde_get_block/fde_get_block.c new file mode 100644 index 000000000000..5ba2270a72ec --- /dev/null +++ b/cpp/ql/test/library-tests/functions/fde_get_block/fde_get_block.c @@ -0,0 +1,5 @@ +void f(); // This FDE should have no result for getBlock() + +void f() +{ +} diff --git a/cpp/ql/test/library-tests/functions/fde_get_block/fde_get_block.expected b/cpp/ql/test/library-tests/functions/fde_get_block/fde_get_block.expected new file mode 100644 index 000000000000..ef972fe59d4b --- /dev/null +++ b/cpp/ql/test/library-tests/functions/fde_get_block/fde_get_block.expected @@ -0,0 +1 @@ +| 3 | 4 | diff --git a/cpp/ql/test/library-tests/functions/fde_get_block/fde_get_block.ql b/cpp/ql/test/library-tests/functions/fde_get_block/fde_get_block.ql new file mode 100644 index 000000000000..151a16e433c1 --- /dev/null +++ b/cpp/ql/test/library-tests/functions/fde_get_block/fde_get_block.ql @@ -0,0 +1,4 @@ +import cpp + +from FunctionDeclarationEntry fde +select fde.getLocation().getStartLine(), fde.getBlock().getLocation().getStartLine() diff --git a/cpp/ql/test/library-tests/functions/functions/FunctionCalls.expected b/cpp/ql/test/library-tests/functions/functions/FunctionCalls.expected new file mode 100644 index 000000000000..f06cec1daddd --- /dev/null +++ b/cpp/ql/test/library-tests/functions/functions/FunctionCalls.expected @@ -0,0 +1,3 @@ +| ODASA-5186.cpp:17:13:17:13 | call to operator!= | file://:0:0:0:0 | operator!= | +| ODASA-5186.hpp:4:83:4:83 | call to operator== | ODASA-5186.cpp:5:8:5:17 | operator== | +| functions.cpp:16:4:16:5 | call to ag | functions.cpp:11:7:11:8 | ag | diff --git a/cpp/ql/test/library-tests/functions/functions/FunctionCalls.ql b/cpp/ql/test/library-tests/functions/functions/FunctionCalls.ql new file mode 100644 index 000000000000..a46094bb9a13 --- /dev/null +++ b/cpp/ql/test/library-tests/functions/functions/FunctionCalls.ql @@ -0,0 +1,4 @@ +import cpp + +from FunctionCall call +select call, call.getTarget() diff --git a/cpp/ql/test/library-tests/functions/functions/Functions1.expected b/cpp/ql/test/library-tests/functions/functions/Functions1.expected new file mode 100644 index 000000000000..d812efa97390 --- /dev/null +++ b/cpp/ql/test/library-tests/functions/functions/Functions1.expected @@ -0,0 +1,32 @@ +| ODASA-5186.cpp:4:8:4:8 | operator= | operator= | | | ODASA-5186.cpp:4:8:4:8 | declaration | +| ODASA-5186.cpp:4:8:4:8 | operator= | operator= | | | ODASA-5186.cpp:4:8:4:8 | declaration | +| ODASA-5186.cpp:5:8:5:17 | operator== | operator== | | | ODASA-5186.cpp:5:8:5:17 | declaration | +| ODASA-5186.cpp:5:8:5:17 | operator== | operator== | | | ODASA-5186.cpp:5:8:5:17 | declaration | +| ODASA-5186.cpp:5:8:5:17 | operator== | operator== | | | ODASA-5186.cpp:5:8:5:17 | definition | +| ODASA-5186.cpp:5:8:5:17 | operator== | operator== | | | ODASA-5186.cpp:5:8:5:17 | definition | +| ODASA-5186.cpp:8:6:8:9 | test | test | isTopLevel | TopLevelFunction | ODASA-5186.cpp:8:6:8:9 | declaration | +| ODASA-5186.cpp:8:6:8:9 | test | test | isTopLevel | TopLevelFunction | ODASA-5186.cpp:8:6:8:9 | definition | +| ODASA-5186.hpp:2:8:2:8 | operator= | operator= | | | ODASA-5186.hpp:2:8:2:8 | declaration | +| ODASA-5186.hpp:2:8:2:8 | operator= | operator= | | | ODASA-5186.hpp:2:8:2:8 | declaration | +| ODASA-5186.hpp:4:18:4:27 | operator!= | operator!= | isTopLevel | TopLevelFunction | ODASA-5186.hpp:4:18:4:27 | declaration | +| ODASA-5186.hpp:4:18:4:27 | operator!= | operator!= | isTopLevel | TopLevelFunction | ODASA-5186.hpp:4:18:4:27 | definition | +| functions.cpp:1:6:1:6 | f | f | isTopLevel | TopLevelFunction | functions.cpp:1:6:1:6 | declaration | +| functions.cpp:1:6:1:6 | f | f | isTopLevel | TopLevelFunction | functions.cpp:1:6:1:6 | definition | +| functions.cpp:7:8:7:8 | operator= | operator= | | | functions.cpp:7:8:7:8 | declaration | +| functions.cpp:7:8:7:8 | operator= | operator= | | | functions.cpp:7:8:7:8 | declaration | +| functions.cpp:8:7:8:8 | af | af | | | functions.cpp:8:7:8:8 | declaration | +| functions.cpp:8:7:8:8 | af | af | | | functions.cpp:8:7:8:8 | definition | +| functions.cpp:11:7:11:8 | ag | ag | | | functions.cpp:11:7:11:8 | declaration | +| functions.cpp:14:6:14:6 | g | g | isTopLevel | TopLevelFunction | functions.cpp:5:6:5:6 | declaration | +| functions.cpp:14:6:14:6 | g | g | isTopLevel | TopLevelFunction | functions.cpp:14:6:14:6 | declaration | +| functions.cpp:14:6:14:6 | g | g | isTopLevel | TopLevelFunction | functions.cpp:14:6:14:6 | definition | +| functions.cpp:19:7:19:7 | operator= | operator= | | | functions.cpp:19:7:19:7 | declaration | +| functions.cpp:19:7:19:7 | operator= | operator= | | | functions.cpp:19:7:19:7 | declaration | +| functions.cpp:23:7:23:7 | Table | Table | | | functions.cpp:23:7:23:7 | declaration | +| functions.cpp:23:7:23:7 | operator= | operator= | | | functions.cpp:23:7:23:7 | declaration | +| functions.cpp:27:3:27:7 | Table | Table | | | functions.cpp:27:3:27:7 | declaration | +| functions.cpp:27:3:27:7 | Table | Table | | | functions.cpp:27:3:27:7 | definition | +| functions.cpp:28:3:28:8 | ~Table | ~Table | | | functions.cpp:28:3:28:8 | declaration | +| functions.cpp:28:3:28:8 | ~Table | ~Table | | | functions.cpp:28:3:28:8 | definition | +| functions.cpp:29:9:29:14 | lookup | lookup | | | functions.cpp:29:9:29:14 | declaration | +| functions.cpp:30:8:30:13 | insert | insert | | | functions.cpp:30:8:30:13 | declaration | diff --git a/cpp/ql/test/library-tests/functions/functions/Functions1.ql b/cpp/ql/test/library-tests/functions/functions/Functions1.ql new file mode 100644 index 000000000000..bcb1d736bb8e --- /dev/null +++ b/cpp/ql/test/library-tests/functions/functions/Functions1.ql @@ -0,0 +1,14 @@ +/** + * @name Functions1 + */ +import cpp + +from Function f, string top1, string top2, Location loc, string loctype +where + if f.isTopLevel() then top1 = "isTopLevel" else top1 = "" and + if f instanceof TopLevelFunction then top2 = "TopLevelFunction" else top2 = "" and + ( + (loc = f.getADeclarationLocation() and loctype = "declaration") or + (loc = f.getDefinitionLocation() and loctype = "definition") + ) +select f, f.getName(), top1, top2, loc.toString(), loctype diff --git a/cpp/ql/test/library-tests/functions/functions/Functions2.expected b/cpp/ql/test/library-tests/functions/functions/Functions2.expected new file mode 100644 index 000000000000..91de943e049c --- /dev/null +++ b/cpp/ql/test/library-tests/functions/functions/Functions2.expected @@ -0,0 +1,23 @@ +| ODASA-5186.cpp:4:8:4:14 | MyClass | Class | ODASA-5186.cpp:5:8:5:17 | operator== | member function | +| ODASA-5186.cpp:4:8:4:14 | MyClass | Struct | ODASA-5186.cpp:4:8:4:8 | operator= | member function | +| ODASA-5186.cpp:4:8:4:14 | MyClass | Struct | ODASA-5186.cpp:4:8:4:8 | operator= | member function | +| ODASA-5186.cpp:4:8:4:14 | MyClass | Struct | ODASA-5186.cpp:5:8:5:17 | operator== | member function | +| ODASA-5186.hpp:2:8:2:17 | NEQ_helper> | Struct | ODASA-5186.hpp:2:8:2:8 | operator= | member function | +| ODASA-5186.hpp:2:8:2:17 | NEQ_helper> | Struct | ODASA-5186.hpp:2:8:2:8 | operator= | member function | +| file://:0:0:0:0 | __va_list_tag | Struct | file://:0:0:0:0 | operator= | member function | +| file://:0:0:0:0 | __va_list_tag | Struct | file://:0:0:0:0 | operator= | member function | +| functions.cpp:7:8:7:8 | A | Struct | functions.cpp:7:8:7:8 | operator= | member function | +| functions.cpp:7:8:7:8 | A | Struct | functions.cpp:7:8:7:8 | operator= | member function | +| functions.cpp:7:8:7:8 | A | Struct | functions.cpp:8:7:8:8 | af | member function | +| functions.cpp:7:8:7:8 | A | Struct | functions.cpp:11:7:11:8 | ag | member function | +| functions.cpp:19:7:19:10 | Name | Class | functions.cpp:19:7:19:7 | operator= | member function | +| functions.cpp:19:7:19:10 | Name | Class | functions.cpp:19:7:19:7 | operator= | member function | +| functions.cpp:23:7:23:11 | Table | Class | functions.cpp:23:7:23:7 | Table | constructor | +| functions.cpp:23:7:23:11 | Table | Class | functions.cpp:23:7:23:7 | Table | member function | +| functions.cpp:23:7:23:11 | Table | Class | functions.cpp:23:7:23:7 | operator= | member function | +| functions.cpp:23:7:23:11 | Table | Class | functions.cpp:27:3:27:7 | Table | constructor | +| functions.cpp:23:7:23:11 | Table | Class | functions.cpp:27:3:27:7 | Table | member function | +| functions.cpp:23:7:23:11 | Table | Class | functions.cpp:28:3:28:8 | ~Table | destructor | +| functions.cpp:23:7:23:11 | Table | Class | functions.cpp:28:3:28:8 | ~Table | member function | +| functions.cpp:23:7:23:11 | Table | Class | functions.cpp:29:9:29:14 | lookup | member function | +| functions.cpp:23:7:23:11 | Table | Class | functions.cpp:30:8:30:13 | insert | member function | diff --git a/cpp/ql/test/library-tests/functions/functions/Functions2.ql b/cpp/ql/test/library-tests/functions/functions/Functions2.ql new file mode 100644 index 000000000000..1907f1b67336 --- /dev/null +++ b/cpp/ql/test/library-tests/functions/functions/Functions2.ql @@ -0,0 +1,15 @@ +/** + * @name Functions2 + * @kind table + */ +import cpp + +from Class c, string ctype, MemberFunction f, string ftype +where + if c instanceof Struct then ctype = "Struct" else ctype = "Class" and + ( + (f = c.getAConstructor() and ftype = "constructor") or + (f = c.getDestructor() and ftype = "destructor") or + (f.getDeclaringType() = c and ftype = "member function") + ) +select c, ctype, f, ftype diff --git a/cpp/ql/test/library-tests/functions/functions/ODASA-5186.cpp b/cpp/ql/test/library-tests/functions/functions/ODASA-5186.cpp new file mode 100644 index 000000000000..e4b534ca0cff --- /dev/null +++ b/cpp/ql/test/library-tests/functions/functions/ODASA-5186.cpp @@ -0,0 +1,18 @@ +#include "ODASA-5186.hpp" + +template +struct MyClass : public NEQ_helper> { + bool operator==(const MyClass& other) const { return true; } +}; + +bool test() { + MyClass x; + MyClass y; + + // This is a regression test for a bug found during the investigation of + // ODASA-5186. Due to a bug in the extractor, the call to `operator!=` + // below did not have a target (that is, `FunctionCall.getTarget()` had + // no results). The bug was related to #include files, because the call + // had a target if we manually included the contents of ODASA-5186.hpp. + return (x != y); +} diff --git a/cpp/ql/test/library-tests/functions/functions/ODASA-5186.hpp b/cpp/ql/test/library-tests/functions/functions/ODASA-5186.hpp new file mode 100644 index 000000000000..6397341b3b7c --- /dev/null +++ b/cpp/ql/test/library-tests/functions/functions/ODASA-5186.hpp @@ -0,0 +1,5 @@ +template +struct NEQ_helper +{ + friend bool operator!=(const T& x, const T& y) { return !static_cast(x == y); } +}; diff --git a/cpp/ql/test/library-tests/functions/functions/ODASA-5722.cpp b/cpp/ql/test/library-tests/functions/functions/ODASA-5722.cpp new file mode 100644 index 000000000000..ebe4f55be9bd --- /dev/null +++ b/cpp/ql/test/library-tests/functions/functions/ODASA-5722.cpp @@ -0,0 +1,7 @@ +template +struct NEQ_helper { +}; + +template +struct MyClass : public NEQ_helper> { +}; diff --git a/cpp/ql/test/library-tests/functions/functions/functions.cpp b/cpp/ql/test/library-tests/functions/functions/functions.cpp new file mode 100644 index 000000000000..f3c630cebbbb --- /dev/null +++ b/cpp/ql/test/library-tests/functions/functions/functions.cpp @@ -0,0 +1,33 @@ +void f(int a, int b) { + bool c = a==b; +} + +void g(); + +struct A { + void af() { + + } + void ag(); +}; + +void g() { + A a; + a.ag(); +} + +class Name { + const char* s; +}; + +class Table { + Name* p; + int sz; +public: + Table(int s=15) { p = new Name[sz=s]; } // constructor + ~Table() { delete[] p; } + Name* lookup (const char*); + bool insert(Name*); +}; + + diff --git a/cpp/ql/test/library-tests/functions/generated/a.cpp b/cpp/ql/test/library-tests/functions/generated/a.cpp new file mode 100644 index 000000000000..c282ae1d33c4 --- /dev/null +++ b/cpp/ql/test/library-tests/functions/generated/a.cpp @@ -0,0 +1,5 @@ +#include "a.h" + +void a() { + A variable; +} diff --git a/cpp/ql/test/library-tests/functions/generated/a.h b/cpp/ql/test/library-tests/functions/generated/a.h new file mode 100644 index 000000000000..a8fff55d29af --- /dev/null +++ b/cpp/ql/test/library-tests/functions/generated/a.h @@ -0,0 +1,10 @@ +struct Base { + Base() {} + ~Base() {} +}; + +struct A : Base { + /* Because of Base() and ~Base(), the extractor generates A() and ~A() here, but it + * only generates them on-demand, and they don't appear in the source sequence list. + */ +}; diff --git a/cpp/ql/test/library-tests/functions/generated/b.cpp b/cpp/ql/test/library-tests/functions/generated/b.cpp new file mode 100644 index 000000000000..c500909d288c --- /dev/null +++ b/cpp/ql/test/library-tests/functions/generated/b.cpp @@ -0,0 +1,5 @@ +#include "a.h" + +void b() { + A nother; +} diff --git a/cpp/ql/test/library-tests/functions/generated/calls.expected b/cpp/ql/test/library-tests/functions/generated/calls.expected new file mode 100644 index 000000000000..3e62b30f5898 --- /dev/null +++ b/cpp/ql/test/library-tests/functions/generated/calls.expected @@ -0,0 +1,6 @@ +| a.cpp:4:5:4:12 | call to A | +| a.cpp:5:1:5:1 | call to ~A | +| a.h:6:8:6:8 | call to Base | +| a.h:6:8:6:8 | call to ~Base | +| b.cpp:4:5:4:10 | call to A | +| b.cpp:5:1:5:1 | call to ~A | diff --git a/cpp/ql/test/library-tests/functions/generated/calls.ql b/cpp/ql/test/library-tests/functions/generated/calls.ql new file mode 100644 index 000000000000..c22d8d273141 --- /dev/null +++ b/cpp/ql/test/library-tests/functions/generated/calls.ql @@ -0,0 +1,4 @@ +import cpp + +from FunctionCall fc +select fc diff --git a/cpp/ql/test/library-tests/functions/override/override.cpp b/cpp/ql/test/library-tests/functions/override/override.cpp new file mode 100644 index 000000000000..63bbc7ad5496 --- /dev/null +++ b/cpp/ql/test/library-tests/functions/override/override.cpp @@ -0,0 +1,9 @@ +struct Base { + virtual void a(); + virtual void b(); +}; + +struct Derived : Base { + void a() override; + void b(); +}; diff --git a/cpp/ql/test/library-tests/functions/override/override.expected b/cpp/ql/test/library-tests/functions/override/override.expected new file mode 100644 index 000000000000..0b92f7974dcc --- /dev/null +++ b/cpp/ql/test/library-tests/functions/override/override.expected @@ -0,0 +1,4 @@ +| override.cpp:1:8:1:11 | Base | override.cpp:2:16:2:16 | a | 0 | +| override.cpp:1:8:1:11 | Base | override.cpp:3:16:3:16 | b | 0 | +| override.cpp:6:8:6:14 | Derived | override.cpp:7:8:7:8 | a | 1 | +| override.cpp:6:8:6:14 | Derived | override.cpp:8:8:8:8 | b | 0 | diff --git a/cpp/ql/test/library-tests/functions/override/override.ql b/cpp/ql/test/library-tests/functions/override/override.ql new file mode 100644 index 000000000000..fdc6f67b9a62 --- /dev/null +++ b/cpp/ql/test/library-tests/functions/override/override.ql @@ -0,0 +1,4 @@ +import cpp + +from VirtualFunction f +select f.getDeclaringType(), f, count(VirtualFunction g | f = g and g.isOverrideExplicit()) diff --git a/cpp/ql/test/library-tests/functions/qualifiers/a.cpp b/cpp/ql/test/library-tests/functions/qualifiers/a.cpp new file mode 100644 index 000000000000..f28f5c855f8f --- /dev/null +++ b/cpp/ql/test/library-tests/functions/qualifiers/a.cpp @@ -0,0 +1,8 @@ + +// This example highlighted a bug in extracting routine type qualifiers. + +template +struct S { + void f(A (B::*)() const); +}; + diff --git a/cpp/ql/test/library-tests/functions/qualifiers/test.expected b/cpp/ql/test/library-tests/functions/qualifiers/test.expected new file mode 100644 index 000000000000..a7f8ef34c91d --- /dev/null +++ b/cpp/ql/test/library-tests/functions/qualifiers/test.expected @@ -0,0 +1 @@ +| file://:0:0:0:0 | ..(..) | true | diff --git a/cpp/ql/test/library-tests/functions/qualifiers/test.ql b/cpp/ql/test/library-tests/functions/qualifiers/test.ql new file mode 100644 index 000000000000..7ded23cf5c16 --- /dev/null +++ b/cpp/ql/test/library-tests/functions/qualifiers/test.ql @@ -0,0 +1,5 @@ +import cpp + +from SpecifiedType t, boolean b +where if t.isConst() then b = true else b = false +select t, b diff --git a/cpp/ql/test/library-tests/functions/unused/unused_functions.c b/cpp/ql/test/library-tests/functions/unused/unused_functions.c new file mode 100644 index 000000000000..bbd7e95d2168 --- /dev/null +++ b/cpp/ql/test/library-tests/functions/unused/unused_functions.c @@ -0,0 +1,64 @@ +// semmle-extractor-options: -fblocks +#ifdef COMPILABLE_TEST +#include +#else +void printf(char *str); +#endif + +static void used_function(void) { + printf("Gets run\n"); +} + +static void used_function2(void) { + printf("Gets run 2\n"); +} + +static void unused_function(void) { + printf("Doesn't get run\n"); +} + +static void unused_function2(void) { + printf("Doesn't get run 2\n"); +} + +static void unused_function3(void) { + printf("Doesn't get run 3\n"); + unused_function2(); +} + +static void __attribute__ ((constructor (300))) f300(void) { + printf("Constructor 300 running\n"); +} + +static void __attribute__ ((constructor)) f(void) { + printf("Constructor running\n"); +} + +int main(void) { + printf("Main running\n"); + used_function(); + used_function2(); + int (^four)(void) = ^{ return 4; }; + return 0; +} + +static void __attribute__ ((destructor)) g(void) { + printf("Destructor running\n"); +} + +static void __attribute__ ((destructor (300))) g300(void) { + printf("Destructor 300 running\n"); +} + +static void h2(void) { +} + +static void __attribute__ ((used)) h1(void) { + h2(); +} + +static void __attribute__ ((unused)) h3(void) { +} + +static void h4(void) { +} diff --git a/cpp/ql/test/library-tests/functions/unused/unused_functions.expected b/cpp/ql/test/library-tests/functions/unused/unused_functions.expected new file mode 100644 index 000000000000..546335812fb3 --- /dev/null +++ b/cpp/ql/test/library-tests/functions/unused/unused_functions.expected @@ -0,0 +1,4 @@ +| unused_functions.c:16:13:16:27 | unused_function | Static function unused_function is unreachable | unused_functions.c:16:13:16:27 | unused_function | unused_function | +| unused_functions.c:20:13:20:28 | unused_function2 | Static function unused_function2 is unreachable ($@ must be removed at the same time) | unused_functions.c:24:13:24:28 | unused_function3 | unused_function3 | +| unused_functions.c:24:13:24:28 | unused_function3 | Static function unused_function3 is unreachable | unused_functions.c:24:13:24:28 | unused_function3 | unused_function3 | +| unused_functions.c:63:13:63:14 | h4 | Static function h4 is unreachable | unused_functions.c:63:13:63:14 | h4 | h4 | diff --git a/cpp/ql/test/library-tests/functions/unused/unused_functions.qlref b/cpp/ql/test/library-tests/functions/unused/unused_functions.qlref new file mode 100644 index 000000000000..dbf4c4e9172c --- /dev/null +++ b/cpp/ql/test/library-tests/functions/unused/unused_functions.qlref @@ -0,0 +1 @@ +Best Practices/Unused Entities/UnusedStaticFunctions.ql diff --git a/cpp/ql/test/library-tests/functions/unused_mut/unused_mut.c b/cpp/ql/test/library-tests/functions/unused_mut/unused_mut.c new file mode 100644 index 000000000000..7ce51610eefd --- /dev/null +++ b/cpp/ql/test/library-tests/functions/unused_mut/unused_mut.c @@ -0,0 +1,19 @@ + +static void mut_unused_function(void); +static void mut_unused_function2(void); + +static void mut_unused_function(void) { + mut_unused_function2(); +} + +static void mut_unused_function2(void) { + mut_unused_function(); +} + +static void f(void) { } +static void g(void) { f(); } +static void h(void) { void (*fun)(void) = g; } +static void i(void) { h(); } +static void j(void) { i(); } +void k(void) { j(); } + diff --git a/cpp/ql/test/library-tests/functions/unused_mut/unused_mut.expected b/cpp/ql/test/library-tests/functions/unused_mut/unused_mut.expected new file mode 100644 index 000000000000..d41677ac4791 --- /dev/null +++ b/cpp/ql/test/library-tests/functions/unused_mut/unused_mut.expected @@ -0,0 +1,2 @@ +| unused_mut.c:5:13:5:31 | mut_unused_function | Static function mut_unused_function is unreachable ($@ must be removed at the same time) | unused_mut.c:9:13:9:32 | mut_unused_function2 | mut_unused_function2 | +| unused_mut.c:9:13:9:32 | mut_unused_function2 | Static function mut_unused_function2 is unreachable ($@ must be removed at the same time) | unused_mut.c:5:13:5:31 | mut_unused_function | mut_unused_function | diff --git a/cpp/ql/test/library-tests/functions/unused_mut/unused_mut.qlref b/cpp/ql/test/library-tests/functions/unused_mut/unused_mut.qlref new file mode 100644 index 000000000000..dbf4c4e9172c --- /dev/null +++ b/cpp/ql/test/library-tests/functions/unused_mut/unused_mut.qlref @@ -0,0 +1 @@ +Best Practices/Unused Entities/UnusedStaticFunctions.ql diff --git a/cpp/ql/test/library-tests/includes/include_next/a/test.h b/cpp/ql/test/library-tests/includes/include_next/a/test.h new file mode 100644 index 000000000000..243179d52421 --- /dev/null +++ b/cpp/ql/test/library-tests/includes/include_next/a/test.h @@ -0,0 +1,3 @@ +#pragma GCC system_header +#include_next + diff --git a/cpp/ql/test/library-tests/includes/include_next/b/loop.h b/cpp/ql/test/library-tests/includes/include_next/b/loop.h new file mode 100644 index 000000000000..5e3643e8ffb3 --- /dev/null +++ b/cpp/ql/test/library-tests/includes/include_next/b/loop.h @@ -0,0 +1,3 @@ + +#include_next + diff --git a/cpp/ql/test/library-tests/includes/include_next/b/test.h b/cpp/ql/test/library-tests/includes/include_next/b/test.h new file mode 100644 index 000000000000..69a56910c4c0 --- /dev/null +++ b/cpp/ql/test/library-tests/includes/include_next/b/test.h @@ -0,0 +1,7 @@ +#ifndef _TEST_H_ +#define _TEST_H_ + +#include "loop.h" + +#endif + diff --git a/cpp/ql/test/library-tests/includes/include_next/foo.c b/cpp/ql/test/library-tests/includes/include_next/foo.c new file mode 100644 index 000000000000..36a3f45a091d --- /dev/null +++ b/cpp/ql/test/library-tests/includes/include_next/foo.c @@ -0,0 +1,3 @@ +// semmle-extractor-options: -I${testdir}/a -I${testdir}/b +#include + diff --git a/cpp/ql/test/library-tests/includes/include_next/includes.expected b/cpp/ql/test/library-tests/includes/include_next/includes.expected new file mode 100644 index 000000000000..ce4e44acc75f --- /dev/null +++ b/cpp/ql/test/library-tests/includes/include_next/includes.expected @@ -0,0 +1,5 @@ +| a/test.h:2:1:2:22 | #include_next | a/test.h:0:0:0:0 | a/test.h | +| a/test.h:2:1:2:22 | #include_next | b/test.h:0:0:0:0 | b/test.h | +| b/loop.h:2:1:2:22 | #include_next | a/test.h:0:0:0:0 | a/test.h | +| b/test.h:4:1:4:17 | #include "loop.h" | b/loop.h:0:0:0:0 | b/loop.h | +| foo.c:2:1:2:17 | #include | a/test.h:0:0:0:0 | a/test.h | diff --git a/cpp/ql/test/library-tests/includes/include_next/includes.ql b/cpp/ql/test/library-tests/includes/include_next/includes.ql new file mode 100644 index 000000000000..9f32e2fa669b --- /dev/null +++ b/cpp/ql/test/library-tests/includes/include_next/includes.ql @@ -0,0 +1,5 @@ +import cpp + +from Include i +select i, i.getIncludedFile() + diff --git a/cpp/ql/test/library-tests/includes/includes/bar.h b/cpp/ql/test/library-tests/includes/includes/bar.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/cpp/ql/test/library-tests/includes/includes/includes.c b/cpp/ql/test/library-tests/includes/includes/includes.c new file mode 100644 index 000000000000..6d29af60879e --- /dev/null +++ b/cpp/ql/test/library-tests/includes/includes/includes.c @@ -0,0 +1,5 @@ + +#line 1 "foo.h" + +#include "bar.h" + diff --git a/cpp/ql/test/library-tests/includes/includes/locations.expected b/cpp/ql/test/library-tests/includes/includes/locations.expected new file mode 100644 index 000000000000..1b6b3b068923 --- /dev/null +++ b/cpp/ql/test/library-tests/includes/includes/locations.expected @@ -0,0 +1,7 @@ +| bar.h:0:0:0:0 | bar.h:0:0:0:0 | +| file://:0:0:0:0 | file://:0:0:0:0 | +| file://:0:0:0:0 | file://:0:0:0:0 | +| file://:0:0:0:0 | file://:0:0:0:0 | +| includes.c:0:0:0:0 | includes.c:0:0:0:0 | +| includes.c:2:1:2:15 | includes.c:2:1:2:15 | +| includes.c:4:1:4:16 | includes.c:4:1:4:16 | diff --git a/cpp/ql/test/library-tests/includes/includes/locations.ql b/cpp/ql/test/library-tests/includes/includes/locations.ql new file mode 100644 index 000000000000..790322dc99b1 --- /dev/null +++ b/cpp/ql/test/library-tests/includes/includes/locations.ql @@ -0,0 +1,6 @@ +import cpp + +from Location l +where not l.getContainer() instanceof Folder +select l + diff --git a/cpp/ql/test/library-tests/includes/non_existent/diags.expected b/cpp/ql/test/library-tests/includes/non_existent/diags.expected new file mode 100644 index 000000000000..7339e9c4b4b7 --- /dev/null +++ b/cpp/ql/test/library-tests/includes/non_existent/diags.expected @@ -0,0 +1,2 @@ +| file://:0:0:0:0 | 4 | Error occurred | There was an error during this compilation | There was an error during this compilation | +| test.c:2:26:2:26 | 5 | cannot_open_file | cannot open source file 'non_existent.h' | "test.c", line 2: catastrophic error: cannot open source file "non_existent.h"\n #include \n ^\n\n | diff --git a/cpp/ql/test/library-tests/includes/non_existent/diags.ql b/cpp/ql/test/library-tests/includes/non_existent/diags.ql new file mode 100644 index 000000000000..d22ba539555c --- /dev/null +++ b/cpp/ql/test/library-tests/includes/non_existent/diags.ql @@ -0,0 +1,5 @@ +import cpp + +from Diagnostic d +select d.getLocation().toString(), d.getSeverity(), d.getTag(), d.getMessage(), d.getFullMessage() + diff --git a/cpp/ql/test/library-tests/includes/non_existent/options b/cpp/ql/test/library-tests/includes/non_existent/options new file mode 100644 index 000000000000..4181beff3c28 --- /dev/null +++ b/cpp/ql/test/library-tests/includes/non_existent/options @@ -0,0 +1 @@ +extractor_flags: --expect_errors diff --git a/cpp/ql/test/library-tests/includes/non_existent/test.c b/cpp/ql/test/library-tests/includes/non_existent/test.c new file mode 100644 index 000000000000..a0cf6a848d3d --- /dev/null +++ b/cpp/ql/test/library-tests/includes/non_existent/test.c @@ -0,0 +1,3 @@ + +#include + diff --git a/cpp/ql/test/library-tests/instantiations/test.cpp b/cpp/ql/test/library-tests/instantiations/test.cpp new file mode 100644 index 000000000000..cf02d6a2f36b --- /dev/null +++ b/cpp/ql/test/library-tests/instantiations/test.cpp @@ -0,0 +1,28 @@ + +class A { public: ~A() { } }; + +class X { public: virtual bool vf(void) = 0; }; + +/* +Y has a generated destructor, but it is only reachable via an +instantiated class. Therefore if indexing of class instantiations +is deferred then we do not index the destructor. + +If *_TEMPLATE_INSTANTIATIONS_IN_SOURCE_SEQUENCE_LISTS are enabled +then it also becomes reachable via the source sequence lists. +*/ +class Y : public X { public: bool vf(void) override; A a; }; + +class B { public: virtual void vfun() = 0; }; + +template +class C : public B { + public: + virtual void vfun() { + T* x; + x->~T(); + } +}; + +void f(void) { new C(); } + diff --git a/cpp/ql/test/library-tests/instantiations/test.expected b/cpp/ql/test/library-tests/instantiations/test.expected new file mode 100644 index 000000000000..44db55b6b37d --- /dev/null +++ b/cpp/ql/test/library-tests/instantiations/test.expected @@ -0,0 +1,33 @@ +| file://:0:0:0:0 | operator delete | +| 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 | operator= | +| test.cpp:2:19:2:20 | ~A | +| test.cpp:4:7:4:7 | X | +| test.cpp:4:7:4:7 | X | +| test.cpp:4:7:4:7 | X | +| test.cpp:4:7:4:7 | operator= | +| test.cpp:4:7:4:7 | operator= | +| test.cpp:4:32:4:33 | vf | +| test.cpp:14:7:14:7 | Y | +| test.cpp:14:7:14:7 | Y | +| test.cpp:14:7:14:7 | Y | +| test.cpp:14:7:14:7 | operator= | +| test.cpp:14:7:14:7 | operator= | +| test.cpp:14:7:14:7 | ~Y | +| test.cpp:14:35:14:36 | vf | +| test.cpp:16:7:16:7 | B | +| test.cpp:16:7:16:7 | B | +| test.cpp:16:7:16:7 | B | +| test.cpp:16:7:16:7 | operator= | +| test.cpp:16:7:16:7 | operator= | +| test.cpp:16:32:16:35 | vfun | +| test.cpp:19:7:19:7 | C | +| test.cpp:19:7:19:7 | C | +| test.cpp:19:7:19:7 | C | +| test.cpp:19:7:19:7 | operator= | +| test.cpp:19:7:19:7 | operator= | +| test.cpp:21:18:21:21 | vfun | +| test.cpp:21:18:21:21 | vfun | +| test.cpp:27:6:27:6 | f | diff --git a/cpp/ql/test/library-tests/instantiations/test.ql b/cpp/ql/test/library-tests/instantiations/test.ql new file mode 100644 index 000000000000..af1f94dc9b5a --- /dev/null +++ b/cpp/ql/test/library-tests/instantiations/test.ql @@ -0,0 +1,5 @@ +import cpp + +from Function f +select f + diff --git a/cpp/ql/test/library-tests/ir/constant_func/constant_func.cpp b/cpp/ql/test/library-tests/ir/constant_func/constant_func.cpp new file mode 100644 index 000000000000..9a176b82b610 --- /dev/null +++ b/cpp/ql/test/library-tests/ir/constant_func/constant_func.cpp @@ -0,0 +1,32 @@ +int ReturnConstant() { + return 7; +} + +int ReturnConstantPhi(bool b) { + if (b) { + return 7; + } + else { + return 7; + } +} + +int GetInt(); + +int ReturnNonConstantPhi(bool b) { + if (b) { + return 7; + } + else { + return GetInt(); + } +} + +int ReturnConstantPhiLoop(int x) { + int y = 7; + while (x > 0) { + y = 7; + --x; + } + return y; +} diff --git a/cpp/ql/test/library-tests/ir/constant_func/constant_func.expected b/cpp/ql/test/library-tests/ir/constant_func/constant_func.expected new file mode 100644 index 000000000000..7aebdccfdfbf --- /dev/null +++ b/cpp/ql/test/library-tests/ir/constant_func/constant_func.expected @@ -0,0 +1,3 @@ +| constant_func.cpp:1:5:1:18 | IR: ReturnConstant | 7 | +| constant_func.cpp:5:5:5:21 | IR: ReturnConstantPhi | 7 | +| constant_func.cpp:25:5:25:25 | IR: ReturnConstantPhiLoop | 7 | diff --git a/cpp/ql/test/library-tests/ir/constant_func/constant_func.ql b/cpp/ql/test/library-tests/ir/constant_func/constant_func.ql new file mode 100644 index 000000000000..e23ac770cbee --- /dev/null +++ b/cpp/ql/test/library-tests/ir/constant_func/constant_func.ql @@ -0,0 +1,30 @@ +import default +import semmle.code.cpp.ssa.SSAIR +import semmle.code.cpp.ssa.internal.IntegerConstant + +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.getLeftOperand()) and + right = getConstantValue(binInstr.getRightOperand()) 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(Instruction operand | operand = phi.getAnOperand() | getConstantValue(operand)) and + result = min(Instruction operand | operand = phi.getAnOperand() | getConstantValue(operand)) + ) +} + +from FunctionIR funcIR, int value +where + value = getValue(getConstantValue(funcIR.getReturnInstruction().(ReturnValueInstruction).getReturnValue())) +select funcIR, value diff --git a/cpp/ql/test/library-tests/ir/ir/AliasedSSAIRSanity.expected b/cpp/ql/test/library-tests/ir/ir/AliasedSSAIRSanity.expected new file mode 100644 index 000000000000..3d93d822b3cf --- /dev/null +++ b/cpp/ql/test/library-tests/ir/ir/AliasedSSAIRSanity.expected @@ -0,0 +1,3 @@ +missingOperand +unexpectedOperand +duplicateOperand diff --git a/cpp/ql/test/library-tests/ir/ir/AliasedSSAIRSanity.qlref b/cpp/ql/test/library-tests/ir/ir/AliasedSSAIRSanity.qlref new file mode 100644 index 000000000000..607a5e6bc1fd --- /dev/null +++ b/cpp/ql/test/library-tests/ir/ir/AliasedSSAIRSanity.qlref @@ -0,0 +1 @@ +semmle/code/cpp/ssa/AliasedSSAIRSanity.ql \ No newline at end of file diff --git a/cpp/ql/test/library-tests/ir/ir/IRSanity.expected b/cpp/ql/test/library-tests/ir/ir/IRSanity.expected new file mode 100644 index 000000000000..3d93d822b3cf --- /dev/null +++ b/cpp/ql/test/library-tests/ir/ir/IRSanity.expected @@ -0,0 +1,3 @@ +missingOperand +unexpectedOperand +duplicateOperand diff --git a/cpp/ql/test/library-tests/ir/ir/IRSanity.qlref b/cpp/ql/test/library-tests/ir/ir/IRSanity.qlref new file mode 100644 index 000000000000..9e1bfefd7137 --- /dev/null +++ b/cpp/ql/test/library-tests/ir/ir/IRSanity.qlref @@ -0,0 +1 @@ +semmle/code/cpp/ir/IRSanity.ql \ No newline at end of file diff --git a/cpp/ql/test/library-tests/ir/ir/PrintAST.expected b/cpp/ql/test/library-tests/ir/ir/PrintAST.expected new file mode 100644 index 000000000000..d40d87320ad2 --- /dev/null +++ b/cpp/ql/test/library-tests/ir/ir/PrintAST.expected @@ -0,0 +1,2368 @@ +| ir.cpp:1:6:1:14 | Constants() -> void | 0 | -1 | 0 | Constants | | | | ir.cpp:1:6:1:14 | +| ir.cpp:1:6:1:14 | Constants() -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:1:18:41:1 | +| ir.cpp:1:6:1:14 | Constants() -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:2:5:2:17 | +| ir.cpp:1:6:1:14 | Constants() -> void | 3 | 2 | 0 | definition of c_i | | | char | ir.cpp:2:10:2:12 | +| ir.cpp:1:6:1:14 | Constants() -> void | 4 | 3 | 0 | initializer for c_i | | | | ir.cpp:2:15:2:16 | +| ir.cpp:1:6:1:14 | Constants() -> void | 5 | 4 | 0 | (char)... | integral conversion | =1 | prvalue: char | ir.cpp:2:16:2:16 | +| ir.cpp:1:6:1:14 | Constants() -> void | 6 | 5 | 0 | 1 | | =1 | prvalue: int | ir.cpp:2:16:2:16 | +| ir.cpp:1:6:1:14 | Constants() -> void | 7 | 1 | 1 | declaration | | | | ir.cpp:3:5:3:19 | +| ir.cpp:1:6:1:14 | Constants() -> void | 8 | 7 | 0 | definition of c_c | | | char | ir.cpp:3:10:3:12 | +| ir.cpp:1:6:1:14 | Constants() -> void | 9 | 8 | 0 | initializer for c_c | | | | ir.cpp:3:15:3:18 | +| ir.cpp:1:6:1:14 | Constants() -> void | 10 | 9 | 0 | 65 | | =65 | prvalue: char | ir.cpp:3:15:3:18 | +| ir.cpp:1:6:1:14 | Constants() -> void | 11 | 1 | 2 | declaration | | | | ir.cpp:5:5:5:26 | +| ir.cpp:1:6:1:14 | Constants() -> void | 12 | 11 | 0 | definition of sc_i | | | signed char | ir.cpp:5:17:5:20 | +| ir.cpp:1:6:1:14 | Constants() -> void | 13 | 12 | 0 | initializer for sc_i | | | | ir.cpp:5:23:5:25 | +| ir.cpp:1:6:1:14 | Constants() -> void | 14 | 13 | 0 | (signed char)... | integral conversion | =-1 | prvalue: signed char | ir.cpp:5:24:5:25 | +| ir.cpp:1:6:1:14 | Constants() -> void | 15 | 14 | 0 | - ... | | =-1 | prvalue: int | ir.cpp:5:24:5:25 | +| ir.cpp:1:6:1:14 | Constants() -> void | 16 | 15 | 0 | 1 | | =1 | prvalue: int | ir.cpp:5:25:5:25 | +| ir.cpp:1:6:1:14 | Constants() -> void | 17 | 1 | 3 | declaration | | | | ir.cpp:6:5:6:27 | +| ir.cpp:1:6:1:14 | Constants() -> void | 18 | 17 | 0 | definition of sc_c | | | signed char | ir.cpp:6:17:6:20 | +| ir.cpp:1:6:1:14 | Constants() -> void | 19 | 18 | 0 | initializer for sc_c | | | | ir.cpp:6:23:6:26 | +| ir.cpp:1:6:1:14 | Constants() -> void | 20 | 19 | 0 | (signed char)... | integral conversion | =65 | prvalue: signed char | ir.cpp:6:24:6:26 | +| ir.cpp:1:6:1:14 | Constants() -> void | 21 | 20 | 0 | 65 | | =65 | prvalue: char | ir.cpp:6:24:6:26 | +| ir.cpp:1:6:1:14 | Constants() -> void | 22 | 1 | 4 | declaration | | | | ir.cpp:8:5:8:27 | +| ir.cpp:1:6:1:14 | Constants() -> void | 23 | 22 | 0 | definition of uc_i | | | unsigned char | ir.cpp:8:19:8:22 | +| ir.cpp:1:6:1:14 | Constants() -> void | 24 | 23 | 0 | initializer for uc_i | | | | ir.cpp:8:25:8:26 | +| ir.cpp:1:6:1:14 | Constants() -> void | 25 | 24 | 0 | (unsigned char)... | integral conversion | =5 | prvalue: unsigned char | ir.cpp:8:26:8:26 | +| ir.cpp:1:6:1:14 | Constants() -> void | 26 | 25 | 0 | 5 | | =5 | prvalue: int | ir.cpp:8:26:8:26 | +| ir.cpp:1:6:1:14 | Constants() -> void | 27 | 1 | 5 | declaration | | | | ir.cpp:9:5:9:29 | +| ir.cpp:1:6:1:14 | Constants() -> void | 28 | 27 | 0 | definition of uc_c | | | unsigned char | ir.cpp:9:19:9:22 | +| ir.cpp:1:6:1:14 | Constants() -> void | 29 | 28 | 0 | initializer for uc_c | | | | ir.cpp:9:25:9:28 | +| ir.cpp:1:6:1:14 | Constants() -> void | 30 | 29 | 0 | (unsigned char)... | integral conversion | =65 | prvalue: unsigned char | ir.cpp:9:26:9:28 | +| ir.cpp:1:6:1:14 | Constants() -> void | 31 | 30 | 0 | 65 | | =65 | prvalue: char | ir.cpp:9:26:9:28 | +| ir.cpp:1:6:1:14 | Constants() -> void | 32 | 1 | 6 | declaration | | | | ir.cpp:11:5:11:16 | +| ir.cpp:1:6:1:14 | Constants() -> void | 33 | 32 | 0 | definition of s | | | short | ir.cpp:11:11:11:11 | +| ir.cpp:1:6:1:14 | Constants() -> void | 34 | 33 | 0 | initializer for s | | | | ir.cpp:11:14:11:15 | +| ir.cpp:1:6:1:14 | Constants() -> void | 35 | 34 | 0 | (short)... | integral conversion | =5 | prvalue: short | ir.cpp:11:15:11:15 | +| ir.cpp:1:6:1:14 | Constants() -> void | 36 | 35 | 0 | 5 | | =5 | prvalue: int | ir.cpp:11:15:11:15 | +| ir.cpp:1:6:1:14 | Constants() -> void | 37 | 1 | 7 | declaration | | | | ir.cpp:12:5:12:26 | +| ir.cpp:1:6:1:14 | Constants() -> void | 38 | 37 | 0 | definition of us | | | unsigned short | ir.cpp:12:20:12:21 | +| ir.cpp:1:6:1:14 | Constants() -> void | 39 | 38 | 0 | initializer for us | | | | ir.cpp:12:24:12:25 | +| ir.cpp:1:6:1:14 | Constants() -> void | 40 | 39 | 0 | (unsigned short)... | integral conversion | =5 | prvalue: unsigned short | ir.cpp:12:25:12:25 | +| ir.cpp:1:6:1:14 | Constants() -> void | 41 | 40 | 0 | 5 | | =5 | prvalue: int | ir.cpp:12:25:12:25 | +| ir.cpp:1:6:1:14 | Constants() -> void | 42 | 1 | 8 | declaration | | | | ir.cpp:14:5:14:14 | +| ir.cpp:1:6:1:14 | Constants() -> void | 43 | 42 | 0 | definition of i | | | int | ir.cpp:14:9:14:9 | +| ir.cpp:1:6:1:14 | Constants() -> void | 44 | 43 | 0 | initializer for i | | | | ir.cpp:14:12:14:13 | +| ir.cpp:1:6:1:14 | Constants() -> void | 45 | 44 | 0 | 5 | | =5 | prvalue: int | ir.cpp:14:12:14:13 | +| ir.cpp:1:6:1:14 | Constants() -> void | 46 | 1 | 9 | declaration | | | | ir.cpp:15:5:15:24 | +| ir.cpp:1:6:1:14 | Constants() -> void | 47 | 46 | 0 | definition of ui | | | unsigned int | ir.cpp:15:18:15:19 | +| ir.cpp:1:6:1:14 | Constants() -> void | 48 | 47 | 0 | initializer for ui | | | | ir.cpp:15:22:15:23 | +| ir.cpp:1:6:1:14 | Constants() -> void | 49 | 48 | 0 | (unsigned int)... | integral conversion | =5 | prvalue: unsigned int | ir.cpp:15:23:15:23 | +| ir.cpp:1:6:1:14 | Constants() -> void | 50 | 49 | 0 | 5 | | =5 | prvalue: int | ir.cpp:15:23:15:23 | +| ir.cpp:1:6:1:14 | Constants() -> void | 51 | 1 | 10 | declaration | | | | ir.cpp:17:5:17:15 | +| ir.cpp:1:6:1:14 | Constants() -> void | 52 | 51 | 0 | definition of l | | | long | ir.cpp:17:10:17:10 | +| ir.cpp:1:6:1:14 | Constants() -> void | 53 | 52 | 0 | initializer for l | | | | ir.cpp:17:13:17:14 | +| ir.cpp:1:6:1:14 | Constants() -> void | 54 | 53 | 0 | (long)... | integral conversion | =5 | prvalue: long | ir.cpp:17:14:17:14 | +| ir.cpp:1:6:1:14 | Constants() -> void | 55 | 54 | 0 | 5 | | =5 | prvalue: int | ir.cpp:17:14:17:14 | +| ir.cpp:1:6:1:14 | Constants() -> void | 56 | 1 | 11 | declaration | | | | ir.cpp:18:5:18:25 | +| ir.cpp:1:6:1:14 | Constants() -> void | 57 | 56 | 0 | definition of ul | | | unsigned long | ir.cpp:18:19:18:20 | +| ir.cpp:1:6:1:14 | Constants() -> void | 58 | 57 | 0 | initializer for ul | | | | ir.cpp:18:23:18:24 | +| ir.cpp:1:6:1:14 | Constants() -> void | 59 | 58 | 0 | (unsigned long)... | integral conversion | =5 | prvalue: unsigned long | ir.cpp:18:24:18:24 | +| ir.cpp:1:6:1:14 | Constants() -> void | 60 | 59 | 0 | 5 | | =5 | prvalue: int | ir.cpp:18:24:18:24 | +| ir.cpp:1:6:1:14 | Constants() -> void | 61 | 1 | 12 | declaration | | | | ir.cpp:20:5:20:23 | +| ir.cpp:1:6:1:14 | Constants() -> void | 62 | 61 | 0 | definition of ll_i | | | long long | ir.cpp:20:15:20:18 | +| ir.cpp:1:6:1:14 | Constants() -> void | 63 | 62 | 0 | initializer for ll_i | | | | ir.cpp:20:21:20:22 | +| ir.cpp:1:6:1:14 | Constants() -> void | 64 | 63 | 0 | (long long)... | integral conversion | =5 | prvalue: long long | ir.cpp:20:22:20:22 | +| ir.cpp:1:6:1:14 | Constants() -> void | 65 | 64 | 0 | 5 | | =5 | prvalue: int | ir.cpp:20:22:20:22 | +| ir.cpp:1:6:1:14 | Constants() -> void | 66 | 1 | 13 | declaration | | | | ir.cpp:21:5:21:26 | +| ir.cpp:1:6:1:14 | Constants() -> void | 67 | 66 | 0 | definition of ll_ll | | | long long | ir.cpp:21:15:21:19 | +| ir.cpp:1:6:1:14 | Constants() -> void | 68 | 67 | 0 | initializer for ll_ll | | | | ir.cpp:21:22:21:25 | +| ir.cpp:1:6:1:14 | Constants() -> void | 69 | 68 | 0 | 5 | | =5 | prvalue: long long | ir.cpp:21:22:21:25 | +| ir.cpp:1:6:1:14 | Constants() -> void | 70 | 1 | 14 | declaration | | | | ir.cpp:22:5:22:33 | +| ir.cpp:1:6:1:14 | Constants() -> void | 71 | 70 | 0 | definition of ull_i | | | unsigned long long | ir.cpp:22:24:22:28 | +| ir.cpp:1:6:1:14 | Constants() -> void | 72 | 71 | 0 | initializer for ull_i | | | | ir.cpp:22:31:22:32 | +| ir.cpp:1:6:1:14 | Constants() -> void | 73 | 72 | 0 | (unsigned long long)... | integral conversion | =5 | prvalue: unsigned long long | ir.cpp:22:32:22:32 | +| ir.cpp:1:6:1:14 | Constants() -> void | 74 | 73 | 0 | 5 | | =5 | prvalue: int | ir.cpp:22:32:22:32 | +| ir.cpp:1:6:1:14 | Constants() -> void | 75 | 1 | 15 | declaration | | | | ir.cpp:23:5:23:38 | +| ir.cpp:1:6:1:14 | Constants() -> void | 76 | 75 | 0 | definition of ull_ull | | | unsigned long long | ir.cpp:23:24:23:30 | +| ir.cpp:1:6:1:14 | Constants() -> void | 77 | 76 | 0 | initializer for ull_ull | | | | ir.cpp:23:33:23:37 | +| ir.cpp:1:6:1:14 | Constants() -> void | 78 | 77 | 0 | 5 | | =5 | prvalue: unsigned long long | ir.cpp:23:33:23:37 | +| ir.cpp:1:6:1:14 | Constants() -> void | 79 | 1 | 16 | declaration | | | | ir.cpp:25:5:25:20 | +| ir.cpp:1:6:1:14 | Constants() -> void | 80 | 79 | 0 | definition of b_t | | | bool | ir.cpp:25:10:25:12 | +| ir.cpp:1:6:1:14 | Constants() -> void | 81 | 80 | 0 | initializer for b_t | | | | ir.cpp:25:15:25:19 | +| ir.cpp:1:6:1:14 | Constants() -> void | 82 | 81 | 0 | 1 | | =1 | prvalue: bool | ir.cpp:25:15:25:19 | +| ir.cpp:1:6:1:14 | Constants() -> void | 83 | 1 | 17 | declaration | | | | ir.cpp:26:5:26:21 | +| ir.cpp:1:6:1:14 | Constants() -> void | 84 | 83 | 0 | definition of b_f | | | bool | ir.cpp:26:10:26:12 | +| ir.cpp:1:6:1:14 | Constants() -> void | 85 | 84 | 0 | initializer for b_f | | | | ir.cpp:26:15:26:20 | +| ir.cpp:1:6:1:14 | Constants() -> void | 86 | 85 | 0 | 0 | | =0 | prvalue: bool | ir.cpp:26:15:26:20 | +| ir.cpp:1:6:1:14 | Constants() -> void | 87 | 1 | 18 | declaration | | | | ir.cpp:28:5:28:21 | +| ir.cpp:1:6:1:14 | Constants() -> void | 88 | 87 | 0 | definition of wc_i | | | wchar_t | ir.cpp:28:13:28:16 | +| ir.cpp:1:6:1:14 | Constants() -> void | 89 | 88 | 0 | initializer for wc_i | | | | ir.cpp:28:19:28:20 | +| ir.cpp:1:6:1:14 | Constants() -> void | 90 | 89 | 0 | (wchar_t)... | integral conversion | =5 | prvalue: wchar_t | ir.cpp:28:20:28:20 | +| ir.cpp:1:6:1:14 | Constants() -> void | 91 | 90 | 0 | 5 | | =5 | prvalue: int | ir.cpp:28:20:28:20 | +| ir.cpp:1:6:1:14 | Constants() -> void | 92 | 1 | 19 | declaration | | | | ir.cpp:29:5:29:24 | +| ir.cpp:1:6:1:14 | Constants() -> void | 93 | 92 | 0 | definition of wc_c | | | wchar_t | ir.cpp:29:13:29:16 | +| ir.cpp:1:6:1:14 | Constants() -> void | 94 | 93 | 0 | initializer for wc_c | | | | ir.cpp:29:19:29:23 | +| ir.cpp:1:6:1:14 | Constants() -> void | 95 | 94 | 0 | 65 | | =65 | prvalue: wchar_t | ir.cpp:29:19:29:23 | +| ir.cpp:1:6:1:14 | Constants() -> void | 96 | 1 | 20 | declaration | | | | ir.cpp:31:5:31:24 | +| ir.cpp:1:6:1:14 | Constants() -> void | 97 | 96 | 0 | definition of c16 | | | char16_t | ir.cpp:31:14:31:16 | +| ir.cpp:1:6:1:14 | Constants() -> void | 98 | 97 | 0 | initializer for c16 | | | | ir.cpp:31:19:31:23 | +| ir.cpp:1:6:1:14 | Constants() -> void | 99 | 98 | 0 | 65 | | =65 | prvalue: char16_t | ir.cpp:31:19:31:23 | +| ir.cpp:1:6:1:14 | Constants() -> void | 100 | 1 | 21 | declaration | | | | ir.cpp:32:5:32:24 | +| ir.cpp:1:6:1:14 | Constants() -> void | 101 | 100 | 0 | definition of c32 | | | char32_t | ir.cpp:32:14:32:16 | +| ir.cpp:1:6:1:14 | Constants() -> void | 102 | 101 | 0 | initializer for c32 | | | | ir.cpp:32:19:32:23 | +| ir.cpp:1:6:1:14 | Constants() -> void | 103 | 102 | 0 | 65 | | =65 | prvalue: char32_t | ir.cpp:32:19:32:23 | +| ir.cpp:1:6:1:14 | Constants() -> void | 104 | 1 | 22 | declaration | | | | ir.cpp:34:5:34:18 | +| ir.cpp:1:6:1:14 | Constants() -> void | 105 | 104 | 0 | definition of f_i | | | float | ir.cpp:34:11:34:13 | +| ir.cpp:1:6:1:14 | Constants() -> void | 106 | 105 | 0 | initializer for f_i | | | | ir.cpp:34:16:34:17 | +| ir.cpp:1:6:1:14 | Constants() -> void | 107 | 106 | 0 | (float)... | integral to floating point conversion | =1.0 | prvalue: float | ir.cpp:34:17:34:17 | +| ir.cpp:1:6:1:14 | Constants() -> void | 108 | 107 | 0 | 1 | | =1 | prvalue: int | ir.cpp:34:17:34:17 | +| ir.cpp:1:6:1:14 | Constants() -> void | 109 | 1 | 23 | declaration | | | | ir.cpp:35:5:35:21 | +| ir.cpp:1:6:1:14 | Constants() -> void | 110 | 109 | 0 | definition of f_f | | | float | ir.cpp:35:11:35:13 | +| ir.cpp:1:6:1:14 | Constants() -> void | 111 | 110 | 0 | initializer for f_f | | | | ir.cpp:35:16:35:20 | +| ir.cpp:1:6:1:14 | Constants() -> void | 112 | 111 | 0 | 1.0 | | =1.0 | prvalue: float | ir.cpp:35:16:35:20 | +| ir.cpp:1:6:1:14 | Constants() -> void | 113 | 1 | 24 | declaration | | | | ir.cpp:36:5:36:20 | +| ir.cpp:1:6:1:14 | Constants() -> void | 114 | 113 | 0 | definition of f_d | | | float | ir.cpp:36:11:36:13 | +| ir.cpp:1:6:1:14 | Constants() -> void | 115 | 114 | 0 | initializer for f_d | | | | ir.cpp:36:16:36:19 | +| ir.cpp:1:6:1:14 | Constants() -> void | 116 | 115 | 0 | (float)... | floating point conversion | =1.0 | prvalue: float | ir.cpp:36:17:36:19 | +| ir.cpp:1:6:1:14 | Constants() -> void | 117 | 116 | 0 | 1.0 | | =1.0 | prvalue: double | ir.cpp:36:17:36:19 | +| ir.cpp:1:6:1:14 | Constants() -> void | 118 | 1 | 25 | declaration | | | | ir.cpp:38:5:38:19 | +| ir.cpp:1:6:1:14 | Constants() -> void | 119 | 118 | 0 | definition of d_i | | | double | ir.cpp:38:12:38:14 | +| ir.cpp:1:6:1:14 | Constants() -> void | 120 | 119 | 0 | initializer for d_i | | | | ir.cpp:38:17:38:18 | +| ir.cpp:1:6:1:14 | Constants() -> void | 121 | 120 | 0 | (double)... | integral to floating point conversion | =1.0 | prvalue: double | ir.cpp:38:18:38:18 | +| ir.cpp:1:6:1:14 | Constants() -> void | 122 | 121 | 0 | 1 | | =1 | prvalue: int | ir.cpp:38:18:38:18 | +| ir.cpp:1:6:1:14 | Constants() -> void | 123 | 1 | 26 | declaration | | | | ir.cpp:39:5:39:22 | +| ir.cpp:1:6:1:14 | Constants() -> void | 124 | 123 | 0 | definition of d_f | | | double | ir.cpp:39:12:39:14 | +| ir.cpp:1:6:1:14 | Constants() -> void | 125 | 124 | 0 | initializer for d_f | | | | ir.cpp:39:17:39:21 | +| ir.cpp:1:6:1:14 | Constants() -> void | 126 | 125 | 0 | (double)... | floating point conversion | =1.0 | prvalue: double | ir.cpp:39:18:39:21 | +| ir.cpp:1:6:1:14 | Constants() -> void | 127 | 126 | 0 | 1.0 | | =1.0 | prvalue: float | ir.cpp:39:18:39:21 | +| ir.cpp:1:6:1:14 | Constants() -> void | 128 | 1 | 27 | declaration | | | | ir.cpp:40:5:40:21 | +| ir.cpp:1:6:1:14 | Constants() -> void | 129 | 128 | 0 | definition of d_d | | | double | ir.cpp:40:12:40:14 | +| ir.cpp:1:6:1:14 | Constants() -> void | 130 | 129 | 0 | initializer for d_d | | | | ir.cpp:40:17:40:20 | +| ir.cpp:1:6:1:14 | Constants() -> void | 131 | 130 | 0 | 1.0 | | =1.0 | prvalue: double | ir.cpp:40:17:40:20 | +| ir.cpp:1:6:1:14 | Constants() -> void | 132 | 1 | 28 | return ... | | | | ir.cpp:41:1:41:1 | +| ir.cpp:43:6:43:8 | Foo() -> void | 0 | -1 | 0 | Foo | | | | ir.cpp:43:6:43:8 | +| ir.cpp:43:6:43:8 | Foo() -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:43:12:48:1 | +| ir.cpp:43:6:43:8 | Foo() -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:44:5:44:19 | +| ir.cpp:43:6:43:8 | Foo() -> void | 3 | 2 | 0 | definition of x | | | int | ir.cpp:44:9:44:9 | +| ir.cpp:43:6:43:8 | Foo() -> void | 4 | 3 | 0 | initializer for x | | | | ir.cpp:44:12:44:18 | +| ir.cpp:43:6:43:8 | Foo() -> void | 5 | 4 | 0 | ... + ... | | =17 | prvalue: int | ir.cpp:44:13:44:18 | +| ir.cpp:43:6:43:8 | Foo() -> void | 6 | 5 | 0 | 5 | | =5 | prvalue: int | ir.cpp:44:13:44:13 | +| ir.cpp:43:6:43:8 | Foo() -> void | 7 | 5 | 1 | 12 | | =12 | prvalue: int | ir.cpp:44:17:44:18 | +| ir.cpp:43:6:43:8 | Foo() -> void | 8 | 1 | 1 | declaration | | | | ir.cpp:45:5:45:16 | +| ir.cpp:43:6:43:8 | Foo() -> void | 9 | 8 | 0 | definition of y | | | short | ir.cpp:45:11:45:11 | +| ir.cpp:43:6:43:8 | Foo() -> void | 10 | 9 | 0 | initializer for y | | | | ir.cpp:45:14:45:15 | +| ir.cpp:43:6:43:8 | Foo() -> void | 11 | 10 | 0 | (short)... | integral conversion | =7 | prvalue: short | ir.cpp:45:15:45:15 | +| ir.cpp:43:6:43:8 | Foo() -> void | 12 | 11 | 0 | 7 | | =7 | prvalue: int | ir.cpp:45:15:45:15 | +| ir.cpp:43:6:43:8 | Foo() -> void | 13 | 1 | 2 | ExprStmt | | | | ir.cpp:46:5:46:14 | +| ir.cpp:43:6:43:8 | Foo() -> void | 14 | 13 | 0 | ... = ... | | | lvalue: short | ir.cpp:46:5:46:13 | +| ir.cpp:43:6:43:8 | Foo() -> void | 15 | 14 | 0 | y | | | lvalue: short | ir.cpp:46:5:46:5 | +| ir.cpp:43:6:43:8 | Foo() -> void | 16 | 14 | 1 | (short)... | integral conversion | | prvalue: short | ir.cpp:46:9:46:13 | +| ir.cpp:43:6:43:8 | Foo() -> void | 17 | 16 | 0 | ... + ... | | | prvalue: int | ir.cpp:46:9:46:13 | +| ir.cpp:43:6:43:8 | Foo() -> void | 18 | 17 | 0 | x | | | prvalue(load): int | ir.cpp:46:9:46:9 | +| ir.cpp:43:6:43:8 | Foo() -> void | 19 | 17 | 1 | (int)... | integral conversion | | prvalue: int | ir.cpp:46:13:46:13 | +| ir.cpp:43:6:43:8 | Foo() -> void | 20 | 19 | 0 | y | | | prvalue(load): short | ir.cpp:46:13:46:13 | +| ir.cpp:43:6:43:8 | Foo() -> void | 21 | 1 | 3 | ExprStmt | | | | ir.cpp:47:5:47:14 | +| ir.cpp:43:6:43:8 | Foo() -> void | 22 | 21 | 0 | ... = ... | | | lvalue: int | ir.cpp:47:5:47:13 | +| ir.cpp:43:6:43:8 | Foo() -> void | 23 | 22 | 0 | x | | | lvalue: int | ir.cpp:47:5:47:5 | +| ir.cpp:43:6:43:8 | Foo() -> void | 24 | 22 | 1 | ... * ... | | | prvalue: int | ir.cpp:47:9:47:13 | +| ir.cpp:43:6:43:8 | Foo() -> void | 25 | 24 | 0 | x | | | prvalue(load): int | ir.cpp:47:9:47:9 | +| ir.cpp:43:6:43:8 | Foo() -> void | 26 | 24 | 1 | (int)... | integral conversion | | prvalue: int | ir.cpp:47:13:47:13 | +| ir.cpp:43:6:43:8 | Foo() -> void | 27 | 26 | 0 | y | | | prvalue(load): short | ir.cpp:47:13:47:13 | +| ir.cpp:43:6:43:8 | Foo() -> void | 28 | 1 | 4 | return ... | | | | ir.cpp:48:1:48:1 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 0 | -1 | 0 | IntegerOps | | | | ir.cpp:50:6:50:15 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:50:31:85:1 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:51:5:51:10 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 3 | 2 | 0 | definition of z | | | int | ir.cpp:51:9:51:9 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 4 | 1 | 1 | ExprStmt | | | | ir.cpp:53:5:53:14 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 5 | 4 | 0 | ... = ... | | | lvalue: int | ir.cpp:53:5:53:13 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 6 | 5 | 0 | z | | | lvalue: int | ir.cpp:53:5:53:5 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 7 | 5 | 1 | ... + ... | | | prvalue: int | ir.cpp:53:9:53:13 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 8 | 7 | 0 | x | | | prvalue(load): int | ir.cpp:53:9:53:9 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 9 | 7 | 1 | y | | | prvalue(load): int | ir.cpp:53:13:53:13 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 10 | 1 | 2 | ExprStmt | | | | ir.cpp:54:5:54:14 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 11 | 10 | 0 | ... = ... | | | lvalue: int | ir.cpp:54:5:54:13 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 12 | 11 | 0 | z | | | lvalue: int | ir.cpp:54:5:54:5 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 13 | 11 | 1 | ... - ... | | | prvalue: int | ir.cpp:54:9:54:13 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 14 | 13 | 0 | x | | | prvalue(load): int | ir.cpp:54:9:54:9 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 15 | 13 | 1 | y | | | prvalue(load): int | ir.cpp:54:13:54:13 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 16 | 1 | 3 | ExprStmt | | | | ir.cpp:55:5:55:14 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 17 | 16 | 0 | ... = ... | | | lvalue: int | ir.cpp:55:5:55:13 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 18 | 17 | 0 | z | | | lvalue: int | ir.cpp:55:5:55:5 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 19 | 17 | 1 | ... * ... | | | prvalue: int | ir.cpp:55:9:55:13 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 20 | 19 | 0 | x | | | prvalue(load): int | ir.cpp:55:9:55:9 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 21 | 19 | 1 | y | | | prvalue(load): int | ir.cpp:55:13:55:13 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 22 | 1 | 4 | ExprStmt | | | | ir.cpp:56:5:56:14 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 23 | 22 | 0 | ... = ... | | | lvalue: int | ir.cpp:56:5:56:13 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 24 | 23 | 0 | z | | | lvalue: int | ir.cpp:56:5:56:5 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 25 | 23 | 1 | ... / ... | | | prvalue: int | ir.cpp:56:9:56:13 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 26 | 25 | 0 | x | | | prvalue(load): int | ir.cpp:56:9:56:9 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 27 | 25 | 1 | y | | | prvalue(load): int | ir.cpp:56:13:56:13 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 28 | 1 | 5 | ExprStmt | | | | ir.cpp:57:5:57:14 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 29 | 28 | 0 | ... = ... | | | lvalue: int | ir.cpp:57:5:57:13 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 30 | 29 | 0 | z | | | lvalue: int | ir.cpp:57:5:57:5 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 31 | 29 | 1 | ... % ... | | | prvalue: int | ir.cpp:57:9:57:13 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 32 | 31 | 0 | x | | | prvalue(load): int | ir.cpp:57:9:57:9 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 33 | 31 | 1 | y | | | prvalue(load): int | ir.cpp:57:13:57:13 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 34 | 1 | 6 | ExprStmt | | | | ir.cpp:59:5:59:14 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 35 | 34 | 0 | ... = ... | | | lvalue: int | ir.cpp:59:5:59:13 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 36 | 35 | 0 | z | | | lvalue: int | ir.cpp:59:5:59:5 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 37 | 35 | 1 | ... & ... | | | prvalue: int | ir.cpp:59:9:59:13 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 38 | 37 | 0 | x | | | prvalue(load): int | ir.cpp:59:9:59:9 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 39 | 37 | 1 | y | | | prvalue(load): int | ir.cpp:59:13:59:13 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 40 | 1 | 7 | ExprStmt | | | | ir.cpp:60:5:60:14 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 41 | 40 | 0 | ... = ... | | | lvalue: int | ir.cpp:60:5:60:13 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 42 | 41 | 0 | z | | | lvalue: int | ir.cpp:60:5:60:5 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 43 | 41 | 1 | ... \| ... | | | prvalue: int | ir.cpp:60:9:60:13 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 44 | 43 | 0 | x | | | prvalue(load): int | ir.cpp:60:9:60:9 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 45 | 43 | 1 | y | | | prvalue(load): int | ir.cpp:60:13:60:13 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 46 | 1 | 8 | ExprStmt | | | | ir.cpp:61:5:61:14 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 47 | 46 | 0 | ... = ... | | | lvalue: int | ir.cpp:61:5:61:13 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 48 | 47 | 0 | z | | | lvalue: int | ir.cpp:61:5:61:5 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 49 | 47 | 1 | ... ^ ... | | | prvalue: int | ir.cpp:61:9:61:13 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 50 | 49 | 0 | x | | | prvalue(load): int | ir.cpp:61:9:61:9 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 51 | 49 | 1 | y | | | prvalue(load): int | ir.cpp:61:13:61:13 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 52 | 1 | 9 | ExprStmt | | | | ir.cpp:63:5:63:15 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 53 | 52 | 0 | ... = ... | | | lvalue: int | ir.cpp:63:5:63:14 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 54 | 53 | 0 | z | | | lvalue: int | ir.cpp:63:5:63:5 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 55 | 53 | 1 | ... << ... | | | prvalue: int | ir.cpp:63:9:63:14 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 56 | 55 | 0 | x | | | prvalue(load): int | ir.cpp:63:9:63:9 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 57 | 55 | 1 | y | | | prvalue(load): int | ir.cpp:63:14:63:14 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 58 | 1 | 10 | ExprStmt | | | | ir.cpp:64:5:64:15 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 59 | 58 | 0 | ... = ... | | | lvalue: int | ir.cpp:64:5:64:14 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 60 | 59 | 0 | z | | | lvalue: int | ir.cpp:64:5:64:5 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 61 | 59 | 1 | ... >> ... | | | prvalue: int | ir.cpp:64:9:64:14 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 62 | 61 | 0 | x | | | prvalue(load): int | ir.cpp:64:9:64:9 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 63 | 61 | 1 | y | | | prvalue(load): int | ir.cpp:64:14:64:14 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 64 | 1 | 11 | ExprStmt | | | | ir.cpp:66:5:66:10 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 65 | 64 | 0 | ... = ... | | | lvalue: int | ir.cpp:66:5:66:9 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 66 | 65 | 0 | z | | | lvalue: int | ir.cpp:66:5:66:5 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 67 | 65 | 1 | x | | | prvalue(load): int | ir.cpp:66:9:66:9 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 68 | 1 | 12 | ExprStmt | | | | ir.cpp:68:5:68:11 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 69 | 68 | 0 | ... += ... | | | lvalue: int | ir.cpp:68:5:68:10 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 70 | 69 | 0 | z | | | lvalue: int | ir.cpp:68:5:68:5 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 71 | 69 | 1 | x | | | prvalue(load): int | ir.cpp:68:10:68:10 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 72 | 1 | 13 | ExprStmt | | | | ir.cpp:69:5:69:11 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 73 | 72 | 0 | ... -= ... | | | lvalue: int | ir.cpp:69:5:69:10 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 74 | 73 | 0 | z | | | lvalue: int | ir.cpp:69:5:69:5 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 75 | 73 | 1 | x | | | prvalue(load): int | ir.cpp:69:10:69:10 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 76 | 1 | 14 | ExprStmt | | | | ir.cpp:70:5:70:11 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 77 | 76 | 0 | ... *= ... | | | lvalue: int | ir.cpp:70:5:70:10 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 78 | 77 | 0 | z | | | lvalue: int | ir.cpp:70:5:70:5 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 79 | 77 | 1 | x | | | prvalue(load): int | ir.cpp:70:10:70:10 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 80 | 1 | 15 | ExprStmt | | | | ir.cpp:71:5:71:11 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 81 | 80 | 0 | ... /= ... | | | lvalue: int | ir.cpp:71:5:71:10 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 82 | 81 | 0 | z | | | lvalue: int | ir.cpp:71:5:71:5 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 83 | 81 | 1 | x | | | prvalue(load): int | ir.cpp:71:10:71:10 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 84 | 1 | 16 | ExprStmt | | | | ir.cpp:72:5:72:11 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 85 | 84 | 0 | ... %= ... | | | lvalue: int | ir.cpp:72:5:72:10 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 86 | 85 | 0 | z | | | lvalue: int | ir.cpp:72:5:72:5 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 87 | 85 | 1 | x | | | prvalue(load): int | ir.cpp:72:10:72:10 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 88 | 1 | 17 | ExprStmt | | | | ir.cpp:74:5:74:11 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 89 | 88 | 0 | ... &= ... | | | lvalue: int | ir.cpp:74:5:74:10 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 90 | 89 | 0 | z | | | lvalue: int | ir.cpp:74:5:74:5 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 91 | 89 | 1 | x | | | prvalue(load): int | ir.cpp:74:10:74:10 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 92 | 1 | 18 | ExprStmt | | | | ir.cpp:75:5:75:11 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 93 | 92 | 0 | ... \|= ... | | | lvalue: int | ir.cpp:75:5:75:10 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 94 | 93 | 0 | z | | | lvalue: int | ir.cpp:75:5:75:5 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 95 | 93 | 1 | x | | | prvalue(load): int | ir.cpp:75:10:75:10 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 96 | 1 | 19 | ExprStmt | | | | ir.cpp:76:5:76:11 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 97 | 96 | 0 | ... ^= ... | | | lvalue: int | ir.cpp:76:5:76:10 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 98 | 97 | 0 | z | | | lvalue: int | ir.cpp:76:5:76:5 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 99 | 97 | 1 | x | | | prvalue(load): int | ir.cpp:76:10:76:10 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 100 | 1 | 20 | ExprStmt | | | | ir.cpp:78:5:78:12 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 101 | 100 | 0 | ... <<= ... | | | lvalue: int | ir.cpp:78:5:78:11 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 102 | 101 | 0 | z | | | lvalue: int | ir.cpp:78:5:78:5 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 103 | 101 | 1 | x | | | prvalue(load): int | ir.cpp:78:11:78:11 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 104 | 1 | 21 | ExprStmt | | | | ir.cpp:79:5:79:12 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 105 | 104 | 0 | ... >>= ... | | | lvalue: int | ir.cpp:79:5:79:11 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 106 | 105 | 0 | z | | | lvalue: int | ir.cpp:79:5:79:5 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 107 | 105 | 1 | x | | | prvalue(load): int | ir.cpp:79:11:79:11 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 108 | 1 | 22 | ExprStmt | | | | ir.cpp:81:5:81:11 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 109 | 108 | 0 | ... = ... | | | lvalue: int | ir.cpp:81:5:81:10 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 110 | 109 | 0 | z | | | lvalue: int | ir.cpp:81:5:81:5 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 111 | 109 | 1 | + ... | | | prvalue: int | ir.cpp:81:9:81:10 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 112 | 111 | 0 | x | | | prvalue(load): int | ir.cpp:81:10:81:10 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 113 | 1 | 23 | ExprStmt | | | | ir.cpp:82:5:82:11 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 114 | 113 | 0 | ... = ... | | | lvalue: int | ir.cpp:82:5:82:10 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 115 | 114 | 0 | z | | | lvalue: int | ir.cpp:82:5:82:5 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 116 | 114 | 1 | - ... | | | prvalue: int | ir.cpp:82:9:82:10 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 117 | 116 | 0 | x | | | prvalue(load): int | ir.cpp:82:10:82:10 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 118 | 1 | 24 | ExprStmt | | | | ir.cpp:83:5:83:11 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 119 | 118 | 0 | ... = ... | | | lvalue: int | ir.cpp:83:5:83:10 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 120 | 119 | 0 | z | | | lvalue: int | ir.cpp:83:5:83:5 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 121 | 119 | 1 | ~ ... | | | prvalue: int | ir.cpp:83:9:83:10 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 122 | 121 | 0 | x | | | prvalue(load): int | ir.cpp:83:10:83:10 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 123 | 1 | 25 | ExprStmt | | | | ir.cpp:84:5:84:11 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 124 | 123 | 0 | ... = ... | | | lvalue: int | ir.cpp:84:5:84:10 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 125 | 124 | 0 | z | | | lvalue: int | ir.cpp:84:5:84:5 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 126 | 124 | 1 | (int)... | integral conversion | | prvalue: int | ir.cpp:84:9:84:10 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 127 | 126 | 0 | ! ... | | | prvalue: bool | ir.cpp:84:9:84:10 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 128 | 127 | 0 | (bool)... | conversion to bool | | prvalue: bool | ir.cpp:84:10:84:10 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 129 | 128 | 0 | x | | | prvalue(load): int | ir.cpp:84:10:84:10 | +| ir.cpp:50:6:50:15 | IntegerOps(int, int) -> void | 130 | 1 | 26 | return ... | | | | ir.cpp:85:1:85:1 | +| ir.cpp:87:6:87:19 | IntegerCompare(int, int) -> void | 0 | -1 | 0 | IntegerCompare | | | | ir.cpp:87:6:87:19 | +| ir.cpp:87:6:87:19 | IntegerCompare(int, int) -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:87:35:96:1 | +| ir.cpp:87:6:87:19 | IntegerCompare(int, int) -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:88:5:88:11 | +| ir.cpp:87:6:87:19 | IntegerCompare(int, int) -> void | 3 | 2 | 0 | definition of b | | | bool | ir.cpp:88:10:88:10 | +| ir.cpp:87:6:87:19 | IntegerCompare(int, int) -> void | 4 | 1 | 1 | ExprStmt | | | | ir.cpp:90:5:90:15 | +| ir.cpp:87:6:87:19 | IntegerCompare(int, int) -> void | 5 | 4 | 0 | ... = ... | | | lvalue: bool | ir.cpp:90:5:90:14 | +| ir.cpp:87:6:87:19 | IntegerCompare(int, int) -> void | 6 | 5 | 0 | b | | | lvalue: bool | ir.cpp:90:5:90:5 | +| ir.cpp:87:6:87:19 | IntegerCompare(int, int) -> void | 7 | 5 | 1 | ... == ... | | | prvalue: bool | ir.cpp:90:9:90:14 | +| ir.cpp:87:6:87:19 | IntegerCompare(int, int) -> void | 8 | 7 | 0 | x | | | prvalue(load): int | ir.cpp:90:9:90:9 | +| ir.cpp:87:6:87:19 | IntegerCompare(int, int) -> void | 9 | 7 | 1 | y | | | prvalue(load): int | ir.cpp:90:14:90:14 | +| ir.cpp:87:6:87:19 | IntegerCompare(int, int) -> void | 10 | 1 | 2 | ExprStmt | | | | ir.cpp:91:5:91:15 | +| ir.cpp:87:6:87:19 | IntegerCompare(int, int) -> void | 11 | 10 | 0 | ... = ... | | | lvalue: bool | ir.cpp:91:5:91:14 | +| ir.cpp:87:6:87:19 | IntegerCompare(int, int) -> void | 12 | 11 | 0 | b | | | lvalue: bool | ir.cpp:91:5:91:5 | +| ir.cpp:87:6:87:19 | IntegerCompare(int, int) -> void | 13 | 11 | 1 | ... != ... | | | prvalue: bool | ir.cpp:91:9:91:14 | +| ir.cpp:87:6:87:19 | IntegerCompare(int, int) -> void | 14 | 13 | 0 | x | | | prvalue(load): int | ir.cpp:91:9:91:9 | +| ir.cpp:87:6:87:19 | IntegerCompare(int, int) -> void | 15 | 13 | 1 | y | | | prvalue(load): int | ir.cpp:91:14:91:14 | +| ir.cpp:87:6:87:19 | IntegerCompare(int, int) -> void | 16 | 1 | 3 | ExprStmt | | | | ir.cpp:92:5:92:14 | +| ir.cpp:87:6:87:19 | IntegerCompare(int, int) -> void | 17 | 16 | 0 | ... = ... | | | lvalue: bool | ir.cpp:92:5:92:13 | +| ir.cpp:87:6:87:19 | IntegerCompare(int, int) -> void | 18 | 17 | 0 | b | | | lvalue: bool | ir.cpp:92:5:92:5 | +| ir.cpp:87:6:87:19 | IntegerCompare(int, int) -> void | 19 | 17 | 1 | ... < ... | | | prvalue: bool | ir.cpp:92:9:92:13 | +| ir.cpp:87:6:87:19 | IntegerCompare(int, int) -> void | 20 | 19 | 0 | x | | | prvalue(load): int | ir.cpp:92:9:92:9 | +| ir.cpp:87:6:87:19 | IntegerCompare(int, int) -> void | 21 | 19 | 1 | y | | | prvalue(load): int | ir.cpp:92:13:92:13 | +| ir.cpp:87:6:87:19 | IntegerCompare(int, int) -> void | 22 | 1 | 4 | ExprStmt | | | | ir.cpp:93:5:93:14 | +| ir.cpp:87:6:87:19 | IntegerCompare(int, int) -> void | 23 | 22 | 0 | ... = ... | | | lvalue: bool | ir.cpp:93:5:93:13 | +| ir.cpp:87:6:87:19 | IntegerCompare(int, int) -> void | 24 | 23 | 0 | b | | | lvalue: bool | ir.cpp:93:5:93:5 | +| ir.cpp:87:6:87:19 | IntegerCompare(int, int) -> void | 25 | 23 | 1 | ... > ... | | | prvalue: bool | ir.cpp:93:9:93:13 | +| ir.cpp:87:6:87:19 | IntegerCompare(int, int) -> void | 26 | 25 | 0 | x | | | prvalue(load): int | ir.cpp:93:9:93:9 | +| ir.cpp:87:6:87:19 | IntegerCompare(int, int) -> void | 27 | 25 | 1 | y | | | prvalue(load): int | ir.cpp:93:13:93:13 | +| ir.cpp:87:6:87:19 | IntegerCompare(int, int) -> void | 28 | 1 | 5 | ExprStmt | | | | ir.cpp:94:5:94:15 | +| ir.cpp:87:6:87:19 | IntegerCompare(int, int) -> void | 29 | 28 | 0 | ... = ... | | | lvalue: bool | ir.cpp:94:5:94:14 | +| ir.cpp:87:6:87:19 | IntegerCompare(int, int) -> void | 30 | 29 | 0 | b | | | lvalue: bool | ir.cpp:94:5:94:5 | +| ir.cpp:87:6:87:19 | IntegerCompare(int, int) -> void | 31 | 29 | 1 | ... <= ... | | | prvalue: bool | ir.cpp:94:9:94:14 | +| ir.cpp:87:6:87:19 | IntegerCompare(int, int) -> void | 32 | 31 | 0 | x | | | prvalue(load): int | ir.cpp:94:9:94:9 | +| ir.cpp:87:6:87:19 | IntegerCompare(int, int) -> void | 33 | 31 | 1 | y | | | prvalue(load): int | ir.cpp:94:14:94:14 | +| ir.cpp:87:6:87:19 | IntegerCompare(int, int) -> void | 34 | 1 | 6 | ExprStmt | | | | ir.cpp:95:5:95:15 | +| ir.cpp:87:6:87:19 | IntegerCompare(int, int) -> void | 35 | 34 | 0 | ... = ... | | | lvalue: bool | ir.cpp:95:5:95:14 | +| ir.cpp:87:6:87:19 | IntegerCompare(int, int) -> void | 36 | 35 | 0 | b | | | lvalue: bool | ir.cpp:95:5:95:5 | +| ir.cpp:87:6:87:19 | IntegerCompare(int, int) -> void | 37 | 35 | 1 | ... >= ... | | | prvalue: bool | ir.cpp:95:9:95:14 | +| ir.cpp:87:6:87:19 | IntegerCompare(int, int) -> void | 38 | 37 | 0 | x | | | prvalue(load): int | ir.cpp:95:9:95:9 | +| ir.cpp:87:6:87:19 | IntegerCompare(int, int) -> void | 39 | 37 | 1 | y | | | prvalue(load): int | ir.cpp:95:14:95:14 | +| ir.cpp:87:6:87:19 | IntegerCompare(int, int) -> void | 40 | 1 | 7 | return ... | | | | ir.cpp:96:1:96:1 | +| ir.cpp:98:6:98:19 | IntegerCrement(int) -> void | 0 | -1 | 0 | IntegerCrement | | | | ir.cpp:98:6:98:19 | +| ir.cpp:98:6:98:19 | IntegerCrement(int) -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:98:28:105:1 | +| ir.cpp:98:6:98:19 | IntegerCrement(int) -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:99:5:99:10 | +| ir.cpp:98:6:98:19 | IntegerCrement(int) -> void | 3 | 2 | 0 | definition of y | | | int | ir.cpp:99:9:99:9 | +| ir.cpp:98:6:98:19 | IntegerCrement(int) -> void | 4 | 1 | 1 | ExprStmt | | | | ir.cpp:101:5:101:12 | +| ir.cpp:98:6:98:19 | IntegerCrement(int) -> void | 5 | 4 | 0 | ... = ... | | | lvalue: int | ir.cpp:101:5:101:11 | +| ir.cpp:98:6:98:19 | IntegerCrement(int) -> void | 6 | 5 | 0 | y | | | lvalue: int | ir.cpp:101:5:101:5 | +| ir.cpp:98:6:98:19 | IntegerCrement(int) -> void | 7 | 5 | 1 | ++ ... | | | prvalue: int | ir.cpp:101:9:101:11 | +| ir.cpp:98:6:98:19 | IntegerCrement(int) -> void | 8 | 7 | 0 | x | | | lvalue: int | ir.cpp:101:11:101:11 | +| ir.cpp:98:6:98:19 | IntegerCrement(int) -> void | 9 | 1 | 2 | ExprStmt | | | | ir.cpp:102:5:102:12 | +| ir.cpp:98:6:98:19 | IntegerCrement(int) -> void | 10 | 9 | 0 | ... = ... | | | lvalue: int | ir.cpp:102:5:102:11 | +| ir.cpp:98:6:98:19 | IntegerCrement(int) -> void | 11 | 10 | 0 | y | | | lvalue: int | ir.cpp:102:5:102:5 | +| ir.cpp:98:6:98:19 | IntegerCrement(int) -> void | 12 | 10 | 1 | -- ... | | | prvalue: int | ir.cpp:102:9:102:11 | +| ir.cpp:98:6:98:19 | IntegerCrement(int) -> void | 13 | 12 | 0 | x | | | lvalue: int | ir.cpp:102:11:102:11 | +| ir.cpp:98:6:98:19 | IntegerCrement(int) -> void | 14 | 1 | 3 | ExprStmt | | | | ir.cpp:103:5:103:12 | +| ir.cpp:98:6:98:19 | IntegerCrement(int) -> void | 15 | 14 | 0 | ... = ... | | | lvalue: int | ir.cpp:103:5:103:11 | +| ir.cpp:98:6:98:19 | IntegerCrement(int) -> void | 16 | 15 | 0 | y | | | lvalue: int | ir.cpp:103:5:103:5 | +| ir.cpp:98:6:98:19 | IntegerCrement(int) -> void | 17 | 15 | 1 | ... ++ | | | prvalue: int | ir.cpp:103:9:103:11 | +| ir.cpp:98:6:98:19 | IntegerCrement(int) -> void | 18 | 17 | 0 | x | | | lvalue: int | ir.cpp:103:9:103:9 | +| ir.cpp:98:6:98:19 | IntegerCrement(int) -> void | 19 | 1 | 4 | ExprStmt | | | | ir.cpp:104:5:104:12 | +| ir.cpp:98:6:98:19 | IntegerCrement(int) -> void | 20 | 19 | 0 | ... = ... | | | lvalue: int | ir.cpp:104:5:104:11 | +| ir.cpp:98:6:98:19 | IntegerCrement(int) -> void | 21 | 20 | 0 | y | | | lvalue: int | ir.cpp:104:5:104:5 | +| ir.cpp:98:6:98:19 | IntegerCrement(int) -> void | 22 | 20 | 1 | ... -- | | | prvalue: int | ir.cpp:104:9:104:11 | +| ir.cpp:98:6:98:19 | IntegerCrement(int) -> void | 23 | 22 | 0 | x | | | lvalue: int | ir.cpp:104:9:104:9 | +| ir.cpp:98:6:98:19 | IntegerCrement(int) -> void | 24 | 1 | 5 | return ... | | | | ir.cpp:105:1:105:1 | +| ir.cpp:107:6:107:26 | IntegerCrement_LValue(int) -> void | 0 | -1 | 0 | IntegerCrement_LValue | | | | ir.cpp:107:6:107:26 | +| ir.cpp:107:6:107:26 | IntegerCrement_LValue(int) -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:107:35:112:1 | +| ir.cpp:107:6:107:26 | IntegerCrement_LValue(int) -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:108:5:108:11 | +| ir.cpp:107:6:107:26 | IntegerCrement_LValue(int) -> void | 3 | 2 | 0 | definition of p | | | int * | ir.cpp:108:10:108:10 | +| ir.cpp:107:6:107:26 | IntegerCrement_LValue(int) -> void | 4 | 1 | 1 | ExprStmt | | | | ir.cpp:110:5:110:15 | +| ir.cpp:107:6:107:26 | IntegerCrement_LValue(int) -> void | 5 | 4 | 0 | ... = ... | | | lvalue: int * | ir.cpp:110:5:110:14 | +| ir.cpp:107:6:107:26 | IntegerCrement_LValue(int) -> void | 6 | 5 | 0 | p | | | lvalue: int * | ir.cpp:110:5:110:5 | +| ir.cpp:107:6:107:26 | IntegerCrement_LValue(int) -> void | 7 | 5 | 1 | & ... | | | prvalue: int * | ir.cpp:110:9:110:14 | +| ir.cpp:107:6:107:26 | IntegerCrement_LValue(int) -> void | 8 | 7 | 0 | (...) | | | lvalue: int | ir.cpp:110:10:110:14 | +| ir.cpp:107:6:107:26 | IntegerCrement_LValue(int) -> void | 9 | 8 | 0 | ++ ... | | | lvalue: int | ir.cpp:110:11:110:13 | +| ir.cpp:107:6:107:26 | IntegerCrement_LValue(int) -> void | 10 | 9 | 0 | x | | | lvalue: int | ir.cpp:110:13:110:13 | +| ir.cpp:107:6:107:26 | IntegerCrement_LValue(int) -> void | 11 | 1 | 2 | ExprStmt | | | | ir.cpp:111:5:111:15 | +| ir.cpp:107:6:107:26 | IntegerCrement_LValue(int) -> void | 12 | 11 | 0 | ... = ... | | | lvalue: int * | ir.cpp:111:5:111:14 | +| ir.cpp:107:6:107:26 | IntegerCrement_LValue(int) -> void | 13 | 12 | 0 | p | | | lvalue: int * | ir.cpp:111:5:111:5 | +| ir.cpp:107:6:107:26 | IntegerCrement_LValue(int) -> void | 14 | 12 | 1 | & ... | | | prvalue: int * | ir.cpp:111:9:111:14 | +| ir.cpp:107:6:107:26 | IntegerCrement_LValue(int) -> void | 15 | 14 | 0 | (...) | | | lvalue: int | ir.cpp:111:10:111:14 | +| ir.cpp:107:6:107:26 | IntegerCrement_LValue(int) -> void | 16 | 15 | 0 | -- ... | | | lvalue: int | ir.cpp:111:11:111:13 | +| ir.cpp:107:6:107:26 | IntegerCrement_LValue(int) -> void | 17 | 16 | 0 | x | | | lvalue: int | ir.cpp:111:13:111:13 | +| ir.cpp:107:6:107:26 | IntegerCrement_LValue(int) -> void | 18 | 1 | 3 | return ... | | | | ir.cpp:112:1:112:1 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 0 | -1 | 0 | FloatOps | | | | ir.cpp:114:6:114:13 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:114:35:131:1 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:115:5:115:13 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 3 | 2 | 0 | definition of z | | | double | ir.cpp:115:12:115:12 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 4 | 1 | 1 | ExprStmt | | | | ir.cpp:117:5:117:14 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 5 | 4 | 0 | ... = ... | | | lvalue: double | ir.cpp:117:5:117:13 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 6 | 5 | 0 | z | | | lvalue: double | ir.cpp:117:5:117:5 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 7 | 5 | 1 | ... + ... | | | prvalue: double | ir.cpp:117:9:117:13 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 8 | 7 | 0 | x | | | prvalue(load): double | ir.cpp:117:9:117:9 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 9 | 7 | 1 | y | | | prvalue(load): double | ir.cpp:117:13:117:13 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 10 | 1 | 2 | ExprStmt | | | | ir.cpp:118:5:118:14 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 11 | 10 | 0 | ... = ... | | | lvalue: double | ir.cpp:118:5:118:13 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 12 | 11 | 0 | z | | | lvalue: double | ir.cpp:118:5:118:5 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 13 | 11 | 1 | ... - ... | | | prvalue: double | ir.cpp:118:9:118:13 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 14 | 13 | 0 | x | | | prvalue(load): double | ir.cpp:118:9:118:9 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 15 | 13 | 1 | y | | | prvalue(load): double | ir.cpp:118:13:118:13 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 16 | 1 | 3 | ExprStmt | | | | ir.cpp:119:5:119:14 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 17 | 16 | 0 | ... = ... | | | lvalue: double | ir.cpp:119:5:119:13 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 18 | 17 | 0 | z | | | lvalue: double | ir.cpp:119:5:119:5 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 19 | 17 | 1 | ... * ... | | | prvalue: double | ir.cpp:119:9:119:13 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 20 | 19 | 0 | x | | | prvalue(load): double | ir.cpp:119:9:119:9 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 21 | 19 | 1 | y | | | prvalue(load): double | ir.cpp:119:13:119:13 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 22 | 1 | 4 | ExprStmt | | | | ir.cpp:120:5:120:14 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 23 | 22 | 0 | ... = ... | | | lvalue: double | ir.cpp:120:5:120:13 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 24 | 23 | 0 | z | | | lvalue: double | ir.cpp:120:5:120:5 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 25 | 23 | 1 | ... / ... | | | prvalue: double | ir.cpp:120:9:120:13 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 26 | 25 | 0 | x | | | prvalue(load): double | ir.cpp:120:9:120:9 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 27 | 25 | 1 | y | | | prvalue(load): double | ir.cpp:120:13:120:13 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 28 | 1 | 5 | ExprStmt | | | | ir.cpp:122:5:122:10 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 29 | 28 | 0 | ... = ... | | | lvalue: double | ir.cpp:122:5:122:9 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 30 | 29 | 0 | z | | | lvalue: double | ir.cpp:122:5:122:5 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 31 | 29 | 1 | x | | | prvalue(load): double | ir.cpp:122:9:122:9 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 32 | 1 | 6 | ExprStmt | | | | ir.cpp:124:5:124:11 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 33 | 32 | 0 | ... += ... | | | lvalue: double | ir.cpp:124:5:124:10 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 34 | 33 | 0 | z | | | lvalue: double | ir.cpp:124:5:124:5 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 35 | 33 | 1 | x | | | prvalue(load): double | ir.cpp:124:10:124:10 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 36 | 1 | 7 | ExprStmt | | | | ir.cpp:125:5:125:11 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 37 | 36 | 0 | ... -= ... | | | lvalue: double | ir.cpp:125:5:125:10 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 38 | 37 | 0 | z | | | lvalue: double | ir.cpp:125:5:125:5 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 39 | 37 | 1 | x | | | prvalue(load): double | ir.cpp:125:10:125:10 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 40 | 1 | 8 | ExprStmt | | | | ir.cpp:126:5:126:11 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 41 | 40 | 0 | ... *= ... | | | lvalue: double | ir.cpp:126:5:126:10 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 42 | 41 | 0 | z | | | lvalue: double | ir.cpp:126:5:126:5 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 43 | 41 | 1 | x | | | prvalue(load): double | ir.cpp:126:10:126:10 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 44 | 1 | 9 | ExprStmt | | | | ir.cpp:127:5:127:11 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 45 | 44 | 0 | ... /= ... | | | lvalue: double | ir.cpp:127:5:127:10 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 46 | 45 | 0 | z | | | lvalue: double | ir.cpp:127:5:127:5 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 47 | 45 | 1 | x | | | prvalue(load): double | ir.cpp:127:10:127:10 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 48 | 1 | 10 | ExprStmt | | | | ir.cpp:129:5:129:11 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 49 | 48 | 0 | ... = ... | | | lvalue: double | ir.cpp:129:5:129:10 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 50 | 49 | 0 | z | | | lvalue: double | ir.cpp:129:5:129:5 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 51 | 49 | 1 | + ... | | | prvalue: double | ir.cpp:129:9:129:10 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 52 | 51 | 0 | x | | | prvalue(load): double | ir.cpp:129:10:129:10 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 53 | 1 | 11 | ExprStmt | | | | ir.cpp:130:5:130:11 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 54 | 53 | 0 | ... = ... | | | lvalue: double | ir.cpp:130:5:130:10 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 55 | 54 | 0 | z | | | lvalue: double | ir.cpp:130:5:130:5 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 56 | 54 | 1 | - ... | | | prvalue: double | ir.cpp:130:9:130:10 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 57 | 56 | 0 | x | | | prvalue(load): double | ir.cpp:130:10:130:10 | +| ir.cpp:114:6:114:13 | FloatOps(double, double) -> void | 58 | 1 | 12 | return ... | | | | ir.cpp:131:1:131:1 | +| ir.cpp:133:6:133:17 | FloatCompare(double, double) -> void | 0 | -1 | 0 | FloatCompare | | | | ir.cpp:133:6:133:17 | +| ir.cpp:133:6:133:17 | FloatCompare(double, double) -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:133:39:142:1 | +| ir.cpp:133:6:133:17 | FloatCompare(double, double) -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:134:5:134:11 | +| ir.cpp:133:6:133:17 | FloatCompare(double, double) -> void | 3 | 2 | 0 | definition of b | | | bool | ir.cpp:134:10:134:10 | +| ir.cpp:133:6:133:17 | FloatCompare(double, double) -> void | 4 | 1 | 1 | ExprStmt | | | | ir.cpp:136:5:136:15 | +| ir.cpp:133:6:133:17 | FloatCompare(double, double) -> void | 5 | 4 | 0 | ... = ... | | | lvalue: bool | ir.cpp:136:5:136:14 | +| ir.cpp:133:6:133:17 | FloatCompare(double, double) -> void | 6 | 5 | 0 | b | | | lvalue: bool | ir.cpp:136:5:136:5 | +| ir.cpp:133:6:133:17 | FloatCompare(double, double) -> void | 7 | 5 | 1 | ... == ... | | | prvalue: bool | ir.cpp:136:9:136:14 | +| ir.cpp:133:6:133:17 | FloatCompare(double, double) -> void | 8 | 7 | 0 | x | | | prvalue(load): double | ir.cpp:136:9:136:9 | +| ir.cpp:133:6:133:17 | FloatCompare(double, double) -> void | 9 | 7 | 1 | y | | | prvalue(load): double | ir.cpp:136:14:136:14 | +| ir.cpp:133:6:133:17 | FloatCompare(double, double) -> void | 10 | 1 | 2 | ExprStmt | | | | ir.cpp:137:5:137:15 | +| ir.cpp:133:6:133:17 | FloatCompare(double, double) -> void | 11 | 10 | 0 | ... = ... | | | lvalue: bool | ir.cpp:137:5:137:14 | +| ir.cpp:133:6:133:17 | FloatCompare(double, double) -> void | 12 | 11 | 0 | b | | | lvalue: bool | ir.cpp:137:5:137:5 | +| ir.cpp:133:6:133:17 | FloatCompare(double, double) -> void | 13 | 11 | 1 | ... != ... | | | prvalue: bool | ir.cpp:137:9:137:14 | +| ir.cpp:133:6:133:17 | FloatCompare(double, double) -> void | 14 | 13 | 0 | x | | | prvalue(load): double | ir.cpp:137:9:137:9 | +| ir.cpp:133:6:133:17 | FloatCompare(double, double) -> void | 15 | 13 | 1 | y | | | prvalue(load): double | ir.cpp:137:14:137:14 | +| ir.cpp:133:6:133:17 | FloatCompare(double, double) -> void | 16 | 1 | 3 | ExprStmt | | | | ir.cpp:138:5:138:14 | +| ir.cpp:133:6:133:17 | FloatCompare(double, double) -> void | 17 | 16 | 0 | ... = ... | | | lvalue: bool | ir.cpp:138:5:138:13 | +| ir.cpp:133:6:133:17 | FloatCompare(double, double) -> void | 18 | 17 | 0 | b | | | lvalue: bool | ir.cpp:138:5:138:5 | +| ir.cpp:133:6:133:17 | FloatCompare(double, double) -> void | 19 | 17 | 1 | ... < ... | | | prvalue: bool | ir.cpp:138:9:138:13 | +| ir.cpp:133:6:133:17 | FloatCompare(double, double) -> void | 20 | 19 | 0 | x | | | prvalue(load): double | ir.cpp:138:9:138:9 | +| ir.cpp:133:6:133:17 | FloatCompare(double, double) -> void | 21 | 19 | 1 | y | | | prvalue(load): double | ir.cpp:138:13:138:13 | +| ir.cpp:133:6:133:17 | FloatCompare(double, double) -> void | 22 | 1 | 4 | ExprStmt | | | | ir.cpp:139:5:139:14 | +| ir.cpp:133:6:133:17 | FloatCompare(double, double) -> void | 23 | 22 | 0 | ... = ... | | | lvalue: bool | ir.cpp:139:5:139:13 | +| ir.cpp:133:6:133:17 | FloatCompare(double, double) -> void | 24 | 23 | 0 | b | | | lvalue: bool | ir.cpp:139:5:139:5 | +| ir.cpp:133:6:133:17 | FloatCompare(double, double) -> void | 25 | 23 | 1 | ... > ... | | | prvalue: bool | ir.cpp:139:9:139:13 | +| ir.cpp:133:6:133:17 | FloatCompare(double, double) -> void | 26 | 25 | 0 | x | | | prvalue(load): double | ir.cpp:139:9:139:9 | +| ir.cpp:133:6:133:17 | FloatCompare(double, double) -> void | 27 | 25 | 1 | y | | | prvalue(load): double | ir.cpp:139:13:139:13 | +| ir.cpp:133:6:133:17 | FloatCompare(double, double) -> void | 28 | 1 | 5 | ExprStmt | | | | ir.cpp:140:5:140:15 | +| ir.cpp:133:6:133:17 | FloatCompare(double, double) -> void | 29 | 28 | 0 | ... = ... | | | lvalue: bool | ir.cpp:140:5:140:14 | +| ir.cpp:133:6:133:17 | FloatCompare(double, double) -> void | 30 | 29 | 0 | b | | | lvalue: bool | ir.cpp:140:5:140:5 | +| ir.cpp:133:6:133:17 | FloatCompare(double, double) -> void | 31 | 29 | 1 | ... <= ... | | | prvalue: bool | ir.cpp:140:9:140:14 | +| ir.cpp:133:6:133:17 | FloatCompare(double, double) -> void | 32 | 31 | 0 | x | | | prvalue(load): double | ir.cpp:140:9:140:9 | +| ir.cpp:133:6:133:17 | FloatCompare(double, double) -> void | 33 | 31 | 1 | y | | | prvalue(load): double | ir.cpp:140:14:140:14 | +| ir.cpp:133:6:133:17 | FloatCompare(double, double) -> void | 34 | 1 | 6 | ExprStmt | | | | ir.cpp:141:5:141:15 | +| ir.cpp:133:6:133:17 | FloatCompare(double, double) -> void | 35 | 34 | 0 | ... = ... | | | lvalue: bool | ir.cpp:141:5:141:14 | +| ir.cpp:133:6:133:17 | FloatCompare(double, double) -> void | 36 | 35 | 0 | b | | | lvalue: bool | ir.cpp:141:5:141:5 | +| ir.cpp:133:6:133:17 | FloatCompare(double, double) -> void | 37 | 35 | 1 | ... >= ... | | | prvalue: bool | ir.cpp:141:9:141:14 | +| ir.cpp:133:6:133:17 | FloatCompare(double, double) -> void | 38 | 37 | 0 | x | | | prvalue(load): double | ir.cpp:141:9:141:9 | +| ir.cpp:133:6:133:17 | FloatCompare(double, double) -> void | 39 | 37 | 1 | y | | | prvalue(load): double | ir.cpp:141:14:141:14 | +| ir.cpp:133:6:133:17 | FloatCompare(double, double) -> void | 40 | 1 | 7 | return ... | | | | ir.cpp:142:1:142:1 | +| ir.cpp:144:6:144:17 | FloatCrement(float) -> void | 0 | -1 | 0 | FloatCrement | | | | ir.cpp:144:6:144:17 | +| ir.cpp:144:6:144:17 | FloatCrement(float) -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:144:28:151:1 | +| ir.cpp:144:6:144:17 | FloatCrement(float) -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:145:5:145:12 | +| ir.cpp:144:6:144:17 | FloatCrement(float) -> void | 3 | 2 | 0 | definition of y | | | float | ir.cpp:145:11:145:11 | +| ir.cpp:144:6:144:17 | FloatCrement(float) -> void | 4 | 1 | 1 | ExprStmt | | | | ir.cpp:147:5:147:12 | +| ir.cpp:144:6:144:17 | FloatCrement(float) -> void | 5 | 4 | 0 | ... = ... | | | lvalue: float | ir.cpp:147:5:147:11 | +| ir.cpp:144:6:144:17 | FloatCrement(float) -> void | 6 | 5 | 0 | y | | | lvalue: float | ir.cpp:147:5:147:5 | +| ir.cpp:144:6:144:17 | FloatCrement(float) -> void | 7 | 5 | 1 | ++ ... | | | prvalue: float | ir.cpp:147:9:147:11 | +| ir.cpp:144:6:144:17 | FloatCrement(float) -> void | 8 | 7 | 0 | x | | | lvalue: float | ir.cpp:147:11:147:11 | +| ir.cpp:144:6:144:17 | FloatCrement(float) -> void | 9 | 1 | 2 | ExprStmt | | | | ir.cpp:148:5:148:12 | +| ir.cpp:144:6:144:17 | FloatCrement(float) -> void | 10 | 9 | 0 | ... = ... | | | lvalue: float | ir.cpp:148:5:148:11 | +| ir.cpp:144:6:144:17 | FloatCrement(float) -> void | 11 | 10 | 0 | y | | | lvalue: float | ir.cpp:148:5:148:5 | +| ir.cpp:144:6:144:17 | FloatCrement(float) -> void | 12 | 10 | 1 | -- ... | | | prvalue: float | ir.cpp:148:9:148:11 | +| ir.cpp:144:6:144:17 | FloatCrement(float) -> void | 13 | 12 | 0 | x | | | lvalue: float | ir.cpp:148:11:148:11 | +| ir.cpp:144:6:144:17 | FloatCrement(float) -> void | 14 | 1 | 3 | ExprStmt | | | | ir.cpp:149:5:149:12 | +| ir.cpp:144:6:144:17 | FloatCrement(float) -> void | 15 | 14 | 0 | ... = ... | | | lvalue: float | ir.cpp:149:5:149:11 | +| ir.cpp:144:6:144:17 | FloatCrement(float) -> void | 16 | 15 | 0 | y | | | lvalue: float | ir.cpp:149:5:149:5 | +| ir.cpp:144:6:144:17 | FloatCrement(float) -> void | 17 | 15 | 1 | ... ++ | | | prvalue: float | ir.cpp:149:9:149:11 | +| ir.cpp:144:6:144:17 | FloatCrement(float) -> void | 18 | 17 | 0 | x | | | lvalue: float | ir.cpp:149:9:149:9 | +| ir.cpp:144:6:144:17 | FloatCrement(float) -> void | 19 | 1 | 4 | ExprStmt | | | | ir.cpp:150:5:150:12 | +| ir.cpp:144:6:144:17 | FloatCrement(float) -> void | 20 | 19 | 0 | ... = ... | | | lvalue: float | ir.cpp:150:5:150:11 | +| ir.cpp:144:6:144:17 | FloatCrement(float) -> void | 21 | 20 | 0 | y | | | lvalue: float | ir.cpp:150:5:150:5 | +| ir.cpp:144:6:144:17 | FloatCrement(float) -> void | 22 | 20 | 1 | ... -- | | | prvalue: float | ir.cpp:150:9:150:11 | +| ir.cpp:144:6:144:17 | FloatCrement(float) -> void | 23 | 22 | 0 | x | | | lvalue: float | ir.cpp:150:9:150:9 | +| ir.cpp:144:6:144:17 | FloatCrement(float) -> void | 24 | 1 | 5 | return ... | | | | ir.cpp:151:1:151:1 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 0 | -1 | 0 | PointerOps | | | | ir.cpp:153:6:153:15 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:153:32:169:1 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:154:5:154:11 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 3 | 2 | 0 | definition of q | | | int * | ir.cpp:154:10:154:10 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 4 | 1 | 1 | declaration | | | | ir.cpp:155:5:155:11 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 5 | 4 | 0 | definition of b | | | bool | ir.cpp:155:10:155:10 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 6 | 1 | 2 | ExprStmt | | | | ir.cpp:157:5:157:14 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 7 | 6 | 0 | ... = ... | | | lvalue: int * | ir.cpp:157:5:157:13 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 8 | 7 | 0 | q | | | lvalue: int * | ir.cpp:157:5:157:5 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 9 | 7 | 1 | ... + ... | | | prvalue: int * | ir.cpp:157:9:157:13 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 10 | 9 | 0 | p | | | prvalue(load): int * | ir.cpp:157:9:157:9 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 11 | 9 | 1 | i | | | prvalue(load): int | ir.cpp:157:13:157:13 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 12 | 1 | 3 | ExprStmt | | | | ir.cpp:158:5:158:14 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 13 | 12 | 0 | ... = ... | | | lvalue: int * | ir.cpp:158:5:158:13 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 14 | 13 | 0 | q | | | lvalue: int * | ir.cpp:158:5:158:5 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 15 | 13 | 1 | ... + ... | | | prvalue: int * | ir.cpp:158:9:158:13 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 16 | 15 | 0 | i | | | prvalue(load): int | ir.cpp:158:9:158:9 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 17 | 15 | 1 | p | | | prvalue(load): int * | ir.cpp:158:13:158:13 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 18 | 1 | 4 | ExprStmt | | | | ir.cpp:159:5:159:14 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 19 | 18 | 0 | ... = ... | | | lvalue: int * | ir.cpp:159:5:159:13 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 20 | 19 | 0 | q | | | lvalue: int * | ir.cpp:159:5:159:5 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 21 | 19 | 1 | ... - ... | | | prvalue: int * | ir.cpp:159:9:159:13 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 22 | 21 | 0 | p | | | prvalue(load): int * | ir.cpp:159:9:159:9 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 23 | 21 | 1 | i | | | prvalue(load): int | ir.cpp:159:13:159:13 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 24 | 1 | 5 | ExprStmt | | | | ir.cpp:160:5:160:14 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 25 | 24 | 0 | ... = ... | | | lvalue: int | ir.cpp:160:5:160:13 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 26 | 25 | 0 | i | | | lvalue: int | ir.cpp:160:5:160:5 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 27 | 25 | 1 | (int)... | integral conversion | | prvalue: int | ir.cpp:160:9:160:13 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 28 | 27 | 0 | ... - ... | | | prvalue: long | ir.cpp:160:9:160:13 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 29 | 28 | 0 | p | | | prvalue(load): int * | ir.cpp:160:9:160:9 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 30 | 28 | 1 | q | | | prvalue(load): int * | ir.cpp:160:13:160:13 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 31 | 1 | 6 | ExprStmt | | | | ir.cpp:162:5:162:10 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 32 | 31 | 0 | ... = ... | | | lvalue: int * | ir.cpp:162:5:162:9 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 33 | 32 | 0 | q | | | lvalue: int * | ir.cpp:162:5:162:5 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 34 | 32 | 1 | p | | | prvalue(load): int * | ir.cpp:162:9:162:9 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 35 | 1 | 7 | ExprStmt | | | | ir.cpp:164:5:164:11 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 36 | 35 | 0 | ... += ... | | | lvalue: int * | ir.cpp:164:5:164:10 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 37 | 36 | 0 | q | | | lvalue: int * | ir.cpp:164:5:164:5 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 38 | 36 | 1 | i | | | prvalue(load): int | ir.cpp:164:10:164:10 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 39 | 1 | 8 | ExprStmt | | | | ir.cpp:165:5:165:11 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 40 | 39 | 0 | ... -= ... | | | lvalue: int * | ir.cpp:165:5:165:10 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 41 | 40 | 0 | q | | | lvalue: int * | ir.cpp:165:5:165:5 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 42 | 40 | 1 | i | | | prvalue(load): int | ir.cpp:165:10:165:10 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 43 | 1 | 9 | ExprStmt | | | | ir.cpp:167:5:167:10 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 44 | 43 | 0 | ... = ... | | | lvalue: bool | ir.cpp:167:5:167:9 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 45 | 44 | 0 | b | | | lvalue: bool | ir.cpp:167:5:167:5 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 46 | 44 | 1 | (bool)... | conversion to bool | | prvalue: bool | ir.cpp:167:9:167:9 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 47 | 46 | 0 | p | | | prvalue(load): int * | ir.cpp:167:9:167:9 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 48 | 1 | 10 | ExprStmt | | | | ir.cpp:168:5:168:11 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 49 | 48 | 0 | ... = ... | | | lvalue: bool | ir.cpp:168:5:168:10 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 50 | 49 | 0 | b | | | lvalue: bool | ir.cpp:168:5:168:5 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 51 | 49 | 1 | ! ... | | | prvalue: bool | ir.cpp:168:9:168:10 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 52 | 51 | 0 | (bool)... | conversion to bool | | prvalue: bool | ir.cpp:168:10:168:10 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 53 | 52 | 0 | p | | | prvalue(load): int * | ir.cpp:168:10:168:10 | +| ir.cpp:153:6:153:15 | PointerOps(int *, int) -> void | 54 | 1 | 11 | return ... | | | | ir.cpp:169:1:169:1 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 0 | -1 | 0 | ArrayAccess | | | | ir.cpp:171:6:171:16 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:171:33:185:1 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:172:5:172:10 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 3 | 2 | 0 | definition of x | | | int | ir.cpp:172:9:172:9 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 4 | 1 | 1 | ExprStmt | | | | ir.cpp:174:5:174:13 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 5 | 4 | 0 | ... = ... | | | lvalue: int | ir.cpp:174:5:174:12 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 6 | 5 | 0 | x | | | lvalue: int | ir.cpp:174:5:174:5 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 7 | 5 | 1 | access to array | | | prvalue(load): int | ir.cpp:174:9:174:12 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 8 | 7 | 0 | p | | | prvalue(load): int * | ir.cpp:174:9:174:9 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 9 | 7 | 1 | i | | | prvalue(load): int | ir.cpp:174:11:174:11 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 10 | 1 | 2 | ExprStmt | | | | ir.cpp:175:5:175:13 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 11 | 10 | 0 | ... = ... | | | lvalue: int | ir.cpp:175:5:175:12 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 12 | 11 | 0 | x | | | lvalue: int | ir.cpp:175:5:175:5 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 13 | 11 | 1 | access to array | | | prvalue(load): int | ir.cpp:175:9:175:12 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 14 | 13 | 0 | p | | | prvalue(load): int * | ir.cpp:175:11:175:11 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 15 | 13 | 1 | i | | | prvalue(load): int | ir.cpp:175:9:175:9 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 16 | 1 | 3 | ExprStmt | | | | ir.cpp:177:5:177:13 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 17 | 16 | 0 | ... = ... | | | lvalue: int | ir.cpp:177:5:177:12 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 18 | 17 | 0 | access to array | | | lvalue: int | ir.cpp:177:5:177:8 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 19 | 18 | 0 | p | | | prvalue(load): int * | ir.cpp:177:5:177:5 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 20 | 18 | 1 | i | | | prvalue(load): int | ir.cpp:177:7:177:7 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 21 | 17 | 1 | x | | | prvalue(load): int | ir.cpp:177:12:177:12 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 22 | 1 | 4 | ExprStmt | | | | ir.cpp:178:5:178:13 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 23 | 22 | 0 | ... = ... | | | lvalue: int | ir.cpp:178:5:178:12 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 24 | 23 | 0 | access to array | | | lvalue: int | ir.cpp:178:5:178:8 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 25 | 24 | 0 | p | | | prvalue(load): int * | ir.cpp:178:7:178:7 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 26 | 24 | 1 | i | | | prvalue(load): int | ir.cpp:178:5:178:5 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 27 | 23 | 1 | x | | | prvalue(load): int | ir.cpp:178:12:178:12 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 28 | 1 | 5 | declaration | | | | ir.cpp:180:5:180:14 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 29 | 28 | 0 | definition of a | | | int[10] | ir.cpp:180:9:180:9 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 30 | 1 | 6 | ExprStmt | | | | ir.cpp:181:5:181:13 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 31 | 30 | 0 | ... = ... | | | lvalue: int | ir.cpp:181:5:181:12 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 32 | 31 | 0 | x | | | lvalue: int | ir.cpp:181:5:181:5 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 33 | 31 | 1 | access to array | | | prvalue(load): int | ir.cpp:181:9:181:12 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 34 | 33 | 0 | array to pointer conversion | | | prvalue: int * | ir.cpp:181:9:181:9 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 35 | 34 | 0 | a | | | lvalue: int[10] | ir.cpp:181:9:181:9 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 36 | 33 | 1 | i | | | prvalue(load): int | ir.cpp:181:11:181:11 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 37 | 1 | 7 | ExprStmt | | | | ir.cpp:182:5:182:13 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 38 | 37 | 0 | ... = ... | | | lvalue: int | ir.cpp:182:5:182:12 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 39 | 38 | 0 | x | | | lvalue: int | ir.cpp:182:5:182:5 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 40 | 38 | 1 | access to array | | | prvalue(load): int | ir.cpp:182:9:182:12 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 41 | 40 | 0 | array to pointer conversion | | | prvalue: int * | ir.cpp:182:11:182:11 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 42 | 41 | 0 | a | | | lvalue: int[10] | ir.cpp:182:11:182:11 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 43 | 40 | 1 | i | | | prvalue(load): int | ir.cpp:182:9:182:9 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 44 | 1 | 8 | ExprStmt | | | | ir.cpp:183:5:183:13 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 45 | 44 | 0 | ... = ... | | | lvalue: int | ir.cpp:183:5:183:12 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 46 | 45 | 0 | access to array | | | lvalue: int | ir.cpp:183:5:183:8 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 47 | 46 | 0 | array to pointer conversion | | | prvalue: int * | ir.cpp:183:5:183:5 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 48 | 47 | 0 | a | | | lvalue: int[10] | ir.cpp:183:5:183:5 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 49 | 46 | 1 | i | | | prvalue(load): int | ir.cpp:183:7:183:7 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 50 | 45 | 1 | x | | | prvalue(load): int | ir.cpp:183:12:183:12 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 51 | 1 | 9 | ExprStmt | | | | ir.cpp:184:5:184:13 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 52 | 51 | 0 | ... = ... | | | lvalue: int | ir.cpp:184:5:184:12 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 53 | 52 | 0 | access to array | | | lvalue: int | ir.cpp:184:5:184:8 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 54 | 53 | 0 | array to pointer conversion | | | prvalue: int * | ir.cpp:184:7:184:7 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 55 | 54 | 0 | a | | | lvalue: int[10] | ir.cpp:184:7:184:7 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 56 | 53 | 1 | i | | | prvalue(load): int | ir.cpp:184:5:184:5 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 57 | 52 | 1 | x | | | prvalue(load): int | ir.cpp:184:12:184:12 | +| ir.cpp:171:6:171:16 | ArrayAccess(int *, int) -> void | 58 | 1 | 10 | return ... | | | | ir.cpp:185:1:185:1 | +| ir.cpp:187:6:187:18 | StringLiteral(int) -> void | 0 | -1 | 0 | StringLiteral | | | | ir.cpp:187:6:187:18 | +| ir.cpp:187:6:187:18 | StringLiteral(int) -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:187:27:191:1 | +| ir.cpp:187:6:187:18 | StringLiteral(int) -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:188:5:188:22 | +| ir.cpp:187:6:187:18 | StringLiteral(int) -> void | 3 | 2 | 0 | definition of c | | | char | ir.cpp:188:10:188:10 | +| ir.cpp:187:6:187:18 | StringLiteral(int) -> void | 4 | 3 | 0 | initializer for c | | | | ir.cpp:188:13:188:21 | +| ir.cpp:187:6:187:18 | StringLiteral(int) -> void | 5 | 4 | 0 | access to array | | | prvalue(load): char | ir.cpp:188:14:188:21 | +| ir.cpp:187:6:187:18 | StringLiteral(int) -> void | 6 | 5 | 0 | array to pointer conversion | | | prvalue: const char * | ir.cpp:188:14:188:18 | +| ir.cpp:187:6:187:18 | StringLiteral(int) -> void | 7 | 6 | 0 | Foo | | =Foo | lvalue: const char[4] | ir.cpp:188:14:188:18 | +| ir.cpp:187:6:187:18 | StringLiteral(int) -> void | 8 | 5 | 1 | i | | | prvalue(load): int | ir.cpp:188:20:188:20 | +| ir.cpp:187:6:187:18 | StringLiteral(int) -> void | 9 | 1 | 1 | declaration | | | | ir.cpp:189:5:189:26 | +| ir.cpp:187:6:187:18 | StringLiteral(int) -> void | 10 | 9 | 0 | definition of pwc | | | wchar_t * | ir.cpp:189:14:189:16 | +| ir.cpp:187:6:187:18 | StringLiteral(int) -> void | 11 | 10 | 0 | initializer for pwc | | | | ir.cpp:189:19:189:25 | +| ir.cpp:187:6:187:18 | StringLiteral(int) -> void | 12 | 11 | 0 | (wchar_t *)... | pointer conversion | | prvalue: wchar_t * | ir.cpp:189:20:189:25 | +| ir.cpp:187:6:187:18 | StringLiteral(int) -> void | 13 | 12 | 0 | array to pointer conversion | | | prvalue: const wchar_t * | ir.cpp:189:20:189:25 | +| ir.cpp:187:6:187:18 | StringLiteral(int) -> void | 14 | 13 | 0 | Bar | | =Bar | lvalue: const wchar_t[4] | ir.cpp:189:20:189:25 | +| ir.cpp:187:6:187:18 | StringLiteral(int) -> void | 15 | 1 | 2 | declaration | | | | ir.cpp:190:5:190:24 | +| ir.cpp:187:6:187:18 | StringLiteral(int) -> void | 16 | 15 | 0 | definition of wc | | | wchar_t | ir.cpp:190:13:190:14 | +| ir.cpp:187:6:187:18 | StringLiteral(int) -> void | 17 | 16 | 0 | initializer for wc | | | | ir.cpp:190:17:190:23 | +| ir.cpp:187:6:187:18 | StringLiteral(int) -> void | 18 | 17 | 0 | access to array | | | prvalue(load): wchar_t | ir.cpp:190:18:190:23 | +| ir.cpp:187:6:187:18 | StringLiteral(int) -> void | 19 | 18 | 0 | pwc | | | prvalue(load): wchar_t * | ir.cpp:190:18:190:20 | +| ir.cpp:187:6:187:18 | StringLiteral(int) -> void | 20 | 18 | 1 | i | | | prvalue(load): int | ir.cpp:190:22:190:22 | +| ir.cpp:187:6:187:18 | StringLiteral(int) -> void | 21 | 1 | 3 | return ... | | | | ir.cpp:191:1:191:1 | +| ir.cpp:193:6:193:19 | PointerCompare(int *, int *) -> void | 0 | -1 | 0 | PointerCompare | | | | ir.cpp:193:6:193:19 | +| ir.cpp:193:6:193:19 | PointerCompare(int *, int *) -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:193:37:202:1 | +| ir.cpp:193:6:193:19 | PointerCompare(int *, int *) -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:194:5:194:11 | +| ir.cpp:193:6:193:19 | PointerCompare(int *, int *) -> void | 3 | 2 | 0 | definition of b | | | bool | ir.cpp:194:10:194:10 | +| ir.cpp:193:6:193:19 | PointerCompare(int *, int *) -> void | 4 | 1 | 1 | ExprStmt | | | | ir.cpp:196:5:196:15 | +| ir.cpp:193:6:193:19 | PointerCompare(int *, int *) -> void | 5 | 4 | 0 | ... = ... | | | lvalue: bool | ir.cpp:196:5:196:14 | +| ir.cpp:193:6:193:19 | PointerCompare(int *, int *) -> void | 6 | 5 | 0 | b | | | lvalue: bool | ir.cpp:196:5:196:5 | +| ir.cpp:193:6:193:19 | PointerCompare(int *, int *) -> void | 7 | 5 | 1 | ... == ... | | | prvalue: bool | ir.cpp:196:9:196:14 | +| ir.cpp:193:6:193:19 | PointerCompare(int *, int *) -> void | 8 | 7 | 0 | p | | | prvalue(load): int * | ir.cpp:196:9:196:9 | +| ir.cpp:193:6:193:19 | PointerCompare(int *, int *) -> void | 9 | 7 | 1 | q | | | prvalue(load): int * | ir.cpp:196:14:196:14 | +| ir.cpp:193:6:193:19 | PointerCompare(int *, int *) -> void | 10 | 1 | 2 | ExprStmt | | | | ir.cpp:197:5:197:15 | +| ir.cpp:193:6:193:19 | PointerCompare(int *, int *) -> void | 11 | 10 | 0 | ... = ... | | | lvalue: bool | ir.cpp:197:5:197:14 | +| ir.cpp:193:6:193:19 | PointerCompare(int *, int *) -> void | 12 | 11 | 0 | b | | | lvalue: bool | ir.cpp:197:5:197:5 | +| ir.cpp:193:6:193:19 | PointerCompare(int *, int *) -> void | 13 | 11 | 1 | ... != ... | | | prvalue: bool | ir.cpp:197:9:197:14 | +| ir.cpp:193:6:193:19 | PointerCompare(int *, int *) -> void | 14 | 13 | 0 | p | | | prvalue(load): int * | ir.cpp:197:9:197:9 | +| ir.cpp:193:6:193:19 | PointerCompare(int *, int *) -> void | 15 | 13 | 1 | q | | | prvalue(load): int * | ir.cpp:197:14:197:14 | +| ir.cpp:193:6:193:19 | PointerCompare(int *, int *) -> void | 16 | 1 | 3 | ExprStmt | | | | ir.cpp:198:5:198:14 | +| ir.cpp:193:6:193:19 | PointerCompare(int *, int *) -> void | 17 | 16 | 0 | ... = ... | | | lvalue: bool | ir.cpp:198:5:198:13 | +| ir.cpp:193:6:193:19 | PointerCompare(int *, int *) -> void | 18 | 17 | 0 | b | | | lvalue: bool | ir.cpp:198:5:198:5 | +| ir.cpp:193:6:193:19 | PointerCompare(int *, int *) -> void | 19 | 17 | 1 | ... < ... | | | prvalue: bool | ir.cpp:198:9:198:13 | +| ir.cpp:193:6:193:19 | PointerCompare(int *, int *) -> void | 20 | 19 | 0 | p | | | prvalue(load): int * | ir.cpp:198:9:198:9 | +| ir.cpp:193:6:193:19 | PointerCompare(int *, int *) -> void | 21 | 19 | 1 | q | | | prvalue(load): int * | ir.cpp:198:13:198:13 | +| ir.cpp:193:6:193:19 | PointerCompare(int *, int *) -> void | 22 | 1 | 4 | ExprStmt | | | | ir.cpp:199:5:199:14 | +| ir.cpp:193:6:193:19 | PointerCompare(int *, int *) -> void | 23 | 22 | 0 | ... = ... | | | lvalue: bool | ir.cpp:199:5:199:13 | +| ir.cpp:193:6:193:19 | PointerCompare(int *, int *) -> void | 24 | 23 | 0 | b | | | lvalue: bool | ir.cpp:199:5:199:5 | +| ir.cpp:193:6:193:19 | PointerCompare(int *, int *) -> void | 25 | 23 | 1 | ... > ... | | | prvalue: bool | ir.cpp:199:9:199:13 | +| ir.cpp:193:6:193:19 | PointerCompare(int *, int *) -> void | 26 | 25 | 0 | p | | | prvalue(load): int * | ir.cpp:199:9:199:9 | +| ir.cpp:193:6:193:19 | PointerCompare(int *, int *) -> void | 27 | 25 | 1 | q | | | prvalue(load): int * | ir.cpp:199:13:199:13 | +| ir.cpp:193:6:193:19 | PointerCompare(int *, int *) -> void | 28 | 1 | 5 | ExprStmt | | | | ir.cpp:200:5:200:15 | +| ir.cpp:193:6:193:19 | PointerCompare(int *, int *) -> void | 29 | 28 | 0 | ... = ... | | | lvalue: bool | ir.cpp:200:5:200:14 | +| ir.cpp:193:6:193:19 | PointerCompare(int *, int *) -> void | 30 | 29 | 0 | b | | | lvalue: bool | ir.cpp:200:5:200:5 | +| ir.cpp:193:6:193:19 | PointerCompare(int *, int *) -> void | 31 | 29 | 1 | ... <= ... | | | prvalue: bool | ir.cpp:200:9:200:14 | +| ir.cpp:193:6:193:19 | PointerCompare(int *, int *) -> void | 32 | 31 | 0 | p | | | prvalue(load): int * | ir.cpp:200:9:200:9 | +| ir.cpp:193:6:193:19 | PointerCompare(int *, int *) -> void | 33 | 31 | 1 | q | | | prvalue(load): int * | ir.cpp:200:14:200:14 | +| ir.cpp:193:6:193:19 | PointerCompare(int *, int *) -> void | 34 | 1 | 6 | ExprStmt | | | | ir.cpp:201:5:201:15 | +| ir.cpp:193:6:193:19 | PointerCompare(int *, int *) -> void | 35 | 34 | 0 | ... = ... | | | lvalue: bool | ir.cpp:201:5:201:14 | +| ir.cpp:193:6:193:19 | PointerCompare(int *, int *) -> void | 36 | 35 | 0 | b | | | lvalue: bool | ir.cpp:201:5:201:5 | +| ir.cpp:193:6:193:19 | PointerCompare(int *, int *) -> void | 37 | 35 | 1 | ... >= ... | | | prvalue: bool | ir.cpp:201:9:201:14 | +| ir.cpp:193:6:193:19 | PointerCompare(int *, int *) -> void | 38 | 37 | 0 | p | | | prvalue(load): int * | ir.cpp:201:9:201:9 | +| ir.cpp:193:6:193:19 | PointerCompare(int *, int *) -> void | 39 | 37 | 1 | q | | | prvalue(load): int * | ir.cpp:201:14:201:14 | +| ir.cpp:193:6:193:19 | PointerCompare(int *, int *) -> void | 40 | 1 | 7 | return ... | | | | ir.cpp:202:1:202:1 | +| ir.cpp:204:6:204:19 | PointerCrement(int *) -> void | 0 | -1 | 0 | PointerCrement | | | | ir.cpp:204:6:204:19 | +| ir.cpp:204:6:204:19 | PointerCrement(int *) -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:204:29:211:1 | +| ir.cpp:204:6:204:19 | PointerCrement(int *) -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:205:5:205:11 | +| ir.cpp:204:6:204:19 | PointerCrement(int *) -> void | 3 | 2 | 0 | definition of q | | | int * | ir.cpp:205:10:205:10 | +| ir.cpp:204:6:204:19 | PointerCrement(int *) -> void | 4 | 1 | 1 | ExprStmt | | | | ir.cpp:207:5:207:12 | +| ir.cpp:204:6:204:19 | PointerCrement(int *) -> void | 5 | 4 | 0 | ... = ... | | | lvalue: int * | ir.cpp:207:5:207:11 | +| ir.cpp:204:6:204:19 | PointerCrement(int *) -> void | 6 | 5 | 0 | q | | | lvalue: int * | ir.cpp:207:5:207:5 | +| ir.cpp:204:6:204:19 | PointerCrement(int *) -> void | 7 | 5 | 1 | ++ ... | | | prvalue: int * | ir.cpp:207:9:207:11 | +| ir.cpp:204:6:204:19 | PointerCrement(int *) -> void | 8 | 7 | 0 | p | | | lvalue: int * | ir.cpp:207:11:207:11 | +| ir.cpp:204:6:204:19 | PointerCrement(int *) -> void | 9 | 1 | 2 | ExprStmt | | | | ir.cpp:208:5:208:12 | +| ir.cpp:204:6:204:19 | PointerCrement(int *) -> void | 10 | 9 | 0 | ... = ... | | | lvalue: int * | ir.cpp:208:5:208:11 | +| ir.cpp:204:6:204:19 | PointerCrement(int *) -> void | 11 | 10 | 0 | q | | | lvalue: int * | ir.cpp:208:5:208:5 | +| ir.cpp:204:6:204:19 | PointerCrement(int *) -> void | 12 | 10 | 1 | -- ... | | | prvalue: int * | ir.cpp:208:9:208:11 | +| ir.cpp:204:6:204:19 | PointerCrement(int *) -> void | 13 | 12 | 0 | p | | | lvalue: int * | ir.cpp:208:11:208:11 | +| ir.cpp:204:6:204:19 | PointerCrement(int *) -> void | 14 | 1 | 3 | ExprStmt | | | | ir.cpp:209:5:209:12 | +| ir.cpp:204:6:204:19 | PointerCrement(int *) -> void | 15 | 14 | 0 | ... = ... | | | lvalue: int * | ir.cpp:209:5:209:11 | +| ir.cpp:204:6:204:19 | PointerCrement(int *) -> void | 16 | 15 | 0 | q | | | lvalue: int * | ir.cpp:209:5:209:5 | +| ir.cpp:204:6:204:19 | PointerCrement(int *) -> void | 17 | 15 | 1 | ... ++ | | | prvalue: int * | ir.cpp:209:9:209:11 | +| ir.cpp:204:6:204:19 | PointerCrement(int *) -> void | 18 | 17 | 0 | p | | | lvalue: int * | ir.cpp:209:9:209:9 | +| ir.cpp:204:6:204:19 | PointerCrement(int *) -> void | 19 | 1 | 4 | ExprStmt | | | | ir.cpp:210:5:210:12 | +| ir.cpp:204:6:204:19 | PointerCrement(int *) -> void | 20 | 19 | 0 | ... = ... | | | lvalue: int * | ir.cpp:210:5:210:11 | +| ir.cpp:204:6:204:19 | PointerCrement(int *) -> void | 21 | 20 | 0 | q | | | lvalue: int * | ir.cpp:210:5:210:5 | +| ir.cpp:204:6:204:19 | PointerCrement(int *) -> void | 22 | 20 | 1 | ... -- | | | prvalue: int * | ir.cpp:210:9:210:11 | +| ir.cpp:204:6:204:19 | PointerCrement(int *) -> void | 23 | 22 | 0 | p | | | lvalue: int * | ir.cpp:210:9:210:9 | +| ir.cpp:204:6:204:19 | PointerCrement(int *) -> void | 24 | 1 | 5 | return ... | | | | ir.cpp:211:1:211:1 | +| ir.cpp:213:6:213:23 | CompoundAssignment() -> void | 0 | -1 | 0 | CompoundAssignment | | | | ir.cpp:213:6:213:23 | +| ir.cpp:213:6:213:23 | CompoundAssignment() -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:213:27:228:1 | +| ir.cpp:213:6:213:23 | CompoundAssignment() -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:215:5:215:14 | +| ir.cpp:213:6:213:23 | CompoundAssignment() -> void | 3 | 2 | 0 | definition of x | | | int | ir.cpp:215:9:215:9 | +| ir.cpp:213:6:213:23 | CompoundAssignment() -> void | 4 | 3 | 0 | initializer for x | | | | ir.cpp:215:12:215:13 | +| ir.cpp:213:6:213:23 | CompoundAssignment() -> void | 5 | 4 | 0 | 5 | | =5 | prvalue: int | ir.cpp:215:12:215:13 | +| ir.cpp:213:6:213:23 | CompoundAssignment() -> void | 6 | 1 | 1 | ExprStmt | | | | ir.cpp:216:5:216:11 | +| ir.cpp:213:6:213:23 | CompoundAssignment() -> void | 7 | 6 | 0 | ... += ... | | | lvalue: int | ir.cpp:216:5:216:10 | +| ir.cpp:213:6:213:23 | CompoundAssignment() -> void | 8 | 7 | 0 | x | | | lvalue: int | ir.cpp:216:5:216:5 | +| ir.cpp:213:6:213:23 | CompoundAssignment() -> void | 9 | 7 | 1 | 7 | | =7 | prvalue: int | ir.cpp:216:10:216:10 | +| ir.cpp:213:6:213:23 | CompoundAssignment() -> void | 10 | 1 | 2 | declaration | | | | ir.cpp:219:5:219:16 | +| ir.cpp:213:6:213:23 | CompoundAssignment() -> void | 11 | 10 | 0 | definition of y | | | short | ir.cpp:219:11:219:11 | +| ir.cpp:213:6:213:23 | CompoundAssignment() -> void | 12 | 11 | 0 | initializer for y | | | | ir.cpp:219:14:219:15 | +| ir.cpp:213:6:213:23 | CompoundAssignment() -> void | 13 | 12 | 0 | (short)... | integral conversion | =5 | prvalue: short | ir.cpp:219:15:219:15 | +| ir.cpp:213:6:213:23 | CompoundAssignment() -> void | 14 | 13 | 0 | 5 | | =5 | prvalue: int | ir.cpp:219:15:219:15 | +| ir.cpp:213:6:213:23 | CompoundAssignment() -> void | 15 | 1 | 3 | ExprStmt | | | | ir.cpp:220:5:220:11 | +| ir.cpp:213:6:213:23 | CompoundAssignment() -> void | 16 | 15 | 0 | ... += ... | | | lvalue: short | ir.cpp:220:5:220:10 | +| ir.cpp:213:6:213:23 | CompoundAssignment() -> void | 17 | 16 | 0 | y | | | lvalue: short | ir.cpp:220:5:220:5 | +| ir.cpp:213:6:213:23 | CompoundAssignment() -> void | 18 | 16 | 1 | x | | | prvalue(load): int | ir.cpp:220:10:220:10 | +| ir.cpp:213:6:213:23 | CompoundAssignment() -> void | 19 | 1 | 4 | ExprStmt | | | | ir.cpp:223:5:223:12 | +| ir.cpp:213:6:213:23 | CompoundAssignment() -> void | 20 | 19 | 0 | ... <<= ... | | | lvalue: short | ir.cpp:223:5:223:11 | +| ir.cpp:213:6:213:23 | CompoundAssignment() -> void | 21 | 20 | 0 | y | | | lvalue: short | ir.cpp:223:5:223:5 | +| ir.cpp:213:6:213:23 | CompoundAssignment() -> void | 22 | 20 | 1 | 1 | | =1 | prvalue: int | ir.cpp:223:11:223:11 | +| ir.cpp:213:6:213:23 | CompoundAssignment() -> void | 23 | 1 | 5 | declaration | | | | ir.cpp:226:5:226:15 | +| ir.cpp:213:6:213:23 | CompoundAssignment() -> void | 24 | 23 | 0 | definition of z | | | long | ir.cpp:226:10:226:10 | +| ir.cpp:213:6:213:23 | CompoundAssignment() -> void | 25 | 24 | 0 | initializer for z | | | | ir.cpp:226:13:226:14 | +| ir.cpp:213:6:213:23 | CompoundAssignment() -> void | 26 | 25 | 0 | (long)... | integral conversion | =7 | prvalue: long | ir.cpp:226:14:226:14 | +| ir.cpp:213:6:213:23 | CompoundAssignment() -> void | 27 | 26 | 0 | 7 | | =7 | prvalue: int | ir.cpp:226:14:226:14 | +| ir.cpp:213:6:213:23 | CompoundAssignment() -> void | 28 | 1 | 6 | ExprStmt | | | | ir.cpp:227:5:227:14 | +| ir.cpp:213:6:213:23 | CompoundAssignment() -> void | 29 | 28 | 0 | ... += ... | | | lvalue: long | ir.cpp:227:5:227:13 | +| ir.cpp:213:6:213:23 | CompoundAssignment() -> void | 30 | 29 | 0 | z | | | lvalue: long | ir.cpp:227:5:227:5 | +| ir.cpp:213:6:213:23 | CompoundAssignment() -> void | 31 | 29 | 1 | 2.0 | | =2.0 | prvalue: float | ir.cpp:227:10:227:13 | +| ir.cpp:213:6:213:23 | CompoundAssignment() -> void | 32 | 1 | 7 | return ... | | | | ir.cpp:228:1:228:1 | +| ir.cpp:230:6:230:27 | UninitializedVariables() -> void | 0 | -1 | 0 | UninitializedVariables | | | | ir.cpp:230:6:230:27 | +| ir.cpp:230:6:230:27 | UninitializedVariables() -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:230:31:233:1 | +| ir.cpp:230:6:230:27 | UninitializedVariables() -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:231:5:231:10 | +| ir.cpp:230:6:230:27 | UninitializedVariables() -> void | 3 | 2 | 0 | definition of x | | | int | ir.cpp:231:9:231:9 | +| ir.cpp:230:6:230:27 | UninitializedVariables() -> void | 4 | 1 | 1 | declaration | | | | ir.cpp:232:5:232:14 | +| ir.cpp:230:6:230:27 | UninitializedVariables() -> void | 5 | 4 | 0 | definition of y | | | int | ir.cpp:232:9:232:9 | +| ir.cpp:230:6:230:27 | UninitializedVariables() -> void | 6 | 5 | 0 | initializer for y | | | | ir.cpp:232:12:232:13 | +| ir.cpp:230:6:230:27 | UninitializedVariables() -> void | 7 | 6 | 0 | x | | | prvalue(load): int | ir.cpp:232:13:232:13 | +| ir.cpp:230:6:230:27 | UninitializedVariables() -> void | 8 | 1 | 2 | return ... | | | | ir.cpp:233:1:233:1 | +| ir.cpp:235:5:235:14 | Parameters(int, int) -> int | 0 | -1 | 0 | Parameters | | | | ir.cpp:235:5:235:14 | +| ir.cpp:235:5:235:14 | Parameters(int, int) -> int | 1 | 0 | 0 | { ... } | | | | ir.cpp:235:30:237:1 | +| ir.cpp:235:5:235:14 | Parameters(int, int) -> int | 2 | 1 | 0 | return ... | | | | ir.cpp:236:5:236:17 | +| ir.cpp:235:5:235:14 | Parameters(int, int) -> int | 3 | 2 | 0 | ... % ... | | | prvalue: int | ir.cpp:236:12:236:16 | +| ir.cpp:235:5:235:14 | Parameters(int, int) -> int | 4 | 3 | 0 | x | | | prvalue(load): int | ir.cpp:236:12:236:12 | +| ir.cpp:235:5:235:14 | Parameters(int, int) -> int | 5 | 3 | 1 | y | | | prvalue(load): int | ir.cpp:236:16:236:16 | +| ir.cpp:239:6:239:17 | IfStatements(bool, int, int) -> void | 0 | -1 | 0 | IfStatements | | | | ir.cpp:239:6:239:17 | +| ir.cpp:239:6:239:17 | IfStatements(bool, int, int) -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:239:41:251:1 | +| ir.cpp:239:6:239:17 | IfStatements(bool, int, int) -> void | 2 | 1 | 0 | if (...) ... | | | | ir.cpp:240:5:241:5 | +| ir.cpp:239:6:239:17 | IfStatements(bool, int, int) -> void | 3 | 2 | 0 | b | | | prvalue(load): bool | ir.cpp:240:9:240:9 | +| ir.cpp:239:6:239:17 | IfStatements(bool, int, int) -> void | 4 | 2 | 1 | { ... } | | | | ir.cpp:240:12:241:5 | +| ir.cpp:239:6:239:17 | IfStatements(bool, int, int) -> void | 5 | 1 | 1 | if (...) ... | | | | ir.cpp:243:5:245:5 | +| ir.cpp:239:6:239:17 | IfStatements(bool, int, int) -> void | 6 | 5 | 0 | b | | | prvalue(load): bool | ir.cpp:243:9:243:9 | +| ir.cpp:239:6:239:17 | IfStatements(bool, int, int) -> void | 7 | 5 | 1 | { ... } | | | | ir.cpp:243:12:245:5 | +| ir.cpp:239:6:239:17 | IfStatements(bool, int, int) -> void | 8 | 7 | 0 | ExprStmt | | | | ir.cpp:244:9:244:14 | +| ir.cpp:239:6:239:17 | IfStatements(bool, int, int) -> void | 9 | 8 | 0 | ... = ... | | | lvalue: int | ir.cpp:244:9:244:13 | +| ir.cpp:239:6:239:17 | IfStatements(bool, int, int) -> void | 10 | 9 | 0 | x | | | lvalue: int | ir.cpp:244:9:244:9 | +| ir.cpp:239:6:239:17 | IfStatements(bool, int, int) -> void | 11 | 9 | 1 | y | | | prvalue(load): int | ir.cpp:244:13:244:13 | +| ir.cpp:239:6:239:17 | IfStatements(bool, int, int) -> void | 12 | 1 | 2 | if (...) ... | | | | ir.cpp:247:5:250:14 | +| ir.cpp:239:6:239:17 | IfStatements(bool, int, int) -> void | 13 | 12 | 0 | ... < ... | | | prvalue: bool | ir.cpp:247:9:247:13 | +| ir.cpp:239:6:239:17 | IfStatements(bool, int, int) -> void | 14 | 13 | 0 | x | | | prvalue(load): int | ir.cpp:247:9:247:9 | +| ir.cpp:239:6:239:17 | IfStatements(bool, int, int) -> void | 15 | 13 | 1 | 7 | | =7 | prvalue: int | ir.cpp:247:13:247:13 | +| ir.cpp:239:6:239:17 | IfStatements(bool, int, int) -> void | 16 | 12 | 1 | ExprStmt | | | | ir.cpp:248:9:248:14 | +| ir.cpp:239:6:239:17 | IfStatements(bool, int, int) -> void | 17 | 16 | 0 | ... = ... | | | lvalue: int | ir.cpp:248:9:248:13 | +| ir.cpp:239:6:239:17 | IfStatements(bool, int, int) -> void | 18 | 17 | 0 | x | | | lvalue: int | ir.cpp:248:9:248:9 | +| ir.cpp:239:6:239:17 | IfStatements(bool, int, int) -> void | 19 | 17 | 1 | 2 | | =2 | prvalue: int | ir.cpp:248:13:248:13 | +| ir.cpp:239:6:239:17 | IfStatements(bool, int, int) -> void | 20 | 12 | 2 | ExprStmt | | | | ir.cpp:250:9:250:14 | +| ir.cpp:239:6:239:17 | IfStatements(bool, int, int) -> void | 21 | 20 | 0 | ... = ... | | | lvalue: int | ir.cpp:250:9:250:13 | +| ir.cpp:239:6:239:17 | IfStatements(bool, int, int) -> void | 22 | 21 | 0 | x | | | lvalue: int | ir.cpp:250:9:250:9 | +| ir.cpp:239:6:239:17 | IfStatements(bool, int, int) -> void | 23 | 21 | 1 | 7 | | =7 | prvalue: int | ir.cpp:250:13:250:13 | +| ir.cpp:239:6:239:17 | IfStatements(bool, int, int) -> void | 24 | 1 | 3 | return ... | | | | ir.cpp:251:1:251:1 | +| ir.cpp:253:6:253:20 | WhileStatements(int) -> void | 0 | -1 | 0 | WhileStatements | | | | ir.cpp:253:6:253:20 | +| ir.cpp:253:6:253:20 | WhileStatements(int) -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:253:29:257:1 | +| ir.cpp:253:6:253:20 | WhileStatements(int) -> void | 2 | 1 | 0 | while (...) ... | | | | ir.cpp:254:5:256:5 | +| ir.cpp:253:6:253:20 | WhileStatements(int) -> void | 3 | 2 | 0 | ... > ... | | | prvalue: bool | ir.cpp:254:12:254:16 | +| ir.cpp:253:6:253:20 | WhileStatements(int) -> void | 4 | 3 | 0 | n | | | prvalue(load): int | ir.cpp:254:12:254:12 | +| ir.cpp:253:6:253:20 | WhileStatements(int) -> void | 5 | 3 | 1 | 0 | | =0 | prvalue: int | ir.cpp:254:16:254:16 | +| ir.cpp:253:6:253:20 | WhileStatements(int) -> void | 6 | 2 | 1 | { ... } | | | | ir.cpp:254:19:256:5 | +| ir.cpp:253:6:253:20 | WhileStatements(int) -> void | 7 | 6 | 0 | ExprStmt | | | | ir.cpp:255:9:255:15 | +| ir.cpp:253:6:253:20 | WhileStatements(int) -> void | 8 | 7 | 0 | ... -= ... | | | lvalue: int | ir.cpp:255:9:255:14 | +| ir.cpp:253:6:253:20 | WhileStatements(int) -> void | 9 | 8 | 0 | n | | | lvalue: int | ir.cpp:255:9:255:9 | +| ir.cpp:253:6:253:20 | WhileStatements(int) -> void | 10 | 8 | 1 | 1 | | =1 | prvalue: int | ir.cpp:255:14:255:14 | +| ir.cpp:253:6:253:20 | WhileStatements(int) -> void | 11 | 1 | 1 | return ... | | | | ir.cpp:257:1:257:1 | +| ir.cpp:259:6:259:17 | DoStatements(int) -> void | 0 | -1 | 0 | DoStatements | | | | ir.cpp:259:6:259:17 | +| ir.cpp:259:6:259:17 | DoStatements(int) -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:259:26:263:1 | +| ir.cpp:259:6:259:17 | DoStatements(int) -> void | 2 | 1 | 0 | do (...) ... | | | | ir.cpp:260:5:262:20 | +| ir.cpp:259:6:259:17 | DoStatements(int) -> void | 3 | 2 | 0 | ... > ... | | | prvalue: bool | ir.cpp:262:14:262:18 | +| ir.cpp:259:6:259:17 | DoStatements(int) -> void | 4 | 3 | 0 | n | | | prvalue(load): int | ir.cpp:262:14:262:14 | +| ir.cpp:259:6:259:17 | DoStatements(int) -> void | 5 | 3 | 1 | 0 | | =0 | prvalue: int | ir.cpp:262:18:262:18 | +| ir.cpp:259:6:259:17 | DoStatements(int) -> void | 6 | 2 | 1 | { ... } | | | | ir.cpp:260:8:262:5 | +| ir.cpp:259:6:259:17 | DoStatements(int) -> void | 7 | 6 | 0 | ExprStmt | | | | ir.cpp:261:9:261:15 | +| ir.cpp:259:6:259:17 | DoStatements(int) -> void | 8 | 7 | 0 | ... -= ... | | | lvalue: int | ir.cpp:261:9:261:14 | +| ir.cpp:259:6:259:17 | DoStatements(int) -> void | 9 | 8 | 0 | n | | | lvalue: int | ir.cpp:261:9:261:9 | +| ir.cpp:259:6:259:17 | DoStatements(int) -> void | 10 | 8 | 1 | 1 | | =1 | prvalue: int | ir.cpp:261:14:261:14 | +| ir.cpp:259:6:259:17 | DoStatements(int) -> void | 11 | 1 | 1 | return ... | | | | ir.cpp:263:1:263:1 | +| ir.cpp:265:6:265:14 | For_Empty() -> void | 0 | -1 | 0 | For_Empty | | | | ir.cpp:265:6:265:14 | +| ir.cpp:265:6:265:14 | For_Empty() -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:265:18:270:1 | +| ir.cpp:265:6:265:14 | For_Empty() -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:266:5:266:10 | +| ir.cpp:265:6:265:14 | For_Empty() -> void | 3 | 2 | 0 | definition of j | | | int | ir.cpp:266:9:266:9 | +| ir.cpp:265:6:265:14 | For_Empty() -> void | 4 | 1 | 1 | for(...;...;...) ... | | | | ir.cpp:267:5:269:5 | +| ir.cpp:265:6:265:14 | For_Empty() -> void | 5 | 4 | 3 | { ... } | | | | ir.cpp:267:14:269:5 | +| ir.cpp:265:6:265:14 | For_Empty() -> void | 6 | 5 | 0 | ; | | | | ir.cpp:268:9:268:9 | +| ir.cpp:272:6:272:13 | For_Init() -> void | 0 | -1 | 0 | For_Init | | | | ir.cpp:272:6:272:13 | +| ir.cpp:272:6:272:13 | For_Init() -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:272:17:276:1 | +| ir.cpp:272:6:272:13 | For_Init() -> void | 2 | 1 | 0 | for(...;...;...) ... | | | | ir.cpp:273:5:275:5 | +| ir.cpp:272:6:272:13 | For_Init() -> void | 3 | 2 | 0 | declaration | | | | ir.cpp:273:10:273:19 | +| ir.cpp:272:6:272:13 | For_Init() -> void | 4 | 3 | 0 | definition of i | | | int | ir.cpp:273:14:273:14 | +| ir.cpp:272:6:272:13 | For_Init() -> void | 5 | 4 | 0 | initializer for i | | | | ir.cpp:273:17:273:18 | +| ir.cpp:272:6:272:13 | For_Init() -> void | 6 | 5 | 0 | 0 | | =0 | prvalue: int | ir.cpp:273:17:273:18 | +| ir.cpp:272:6:272:13 | For_Init() -> void | 7 | 2 | 3 | { ... } | | | | ir.cpp:273:23:275:5 | +| ir.cpp:272:6:272:13 | For_Init() -> void | 8 | 7 | 0 | ; | | | | ir.cpp:274:9:274:9 | +| ir.cpp:278:6:278:18 | For_Condition() -> void | 0 | -1 | 0 | For_Condition | | | | ir.cpp:278:6:278:18 | +| ir.cpp:278:6:278:18 | For_Condition() -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:278:22:283:1 | +| ir.cpp:278:6:278:18 | For_Condition() -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:279:5:279:14 | +| ir.cpp:278:6:278:18 | For_Condition() -> void | 3 | 2 | 0 | definition of i | | | int | ir.cpp:279:9:279:9 | +| ir.cpp:278:6:278:18 | For_Condition() -> void | 4 | 3 | 0 | initializer for i | | | | ir.cpp:279:12:279:13 | +| ir.cpp:278:6:278:18 | For_Condition() -> void | 5 | 4 | 0 | 0 | | =0 | prvalue: int | ir.cpp:279:12:279:13 | +| ir.cpp:278:6:278:18 | For_Condition() -> void | 6 | 1 | 1 | for(...;...;...) ... | | | | ir.cpp:280:5:282:5 | +| ir.cpp:278:6:278:18 | For_Condition() -> void | 7 | 6 | 1 | ... < ... | | | prvalue: bool | ir.cpp:280:12:280:17 | +| ir.cpp:278:6:278:18 | For_Condition() -> void | 8 | 7 | 0 | i | | | prvalue(load): int | ir.cpp:280:12:280:12 | +| ir.cpp:278:6:278:18 | For_Condition() -> void | 9 | 7 | 1 | 10 | | =10 | prvalue: int | ir.cpp:280:16:280:17 | +| ir.cpp:278:6:278:18 | For_Condition() -> void | 10 | 6 | 3 | { ... } | | | | ir.cpp:280:21:282:5 | +| ir.cpp:278:6:278:18 | For_Condition() -> void | 11 | 10 | 0 | ; | | | | ir.cpp:281:9:281:9 | +| ir.cpp:278:6:278:18 | For_Condition() -> void | 12 | 1 | 2 | return ... | | | | ir.cpp:283:1:283:1 | +| ir.cpp:285:6:285:15 | For_Update() -> void | 0 | -1 | 0 | For_Update | | | | ir.cpp:285:6:285:15 | +| ir.cpp:285:6:285:15 | For_Update() -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:285:19:290:1 | +| ir.cpp:285:6:285:15 | For_Update() -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:286:5:286:14 | +| ir.cpp:285:6:285:15 | For_Update() -> void | 3 | 2 | 0 | definition of i | | | int | ir.cpp:286:9:286:9 | +| ir.cpp:285:6:285:15 | For_Update() -> void | 4 | 3 | 0 | initializer for i | | | | ir.cpp:286:12:286:13 | +| ir.cpp:285:6:285:15 | For_Update() -> void | 5 | 4 | 0 | 0 | | =0 | prvalue: int | ir.cpp:286:12:286:13 | +| ir.cpp:285:6:285:15 | For_Update() -> void | 6 | 1 | 1 | for(...;...;...) ... | | | | ir.cpp:287:5:289:5 | +| ir.cpp:285:6:285:15 | For_Update() -> void | 7 | 6 | 2 | ... += ... | | | lvalue: int | ir.cpp:287:13:287:18 | +| ir.cpp:285:6:285:15 | For_Update() -> void | 8 | 7 | 0 | i | | | lvalue: int | ir.cpp:287:13:287:13 | +| ir.cpp:285:6:285:15 | For_Update() -> void | 9 | 7 | 1 | 1 | | =1 | prvalue: int | ir.cpp:287:18:287:18 | +| ir.cpp:285:6:285:15 | For_Update() -> void | 10 | 6 | 3 | { ... } | | | | ir.cpp:287:21:289:5 | +| ir.cpp:285:6:285:15 | For_Update() -> void | 11 | 10 | 0 | ; | | | | ir.cpp:288:9:288:9 | +| ir.cpp:292:6:292:22 | For_InitCondition() -> void | 0 | -1 | 0 | For_InitCondition | | | | ir.cpp:292:6:292:22 | +| ir.cpp:292:6:292:22 | For_InitCondition() -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:292:26:296:1 | +| ir.cpp:292:6:292:22 | For_InitCondition() -> void | 2 | 1 | 0 | for(...;...;...) ... | | | | ir.cpp:293:5:295:5 | +| ir.cpp:292:6:292:22 | For_InitCondition() -> void | 3 | 2 | 0 | declaration | | | | ir.cpp:293:10:293:19 | +| ir.cpp:292:6:292:22 | For_InitCondition() -> void | 4 | 3 | 0 | definition of i | | | int | ir.cpp:293:14:293:14 | +| ir.cpp:292:6:292:22 | For_InitCondition() -> void | 5 | 4 | 0 | initializer for i | | | | ir.cpp:293:17:293:18 | +| ir.cpp:292:6:292:22 | For_InitCondition() -> void | 6 | 5 | 0 | 0 | | =0 | prvalue: int | ir.cpp:293:17:293:18 | +| ir.cpp:292:6:292:22 | For_InitCondition() -> void | 7 | 2 | 1 | ... < ... | | | prvalue: bool | ir.cpp:293:21:293:26 | +| ir.cpp:292:6:292:22 | For_InitCondition() -> void | 8 | 7 | 0 | i | | | prvalue(load): int | ir.cpp:293:21:293:21 | +| ir.cpp:292:6:292:22 | For_InitCondition() -> void | 9 | 7 | 1 | 10 | | =10 | prvalue: int | ir.cpp:293:25:293:26 | +| ir.cpp:292:6:292:22 | For_InitCondition() -> void | 10 | 2 | 3 | { ... } | | | | ir.cpp:293:30:295:5 | +| ir.cpp:292:6:292:22 | For_InitCondition() -> void | 11 | 10 | 0 | ; | | | | ir.cpp:294:9:294:9 | +| ir.cpp:292:6:292:22 | For_InitCondition() -> void | 12 | 1 | 1 | return ... | | | | ir.cpp:296:1:296:1 | +| ir.cpp:298:6:298:19 | For_InitUpdate() -> void | 0 | -1 | 0 | For_InitUpdate | | | | ir.cpp:298:6:298:19 | +| ir.cpp:298:6:298:19 | For_InitUpdate() -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:298:23:302:1 | +| ir.cpp:298:6:298:19 | For_InitUpdate() -> void | 2 | 1 | 0 | for(...;...;...) ... | | | | ir.cpp:299:5:301:5 | +| ir.cpp:298:6:298:19 | For_InitUpdate() -> void | 3 | 2 | 0 | declaration | | | | ir.cpp:299:10:299:19 | +| ir.cpp:298:6:298:19 | For_InitUpdate() -> void | 4 | 3 | 0 | definition of i | | | int | ir.cpp:299:14:299:14 | +| ir.cpp:298:6:298:19 | For_InitUpdate() -> void | 5 | 4 | 0 | initializer for i | | | | ir.cpp:299:17:299:18 | +| ir.cpp:298:6:298:19 | For_InitUpdate() -> void | 6 | 5 | 0 | 0 | | =0 | prvalue: int | ir.cpp:299:17:299:18 | +| ir.cpp:298:6:298:19 | For_InitUpdate() -> void | 7 | 2 | 2 | ... += ... | | | lvalue: int | ir.cpp:299:22:299:27 | +| ir.cpp:298:6:298:19 | For_InitUpdate() -> void | 8 | 7 | 0 | i | | | lvalue: int | ir.cpp:299:22:299:22 | +| ir.cpp:298:6:298:19 | For_InitUpdate() -> void | 9 | 7 | 1 | 1 | | =1 | prvalue: int | ir.cpp:299:27:299:27 | +| ir.cpp:298:6:298:19 | For_InitUpdate() -> void | 10 | 2 | 3 | { ... } | | | | ir.cpp:299:30:301:5 | +| ir.cpp:298:6:298:19 | For_InitUpdate() -> void | 11 | 10 | 0 | ; | | | | ir.cpp:300:9:300:9 | +| ir.cpp:304:6:304:24 | For_ConditionUpdate() -> void | 0 | -1 | 0 | For_ConditionUpdate | | | | ir.cpp:304:6:304:24 | +| ir.cpp:304:6:304:24 | For_ConditionUpdate() -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:304:28:309:1 | +| ir.cpp:304:6:304:24 | For_ConditionUpdate() -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:305:5:305:14 | +| ir.cpp:304:6:304:24 | For_ConditionUpdate() -> void | 3 | 2 | 0 | definition of i | | | int | ir.cpp:305:9:305:9 | +| ir.cpp:304:6:304:24 | For_ConditionUpdate() -> void | 4 | 3 | 0 | initializer for i | | | | ir.cpp:305:12:305:13 | +| ir.cpp:304:6:304:24 | For_ConditionUpdate() -> void | 5 | 4 | 0 | 0 | | =0 | prvalue: int | ir.cpp:305:12:305:13 | +| ir.cpp:304:6:304:24 | For_ConditionUpdate() -> void | 6 | 1 | 1 | for(...;...;...) ... | | | | ir.cpp:306:5:308:5 | +| ir.cpp:304:6:304:24 | For_ConditionUpdate() -> void | 7 | 6 | 1 | ... < ... | | | prvalue: bool | ir.cpp:306:12:306:17 | +| ir.cpp:304:6:304:24 | For_ConditionUpdate() -> void | 8 | 7 | 0 | i | | | prvalue(load): int | ir.cpp:306:12:306:12 | +| ir.cpp:304:6:304:24 | For_ConditionUpdate() -> void | 9 | 7 | 1 | 10 | | =10 | prvalue: int | ir.cpp:306:16:306:17 | +| ir.cpp:304:6:304:24 | For_ConditionUpdate() -> void | 10 | 6 | 2 | ... += ... | | | lvalue: int | ir.cpp:306:20:306:25 | +| ir.cpp:304:6:304:24 | For_ConditionUpdate() -> void | 11 | 10 | 0 | i | | | lvalue: int | ir.cpp:306:20:306:20 | +| ir.cpp:304:6:304:24 | For_ConditionUpdate() -> void | 12 | 10 | 1 | 1 | | =1 | prvalue: int | ir.cpp:306:25:306:25 | +| ir.cpp:304:6:304:24 | For_ConditionUpdate() -> void | 13 | 6 | 3 | { ... } | | | | ir.cpp:306:28:308:5 | +| ir.cpp:304:6:304:24 | For_ConditionUpdate() -> void | 14 | 13 | 0 | ; | | | | ir.cpp:307:9:307:9 | +| ir.cpp:304:6:304:24 | For_ConditionUpdate() -> void | 15 | 1 | 2 | return ... | | | | ir.cpp:309:1:309:1 | +| ir.cpp:311:6:311:28 | For_InitConditionUpdate() -> void | 0 | -1 | 0 | For_InitConditionUpdate | | | | ir.cpp:311:6:311:28 | +| ir.cpp:311:6:311:28 | For_InitConditionUpdate() -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:311:32:315:1 | +| ir.cpp:311:6:311:28 | For_InitConditionUpdate() -> void | 2 | 1 | 0 | for(...;...;...) ... | | | | ir.cpp:312:5:314:5 | +| ir.cpp:311:6:311:28 | For_InitConditionUpdate() -> void | 3 | 2 | 0 | declaration | | | | ir.cpp:312:10:312:19 | +| ir.cpp:311:6:311:28 | For_InitConditionUpdate() -> void | 4 | 3 | 0 | definition of i | | | int | ir.cpp:312:14:312:14 | +| ir.cpp:311:6:311:28 | For_InitConditionUpdate() -> void | 5 | 4 | 0 | initializer for i | | | | ir.cpp:312:17:312:18 | +| ir.cpp:311:6:311:28 | For_InitConditionUpdate() -> void | 6 | 5 | 0 | 0 | | =0 | prvalue: int | ir.cpp:312:17:312:18 | +| ir.cpp:311:6:311:28 | For_InitConditionUpdate() -> void | 7 | 2 | 1 | ... < ... | | | prvalue: bool | ir.cpp:312:21:312:26 | +| ir.cpp:311:6:311:28 | For_InitConditionUpdate() -> void | 8 | 7 | 0 | i | | | prvalue(load): int | ir.cpp:312:21:312:21 | +| ir.cpp:311:6:311:28 | For_InitConditionUpdate() -> void | 9 | 7 | 1 | 10 | | =10 | prvalue: int | ir.cpp:312:25:312:26 | +| ir.cpp:311:6:311:28 | For_InitConditionUpdate() -> void | 10 | 2 | 2 | ... += ... | | | lvalue: int | ir.cpp:312:29:312:34 | +| ir.cpp:311:6:311:28 | For_InitConditionUpdate() -> void | 11 | 10 | 0 | i | | | lvalue: int | ir.cpp:312:29:312:29 | +| ir.cpp:311:6:311:28 | For_InitConditionUpdate() -> void | 12 | 10 | 1 | 1 | | =1 | prvalue: int | ir.cpp:312:34:312:34 | +| ir.cpp:311:6:311:28 | For_InitConditionUpdate() -> void | 13 | 2 | 3 | { ... } | | | | ir.cpp:312:37:314:5 | +| ir.cpp:311:6:311:28 | For_InitConditionUpdate() -> void | 14 | 13 | 0 | ; | | | | ir.cpp:313:9:313:9 | +| ir.cpp:311:6:311:28 | For_InitConditionUpdate() -> void | 15 | 1 | 1 | return ... | | | | ir.cpp:315:1:315:1 | +| ir.cpp:317:6:317:14 | For_Break() -> void | 0 | -1 | 0 | For_Break | | | | ir.cpp:317:6:317:14 | +| ir.cpp:317:6:317:14 | For_Break() -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:317:18:323:1 | +| ir.cpp:317:6:317:14 | For_Break() -> void | 2 | 1 | 0 | for(...;...;...) ... | | | | ir.cpp:318:5:322:5 | +| ir.cpp:317:6:317:14 | For_Break() -> void | 3 | 2 | 0 | declaration | | | | ir.cpp:318:10:318:19 | +| ir.cpp:317:6:317:14 | For_Break() -> void | 4 | 3 | 0 | definition of i | | | int | ir.cpp:318:14:318:14 | +| ir.cpp:317:6:317:14 | For_Break() -> void | 5 | 4 | 0 | initializer for i | | | | ir.cpp:318:17:318:18 | +| ir.cpp:317:6:317:14 | For_Break() -> void | 6 | 5 | 0 | 0 | | =0 | prvalue: int | ir.cpp:318:17:318:18 | +| ir.cpp:317:6:317:14 | For_Break() -> void | 7 | 2 | 1 | ... < ... | | | prvalue: bool | ir.cpp:318:21:318:26 | +| ir.cpp:317:6:317:14 | For_Break() -> void | 8 | 7 | 0 | i | | | prvalue(load): int | ir.cpp:318:21:318:21 | +| ir.cpp:317:6:317:14 | For_Break() -> void | 9 | 7 | 1 | 10 | | =10 | prvalue: int | ir.cpp:318:25:318:26 | +| ir.cpp:317:6:317:14 | For_Break() -> void | 10 | 2 | 2 | ... += ... | | | lvalue: int | ir.cpp:318:29:318:34 | +| ir.cpp:317:6:317:14 | For_Break() -> void | 11 | 10 | 0 | i | | | lvalue: int | ir.cpp:318:29:318:29 | +| ir.cpp:317:6:317:14 | For_Break() -> void | 12 | 10 | 1 | 1 | | =1 | prvalue: int | ir.cpp:318:34:318:34 | +| ir.cpp:317:6:317:14 | For_Break() -> void | 13 | 2 | 3 | { ... } | | | | ir.cpp:318:37:322:5 | +| ir.cpp:317:6:317:14 | For_Break() -> void | 14 | 13 | 0 | if (...) ... | | | | ir.cpp:319:9:321:9 | +| ir.cpp:317:6:317:14 | For_Break() -> void | 15 | 14 | 0 | ... == ... | | | prvalue: bool | ir.cpp:319:13:319:18 | +| ir.cpp:317:6:317:14 | For_Break() -> void | 16 | 15 | 0 | i | | | prvalue(load): int | ir.cpp:319:13:319:13 | +| ir.cpp:317:6:317:14 | For_Break() -> void | 17 | 15 | 1 | 5 | | =5 | prvalue: int | ir.cpp:319:18:319:18 | +| ir.cpp:317:6:317:14 | For_Break() -> void | 18 | 14 | 1 | { ... } | | | | ir.cpp:319:21:321:9 | +| ir.cpp:317:6:317:14 | For_Break() -> void | 19 | 18 | 0 | break; | | | | ir.cpp:320:13:320:18 | +| ir.cpp:317:6:317:14 | For_Break() -> void | 20 | 1 | 1 | label ...: | | | | ir.cpp:322:5:322:5 | +| ir.cpp:317:6:317:14 | For_Break() -> void | 21 | 1 | 2 | return ... | | | | ir.cpp:323:1:323:1 | +| ir.cpp:325:6:325:24 | For_Continue_Update() -> void | 0 | -1 | 0 | For_Continue_Update | | | | ir.cpp:325:6:325:24 | +| ir.cpp:325:6:325:24 | For_Continue_Update() -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:325:28:331:1 | +| ir.cpp:325:6:325:24 | For_Continue_Update() -> void | 2 | 1 | 0 | for(...;...;...) ... | | | | ir.cpp:326:5:330:5 | +| ir.cpp:325:6:325:24 | For_Continue_Update() -> void | 3 | 2 | 0 | declaration | | | | ir.cpp:326:10:326:19 | +| ir.cpp:325:6:325:24 | For_Continue_Update() -> void | 4 | 3 | 0 | definition of i | | | int | ir.cpp:326:14:326:14 | +| ir.cpp:325:6:325:24 | For_Continue_Update() -> void | 5 | 4 | 0 | initializer for i | | | | ir.cpp:326:17:326:18 | +| ir.cpp:325:6:325:24 | For_Continue_Update() -> void | 6 | 5 | 0 | 0 | | =0 | prvalue: int | ir.cpp:326:17:326:18 | +| ir.cpp:325:6:325:24 | For_Continue_Update() -> void | 7 | 2 | 1 | ... < ... | | | prvalue: bool | ir.cpp:326:21:326:26 | +| ir.cpp:325:6:325:24 | For_Continue_Update() -> void | 8 | 7 | 0 | i | | | prvalue(load): int | ir.cpp:326:21:326:21 | +| ir.cpp:325:6:325:24 | For_Continue_Update() -> void | 9 | 7 | 1 | 10 | | =10 | prvalue: int | ir.cpp:326:25:326:26 | +| ir.cpp:325:6:325:24 | For_Continue_Update() -> void | 10 | 2 | 2 | ... += ... | | | lvalue: int | ir.cpp:326:29:326:34 | +| ir.cpp:325:6:325:24 | For_Continue_Update() -> void | 11 | 10 | 0 | i | | | lvalue: int | ir.cpp:326:29:326:29 | +| ir.cpp:325:6:325:24 | For_Continue_Update() -> void | 12 | 10 | 1 | 1 | | =1 | prvalue: int | ir.cpp:326:34:326:34 | +| ir.cpp:325:6:325:24 | For_Continue_Update() -> void | 13 | 2 | 3 | { ... } | | | | ir.cpp:326:37:330:5 | +| ir.cpp:325:6:325:24 | For_Continue_Update() -> void | 14 | 13 | 0 | if (...) ... | | | | ir.cpp:327:9:329:9 | +| ir.cpp:325:6:325:24 | For_Continue_Update() -> void | 15 | 14 | 0 | ... == ... | | | prvalue: bool | ir.cpp:327:13:327:18 | +| ir.cpp:325:6:325:24 | For_Continue_Update() -> void | 16 | 15 | 0 | i | | | prvalue(load): int | ir.cpp:327:13:327:13 | +| ir.cpp:325:6:325:24 | For_Continue_Update() -> void | 17 | 15 | 1 | 5 | | =5 | prvalue: int | ir.cpp:327:18:327:18 | +| ir.cpp:325:6:325:24 | For_Continue_Update() -> void | 18 | 14 | 1 | { ... } | | | | ir.cpp:327:21:329:9 | +| ir.cpp:325:6:325:24 | For_Continue_Update() -> void | 19 | 18 | 0 | continue; | | | | ir.cpp:328:13:328:21 | +| ir.cpp:325:6:325:24 | For_Continue_Update() -> void | 20 | 13 | 1 | label ...: | | | | ir.cpp:326:5:326:5 | +| ir.cpp:325:6:325:24 | For_Continue_Update() -> void | 21 | 1 | 1 | return ... | | | | ir.cpp:331:1:331:1 | +| ir.cpp:333:6:333:26 | For_Continue_NoUpdate() -> void | 0 | -1 | 0 | For_Continue_NoUpdate | | | | ir.cpp:333:6:333:26 | +| ir.cpp:333:6:333:26 | For_Continue_NoUpdate() -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:333:30:339:1 | +| ir.cpp:333:6:333:26 | For_Continue_NoUpdate() -> void | 2 | 1 | 0 | for(...;...;...) ... | | | | ir.cpp:334:5:338:5 | +| ir.cpp:333:6:333:26 | For_Continue_NoUpdate() -> void | 3 | 2 | 0 | declaration | | | | ir.cpp:334:10:334:19 | +| ir.cpp:333:6:333:26 | For_Continue_NoUpdate() -> void | 4 | 3 | 0 | definition of i | | | int | ir.cpp:334:14:334:14 | +| ir.cpp:333:6:333:26 | For_Continue_NoUpdate() -> void | 5 | 4 | 0 | initializer for i | | | | ir.cpp:334:17:334:18 | +| ir.cpp:333:6:333:26 | For_Continue_NoUpdate() -> void | 6 | 5 | 0 | 0 | | =0 | prvalue: int | ir.cpp:334:17:334:18 | +| ir.cpp:333:6:333:26 | For_Continue_NoUpdate() -> void | 7 | 2 | 1 | ... < ... | | | prvalue: bool | ir.cpp:334:21:334:26 | +| ir.cpp:333:6:333:26 | For_Continue_NoUpdate() -> void | 8 | 7 | 0 | i | | | prvalue(load): int | ir.cpp:334:21:334:21 | +| ir.cpp:333:6:333:26 | For_Continue_NoUpdate() -> void | 9 | 7 | 1 | 10 | | =10 | prvalue: int | ir.cpp:334:25:334:26 | +| ir.cpp:333:6:333:26 | For_Continue_NoUpdate() -> void | 10 | 2 | 3 | { ... } | | | | ir.cpp:334:30:338:5 | +| ir.cpp:333:6:333:26 | For_Continue_NoUpdate() -> void | 11 | 10 | 0 | if (...) ... | | | | ir.cpp:335:9:337:9 | +| ir.cpp:333:6:333:26 | For_Continue_NoUpdate() -> void | 12 | 11 | 0 | ... == ... | | | prvalue: bool | ir.cpp:335:13:335:18 | +| ir.cpp:333:6:333:26 | For_Continue_NoUpdate() -> void | 13 | 12 | 0 | i | | | prvalue(load): int | ir.cpp:335:13:335:13 | +| ir.cpp:333:6:333:26 | For_Continue_NoUpdate() -> void | 14 | 12 | 1 | 5 | | =5 | prvalue: int | ir.cpp:335:18:335:18 | +| ir.cpp:333:6:333:26 | For_Continue_NoUpdate() -> void | 15 | 11 | 1 | { ... } | | | | ir.cpp:335:21:337:9 | +| ir.cpp:333:6:333:26 | For_Continue_NoUpdate() -> void | 16 | 15 | 0 | continue; | | | | ir.cpp:336:13:336:21 | +| ir.cpp:333:6:333:26 | For_Continue_NoUpdate() -> void | 17 | 10 | 1 | label ...: | | | | ir.cpp:334:5:334:5 | +| ir.cpp:333:6:333:26 | For_Continue_NoUpdate() -> void | 18 | 1 | 1 | return ... | | | | ir.cpp:339:1:339:1 | +| ir.cpp:341:5:341:15 | Dereference(int *) -> int | 0 | -1 | 0 | Dereference | | | | ir.cpp:341:5:341:15 | +| ir.cpp:341:5:341:15 | Dereference(int *) -> int | 1 | 0 | 0 | { ... } | | | | ir.cpp:341:25:344:1 | +| ir.cpp:341:5:341:15 | Dereference(int *) -> int | 2 | 1 | 0 | ExprStmt | | | | ir.cpp:342:5:342:11 | +| ir.cpp:341:5:341:15 | Dereference(int *) -> int | 3 | 2 | 0 | ... = ... | | | lvalue: int | ir.cpp:342:5:342:10 | +| ir.cpp:341:5:341:15 | Dereference(int *) -> int | 4 | 3 | 0 | * ... | | | lvalue: int | ir.cpp:342:5:342:6 | +| ir.cpp:341:5:341:15 | Dereference(int *) -> int | 5 | 4 | 0 | p | | | prvalue(load): int * | ir.cpp:342:6:342:6 | +| ir.cpp:341:5:341:15 | Dereference(int *) -> int | 6 | 3 | 1 | 1 | | =1 | prvalue: int | ir.cpp:342:10:342:10 | +| ir.cpp:341:5:341:15 | Dereference(int *) -> int | 7 | 1 | 1 | return ... | | | | ir.cpp:343:5:343:14 | +| ir.cpp:341:5:341:15 | Dereference(int *) -> int | 8 | 7 | 0 | * ... | | | prvalue(load): int | ir.cpp:343:12:343:13 | +| ir.cpp:341:5:341:15 | Dereference(int *) -> int | 9 | 8 | 0 | p | | | prvalue(load): int * | ir.cpp:343:13:343:13 | +| ir.cpp:348:6:348:14 | AddressOf() -> int * | 0 | -1 | 0 | AddressOf | | | | ir.cpp:348:6:348:14 | +| ir.cpp:348:6:348:14 | AddressOf() -> int * | 1 | 0 | 0 | { ... } | | | | ir.cpp:348:18:350:1 | +| ir.cpp:348:6:348:14 | AddressOf() -> int * | 2 | 1 | 0 | return ... | | | | ir.cpp:349:5:349:14 | +| ir.cpp:348:6:348:14 | AddressOf() -> int * | 3 | 2 | 0 | & ... | | | prvalue: int * | ir.cpp:349:12:349:13 | +| ir.cpp:348:6:348:14 | AddressOf() -> int * | 4 | 3 | 0 | g | | | lvalue: int | ir.cpp:349:13:349:13 | +| ir.cpp:352:6:352:10 | Break(int) -> void | 0 | -1 | 0 | Break | | | | ir.cpp:352:6:352:10 | +| ir.cpp:352:6:352:10 | Break(int) -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:352:19:358:1 | +| ir.cpp:352:6:352:10 | Break(int) -> void | 2 | 1 | 0 | while (...) ... | | | | ir.cpp:353:5:357:5 | +| ir.cpp:352:6:352:10 | Break(int) -> void | 3 | 2 | 0 | ... > ... | | | prvalue: bool | ir.cpp:353:12:353:16 | +| ir.cpp:352:6:352:10 | Break(int) -> void | 4 | 3 | 0 | n | | | prvalue(load): int | ir.cpp:353:12:353:12 | +| ir.cpp:352:6:352:10 | Break(int) -> void | 5 | 3 | 1 | 0 | | =0 | prvalue: int | ir.cpp:353:16:353:16 | +| ir.cpp:352:6:352:10 | Break(int) -> void | 6 | 2 | 1 | { ... } | | | | ir.cpp:353:19:357:5 | +| ir.cpp:352:6:352:10 | Break(int) -> void | 7 | 6 | 0 | if (...) ... | | | | ir.cpp:354:9:355:18 | +| ir.cpp:352:6:352:10 | Break(int) -> void | 8 | 7 | 0 | ... == ... | | | prvalue: bool | ir.cpp:354:13:354:18 | +| ir.cpp:352:6:352:10 | Break(int) -> void | 9 | 8 | 0 | n | | | prvalue(load): int | ir.cpp:354:13:354:13 | +| ir.cpp:352:6:352:10 | Break(int) -> void | 10 | 8 | 1 | 1 | | =1 | prvalue: int | ir.cpp:354:18:354:18 | +| ir.cpp:352:6:352:10 | Break(int) -> void | 11 | 7 | 1 | break; | | | | ir.cpp:355:13:355:18 | +| ir.cpp:352:6:352:10 | Break(int) -> void | 12 | 6 | 1 | ExprStmt | | | | ir.cpp:356:9:356:15 | +| ir.cpp:352:6:352:10 | Break(int) -> void | 13 | 12 | 0 | ... -= ... | | | lvalue: int | ir.cpp:356:9:356:14 | +| ir.cpp:352:6:352:10 | Break(int) -> void | 14 | 13 | 0 | n | | | lvalue: int | ir.cpp:356:9:356:9 | +| ir.cpp:352:6:352:10 | Break(int) -> void | 15 | 13 | 1 | 1 | | =1 | prvalue: int | ir.cpp:356:14:356:14 | +| ir.cpp:352:6:352:10 | Break(int) -> void | 16 | 1 | 1 | label ...: | | | | ir.cpp:357:5:357:5 | +| ir.cpp:352:6:352:10 | Break(int) -> void | 17 | 1 | 2 | return ... | | | | ir.cpp:358:1:358:1 | +| ir.cpp:360:6:360:13 | Continue(int) -> void | 0 | -1 | 0 | Continue | | | | ir.cpp:360:6:360:13 | +| ir.cpp:360:6:360:13 | Continue(int) -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:360:22:367:1 | +| ir.cpp:360:6:360:13 | Continue(int) -> void | 2 | 1 | 0 | do (...) ... | | | | ir.cpp:361:5:366:20 | +| ir.cpp:360:6:360:13 | Continue(int) -> void | 3 | 2 | 0 | ... > ... | | | prvalue: bool | ir.cpp:366:14:366:18 | +| ir.cpp:360:6:360:13 | Continue(int) -> void | 4 | 3 | 0 | n | | | prvalue(load): int | ir.cpp:366:14:366:14 | +| ir.cpp:360:6:360:13 | Continue(int) -> void | 5 | 3 | 1 | 0 | | =0 | prvalue: int | ir.cpp:366:18:366:18 | +| ir.cpp:360:6:360:13 | Continue(int) -> void | 6 | 2 | 1 | { ... } | | | | ir.cpp:361:8:366:5 | +| ir.cpp:360:6:360:13 | Continue(int) -> void | 7 | 6 | 0 | if (...) ... | | | | ir.cpp:362:9:364:9 | +| ir.cpp:360:6:360:13 | Continue(int) -> void | 8 | 7 | 0 | ... == ... | | | prvalue: bool | ir.cpp:362:13:362:18 | +| ir.cpp:360:6:360:13 | Continue(int) -> void | 9 | 8 | 0 | n | | | prvalue(load): int | ir.cpp:362:13:362:13 | +| ir.cpp:360:6:360:13 | Continue(int) -> void | 10 | 8 | 1 | 1 | | =1 | prvalue: int | ir.cpp:362:18:362:18 | +| ir.cpp:360:6:360:13 | Continue(int) -> void | 11 | 7 | 1 | { ... } | | | | ir.cpp:362:21:364:9 | +| ir.cpp:360:6:360:13 | Continue(int) -> void | 12 | 11 | 0 | continue; | | | | ir.cpp:363:13:363:21 | +| ir.cpp:360:6:360:13 | Continue(int) -> void | 13 | 6 | 1 | ExprStmt | | | | ir.cpp:365:9:365:15 | +| ir.cpp:360:6:360:13 | Continue(int) -> void | 14 | 13 | 0 | ... -= ... | | | lvalue: int | ir.cpp:365:9:365:14 | +| ir.cpp:360:6:360:13 | Continue(int) -> void | 15 | 14 | 0 | n | | | lvalue: int | ir.cpp:365:9:365:9 | +| ir.cpp:360:6:360:13 | Continue(int) -> void | 16 | 14 | 1 | 1 | | =1 | prvalue: int | ir.cpp:365:14:365:14 | +| ir.cpp:360:6:360:13 | Continue(int) -> void | 17 | 6 | 2 | label ...: | | | | ir.cpp:361:5:361:5 | +| ir.cpp:360:6:360:13 | Continue(int) -> void | 18 | 1 | 1 | return ... | | | | ir.cpp:367:1:367:1 | +| ir.cpp:369:6:369:13 | VoidFunc() -> void | 0 | -1 | 0 | VoidFunc | | | | ir.cpp:369:6:369:13 | +| ir.cpp:370:5:370:7 | Add(int, int) -> int | 0 | -1 | 0 | Add | | | | ir.cpp:370:5:370:7 | +| ir.cpp:372:6:372:9 | Call() -> void | 0 | -1 | 0 | Call | | | | ir.cpp:372:6:372:9 | +| ir.cpp:372:6:372:9 | Call() -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:372:13:374:1 | +| ir.cpp:372:6:372:9 | Call() -> void | 2 | 1 | 0 | ExprStmt | | | | ir.cpp:373:5:373:15 | +| ir.cpp:372:6:372:9 | Call() -> void | 3 | 2 | 0 | call to VoidFunc | | | prvalue: void | ir.cpp:373:5:373:12 | +| ir.cpp:372:6:372:9 | Call() -> void | 4 | 1 | 1 | return ... | | | | ir.cpp:374:1:374:1 | +| ir.cpp:376:5:376:11 | CallAdd(int, int) -> int | 0 | -1 | 0 | CallAdd | | | | ir.cpp:376:5:376:11 | +| ir.cpp:376:5:376:11 | CallAdd(int, int) -> int | 1 | 0 | 0 | { ... } | | | | ir.cpp:376:27:378:1 | +| ir.cpp:376:5:376:11 | CallAdd(int, int) -> int | 2 | 1 | 0 | return ... | | | | ir.cpp:377:5:377:21 | +| ir.cpp:376:5:376:11 | CallAdd(int, int) -> int | 3 | 2 | 0 | call to Add | | | prvalue: int | ir.cpp:377:12:377:14 | +| ir.cpp:376:5:376:11 | CallAdd(int, int) -> int | 4 | 3 | 0 | x | | | prvalue(load): int | ir.cpp:377:16:377:16 | +| ir.cpp:376:5:376:11 | CallAdd(int, int) -> int | 5 | 3 | 1 | y | | | prvalue(load): int | ir.cpp:377:19:377:19 | +| ir.cpp:380:5:380:9 | Comma(int, int) -> int | 0 | -1 | 0 | Comma | | | | ir.cpp:380:5:380:9 | +| ir.cpp:380:5:380:9 | Comma(int, int) -> int | 1 | 0 | 0 | { ... } | | | | ir.cpp:380:25:382:1 | +| ir.cpp:380:5:380:9 | Comma(int, int) -> int | 2 | 1 | 0 | return ... | | | | ir.cpp:381:5:381:37 | +| ir.cpp:380:5:380:9 | Comma(int, int) -> int | 3 | 2 | 0 | ... , ... | | | prvalue: int | ir.cpp:381:12:381:36 | +| ir.cpp:380:5:380:9 | Comma(int, int) -> int | 4 | 3 | 0 | call to VoidFunc | | | prvalue: void | ir.cpp:381:12:381:19 | +| ir.cpp:380:5:380:9 | Comma(int, int) -> int | 5 | 3 | 1 | call to CallAdd | | | prvalue: int | ir.cpp:381:24:381:30 | +| ir.cpp:380:5:380:9 | Comma(int, int) -> int | 6 | 5 | 0 | x | | | prvalue(load): int | ir.cpp:381:32:381:32 | +| ir.cpp:380:5:380:9 | Comma(int, int) -> int | 7 | 5 | 1 | y | | | prvalue(load): int | ir.cpp:381:35:381:35 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 0 | -1 | 0 | Switch | | | | ir.cpp:384:6:384:11 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:384:20:410:1 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:385:5:385:10 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 3 | 2 | 0 | definition of y | | | int | ir.cpp:385:9:385:9 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 4 | 1 | 1 | switch (...) ... | | | | ir.cpp:386:5:409:5 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 5 | 4 | 0 | x | | | prvalue(load): int | ir.cpp:386:13:386:13 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 6 | 4 | 1 | { ... } | | | | ir.cpp:386:16:409:5 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 7 | 6 | 0 | ExprStmt | | | | ir.cpp:387:9:387:17 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 8 | 7 | 0 | ... = ... | | | lvalue: int | ir.cpp:387:9:387:16 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 9 | 8 | 0 | y | | | lvalue: int | ir.cpp:387:9:387:9 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 10 | 8 | 1 | 1234 | | =1234 | prvalue: int | ir.cpp:387:13:387:16 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 11 | 6 | 1 | case ...: | | | | ir.cpp:389:9:389:16 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 12 | 11 | 0 | - ... | | =-1 | prvalue: int | ir.cpp:389:14:389:15 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 13 | 12 | 0 | 1 | | =1 | prvalue: int | ir.cpp:389:15:389:15 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 14 | 6 | 2 | ExprStmt | | | | ir.cpp:390:13:390:19 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 15 | 14 | 0 | ... = ... | | | lvalue: int | ir.cpp:390:13:390:18 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 16 | 15 | 0 | y | | | lvalue: int | ir.cpp:390:13:390:13 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 17 | 15 | 1 | - ... | | =-1 | prvalue: int | ir.cpp:390:17:390:18 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 18 | 17 | 0 | 1 | | =1 | prvalue: int | ir.cpp:390:18:390:18 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 19 | 6 | 3 | break; | | | | ir.cpp:391:13:391:18 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 20 | 6 | 4 | case ...: | | | | ir.cpp:393:9:393:15 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 21 | 20 | 0 | 1 | | =1 | prvalue: int | ir.cpp:393:14:393:14 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 22 | 6 | 5 | case ...: | | | | ir.cpp:394:9:394:15 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 23 | 22 | 0 | 2 | | =2 | prvalue: int | ir.cpp:394:14:394:14 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 24 | 6 | 6 | ExprStmt | | | | ir.cpp:395:13:395:18 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 25 | 24 | 0 | ... = ... | | | lvalue: int | ir.cpp:395:13:395:17 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 26 | 25 | 0 | y | | | lvalue: int | ir.cpp:395:13:395:13 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 27 | 25 | 1 | 1 | | =1 | prvalue: int | ir.cpp:395:17:395:17 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 28 | 6 | 7 | break; | | | | ir.cpp:396:13:396:18 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 29 | 6 | 8 | case ...: | | | | ir.cpp:398:9:398:15 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 30 | 29 | 0 | 3 | | =3 | prvalue: int | ir.cpp:398:14:398:14 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 31 | 6 | 9 | ExprStmt | | | | ir.cpp:399:13:399:18 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 32 | 31 | 0 | ... = ... | | | lvalue: int | ir.cpp:399:13:399:17 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 33 | 32 | 0 | y | | | lvalue: int | ir.cpp:399:13:399:13 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 34 | 32 | 1 | 3 | | =3 | prvalue: int | ir.cpp:399:17:399:17 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 35 | 6 | 10 | case ...: | | | | ir.cpp:400:9:400:15 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 36 | 35 | 0 | 4 | | =4 | prvalue: int | ir.cpp:400:14:400:14 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 37 | 6 | 11 | ExprStmt | | | | ir.cpp:401:13:401:18 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 38 | 37 | 0 | ... = ... | | | lvalue: int | ir.cpp:401:13:401:17 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 39 | 38 | 0 | y | | | lvalue: int | ir.cpp:401:13:401:13 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 40 | 38 | 1 | 4 | | =4 | prvalue: int | ir.cpp:401:17:401:17 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 41 | 6 | 12 | break; | | | | ir.cpp:402:13:402:18 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 42 | 6 | 13 | default: | | | | ir.cpp:404:9:404:16 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 43 | 6 | 14 | ExprStmt | | | | ir.cpp:405:13:405:18 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 44 | 43 | 0 | ... = ... | | | lvalue: int | ir.cpp:405:13:405:17 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 45 | 44 | 0 | y | | | lvalue: int | ir.cpp:405:13:405:13 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 46 | 44 | 1 | 0 | | =0 | prvalue: int | ir.cpp:405:17:405:17 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 47 | 6 | 15 | break; | | | | ir.cpp:406:13:406:18 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 48 | 6 | 16 | ExprStmt | | | | ir.cpp:408:9:408:17 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 49 | 48 | 0 | ... = ... | | | lvalue: int | ir.cpp:408:9:408:16 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 50 | 49 | 0 | y | | | lvalue: int | ir.cpp:408:9:408:9 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 51 | 49 | 1 | 5678 | | =5678 | prvalue: int | ir.cpp:408:13:408:16 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 52 | 1 | 2 | label ...: | | | | ir.cpp:409:5:409:5 | +| ir.cpp:384:6:384:11 | Switch(int) -> void | 53 | 1 | 3 | return ... | | | | ir.cpp:410:1:410:1 | +| ir.cpp:412:8:412:8 | Point::operator=(Point &&) -> Point & | 0 | -1 | 0 | operator= | | | | ir.cpp:412:8:412:8 | +| ir.cpp:412:8:412:8 | Point::operator=(const Point &) -> Point & | 0 | -1 | 0 | operator= | | | | ir.cpp:412:8:412:8 | +| ir.cpp:417:8:417:8 | Rect::operator=(Rect &&) -> Rect & | 0 | -1 | 0 | operator= | | | | ir.cpp:417:8:417:8 | +| ir.cpp:417:8:417:8 | Rect::operator=(const Rect &) -> Rect & | 0 | -1 | 0 | operator= | | | | ir.cpp:417:8:417:8 | +| ir.cpp:422:7:422:18 | ReturnStruct(Point) -> Point | 0 | -1 | 0 | ReturnStruct | | | | ir.cpp:422:7:422:18 | +| ir.cpp:422:7:422:18 | ReturnStruct(Point) -> Point | 1 | 0 | 0 | { ... } | | | | ir.cpp:422:30:424:1 | +| ir.cpp:422:7:422:18 | ReturnStruct(Point) -> Point | 2 | 1 | 0 | return ... | | | | ir.cpp:423:5:423:14 | +| ir.cpp:422:7:422:18 | ReturnStruct(Point) -> Point | 3 | 2 | 0 | pt | | | prvalue(load): Point | ir.cpp:423:12:423:13 | +| ir.cpp:426:6:426:16 | FieldAccess() -> void | 0 | -1 | 0 | FieldAccess | | | | ir.cpp:426:6:426:16 | +| ir.cpp:426:6:426:16 | FieldAccess() -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:426:20:431:1 | +| ir.cpp:426:6:426:16 | FieldAccess() -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:427:5:427:13 | +| ir.cpp:426:6:426:16 | FieldAccess() -> void | 3 | 2 | 0 | definition of pt | | | Point | ir.cpp:427:11:427:12 | +| ir.cpp:426:6:426:16 | FieldAccess() -> void | 4 | 1 | 1 | ExprStmt | | | | ir.cpp:428:5:428:13 | +| ir.cpp:426:6:426:16 | FieldAccess() -> void | 5 | 4 | 0 | ... = ... | | | lvalue: int | ir.cpp:428:5:428:12 | +| ir.cpp:426:6:426:16 | FieldAccess() -> void | 6 | 5 | 0 | x | | | lvalue: int | ir.cpp:428:8:428:8 | +| ir.cpp:426:6:426:16 | FieldAccess() -> void | 7 | 6 | -1 | pt | | | lvalue: Point | ir.cpp:428:5:428:6 | +| ir.cpp:426:6:426:16 | FieldAccess() -> void | 8 | 5 | 1 | 5 | | =5 | prvalue: int | ir.cpp:428:12:428:12 | +| ir.cpp:426:6:426:16 | FieldAccess() -> void | 9 | 1 | 2 | ExprStmt | | | | ir.cpp:429:5:429:16 | +| ir.cpp:426:6:426:16 | FieldAccess() -> void | 10 | 9 | 0 | ... = ... | | | lvalue: int | ir.cpp:429:5:429:15 | +| ir.cpp:426:6:426:16 | FieldAccess() -> void | 11 | 10 | 0 | y | | | lvalue: int | ir.cpp:429:8:429:8 | +| ir.cpp:426:6:426:16 | FieldAccess() -> void | 12 | 11 | -1 | pt | | | lvalue: Point | ir.cpp:429:5:429:6 | +| ir.cpp:426:6:426:16 | FieldAccess() -> void | 13 | 10 | 1 | x | | | prvalue(load): int | ir.cpp:429:15:429:15 | +| ir.cpp:426:6:426:16 | FieldAccess() -> void | 14 | 13 | -1 | pt | | | lvalue: Point | ir.cpp:429:12:429:13 | +| ir.cpp:426:6:426:16 | FieldAccess() -> void | 15 | 1 | 3 | declaration | | | | ir.cpp:430:5:430:19 | +| ir.cpp:426:6:426:16 | FieldAccess() -> void | 16 | 15 | 0 | definition of p | | | int * | ir.cpp:430:10:430:10 | +| ir.cpp:426:6:426:16 | FieldAccess() -> void | 17 | 16 | 0 | initializer for p | | | | ir.cpp:430:13:430:18 | +| ir.cpp:426:6:426:16 | FieldAccess() -> void | 18 | 17 | 0 | & ... | | | prvalue: int * | ir.cpp:430:14:430:18 | +| ir.cpp:426:6:426:16 | FieldAccess() -> void | 19 | 18 | 0 | y | | | lvalue: int | ir.cpp:430:18:430:18 | +| ir.cpp:426:6:426:16 | FieldAccess() -> void | 20 | 19 | -1 | pt | | | lvalue: Point | ir.cpp:430:15:430:16 | +| ir.cpp:426:6:426:16 | FieldAccess() -> void | 21 | 1 | 4 | return ... | | | | ir.cpp:431:1:431:1 | +| ir.cpp:433:6:433:14 | LogicalOr(bool, bool) -> void | 0 | -1 | 0 | LogicalOr | | | | ir.cpp:433:6:433:14 | +| ir.cpp:433:6:433:14 | LogicalOr(bool, bool) -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:433:32:445:1 | +| ir.cpp:433:6:433:14 | LogicalOr(bool, bool) -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:434:5:434:10 | +| ir.cpp:433:6:433:14 | LogicalOr(bool, bool) -> void | 3 | 2 | 0 | definition of x | | | int | ir.cpp:434:9:434:9 | +| ir.cpp:433:6:433:14 | LogicalOr(bool, bool) -> void | 4 | 1 | 1 | if (...) ... | | | | ir.cpp:435:5:437:5 | +| ir.cpp:433:6:433:14 | LogicalOr(bool, bool) -> void | 5 | 4 | 0 | ... \|\| ... | | | prvalue: bool | ir.cpp:435:9:435:14 | +| ir.cpp:433:6:433:14 | LogicalOr(bool, bool) -> void | 6 | 5 | 0 | a | | | prvalue(load): bool | ir.cpp:435:9:435:9 | +| ir.cpp:433:6:433:14 | LogicalOr(bool, bool) -> void | 7 | 5 | 1 | b | | | prvalue(load): bool | ir.cpp:435:14:435:14 | +| ir.cpp:433:6:433:14 | LogicalOr(bool, bool) -> void | 8 | 4 | 1 | { ... } | | | | ir.cpp:435:17:437:5 | +| ir.cpp:433:6:433:14 | LogicalOr(bool, bool) -> void | 9 | 8 | 0 | ExprStmt | | | | ir.cpp:436:9:436:14 | +| ir.cpp:433:6:433:14 | LogicalOr(bool, bool) -> void | 10 | 9 | 0 | ... = ... | | | lvalue: int | ir.cpp:436:9:436:13 | +| ir.cpp:433:6:433:14 | LogicalOr(bool, bool) -> void | 11 | 10 | 0 | x | | | lvalue: int | ir.cpp:436:9:436:9 | +| ir.cpp:433:6:433:14 | LogicalOr(bool, bool) -> void | 12 | 10 | 1 | 7 | | =7 | prvalue: int | ir.cpp:436:13:436:13 | +| ir.cpp:433:6:433:14 | LogicalOr(bool, bool) -> void | 13 | 1 | 2 | if (...) ... | | | | ir.cpp:439:5:444:5 | +| ir.cpp:433:6:433:14 | LogicalOr(bool, bool) -> void | 14 | 13 | 0 | ... \|\| ... | | | prvalue: bool | ir.cpp:439:9:439:14 | +| ir.cpp:433:6:433:14 | LogicalOr(bool, bool) -> void | 15 | 14 | 0 | a | | | prvalue(load): bool | ir.cpp:439:9:439:9 | +| ir.cpp:433:6:433:14 | LogicalOr(bool, bool) -> void | 16 | 14 | 1 | b | | | prvalue(load): bool | ir.cpp:439:14:439:14 | +| ir.cpp:433:6:433:14 | LogicalOr(bool, bool) -> void | 17 | 13 | 1 | { ... } | | | | ir.cpp:439:17:441:5 | +| ir.cpp:433:6:433:14 | LogicalOr(bool, bool) -> void | 18 | 17 | 0 | ExprStmt | | | | ir.cpp:440:9:440:14 | +| ir.cpp:433:6:433:14 | LogicalOr(bool, bool) -> void | 19 | 18 | 0 | ... = ... | | | lvalue: int | ir.cpp:440:9:440:13 | +| ir.cpp:433:6:433:14 | LogicalOr(bool, bool) -> void | 20 | 19 | 0 | x | | | lvalue: int | ir.cpp:440:9:440:9 | +| ir.cpp:433:6:433:14 | LogicalOr(bool, bool) -> void | 21 | 19 | 1 | 1 | | =1 | prvalue: int | ir.cpp:440:13:440:13 | +| ir.cpp:433:6:433:14 | LogicalOr(bool, bool) -> void | 22 | 13 | 2 | { ... } | | | | ir.cpp:442:10:444:5 | +| ir.cpp:433:6:433:14 | LogicalOr(bool, bool) -> void | 23 | 22 | 0 | ExprStmt | | | | ir.cpp:443:9:443:14 | +| ir.cpp:433:6:433:14 | LogicalOr(bool, bool) -> void | 24 | 23 | 0 | ... = ... | | | lvalue: int | ir.cpp:443:9:443:13 | +| ir.cpp:433:6:433:14 | LogicalOr(bool, bool) -> void | 25 | 24 | 0 | x | | | lvalue: int | ir.cpp:443:9:443:9 | +| ir.cpp:433:6:433:14 | LogicalOr(bool, bool) -> void | 26 | 24 | 1 | 5 | | =5 | prvalue: int | ir.cpp:443:13:443:13 | +| ir.cpp:433:6:433:14 | LogicalOr(bool, bool) -> void | 27 | 1 | 3 | return ... | | | | ir.cpp:445:1:445:1 | +| ir.cpp:447:6:447:15 | LogicalAnd(bool, bool) -> void | 0 | -1 | 0 | LogicalAnd | | | | ir.cpp:447:6:447:15 | +| ir.cpp:447:6:447:15 | LogicalAnd(bool, bool) -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:447:33:459:1 | +| ir.cpp:447:6:447:15 | LogicalAnd(bool, bool) -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:448:5:448:10 | +| ir.cpp:447:6:447:15 | LogicalAnd(bool, bool) -> void | 3 | 2 | 0 | definition of x | | | int | ir.cpp:448:9:448:9 | +| ir.cpp:447:6:447:15 | LogicalAnd(bool, bool) -> void | 4 | 1 | 1 | if (...) ... | | | | ir.cpp:449:5:451:5 | +| ir.cpp:447:6:447:15 | LogicalAnd(bool, bool) -> void | 5 | 4 | 0 | ... && ... | | | prvalue: bool | ir.cpp:449:9:449:14 | +| ir.cpp:447:6:447:15 | LogicalAnd(bool, bool) -> void | 6 | 5 | 0 | a | | | prvalue(load): bool | ir.cpp:449:9:449:9 | +| ir.cpp:447:6:447:15 | LogicalAnd(bool, bool) -> void | 7 | 5 | 1 | b | | | prvalue(load): bool | ir.cpp:449:14:449:14 | +| ir.cpp:447:6:447:15 | LogicalAnd(bool, bool) -> void | 8 | 4 | 1 | { ... } | | | | ir.cpp:449:17:451:5 | +| ir.cpp:447:6:447:15 | LogicalAnd(bool, bool) -> void | 9 | 8 | 0 | ExprStmt | | | | ir.cpp:450:9:450:14 | +| ir.cpp:447:6:447:15 | LogicalAnd(bool, bool) -> void | 10 | 9 | 0 | ... = ... | | | lvalue: int | ir.cpp:450:9:450:13 | +| ir.cpp:447:6:447:15 | LogicalAnd(bool, bool) -> void | 11 | 10 | 0 | x | | | lvalue: int | ir.cpp:450:9:450:9 | +| ir.cpp:447:6:447:15 | LogicalAnd(bool, bool) -> void | 12 | 10 | 1 | 7 | | =7 | prvalue: int | ir.cpp:450:13:450:13 | +| ir.cpp:447:6:447:15 | LogicalAnd(bool, bool) -> void | 13 | 1 | 2 | if (...) ... | | | | ir.cpp:453:5:458:5 | +| ir.cpp:447:6:447:15 | LogicalAnd(bool, bool) -> void | 14 | 13 | 0 | ... && ... | | | prvalue: bool | ir.cpp:453:9:453:14 | +| ir.cpp:447:6:447:15 | LogicalAnd(bool, bool) -> void | 15 | 14 | 0 | a | | | prvalue(load): bool | ir.cpp:453:9:453:9 | +| ir.cpp:447:6:447:15 | LogicalAnd(bool, bool) -> void | 16 | 14 | 1 | b | | | prvalue(load): bool | ir.cpp:453:14:453:14 | +| ir.cpp:447:6:447:15 | LogicalAnd(bool, bool) -> void | 17 | 13 | 1 | { ... } | | | | ir.cpp:453:17:455:5 | +| ir.cpp:447:6:447:15 | LogicalAnd(bool, bool) -> void | 18 | 17 | 0 | ExprStmt | | | | ir.cpp:454:9:454:14 | +| ir.cpp:447:6:447:15 | LogicalAnd(bool, bool) -> void | 19 | 18 | 0 | ... = ... | | | lvalue: int | ir.cpp:454:9:454:13 | +| ir.cpp:447:6:447:15 | LogicalAnd(bool, bool) -> void | 20 | 19 | 0 | x | | | lvalue: int | ir.cpp:454:9:454:9 | +| ir.cpp:447:6:447:15 | LogicalAnd(bool, bool) -> void | 21 | 19 | 1 | 1 | | =1 | prvalue: int | ir.cpp:454:13:454:13 | +| ir.cpp:447:6:447:15 | LogicalAnd(bool, bool) -> void | 22 | 13 | 2 | { ... } | | | | ir.cpp:456:10:458:5 | +| ir.cpp:447:6:447:15 | LogicalAnd(bool, bool) -> void | 23 | 22 | 0 | ExprStmt | | | | ir.cpp:457:9:457:14 | +| ir.cpp:447:6:447:15 | LogicalAnd(bool, bool) -> void | 24 | 23 | 0 | ... = ... | | | lvalue: int | ir.cpp:457:9:457:13 | +| ir.cpp:447:6:447:15 | LogicalAnd(bool, bool) -> void | 25 | 24 | 0 | x | | | lvalue: int | ir.cpp:457:9:457:9 | +| ir.cpp:447:6:447:15 | LogicalAnd(bool, bool) -> void | 26 | 24 | 1 | 5 | | =5 | prvalue: int | ir.cpp:457:13:457:13 | +| ir.cpp:447:6:447:15 | LogicalAnd(bool, bool) -> void | 27 | 1 | 3 | return ... | | | | ir.cpp:459:1:459:1 | +| ir.cpp:461:6:461:15 | LogicalNot(bool, bool) -> void | 0 | -1 | 0 | LogicalNot | | | | ir.cpp:461:6:461:15 | +| ir.cpp:461:6:461:15 | LogicalNot(bool, bool) -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:461:33:473:1 | +| ir.cpp:461:6:461:15 | LogicalNot(bool, bool) -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:462:5:462:10 | +| ir.cpp:461:6:461:15 | LogicalNot(bool, bool) -> void | 3 | 2 | 0 | definition of x | | | int | ir.cpp:462:9:462:9 | +| ir.cpp:461:6:461:15 | LogicalNot(bool, bool) -> void | 4 | 1 | 1 | if (...) ... | | | | ir.cpp:463:5:465:5 | +| ir.cpp:461:6:461:15 | LogicalNot(bool, bool) -> void | 5 | 4 | 0 | ! ... | | | prvalue: bool | ir.cpp:463:9:463:10 | +| ir.cpp:461:6:461:15 | LogicalNot(bool, bool) -> void | 6 | 5 | 0 | a | | | prvalue(load): bool | ir.cpp:463:10:463:10 | +| ir.cpp:461:6:461:15 | LogicalNot(bool, bool) -> void | 7 | 4 | 1 | { ... } | | | | ir.cpp:463:13:465:5 | +| ir.cpp:461:6:461:15 | LogicalNot(bool, bool) -> void | 8 | 7 | 0 | ExprStmt | | | | ir.cpp:464:9:464:14 | +| ir.cpp:461:6:461:15 | LogicalNot(bool, bool) -> void | 9 | 8 | 0 | ... = ... | | | lvalue: int | ir.cpp:464:9:464:13 | +| ir.cpp:461:6:461:15 | LogicalNot(bool, bool) -> void | 10 | 9 | 0 | x | | | lvalue: int | ir.cpp:464:9:464:9 | +| ir.cpp:461:6:461:15 | LogicalNot(bool, bool) -> void | 11 | 9 | 1 | 1 | | =1 | prvalue: int | ir.cpp:464:13:464:13 | +| ir.cpp:461:6:461:15 | LogicalNot(bool, bool) -> void | 12 | 1 | 2 | if (...) ... | | | | ir.cpp:467:5:472:5 | +| ir.cpp:461:6:461:15 | LogicalNot(bool, bool) -> void | 13 | 12 | 0 | ! ... | | | prvalue: bool | ir.cpp:467:9:467:17 | +| ir.cpp:461:6:461:15 | LogicalNot(bool, bool) -> void | 14 | 13 | 0 | (...) | | | prvalue: bool | ir.cpp:467:10:467:17 | +| ir.cpp:461:6:461:15 | LogicalNot(bool, bool) -> void | 15 | 14 | 0 | ... && ... | | | prvalue: bool | ir.cpp:467:11:467:16 | +| ir.cpp:461:6:461:15 | LogicalNot(bool, bool) -> void | 16 | 15 | 0 | a | | | prvalue(load): bool | ir.cpp:467:11:467:11 | +| ir.cpp:461:6:461:15 | LogicalNot(bool, bool) -> void | 17 | 15 | 1 | b | | | prvalue(load): bool | ir.cpp:467:16:467:16 | +| ir.cpp:461:6:461:15 | LogicalNot(bool, bool) -> void | 18 | 12 | 1 | { ... } | | | | ir.cpp:467:20:469:5 | +| ir.cpp:461:6:461:15 | LogicalNot(bool, bool) -> void | 19 | 18 | 0 | ExprStmt | | | | ir.cpp:468:9:468:14 | +| ir.cpp:461:6:461:15 | LogicalNot(bool, bool) -> void | 20 | 19 | 0 | ... = ... | | | lvalue: int | ir.cpp:468:9:468:13 | +| ir.cpp:461:6:461:15 | LogicalNot(bool, bool) -> void | 21 | 20 | 0 | x | | | lvalue: int | ir.cpp:468:9:468:9 | +| ir.cpp:461:6:461:15 | LogicalNot(bool, bool) -> void | 22 | 20 | 1 | 2 | | =2 | prvalue: int | ir.cpp:468:13:468:13 | +| ir.cpp:461:6:461:15 | LogicalNot(bool, bool) -> void | 23 | 12 | 2 | { ... } | | | | ir.cpp:470:10:472:5 | +| ir.cpp:461:6:461:15 | LogicalNot(bool, bool) -> void | 24 | 23 | 0 | ExprStmt | | | | ir.cpp:471:9:471:14 | +| ir.cpp:461:6:461:15 | LogicalNot(bool, bool) -> void | 25 | 24 | 0 | ... = ... | | | lvalue: int | ir.cpp:471:9:471:13 | +| ir.cpp:461:6:461:15 | LogicalNot(bool, bool) -> void | 26 | 25 | 0 | x | | | lvalue: int | ir.cpp:471:9:471:9 | +| ir.cpp:461:6:461:15 | LogicalNot(bool, bool) -> void | 27 | 25 | 1 | 3 | | =3 | prvalue: int | ir.cpp:471:13:471:13 | +| ir.cpp:461:6:461:15 | LogicalNot(bool, bool) -> void | 28 | 1 | 3 | return ... | | | | ir.cpp:473:1:473:1 | +| ir.cpp:475:6:475:20 | ConditionValues(bool, bool) -> void | 0 | -1 | 0 | ConditionValues | | | | ir.cpp:475:6:475:20 | +| ir.cpp:475:6:475:20 | ConditionValues(bool, bool) -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:475:38:480:1 | +| ir.cpp:475:6:475:20 | ConditionValues(bool, bool) -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:476:5:476:11 | +| ir.cpp:475:6:475:20 | ConditionValues(bool, bool) -> void | 3 | 2 | 0 | definition of x | | | bool | ir.cpp:476:10:476:10 | +| ir.cpp:475:6:475:20 | ConditionValues(bool, bool) -> void | 4 | 1 | 1 | ExprStmt | | | | ir.cpp:477:5:477:15 | +| ir.cpp:475:6:475:20 | ConditionValues(bool, bool) -> void | 5 | 4 | 0 | ... = ... | | | lvalue: bool | ir.cpp:477:5:477:14 | +| ir.cpp:475:6:475:20 | ConditionValues(bool, bool) -> void | 6 | 5 | 0 | x | | | lvalue: bool | ir.cpp:477:5:477:5 | +| ir.cpp:475:6:475:20 | ConditionValues(bool, bool) -> void | 7 | 5 | 1 | ... && ... | | | prvalue: bool | ir.cpp:477:9:477:14 | +| ir.cpp:475:6:475:20 | ConditionValues(bool, bool) -> void | 8 | 7 | 0 | a | | | prvalue(load): bool | ir.cpp:477:9:477:9 | +| ir.cpp:475:6:475:20 | ConditionValues(bool, bool) -> void | 9 | 7 | 1 | b | | | prvalue(load): bool | ir.cpp:477:14:477:14 | +| ir.cpp:475:6:475:20 | ConditionValues(bool, bool) -> void | 10 | 1 | 2 | ExprStmt | | | | ir.cpp:478:5:478:15 | +| ir.cpp:475:6:475:20 | ConditionValues(bool, bool) -> void | 11 | 10 | 0 | ... = ... | | | lvalue: bool | ir.cpp:478:5:478:14 | +| ir.cpp:475:6:475:20 | ConditionValues(bool, bool) -> void | 12 | 11 | 0 | x | | | lvalue: bool | ir.cpp:478:5:478:5 | +| ir.cpp:475:6:475:20 | ConditionValues(bool, bool) -> void | 13 | 11 | 1 | ... \|\| ... | | | prvalue: bool | ir.cpp:478:9:478:14 | +| ir.cpp:475:6:475:20 | ConditionValues(bool, bool) -> void | 14 | 13 | 0 | a | | | prvalue(load): bool | ir.cpp:478:9:478:9 | +| ir.cpp:475:6:475:20 | ConditionValues(bool, bool) -> void | 15 | 13 | 1 | b | | | prvalue(load): bool | ir.cpp:478:14:478:14 | +| ir.cpp:475:6:475:20 | ConditionValues(bool, bool) -> void | 16 | 1 | 3 | ExprStmt | | | | ir.cpp:479:5:479:18 | +| ir.cpp:475:6:475:20 | ConditionValues(bool, bool) -> void | 17 | 16 | 0 | ... = ... | | | lvalue: bool | ir.cpp:479:5:479:17 | +| ir.cpp:475:6:475:20 | ConditionValues(bool, bool) -> void | 18 | 17 | 0 | x | | | lvalue: bool | ir.cpp:479:5:479:5 | +| ir.cpp:475:6:475:20 | ConditionValues(bool, bool) -> void | 19 | 17 | 1 | ! ... | | | prvalue: bool | ir.cpp:479:9:479:17 | +| ir.cpp:475:6:475:20 | ConditionValues(bool, bool) -> void | 20 | 19 | 0 | (...) | | | prvalue: bool | ir.cpp:479:10:479:17 | +| ir.cpp:475:6:475:20 | ConditionValues(bool, bool) -> void | 21 | 20 | 0 | ... \|\| ... | | | prvalue: bool | ir.cpp:479:11:479:16 | +| ir.cpp:475:6:475:20 | ConditionValues(bool, bool) -> void | 22 | 21 | 0 | a | | | prvalue(load): bool | ir.cpp:479:11:479:11 | +| ir.cpp:475:6:475:20 | ConditionValues(bool, bool) -> void | 23 | 21 | 1 | b | | | prvalue(load): bool | ir.cpp:479:16:479:16 | +| ir.cpp:475:6:475:20 | ConditionValues(bool, bool) -> void | 24 | 1 | 4 | return ... | | | | ir.cpp:480:1:480:1 | +| ir.cpp:482:6:482:16 | Conditional(bool, int, int) -> void | 0 | -1 | 0 | Conditional | | | | ir.cpp:482:6:482:16 | +| ir.cpp:482:6:482:16 | Conditional(bool, int, int) -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:482:40:484:1 | +| ir.cpp:482:6:482:16 | Conditional(bool, int, int) -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:483:5:483:22 | +| ir.cpp:482:6:482:16 | Conditional(bool, int, int) -> void | 3 | 2 | 0 | definition of z | | | int | ir.cpp:483:9:483:9 | +| ir.cpp:482:6:482:16 | Conditional(bool, int, int) -> void | 4 | 3 | 0 | initializer for z | | | | ir.cpp:483:12:483:21 | +| ir.cpp:482:6:482:16 | Conditional(bool, int, int) -> void | 5 | 4 | 0 | ... ? ... : ... | | | prvalue: int | ir.cpp:483:13:483:21 | +| ir.cpp:482:6:482:16 | Conditional(bool, int, int) -> void | 6 | 5 | 0 | a | | | prvalue(load): bool | ir.cpp:483:13:483:13 | +| ir.cpp:482:6:482:16 | Conditional(bool, int, int) -> void | 7 | 5 | 1 | x | | | prvalue(load): int | ir.cpp:483:17:483:17 | +| ir.cpp:482:6:482:16 | Conditional(bool, int, int) -> void | 8 | 5 | 2 | y | | | prvalue(load): int | ir.cpp:483:21:483:21 | +| ir.cpp:482:6:482:16 | Conditional(bool, int, int) -> void | 9 | 1 | 1 | return ... | | | | ir.cpp:484:1:484:1 | +| ir.cpp:486:6:486:23 | Conditional_LValue(bool) -> void | 0 | -1 | 0 | Conditional_LValue | | | | ir.cpp:486:6:486:23 | +| ir.cpp:486:6:486:23 | Conditional_LValue(bool) -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:486:33:490:1 | +| ir.cpp:486:6:486:23 | Conditional_LValue(bool) -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:487:5:487:10 | +| ir.cpp:486:6:486:23 | Conditional_LValue(bool) -> void | 3 | 2 | 0 | definition of x | | | int | ir.cpp:487:9:487:9 | +| ir.cpp:486:6:486:23 | Conditional_LValue(bool) -> void | 4 | 1 | 1 | declaration | | | | ir.cpp:488:5:488:10 | +| ir.cpp:486:6:486:23 | Conditional_LValue(bool) -> void | 5 | 4 | 0 | definition of y | | | int | ir.cpp:488:9:488:9 | +| ir.cpp:486:6:486:23 | Conditional_LValue(bool) -> void | 6 | 1 | 2 | ExprStmt | | | | ir.cpp:489:5:489:20 | +| ir.cpp:486:6:486:23 | Conditional_LValue(bool) -> void | 7 | 6 | 0 | ... = ... | | | lvalue: int | ir.cpp:489:5:489:19 | +| ir.cpp:486:6:486:23 | Conditional_LValue(bool) -> void | 8 | 7 | 0 | (...) | | | lvalue: int | ir.cpp:489:5:489:15 | +| ir.cpp:486:6:486:23 | Conditional_LValue(bool) -> void | 9 | 8 | 0 | ... ? ... : ... | | | lvalue: int | ir.cpp:489:6:489:14 | +| ir.cpp:486:6:486:23 | Conditional_LValue(bool) -> void | 10 | 9 | 0 | a | | | prvalue(load): bool | ir.cpp:489:6:489:6 | +| ir.cpp:486:6:486:23 | Conditional_LValue(bool) -> void | 11 | 9 | 1 | x | | | lvalue: int | ir.cpp:489:10:489:10 | +| ir.cpp:486:6:486:23 | Conditional_LValue(bool) -> void | 12 | 9 | 2 | y | | | lvalue: int | ir.cpp:489:14:489:14 | +| ir.cpp:486:6:486:23 | Conditional_LValue(bool) -> void | 13 | 7 | 1 | 5 | | =5 | prvalue: int | ir.cpp:489:19:489:19 | +| ir.cpp:486:6:486:23 | Conditional_LValue(bool) -> void | 14 | 1 | 3 | return ... | | | | ir.cpp:490:1:490:1 | +| ir.cpp:492:6:492:21 | Conditional_Void(bool) -> void | 0 | -1 | 0 | Conditional_Void | | | | ir.cpp:492:6:492:21 | +| ir.cpp:492:6:492:21 | Conditional_Void(bool) -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:492:31:494:1 | +| ir.cpp:492:6:492:21 | Conditional_Void(bool) -> void | 2 | 1 | 0 | ExprStmt | | | | ir.cpp:493:5:493:32 | +| ir.cpp:492:6:492:21 | Conditional_Void(bool) -> void | 3 | 2 | 0 | ... ? ... : ... | | | prvalue: void | ir.cpp:493:5:493:31 | +| ir.cpp:492:6:492:21 | Conditional_Void(bool) -> void | 4 | 3 | 0 | a | | | prvalue(load): bool | ir.cpp:493:5:493:5 | +| ir.cpp:492:6:492:21 | Conditional_Void(bool) -> void | 5 | 3 | 1 | call to VoidFunc | | | prvalue: void | ir.cpp:493:9:493:16 | +| ir.cpp:492:6:492:21 | Conditional_Void(bool) -> void | 6 | 3 | 2 | call to VoidFunc | | | prvalue: void | ir.cpp:493:22:493:29 | +| ir.cpp:492:6:492:21 | Conditional_Void(bool) -> void | 7 | 1 | 1 | return ... | | | | ir.cpp:494:1:494:1 | +| ir.cpp:496:6:496:12 | Nullptr() -> void | 0 | -1 | 0 | Nullptr | | | | ir.cpp:496:6:496:12 | +| ir.cpp:496:6:496:12 | Nullptr() -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:496:16:501:1 | +| ir.cpp:496:6:496:12 | Nullptr() -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:497:5:497:21 | +| ir.cpp:496:6:496:12 | Nullptr() -> void | 3 | 2 | 0 | definition of p | | | int * | ir.cpp:497:10:497:10 | +| ir.cpp:496:6:496:12 | Nullptr() -> void | 4 | 3 | 0 | initializer for p | | | | ir.cpp:497:13:497:20 | +| ir.cpp:496:6:496:12 | Nullptr() -> void | 5 | 4 | 0 | (int *)... | pointer conversion | =0 | prvalue: int * | ir.cpp:497:14:497:20 | +| ir.cpp:496:6:496:12 | Nullptr() -> void | 6 | 5 | 0 | 0 | | =0 | prvalue: decltype(nullptr) | ir.cpp:497:14:497:20 | +| ir.cpp:496:6:496:12 | Nullptr() -> void | 7 | 1 | 1 | declaration | | | | ir.cpp:498:5:498:15 | +| ir.cpp:496:6:496:12 | Nullptr() -> void | 8 | 7 | 0 | definition of q | | | int * | ir.cpp:498:10:498:10 | +| ir.cpp:496:6:496:12 | Nullptr() -> void | 9 | 8 | 0 | initializer for q | | | | ir.cpp:498:13:498:14 | +| ir.cpp:496:6:496:12 | Nullptr() -> void | 10 | 9 | 0 | (int *)... | integral to pointer conversion | =0 | prvalue: int * | ir.cpp:498:14:498:14 | +| ir.cpp:496:6:496:12 | Nullptr() -> void | 11 | 10 | 0 | 0 | | =0 | prvalue: int | ir.cpp:498:14:498:14 | +| ir.cpp:496:6:496:12 | Nullptr() -> void | 12 | 1 | 2 | ExprStmt | | | | ir.cpp:499:5:499:16 | +| ir.cpp:496:6:496:12 | Nullptr() -> void | 13 | 12 | 0 | ... = ... | | | lvalue: int * | ir.cpp:499:5:499:15 | +| ir.cpp:496:6:496:12 | Nullptr() -> void | 14 | 13 | 0 | p | | | lvalue: int * | ir.cpp:499:5:499:5 | +| ir.cpp:496:6:496:12 | Nullptr() -> void | 15 | 13 | 1 | (int *)... | pointer conversion | =0 | prvalue: int * | ir.cpp:499:9:499:15 | +| ir.cpp:496:6:496:12 | Nullptr() -> void | 16 | 15 | 0 | 0 | | =0 | prvalue: decltype(nullptr) | ir.cpp:499:9:499:15 | +| ir.cpp:496:6:496:12 | Nullptr() -> void | 17 | 1 | 3 | ExprStmt | | | | ir.cpp:500:5:500:10 | +| ir.cpp:496:6:496:12 | Nullptr() -> void | 18 | 17 | 0 | ... = ... | | | lvalue: int * | ir.cpp:500:5:500:9 | +| ir.cpp:496:6:496:12 | Nullptr() -> void | 19 | 18 | 0 | q | | | lvalue: int * | ir.cpp:500:5:500:5 | +| ir.cpp:496:6:496:12 | Nullptr() -> void | 20 | 18 | 1 | (int *)... | integral to pointer conversion | =0 | prvalue: int * | ir.cpp:500:9:500:9 | +| ir.cpp:496:6:496:12 | Nullptr() -> void | 21 | 20 | 0 | 0 | | =0 | prvalue: int | ir.cpp:500:9:500:9 | +| ir.cpp:496:6:496:12 | Nullptr() -> void | 22 | 1 | 4 | return ... | | | | ir.cpp:501:1:501:1 | +| ir.cpp:503:6:503:13 | InitList(int, float) -> void | 0 | -1 | 0 | InitList | | | | ir.cpp:503:6:503:13 | +| ir.cpp:503:6:503:13 | InitList(int, float) -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:503:31:510:1 | +| ir.cpp:503:6:503:13 | InitList(int, float) -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:504:5:504:25 | +| ir.cpp:503:6:503:13 | InitList(int, float) -> void | 3 | 2 | 0 | definition of pt1 | | | Point | ir.cpp:504:11:504:13 | +| ir.cpp:503:6:503:13 | InitList(int, float) -> void | 4 | 3 | 0 | initializer for pt1 | | | | ir.cpp:504:16:504:24 | +| ir.cpp:503:6:503:13 | InitList(int, float) -> void | 5 | 4 | 0 | {...} | | | prvalue: Point | ir.cpp:504:16:504:24 | +| ir.cpp:503:6:503:13 | InitList(int, float) -> void | 6 | 5 | 0 | x | | | prvalue(load): int | ir.cpp:504:19:504:19 | +| ir.cpp:503:6:503:13 | InitList(int, float) -> void | 7 | 5 | 1 | (int)... | floating point to integral conversion | | prvalue: int | ir.cpp:504:22:504:22 | +| ir.cpp:503:6:503:13 | InitList(int, float) -> void | 8 | 7 | 0 | f | | | prvalue(load): float | ir.cpp:504:22:504:22 | +| ir.cpp:503:6:503:13 | InitList(int, float) -> void | 9 | 1 | 1 | declaration | | | | ir.cpp:505:5:505:22 | +| ir.cpp:503:6:503:13 | InitList(int, float) -> void | 10 | 9 | 0 | definition of pt2 | | | Point | ir.cpp:505:11:505:13 | +| ir.cpp:503:6:503:13 | InitList(int, float) -> void | 11 | 10 | 0 | initializer for pt2 | | | | ir.cpp:505:16:505:21 | +| ir.cpp:503:6:503:13 | InitList(int, float) -> void | 12 | 11 | 0 | {...} | | | prvalue: Point | ir.cpp:505:16:505:21 | +| ir.cpp:503:6:503:13 | InitList(int, float) -> void | 13 | 12 | 0 | x | | | prvalue(load): int | ir.cpp:505:19:505:19 | +| ir.cpp:503:6:503:13 | InitList(int, float) -> void | 14 | 1 | 2 | declaration | | | | ir.cpp:506:5:506:19 | +| ir.cpp:503:6:503:13 | InitList(int, float) -> void | 15 | 14 | 0 | definition of pt3 | | | Point | ir.cpp:506:11:506:13 | +| ir.cpp:503:6:503:13 | InitList(int, float) -> void | 16 | 15 | 0 | initializer for pt3 | | | | ir.cpp:506:16:506:18 | +| ir.cpp:503:6:503:13 | InitList(int, float) -> void | 17 | 16 | 0 | {...} | | | prvalue: Point | ir.cpp:506:16:506:18 | +| ir.cpp:503:6:503:13 | InitList(int, float) -> void | 18 | 1 | 3 | declaration | | | | ir.cpp:508:5:508:19 | +| ir.cpp:503:6:503:13 | InitList(int, float) -> void | 19 | 18 | 0 | definition of x1 | | | int | ir.cpp:508:9:508:10 | +| ir.cpp:503:6:503:13 | InitList(int, float) -> void | 20 | 19 | 0 | initializer for x1 | | | | ir.cpp:508:13:508:18 | +| ir.cpp:503:6:503:13 | InitList(int, float) -> void | 21 | 20 | 0 | 1 | | =1 | prvalue: int | ir.cpp:508:13:508:18 | +| ir.cpp:503:6:503:13 | InitList(int, float) -> void | 22 | 1 | 4 | declaration | | | | ir.cpp:509:5:509:16 | +| ir.cpp:503:6:503:13 | InitList(int, float) -> void | 23 | 22 | 0 | definition of x2 | | | int | ir.cpp:509:9:509:10 | +| ir.cpp:503:6:503:13 | InitList(int, float) -> void | 24 | 23 | 0 | initializer for x2 | | | | ir.cpp:509:13:509:15 | +| ir.cpp:503:6:503:13 | InitList(int, float) -> void | 25 | 24 | 0 | 0 | | =0 | prvalue: int | ir.cpp:509:13:509:15 | +| ir.cpp:503:6:503:13 | InitList(int, float) -> void | 26 | 1 | 5 | return ... | | | | ir.cpp:510:1:510:1 | +| ir.cpp:512:6:512:19 | NestedInitList(int, float) -> void | 0 | -1 | 0 | NestedInitList | | | | ir.cpp:512:6:512:19 | +| ir.cpp:512:6:512:19 | NestedInitList(int, float) -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:512:37:517:1 | +| ir.cpp:512:6:512:19 | NestedInitList(int, float) -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:513:5:513:17 | +| ir.cpp:512:6:512:19 | NestedInitList(int, float) -> void | 3 | 2 | 0 | definition of r1 | | | Rect | ir.cpp:513:10:513:11 | +| ir.cpp:512:6:512:19 | NestedInitList(int, float) -> void | 4 | 3 | 0 | initializer for r1 | | | | ir.cpp:513:14:513:16 | +| ir.cpp:512:6:512:19 | NestedInitList(int, float) -> void | 5 | 4 | 0 | {...} | | | prvalue: Rect | ir.cpp:513:14:513:16 | +| ir.cpp:512:6:512:19 | NestedInitList(int, float) -> void | 6 | 1 | 1 | declaration | | | | ir.cpp:514:5:514:27 | +| ir.cpp:512:6:512:19 | NestedInitList(int, float) -> void | 7 | 6 | 0 | definition of r2 | | | Rect | ir.cpp:514:10:514:11 | +| ir.cpp:512:6:512:19 | NestedInitList(int, float) -> void | 8 | 7 | 0 | initializer for r2 | | | | ir.cpp:514:14:514:26 | +| ir.cpp:512:6:512:19 | NestedInitList(int, float) -> void | 9 | 8 | 0 | {...} | | | prvalue: Rect | ir.cpp:514:14:514:26 | +| ir.cpp:512:6:512:19 | NestedInitList(int, float) -> void | 10 | 9 | 0 | {...} | | | prvalue: Point | ir.cpp:514:17:514:24 | +| ir.cpp:512:6:512:19 | NestedInitList(int, float) -> void | 11 | 10 | 0 | x | | | prvalue(load): int | ir.cpp:514:19:514:19 | +| ir.cpp:512:6:512:19 | NestedInitList(int, float) -> void | 12 | 10 | 1 | (int)... | floating point to integral conversion | | prvalue: int | ir.cpp:514:22:514:22 | +| ir.cpp:512:6:512:19 | NestedInitList(int, float) -> void | 13 | 12 | 0 | f | | | prvalue(load): float | ir.cpp:514:22:514:22 | +| ir.cpp:512:6:512:19 | NestedInitList(int, float) -> void | 14 | 1 | 2 | declaration | | | | ir.cpp:515:5:515:37 | +| ir.cpp:512:6:512:19 | NestedInitList(int, float) -> void | 15 | 14 | 0 | definition of r3 | | | Rect | ir.cpp:515:10:515:11 | +| ir.cpp:512:6:512:19 | NestedInitList(int, float) -> void | 16 | 15 | 0 | initializer for r3 | | | | ir.cpp:515:14:515:36 | +| ir.cpp:512:6:512:19 | NestedInitList(int, float) -> void | 17 | 16 | 0 | {...} | | | prvalue: Rect | ir.cpp:515:14:515:36 | +| ir.cpp:512:6:512:19 | NestedInitList(int, float) -> void | 18 | 17 | 0 | {...} | | | prvalue: Point | ir.cpp:515:17:515:24 | +| ir.cpp:512:6:512:19 | NestedInitList(int, float) -> void | 19 | 18 | 0 | x | | | prvalue(load): int | ir.cpp:515:19:515:19 | +| ir.cpp:512:6:512:19 | NestedInitList(int, float) -> void | 20 | 18 | 1 | (int)... | floating point to integral conversion | | prvalue: int | ir.cpp:515:22:515:22 | +| ir.cpp:512:6:512:19 | NestedInitList(int, float) -> void | 21 | 20 | 0 | f | | | prvalue(load): float | ir.cpp:515:22:515:22 | +| ir.cpp:512:6:512:19 | NestedInitList(int, float) -> void | 22 | 17 | 1 | {...} | | | prvalue: Point | ir.cpp:515:27:515:34 | +| ir.cpp:512:6:512:19 | NestedInitList(int, float) -> void | 23 | 22 | 0 | x | | | prvalue(load): int | ir.cpp:515:29:515:29 | +| ir.cpp:512:6:512:19 | NestedInitList(int, float) -> void | 24 | 22 | 1 | (int)... | floating point to integral conversion | | prvalue: int | ir.cpp:515:32:515:32 | +| ir.cpp:512:6:512:19 | NestedInitList(int, float) -> void | 25 | 24 | 0 | f | | | prvalue(load): float | ir.cpp:515:32:515:32 | +| ir.cpp:512:6:512:19 | NestedInitList(int, float) -> void | 26 | 1 | 3 | declaration | | | | ir.cpp:516:5:516:31 | +| ir.cpp:512:6:512:19 | NestedInitList(int, float) -> void | 27 | 26 | 0 | definition of r4 | | | Rect | ir.cpp:516:10:516:11 | +| ir.cpp:512:6:512:19 | NestedInitList(int, float) -> void | 28 | 27 | 0 | initializer for r4 | | | | ir.cpp:516:14:516:30 | +| ir.cpp:512:6:512:19 | NestedInitList(int, float) -> void | 29 | 28 | 0 | {...} | | | prvalue: Rect | ir.cpp:516:14:516:30 | +| ir.cpp:512:6:512:19 | NestedInitList(int, float) -> void | 30 | 29 | 0 | {...} | | | prvalue: Point | ir.cpp:516:17:516:21 | +| ir.cpp:512:6:512:19 | NestedInitList(int, float) -> void | 31 | 30 | 0 | x | | | prvalue(load): int | ir.cpp:516:19:516:19 | +| ir.cpp:512:6:512:19 | NestedInitList(int, float) -> void | 32 | 29 | 1 | {...} | | | prvalue: Point | ir.cpp:516:24:516:28 | +| ir.cpp:512:6:512:19 | NestedInitList(int, float) -> void | 33 | 32 | 0 | x | | | prvalue(load): int | ir.cpp:516:26:516:26 | +| ir.cpp:512:6:512:19 | NestedInitList(int, float) -> void | 34 | 1 | 4 | return ... | | | | ir.cpp:517:1:517:1 | +| ir.cpp:519:6:519:14 | ArrayInit(int, float) -> void | 0 | -1 | 0 | ArrayInit | | | | ir.cpp:519:6:519:14 | +| ir.cpp:519:6:519:14 | ArrayInit(int, float) -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:519:32:523:1 | +| ir.cpp:519:6:519:14 | ArrayInit(int, float) -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:520:5:520:19 | +| ir.cpp:519:6:519:14 | ArrayInit(int, float) -> void | 3 | 2 | 0 | definition of a1 | | | int[3] | ir.cpp:520:9:520:10 | +| ir.cpp:519:6:519:14 | ArrayInit(int, float) -> void | 4 | 3 | 0 | initializer for a1 | | | | ir.cpp:520:16:520:18 | +| ir.cpp:519:6:519:14 | ArrayInit(int, float) -> void | 5 | 4 | 0 | {...} | | | prvalue: int[3] | ir.cpp:520:16:520:18 | +| ir.cpp:519:6:519:14 | ArrayInit(int, float) -> void | 6 | 1 | 1 | declaration | | | | ir.cpp:521:5:521:28 | +| ir.cpp:519:6:519:14 | ArrayInit(int, float) -> void | 7 | 6 | 0 | definition of a2 | | | int[3] | ir.cpp:521:9:521:10 | +| ir.cpp:519:6:519:14 | ArrayInit(int, float) -> void | 8 | 7 | 0 | initializer for a2 | | | | ir.cpp:521:16:521:27 | +| ir.cpp:519:6:519:14 | ArrayInit(int, float) -> void | 9 | 8 | 0 | {...} | | | prvalue: int[3] | ir.cpp:521:16:521:27 | +| ir.cpp:519:6:519:14 | ArrayInit(int, float) -> void | 10 | 9 | 0 | x | | | prvalue(load): int | ir.cpp:521:19:521:19 | +| ir.cpp:519:6:519:14 | ArrayInit(int, float) -> void | 11 | 9 | 1 | (int)... | floating point to integral conversion | | prvalue: int | ir.cpp:521:22:521:22 | +| ir.cpp:519:6:519:14 | ArrayInit(int, float) -> void | 12 | 11 | 0 | f | | | prvalue(load): float | ir.cpp:521:22:521:22 | +| ir.cpp:519:6:519:14 | ArrayInit(int, float) -> void | 13 | 9 | 2 | 0 | | =0 | prvalue: int | ir.cpp:521:25:521:25 | +| ir.cpp:519:6:519:14 | ArrayInit(int, float) -> void | 14 | 1 | 2 | declaration | | | | ir.cpp:522:5:522:22 | +| ir.cpp:519:6:519:14 | ArrayInit(int, float) -> void | 15 | 14 | 0 | definition of a3 | | | int[3] | ir.cpp:522:9:522:10 | +| ir.cpp:519:6:519:14 | ArrayInit(int, float) -> void | 16 | 15 | 0 | initializer for a3 | | | | ir.cpp:522:16:522:21 | +| ir.cpp:519:6:519:14 | ArrayInit(int, float) -> void | 17 | 16 | 0 | {...} | | | prvalue: int[3] | ir.cpp:522:16:522:21 | +| ir.cpp:519:6:519:14 | ArrayInit(int, float) -> void | 18 | 17 | 0 | x | | | prvalue(load): int | ir.cpp:522:19:522:19 | +| ir.cpp:519:6:519:14 | ArrayInit(int, float) -> void | 19 | 1 | 3 | return ... | | | | ir.cpp:523:1:523:1 | +| ir.cpp:525:7:525:7 | U::operator=(U &&) -> U & | 0 | -1 | 0 | operator= | | | | ir.cpp:525:7:525:7 | +| ir.cpp:525:7:525:7 | U::operator=(const U &) -> U & | 0 | -1 | 0 | operator= | | | | ir.cpp:525:7:525:7 | +| ir.cpp:530:6:530:14 | UnionInit(int, float) -> void | 0 | -1 | 0 | UnionInit | | | | ir.cpp:530:6:530:14 | +| ir.cpp:530:6:530:14 | UnionInit(int, float) -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:530:32:533:1 | +| ir.cpp:530:6:530:14 | UnionInit(int, float) -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:531:5:531:17 | +| ir.cpp:530:6:530:14 | UnionInit(int, float) -> void | 3 | 2 | 0 | definition of u1 | | | U | ir.cpp:531:7:531:8 | +| ir.cpp:530:6:530:14 | UnionInit(int, float) -> void | 4 | 3 | 0 | initializer for u1 | | | | ir.cpp:531:11:531:16 | +| ir.cpp:530:6:530:14 | UnionInit(int, float) -> void | 5 | 4 | 0 | {...} | | | prvalue: U | ir.cpp:531:11:531:16 | +| ir.cpp:530:6:530:14 | UnionInit(int, float) -> void | 6 | 5 | 0 | (double)... | floating point conversion | | prvalue: double | ir.cpp:531:14:531:14 | +| ir.cpp:530:6:530:14 | UnionInit(int, float) -> void | 7 | 6 | 0 | f | | | prvalue(load): float | ir.cpp:531:14:531:14 | +| ir.cpp:530:6:530:14 | UnionInit(int, float) -> void | 8 | 1 | 1 | return ... | | | | ir.cpp:533:1:533:1 | +| ir.cpp:535:6:535:16 | EarlyReturn(int, int) -> void | 0 | -1 | 0 | EarlyReturn | | | | ir.cpp:535:6:535:16 | +| ir.cpp:535:6:535:16 | EarlyReturn(int, int) -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:535:32:541:1 | +| ir.cpp:535:6:535:16 | EarlyReturn(int, int) -> void | 2 | 1 | 0 | if (...) ... | | | | ir.cpp:536:5:538:5 | +| ir.cpp:535:6:535:16 | EarlyReturn(int, int) -> void | 3 | 2 | 0 | ... < ... | | | prvalue: bool | ir.cpp:536:9:536:13 | +| ir.cpp:535:6:535:16 | EarlyReturn(int, int) -> void | 4 | 3 | 0 | x | | | prvalue(load): int | ir.cpp:536:9:536:9 | +| ir.cpp:535:6:535:16 | EarlyReturn(int, int) -> void | 5 | 3 | 1 | y | | | prvalue(load): int | ir.cpp:536:13:536:13 | +| ir.cpp:535:6:535:16 | EarlyReturn(int, int) -> void | 6 | 2 | 1 | { ... } | | | | ir.cpp:536:16:538:5 | +| ir.cpp:535:6:535:16 | EarlyReturn(int, int) -> void | 7 | 6 | 0 | return ... | | | | ir.cpp:537:9:537:15 | +| ir.cpp:535:6:535:16 | EarlyReturn(int, int) -> void | 8 | 1 | 1 | ExprStmt | | | | ir.cpp:540:5:540:10 | +| ir.cpp:535:6:535:16 | EarlyReturn(int, int) -> void | 9 | 8 | 0 | ... = ... | | | lvalue: int | ir.cpp:540:5:540:9 | +| ir.cpp:535:6:535:16 | EarlyReturn(int, int) -> void | 10 | 9 | 0 | y | | | lvalue: int | ir.cpp:540:5:540:5 | +| ir.cpp:535:6:535:16 | EarlyReturn(int, int) -> void | 11 | 9 | 1 | x | | | prvalue(load): int | ir.cpp:540:9:540:9 | +| ir.cpp:535:6:535:16 | EarlyReturn(int, int) -> void | 12 | 1 | 2 | return ... | | | | ir.cpp:541:1:541:1 | +| ir.cpp:543:5:543:20 | EarlyReturnValue(int, int) -> int | 0 | -1 | 0 | EarlyReturnValue | | | | ir.cpp:543:5:543:20 | +| ir.cpp:543:5:543:20 | EarlyReturnValue(int, int) -> int | 1 | 0 | 0 | { ... } | | | | ir.cpp:543:36:549:1 | +| ir.cpp:543:5:543:20 | EarlyReturnValue(int, int) -> int | 2 | 1 | 0 | if (...) ... | | | | ir.cpp:544:5:546:5 | +| ir.cpp:543:5:543:20 | EarlyReturnValue(int, int) -> int | 3 | 2 | 0 | ... < ... | | | prvalue: bool | ir.cpp:544:9:544:13 | +| ir.cpp:543:5:543:20 | EarlyReturnValue(int, int) -> int | 4 | 3 | 0 | x | | | prvalue(load): int | ir.cpp:544:9:544:9 | +| ir.cpp:543:5:543:20 | EarlyReturnValue(int, int) -> int | 5 | 3 | 1 | y | | | prvalue(load): int | ir.cpp:544:13:544:13 | +| ir.cpp:543:5:543:20 | EarlyReturnValue(int, int) -> int | 6 | 2 | 1 | { ... } | | | | ir.cpp:544:16:546:5 | +| ir.cpp:543:5:543:20 | EarlyReturnValue(int, int) -> int | 7 | 6 | 0 | return ... | | | | ir.cpp:545:9:545:17 | +| ir.cpp:543:5:543:20 | EarlyReturnValue(int, int) -> int | 8 | 7 | 0 | x | | | prvalue(load): int | ir.cpp:545:16:545:16 | +| ir.cpp:543:5:543:20 | EarlyReturnValue(int, int) -> int | 9 | 1 | 1 | return ... | | | | ir.cpp:548:5:548:17 | +| ir.cpp:543:5:543:20 | EarlyReturnValue(int, int) -> int | 10 | 9 | 0 | ... + ... | | | prvalue: int | ir.cpp:548:12:548:16 | +| ir.cpp:543:5:543:20 | EarlyReturnValue(int, int) -> int | 11 | 10 | 0 | x | | | prvalue(load): int | ir.cpp:548:12:548:12 | +| ir.cpp:543:5:543:20 | EarlyReturnValue(int, int) -> int | 12 | 10 | 1 | y | | | prvalue(load): int | ir.cpp:548:16:548:16 | +| ir.cpp:551:5:551:18 | CallViaFuncPtr(..(*)(..)) -> int | 0 | -1 | 0 | CallViaFuncPtr | | | | ir.cpp:551:5:551:18 | +| ir.cpp:551:5:551:18 | CallViaFuncPtr(..(*)(..)) -> int | 1 | 0 | 0 | { ... } | | | | ir.cpp:551:37:553:1 | +| ir.cpp:551:5:551:18 | CallViaFuncPtr(..(*)(..)) -> int | 2 | 1 | 0 | return ... | | | | ir.cpp:552:5:552:18 | +| ir.cpp:551:5:551:18 | CallViaFuncPtr(..(*)(..)) -> int | 3 | 2 | 0 | call to expression | | | prvalue: int | ir.cpp:552:12:552:17 | +| ir.cpp:551:5:551:18 | CallViaFuncPtr(..(*)(..)) -> int | 4 | 3 | 0 | pfn | | | prvalue(load): ..(*)(..) | ir.cpp:552:12:552:14 | +| ir.cpp:551:5:551:18 | CallViaFuncPtr(..(*)(..)) -> int | 5 | 3 | 1 | 5 | | =5 | prvalue: int | ir.cpp:552:16:552:16 | +| ir.cpp:560:5:560:14 | EnumSwitch(E) -> int | 0 | -1 | 0 | EnumSwitch | | | | ir.cpp:560:5:560:14 | +| ir.cpp:560:5:560:14 | EnumSwitch(E) -> int | 1 | 0 | 0 | { ... } | | | | ir.cpp:560:21:569:1 | +| ir.cpp:560:5:560:14 | EnumSwitch(E) -> int | 2 | 1 | 0 | switch (...) ... | | | | ir.cpp:561:5:568:5 | +| ir.cpp:560:5:560:14 | EnumSwitch(E) -> int | 3 | 2 | 0 | (int)... | integral conversion | | prvalue: int | ir.cpp:561:13:561:13 | +| ir.cpp:560:5:560:14 | EnumSwitch(E) -> int | 4 | 3 | 0 | e | | | prvalue(load): E | ir.cpp:561:13:561:13 | +| ir.cpp:560:5:560:14 | EnumSwitch(E) -> int | 5 | 2 | 1 | { ... } | | | | ir.cpp:561:16:568:5 | +| ir.cpp:560:5:560:14 | EnumSwitch(E) -> int | 6 | 5 | 0 | case ...: | | | | ir.cpp:562:9:562:17 | +| ir.cpp:560:5:560:14 | EnumSwitch(E) -> int | 7 | 6 | 0 | (int)... | integral conversion | =0 | prvalue: int | ir.cpp:562:14:562:16 | +| ir.cpp:560:5:560:14 | EnumSwitch(E) -> int | 8 | 7 | 0 | E_0 | | =0 | prvalue: E | ir.cpp:562:14:562:16 | +| ir.cpp:560:5:560:14 | EnumSwitch(E) -> int | 9 | 5 | 1 | return ... | | | | ir.cpp:563:13:563:21 | +| ir.cpp:560:5:560:14 | EnumSwitch(E) -> int | 10 | 9 | 0 | 0 | | =0 | prvalue: int | ir.cpp:563:20:563:20 | +| ir.cpp:560:5:560:14 | EnumSwitch(E) -> int | 11 | 5 | 2 | case ...: | | | | ir.cpp:564:9:564:17 | +| ir.cpp:560:5:560:14 | EnumSwitch(E) -> int | 12 | 11 | 0 | (int)... | integral conversion | =1 | prvalue: int | ir.cpp:564:14:564:16 | +| ir.cpp:560:5:560:14 | EnumSwitch(E) -> int | 13 | 12 | 0 | E_1 | | =1 | prvalue: E | ir.cpp:564:14:564:16 | +| ir.cpp:560:5:560:14 | EnumSwitch(E) -> int | 14 | 5 | 3 | return ... | | | | ir.cpp:565:13:565:21 | +| ir.cpp:560:5:560:14 | EnumSwitch(E) -> int | 15 | 14 | 0 | 1 | | =1 | prvalue: int | ir.cpp:565:20:565:20 | +| ir.cpp:560:5:560:14 | EnumSwitch(E) -> int | 16 | 5 | 4 | default: | | | | ir.cpp:566:9:566:16 | +| ir.cpp:560:5:560:14 | EnumSwitch(E) -> int | 17 | 5 | 5 | return ... | | | | ir.cpp:567:13:567:22 | +| ir.cpp:560:5:560:14 | EnumSwitch(E) -> int | 18 | 17 | 0 | - ... | | =-1 | prvalue: int | ir.cpp:567:20:567:21 | +| ir.cpp:560:5:560:14 | EnumSwitch(E) -> int | 19 | 18 | 0 | 1 | | =1 | prvalue: int | ir.cpp:567:21:567:21 | +| ir.cpp:571:6:571:14 | InitArray() -> void | 0 | -1 | 0 | InitArray | | | | ir.cpp:571:6:571:14 | +| ir.cpp:571:6:571:14 | InitArray() -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:571:18:580:1 | +| ir.cpp:571:6:571:14 | InitArray() -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:572:5:572:24 | +| ir.cpp:571:6:571:14 | InitArray() -> void | 3 | 2 | 0 | definition of a_pad | | | char[32] | ir.cpp:572:10:572:14 | +| ir.cpp:571:6:571:14 | InitArray() -> void | 4 | 3 | 0 | initializer for a_pad | | | | ir.cpp:572:22:572:23 | +| ir.cpp:571:6:571:14 | InitArray() -> void | 5 | 4 | 0 | | | = | lvalue: const char[1] | ir.cpp:572:22:572:23 | +| ir.cpp:571:6:571:14 | InitArray() -> void | 6 | 1 | 1 | declaration | | | | ir.cpp:573:5:573:28 | +| ir.cpp:571:6:571:14 | InitArray() -> void | 7 | 6 | 0 | definition of a_nopad | | | char[4] | ir.cpp:573:10:573:16 | +| ir.cpp:571:6:571:14 | InitArray() -> void | 8 | 7 | 0 | initializer for a_nopad | | | | ir.cpp:573:23:573:27 | +| ir.cpp:571:6:571:14 | InitArray() -> void | 9 | 8 | 0 | foo | | =foo | lvalue: const char[4] | ir.cpp:573:23:573:27 | +| ir.cpp:571:6:571:14 | InitArray() -> void | 10 | 1 | 2 | declaration | | | | ir.cpp:574:5:574:28 | +| ir.cpp:571:6:571:14 | InitArray() -> void | 11 | 10 | 0 | definition of a_infer | | | char[] | ir.cpp:574:10:574:16 | +| ir.cpp:571:6:571:14 | InitArray() -> void | 12 | 11 | 0 | initializer for a_infer | | | | ir.cpp:574:22:574:27 | +| ir.cpp:571:6:571:14 | InitArray() -> void | 13 | 12 | 0 | blah | | =blah | lvalue: const char[5] | ir.cpp:574:22:574:27 | +| ir.cpp:571:6:571:14 | InitArray() -> void | 14 | 1 | 3 | declaration | | | | ir.cpp:575:5:575:14 | +| ir.cpp:571:6:571:14 | InitArray() -> void | 15 | 14 | 0 | definition of b | | | char[2] | ir.cpp:575:10:575:10 | +| ir.cpp:571:6:571:14 | InitArray() -> void | 16 | 1 | 4 | declaration | | | | ir.cpp:576:5:576:19 | +| ir.cpp:571:6:571:14 | InitArray() -> void | 17 | 16 | 0 | definition of c | | | char[2] | ir.cpp:576:10:576:10 | +| ir.cpp:571:6:571:14 | InitArray() -> void | 18 | 17 | 0 | initializer for c | | | | ir.cpp:576:16:576:18 | +| ir.cpp:571:6:571:14 | InitArray() -> void | 19 | 18 | 0 | {...} | | | prvalue: char[2] | ir.cpp:576:16:576:18 | +| ir.cpp:571:6:571:14 | InitArray() -> void | 20 | 1 | 5 | declaration | | | | ir.cpp:577:5:577:22 | +| ir.cpp:571:6:571:14 | InitArray() -> void | 21 | 20 | 0 | definition of d | | | char[2] | ir.cpp:577:10:577:10 | +| ir.cpp:571:6:571:14 | InitArray() -> void | 22 | 21 | 0 | initializer for d | | | | ir.cpp:577:16:577:21 | +| ir.cpp:571:6:571:14 | InitArray() -> void | 23 | 22 | 0 | {...} | | | prvalue: char[2] | ir.cpp:577:16:577:21 | +| ir.cpp:571:6:571:14 | InitArray() -> void | 24 | 23 | 0 | (char)... | integral conversion | =0 | prvalue: char | ir.cpp:577:19:577:19 | +| ir.cpp:571:6:571:14 | InitArray() -> void | 25 | 24 | 0 | 0 | | =0 | prvalue: int | ir.cpp:577:19:577:19 | +| ir.cpp:571:6:571:14 | InitArray() -> void | 26 | 1 | 6 | declaration | | | | ir.cpp:578:5:578:25 | +| ir.cpp:571:6:571:14 | InitArray() -> void | 27 | 26 | 0 | definition of e | | | char[2] | ir.cpp:578:10:578:10 | +| ir.cpp:571:6:571:14 | InitArray() -> void | 28 | 27 | 0 | initializer for e | | | | ir.cpp:578:16:578:24 | +| ir.cpp:571:6:571:14 | InitArray() -> void | 29 | 28 | 0 | {...} | | | prvalue: char[2] | ir.cpp:578:16:578:24 | +| ir.cpp:571:6:571:14 | InitArray() -> void | 30 | 29 | 0 | (char)... | integral conversion | =0 | prvalue: char | ir.cpp:578:19:578:19 | +| ir.cpp:571:6:571:14 | InitArray() -> void | 31 | 30 | 0 | 0 | | =0 | prvalue: int | ir.cpp:578:19:578:19 | +| ir.cpp:571:6:571:14 | InitArray() -> void | 32 | 29 | 1 | (char)... | integral conversion | =1 | prvalue: char | ir.cpp:578:22:578:22 | +| ir.cpp:571:6:571:14 | InitArray() -> void | 33 | 32 | 0 | 1 | | =1 | prvalue: int | ir.cpp:578:22:578:22 | +| ir.cpp:571:6:571:14 | InitArray() -> void | 34 | 1 | 7 | declaration | | | | ir.cpp:579:5:579:22 | +| ir.cpp:571:6:571:14 | InitArray() -> void | 35 | 34 | 0 | definition of f | | | char[3] | ir.cpp:579:10:579:10 | +| ir.cpp:571:6:571:14 | InitArray() -> void | 36 | 35 | 0 | initializer for f | | | | ir.cpp:579:16:579:21 | +| ir.cpp:571:6:571:14 | InitArray() -> void | 37 | 36 | 0 | {...} | | | prvalue: char[3] | ir.cpp:579:16:579:21 | +| ir.cpp:571:6:571:14 | InitArray() -> void | 38 | 37 | 0 | (char)... | integral conversion | =0 | prvalue: char | ir.cpp:579:19:579:19 | +| ir.cpp:571:6:571:14 | InitArray() -> void | 39 | 38 | 0 | 0 | | =0 | prvalue: int | ir.cpp:579:19:579:19 | +| ir.cpp:571:6:571:14 | InitArray() -> void | 40 | 1 | 8 | return ... | | | | ir.cpp:580:1:580:1 | +| ir.cpp:582:6:582:19 | VarArgFunction(const char *) -> void | 0 | -1 | 0 | VarArgFunction | | | | ir.cpp:582:6:582:19 | +| ir.cpp:584:6:584:12 | VarArgs() -> void | 0 | -1 | 0 | VarArgs | | | | ir.cpp:584:6:584:12 | +| ir.cpp:584:6:584:12 | VarArgs() -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:584:16:586:1 | +| ir.cpp:584:6:584:12 | VarArgs() -> void | 2 | 1 | 0 | ExprStmt | | | | ir.cpp:585:5:585:41 | +| ir.cpp:584:6:584:12 | VarArgs() -> void | 3 | 2 | 0 | call to VarArgFunction | | | prvalue: void | ir.cpp:585:5:585:18 | +| ir.cpp:584:6:584:12 | VarArgs() -> void | 4 | 3 | 0 | array to pointer conversion | | | prvalue: const char * | ir.cpp:585:20:585:26 | +| ir.cpp:584:6:584:12 | VarArgs() -> void | 5 | 4 | 0 | %d %s | | =%d %s | lvalue: const char[6] | ir.cpp:585:20:585:26 | +| ir.cpp:584:6:584:12 | VarArgs() -> void | 6 | 3 | 1 | 1 | | =1 | prvalue: int | ir.cpp:585:29:585:29 | +| ir.cpp:584:6:584:12 | VarArgs() -> void | 7 | 3 | 2 | array to pointer conversion | | | prvalue: const char * | ir.cpp:585:32:585:39 | +| ir.cpp:584:6:584:12 | VarArgs() -> void | 8 | 7 | 0 | string | | =string | lvalue: const char[7] | ir.cpp:585:32:585:39 | +| ir.cpp:584:6:584:12 | VarArgs() -> void | 9 | 1 | 1 | return ... | | | | ir.cpp:586:1:586:1 | +| ir.cpp:588:5:588:17 | FuncPtrTarget(int) -> int | 0 | -1 | 0 | FuncPtrTarget | | | | ir.cpp:588:5:588:17 | +| ir.cpp:590:6:590:15 | SetFuncPtr() -> void | 0 | -1 | 0 | SetFuncPtr | | | | ir.cpp:590:6:590:15 | +| ir.cpp:590:6:590:15 | SetFuncPtr() -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:590:19:595:1 | +| ir.cpp:590:6:590:15 | SetFuncPtr() -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:591:5:591:36 | +| ir.cpp:590:6:590:15 | SetFuncPtr() -> void | 3 | 2 | 0 | definition of pfn | | | ..(*)(..) | ir.cpp:591:11:591:13 | +| ir.cpp:590:6:590:15 | SetFuncPtr() -> void | 4 | 3 | 0 | initializer for pfn | | | | ir.cpp:591:22:591:35 | +| ir.cpp:590:6:590:15 | SetFuncPtr() -> void | 5 | 4 | 0 | FuncPtrTarget | | | prvalue(load): ..(*)(..) | ir.cpp:591:23:591:35 | +| ir.cpp:590:6:590:15 | SetFuncPtr() -> void | 6 | 1 | 1 | ExprStmt | | | | ir.cpp:592:5:592:25 | +| ir.cpp:590:6:590:15 | SetFuncPtr() -> void | 7 | 6 | 0 | ... = ... | | | lvalue: ..(*)(..) | ir.cpp:592:5:592:24 | +| ir.cpp:590:6:590:15 | SetFuncPtr() -> void | 8 | 7 | 0 | pfn | | | lvalue: ..(*)(..) | ir.cpp:592:5:592:7 | +| ir.cpp:590:6:590:15 | SetFuncPtr() -> void | 9 | 7 | 1 | & ... | | | prvalue: ..(*)(..) | ir.cpp:592:11:592:24 | +| ir.cpp:590:6:590:15 | SetFuncPtr() -> void | 10 | 9 | 0 | FuncPtrTarget | | | lvalue: ..()(..) | ir.cpp:592:12:592:24 | +| ir.cpp:590:6:590:15 | SetFuncPtr() -> void | 11 | 1 | 2 | ExprStmt | | | | ir.cpp:593:5:593:25 | +| ir.cpp:590:6:590:15 | SetFuncPtr() -> void | 12 | 11 | 0 | ... = ... | | | lvalue: ..(*)(..) | ir.cpp:593:5:593:24 | +| ir.cpp:590:6:590:15 | SetFuncPtr() -> void | 13 | 12 | 0 | pfn | | | lvalue: ..(*)(..) | ir.cpp:593:5:593:7 | +| ir.cpp:590:6:590:15 | SetFuncPtr() -> void | 14 | 12 | 1 | * ... | | | prvalue(load): ..(*)(..) | ir.cpp:593:11:593:24 | +| ir.cpp:590:6:590:15 | SetFuncPtr() -> void | 15 | 14 | 0 | FuncPtrTarget | | | prvalue(load): ..(*)(..) | ir.cpp:593:12:593:24 | +| ir.cpp:590:6:590:15 | SetFuncPtr() -> void | 16 | 1 | 3 | ExprStmt | | | | ir.cpp:594:5:594:28 | +| ir.cpp:590:6:590:15 | SetFuncPtr() -> void | 17 | 16 | 0 | ... = ... | | | lvalue: ..(*)(..) | ir.cpp:594:5:594:27 | +| ir.cpp:590:6:590:15 | SetFuncPtr() -> void | 18 | 17 | 0 | pfn | | | lvalue: ..(*)(..) | ir.cpp:594:5:594:7 | +| ir.cpp:590:6:590:15 | SetFuncPtr() -> void | 19 | 17 | 1 | * ... | | | prvalue(load): ..(*)(..) | ir.cpp:594:11:594:27 | +| ir.cpp:590:6:590:15 | SetFuncPtr() -> void | 20 | 19 | 0 | * ... | | | prvalue(load): ..(*)(..) | ir.cpp:594:12:594:27 | +| ir.cpp:590:6:590:15 | SetFuncPtr() -> void | 21 | 20 | 0 | * ... | | | prvalue(load): ..(*)(..) | ir.cpp:594:13:594:27 | +| ir.cpp:590:6:590:15 | SetFuncPtr() -> void | 22 | 21 | 0 | & ... | | | prvalue: ..(*)(..) | ir.cpp:594:14:594:27 | +| ir.cpp:590:6:590:15 | SetFuncPtr() -> void | 23 | 22 | 0 | FuncPtrTarget | | | lvalue: ..()(..) | ir.cpp:594:15:594:27 | +| ir.cpp:590:6:590:15 | SetFuncPtr() -> void | 24 | 1 | 4 | return ... | | | | ir.cpp:595:1:595:1 | +| ir.cpp:599:5:599:10 | String::String(const String &) -> void | 0 | -1 | 0 | String | | | | ir.cpp:599:5:599:10 | +| ir.cpp:600:5:600:10 | String::String(String &&) -> void | 0 | -1 | 0 | String | | | | ir.cpp:600:5:600:10 | +| ir.cpp:601:5:601:10 | String::String(const char *) -> void | 0 | -1 | 0 | String | | | | ir.cpp:601:5:601:10 | +| ir.cpp:602:5:602:11 | String::~String() -> void | 0 | -1 | 0 | ~String | | | | ir.cpp:602:5:602:11 | +| ir.cpp:604:13:604:21 | String::operator=(const String &) -> String & | 0 | -1 | 0 | operator= | | | | ir.cpp:604:13:604:21 | +| ir.cpp:605:13:605:21 | String::operator=(String &&) -> String & | 0 | -1 | 0 | operator= | | | | ir.cpp:605:13:605:21 | +| ir.cpp:607:17:607:21 | String::c_str() -> const char * | 0 | -1 | 0 | c_str | | | | ir.cpp:607:17:607:21 | +| ir.cpp:613:8:613:19 | ReturnObject() -> String | 0 | -1 | 0 | ReturnObject | | | | ir.cpp:613:8:613:19 | +| ir.cpp:615:6:615:18 | DeclareObject() -> void | 0 | -1 | 0 | DeclareObject | | | | ir.cpp:615:6:615:18 | +| ir.cpp:615:6:615:18 | DeclareObject() -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:615:22:620:1 | +| ir.cpp:615:6:615:18 | DeclareObject() -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:616:5:616:14 | +| ir.cpp:615:6:615:18 | DeclareObject() -> void | 3 | 2 | 0 | definition of s1 | | | String | ir.cpp:616:12:616:13 | +| ir.cpp:615:6:615:18 | DeclareObject() -> void | 4 | 3 | 0 | initializer for s1 | | | | ir.cpp:616:12:616:13 | +| ir.cpp:615:6:615:18 | DeclareObject() -> void | 5 | 4 | 0 | call to String | | | prvalue: void | ir.cpp:616:12:616:13 | +| ir.cpp:615:6:615:18 | DeclareObject() -> void | 6 | 1 | 1 | declaration | | | | ir.cpp:617:5:617:23 | +| ir.cpp:615:6:615:18 | DeclareObject() -> void | 7 | 6 | 0 | definition of s2 | | | String | ir.cpp:617:12:617:13 | +| ir.cpp:615:6:615:18 | DeclareObject() -> void | 8 | 7 | 0 | initializer for s2 | | | | ir.cpp:617:15:617:22 | +| ir.cpp:615:6:615:18 | DeclareObject() -> void | 9 | 8 | 0 | call to String | | | prvalue: void | ir.cpp:617:15:617:22 | +| ir.cpp:615:6:615:18 | DeclareObject() -> void | 10 | 9 | 0 | array to pointer conversion | | | prvalue: const char * | ir.cpp:617:15:617:21 | +| ir.cpp:615:6:615:18 | DeclareObject() -> void | 11 | 10 | 0 | hello | | =hello | lvalue: const char[6] | ir.cpp:617:15:617:21 | +| ir.cpp:615:6:615:18 | DeclareObject() -> void | 12 | 1 | 2 | declaration | | | | ir.cpp:618:5:618:31 | +| ir.cpp:615:6:615:18 | DeclareObject() -> void | 13 | 12 | 0 | definition of s3 | | | String | ir.cpp:618:12:618:13 | +| ir.cpp:615:6:615:18 | DeclareObject() -> void | 14 | 13 | 0 | initializer for s3 | | | | ir.cpp:618:16:618:30 | +| ir.cpp:615:6:615:18 | DeclareObject() -> void | 15 | 14 | 0 | call to ReturnObject | | | prvalue: String | ir.cpp:618:17:618:28 | +| ir.cpp:615:6:615:18 | DeclareObject() -> void | 16 | 1 | 3 | declaration | | | | ir.cpp:619:5:619:31 | +| ir.cpp:615:6:615:18 | DeclareObject() -> void | 17 | 16 | 0 | definition of s4 | | | String | ir.cpp:619:12:619:13 | +| ir.cpp:615:6:615:18 | DeclareObject() -> void | 18 | 17 | 0 | initializer for s4 | | | | ir.cpp:619:16:619:30 | +| ir.cpp:615:6:615:18 | DeclareObject() -> void | 19 | 18 | 0 | call to String | | | prvalue: void | ir.cpp:619:16:619:30 | +| ir.cpp:615:6:615:18 | DeclareObject() -> void | 20 | 19 | 0 | array to pointer conversion | | | prvalue: const char * | ir.cpp:619:24:619:29 | +| ir.cpp:615:6:615:18 | DeclareObject() -> void | 21 | 20 | 0 | test | | =test | lvalue: const char[5] | ir.cpp:619:24:619:29 | +| ir.cpp:615:6:615:18 | DeclareObject() -> void | 22 | 1 | 4 | return ... | | | | ir.cpp:620:1:620:1 | +| ir.cpp:622:6:622:16 | CallMethods(String &, String *, String) -> void | 0 | -1 | 0 | CallMethods | | | | ir.cpp:622:6:622:16 | +| ir.cpp:622:6:622:16 | CallMethods(String &, String *, String) -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:622:50:626:1 | +| ir.cpp:622:6:622:16 | CallMethods(String &, String *, String) -> void | 2 | 1 | 0 | ExprStmt | | | | ir.cpp:623:5:623:14 | +| ir.cpp:622:6:622:16 | CallMethods(String &, String *, String) -> void | 3 | 2 | 0 | call to c_str | | | prvalue: const char * | ir.cpp:623:7:623:11 | +| ir.cpp:622:6:622:16 | CallMethods(String &, String *, String) -> void | 4 | 3 | -1 | (const String)... | glvalue conversion | | lvalue: const String | ir.cpp:623:5:623:5 | +| ir.cpp:622:6:622:16 | CallMethods(String &, String *, String) -> void | 5 | 4 | 0 | (reference dereference) | | | lvalue: String | ir.cpp:623:5:623:5 | +| ir.cpp:622:6:622:16 | CallMethods(String &, String *, String) -> void | 6 | 5 | 0 | r | | | prvalue(load): String & | ir.cpp:623:5:623:5 | +| ir.cpp:622:6:622:16 | CallMethods(String &, String *, String) -> void | 7 | 1 | 1 | ExprStmt | | | | ir.cpp:624:5:624:15 | +| ir.cpp:622:6:622:16 | CallMethods(String &, String *, String) -> void | 8 | 7 | 0 | call to c_str | | | prvalue: const char * | ir.cpp:624:8:624:12 | +| ir.cpp:622:6:622:16 | CallMethods(String &, String *, String) -> void | 9 | 8 | -1 | (const String *)... | pointer conversion | | prvalue: const String * | ir.cpp:624:5:624:5 | +| ir.cpp:622:6:622:16 | CallMethods(String &, String *, String) -> void | 10 | 9 | 0 | p | | | prvalue(load): String * | ir.cpp:624:5:624:5 | +| ir.cpp:622:6:622:16 | CallMethods(String &, String *, String) -> void | 11 | 1 | 2 | ExprStmt | | | | ir.cpp:625:5:625:14 | +| ir.cpp:622:6:622:16 | CallMethods(String &, String *, String) -> void | 12 | 11 | 0 | call to c_str | | | prvalue: const char * | ir.cpp:625:7:625:11 | +| ir.cpp:622:6:622:16 | CallMethods(String &, String *, String) -> void | 13 | 12 | -1 | (const String)... | glvalue conversion | | lvalue: const String | ir.cpp:625:5:625:5 | +| ir.cpp:622:6:622:16 | CallMethods(String &, String *, String) -> void | 14 | 13 | 0 | s | | | lvalue: String | ir.cpp:625:5:625:5 | +| ir.cpp:622:6:622:16 | CallMethods(String &, String *, String) -> void | 15 | 1 | 3 | return ... | | | | ir.cpp:626:1:626:1 | +| ir.cpp:628:7:628:7 | C::C(C &&) -> void | 0 | -1 | 0 | C | | | | ir.cpp:628:7:628:7 | +| ir.cpp:628:7:628:7 | C::C(const C &) -> void | 0 | -1 | 0 | C | | | | ir.cpp:628:7:628:7 | +| ir.cpp:628:7:628:7 | C::operator=(C &&) -> C & | 0 | -1 | 0 | operator= | | | | ir.cpp:628:7:628:7 | +| ir.cpp:628:7:628:7 | C::operator=(const C &) -> C & | 0 | -1 | 0 | operator= | | | | ir.cpp:628:7:628:7 | +| ir.cpp:628:7:628:7 | C::~C() -> void | 0 | -1 | 0 | ~C | | | | ir.cpp:628:7:628:7 | +| ir.cpp:630:16:630:35 | C::StaticMemberFunction(int) -> int | 0 | -1 | 0 | StaticMemberFunction | | | | ir.cpp:630:16:630:35 | +| ir.cpp:630:16:630:35 | C::StaticMemberFunction(int) -> int | 1 | 0 | 0 | { ... } | | | | ir.cpp:630:44:632:5 | +| ir.cpp:630:16:630:35 | C::StaticMemberFunction(int) -> int | 2 | 1 | 0 | return ... | | | | ir.cpp:631:9:631:17 | +| ir.cpp:630:16:630:35 | C::StaticMemberFunction(int) -> int | 3 | 2 | 0 | x | | | prvalue(load): int | ir.cpp:631:16:631:16 | +| ir.cpp:634:9:634:30 | C::InstanceMemberFunction(int) -> int | 0 | -1 | 0 | InstanceMemberFunction | | | | ir.cpp:634:9:634:30 | +| ir.cpp:634:9:634:30 | C::InstanceMemberFunction(int) -> int | 1 | 0 | 0 | { ... } | | | | ir.cpp:634:39:636:5 | +| ir.cpp:634:9:634:30 | C::InstanceMemberFunction(int) -> int | 2 | 1 | 0 | return ... | | | | ir.cpp:635:9:635:17 | +| ir.cpp:634:9:634:30 | C::InstanceMemberFunction(int) -> int | 3 | 2 | 0 | x | | | prvalue(load): int | ir.cpp:635:16:635:16 | +| ir.cpp:638:17:638:37 | C::VirtualMemberFunction(int) -> int | 0 | -1 | 0 | VirtualMemberFunction | | | | ir.cpp:638:17:638:37 | +| ir.cpp:638:17:638:37 | C::VirtualMemberFunction(int) -> int | 1 | 0 | 0 | { ... } | | | | ir.cpp:638:46:640:5 | +| ir.cpp:638:17:638:37 | C::VirtualMemberFunction(int) -> int | 2 | 1 | 0 | return ... | | | | ir.cpp:639:9:639:17 | +| ir.cpp:638:17:638:37 | C::VirtualMemberFunction(int) -> int | 3 | 2 | 0 | x | | | prvalue(load): int | ir.cpp:639:16:639:16 | +| ir.cpp:642:10:642:20 | C::FieldAccess() -> void | 0 | -1 | 0 | FieldAccess | | | | ir.cpp:642:10:642:20 | +| ir.cpp:642:10:642:20 | C::FieldAccess() -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:642:24:650:5 | +| ir.cpp:642:10:642:20 | C::FieldAccess() -> void | 2 | 1 | 0 | ExprStmt | | | | ir.cpp:643:9:643:22 | +| ir.cpp:642:10:642:20 | C::FieldAccess() -> void | 3 | 2 | 0 | ... = ... | | | lvalue: int | ir.cpp:643:9:643:21 | +| ir.cpp:642:10:642:20 | C::FieldAccess() -> void | 4 | 3 | 0 | m_a | | | lvalue: int | ir.cpp:643:15:643:17 | +| ir.cpp:642:10:642:20 | C::FieldAccess() -> void | 5 | 4 | -1 | this | | | prvalue(load): C * | ir.cpp:643:9:643:12 | +| ir.cpp:642:10:642:20 | C::FieldAccess() -> void | 6 | 3 | 1 | 0 | | =0 | prvalue: int | ir.cpp:643:21:643:21 | +| ir.cpp:642:10:642:20 | C::FieldAccess() -> void | 7 | 1 | 1 | ExprStmt | | | | ir.cpp:644:9:644:24 | +| ir.cpp:642:10:642:20 | C::FieldAccess() -> void | 8 | 7 | 0 | ... = ... | | | lvalue: int | ir.cpp:644:9:644:23 | +| ir.cpp:642:10:642:20 | C::FieldAccess() -> void | 9 | 8 | 0 | m_a | | | lvalue: int | ir.cpp:644:17:644:19 | +| ir.cpp:642:10:642:20 | C::FieldAccess() -> void | 10 | 9 | -1 | (...) | | | lvalue: C | ir.cpp:644:9:644:15 | +| ir.cpp:642:10:642:20 | C::FieldAccess() -> void | 11 | 10 | 0 | * ... | | | lvalue: C | ir.cpp:644:10:644:14 | +| ir.cpp:642:10:642:20 | C::FieldAccess() -> void | 12 | 11 | 0 | this | | | prvalue(load): C * | ir.cpp:644:11:644:14 | +| ir.cpp:642:10:642:20 | C::FieldAccess() -> void | 13 | 8 | 1 | 1 | | =1 | prvalue: int | ir.cpp:644:23:644:23 | +| ir.cpp:642:10:642:20 | C::FieldAccess() -> void | 14 | 1 | 2 | ExprStmt | | | | ir.cpp:645:9:645:16 | +| ir.cpp:642:10:642:20 | C::FieldAccess() -> void | 15 | 14 | 0 | ... = ... | | | lvalue: int | ir.cpp:645:9:645:15 | +| ir.cpp:642:10:642:20 | C::FieldAccess() -> void | 16 | 15 | 0 | m_a | | | lvalue: int | ir.cpp:645:9:645:11 | +| ir.cpp:642:10:642:20 | C::FieldAccess() -> void | 17 | 16 | -1 | this | | | prvalue(load): C * | file://:0:0:0:0 | +| ir.cpp:642:10:642:20 | C::FieldAccess() -> void | 18 | 15 | 1 | 2 | | =2 | prvalue: int | ir.cpp:645:15:645:15 | +| ir.cpp:642:10:642:20 | C::FieldAccess() -> void | 19 | 1 | 3 | declaration | | | | ir.cpp:646:9:646:14 | +| ir.cpp:642:10:642:20 | C::FieldAccess() -> void | 20 | 19 | 0 | definition of x | | | int | ir.cpp:646:13:646:13 | +| ir.cpp:642:10:642:20 | C::FieldAccess() -> void | 21 | 1 | 4 | ExprStmt | | | | ir.cpp:647:9:647:22 | +| ir.cpp:642:10:642:20 | C::FieldAccess() -> void | 22 | 21 | 0 | ... = ... | | | lvalue: int | ir.cpp:647:9:647:21 | +| ir.cpp:642:10:642:20 | C::FieldAccess() -> void | 23 | 22 | 0 | x | | | lvalue: int | ir.cpp:647:9:647:9 | +| ir.cpp:642:10:642:20 | C::FieldAccess() -> void | 24 | 22 | 1 | m_a | | | prvalue(load): int | ir.cpp:647:19:647:21 | +| ir.cpp:642:10:642:20 | C::FieldAccess() -> void | 25 | 24 | -1 | this | | | prvalue(load): C * | ir.cpp:647:13:647:16 | +| ir.cpp:642:10:642:20 | C::FieldAccess() -> void | 26 | 1 | 5 | ExprStmt | | | | ir.cpp:648:9:648:24 | +| ir.cpp:642:10:642:20 | C::FieldAccess() -> void | 27 | 26 | 0 | ... = ... | | | lvalue: int | ir.cpp:648:9:648:23 | +| ir.cpp:642:10:642:20 | C::FieldAccess() -> void | 28 | 27 | 0 | x | | | lvalue: int | ir.cpp:648:9:648:9 | +| ir.cpp:642:10:642:20 | C::FieldAccess() -> void | 29 | 27 | 1 | m_a | | | prvalue(load): int | ir.cpp:648:21:648:23 | +| ir.cpp:642:10:642:20 | C::FieldAccess() -> void | 30 | 29 | -1 | (...) | | | lvalue: C | ir.cpp:648:13:648:19 | +| ir.cpp:642:10:642:20 | C::FieldAccess() -> void | 31 | 30 | 0 | * ... | | | lvalue: C | ir.cpp:648:14:648:18 | +| ir.cpp:642:10:642:20 | C::FieldAccess() -> void | 32 | 31 | 0 | this | | | prvalue(load): C * | ir.cpp:648:15:648:18 | +| ir.cpp:642:10:642:20 | C::FieldAccess() -> void | 33 | 1 | 6 | ExprStmt | | | | ir.cpp:649:9:649:16 | +| ir.cpp:642:10:642:20 | C::FieldAccess() -> void | 34 | 33 | 0 | ... = ... | | | lvalue: int | ir.cpp:649:9:649:15 | +| ir.cpp:642:10:642:20 | C::FieldAccess() -> void | 35 | 34 | 0 | x | | | lvalue: int | ir.cpp:649:9:649:9 | +| ir.cpp:642:10:642:20 | C::FieldAccess() -> void | 36 | 34 | 1 | m_a | | | prvalue(load): int | ir.cpp:649:13:649:15 | +| ir.cpp:642:10:642:20 | C::FieldAccess() -> void | 37 | 36 | -1 | this | | | prvalue(load): C * | file://:0:0:0:0 | +| ir.cpp:642:10:642:20 | C::FieldAccess() -> void | 38 | 1 | 7 | return ... | | | | ir.cpp:650:5:650:5 | +| ir.cpp:652:10:652:20 | C::MethodCalls() -> void | 0 | -1 | 0 | MethodCalls | | | | ir.cpp:652:10:652:20 | +| ir.cpp:652:10:652:20 | C::MethodCalls() -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:652:24:656:5 | +| ir.cpp:652:10:652:20 | C::MethodCalls() -> void | 2 | 1 | 0 | ExprStmt | | | | ir.cpp:653:9:653:40 | +| ir.cpp:652:10:652:20 | C::MethodCalls() -> void | 3 | 2 | 0 | call to InstanceMemberFunction | | | prvalue: int | ir.cpp:653:15:653:36 | +| ir.cpp:652:10:652:20 | C::MethodCalls() -> void | 4 | 3 | -1 | this | | | prvalue(load): C * | ir.cpp:653:9:653:12 | +| ir.cpp:652:10:652:20 | C::MethodCalls() -> void | 5 | 3 | 0 | 0 | | =0 | prvalue: int | ir.cpp:653:38:653:38 | +| ir.cpp:652:10:652:20 | C::MethodCalls() -> void | 6 | 1 | 1 | ExprStmt | | | | ir.cpp:654:9:654:42 | +| ir.cpp:652:10:652:20 | C::MethodCalls() -> void | 7 | 6 | 0 | call to InstanceMemberFunction | | | prvalue: int | ir.cpp:654:17:654:38 | +| ir.cpp:652:10:652:20 | C::MethodCalls() -> void | 8 | 7 | -1 | (...) | | | lvalue: C | ir.cpp:654:9:654:15 | +| ir.cpp:652:10:652:20 | C::MethodCalls() -> void | 9 | 8 | 0 | * ... | | | lvalue: C | ir.cpp:654:10:654:14 | +| ir.cpp:652:10:652:20 | C::MethodCalls() -> void | 10 | 9 | 0 | this | | | prvalue(load): C * | ir.cpp:654:11:654:14 | +| ir.cpp:652:10:652:20 | C::MethodCalls() -> void | 11 | 7 | 0 | 1 | | =1 | prvalue: int | ir.cpp:654:40:654:40 | +| ir.cpp:652:10:652:20 | C::MethodCalls() -> void | 12 | 1 | 2 | ExprStmt | | | | ir.cpp:655:9:655:34 | +| ir.cpp:652:10:652:20 | C::MethodCalls() -> void | 13 | 12 | 0 | call to InstanceMemberFunction | | | prvalue: int | ir.cpp:655:9:655:30 | +| ir.cpp:652:10:652:20 | C::MethodCalls() -> void | 14 | 13 | -1 | this | | | prvalue(load): C * | file://:0:0:0:0 | +| ir.cpp:652:10:652:20 | C::MethodCalls() -> void | 15 | 13 | 0 | 2 | | =2 | prvalue: int | ir.cpp:655:32:655:32 | +| ir.cpp:652:10:652:20 | C::MethodCalls() -> void | 16 | 1 | 3 | return ... | | | | ir.cpp:656:5:656:5 | +| ir.cpp:658:5:658:5 | C::C() -> void | 0 | -1 | 0 | C | | | | ir.cpp:658:5:658:5 | +| ir.cpp:658:5:658:5 | C::C() -> void | 1 | 0 | 0 | constructor init of field m_a | | | prvalue: int | ir.cpp:659:9:659:14 | +| ir.cpp:658:5:658:5 | C::C() -> void | 2 | 1 | 0 | 1 | | =1 | prvalue: int | ir.cpp:659:9:659:14 | +| ir.cpp:658:5:658:5 | C::C() -> void | 3 | 0 | 1 | constructor init of field m_b | | | prvalue: String | ir.cpp:663:5:663:5 | +| ir.cpp:658:5:658:5 | C::C() -> void | 4 | 3 | 0 | call to String | | | prvalue: void | ir.cpp:663:5:663:5 | +| ir.cpp:658:5:658:5 | C::C() -> void | 5 | 0 | 2 | constructor init of field m_c | | | prvalue: char | ir.cpp:660:9:660:14 | +| ir.cpp:658:5:658:5 | C::C() -> void | 6 | 5 | 0 | (char)... | integral conversion | =3 | prvalue: char | ir.cpp:660:13:660:13 | +| ir.cpp:658:5:658:5 | C::C() -> void | 7 | 6 | 0 | 3 | | =3 | prvalue: int | ir.cpp:660:13:660:13 | +| ir.cpp:658:5:658:5 | C::C() -> void | 8 | 0 | 3 | constructor init of field m_e | | | prvalue: void * | ir.cpp:661:9:661:13 | +| ir.cpp:658:5:658:5 | C::C() -> void | 9 | 8 | 0 | 0 | | =0 | prvalue: void * | ir.cpp:661:9:661:13 | +| ir.cpp:658:5:658:5 | C::C() -> void | 10 | 0 | 4 | constructor init of field m_f | | | prvalue: String | ir.cpp:662:9:662:19 | +| ir.cpp:658:5:658:5 | C::C() -> void | 11 | 10 | 0 | call to String | | | prvalue: void | ir.cpp:662:9:662:19 | +| ir.cpp:658:5:658:5 | C::C() -> void | 12 | 11 | 0 | array to pointer conversion | | | prvalue: const char * | ir.cpp:662:13:662:18 | +| ir.cpp:658:5:658:5 | C::C() -> void | 13 | 12 | 0 | test | | =test | lvalue: const char[5] | ir.cpp:662:13:662:18 | +| ir.cpp:658:5:658:5 | C::C() -> void | 14 | 0 | 5 | { ... } | | | | ir.cpp:663:5:664:5 | +| ir.cpp:658:5:658:5 | C::C() -> void | 15 | 14 | 0 | return ... | | | | ir.cpp:664:5:664:5 | +| ir.cpp:675:5:675:18 | DerefReference(int &) -> int | 0 | -1 | 0 | DerefReference | | | | ir.cpp:675:5:675:18 | +| ir.cpp:675:5:675:18 | DerefReference(int &) -> int | 1 | 0 | 0 | { ... } | | | | ir.cpp:675:28:677:1 | +| ir.cpp:675:5:675:18 | DerefReference(int &) -> int | 2 | 1 | 0 | return ... | | | | ir.cpp:676:5:676:13 | +| ir.cpp:675:5:675:18 | DerefReference(int &) -> int | 3 | 2 | 0 | (reference dereference) | | | prvalue(load): int | ir.cpp:676:12:676:12 | +| ir.cpp:675:5:675:18 | DerefReference(int &) -> int | 4 | 3 | 0 | r | | | prvalue(load): int & | ir.cpp:676:12:676:12 | +| ir.cpp:679:6:679:18 | TakeReference() -> int & | 0 | -1 | 0 | TakeReference | | | | ir.cpp:679:6:679:18 | +| ir.cpp:679:6:679:18 | TakeReference() -> int & | 1 | 0 | 0 | { ... } | | | | ir.cpp:679:22:681:1 | +| ir.cpp:679:6:679:18 | TakeReference() -> int & | 2 | 1 | 0 | return ... | | | | ir.cpp:680:5:680:13 | +| ir.cpp:679:6:679:18 | TakeReference() -> int & | 3 | 2 | 0 | (reference to) | | | prvalue: int & | ir.cpp:680:12:680:12 | +| ir.cpp:679:6:679:18 | TakeReference() -> int & | 4 | 3 | 0 | g | | | lvalue: int | ir.cpp:680:12:680:12 | +| ir.cpp:683:9:683:23 | ReturnReference() -> String & | 0 | -1 | 0 | ReturnReference | | | | ir.cpp:683:9:683:23 | +| ir.cpp:685:6:685:18 | InitReference(int) -> void | 0 | -1 | 0 | InitReference | | | | ir.cpp:685:6:685:18 | +| ir.cpp:685:6:685:18 | InitReference(int) -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:685:27:689:1 | +| ir.cpp:685:6:685:18 | InitReference(int) -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:686:5:686:15 | +| ir.cpp:685:6:685:18 | InitReference(int) -> void | 3 | 2 | 0 | definition of r | | | int & | ir.cpp:686:10:686:10 | +| ir.cpp:685:6:685:18 | InitReference(int) -> void | 4 | 3 | 0 | initializer for r | | | | ir.cpp:686:13:686:14 | +| ir.cpp:685:6:685:18 | InitReference(int) -> void | 5 | 4 | 0 | (reference to) | | | prvalue: int & | ir.cpp:686:14:686:14 | +| ir.cpp:685:6:685:18 | InitReference(int) -> void | 6 | 5 | 0 | x | | | lvalue: int | ir.cpp:686:14:686:14 | +| ir.cpp:685:6:685:18 | InitReference(int) -> void | 7 | 1 | 1 | declaration | | | | ir.cpp:687:5:687:16 | +| ir.cpp:685:6:685:18 | InitReference(int) -> void | 8 | 7 | 0 | definition of r2 | | | int & | ir.cpp:687:10:687:11 | +| ir.cpp:685:6:685:18 | InitReference(int) -> void | 9 | 8 | 0 | initializer for r2 | | | | ir.cpp:687:14:687:15 | +| ir.cpp:685:6:685:18 | InitReference(int) -> void | 10 | 9 | 0 | (reference to) | | | prvalue: int & | ir.cpp:687:15:687:15 | +| ir.cpp:685:6:685:18 | InitReference(int) -> void | 11 | 10 | 0 | (reference dereference) | | | lvalue: int | ir.cpp:687:15:687:15 | +| ir.cpp:685:6:685:18 | InitReference(int) -> void | 12 | 11 | 0 | r | | | prvalue(load): int & | ir.cpp:687:15:687:15 | +| ir.cpp:685:6:685:18 | InitReference(int) -> void | 13 | 1 | 2 | declaration | | | | ir.cpp:688:5:688:41 | +| ir.cpp:685:6:685:18 | InitReference(int) -> void | 14 | 13 | 0 | definition of r3 | | | const String & | ir.cpp:688:19:688:20 | +| ir.cpp:685:6:685:18 | InitReference(int) -> void | 15 | 14 | 0 | initializer for r3 | | | | ir.cpp:688:23:688:40 | +| ir.cpp:685:6:685:18 | InitReference(int) -> void | 16 | 15 | 0 | (reference to) | | | prvalue: const String & | ir.cpp:688:24:688:41 | +| ir.cpp:685:6:685:18 | InitReference(int) -> void | 17 | 16 | 0 | (const String)... | glvalue conversion | | lvalue: const String | ir.cpp:688:24:688:41 | +| ir.cpp:685:6:685:18 | InitReference(int) -> void | 18 | 17 | 0 | (reference dereference) | | | lvalue: String | ir.cpp:688:24:688:41 | +| ir.cpp:685:6:685:18 | InitReference(int) -> void | 19 | 18 | 0 | call to ReturnReference | | | prvalue: String & | ir.cpp:688:24:688:38 | +| ir.cpp:685:6:685:18 | InitReference(int) -> void | 20 | 1 | 3 | return ... | | | | ir.cpp:689:1:689:1 | +| ir.cpp:691:6:691:20 | ArrayReferences() -> void | 0 | -1 | 0 | ArrayReferences | | | | ir.cpp:691:6:691:20 | +| ir.cpp:691:6:691:20 | ArrayReferences() -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:691:24:695:1 | +| ir.cpp:691:6:691:20 | ArrayReferences() -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:692:3:692:12 | +| ir.cpp:691:6:691:20 | ArrayReferences() -> void | 3 | 2 | 0 | definition of a | | | int[10] | ir.cpp:692:7:692:7 | +| ir.cpp:691:6:691:20 | ArrayReferences() -> void | 4 | 1 | 1 | declaration | | | | ir.cpp:693:3:693:20 | +| ir.cpp:691:6:691:20 | ArrayReferences() -> void | 5 | 4 | 0 | definition of ra | | | int(&)[10] | ir.cpp:693:9:693:10 | +| ir.cpp:691:6:691:20 | ArrayReferences() -> void | 6 | 5 | 0 | initializer for ra | | | | ir.cpp:693:18:693:19 | +| ir.cpp:691:6:691:20 | ArrayReferences() -> void | 7 | 6 | 0 | (reference to) | | | prvalue: int(&)[10] | ir.cpp:693:19:693:19 | +| ir.cpp:691:6:691:20 | ArrayReferences() -> void | 8 | 7 | 0 | a | | | lvalue: int[10] | ir.cpp:693:19:693:19 | +| ir.cpp:691:6:691:20 | ArrayReferences() -> void | 9 | 1 | 2 | declaration | | | | ir.cpp:694:3:694:16 | +| ir.cpp:691:6:691:20 | ArrayReferences() -> void | 10 | 9 | 0 | definition of x | | | int | ir.cpp:694:7:694:7 | +| ir.cpp:691:6:691:20 | ArrayReferences() -> void | 11 | 10 | 0 | initializer for x | | | | ir.cpp:694:10:694:15 | +| ir.cpp:691:6:691:20 | ArrayReferences() -> void | 12 | 11 | 0 | access to array | | | prvalue(load): int | ir.cpp:694:11:694:15 | +| ir.cpp:691:6:691:20 | ArrayReferences() -> void | 13 | 12 | 0 | array to pointer conversion | | | prvalue: int * | ir.cpp:694:11:694:12 | +| ir.cpp:691:6:691:20 | ArrayReferences() -> void | 14 | 13 | 0 | (reference dereference) | | | lvalue: int[10] | ir.cpp:694:11:694:12 | +| ir.cpp:691:6:691:20 | ArrayReferences() -> void | 15 | 14 | 0 | ra | | | prvalue(load): int(&)[10] | ir.cpp:694:11:694:12 | +| ir.cpp:691:6:691:20 | ArrayReferences() -> void | 16 | 12 | 1 | 5 | | =5 | prvalue: int | ir.cpp:694:14:694:14 | +| ir.cpp:691:6:691:20 | ArrayReferences() -> void | 17 | 1 | 3 | return ... | | | | ir.cpp:695:1:695:1 | +| ir.cpp:697:6:697:23 | FunctionReferences() -> void | 0 | -1 | 0 | FunctionReferences | | | | ir.cpp:697:6:697:23 | +| ir.cpp:697:6:697:23 | FunctionReferences() -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:697:27:701:1 | +| ir.cpp:697:6:697:23 | FunctionReferences() -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:698:3:698:33 | +| ir.cpp:697:6:697:23 | FunctionReferences() -> void | 3 | 2 | 0 | definition of rfn | | | ..(&)(..) | ir.cpp:698:8:698:10 | +| ir.cpp:697:6:697:23 | FunctionReferences() -> void | 4 | 3 | 0 | initializer for rfn | | | | ir.cpp:698:19:698:32 | +| ir.cpp:697:6:697:23 | FunctionReferences() -> void | 5 | 4 | 0 | (reference to) | | | prvalue: ..(&)(..) | ir.cpp:698:20:698:32 | +| ir.cpp:697:6:697:23 | FunctionReferences() -> void | 6 | 5 | 0 | FuncPtrTarget | | | lvalue: ..()(..) | ir.cpp:698:20:698:32 | +| ir.cpp:697:6:697:23 | FunctionReferences() -> void | 7 | 1 | 1 | declaration | | | | ir.cpp:699:3:699:23 | +| ir.cpp:697:6:697:23 | FunctionReferences() -> void | 8 | 7 | 0 | definition of pfn | | | ..(*)(..) | ir.cpp:699:8:699:10 | +| ir.cpp:697:6:697:23 | FunctionReferences() -> void | 9 | 8 | 0 | initializer for pfn | | | | ir.cpp:699:19:699:22 | +| ir.cpp:697:6:697:23 | FunctionReferences() -> void | 10 | 9 | 0 | (reference dereference) | | | prvalue(load): ..(*)(..) | ir.cpp:699:20:699:22 | +| ir.cpp:697:6:697:23 | FunctionReferences() -> void | 11 | 10 | 0 | rfn | | | prvalue(load): ..(&)(..) | ir.cpp:699:20:699:22 | +| ir.cpp:697:6:697:23 | FunctionReferences() -> void | 12 | 1 | 2 | ExprStmt | | | | ir.cpp:700:3:700:9 | +| ir.cpp:697:6:697:23 | FunctionReferences() -> void | 13 | 12 | 0 | call to expression | | | prvalue: int | ir.cpp:700:3:700:8 | +| ir.cpp:697:6:697:23 | FunctionReferences() -> void | 14 | 13 | 0 | (reference dereference) | | | prvalue(load): ..(*)(..) | ir.cpp:700:3:700:5 | +| ir.cpp:697:6:697:23 | FunctionReferences() -> void | 15 | 14 | 0 | rfn | | | prvalue(load): ..(&)(..) | ir.cpp:700:3:700:5 | +| ir.cpp:697:6:697:23 | FunctionReferences() -> void | 16 | 13 | 1 | 5 | | =5 | prvalue: int | ir.cpp:700:7:700:7 | +| ir.cpp:697:6:697:23 | FunctionReferences() -> void | 17 | 1 | 3 | return ... | | | | ir.cpp:701:1:701:1 | +| ir.cpp:704:3:704:5 | min(T, T) -> T | 0 | -1 | 0 | min | | | | ir.cpp:704:3:704:5 | +| ir.cpp:704:3:704:5 | min(T, T) -> T | 1 | 0 | 0 | { ... } | | | | ir.cpp:704:17:706:1 | +| ir.cpp:704:3:704:5 | min(T, T) -> T | 2 | 1 | 0 | return ... | | | | ir.cpp:705:3:705:25 | +| ir.cpp:704:3:704:5 | min(T, T) -> T | 3 | 2 | 0 | ... ? ... : ... | | | prvalue: unknown | ir.cpp:705:10:705:24 | +| ir.cpp:704:3:704:5 | min(T, T) -> T | 4 | 3 | 0 | (bool)... | conversion to bool | | prvalue: bool | ir.cpp:705:10:705:16 | +| ir.cpp:704:3:704:5 | min(T, T) -> T | 5 | 4 | 0 | (...) | | | prvalue: unknown | ir.cpp:705:10:705:16 | +| ir.cpp:704:3:704:5 | min(T, T) -> T | 6 | 5 | 0 | ... < ... | | | prvalue: unknown | ir.cpp:705:11:705:15 | +| ir.cpp:704:3:704:5 | min(T, T) -> T | 7 | 6 | 0 | x | | | lvalue: T | ir.cpp:705:11:705:11 | +| ir.cpp:704:3:704:5 | min(T, T) -> T | 8 | 6 | 1 | y | | | lvalue: T | ir.cpp:705:15:705:15 | +| ir.cpp:704:3:704:5 | min(T, T) -> T | 9 | 3 | 1 | x | | | lvalue: T | ir.cpp:705:20:705:20 | +| ir.cpp:704:3:704:5 | min(T, T) -> T | 10 | 3 | 2 | y | | | lvalue: T | ir.cpp:705:24:705:24 | +| ir.cpp:704:3:704:5 | min(int, int) -> int | 0 | -1 | 0 | min | | | | ir.cpp:704:3:704:5 | +| ir.cpp:704:3:704:5 | min(int, int) -> int | 1 | 0 | 0 | { ... } | | | | ir.cpp:704:17:706:1 | +| ir.cpp:704:3:704:5 | min(int, int) -> int | 2 | 1 | 0 | return ... | | | | ir.cpp:705:3:705:25 | +| ir.cpp:704:3:704:5 | min(int, int) -> int | 3 | 2 | 0 | ... ? ... : ... | | | prvalue: int | ir.cpp:705:10:705:24 | +| ir.cpp:704:3:704:5 | min(int, int) -> int | 4 | 3 | 0 | (...) | | | prvalue: bool | ir.cpp:705:10:705:16 | +| ir.cpp:704:3:704:5 | min(int, int) -> int | 5 | 4 | 0 | ... < ... | | | prvalue: bool | ir.cpp:705:11:705:15 | +| ir.cpp:704:3:704:5 | min(int, int) -> int | 6 | 5 | 0 | x | | | prvalue(load): int | ir.cpp:705:11:705:11 | +| ir.cpp:704:3:704:5 | min(int, int) -> int | 7 | 5 | 1 | y | | | prvalue(load): int | ir.cpp:705:15:705:15 | +| ir.cpp:704:3:704:5 | min(int, int) -> int | 8 | 3 | 1 | x | | | prvalue(load): int | ir.cpp:705:20:705:20 | +| ir.cpp:704:3:704:5 | min(int, int) -> int | 9 | 3 | 2 | y | | | prvalue(load): int | ir.cpp:705:24:705:24 | +| ir.cpp:708:5:708:11 | CallMin(int, int) -> int | 0 | -1 | 0 | CallMin | | | | ir.cpp:708:5:708:11 | +| ir.cpp:708:5:708:11 | CallMin(int, int) -> int | 1 | 0 | 0 | { ... } | | | | ir.cpp:708:27:710:1 | +| ir.cpp:708:5:708:11 | CallMin(int, int) -> int | 2 | 1 | 0 | return ... | | | | ir.cpp:709:3:709:19 | +| ir.cpp:708:5:708:11 | CallMin(int, int) -> int | 3 | 2 | 0 | call to min | | | prvalue: int | ir.cpp:709:10:709:12 | +| ir.cpp:708:5:708:11 | CallMin(int, int) -> int | 4 | 3 | 0 | x | | | prvalue(load): int | ir.cpp:709:14:709:14 | +| ir.cpp:708:5:708:11 | CallMin(int, int) -> int | 5 | 3 | 1 | y | | | prvalue(load): int | ir.cpp:709:17:709:17 | +| ir.cpp:713:8:713:8 | Outer::operator=(Outer &&) -> Outer & | 0 | -1 | 0 | operator= | | | | ir.cpp:713:8:713:8 | +| ir.cpp:713:8:713:8 | Outer::operator=(const Outer &) -> Outer & | 0 | -1 | 0 | operator= | | | | ir.cpp:713:8:713:8 | +| ir.cpp:715:12:715:15 | Outer::Func(U, V) -> T | 0 | -1 | 0 | Func | | | | ir.cpp:715:12:715:15 | +| ir.cpp:715:12:715:15 | Outer::Func(U, V) -> T | 1 | 0 | 0 | { ... } | | | | ir.cpp:715:27:717:3 | +| ir.cpp:715:12:715:15 | Outer::Func(U, V) -> T | 2 | 1 | 0 | return ... | | | | ir.cpp:716:5:716:15 | +| ir.cpp:715:12:715:15 | Outer::Func(U, V) -> T | 3 | 2 | 0 | 0 | | =0 | prvalue: T | ir.cpp:716:12:716:14 | +| ir.cpp:715:12:715:15 | Outer::Func(U, V) -> long | 0 | -1 | 0 | Func | | | | ir.cpp:715:12:715:15 | +| ir.cpp:715:12:715:15 | Outer::Func(void *, char) -> long | 0 | -1 | 0 | Func | | | | ir.cpp:715:12:715:15 | +| ir.cpp:715:12:715:15 | Outer::Func(void *, char) -> long | 1 | 0 | 0 | { ... } | | | | ir.cpp:715:27:717:3 | +| ir.cpp:715:12:715:15 | Outer::Func(void *, char) -> long | 2 | 1 | 0 | return ... | | | | ir.cpp:716:5:716:15 | +| ir.cpp:715:12:715:15 | Outer::Func(void *, char) -> long | 3 | 2 | 0 | 0 | | =0 | prvalue: long | ir.cpp:716:12:716:14 | +| ir.cpp:720:8:720:29 | CallNestedTemplateFunc() -> double | 0 | -1 | 0 | CallNestedTemplateFunc | | | | ir.cpp:720:8:720:29 | +| ir.cpp:720:8:720:29 | CallNestedTemplateFunc() -> double | 1 | 0 | 0 | { ... } | | | | ir.cpp:720:33:722:1 | +| ir.cpp:720:8:720:29 | CallNestedTemplateFunc() -> double | 2 | 1 | 0 | return ... | | | | ir.cpp:721:3:721:54 | +| ir.cpp:720:8:720:29 | CallNestedTemplateFunc() -> double | 3 | 2 | 0 | (double)... | integral to floating point conversion | | prvalue: double | ir.cpp:721:10:721:53 | +| ir.cpp:720:8:720:29 | CallNestedTemplateFunc() -> double | 4 | 3 | 0 | call to Func | | | prvalue: long | ir.cpp:721:10:721:39 | +| ir.cpp:720:8:720:29 | CallNestedTemplateFunc() -> double | 5 | 4 | 0 | (void *)... | pointer conversion | =0 | prvalue: void * | ir.cpp:721:41:721:47 | +| ir.cpp:720:8:720:29 | CallNestedTemplateFunc() -> double | 6 | 5 | 0 | 0 | | =0 | prvalue: decltype(nullptr) | ir.cpp:721:41:721:47 | +| ir.cpp:720:8:720:29 | CallNestedTemplateFunc() -> double | 7 | 4 | 1 | 111 | | =111 | prvalue: char | ir.cpp:721:50:721:52 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 0 | -1 | 0 | TryCatch | | | | ir.cpp:724:6:724:13 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:724:23:743:1 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 2 | 1 | 0 | try { ... } | | | | ir.cpp:725:3:734:3 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 3 | 2 | 0 | { ... } | | | | ir.cpp:725:7:734:3 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 4 | 3 | 0 | declaration | | | | ir.cpp:726:5:726:14 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 5 | 4 | 0 | definition of x | | | int | ir.cpp:726:9:726:9 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 6 | 5 | 0 | initializer for x | | | | ir.cpp:726:12:726:13 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 7 | 6 | 0 | 5 | | =5 | prvalue: int | ir.cpp:726:12:726:13 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 8 | 3 | 1 | if (...) ... | | | | ir.cpp:727:5:732:5 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 9 | 8 | 0 | b | | | prvalue(load): bool | ir.cpp:727:9:727:9 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 10 | 8 | 1 | { ... } | | | | ir.cpp:727:12:729:5 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 11 | 10 | 0 | ExprStmt | | | | ir.cpp:728:7:728:29 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 12 | 11 | 0 | throw ... | | | prvalue: const char * | ir.cpp:728:7:728:28 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 13 | 12 | 0 | array to pointer conversion | | | prvalue: const char * | ir.cpp:728:13:728:28 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 14 | 13 | 0 | string literal | | =string literal | lvalue: const char[15] | ir.cpp:728:13:728:28 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 15 | 8 | 2 | if (...) ... | | | | ir.cpp:730:10:732:5 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 16 | 15 | 0 | ... < ... | | | prvalue: bool | ir.cpp:730:14:730:18 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 17 | 16 | 0 | x | | | prvalue(load): int | ir.cpp:730:14:730:14 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 18 | 16 | 1 | 2 | | =2 | prvalue: int | ir.cpp:730:18:730:18 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 19 | 15 | 1 | { ... } | | | | ir.cpp:730:21:732:5 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 20 | 19 | 0 | ExprStmt | | | | ir.cpp:731:7:731:48 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 21 | 20 | 0 | ... = ... | | | lvalue: int | ir.cpp:731:7:731:47 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 22 | 21 | 0 | x | | | lvalue: int | ir.cpp:731:7:731:7 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 23 | 21 | 1 | ... ? ... : ... | | | prvalue: int | ir.cpp:731:11:731:47 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 24 | 23 | 0 | b | | | prvalue(load): bool | ir.cpp:731:11:731:11 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 25 | 23 | 1 | 7 | | =7 | prvalue: int | ir.cpp:731:15:731:15 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 26 | 23 | 2 | throw ... | | | prvalue: String | ir.cpp:731:19:731:47 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 27 | 26 | 0 | call to String | | | prvalue: void | ir.cpp:731:19:731:47 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 28 | 27 | 0 | array to pointer conversion | | | prvalue: const char * | ir.cpp:731:32:731:46 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 29 | 28 | 0 | String object | | =String object | lvalue: const char[14] | ir.cpp:731:32:731:46 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 30 | 3 | 2 | ExprStmt | | | | ir.cpp:733:5:733:10 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 31 | 30 | 0 | ... = ... | | | lvalue: int | ir.cpp:733:5:733:9 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 32 | 31 | 0 | x | | | lvalue: int | ir.cpp:733:5:733:5 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 33 | 31 | 1 | 7 | | =7 | prvalue: int | ir.cpp:733:9:733:9 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 34 | 2 | 1 | | | | | ir.cpp:735:25:737:3 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 35 | 34 | 0 | { ... } | | | | ir.cpp:735:25:737:3 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 36 | 35 | 0 | ExprStmt | | | | ir.cpp:736:5:736:20 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 37 | 36 | 0 | throw ... | | | prvalue: String | ir.cpp:736:5:736:19 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 38 | 37 | 0 | call to String | | | prvalue: void | ir.cpp:736:5:736:19 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 39 | 38 | 0 | s | | | prvalue(load): const char * | ir.cpp:736:18:736:18 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 40 | 2 | 2 | | | | | ir.cpp:738:27:739:3 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 41 | 40 | 0 | { ... } | | | | ir.cpp:738:27:739:3 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 42 | 2 | 3 | | | | | ir.cpp:740:15:742:3 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 43 | 42 | 0 | { ... } | | | | ir.cpp:740:15:742:3 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 44 | 43 | 0 | ExprStmt | | | | ir.cpp:741:5:741:10 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 45 | 44 | 0 | re-throw exception | | | prvalue: void | ir.cpp:741:5:741:9 | +| ir.cpp:724:6:724:13 | TryCatch(bool) -> void | 46 | 1 | 1 | return ... | | | | ir.cpp:743:1:743:1 | +| ir.cpp:745:8:745:8 | Base::Base(const Base &) -> void | 0 | -1 | 0 | Base | | | | ir.cpp:745:8:745:8 | +| ir.cpp:745:8:745:8 | Base::Base(const Base &) -> void | 1 | 0 | 0 | constructor init of field base_s | | | prvalue: String | ir.cpp:745:8:745:8 | +| ir.cpp:745:8:745:8 | Base::Base(const Base &) -> void | 2 | 1 | 0 | call to String | | | prvalue: void | ir.cpp:745:8:745:8 | +| ir.cpp:745:8:745:8 | Base::Base(const Base &) -> void | 3 | 0 | 1 | { ... } | | | | ir.cpp:745:8:745:8 | +| ir.cpp:745:8:745:8 | Base::Base(const Base &) -> void | 4 | 3 | 0 | return ... | | | | ir.cpp:745:8:745:8 | +| ir.cpp:745:8:745:8 | Base::operator=(const Base &) -> Base & | 0 | -1 | 0 | operator= | | | | ir.cpp:745:8:745:8 | +| ir.cpp:745:8:745:8 | Base::operator=(const Base &) -> Base & | 1 | 0 | 0 | { ... } | | | | file://:0:0:0:0 | +| ir.cpp:745:8:745:8 | Base::operator=(const Base &) -> Base & | 2 | 1 | 0 | ExprStmt | | | | file://:0:0:0:0 | +| ir.cpp:745:8:745:8 | Base::operator=(const Base &) -> Base & | 3 | 2 | 0 | (reference dereference) | | | lvalue: String | file://:0:0:0:0 | +| ir.cpp:745:8:745:8 | Base::operator=(const Base &) -> Base & | 4 | 3 | 0 | call to operator= | | | prvalue: String & | ir.cpp:745:8:745:8 | +| ir.cpp:745:8:745:8 | Base::operator=(const Base &) -> Base & | 5 | 4 | -1 | & ... | | | prvalue: String * | file://:0:0:0:0 | +| ir.cpp:745:8:745:8 | Base::operator=(const Base &) -> Base & | 6 | 5 | 0 | base_s | | | lvalue: String | file://:0:0:0:0 | +| ir.cpp:745:8:745:8 | Base::operator=(const Base &) -> Base & | 7 | 6 | -1 | this | | | prvalue(load): Base * | file://:0:0:0:0 | +| ir.cpp:745:8:745:8 | Base::operator=(const Base &) -> Base & | 8 | 4 | 0 | (reference to) | | | prvalue: const String & | file://:0:0:0:0 | +| ir.cpp:745:8:745:8 | Base::operator=(const Base &) -> Base & | 9 | 8 | 0 | base_s | | | lvalue: String | file://:0:0:0:0 | +| ir.cpp:745:8:745:8 | Base::operator=(const Base &) -> Base & | 10 | 9 | -1 | (reference dereference) | | | lvalue: const Base | file://:0:0:0:0 | +| ir.cpp:745:8:745:8 | Base::operator=(const Base &) -> Base & | 11 | 10 | 0 | p#0 | | | prvalue(load): const Base & | file://:0:0:0:0 | +| ir.cpp:745:8:745:8 | Base::operator=(const Base &) -> Base & | 12 | 1 | 1 | return ... | | | | file://:0:0:0:0 | +| ir.cpp:745:8:745:8 | Base::operator=(const Base &) -> Base & | 13 | 12 | 0 | (reference to) | | | prvalue: Base & | file://:0:0:0:0 | +| ir.cpp:745:8:745:8 | Base::operator=(const Base &) -> Base & | 14 | 13 | 0 | * ... | | | lvalue: Base | file://:0:0:0:0 | +| ir.cpp:745:8:745:8 | Base::operator=(const Base &) -> Base & | 15 | 14 | 0 | this | | | prvalue(load): Base * | file://:0:0:0:0 | +| ir.cpp:748:3:748:6 | Base::Base() -> void | 0 | -1 | 0 | Base | | | | ir.cpp:748:3:748:6 | +| ir.cpp:748:3:748:6 | Base::Base() -> void | 1 | 0 | 0 | constructor init of field base_s | | | prvalue: String | ir.cpp:748:10:748:10 | +| ir.cpp:748:3:748:6 | Base::Base() -> void | 2 | 1 | 0 | call to String | | | prvalue: void | ir.cpp:748:10:748:10 | +| ir.cpp:748:3:748:6 | Base::Base() -> void | 3 | 0 | 1 | { ... } | | | | ir.cpp:748:10:749:3 | +| ir.cpp:748:3:748:6 | Base::Base() -> void | 4 | 3 | 0 | return ... | | | | ir.cpp:749:3:749:3 | +| ir.cpp:750:3:750:7 | Base::~Base() -> void | 0 | -1 | 0 | ~Base | | | | ir.cpp:750:3:750:7 | +| ir.cpp:750:3:750:7 | Base::~Base() -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:750:11:751:3 | +| ir.cpp:750:3:750:7 | Base::~Base() -> void | 2 | 1 | 0 | return ... | | | | ir.cpp:751:3:751:3 | +| ir.cpp:750:3:750:7 | Base::~Base() -> void | 3 | 0 | 1 | destructor field destruction of base_s | | | prvalue: String | ir.cpp:751:3:751:3 | +| ir.cpp:750:3:750:7 | Base::~Base() -> void | 4 | 3 | 0 | call to ~String | | | prvalue: void | ir.cpp:751:3:751:3 | +| ir.cpp:750:3:750:7 | Base::~Base() -> void | 5 | 4 | -1 | base_s | | | lvalue: String | ir.cpp:751:3:751:3 | +| ir.cpp:754:8:754:8 | Middle::Middle(const Middle &) -> void | 0 | -1 | 0 | Middle | | | | ir.cpp:754:8:754:8 | +| ir.cpp:754:8:754:8 | Middle::operator=(const Middle &) -> Middle & | 0 | -1 | 0 | operator= | | | | ir.cpp:754:8:754:8 | +| ir.cpp:754:8:754:8 | Middle::operator=(const Middle &) -> Middle & | 1 | 0 | 0 | { ... } | | | | file://:0:0:0:0 | +| ir.cpp:754:8:754:8 | Middle::operator=(const Middle &) -> Middle & | 2 | 1 | 0 | ExprStmt | | | | file://:0:0:0:0 | +| ir.cpp:754:8:754:8 | Middle::operator=(const Middle &) -> Middle & | 3 | 2 | 0 | (reference dereference) | | | lvalue: Base | file://:0:0:0:0 | +| ir.cpp:754:8:754:8 | Middle::operator=(const Middle &) -> Middle & | 4 | 3 | 0 | call to operator= | | | prvalue: Base & | ir.cpp:754:8:754:8 | +| ir.cpp:754:8:754:8 | Middle::operator=(const Middle &) -> Middle & | 5 | 4 | -1 | (Base *)... | base class conversion | | prvalue: Base * | file://:0:0:0:0 | +| ir.cpp:754:8:754:8 | Middle::operator=(const Middle &) -> Middle & | 6 | 5 | 0 | this | | | prvalue(load): Middle * | file://:0:0:0:0 | +| ir.cpp:754:8:754:8 | Middle::operator=(const Middle &) -> Middle & | 7 | 4 | 0 | (reference to) | | | prvalue: const Base & | file://:0:0:0:0 | +| ir.cpp:754:8:754:8 | Middle::operator=(const Middle &) -> Middle & | 8 | 7 | 0 | * ... | | | lvalue: const Base | file://:0:0:0:0 | +| ir.cpp:754:8:754:8 | Middle::operator=(const Middle &) -> Middle & | 9 | 8 | 0 | (const Base *)... | base class conversion | | prvalue: const Base * | file://:0:0:0:0 | +| ir.cpp:754:8:754:8 | Middle::operator=(const Middle &) -> Middle & | 10 | 9 | 0 | & ... | | | prvalue: const Middle * | file://:0:0:0:0 | +| ir.cpp:754:8:754:8 | Middle::operator=(const Middle &) -> Middle & | 11 | 10 | 0 | (reference dereference) | | | lvalue: const Middle | file://:0:0:0:0 | +| ir.cpp:754:8:754:8 | Middle::operator=(const Middle &) -> Middle & | 12 | 11 | 0 | p#0 | | | prvalue(load): const Middle & | file://:0:0:0:0 | +| ir.cpp:754:8:754:8 | Middle::operator=(const Middle &) -> Middle & | 13 | 1 | 1 | ExprStmt | | | | file://:0:0:0:0 | +| ir.cpp:754:8:754:8 | Middle::operator=(const Middle &) -> Middle & | 14 | 13 | 0 | (reference dereference) | | | lvalue: String | file://:0:0:0:0 | +| ir.cpp:754:8:754:8 | Middle::operator=(const Middle &) -> Middle & | 15 | 14 | 0 | call to operator= | | | prvalue: String & | ir.cpp:754:8:754:8 | +| ir.cpp:754:8:754:8 | Middle::operator=(const Middle &) -> Middle & | 16 | 15 | -1 | & ... | | | prvalue: String * | file://:0:0:0:0 | +| ir.cpp:754:8:754:8 | Middle::operator=(const Middle &) -> Middle & | 17 | 16 | 0 | middle_s | | | lvalue: String | file://:0:0:0:0 | +| ir.cpp:754:8:754:8 | Middle::operator=(const Middle &) -> Middle & | 18 | 17 | -1 | this | | | prvalue(load): Middle * | file://:0:0:0:0 | +| ir.cpp:754:8:754:8 | Middle::operator=(const Middle &) -> Middle & | 19 | 15 | 0 | (reference to) | | | prvalue: const String & | file://:0:0:0:0 | +| ir.cpp:754:8:754:8 | Middle::operator=(const Middle &) -> Middle & | 20 | 19 | 0 | middle_s | | | lvalue: String | file://:0:0:0:0 | +| ir.cpp:754:8:754:8 | Middle::operator=(const Middle &) -> Middle & | 21 | 20 | -1 | (reference dereference) | | | lvalue: const Middle | file://:0:0:0:0 | +| ir.cpp:754:8:754:8 | Middle::operator=(const Middle &) -> Middle & | 22 | 21 | 0 | p#0 | | | prvalue(load): const Middle & | file://:0:0:0:0 | +| ir.cpp:754:8:754:8 | Middle::operator=(const Middle &) -> Middle & | 23 | 1 | 2 | return ... | | | | file://:0:0:0:0 | +| ir.cpp:754:8:754:8 | Middle::operator=(const Middle &) -> Middle & | 24 | 23 | 0 | (reference to) | | | prvalue: Middle & | file://:0:0:0:0 | +| ir.cpp:754:8:754:8 | Middle::operator=(const Middle &) -> Middle & | 25 | 24 | 0 | * ... | | | lvalue: Middle | file://:0:0:0:0 | +| ir.cpp:754:8:754:8 | Middle::operator=(const Middle &) -> Middle & | 26 | 25 | 0 | this | | | prvalue(load): Middle * | file://:0:0:0:0 | +| ir.cpp:757:3:757:8 | Middle::Middle() -> void | 0 | -1 | 0 | Middle | | | | ir.cpp:757:3:757:8 | +| ir.cpp:757:3:757:8 | Middle::Middle() -> void | 1 | 0 | 0 | call to Base | | | prvalue: void | ir.cpp:757:12:757:12 | +| ir.cpp:757:3:757:8 | Middle::Middle() -> void | 2 | 0 | 1 | constructor init of field middle_s | | | prvalue: String | ir.cpp:757:12:757:12 | +| ir.cpp:757:3:757:8 | Middle::Middle() -> void | 3 | 2 | 0 | call to String | | | prvalue: void | ir.cpp:757:12:757:12 | +| ir.cpp:757:3:757:8 | Middle::Middle() -> void | 4 | 0 | 2 | { ... } | | | | ir.cpp:757:12:758:3 | +| ir.cpp:757:3:757:8 | Middle::Middle() -> void | 5 | 4 | 0 | return ... | | | | ir.cpp:758:3:758:3 | +| ir.cpp:759:3:759:9 | Middle::~Middle() -> void | 0 | -1 | 0 | ~Middle | | | | ir.cpp:759:3:759:9 | +| ir.cpp:759:3:759:9 | Middle::~Middle() -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:759:13:760:3 | +| ir.cpp:759:3:759:9 | Middle::~Middle() -> void | 2 | 1 | 0 | return ... | | | | ir.cpp:760:3:760:3 | +| ir.cpp:759:3:759:9 | Middle::~Middle() -> void | 3 | 0 | 1 | destructor field destruction of middle_s | | | prvalue: String | ir.cpp:760:3:760:3 | +| ir.cpp:759:3:759:9 | Middle::~Middle() -> void | 4 | 3 | 0 | call to ~String | | | prvalue: void | ir.cpp:760:3:760:3 | +| ir.cpp:759:3:759:9 | Middle::~Middle() -> void | 5 | 4 | -1 | middle_s | | | lvalue: String | ir.cpp:760:3:760:3 | +| ir.cpp:759:3:759:9 | Middle::~Middle() -> void | 6 | 0 | 2 | call to ~Base | | | prvalue: void | ir.cpp:760:3:760:3 | +| ir.cpp:763:8:763:8 | Derived::Derived(const Derived &) -> void | 0 | -1 | 0 | Derived | | | | ir.cpp:763:8:763:8 | +| ir.cpp:763:8:763:8 | Derived::operator=(const Derived &) -> Derived & | 0 | -1 | 0 | operator= | | | | ir.cpp:763:8:763:8 | +| ir.cpp:763:8:763:8 | Derived::operator=(const Derived &) -> Derived & | 1 | 0 | 0 | { ... } | | | | file://:0:0:0:0 | +| ir.cpp:763:8:763:8 | Derived::operator=(const Derived &) -> Derived & | 2 | 1 | 0 | ExprStmt | | | | file://:0:0:0:0 | +| ir.cpp:763:8:763:8 | Derived::operator=(const Derived &) -> Derived & | 3 | 2 | 0 | (reference dereference) | | | lvalue: Middle | file://:0:0:0:0 | +| ir.cpp:763:8:763:8 | Derived::operator=(const Derived &) -> Derived & | 4 | 3 | 0 | call to operator= | | | prvalue: Middle & | ir.cpp:763:8:763:8 | +| ir.cpp:763:8:763:8 | Derived::operator=(const Derived &) -> Derived & | 5 | 4 | -1 | (Middle *)... | base class conversion | | prvalue: Middle * | file://:0:0:0:0 | +| ir.cpp:763:8:763:8 | Derived::operator=(const Derived &) -> Derived & | 6 | 5 | 0 | this | | | prvalue(load): Derived * | file://:0:0:0:0 | +| ir.cpp:763:8:763:8 | Derived::operator=(const Derived &) -> Derived & | 7 | 4 | 0 | (reference to) | | | prvalue: const Middle & | file://:0:0:0:0 | +| ir.cpp:763:8:763:8 | Derived::operator=(const Derived &) -> Derived & | 8 | 7 | 0 | * ... | | | lvalue: const Middle | file://:0:0:0:0 | +| ir.cpp:763:8:763:8 | Derived::operator=(const Derived &) -> Derived & | 9 | 8 | 0 | (const Middle *)... | base class conversion | | prvalue: const Middle * | file://:0:0:0:0 | +| ir.cpp:763:8:763:8 | Derived::operator=(const Derived &) -> Derived & | 10 | 9 | 0 | & ... | | | prvalue: const Derived * | file://:0:0:0:0 | +| ir.cpp:763:8:763:8 | Derived::operator=(const Derived &) -> Derived & | 11 | 10 | 0 | (reference dereference) | | | lvalue: const Derived | file://:0:0:0:0 | +| ir.cpp:763:8:763:8 | Derived::operator=(const Derived &) -> Derived & | 12 | 11 | 0 | p#0 | | | prvalue(load): const Derived & | file://:0:0:0:0 | +| ir.cpp:763:8:763:8 | Derived::operator=(const Derived &) -> Derived & | 13 | 1 | 1 | ExprStmt | | | | file://:0:0:0:0 | +| ir.cpp:763:8:763:8 | Derived::operator=(const Derived &) -> Derived & | 14 | 13 | 0 | (reference dereference) | | | lvalue: String | file://:0:0:0:0 | +| ir.cpp:763:8:763:8 | Derived::operator=(const Derived &) -> Derived & | 15 | 14 | 0 | call to operator= | | | prvalue: String & | ir.cpp:763:8:763:8 | +| ir.cpp:763:8:763:8 | Derived::operator=(const Derived &) -> Derived & | 16 | 15 | -1 | & ... | | | prvalue: String * | file://:0:0:0:0 | +| ir.cpp:763:8:763:8 | Derived::operator=(const Derived &) -> Derived & | 17 | 16 | 0 | derived_s | | | lvalue: String | file://:0:0:0:0 | +| ir.cpp:763:8:763:8 | Derived::operator=(const Derived &) -> Derived & | 18 | 17 | -1 | this | | | prvalue(load): Derived * | file://:0:0:0:0 | +| ir.cpp:763:8:763:8 | Derived::operator=(const Derived &) -> Derived & | 19 | 15 | 0 | (reference to) | | | prvalue: const String & | file://:0:0:0:0 | +| ir.cpp:763:8:763:8 | Derived::operator=(const Derived &) -> Derived & | 20 | 19 | 0 | derived_s | | | lvalue: String | file://:0:0:0:0 | +| ir.cpp:763:8:763:8 | Derived::operator=(const Derived &) -> Derived & | 21 | 20 | -1 | (reference dereference) | | | lvalue: const Derived | file://:0:0:0:0 | +| ir.cpp:763:8:763:8 | Derived::operator=(const Derived &) -> Derived & | 22 | 21 | 0 | p#0 | | | prvalue(load): const Derived & | file://:0:0:0:0 | +| ir.cpp:763:8:763:8 | Derived::operator=(const Derived &) -> Derived & | 23 | 1 | 2 | return ... | | | | file://:0:0:0:0 | +| ir.cpp:763:8:763:8 | Derived::operator=(const Derived &) -> Derived & | 24 | 23 | 0 | (reference to) | | | prvalue: Derived & | file://:0:0:0:0 | +| ir.cpp:763:8:763:8 | Derived::operator=(const Derived &) -> Derived & | 25 | 24 | 0 | * ... | | | lvalue: Derived | file://:0:0:0:0 | +| ir.cpp:763:8:763:8 | Derived::operator=(const Derived &) -> Derived & | 26 | 25 | 0 | this | | | prvalue(load): Derived * | file://:0:0:0:0 | +| ir.cpp:766:3:766:9 | Derived::Derived() -> void | 0 | -1 | 0 | Derived | | | | ir.cpp:766:3:766:9 | +| ir.cpp:766:3:766:9 | Derived::Derived() -> void | 1 | 0 | 0 | call to Middle | | | prvalue: void | ir.cpp:766:13:766:13 | +| ir.cpp:766:3:766:9 | Derived::Derived() -> void | 2 | 0 | 1 | constructor init of field derived_s | | | prvalue: String | ir.cpp:766:13:766:13 | +| ir.cpp:766:3:766:9 | Derived::Derived() -> void | 3 | 2 | 0 | call to String | | | prvalue: void | ir.cpp:766:13:766:13 | +| ir.cpp:766:3:766:9 | Derived::Derived() -> void | 4 | 0 | 2 | { ... } | | | | ir.cpp:766:13:767:3 | +| ir.cpp:766:3:766:9 | Derived::Derived() -> void | 5 | 4 | 0 | return ... | | | | ir.cpp:767:3:767:3 | +| ir.cpp:768:3:768:10 | Derived::~Derived() -> void | 0 | -1 | 0 | ~Derived | | | | ir.cpp:768:3:768:10 | +| ir.cpp:768:3:768:10 | Derived::~Derived() -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:768:14:769:3 | +| ir.cpp:768:3:768:10 | Derived::~Derived() -> void | 2 | 1 | 0 | return ... | | | | ir.cpp:769:3:769:3 | +| ir.cpp:768:3:768:10 | Derived::~Derived() -> void | 3 | 0 | 1 | destructor field destruction of derived_s | | | prvalue: String | ir.cpp:769:3:769:3 | +| ir.cpp:768:3:768:10 | Derived::~Derived() -> void | 4 | 3 | 0 | call to ~String | | | prvalue: void | ir.cpp:769:3:769:3 | +| ir.cpp:768:3:768:10 | Derived::~Derived() -> void | 5 | 4 | -1 | derived_s | | | lvalue: String | ir.cpp:769:3:769:3 | +| ir.cpp:768:3:768:10 | Derived::~Derived() -> void | 6 | 0 | 2 | call to ~Middle | | | prvalue: void | ir.cpp:769:3:769:3 | +| ir.cpp:772:8:772:8 | MiddleVB1::MiddleVB1(const MiddleVB1 &) -> void | 0 | -1 | 0 | MiddleVB1 | | | | ir.cpp:772:8:772:8 | +| ir.cpp:772:8:772:8 | MiddleVB1::operator=(const MiddleVB1 &) -> MiddleVB1 & | 0 | -1 | 0 | operator= | | | | ir.cpp:772:8:772:8 | +| ir.cpp:775:3:775:11 | MiddleVB1::MiddleVB1() -> void | 0 | -1 | 0 | MiddleVB1 | | | | ir.cpp:775:3:775:11 | +| ir.cpp:775:3:775:11 | MiddleVB1::MiddleVB1() -> void | 1 | 0 | 0 | call to Base | | | prvalue: void | ir.cpp:775:15:775:15 | +| ir.cpp:775:3:775:11 | MiddleVB1::MiddleVB1() -> void | 2 | 0 | 1 | constructor init of field middlevb1_s | | | prvalue: String | ir.cpp:775:15:775:15 | +| ir.cpp:775:3:775:11 | MiddleVB1::MiddleVB1() -> void | 3 | 2 | 0 | call to String | | | prvalue: void | ir.cpp:775:15:775:15 | +| ir.cpp:775:3:775:11 | MiddleVB1::MiddleVB1() -> void | 4 | 0 | 2 | { ... } | | | | ir.cpp:775:15:776:3 | +| ir.cpp:775:3:775:11 | MiddleVB1::MiddleVB1() -> void | 5 | 4 | 0 | return ... | | | | ir.cpp:776:3:776:3 | +| ir.cpp:777:3:777:12 | MiddleVB1::~MiddleVB1() -> void | 0 | -1 | 0 | ~MiddleVB1 | | | | ir.cpp:777:3:777:12 | +| ir.cpp:777:3:777:12 | MiddleVB1::~MiddleVB1() -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:777:16:778:3 | +| ir.cpp:777:3:777:12 | MiddleVB1::~MiddleVB1() -> void | 2 | 1 | 0 | return ... | | | | ir.cpp:778:3:778:3 | +| ir.cpp:777:3:777:12 | MiddleVB1::~MiddleVB1() -> void | 3 | 0 | 1 | destructor field destruction of middlevb1_s | | | prvalue: String | ir.cpp:778:3:778:3 | +| ir.cpp:777:3:777:12 | MiddleVB1::~MiddleVB1() -> void | 4 | 3 | 0 | call to ~String | | | prvalue: void | ir.cpp:778:3:778:3 | +| ir.cpp:777:3:777:12 | MiddleVB1::~MiddleVB1() -> void | 5 | 4 | -1 | middlevb1_s | | | lvalue: String | ir.cpp:778:3:778:3 | +| ir.cpp:777:3:777:12 | MiddleVB1::~MiddleVB1() -> void | 6 | 0 | 2 | call to ~Base | | | prvalue: void | ir.cpp:778:3:778:3 | +| ir.cpp:781:8:781:8 | MiddleVB2::MiddleVB2(const MiddleVB2 &) -> void | 0 | -1 | 0 | MiddleVB2 | | | | ir.cpp:781:8:781:8 | +| ir.cpp:781:8:781:8 | MiddleVB2::operator=(const MiddleVB2 &) -> MiddleVB2 & | 0 | -1 | 0 | operator= | | | | ir.cpp:781:8:781:8 | +| ir.cpp:784:3:784:11 | MiddleVB2::MiddleVB2() -> void | 0 | -1 | 0 | MiddleVB2 | | | | ir.cpp:784:3:784:11 | +| ir.cpp:784:3:784:11 | MiddleVB2::MiddleVB2() -> void | 1 | 0 | 0 | call to Base | | | prvalue: void | ir.cpp:784:15:784:15 | +| ir.cpp:784:3:784:11 | MiddleVB2::MiddleVB2() -> void | 2 | 0 | 1 | constructor init of field middlevb2_s | | | prvalue: String | ir.cpp:784:15:784:15 | +| ir.cpp:784:3:784:11 | MiddleVB2::MiddleVB2() -> void | 3 | 2 | 0 | call to String | | | prvalue: void | ir.cpp:784:15:784:15 | +| ir.cpp:784:3:784:11 | MiddleVB2::MiddleVB2() -> void | 4 | 0 | 2 | { ... } | | | | ir.cpp:784:15:785:3 | +| ir.cpp:784:3:784:11 | MiddleVB2::MiddleVB2() -> void | 5 | 4 | 0 | return ... | | | | ir.cpp:785:3:785:3 | +| ir.cpp:786:3:786:12 | MiddleVB2::~MiddleVB2() -> void | 0 | -1 | 0 | ~MiddleVB2 | | | | ir.cpp:786:3:786:12 | +| ir.cpp:786:3:786:12 | MiddleVB2::~MiddleVB2() -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:786:16:787:3 | +| ir.cpp:786:3:786:12 | MiddleVB2::~MiddleVB2() -> void | 2 | 1 | 0 | return ... | | | | ir.cpp:787:3:787:3 | +| ir.cpp:786:3:786:12 | MiddleVB2::~MiddleVB2() -> void | 3 | 0 | 1 | destructor field destruction of middlevb2_s | | | prvalue: String | ir.cpp:787:3:787:3 | +| ir.cpp:786:3:786:12 | MiddleVB2::~MiddleVB2() -> void | 4 | 3 | 0 | call to ~String | | | prvalue: void | ir.cpp:787:3:787:3 | +| ir.cpp:786:3:786:12 | MiddleVB2::~MiddleVB2() -> void | 5 | 4 | -1 | middlevb2_s | | | lvalue: String | ir.cpp:787:3:787:3 | +| ir.cpp:786:3:786:12 | MiddleVB2::~MiddleVB2() -> void | 6 | 0 | 2 | call to ~Base | | | prvalue: void | ir.cpp:787:3:787:3 | +| ir.cpp:790:8:790:8 | DerivedVB::DerivedVB(const DerivedVB &) -> void | 0 | -1 | 0 | DerivedVB | | | | ir.cpp:790:8:790:8 | +| ir.cpp:790:8:790:8 | DerivedVB::operator=(const DerivedVB &) -> DerivedVB & | 0 | -1 | 0 | operator= | | | | ir.cpp:790:8:790:8 | +| ir.cpp:793:3:793:11 | DerivedVB::DerivedVB() -> void | 0 | -1 | 0 | DerivedVB | | | | ir.cpp:793:3:793:11 | +| ir.cpp:793:3:793:11 | DerivedVB::DerivedVB() -> void | 1 | 0 | 0 | call to Base | | | prvalue: void | ir.cpp:793:15:793:15 | +| ir.cpp:793:3:793:11 | DerivedVB::DerivedVB() -> void | 2 | 0 | 1 | call to MiddleVB1 | | | prvalue: void | ir.cpp:793:15:793:15 | +| ir.cpp:793:3:793:11 | DerivedVB::DerivedVB() -> void | 3 | 0 | 2 | call to MiddleVB2 | | | prvalue: void | ir.cpp:793:15:793:15 | +| ir.cpp:793:3:793:11 | DerivedVB::DerivedVB() -> void | 4 | 0 | 3 | constructor init of field derivedvb_s | | | prvalue: String | ir.cpp:793:15:793:15 | +| ir.cpp:793:3:793:11 | DerivedVB::DerivedVB() -> void | 5 | 4 | 0 | call to String | | | prvalue: void | ir.cpp:793:15:793:15 | +| ir.cpp:793:3:793:11 | DerivedVB::DerivedVB() -> void | 6 | 0 | 4 | { ... } | | | | ir.cpp:793:15:794:3 | +| ir.cpp:793:3:793:11 | DerivedVB::DerivedVB() -> void | 7 | 6 | 0 | return ... | | | | ir.cpp:794:3:794:3 | +| ir.cpp:795:3:795:12 | DerivedVB::~DerivedVB() -> void | 0 | -1 | 0 | ~DerivedVB | | | | ir.cpp:795:3:795:12 | +| ir.cpp:795:3:795:12 | DerivedVB::~DerivedVB() -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:795:16:796:3 | +| ir.cpp:795:3:795:12 | DerivedVB::~DerivedVB() -> void | 2 | 1 | 0 | return ... | | | | ir.cpp:796:3:796:3 | +| ir.cpp:795:3:795:12 | DerivedVB::~DerivedVB() -> void | 3 | 0 | 1 | destructor field destruction of derivedvb_s | | | prvalue: String | ir.cpp:796:3:796:3 | +| ir.cpp:795:3:795:12 | DerivedVB::~DerivedVB() -> void | 4 | 3 | 0 | call to ~String | | | prvalue: void | ir.cpp:796:3:796:3 | +| ir.cpp:795:3:795:12 | DerivedVB::~DerivedVB() -> void | 5 | 4 | -1 | derivedvb_s | | | lvalue: String | ir.cpp:796:3:796:3 | +| ir.cpp:795:3:795:12 | DerivedVB::~DerivedVB() -> void | 6 | 0 | 2 | call to ~MiddleVB2 | | | prvalue: void | ir.cpp:796:3:796:3 | +| ir.cpp:795:3:795:12 | DerivedVB::~DerivedVB() -> void | 7 | 0 | 3 | call to ~MiddleVB1 | | | prvalue: void | ir.cpp:796:3:796:3 | +| ir.cpp:795:3:795:12 | DerivedVB::~DerivedVB() -> void | 8 | 0 | 4 | call to ~Base | | | prvalue: void | ir.cpp:796:3:796:3 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 0 | -1 | 0 | HierarchyConversions | | | | ir.cpp:799:6:799:25 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:799:29:840:1 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:800:3:800:9 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 3 | 2 | 0 | definition of b | | | Base | ir.cpp:800:8:800:8 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 4 | 3 | 0 | initializer for b | | | | ir.cpp:800:8:800:8 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 5 | 4 | 0 | call to Base | | | prvalue: void | ir.cpp:800:8:800:8 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 6 | 1 | 1 | declaration | | | | ir.cpp:801:3:801:11 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 7 | 6 | 0 | definition of m | | | Middle | ir.cpp:801:10:801:10 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 8 | 7 | 0 | initializer for m | | | | ir.cpp:801:10:801:10 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 9 | 8 | 0 | call to Middle | | | prvalue: void | ir.cpp:801:10:801:10 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 10 | 1 | 2 | declaration | | | | ir.cpp:802:3:802:12 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 11 | 10 | 0 | definition of d | | | Derived | ir.cpp:802:11:802:11 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 12 | 11 | 0 | initializer for d | | | | ir.cpp:802:11:802:11 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 13 | 12 | 0 | call to Derived | | | prvalue: void | ir.cpp:802:11:802:11 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 14 | 1 | 3 | declaration | | | | ir.cpp:804:3:804:16 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 15 | 14 | 0 | definition of pb | | | Base * | ir.cpp:804:9:804:10 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 16 | 15 | 0 | initializer for pb | | | | ir.cpp:804:13:804:15 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 17 | 16 | 0 | & ... | | | prvalue: Base * | ir.cpp:804:14:804:15 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 18 | 17 | 0 | b | | | lvalue: Base | ir.cpp:804:15:804:15 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 19 | 1 | 4 | declaration | | | | ir.cpp:805:3:805:18 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 20 | 19 | 0 | definition of pm | | | Middle * | ir.cpp:805:11:805:12 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 21 | 20 | 0 | initializer for pm | | | | ir.cpp:805:15:805:17 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 22 | 21 | 0 | & ... | | | prvalue: Middle * | ir.cpp:805:16:805:17 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 23 | 22 | 0 | m | | | lvalue: Middle | ir.cpp:805:17:805:17 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 24 | 1 | 5 | declaration | | | | ir.cpp:806:3:806:19 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 25 | 24 | 0 | definition of pd | | | Derived * | ir.cpp:806:12:806:13 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 26 | 25 | 0 | initializer for pd | | | | ir.cpp:806:16:806:18 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 27 | 26 | 0 | & ... | | | prvalue: Derived * | ir.cpp:806:17:806:18 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 28 | 27 | 0 | d | | | lvalue: Derived | ir.cpp:806:18:806:18 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 29 | 1 | 6 | ExprStmt | | | | ir.cpp:808:3:808:8 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 30 | 29 | 0 | (reference dereference) | | | lvalue: Base | ir.cpp:808:5:808:8 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 31 | 30 | 0 | call to operator= | | | prvalue: Base & | ir.cpp:808:5:808:5 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 32 | 31 | -1 | b | | | lvalue: Base | ir.cpp:808:3:808:3 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 33 | 31 | 0 | (reference to) | | | prvalue: const Base & | ir.cpp:808:7:808:7 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 34 | 33 | 0 | (const Base)... | base class conversion | | lvalue: const Base | ir.cpp:808:7:808:7 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 35 | 34 | 0 | m | | | lvalue: Middle | ir.cpp:808:7:808:7 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 36 | 1 | 7 | ExprStmt | | | | ir.cpp:809:3:809:14 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 37 | 36 | 0 | (reference dereference) | | | lvalue: Base | ir.cpp:809:5:809:14 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 38 | 37 | 0 | call to operator= | | | prvalue: Base & | ir.cpp:809:5:809:5 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 39 | 38 | -1 | b | | | lvalue: Base | ir.cpp:809:3:809:3 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 40 | 38 | 0 | (reference to) | | | prvalue: const Base & | ir.cpp:809:7:809:13 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 41 | 40 | 0 | (const Base)... | prvalue adjustment conversion | | prvalue: const Base | ir.cpp:809:7:809:13 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 42 | 41 | 0 | call to Base | | | prvalue: void | ir.cpp:809:7:809:13 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 43 | 42 | 0 | (reference to) | | | prvalue: const Base & | ir.cpp:809:13:809:13 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 44 | 43 | 0 | (const Base)... | base class conversion | | lvalue: const Base | ir.cpp:809:13:809:13 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 45 | 44 | 0 | m | | | lvalue: Middle | ir.cpp:809:13:809:13 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 46 | 1 | 8 | ExprStmt | | | | ir.cpp:810:3:810:27 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 47 | 46 | 0 | (reference dereference) | | | lvalue: Base | ir.cpp:810:5:810:27 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 48 | 47 | 0 | call to operator= | | | prvalue: Base & | ir.cpp:810:5:810:5 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 49 | 48 | -1 | b | | | lvalue: Base | ir.cpp:810:3:810:3 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 50 | 48 | 0 | (reference to) | | | prvalue: const Base & | ir.cpp:810:7:810:26 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 51 | 50 | 0 | (const Base)... | prvalue adjustment conversion | | prvalue: const Base | ir.cpp:810:7:810:26 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 52 | 51 | 0 | call to Base | | | prvalue: void | ir.cpp:810:7:810:26 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 53 | 52 | 0 | (reference to) | | | prvalue: const Base & | ir.cpp:810:25:810:25 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 54 | 53 | 0 | (const Base)... | base class conversion | | lvalue: const Base | ir.cpp:810:25:810:25 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 55 | 54 | 0 | m | | | lvalue: Middle | ir.cpp:810:25:810:25 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 56 | 1 | 9 | ExprStmt | | | | ir.cpp:811:3:811:10 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 57 | 56 | 0 | ... = ... | | | lvalue: Base * | ir.cpp:811:3:811:9 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 58 | 57 | 0 | pb | | | lvalue: Base * | ir.cpp:811:3:811:4 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 59 | 57 | 1 | (Base *)... | base class conversion | | prvalue: Base * | ir.cpp:811:8:811:9 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 60 | 59 | 0 | pm | | | prvalue(load): Middle * | ir.cpp:811:8:811:9 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 61 | 1 | 10 | ExprStmt | | | | ir.cpp:812:3:812:17 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 62 | 61 | 0 | ... = ... | | | lvalue: Base * | ir.cpp:812:3:812:16 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 63 | 62 | 0 | pb | | | lvalue: Base * | ir.cpp:812:3:812:4 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 64 | 62 | 1 | (Base *)... | base class conversion | | prvalue: Base * | ir.cpp:812:8:812:16 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 65 | 64 | 0 | pm | | | prvalue(load): Middle * | ir.cpp:812:15:812:16 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 66 | 1 | 11 | ExprStmt | | | | ir.cpp:813:3:813:30 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 67 | 66 | 0 | ... = ... | | | lvalue: Base * | ir.cpp:813:3:813:29 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 68 | 67 | 0 | pb | | | lvalue: Base * | ir.cpp:813:3:813:4 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 69 | 67 | 1 | static_cast... | base class conversion | | prvalue: Base * | ir.cpp:813:8:813:29 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 70 | 69 | 0 | pm | | | prvalue(load): Middle * | ir.cpp:813:27:813:28 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 71 | 1 | 12 | ExprStmt | | | | ir.cpp:814:3:814:35 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 72 | 71 | 0 | ... = ... | | | lvalue: Base * | ir.cpp:814:3:814:34 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 73 | 72 | 0 | pb | | | lvalue: Base * | ir.cpp:814:3:814:4 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 74 | 72 | 1 | reinterpret_cast... | pointer conversion | | prvalue: Base * | ir.cpp:814:8:814:34 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 75 | 74 | 0 | pm | | | prvalue(load): Middle * | ir.cpp:814:32:814:33 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 76 | 1 | 13 | ExprStmt | | | | ir.cpp:816:3:816:17 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 77 | 76 | 0 | (reference dereference) | | | lvalue: Middle | ir.cpp:816:5:816:17 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 78 | 77 | 0 | call to operator= | | | prvalue: Middle & | ir.cpp:816:5:816:5 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 79 | 78 | -1 | m | | | lvalue: Middle | ir.cpp:816:3:816:3 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 80 | 78 | 0 | (reference to) | | | prvalue: const Middle & | ir.cpp:816:7:816:16 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 81 | 80 | 0 | (const Middle)... | glvalue conversion | | lvalue: const Middle | ir.cpp:816:7:816:16 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 82 | 81 | 0 | (Middle)... | derived class conversion | | lvalue: Middle | ir.cpp:816:7:816:16 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 83 | 82 | 0 | b | | | lvalue: Base | ir.cpp:816:16:816:16 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 84 | 1 | 14 | ExprStmt | | | | ir.cpp:817:3:817:30 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 85 | 84 | 0 | (reference dereference) | | | lvalue: Middle | ir.cpp:817:5:817:30 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 86 | 85 | 0 | call to operator= | | | prvalue: Middle & | ir.cpp:817:5:817:5 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 87 | 86 | -1 | m | | | lvalue: Middle | ir.cpp:817:3:817:3 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 88 | 86 | 0 | (reference to) | | | prvalue: const Middle & | ir.cpp:817:7:817:29 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 89 | 88 | 0 | (const Middle)... | glvalue conversion | | lvalue: const Middle | ir.cpp:817:7:817:29 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 90 | 89 | 0 | static_cast... | derived class conversion | | lvalue: Middle | ir.cpp:817:7:817:29 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 91 | 90 | 0 | b | | | lvalue: Base | ir.cpp:817:28:817:28 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 92 | 1 | 15 | ExprStmt | | | | ir.cpp:818:3:818:19 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 93 | 92 | 0 | ... = ... | | | lvalue: Middle * | ir.cpp:818:3:818:18 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 94 | 93 | 0 | pm | | | lvalue: Middle * | ir.cpp:818:3:818:4 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 95 | 93 | 1 | (Middle *)... | derived class conversion | | prvalue: Middle * | ir.cpp:818:8:818:18 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 96 | 95 | 0 | pb | | | prvalue(load): Base * | ir.cpp:818:17:818:18 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 97 | 1 | 16 | ExprStmt | | | | ir.cpp:819:3:819:32 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 98 | 97 | 0 | ... = ... | | | lvalue: Middle * | ir.cpp:819:3:819:31 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 99 | 98 | 0 | pm | | | lvalue: Middle * | ir.cpp:819:3:819:4 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 100 | 98 | 1 | static_cast... | derived class conversion | | prvalue: Middle * | ir.cpp:819:8:819:31 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 101 | 100 | 0 | pb | | | prvalue(load): Base * | ir.cpp:819:29:819:30 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 102 | 1 | 17 | ExprStmt | | | | ir.cpp:820:3:820:37 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 103 | 102 | 0 | ... = ... | | | lvalue: Middle * | ir.cpp:820:3:820:36 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 104 | 103 | 0 | pm | | | lvalue: Middle * | ir.cpp:820:3:820:4 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 105 | 103 | 1 | reinterpret_cast... | pointer conversion | | prvalue: Middle * | ir.cpp:820:8:820:36 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 106 | 105 | 0 | pb | | | prvalue(load): Base * | ir.cpp:820:34:820:35 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 107 | 1 | 18 | ExprStmt | | | | ir.cpp:822:3:822:8 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 108 | 107 | 0 | (reference dereference) | | | lvalue: Base | ir.cpp:822:5:822:8 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 109 | 108 | 0 | call to operator= | | | prvalue: Base & | ir.cpp:822:5:822:5 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 110 | 109 | -1 | b | | | lvalue: Base | ir.cpp:822:3:822:3 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 111 | 109 | 0 | (reference to) | | | prvalue: const Base & | ir.cpp:822:7:822:7 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 112 | 111 | 0 | (const Base)... | base class conversion | | lvalue: const Base | ir.cpp:822:7:822:7 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 113 | 112 | 0 | (const Middle)... | base class conversion | | lvalue: const Middle | ir.cpp:822:7:822:7 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 114 | 113 | 0 | d | | | lvalue: Derived | ir.cpp:822:7:822:7 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 115 | 1 | 19 | ExprStmt | | | | ir.cpp:823:3:823:14 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 116 | 115 | 0 | (reference dereference) | | | lvalue: Base | ir.cpp:823:5:823:14 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 117 | 116 | 0 | call to operator= | | | prvalue: Base & | ir.cpp:823:5:823:5 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 118 | 117 | -1 | b | | | lvalue: Base | ir.cpp:823:3:823:3 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 119 | 117 | 0 | (reference to) | | | prvalue: const Base & | ir.cpp:823:7:823:13 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 120 | 119 | 0 | (const Base)... | prvalue adjustment conversion | | prvalue: const Base | ir.cpp:823:7:823:13 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 121 | 120 | 0 | call to Base | | | prvalue: void | ir.cpp:823:7:823:13 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 122 | 121 | 0 | (reference to) | | | prvalue: const Base & | ir.cpp:823:13:823:13 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 123 | 122 | 0 | (const Base)... | base class conversion | | lvalue: const Base | ir.cpp:823:13:823:13 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 124 | 123 | 0 | (const Middle)... | base class conversion | | lvalue: const Middle | ir.cpp:823:13:823:13 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 125 | 124 | 0 | d | | | lvalue: Derived | ir.cpp:823:13:823:13 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 126 | 1 | 20 | ExprStmt | | | | ir.cpp:824:3:824:27 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 127 | 126 | 0 | (reference dereference) | | | lvalue: Base | ir.cpp:824:5:824:27 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 128 | 127 | 0 | call to operator= | | | prvalue: Base & | ir.cpp:824:5:824:5 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 129 | 128 | -1 | b | | | lvalue: Base | ir.cpp:824:3:824:3 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 130 | 128 | 0 | (reference to) | | | prvalue: const Base & | ir.cpp:824:7:824:26 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 131 | 130 | 0 | (const Base)... | prvalue adjustment conversion | | prvalue: const Base | ir.cpp:824:7:824:26 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 132 | 131 | 0 | call to Base | | | prvalue: void | ir.cpp:824:7:824:26 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 133 | 132 | 0 | (reference to) | | | prvalue: const Base & | ir.cpp:824:25:824:25 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 134 | 133 | 0 | (const Base)... | base class conversion | | lvalue: const Base | ir.cpp:824:25:824:25 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 135 | 134 | 0 | (const Middle)... | base class conversion | | lvalue: const Middle | ir.cpp:824:25:824:25 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 136 | 135 | 0 | d | | | lvalue: Derived | ir.cpp:824:25:824:25 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 137 | 1 | 21 | ExprStmt | | | | ir.cpp:825:3:825:10 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 138 | 137 | 0 | ... = ... | | | lvalue: Base * | ir.cpp:825:3:825:9 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 139 | 138 | 0 | pb | | | lvalue: Base * | ir.cpp:825:3:825:4 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 140 | 138 | 1 | (Base *)... | base class conversion | | prvalue: Base * | ir.cpp:825:8:825:9 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 141 | 140 | 0 | (Middle *)... | base class conversion | | prvalue: Middle * | ir.cpp:825:8:825:9 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 142 | 141 | 0 | pd | | | prvalue(load): Derived * | ir.cpp:825:8:825:9 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 143 | 1 | 22 | ExprStmt | | | | ir.cpp:826:3:826:17 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 144 | 143 | 0 | ... = ... | | | lvalue: Base * | ir.cpp:826:3:826:16 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 145 | 144 | 0 | pb | | | lvalue: Base * | ir.cpp:826:3:826:4 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 146 | 144 | 1 | (Base *)... | base class conversion | | prvalue: Base * | ir.cpp:826:8:826:16 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 147 | 146 | 0 | (Middle *)... | base class conversion | | prvalue: Middle * | ir.cpp:826:15:826:16 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 148 | 147 | 0 | pd | | | prvalue(load): Derived * | ir.cpp:826:15:826:16 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 149 | 1 | 23 | ExprStmt | | | | ir.cpp:827:3:827:30 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 150 | 149 | 0 | ... = ... | | | lvalue: Base * | ir.cpp:827:3:827:29 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 151 | 150 | 0 | pb | | | lvalue: Base * | ir.cpp:827:3:827:4 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 152 | 150 | 1 | static_cast... | base class conversion | | prvalue: Base * | ir.cpp:827:8:827:29 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 153 | 152 | 0 | (Middle *)... | base class conversion | | prvalue: Middle * | ir.cpp:827:27:827:28 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 154 | 153 | 0 | pd | | | prvalue(load): Derived * | ir.cpp:827:27:827:28 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 155 | 1 | 24 | ExprStmt | | | | ir.cpp:828:3:828:35 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 156 | 155 | 0 | ... = ... | | | lvalue: Base * | ir.cpp:828:3:828:34 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 157 | 156 | 0 | pb | | | lvalue: Base * | ir.cpp:828:3:828:4 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 158 | 156 | 1 | reinterpret_cast... | pointer conversion | | prvalue: Base * | ir.cpp:828:8:828:34 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 159 | 158 | 0 | pd | | | prvalue(load): Derived * | ir.cpp:828:32:828:33 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 160 | 1 | 25 | ExprStmt | | | | ir.cpp:830:3:830:18 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 161 | 160 | 0 | (reference dereference) | | | lvalue: Derived | ir.cpp:830:5:830:18 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 162 | 161 | 0 | call to operator= | | | prvalue: Derived & | ir.cpp:830:5:830:5 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 163 | 162 | -1 | d | | | lvalue: Derived | ir.cpp:830:3:830:3 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 164 | 162 | 0 | (reference to) | | | prvalue: const Derived & | ir.cpp:830:7:830:17 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 165 | 164 | 0 | (const Derived)... | glvalue conversion | | lvalue: const Derived | ir.cpp:830:7:830:17 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 166 | 165 | 0 | (Derived)... | derived class conversion | | lvalue: Derived | ir.cpp:830:7:830:17 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 167 | 166 | 0 | (Middle)... | derived class conversion | | lvalue: Middle | ir.cpp:830:17:830:17 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 168 | 167 | 0 | b | | | lvalue: Base | ir.cpp:830:17:830:17 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 169 | 1 | 26 | ExprStmt | | | | ir.cpp:831:3:831:31 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 170 | 169 | 0 | (reference dereference) | | | lvalue: Derived | ir.cpp:831:5:831:31 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 171 | 170 | 0 | call to operator= | | | prvalue: Derived & | ir.cpp:831:5:831:5 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 172 | 171 | -1 | d | | | lvalue: Derived | ir.cpp:831:3:831:3 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 173 | 171 | 0 | (reference to) | | | prvalue: const Derived & | ir.cpp:831:7:831:30 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 174 | 173 | 0 | (const Derived)... | glvalue conversion | | lvalue: const Derived | ir.cpp:831:7:831:30 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 175 | 174 | 0 | static_cast... | derived class conversion | | lvalue: Derived | ir.cpp:831:7:831:30 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 176 | 175 | 0 | (Middle)... | derived class conversion | | lvalue: Middle | ir.cpp:831:29:831:29 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 177 | 176 | 0 | b | | | lvalue: Base | ir.cpp:831:29:831:29 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 178 | 1 | 27 | ExprStmt | | | | ir.cpp:832:3:832:20 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 179 | 178 | 0 | ... = ... | | | lvalue: Derived * | ir.cpp:832:3:832:19 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 180 | 179 | 0 | pd | | | lvalue: Derived * | ir.cpp:832:3:832:4 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 181 | 179 | 1 | (Derived *)... | derived class conversion | | prvalue: Derived * | ir.cpp:832:8:832:19 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 182 | 181 | 0 | (Middle *)... | derived class conversion | | prvalue: Middle * | ir.cpp:832:18:832:19 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 183 | 182 | 0 | pb | | | prvalue(load): Base * | ir.cpp:832:18:832:19 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 184 | 1 | 28 | ExprStmt | | | | ir.cpp:833:3:833:33 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 185 | 184 | 0 | ... = ... | | | lvalue: Derived * | ir.cpp:833:3:833:32 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 186 | 185 | 0 | pd | | | lvalue: Derived * | ir.cpp:833:3:833:4 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 187 | 185 | 1 | static_cast... | derived class conversion | | prvalue: Derived * | ir.cpp:833:8:833:32 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 188 | 187 | 0 | (Middle *)... | derived class conversion | | prvalue: Middle * | ir.cpp:833:30:833:31 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 189 | 188 | 0 | pb | | | prvalue(load): Base * | ir.cpp:833:30:833:31 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 190 | 1 | 29 | ExprStmt | | | | ir.cpp:834:3:834:38 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 191 | 190 | 0 | ... = ... | | | lvalue: Derived * | ir.cpp:834:3:834:37 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 192 | 191 | 0 | pd | | | lvalue: Derived * | ir.cpp:834:3:834:4 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 193 | 191 | 1 | reinterpret_cast... | pointer conversion | | prvalue: Derived * | ir.cpp:834:8:834:37 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 194 | 193 | 0 | pb | | | prvalue(load): Base * | ir.cpp:834:35:834:36 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 195 | 1 | 30 | declaration | | | | ir.cpp:836:3:836:27 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 196 | 195 | 0 | definition of pmv | | | MiddleVB1 * | ir.cpp:836:14:836:16 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 197 | 196 | 0 | initializer for pmv | | | | ir.cpp:836:19:836:26 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 198 | 197 | 0 | (MiddleVB1 *)... | pointer conversion | =0 | prvalue: MiddleVB1 * | ir.cpp:836:20:836:26 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 199 | 198 | 0 | 0 | | =0 | prvalue: decltype(nullptr) | ir.cpp:836:20:836:26 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 200 | 1 | 31 | declaration | | | | ir.cpp:837:3:837:27 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 201 | 200 | 0 | definition of pdv | | | DerivedVB * | ir.cpp:837:14:837:16 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 202 | 201 | 0 | initializer for pdv | | | | ir.cpp:837:19:837:26 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 203 | 202 | 0 | (DerivedVB *)... | pointer conversion | =0 | prvalue: DerivedVB * | ir.cpp:837:20:837:26 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 204 | 203 | 0 | 0 | | =0 | prvalue: decltype(nullptr) | ir.cpp:837:20:837:26 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 205 | 1 | 32 | ExprStmt | | | | ir.cpp:838:3:838:11 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 206 | 205 | 0 | ... = ... | | | lvalue: Base * | ir.cpp:838:3:838:10 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 207 | 206 | 0 | pb | | | lvalue: Base * | ir.cpp:838:3:838:4 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 208 | 206 | 1 | (Base *)... | base class conversion | | prvalue: Base * | ir.cpp:838:8:838:10 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 209 | 208 | 0 | pmv | | | prvalue(load): MiddleVB1 * | ir.cpp:838:8:838:10 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 210 | 1 | 33 | ExprStmt | | | | ir.cpp:839:3:839:11 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 211 | 210 | 0 | ... = ... | | | lvalue: Base * | ir.cpp:839:3:839:10 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 212 | 211 | 0 | pb | | | lvalue: Base * | ir.cpp:839:3:839:4 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 213 | 211 | 1 | (Base *)... | base class conversion | | prvalue: Base * | ir.cpp:839:8:839:10 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 214 | 213 | 0 | pdv | | | prvalue(load): DerivedVB * | ir.cpp:839:8:839:10 | +| ir.cpp:799:6:799:25 | HierarchyConversions() -> void | 215 | 1 | 34 | return ... | | | | ir.cpp:840:1:840:1 | +| ir.cpp:842:8:842:8 | PolymorphicBase::PolymorphicBase() -> void | 0 | -1 | 0 | PolymorphicBase | | | | ir.cpp:842:8:842:8 | +| ir.cpp:842:8:842:8 | PolymorphicBase::PolymorphicBase() -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:842:8:842:8 | +| ir.cpp:842:8:842:8 | PolymorphicBase::PolymorphicBase() -> void | 2 | 1 | 0 | return ... | | | | ir.cpp:842:8:842:8 | +| ir.cpp:842:8:842:8 | PolymorphicBase::PolymorphicBase(const PolymorphicBase &) -> void | 0 | -1 | 0 | PolymorphicBase | | | | ir.cpp:842:8:842:8 | +| ir.cpp:842:8:842:8 | PolymorphicBase::operator=(const PolymorphicBase &) -> PolymorphicBase & | 0 | -1 | 0 | operator= | | | | ir.cpp:842:8:842:8 | +| ir.cpp:843:11:843:26 | PolymorphicBase::~PolymorphicBase() -> void | 0 | -1 | 0 | ~PolymorphicBase | | | | ir.cpp:843:11:843:26 | +| ir.cpp:846:8:846:8 | PolymorphicDerived::PolymorphicDerived() -> void | 0 | -1 | 0 | PolymorphicDerived | | | | ir.cpp:846:8:846:8 | +| ir.cpp:846:8:846:8 | PolymorphicDerived::PolymorphicDerived() -> void | 1 | 0 | 0 | call to PolymorphicBase | | | prvalue: void | ir.cpp:846:8:846:8 | +| ir.cpp:846:8:846:8 | PolymorphicDerived::PolymorphicDerived() -> void | 2 | 0 | 1 | { ... } | | | | ir.cpp:846:8:846:8 | +| ir.cpp:846:8:846:8 | PolymorphicDerived::PolymorphicDerived() -> void | 3 | 2 | 0 | return ... | | | | ir.cpp:846:8:846:8 | +| ir.cpp:846:8:846:8 | PolymorphicDerived::PolymorphicDerived(PolymorphicDerived &&) -> void | 0 | -1 | 0 | PolymorphicDerived | | | | ir.cpp:846:8:846:8 | +| ir.cpp:846:8:846:8 | PolymorphicDerived::PolymorphicDerived(const PolymorphicDerived &) -> void | 0 | -1 | 0 | PolymorphicDerived | | | | ir.cpp:846:8:846:8 | +| ir.cpp:846:8:846:8 | PolymorphicDerived::operator=(PolymorphicDerived &&) -> PolymorphicDerived & | 0 | -1 | 0 | operator= | | | | ir.cpp:846:8:846:8 | +| ir.cpp:846:8:846:8 | PolymorphicDerived::operator=(const PolymorphicDerived &) -> PolymorphicDerived & | 0 | -1 | 0 | operator= | | | | ir.cpp:846:8:846:8 | +| ir.cpp:846:8:846:8 | PolymorphicDerived::~PolymorphicDerived() -> void | 0 | -1 | 0 | ~PolymorphicDerived | | | | ir.cpp:846:8:846:8 | +| ir.cpp:846:8:846:8 | PolymorphicDerived::~PolymorphicDerived() -> void | 1 | 0 | 0 | { ... } | | | | file://:0:0:0:0 | +| ir.cpp:846:8:846:8 | PolymorphicDerived::~PolymorphicDerived() -> void | 2 | 1 | 0 | return ... | | | | file://:0:0:0:0 | +| ir.cpp:846:8:846:8 | PolymorphicDerived::~PolymorphicDerived() -> void | 3 | 0 | 1 | call to ~PolymorphicBase | | | prvalue: void | ir.cpp:846:8:846:8 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 0 | -1 | 0 | DynamicCast | | | | ir.cpp:849:6:849:16 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:849:20:865:1 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:850:3:850:20 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 3 | 2 | 0 | definition of b | | | PolymorphicBase | ir.cpp:850:19:850:19 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 4 | 3 | 0 | initializer for b | | | | ir.cpp:850:19:850:19 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 5 | 4 | 0 | call to PolymorphicBase | | | prvalue: void | file://:0:0:0:0 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 6 | 1 | 1 | declaration | | | | ir.cpp:851:3:851:23 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 7 | 6 | 0 | definition of d | | | PolymorphicDerived | ir.cpp:851:22:851:22 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 8 | 7 | 0 | initializer for d | | | | ir.cpp:851:22:851:22 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 9 | 8 | 0 | call to PolymorphicDerived | | | prvalue: void | ir.cpp:851:22:851:22 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 10 | 1 | 2 | declaration | | | | ir.cpp:853:3:853:27 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 11 | 10 | 0 | definition of pb | | | PolymorphicBase * | ir.cpp:853:20:853:21 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 12 | 11 | 0 | initializer for pb | | | | ir.cpp:853:24:853:26 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 13 | 12 | 0 | & ... | | | prvalue: PolymorphicBase * | ir.cpp:853:25:853:26 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 14 | 13 | 0 | b | | | lvalue: PolymorphicBase | ir.cpp:853:26:853:26 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 15 | 1 | 3 | declaration | | | | ir.cpp:854:3:854:30 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 16 | 15 | 0 | definition of pd | | | PolymorphicDerived * | ir.cpp:854:23:854:24 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 17 | 16 | 0 | initializer for pd | | | | ir.cpp:854:27:854:29 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 18 | 17 | 0 | & ... | | | prvalue: PolymorphicDerived * | ir.cpp:854:28:854:29 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 19 | 18 | 0 | d | | | lvalue: PolymorphicDerived | ir.cpp:854:29:854:29 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 20 | 1 | 4 | ExprStmt | | | | ir.cpp:857:3:857:42 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 21 | 20 | 0 | ... = ... | | | lvalue: PolymorphicBase * | ir.cpp:857:3:857:41 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 22 | 21 | 0 | pb | | | lvalue: PolymorphicBase * | ir.cpp:857:3:857:4 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 23 | 21 | 1 | (PolymorphicBase *)... | base class conversion | | prvalue: PolymorphicBase * | ir.cpp:857:8:857:41 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 24 | 23 | 0 | pd | | | prvalue(load): PolymorphicDerived * | ir.cpp:857:39:857:40 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 25 | 1 | 5 | declaration | | | | ir.cpp:858:3:858:58 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 26 | 25 | 0 | definition of rb | | | PolymorphicBase & | ir.cpp:858:20:858:21 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 27 | 26 | 0 | initializer for rb | | | | ir.cpp:858:24:858:57 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 28 | 27 | 0 | (reference to) | | | prvalue: PolymorphicBase & | ir.cpp:858:25:858:57 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 29 | 28 | 0 | (PolymorphicBase)... | base class conversion | | lvalue: PolymorphicBase | ir.cpp:858:25:858:57 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 30 | 29 | 0 | d | | | lvalue: PolymorphicDerived | ir.cpp:858:56:858:56 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 31 | 1 | 6 | ExprStmt | | | | ir.cpp:860:3:860:45 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 32 | 31 | 0 | ... = ... | | | lvalue: PolymorphicDerived * | ir.cpp:860:3:860:44 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 33 | 32 | 0 | pd | | | lvalue: PolymorphicDerived * | ir.cpp:860:3:860:4 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 34 | 32 | 1 | dynamic_cast... | dynamic_cast | | prvalue: PolymorphicDerived * | ir.cpp:860:8:860:44 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 35 | 34 | 0 | pb | | | prvalue(load): PolymorphicBase * | ir.cpp:860:42:860:43 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 36 | 1 | 7 | declaration | | | | ir.cpp:861:3:861:64 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 37 | 36 | 0 | definition of rd | | | PolymorphicDerived & | ir.cpp:861:23:861:24 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 38 | 37 | 0 | initializer for rd | | | | ir.cpp:861:27:861:63 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 39 | 38 | 0 | (reference to) | | | prvalue: PolymorphicDerived & | ir.cpp:861:28:861:63 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 40 | 39 | 0 | dynamic_cast... | dynamic_cast | | lvalue: PolymorphicDerived | ir.cpp:861:28:861:63 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 41 | 40 | 0 | b | | | lvalue: PolymorphicBase | ir.cpp:861:62:861:62 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 42 | 1 | 8 | declaration | | | | ir.cpp:863:3:863:37 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 43 | 42 | 0 | definition of pv | | | void * | ir.cpp:863:9:863:10 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 44 | 43 | 0 | initializer for pv | | | | ir.cpp:863:13:863:36 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 45 | 44 | 0 | dynamic_cast... | dynamic_cast | | prvalue: void * | ir.cpp:863:14:863:36 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 46 | 45 | 0 | pb | | | prvalue(load): PolymorphicBase * | ir.cpp:863:34:863:35 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 47 | 1 | 9 | declaration | | | | ir.cpp:864:3:864:50 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 48 | 47 | 0 | definition of pcv | | | const void * | ir.cpp:864:15:864:17 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 49 | 48 | 0 | initializer for pcv | | | | ir.cpp:864:20:864:49 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 50 | 49 | 0 | dynamic_cast... | dynamic_cast | | prvalue: const void * | ir.cpp:864:21:864:49 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 51 | 50 | 0 | pd | | | prvalue(load): PolymorphicDerived * | ir.cpp:864:47:864:48 | +| ir.cpp:849:6:849:16 | DynamicCast() -> void | 52 | 1 | 10 | return ... | | | | ir.cpp:865:1:865:1 | +| ir.cpp:867:1:867:14 | String::String() -> void | 0 | -1 | 0 | String | | | | ir.cpp:867:1:867:14 | +| ir.cpp:867:1:867:14 | String::String() -> void | 1 | 0 | 0 | call to String | | | prvalue: void | ir.cpp:868:3:868:12 | +| ir.cpp:867:1:867:14 | String::String() -> void | 2 | 1 | 0 | array to pointer conversion | | | prvalue: const char * | ir.cpp:868:10:868:11 | +| ir.cpp:867:1:867:14 | String::String() -> void | 3 | 2 | 0 | | | = | lvalue: const char[1] | ir.cpp:868:10:868:11 | +| ir.cpp:867:1:867:14 | String::String() -> void | 4 | 0 | 1 | { ... } | | | | ir.cpp:868:14:869:1 | +| ir.cpp:867:1:867:14 | String::String() -> void | 5 | 4 | 0 | return ... | | | | ir.cpp:869:1:869:1 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 0 | -1 | 0 | ArrayConversions | | | | ir.cpp:871:6:871:21 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:871:25:881:1 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:872:3:872:12 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 3 | 2 | 0 | definition of a | | | char[5] | ir.cpp:872:8:872:8 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 4 | 1 | 1 | declaration | | | | ir.cpp:873:3:873:20 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 5 | 4 | 0 | definition of p | | | const char * | ir.cpp:873:15:873:15 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 6 | 5 | 0 | initializer for p | | | | ir.cpp:873:18:873:19 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 7 | 6 | 0 | (const char *)... | pointer conversion | | prvalue: const char * | ir.cpp:873:19:873:19 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 8 | 7 | 0 | array to pointer conversion | | | prvalue: char * | ir.cpp:873:19:873:19 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 9 | 8 | 0 | a | | | lvalue: char[5] | ir.cpp:873:19:873:19 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 10 | 1 | 2 | ExprStmt | | | | ir.cpp:874:3:874:13 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 11 | 10 | 0 | ... = ... | | | lvalue: const char * | ir.cpp:874:3:874:12 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 12 | 11 | 0 | p | | | lvalue: const char * | ir.cpp:874:3:874:3 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 13 | 11 | 1 | array to pointer conversion | | | prvalue: const char * | ir.cpp:874:7:874:12 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 14 | 13 | 0 | test | | =test | lvalue: const char[5] | ir.cpp:874:7:874:12 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 15 | 1 | 3 | ExprStmt | | | | ir.cpp:875:3:875:12 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 16 | 15 | 0 | ... = ... | | | lvalue: const char * | ir.cpp:875:3:875:11 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 17 | 16 | 0 | p | | | lvalue: const char * | ir.cpp:875:3:875:3 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 18 | 16 | 1 | (const char *)... | pointer conversion | | prvalue: const char * | ir.cpp:875:7:875:11 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 19 | 18 | 0 | & ... | | | prvalue: char * | ir.cpp:875:7:875:11 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 20 | 19 | 0 | access to array | | | lvalue: char | ir.cpp:875:8:875:11 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 21 | 20 | 0 | array to pointer conversion | | | prvalue: char * | ir.cpp:875:8:875:8 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 22 | 21 | 0 | a | | | lvalue: char[5] | ir.cpp:875:8:875:8 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 23 | 20 | 1 | 0 | | =0 | prvalue: int | ir.cpp:875:10:875:10 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 24 | 1 | 4 | ExprStmt | | | | ir.cpp:876:3:876:17 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 25 | 24 | 0 | ... = ... | | | lvalue: const char * | ir.cpp:876:3:876:16 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 26 | 25 | 0 | p | | | lvalue: const char * | ir.cpp:876:3:876:3 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 27 | 25 | 1 | & ... | | | prvalue: const char * | ir.cpp:876:7:876:16 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 28 | 27 | 0 | access to array | | | lvalue: const char | ir.cpp:876:8:876:16 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 29 | 28 | 0 | array to pointer conversion | | | prvalue: const char * | ir.cpp:876:8:876:13 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 30 | 29 | 0 | test | | =test | lvalue: const char[5] | ir.cpp:876:8:876:13 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 31 | 28 | 1 | 0 | | =0 | prvalue: int | ir.cpp:876:15:876:15 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 32 | 1 | 5 | declaration | | | | ir.cpp:877:3:877:20 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 33 | 32 | 0 | definition of ra | | | char(&)[5] | ir.cpp:877:10:877:11 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 34 | 33 | 0 | initializer for ra | | | | ir.cpp:877:18:877:19 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 35 | 34 | 0 | (reference to) | | | prvalue: char(&)[5] | ir.cpp:877:19:877:19 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 36 | 35 | 0 | a | | | lvalue: char[5] | ir.cpp:877:19:877:19 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 37 | 1 | 6 | declaration | | | | ir.cpp:878:3:878:31 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 38 | 37 | 0 | definition of rs | | | const char(&)[5] | ir.cpp:878:16:878:17 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 39 | 38 | 0 | initializer for rs | | | | ir.cpp:878:24:878:30 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 40 | 39 | 0 | (reference to) | | | prvalue: const char(&)[5] | ir.cpp:878:25:878:30 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 41 | 40 | 0 | test | | =test | lvalue: const char[5] | ir.cpp:878:25:878:30 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 42 | 1 | 7 | declaration | | | | ir.cpp:879:3:879:27 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 43 | 42 | 0 | definition of pa | | | const char(*)[5] | ir.cpp:879:16:879:17 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 44 | 43 | 0 | initializer for pa | | | | ir.cpp:879:24:879:26 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 45 | 44 | 0 | (const char(*)[5])... | pointer conversion | | prvalue: const char(*)[5] | ir.cpp:879:25:879:26 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 46 | 45 | 0 | & ... | | | prvalue: char(*)[5] | ir.cpp:879:25:879:26 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 47 | 46 | 0 | a | | | lvalue: char[5] | ir.cpp:879:26:879:26 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 48 | 1 | 8 | ExprStmt | | | | ir.cpp:880:3:880:15 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 49 | 48 | 0 | ... = ... | | | lvalue: const char(*)[5] | ir.cpp:880:3:880:14 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 50 | 49 | 0 | pa | | | lvalue: const char(*)[5] | ir.cpp:880:3:880:4 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 51 | 49 | 1 | & ... | | | prvalue: const char(*)[5] | ir.cpp:880:8:880:14 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 52 | 51 | 0 | test | | =test | lvalue: const char[5] | ir.cpp:880:9:880:14 | +| ir.cpp:871:6:871:21 | ArrayConversions() -> void | 53 | 1 | 9 | return ... | | | | ir.cpp:881:1:881:1 | +| ir.cpp:883:6:883:23 | FuncPtrConversions(..(*)(..), void *) -> void | 0 | -1 | 0 | FuncPtrConversions | | | | ir.cpp:883:6:883:23 | +| ir.cpp:883:6:883:23 | FuncPtrConversions(..(*)(..), void *) -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:883:50:886:1 | +| ir.cpp:883:6:883:23 | FuncPtrConversions(..(*)(..), void *) -> void | 2 | 1 | 0 | ExprStmt | | | | ir.cpp:884:3:884:17 | +| ir.cpp:883:6:883:23 | FuncPtrConversions(..(*)(..), void *) -> void | 3 | 2 | 0 | ... = ... | | | lvalue: void * | ir.cpp:884:3:884:16 | +| ir.cpp:883:6:883:23 | FuncPtrConversions(..(*)(..), void *) -> void | 4 | 3 | 0 | p | | | lvalue: void * | ir.cpp:884:3:884:3 | +| ir.cpp:883:6:883:23 | FuncPtrConversions(..(*)(..), void *) -> void | 5 | 3 | 1 | (void *)... | pointer conversion | | prvalue: void * | ir.cpp:884:7:884:16 | +| ir.cpp:883:6:883:23 | FuncPtrConversions(..(*)(..), void *) -> void | 6 | 5 | 0 | pfn | | | prvalue(load): ..(*)(..) | ir.cpp:884:14:884:16 | +| ir.cpp:883:6:883:23 | FuncPtrConversions(..(*)(..), void *) -> void | 7 | 1 | 1 | ExprStmt | | | | ir.cpp:885:3:885:23 | +| ir.cpp:883:6:883:23 | FuncPtrConversions(..(*)(..), void *) -> void | 8 | 7 | 0 | ... = ... | | | lvalue: ..(*)(..) | ir.cpp:885:3:885:22 | +| ir.cpp:883:6:883:23 | FuncPtrConversions(..(*)(..), void *) -> void | 9 | 8 | 0 | pfn | | | lvalue: ..(*)(..) | ir.cpp:885:3:885:5 | +| ir.cpp:883:6:883:23 | FuncPtrConversions(..(*)(..), void *) -> void | 10 | 8 | 1 | (..(*)(..))... | pointer conversion | | prvalue: ..(*)(..) | ir.cpp:885:9:885:22 | +| ir.cpp:883:6:883:23 | FuncPtrConversions(..(*)(..), void *) -> void | 11 | 10 | 0 | p | | | prvalue(load): void * | ir.cpp:885:22:885:22 | +| ir.cpp:883:6:883:23 | FuncPtrConversions(..(*)(..), void *) -> void | 12 | 1 | 2 | return ... | | | | ir.cpp:886:1:886:1 | +| ir.cpp:888:6:888:16 | VarArgUsage(int) -> void | 0 | -1 | 0 | VarArgUsage | | | | ir.cpp:888:6:888:16 | +| ir.cpp:888:6:888:16 | VarArgUsage(int) -> void | 1 | 0 | 0 | { ... } | | | | ir.cpp:888:30:898:1 | +| ir.cpp:888:6:888:16 | VarArgUsage(int) -> void | 2 | 1 | 0 | declaration | | | | ir.cpp:889:3:889:25 | +| ir.cpp:888:6:888:16 | VarArgUsage(int) -> void | 3 | 2 | 0 | definition of args | | | __va_list_tag[1] | ir.cpp:889:21:889:24 | +| ir.cpp:888:6:888:16 | VarArgUsage(int) -> void | 4 | 1 | 1 | ExprStmt | | | | ir.cpp:891:3:891:30 | +| ir.cpp:888:6:888:16 | VarArgUsage(int) -> void | 5 | 4 | 0 | __builtin_va_start | | | prvalue: void | ir.cpp:891:3:891:29 | +| ir.cpp:888:6:888:16 | VarArgUsage(int) -> void | 6 | 5 | 0 | array to pointer conversion | | | prvalue: __va_list_tag * | ir.cpp:891:22:891:25 | +| ir.cpp:888:6:888:16 | VarArgUsage(int) -> void | 7 | 6 | 0 | args | | | lvalue: __va_list_tag[1] | ir.cpp:891:22:891:25 | +| ir.cpp:888:6:888:16 | VarArgUsage(int) -> void | 8 | 5 | 1 | x | | | lvalue: int | ir.cpp:891:28:891:28 | +| ir.cpp:888:6:888:16 | VarArgUsage(int) -> void | 9 | 1 | 2 | declaration | | | | ir.cpp:892:3:892:26 | +| ir.cpp:888:6:888:16 | VarArgUsage(int) -> void | 10 | 9 | 0 | definition of args2 | | | __va_list_tag[1] | ir.cpp:892:21:892:25 | +| ir.cpp:888:6:888:16 | VarArgUsage(int) -> void | 11 | 1 | 3 | ExprStmt | | | | ir.cpp:893:3:893:34 | +| ir.cpp:888:6:888:16 | VarArgUsage(int) -> void | 12 | 11 | 0 | __builtin_va_start | | | prvalue: void | ir.cpp:893:3:893:33 | +| ir.cpp:888:6:888:16 | VarArgUsage(int) -> void | 13 | 12 | 0 | array to pointer conversion | | | prvalue: __va_list_tag * | ir.cpp:893:22:893:26 | +| ir.cpp:888:6:888:16 | VarArgUsage(int) -> void | 14 | 13 | 0 | args2 | | | lvalue: __va_list_tag[1] | ir.cpp:893:22:893:26 | +| ir.cpp:888:6:888:16 | VarArgUsage(int) -> void | 15 | 12 | 1 | array to pointer conversion | | | prvalue: __va_list_tag * | ir.cpp:893:29:893:32 | +| ir.cpp:888:6:888:16 | VarArgUsage(int) -> void | 16 | 15 | 0 | args | | | lvalue: __va_list_tag[1] | ir.cpp:893:29:893:32 | +| ir.cpp:888:6:888:16 | VarArgUsage(int) -> void | 17 | 1 | 4 | declaration | | | | ir.cpp:894:3:894:44 | +| ir.cpp:888:6:888:16 | VarArgUsage(int) -> void | 18 | 17 | 0 | definition of d | | | double | ir.cpp:894:10:894:10 | +| ir.cpp:888:6:888:16 | VarArgUsage(int) -> void | 19 | 18 | 0 | initializer for d | | | | ir.cpp:894:13:894:43 | +| ir.cpp:888:6:888:16 | VarArgUsage(int) -> void | 20 | 19 | 0 | __builtin_va_arg | | | prvalue(load): double | ir.cpp:894:14:894:43 | +| ir.cpp:888:6:888:16 | VarArgUsage(int) -> void | 21 | 20 | 0 | array to pointer conversion | | | prvalue: __va_list_tag * | ir.cpp:894:31:894:34 | +| ir.cpp:888:6:888:16 | VarArgUsage(int) -> void | 22 | 21 | 0 | args | | | lvalue: __va_list_tag[1] | ir.cpp:894:31:894:34 | +| ir.cpp:888:6:888:16 | VarArgUsage(int) -> void | 23 | 1 | 5 | declaration | | | | ir.cpp:895:3:895:42 | +| ir.cpp:888:6:888:16 | VarArgUsage(int) -> void | 24 | 23 | 0 | definition of f | | | float | ir.cpp:895:9:895:9 | +| ir.cpp:888:6:888:16 | VarArgUsage(int) -> void | 25 | 24 | 0 | initializer for f | | | | ir.cpp:895:12:895:41 | +| ir.cpp:888:6:888:16 | VarArgUsage(int) -> void | 26 | 25 | 0 | (float)... | floating point conversion | | prvalue: float | ir.cpp:895:13:895:41 | +| ir.cpp:888:6:888:16 | VarArgUsage(int) -> void | 27 | 26 | 0 | __builtin_va_arg | | | prvalue(load): double | ir.cpp:895:30:895:33 | +| ir.cpp:888:6:888:16 | VarArgUsage(int) -> void | 28 | 27 | 0 | array to pointer conversion | | | prvalue: __va_list_tag * | ir.cpp:895:30:895:33 | +| ir.cpp:888:6:888:16 | VarArgUsage(int) -> void | 29 | 28 | 0 | args | | | lvalue: __va_list_tag[1] | ir.cpp:895:30:895:33 | +| ir.cpp:888:6:888:16 | VarArgUsage(int) -> void | 30 | 1 | 6 | ExprStmt | | | | ir.cpp:896:3:896:25 | +| ir.cpp:888:6:888:16 | VarArgUsage(int) -> void | 31 | 30 | 0 | __builtin_va_end | | | prvalue: void | ir.cpp:896:3:896:24 | +| ir.cpp:888:6:888:16 | VarArgUsage(int) -> void | 32 | 31 | 0 | array to pointer conversion | | | prvalue: __va_list_tag * | ir.cpp:896:20:896:23 | +| ir.cpp:888:6:888:16 | VarArgUsage(int) -> void | 33 | 32 | 0 | args | | | lvalue: __va_list_tag[1] | ir.cpp:896:20:896:23 | +| ir.cpp:888:6:888:16 | VarArgUsage(int) -> void | 34 | 1 | 7 | ExprStmt | | | | ir.cpp:897:3:897:26 | +| ir.cpp:888:6:888:16 | VarArgUsage(int) -> void | 35 | 34 | 0 | __builtin_va_end | | | prvalue: void | ir.cpp:897:3:897:25 | +| ir.cpp:888:6:888:16 | VarArgUsage(int) -> void | 36 | 35 | 0 | array to pointer conversion | | | prvalue: __va_list_tag * | ir.cpp:897:20:897:24 | +| ir.cpp:888:6:888:16 | VarArgUsage(int) -> void | 37 | 36 | 0 | args2 | | | lvalue: __va_list_tag[1] | ir.cpp:897:20:897:24 | +| ir.cpp:888:6:888:16 | VarArgUsage(int) -> void | 38 | 1 | 8 | return ... | | | | ir.cpp:898:1:898:1 | diff --git a/cpp/ql/test/library-tests/ir/ir/PrintAST.qlref b/cpp/ql/test/library-tests/ir/ir/PrintAST.qlref new file mode 100644 index 000000000000..6fcb30ac7a6e --- /dev/null +++ b/cpp/ql/test/library-tests/ir/ir/PrintAST.qlref @@ -0,0 +1 @@ +semmle/code/cpp/PrintAST.ql \ No newline at end of file diff --git a/cpp/ql/test/library-tests/ir/ir/SSAIRSanity.expected b/cpp/ql/test/library-tests/ir/ir/SSAIRSanity.expected new file mode 100644 index 000000000000..3d93d822b3cf --- /dev/null +++ b/cpp/ql/test/library-tests/ir/ir/SSAIRSanity.expected @@ -0,0 +1,3 @@ +missingOperand +unexpectedOperand +duplicateOperand diff --git a/cpp/ql/test/library-tests/ir/ir/SSAIRSanity.qlref b/cpp/ql/test/library-tests/ir/ir/SSAIRSanity.qlref new file mode 100644 index 000000000000..7150a050efba --- /dev/null +++ b/cpp/ql/test/library-tests/ir/ir/SSAIRSanity.qlref @@ -0,0 +1 @@ +semmle/code/cpp/ssa/SSAIRSanity.ql \ No newline at end of file diff --git a/cpp/ql/test/library-tests/ir/ir/aliased_ssa_ir.expected b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_ir.expected new file mode 100644 index 000000000000..dd5c3a007a9f --- /dev/null +++ b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_ir.expected @@ -0,0 +1,8513 @@ +printIRGraphScopes +| AddressOf() -> int * | AddressOf | +| ArrayAccess(int *, int) -> void | ArrayAccess | +| ArrayConversions() -> void | ArrayConversions | +| ArrayInit(int, float) -> void | ArrayInit | +| ArrayReferences() -> void | ArrayReferences | +| Base::Base() -> void | Base::Base | +| Base::Base(const Base &) -> void | Base::Base | +| Base::operator=(const Base &) -> Base & | Base::operator= | +| Base::~Base() -> void | Base::~Base | +| Break(int) -> void | Break | +| C::C() -> void | C::C | +| C::FieldAccess() -> void | C::FieldAccess | +| C::InstanceMemberFunction(int) -> int | C::InstanceMemberFunction | +| C::MethodCalls() -> void | C::MethodCalls | +| C::StaticMemberFunction(int) -> int | C::StaticMemberFunction | +| C::VirtualMemberFunction(int) -> int | C::VirtualMemberFunction | +| Call() -> void | Call | +| CallAdd(int, int) -> int | CallAdd | +| CallMethods(String &, String *, String) -> void | CallMethods | +| CallMin(int, int) -> int | CallMin | +| CallNestedTemplateFunc() -> double | CallNestedTemplateFunc | +| CallViaFuncPtr(..(*)(..)) -> int | CallViaFuncPtr | +| Comma(int, int) -> int | Comma | +| CompoundAssignment() -> void | CompoundAssignment | +| ConditionValues(bool, bool) -> void | ConditionValues | +| Conditional(bool, int, int) -> void | Conditional | +| Conditional_LValue(bool) -> void | Conditional_LValue | +| Conditional_Void(bool) -> void | Conditional_Void | +| Constants() -> void | Constants | +| Continue(int) -> void | Continue | +| DeclareObject() -> void | DeclareObject | +| DerefReference(int &) -> int | DerefReference | +| Dereference(int *) -> int | Dereference | +| Derived::Derived() -> void | Derived::Derived | +| Derived::operator=(const Derived &) -> Derived & | Derived::operator= | +| Derived::~Derived() -> void | Derived::~Derived | +| DerivedVB::DerivedVB() -> void | DerivedVB::DerivedVB | +| DerivedVB::~DerivedVB() -> void | DerivedVB::~DerivedVB | +| DoStatements(int) -> void | DoStatements | +| DynamicCast() -> void | DynamicCast | +| EarlyReturn(int, int) -> void | EarlyReturn | +| EarlyReturnValue(int, int) -> int | EarlyReturnValue | +| EnumSwitch(E) -> int | EnumSwitch | +| FieldAccess() -> void | FieldAccess | +| FloatCompare(double, double) -> void | FloatCompare | +| FloatCrement(float) -> void | FloatCrement | +| FloatOps(double, double) -> void | FloatOps | +| Foo() -> void | Foo | +| For_Break() -> void | For_Break | +| For_Condition() -> void | For_Condition | +| For_ConditionUpdate() -> void | For_ConditionUpdate | +| For_Continue_NoUpdate() -> void | For_Continue_NoUpdate | +| For_Continue_Update() -> void | For_Continue_Update | +| For_Empty() -> void | For_Empty | +| For_Init() -> void | For_Init | +| For_InitCondition() -> void | For_InitCondition | +| For_InitConditionUpdate() -> void | For_InitConditionUpdate | +| For_InitUpdate() -> void | For_InitUpdate | +| For_Update() -> void | For_Update | +| FuncPtrConversions(..(*)(..), void *) -> void | FuncPtrConversions | +| FunctionReferences() -> void | FunctionReferences | +| HierarchyConversions() -> void | HierarchyConversions | +| IfStatements(bool, int, int) -> void | IfStatements | +| InitArray() -> void | InitArray | +| InitList(int, float) -> void | InitList | +| InitReference(int) -> void | InitReference | +| IntegerCompare(int, int) -> void | IntegerCompare | +| IntegerCrement(int) -> void | IntegerCrement | +| IntegerCrement_LValue(int) -> void | IntegerCrement_LValue | +| IntegerOps(int, int) -> void | IntegerOps | +| LogicalAnd(bool, bool) -> void | LogicalAnd | +| LogicalNot(bool, bool) -> void | LogicalNot | +| LogicalOr(bool, bool) -> void | LogicalOr | +| Middle::Middle() -> void | Middle::Middle | +| Middle::operator=(const Middle &) -> Middle & | Middle::operator= | +| Middle::~Middle() -> void | Middle::~Middle | +| MiddleVB1::MiddleVB1() -> void | MiddleVB1::MiddleVB1 | +| MiddleVB1::~MiddleVB1() -> void | MiddleVB1::~MiddleVB1 | +| MiddleVB2::MiddleVB2() -> void | MiddleVB2::MiddleVB2 | +| MiddleVB2::~MiddleVB2() -> void | MiddleVB2::~MiddleVB2 | +| NestedInitList(int, float) -> void | NestedInitList | +| Nullptr() -> void | Nullptr | +| Outer::Func(void *, char) -> long | Outer::Func | +| Parameters(int, int) -> int | Parameters | +| PointerCompare(int *, int *) -> void | PointerCompare | +| PointerCrement(int *) -> void | PointerCrement | +| PointerOps(int *, int) -> void | PointerOps | +| PolymorphicBase::PolymorphicBase() -> void | PolymorphicBase::PolymorphicBase | +| PolymorphicDerived::PolymorphicDerived() -> void | PolymorphicDerived::PolymorphicDerived | +| PolymorphicDerived::~PolymorphicDerived() -> void | PolymorphicDerived::~PolymorphicDerived | +| ReturnStruct(Point) -> Point | ReturnStruct | +| SetFuncPtr() -> void | SetFuncPtr | +| String::String() -> void | String::String | +| StringLiteral(int) -> void | StringLiteral | +| Switch(int) -> void | Switch | +| TakeReference() -> int & | TakeReference | +| TryCatch(bool) -> void | TryCatch | +| UninitializedVariables() -> void | UninitializedVariables | +| UnionInit(int, float) -> void | UnionInit | +| VarArgUsage(int) -> void | VarArgUsage | +| VarArgs() -> void | VarArgs | +| WhileStatements(int) -> void | WhileStatements | +| min(int, int) -> int | min | +printIRGraphNodes +| AddressOf() -> int * | 0 | | | +| ArrayAccess(int *, int) -> void | 0 | | | +| ArrayConversions() -> void | 0 | | | +| ArrayInit(int, float) -> void | 0 | | | +| ArrayReferences() -> void | 0 | | | +| Base::Base() -> void | 0 | | | +| Base::Base(const Base &) -> void | 0 | | | +| Base::operator=(const Base &) -> Base & | 0 | | | +| Base::~Base() -> void | 0 | | | +| Break(int) -> void | 0 | | | +| Break(int) -> void | 1 | | | +| Break(int) -> void | 2 | | | +| Break(int) -> void | 3 | | | +| Break(int) -> void | 4 | | | +| Break(int) -> void | 5 | | | +| C::C() -> void | 0 | | | +| C::FieldAccess() -> void | 0 | | | +| C::InstanceMemberFunction(int) -> int | 0 | | | +| C::MethodCalls() -> void | 0 | | | +| C::StaticMemberFunction(int) -> int | 0 | | | +| C::VirtualMemberFunction(int) -> int | 0 | | | +| Call() -> void | 0 | | | +| CallAdd(int, int) -> int | 0 | | | +| CallMethods(String &, String *, String) -> void | 0 | | | +| CallMin(int, int) -> int | 0 | | | +| CallNestedTemplateFunc() -> double | 0 | | | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | | | +| Comma(int, int) -> int | 0 | | | +| CompoundAssignment() -> void | 0 | | | +| ConditionValues(bool, bool) -> void | 0 | | | +| ConditionValues(bool, bool) -> void | 1 | | | +| ConditionValues(bool, bool) -> void | 2 | | | +| ConditionValues(bool, bool) -> void | 3 | | | +| ConditionValues(bool, bool) -> void | 4 | | | +| ConditionValues(bool, bool) -> void | 5 | | | +| ConditionValues(bool, bool) -> void | 6 | | | +| ConditionValues(bool, bool) -> void | 7 | | | +| ConditionValues(bool, bool) -> void | 8 | | | +| ConditionValues(bool, bool) -> void | 9 | | | +| ConditionValues(bool, bool) -> void | 10 | | | +| ConditionValues(bool, bool) -> void | 11 | | | +| ConditionValues(bool, bool) -> void | 12 | | | +| Conditional(bool, int, int) -> void | 0 | | | +| Conditional(bool, int, int) -> void | 1 | | | +| Conditional(bool, int, int) -> void | 2 | | | +| Conditional(bool, int, int) -> void | 3 | | | +| Conditional_LValue(bool) -> void | 0 | | | +| Conditional_LValue(bool) -> void | 1 | | | +| Conditional_LValue(bool) -> void | 2 | | | +| Conditional_LValue(bool) -> void | 3 | | | +| Conditional_Void(bool) -> void | 0 | | | +| Conditional_Void(bool) -> void | 1 | | | +| Conditional_Void(bool) -> void | 2 | | | +| Conditional_Void(bool) -> void | 3 | | | +| Constants() -> void | 0 | | | +| Continue(int) -> void | 0 | | | +| Continue(int) -> void | 1 | | | +| Continue(int) -> void | 2 | | | +| Continue(int) -> void | 3 | | | +| Continue(int) -> void | 4 | | | +| Continue(int) -> void | 5 | | | +| DeclareObject() -> void | 0 | | | +| DerefReference(int &) -> int | 0 | | | +| Dereference(int *) -> int | 0 | | | +| Derived::Derived() -> void | 0 | | | +| Derived::operator=(const Derived &) -> Derived & | 0 | | | +| Derived::~Derived() -> void | 0 | | | +| DerivedVB::DerivedVB() -> void | 0 | | | +| DerivedVB::~DerivedVB() -> void | 0 | | | +| DoStatements(int) -> void | 0 | | | +| DoStatements(int) -> void | 1 | | | +| DoStatements(int) -> void | 2 | | | +| DynamicCast() -> void | 0 | | | +| EarlyReturn(int, int) -> void | 0 | | | +| EarlyReturn(int, int) -> void | 1 | | | +| EarlyReturn(int, int) -> void | 2 | | | +| EarlyReturn(int, int) -> void | 3 | | | +| EarlyReturnValue(int, int) -> int | 0 | | | +| EarlyReturnValue(int, int) -> int | 1 | | | +| EarlyReturnValue(int, int) -> int | 2 | | | +| EarlyReturnValue(int, int) -> int | 3 | | | +| EnumSwitch(E) -> int | 0 | | | +| EnumSwitch(E) -> int | 1 | | | +| EnumSwitch(E) -> int | 2 | | | +| EnumSwitch(E) -> int | 3 | | | +| EnumSwitch(E) -> int | 4 | | | +| FieldAccess() -> void | 0 | | | +| FloatCompare(double, double) -> void | 0 | | | +| FloatCrement(float) -> void | 0 | | | +| FloatOps(double, double) -> void | 0 | | | +| Foo() -> void | 0 | | | +| For_Break() -> void | 0 | | | +| For_Break() -> void | 1 | | | +| For_Break() -> void | 2 | | | +| For_Break() -> void | 3 | | | +| For_Break() -> void | 4 | | | +| For_Break() -> void | 5 | | | +| For_Condition() -> void | 0 | | | +| For_Condition() -> void | 1 | | | +| For_Condition() -> void | 2 | | | +| For_Condition() -> void | 3 | | | +| For_ConditionUpdate() -> void | 0 | | | +| For_ConditionUpdate() -> void | 1 | | | +| For_ConditionUpdate() -> void | 2 | | | +| For_ConditionUpdate() -> void | 3 | | | +| For_Continue_NoUpdate() -> void | 0 | | | +| For_Continue_NoUpdate() -> void | 1 | | | +| For_Continue_NoUpdate() -> void | 2 | | | +| For_Continue_NoUpdate() -> void | 3 | | | +| For_Continue_NoUpdate() -> void | 4 | | | +| For_Continue_NoUpdate() -> void | 5 | | | +| For_Continue_Update() -> void | 0 | | | +| For_Continue_Update() -> void | 1 | | | +| For_Continue_Update() -> void | 2 | | | +| For_Continue_Update() -> void | 3 | | | +| For_Continue_Update() -> void | 4 | | | +| For_Continue_Update() -> void | 5 | | | +| For_Empty() -> void | 0 | | | +| For_Empty() -> void | 1 | | | +| For_Empty() -> void | 2 | | | +| For_Init() -> void | 0 | | | +| For_Init() -> void | 1 | | | +| For_Init() -> void | 2 | | | +| For_InitCondition() -> void | 0 | | | +| For_InitCondition() -> void | 1 | | | +| For_InitCondition() -> void | 2 | | | +| For_InitCondition() -> void | 3 | | | +| For_InitConditionUpdate() -> void | 0 | | | +| For_InitConditionUpdate() -> void | 1 | | | +| For_InitConditionUpdate() -> void | 2 | | | +| For_InitConditionUpdate() -> void | 3 | | | +| For_InitUpdate() -> void | 0 | | | +| For_InitUpdate() -> void | 1 | | | +| For_InitUpdate() -> void | 2 | | | +| For_Update() -> void | 0 | | | +| For_Update() -> void | 1 | | | +| For_Update() -> void | 2 | | | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | | | +| FunctionReferences() -> void | 0 | | | +| HierarchyConversions() -> void | 0 | | | +| IfStatements(bool, int, int) -> void | 0 | | | +| IfStatements(bool, int, int) -> void | 1 | | | +| IfStatements(bool, int, int) -> void | 2 | | | +| IfStatements(bool, int, int) -> void | 3 | | | +| IfStatements(bool, int, int) -> void | 4 | | | +| IfStatements(bool, int, int) -> void | 5 | | | +| IfStatements(bool, int, int) -> void | 6 | | | +| IfStatements(bool, int, int) -> void | 7 | | | +| InitArray() -> void | 0 | | | +| InitList(int, float) -> void | 0 | | | +| InitReference(int) -> void | 0 | | | +| IntegerCompare(int, int) -> void | 0 | | | +| IntegerCrement(int) -> void | 0 | | | +| IntegerCrement_LValue(int) -> void | 0 | | | +| IntegerOps(int, int) -> void | 0 | | | +| LogicalAnd(bool, bool) -> void | 0 | | | +| LogicalAnd(bool, bool) -> void | 1 | | | +| LogicalAnd(bool, bool) -> void | 2 | | | +| LogicalAnd(bool, bool) -> void | 3 | | | +| LogicalAnd(bool, bool) -> void | 4 | | | +| LogicalAnd(bool, bool) -> void | 5 | | | +| LogicalAnd(bool, bool) -> void | 6 | | | +| LogicalAnd(bool, bool) -> void | 7 | | | +| LogicalNot(bool, bool) -> void | 0 | | | +| LogicalNot(bool, bool) -> void | 1 | | | +| LogicalNot(bool, bool) -> void | 2 | | | +| LogicalNot(bool, bool) -> void | 3 | | | +| LogicalNot(bool, bool) -> void | 4 | | | +| LogicalNot(bool, bool) -> void | 5 | | | +| LogicalNot(bool, bool) -> void | 6 | | | +| LogicalOr(bool, bool) -> void | 0 | | | +| LogicalOr(bool, bool) -> void | 1 | | | +| LogicalOr(bool, bool) -> void | 2 | | | +| LogicalOr(bool, bool) -> void | 3 | | | +| LogicalOr(bool, bool) -> void | 4 | | | +| LogicalOr(bool, bool) -> void | 5 | | | +| LogicalOr(bool, bool) -> void | 6 | | | +| LogicalOr(bool, bool) -> void | 7 | | | +| Middle::Middle() -> void | 0 | | | +| Middle::operator=(const Middle &) -> Middle & | 0 | | | +| Middle::~Middle() -> void | 0 | | | +| MiddleVB1::MiddleVB1() -> void | 0 | | | +| MiddleVB1::~MiddleVB1() -> void | 0 | | | +| MiddleVB2::MiddleVB2() -> void | 0 | | | +| MiddleVB2::~MiddleVB2() -> void | 0 | | | +| NestedInitList(int, float) -> void | 0 | | | +| Nullptr() -> void | 0 | | | +| Outer::Func(void *, char) -> long | 0 | | | +| Parameters(int, int) -> int | 0 | | | +| PointerCompare(int *, int *) -> void | 0 | | | +| PointerCrement(int *) -> void | 0 | | | +| PointerOps(int *, int) -> void | 0 | | | +| PolymorphicBase::PolymorphicBase() -> void | 0 | | | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | | | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | | | +| ReturnStruct(Point) -> Point | 0 | | | +| SetFuncPtr() -> void | 0 | | | +| String::String() -> void | 0 | | | +| StringLiteral(int) -> void | 0 | | | +| Switch(int) -> void | 0 | | | +| Switch(int) -> void | 1 | | | +| Switch(int) -> void | 2 | | | +| Switch(int) -> void | 3 | | | +| Switch(int) -> void | 4 | | | +| Switch(int) -> void | 5 | | | +| Switch(int) -> void | 6 | | | +| Switch(int) -> void | 7 | | | +| Switch(int) -> void | 8 | | | +| Switch(int) -> void | 9 | | | +| TakeReference() -> int & | 0 | | | +| TryCatch(bool) -> void | 0 | | | +| TryCatch(bool) -> void | 1 | | | +| TryCatch(bool) -> void | 2 | | | +| TryCatch(bool) -> void | 3 | | | +| TryCatch(bool) -> void | 4 | | | +| TryCatch(bool) -> void | 5 | | | +| TryCatch(bool) -> void | 6 | | | +| TryCatch(bool) -> void | 7 | | | +| TryCatch(bool) -> void | 8 | | | +| TryCatch(bool) -> void | 9 | | | +| TryCatch(bool) -> void | 10 | | | +| TryCatch(bool) -> void | 11 | | | +| TryCatch(bool) -> void | 12 | | | +| TryCatch(bool) -> void | 13 | | | +| TryCatch(bool) -> void | 14 | | | +| UninitializedVariables() -> void | 0 | | | +| UnionInit(int, float) -> void | 0 | | | +| VarArgUsage(int) -> void | 0 | | | +| VarArgs() -> void | 0 | | | +| WhileStatements(int) -> void | 0 | | | +| WhileStatements(int) -> void | 1 | | | +| WhileStatements(int) -> void | 2 | | | +| WhileStatements(int) -> void | 3 | | | +| min(int, int) -> int | 0 | | | +| min(int, int) -> int | 1 | | | +| min(int, int) -> int | 2 | | | +| min(int, int) -> int | 3 | | | +printIRGraphInstructions +| AddressOf() -> int * | 0 | 0 | EnterFunction | ir.cpp:348:6:348:14 | +| AddressOf() -> int * | 0 | 1 | UnmodeledDefinition | ir.cpp:348:6:348:14 | +| AddressOf() -> int * | 0 | 2 | VariableAddress[#return] | ir.cpp:349:5:349:14 | +| AddressOf() -> int * | 0 | 3 | VariableAddress[g] | ir.cpp:349:13:349:13 | +| AddressOf() -> int * | 0 | 4 | Store | ir.cpp:349:12:349:13 | +| AddressOf() -> int * | 0 | 5 | VariableAddress[#return] | ir.cpp:348:6:348:14 | +| AddressOf() -> int * | 0 | 6 | ReturnValue | ir.cpp:348:6:348:14 | +| AddressOf() -> int * | 0 | 7 | UnmodeledUse | ir.cpp:348:6:348:14 | +| AddressOf() -> int * | 0 | 8 | ExitFunction | ir.cpp:348:6:348:14 | +| ArrayAccess(int *, int) -> void | 0 | 0 | EnterFunction | ir.cpp:171:6:171:16 | +| ArrayAccess(int *, int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:171:6:171:16 | +| ArrayAccess(int *, int) -> void | 0 | 2 | InitializeParameter[p] | ir.cpp:171:23:171:23 | +| ArrayAccess(int *, int) -> void | 0 | 3 | VariableAddress[p] | ir.cpp:171:23:171:23 | +| ArrayAccess(int *, int) -> void | 0 | 4 | Store | ir.cpp:171:23:171:23 | +| ArrayAccess(int *, int) -> void | 0 | 5 | InitializeParameter[i] | ir.cpp:171:30:171:30 | +| ArrayAccess(int *, int) -> void | 0 | 6 | VariableAddress[i] | ir.cpp:171:30:171:30 | +| ArrayAccess(int *, int) -> void | 0 | 7 | Store | ir.cpp:171:30:171:30 | +| ArrayAccess(int *, int) -> void | 0 | 8 | VariableAddress[x] | ir.cpp:172:9:172:9 | +| ArrayAccess(int *, int) -> void | 0 | 9 | Uninitialized | ir.cpp:172:9:172:9 | +| ArrayAccess(int *, int) -> void | 0 | 10 | Store | ir.cpp:172:9:172:9 | +| ArrayAccess(int *, int) -> void | 0 | 11 | VariableAddress[p] | ir.cpp:174:9:174:9 | +| ArrayAccess(int *, int) -> void | 0 | 12 | Load | ir.cpp:174:9:174:9 | +| ArrayAccess(int *, int) -> void | 0 | 13 | VariableAddress[i] | ir.cpp:174:11:174:11 | +| ArrayAccess(int *, int) -> void | 0 | 14 | Load | ir.cpp:174:11:174:11 | +| ArrayAccess(int *, int) -> void | 0 | 15 | PointerAdd[4] | ir.cpp:174:9:174:12 | +| ArrayAccess(int *, int) -> void | 0 | 16 | Load | ir.cpp:174:9:174:12 | +| ArrayAccess(int *, int) -> void | 0 | 17 | VariableAddress[x] | ir.cpp:174:5:174:5 | +| ArrayAccess(int *, int) -> void | 0 | 18 | Store | ir.cpp:174:5:174:12 | +| ArrayAccess(int *, int) -> void | 0 | 19 | VariableAddress[p] | ir.cpp:175:11:175:11 | +| ArrayAccess(int *, int) -> void | 0 | 20 | Load | ir.cpp:175:11:175:11 | +| ArrayAccess(int *, int) -> void | 0 | 21 | VariableAddress[i] | ir.cpp:175:9:175:9 | +| ArrayAccess(int *, int) -> void | 0 | 22 | Load | ir.cpp:175:9:175:9 | +| ArrayAccess(int *, int) -> void | 0 | 23 | PointerAdd[4] | ir.cpp:175:9:175:12 | +| ArrayAccess(int *, int) -> void | 0 | 24 | Load | ir.cpp:175:9:175:12 | +| ArrayAccess(int *, int) -> void | 0 | 25 | VariableAddress[x] | ir.cpp:175:5:175:5 | +| ArrayAccess(int *, int) -> void | 0 | 26 | Store | ir.cpp:175:5:175:12 | +| ArrayAccess(int *, int) -> void | 0 | 27 | VariableAddress[x] | ir.cpp:177:12:177:12 | +| ArrayAccess(int *, int) -> void | 0 | 28 | Load | ir.cpp:177:12:177:12 | +| ArrayAccess(int *, int) -> void | 0 | 29 | VariableAddress[p] | ir.cpp:177:5:177:5 | +| ArrayAccess(int *, int) -> void | 0 | 30 | Load | ir.cpp:177:5:177:5 | +| ArrayAccess(int *, int) -> void | 0 | 31 | VariableAddress[i] | ir.cpp:177:7:177:7 | +| ArrayAccess(int *, int) -> void | 0 | 32 | Load | ir.cpp:177:7:177:7 | +| ArrayAccess(int *, int) -> void | 0 | 33 | PointerAdd[4] | ir.cpp:177:5:177:8 | +| ArrayAccess(int *, int) -> void | 0 | 34 | Store | ir.cpp:177:5:177:12 | +| ArrayAccess(int *, int) -> void | 0 | 35 | VariableAddress[x] | ir.cpp:178:12:178:12 | +| ArrayAccess(int *, int) -> void | 0 | 36 | Load | ir.cpp:178:12:178:12 | +| ArrayAccess(int *, int) -> void | 0 | 37 | VariableAddress[p] | ir.cpp:178:7:178:7 | +| ArrayAccess(int *, int) -> void | 0 | 38 | Load | ir.cpp:178:7:178:7 | +| ArrayAccess(int *, int) -> void | 0 | 39 | VariableAddress[i] | ir.cpp:178:5:178:5 | +| ArrayAccess(int *, int) -> void | 0 | 40 | Load | ir.cpp:178:5:178:5 | +| ArrayAccess(int *, int) -> void | 0 | 41 | PointerAdd[4] | ir.cpp:178:5:178:8 | +| ArrayAccess(int *, int) -> void | 0 | 42 | Store | ir.cpp:178:5:178:12 | +| ArrayAccess(int *, int) -> void | 0 | 43 | VariableAddress[a] | ir.cpp:180:9:180:9 | +| ArrayAccess(int *, int) -> void | 0 | 44 | Uninitialized | ir.cpp:180:9:180:9 | +| ArrayAccess(int *, int) -> void | 0 | 45 | Store | ir.cpp:180:9:180:9 | +| ArrayAccess(int *, int) -> void | 0 | 46 | VariableAddress[a] | ir.cpp:181:9:181:9 | +| ArrayAccess(int *, int) -> void | 0 | 47 | Convert | ir.cpp:181:9:181:9 | +| ArrayAccess(int *, int) -> void | 0 | 48 | VariableAddress[i] | ir.cpp:181:11:181:11 | +| ArrayAccess(int *, int) -> void | 0 | 49 | Load | ir.cpp:181:11:181:11 | +| ArrayAccess(int *, int) -> void | 0 | 50 | PointerAdd[4] | ir.cpp:181:9:181:12 | +| ArrayAccess(int *, int) -> void | 0 | 51 | Load | ir.cpp:181:9:181:12 | +| ArrayAccess(int *, int) -> void | 0 | 52 | VariableAddress[x] | ir.cpp:181:5:181:5 | +| ArrayAccess(int *, int) -> void | 0 | 53 | Store | ir.cpp:181:5:181:12 | +| ArrayAccess(int *, int) -> void | 0 | 54 | VariableAddress[a] | ir.cpp:182:11:182:11 | +| ArrayAccess(int *, int) -> void | 0 | 55 | Convert | ir.cpp:182:11:182:11 | +| ArrayAccess(int *, int) -> void | 0 | 56 | VariableAddress[i] | ir.cpp:182:9:182:9 | +| ArrayAccess(int *, int) -> void | 0 | 57 | Load | ir.cpp:182:9:182:9 | +| ArrayAccess(int *, int) -> void | 0 | 58 | PointerAdd[4] | ir.cpp:182:9:182:12 | +| ArrayAccess(int *, int) -> void | 0 | 59 | Load | ir.cpp:182:9:182:12 | +| ArrayAccess(int *, int) -> void | 0 | 60 | VariableAddress[x] | ir.cpp:182:5:182:5 | +| ArrayAccess(int *, int) -> void | 0 | 61 | Store | ir.cpp:182:5:182:12 | +| ArrayAccess(int *, int) -> void | 0 | 62 | VariableAddress[x] | ir.cpp:183:12:183:12 | +| ArrayAccess(int *, int) -> void | 0 | 63 | Load | ir.cpp:183:12:183:12 | +| ArrayAccess(int *, int) -> void | 0 | 64 | VariableAddress[a] | ir.cpp:183:5:183:5 | +| ArrayAccess(int *, int) -> void | 0 | 65 | Convert | ir.cpp:183:5:183:5 | +| ArrayAccess(int *, int) -> void | 0 | 66 | VariableAddress[i] | ir.cpp:183:7:183:7 | +| ArrayAccess(int *, int) -> void | 0 | 67 | Load | ir.cpp:183:7:183:7 | +| ArrayAccess(int *, int) -> void | 0 | 68 | PointerAdd[4] | ir.cpp:183:5:183:8 | +| ArrayAccess(int *, int) -> void | 0 | 69 | Store | ir.cpp:183:5:183:12 | +| ArrayAccess(int *, int) -> void | 0 | 70 | VariableAddress[x] | ir.cpp:184:12:184:12 | +| ArrayAccess(int *, int) -> void | 0 | 71 | Load | ir.cpp:184:12:184:12 | +| ArrayAccess(int *, int) -> void | 0 | 72 | VariableAddress[a] | ir.cpp:184:7:184:7 | +| ArrayAccess(int *, int) -> void | 0 | 73 | Convert | ir.cpp:184:7:184:7 | +| ArrayAccess(int *, int) -> void | 0 | 74 | VariableAddress[i] | ir.cpp:184:5:184:5 | +| ArrayAccess(int *, int) -> void | 0 | 75 | Load | ir.cpp:184:5:184:5 | +| ArrayAccess(int *, int) -> void | 0 | 76 | PointerAdd[4] | ir.cpp:184:5:184:8 | +| ArrayAccess(int *, int) -> void | 0 | 77 | Store | ir.cpp:184:5:184:12 | +| ArrayAccess(int *, int) -> void | 0 | 78 | NoOp | ir.cpp:185:1:185:1 | +| ArrayAccess(int *, int) -> void | 0 | 79 | ReturnVoid | ir.cpp:171:6:171:16 | +| ArrayAccess(int *, int) -> void | 0 | 80 | UnmodeledUse | ir.cpp:171:6:171:16 | +| ArrayAccess(int *, int) -> void | 0 | 81 | ExitFunction | ir.cpp:171:6:171:16 | +| ArrayConversions() -> void | 0 | 0 | EnterFunction | ir.cpp:871:6:871:21 | +| ArrayConversions() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:871:6:871:21 | +| ArrayConversions() -> void | 0 | 2 | VariableAddress[a] | ir.cpp:872:8:872:8 | +| ArrayConversions() -> void | 0 | 3 | Uninitialized | ir.cpp:872:8:872:8 | +| ArrayConversions() -> void | 0 | 4 | Store | ir.cpp:872:8:872:8 | +| ArrayConversions() -> void | 0 | 5 | VariableAddress[p] | ir.cpp:873:15:873:15 | +| ArrayConversions() -> void | 0 | 6 | VariableAddress[a] | ir.cpp:873:19:873:19 | +| ArrayConversions() -> void | 0 | 7 | Convert | ir.cpp:873:19:873:19 | +| ArrayConversions() -> void | 0 | 8 | Convert | ir.cpp:873:19:873:19 | +| ArrayConversions() -> void | 0 | 9 | Store | ir.cpp:873:19:873:19 | +| ArrayConversions() -> void | 0 | 10 | StringConstant["test"] | ir.cpp:874:7:874:12 | +| ArrayConversions() -> void | 0 | 11 | Convert | ir.cpp:874:7:874:12 | +| ArrayConversions() -> void | 0 | 12 | VariableAddress[p] | ir.cpp:874:3:874:3 | +| ArrayConversions() -> void | 0 | 13 | Store | ir.cpp:874:3:874:12 | +| ArrayConversions() -> void | 0 | 14 | VariableAddress[a] | ir.cpp:875:8:875:8 | +| ArrayConversions() -> void | 0 | 15 | Convert | ir.cpp:875:8:875:8 | +| ArrayConversions() -> void | 0 | 16 | Constant[0] | ir.cpp:875:10:875:10 | +| ArrayConversions() -> void | 0 | 17 | PointerAdd[1] | ir.cpp:875:8:875:11 | +| ArrayConversions() -> void | 0 | 18 | Convert | ir.cpp:875:7:875:11 | +| ArrayConversions() -> void | 0 | 19 | VariableAddress[p] | ir.cpp:875:3:875:3 | +| ArrayConversions() -> void | 0 | 20 | Store | ir.cpp:875:3:875:11 | +| ArrayConversions() -> void | 0 | 21 | StringConstant["test"] | ir.cpp:876:8:876:13 | +| ArrayConversions() -> void | 0 | 22 | Convert | ir.cpp:876:8:876:13 | +| ArrayConversions() -> void | 0 | 23 | Constant[0] | ir.cpp:876:15:876:15 | +| ArrayConversions() -> void | 0 | 24 | PointerAdd[1] | ir.cpp:876:8:876:16 | +| ArrayConversions() -> void | 0 | 25 | VariableAddress[p] | ir.cpp:876:3:876:3 | +| ArrayConversions() -> void | 0 | 26 | Store | ir.cpp:876:3:876:16 | +| ArrayConversions() -> void | 0 | 27 | VariableAddress[ra] | ir.cpp:877:10:877:11 | +| ArrayConversions() -> void | 0 | 28 | VariableAddress[a] | ir.cpp:877:19:877:19 | +| ArrayConversions() -> void | 0 | 29 | Store | ir.cpp:877:19:877:19 | +| ArrayConversions() -> void | 0 | 30 | VariableAddress[rs] | ir.cpp:878:16:878:17 | +| ArrayConversions() -> void | 0 | 31 | StringConstant["test"] | ir.cpp:878:25:878:30 | +| ArrayConversions() -> void | 0 | 32 | Store | ir.cpp:878:25:878:30 | +| ArrayConversions() -> void | 0 | 33 | VariableAddress[pa] | ir.cpp:879:16:879:17 | +| ArrayConversions() -> void | 0 | 34 | VariableAddress[a] | ir.cpp:879:26:879:26 | +| ArrayConversions() -> void | 0 | 35 | Convert | ir.cpp:879:25:879:26 | +| ArrayConversions() -> void | 0 | 36 | Store | ir.cpp:879:25:879:26 | +| ArrayConversions() -> void | 0 | 37 | StringConstant["test"] | ir.cpp:880:9:880:14 | +| ArrayConversions() -> void | 0 | 38 | VariableAddress[pa] | ir.cpp:880:3:880:4 | +| ArrayConversions() -> void | 0 | 39 | Store | ir.cpp:880:3:880:14 | +| ArrayConversions() -> void | 0 | 40 | NoOp | ir.cpp:881:1:881:1 | +| ArrayConversions() -> void | 0 | 41 | ReturnVoid | ir.cpp:871:6:871:21 | +| ArrayConversions() -> void | 0 | 42 | UnmodeledUse | ir.cpp:871:6:871:21 | +| ArrayConversions() -> void | 0 | 43 | ExitFunction | ir.cpp:871:6:871:21 | +| ArrayInit(int, float) -> void | 0 | 0 | EnterFunction | ir.cpp:519:6:519:14 | +| ArrayInit(int, float) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:519:6:519:14 | +| ArrayInit(int, float) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:519:20:519:20 | +| ArrayInit(int, float) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:519:20:519:20 | +| ArrayInit(int, float) -> void | 0 | 4 | Store | ir.cpp:519:20:519:20 | +| ArrayInit(int, float) -> void | 0 | 5 | InitializeParameter[f] | ir.cpp:519:29:519:29 | +| ArrayInit(int, float) -> void | 0 | 6 | VariableAddress[f] | ir.cpp:519:29:519:29 | +| ArrayInit(int, float) -> void | 0 | 7 | Store | ir.cpp:519:29:519:29 | +| ArrayInit(int, float) -> void | 0 | 8 | VariableAddress[a1] | ir.cpp:520:9:520:10 | +| ArrayInit(int, float) -> void | 0 | 9 | Constant[0] | ir.cpp:520:16:520:18 | +| ArrayInit(int, float) -> void | 0 | 10 | PointerAdd | ir.cpp:520:16:520:18 | +| ArrayInit(int, float) -> void | 0 | 11 | Constant[0] | ir.cpp:520:16:520:18 | +| ArrayInit(int, float) -> void | 0 | 12 | Store | ir.cpp:520:16:520:18 | +| ArrayInit(int, float) -> void | 0 | 13 | VariableAddress[a2] | ir.cpp:521:9:521:10 | +| ArrayInit(int, float) -> void | 0 | 14 | Constant[0] | ir.cpp:521:16:521:27 | +| ArrayInit(int, float) -> void | 0 | 15 | PointerAdd | ir.cpp:521:16:521:27 | +| ArrayInit(int, float) -> void | 0 | 16 | VariableAddress[x] | ir.cpp:521:19:521:19 | +| ArrayInit(int, float) -> void | 0 | 17 | Load | ir.cpp:521:19:521:19 | +| ArrayInit(int, float) -> void | 0 | 18 | Store | ir.cpp:521:19:521:19 | +| ArrayInit(int, float) -> void | 0 | 19 | Constant[1] | ir.cpp:521:16:521:27 | +| ArrayInit(int, float) -> void | 0 | 20 | PointerAdd | ir.cpp:521:16:521:27 | +| ArrayInit(int, float) -> void | 0 | 21 | VariableAddress[f] | ir.cpp:521:22:521:22 | +| ArrayInit(int, float) -> void | 0 | 22 | Load | ir.cpp:521:22:521:22 | +| ArrayInit(int, float) -> void | 0 | 23 | Convert | ir.cpp:521:22:521:22 | +| ArrayInit(int, float) -> void | 0 | 24 | Store | ir.cpp:521:22:521:22 | +| ArrayInit(int, float) -> void | 0 | 25 | Constant[2] | ir.cpp:521:16:521:27 | +| ArrayInit(int, float) -> void | 0 | 26 | PointerAdd | ir.cpp:521:16:521:27 | +| ArrayInit(int, float) -> void | 0 | 27 | Constant[0] | ir.cpp:521:25:521:25 | +| ArrayInit(int, float) -> void | 0 | 28 | Store | ir.cpp:521:25:521:25 | +| ArrayInit(int, float) -> void | 0 | 29 | VariableAddress[a3] | ir.cpp:522:9:522:10 | +| ArrayInit(int, float) -> void | 0 | 30 | Constant[0] | ir.cpp:522:16:522:21 | +| ArrayInit(int, float) -> void | 0 | 31 | PointerAdd | ir.cpp:522:16:522:21 | +| ArrayInit(int, float) -> void | 0 | 32 | VariableAddress[x] | ir.cpp:522:19:522:19 | +| ArrayInit(int, float) -> void | 0 | 33 | Load | ir.cpp:522:19:522:19 | +| ArrayInit(int, float) -> void | 0 | 34 | Store | ir.cpp:522:19:522:19 | +| ArrayInit(int, float) -> void | 0 | 35 | Constant[1] | ir.cpp:522:16:522:21 | +| ArrayInit(int, float) -> void | 0 | 36 | PointerAdd | ir.cpp:522:16:522:21 | +| ArrayInit(int, float) -> void | 0 | 37 | Constant[0] | ir.cpp:522:16:522:21 | +| ArrayInit(int, float) -> void | 0 | 38 | Store | ir.cpp:522:16:522:21 | +| ArrayInit(int, float) -> void | 0 | 39 | NoOp | ir.cpp:523:1:523:1 | +| ArrayInit(int, float) -> void | 0 | 40 | ReturnVoid | ir.cpp:519:6:519:14 | +| ArrayInit(int, float) -> void | 0 | 41 | UnmodeledUse | ir.cpp:519:6:519:14 | +| ArrayInit(int, float) -> void | 0 | 42 | ExitFunction | ir.cpp:519:6:519:14 | +| ArrayReferences() -> void | 0 | 0 | EnterFunction | ir.cpp:691:6:691:20 | +| ArrayReferences() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:691:6:691:20 | +| ArrayReferences() -> void | 0 | 2 | VariableAddress[a] | ir.cpp:692:7:692:7 | +| ArrayReferences() -> void | 0 | 3 | Uninitialized | ir.cpp:692:7:692:7 | +| ArrayReferences() -> void | 0 | 4 | Store | ir.cpp:692:7:692:7 | +| ArrayReferences() -> void | 0 | 5 | VariableAddress[ra] | ir.cpp:693:9:693:10 | +| ArrayReferences() -> void | 0 | 6 | VariableAddress[a] | ir.cpp:693:19:693:19 | +| ArrayReferences() -> void | 0 | 7 | Store | ir.cpp:693:19:693:19 | +| ArrayReferences() -> void | 0 | 8 | VariableAddress[x] | ir.cpp:694:7:694:7 | +| ArrayReferences() -> void | 0 | 9 | VariableAddress[ra] | ir.cpp:694:11:694:12 | +| ArrayReferences() -> void | 0 | 10 | Load | ir.cpp:694:11:694:12 | +| ArrayReferences() -> void | 0 | 11 | Convert | ir.cpp:694:11:694:12 | +| ArrayReferences() -> void | 0 | 12 | Constant[5] | ir.cpp:694:14:694:14 | +| ArrayReferences() -> void | 0 | 13 | PointerAdd[4] | ir.cpp:694:11:694:15 | +| ArrayReferences() -> void | 0 | 14 | Load | ir.cpp:694:11:694:15 | +| ArrayReferences() -> void | 0 | 15 | Store | ir.cpp:694:11:694:15 | +| ArrayReferences() -> void | 0 | 16 | NoOp | ir.cpp:695:1:695:1 | +| ArrayReferences() -> void | 0 | 17 | ReturnVoid | ir.cpp:691:6:691:20 | +| ArrayReferences() -> void | 0 | 18 | UnmodeledUse | ir.cpp:691:6:691:20 | +| ArrayReferences() -> void | 0 | 19 | ExitFunction | ir.cpp:691:6:691:20 | +| Base::Base() -> void | 0 | 0 | EnterFunction | ir.cpp:748:3:748:6 | +| Base::Base() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:748:3:748:6 | +| Base::Base() -> void | 0 | 2 | InitializeThis | ir.cpp:748:3:748:6 | +| Base::Base() -> void | 0 | 3 | FieldAddress[base_s] | ir.cpp:748:10:748:10 | +| Base::Base() -> void | 0 | 4 | FunctionAddress[String] | ir.cpp:748:10:748:10 | +| Base::Base() -> void | 0 | 5 | Invoke | ir.cpp:748:10:748:10 | +| Base::Base() -> void | 0 | 6 | NoOp | ir.cpp:749:3:749:3 | +| Base::Base() -> void | 0 | 7 | ReturnVoid | ir.cpp:748:3:748:6 | +| Base::Base() -> void | 0 | 8 | UnmodeledUse | ir.cpp:748:3:748:6 | +| Base::Base() -> void | 0 | 9 | ExitFunction | ir.cpp:748:3:748:6 | +| Base::Base(const Base &) -> void | 0 | 0 | EnterFunction | ir.cpp:745:8:745:8 | +| Base::Base(const Base &) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:745:8:745:8 | +| Base::Base(const Base &) -> void | 0 | 2 | InitializeThis | ir.cpp:745:8:745:8 | +| Base::Base(const Base &) -> void | 0 | 3 | InitializeParameter[p#0] | file://:0:0:0:0 | +| Base::Base(const Base &) -> void | 0 | 4 | VariableAddress[p#0] | file://:0:0:0:0 | +| Base::Base(const Base &) -> void | 0 | 5 | Store | file://:0:0:0:0 | +| Base::Base(const Base &) -> void | 0 | 6 | FieldAddress[base_s] | ir.cpp:745:8:745:8 | +| Base::Base(const Base &) -> void | 0 | 7 | FunctionAddress[String] | ir.cpp:745:8:745:8 | +| Base::Base(const Base &) -> void | 0 | 8 | Invoke | ir.cpp:745:8:745:8 | +| Base::Base(const Base &) -> void | 0 | 9 | NoOp | ir.cpp:745:8:745:8 | +| Base::Base(const Base &) -> void | 0 | 10 | ReturnVoid | ir.cpp:745:8:745:8 | +| Base::Base(const Base &) -> void | 0 | 11 | UnmodeledUse | ir.cpp:745:8:745:8 | +| Base::Base(const Base &) -> void | 0 | 12 | ExitFunction | ir.cpp:745:8:745:8 | +| Base::operator=(const Base &) -> Base & | 0 | 0 | EnterFunction | ir.cpp:745:8:745:8 | +| Base::operator=(const Base &) -> Base & | 0 | 1 | UnmodeledDefinition | ir.cpp:745:8:745:8 | +| Base::operator=(const Base &) -> Base & | 0 | 2 | InitializeThis | ir.cpp:745:8:745:8 | +| Base::operator=(const Base &) -> Base & | 0 | 3 | InitializeParameter[p#0] | file://:0:0:0:0 | +| Base::operator=(const Base &) -> Base & | 0 | 4 | VariableAddress[p#0] | file://:0:0:0:0 | +| Base::operator=(const Base &) -> Base & | 0 | 5 | Store | file://:0:0:0:0 | +| Base::operator=(const Base &) -> Base & | 0 | 6 | CopyValue | file://:0:0:0:0 | +| Base::operator=(const Base &) -> Base & | 0 | 7 | FieldAddress[base_s] | file://:0:0:0:0 | +| Base::operator=(const Base &) -> Base & | 0 | 8 | FunctionAddress[operator=] | ir.cpp:745:8:745:8 | +| Base::operator=(const Base &) -> Base & | 0 | 9 | VariableAddress[p#0] | file://:0:0:0:0 | +| Base::operator=(const Base &) -> Base & | 0 | 10 | Load | file://:0:0:0:0 | +| Base::operator=(const Base &) -> Base & | 0 | 11 | FieldAddress[base_s] | file://:0:0:0:0 | +| Base::operator=(const Base &) -> Base & | 0 | 12 | Invoke | ir.cpp:745:8:745:8 | +| Base::operator=(const Base &) -> Base & | 0 | 13 | VariableAddress[#return] | file://:0:0:0:0 | +| Base::operator=(const Base &) -> Base & | 0 | 14 | CopyValue | file://:0:0:0:0 | +| Base::operator=(const Base &) -> Base & | 0 | 15 | Store | file://:0:0:0:0 | +| Base::operator=(const Base &) -> Base & | 0 | 16 | VariableAddress[#return] | ir.cpp:745:8:745:8 | +| Base::operator=(const Base &) -> Base & | 0 | 17 | ReturnValue | ir.cpp:745:8:745:8 | +| Base::operator=(const Base &) -> Base & | 0 | 18 | UnmodeledUse | ir.cpp:745:8:745:8 | +| Base::operator=(const Base &) -> Base & | 0 | 19 | ExitFunction | ir.cpp:745:8:745:8 | +| Base::~Base() -> void | 0 | 0 | EnterFunction | ir.cpp:750:3:750:7 | +| Base::~Base() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:750:3:750:7 | +| Base::~Base() -> void | 0 | 2 | InitializeThis | ir.cpp:750:3:750:7 | +| Base::~Base() -> void | 0 | 3 | NoOp | ir.cpp:751:3:751:3 | +| Base::~Base() -> void | 0 | 4 | FieldAddress[base_s] | ir.cpp:751:3:751:3 | +| Base::~Base() -> void | 0 | 5 | FunctionAddress[~String] | ir.cpp:751:3:751:3 | +| Base::~Base() -> void | 0 | 6 | Invoke | ir.cpp:751:3:751:3 | +| Base::~Base() -> void | 0 | 7 | ReturnVoid | ir.cpp:750:3:750:7 | +| Base::~Base() -> void | 0 | 8 | UnmodeledUse | ir.cpp:750:3:750:7 | +| Base::~Base() -> void | 0 | 9 | ExitFunction | ir.cpp:750:3:750:7 | +| Break(int) -> void | 0 | 0 | EnterFunction | ir.cpp:352:6:352:10 | +| Break(int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:352:6:352:10 | +| Break(int) -> void | 0 | 2 | InitializeParameter[n] | ir.cpp:352:16:352:16 | +| Break(int) -> void | 0 | 3 | VariableAddress[n] | ir.cpp:352:16:352:16 | +| Break(int) -> void | 0 | 4 | Store | ir.cpp:352:16:352:16 | +| Break(int) -> void | 1 | 0 | VariableAddress[n] | ir.cpp:354:13:354:13 | +| Break(int) -> void | 1 | 1 | Load | ir.cpp:354:13:354:13 | +| Break(int) -> void | 1 | 2 | Constant[1] | ir.cpp:354:18:354:18 | +| Break(int) -> void | 1 | 3 | CompareEQ | ir.cpp:354:13:354:18 | +| Break(int) -> void | 1 | 4 | ConditionalBranch | ir.cpp:354:13:354:18 | +| Break(int) -> void | 2 | 0 | NoOp | ir.cpp:355:13:355:18 | +| Break(int) -> void | 3 | 0 | Constant[1] | ir.cpp:356:14:356:14 | +| Break(int) -> void | 3 | 1 | VariableAddress[n] | ir.cpp:356:9:356:9 | +| Break(int) -> void | 3 | 2 | Load | ir.cpp:356:9:356:14 | +| Break(int) -> void | 3 | 3 | Sub | ir.cpp:356:9:356:14 | +| Break(int) -> void | 3 | 4 | Store | ir.cpp:356:9:356:14 | +| Break(int) -> void | 4 | 0 | NoOp | ir.cpp:357:5:357:5 | +| Break(int) -> void | 4 | 1 | NoOp | ir.cpp:358:1:358:1 | +| Break(int) -> void | 4 | 2 | ReturnVoid | ir.cpp:352:6:352:10 | +| Break(int) -> void | 4 | 3 | UnmodeledUse | ir.cpp:352:6:352:10 | +| Break(int) -> void | 4 | 4 | ExitFunction | ir.cpp:352:6:352:10 | +| Break(int) -> void | 5 | 0 | Phi | ir.cpp:353:12:353:12 | +| Break(int) -> void | 5 | 1 | VariableAddress[n] | ir.cpp:353:12:353:12 | +| Break(int) -> void | 5 | 2 | Load | ir.cpp:353:12:353:12 | +| Break(int) -> void | 5 | 3 | Constant[0] | ir.cpp:353:16:353:16 | +| Break(int) -> void | 5 | 4 | CompareGT | ir.cpp:353:12:353:16 | +| Break(int) -> void | 5 | 5 | ConditionalBranch | ir.cpp:353:12:353:16 | +| C::C() -> void | 0 | 0 | EnterFunction | ir.cpp:658:5:658:5 | +| C::C() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:658:5:658:5 | +| C::C() -> void | 0 | 2 | InitializeThis | ir.cpp:658:5:658:5 | +| C::C() -> void | 0 | 3 | FieldAddress[m_a] | ir.cpp:659:9:659:14 | +| C::C() -> void | 0 | 4 | Constant[1] | ir.cpp:659:9:659:14 | +| C::C() -> void | 0 | 5 | Store | ir.cpp:659:9:659:14 | +| C::C() -> void | 0 | 6 | FieldAddress[m_b] | ir.cpp:663:5:663:5 | +| C::C() -> void | 0 | 7 | FunctionAddress[String] | ir.cpp:663:5:663:5 | +| C::C() -> void | 0 | 8 | Invoke | ir.cpp:663:5:663:5 | +| C::C() -> void | 0 | 9 | FieldAddress[m_c] | ir.cpp:660:9:660:14 | +| C::C() -> void | 0 | 10 | Constant[3] | ir.cpp:660:13:660:13 | +| C::C() -> void | 0 | 11 | Store | ir.cpp:660:13:660:13 | +| C::C() -> void | 0 | 12 | FieldAddress[m_e] | ir.cpp:661:9:661:13 | +| C::C() -> void | 0 | 13 | Constant[0] | ir.cpp:661:9:661:13 | +| C::C() -> void | 0 | 14 | Store | ir.cpp:661:9:661:13 | +| C::C() -> void | 0 | 15 | FieldAddress[m_f] | ir.cpp:662:9:662:19 | +| C::C() -> void | 0 | 16 | FunctionAddress[String] | ir.cpp:662:9:662:19 | +| C::C() -> void | 0 | 17 | StringConstant["test"] | ir.cpp:662:13:662:18 | +| C::C() -> void | 0 | 18 | Convert | ir.cpp:662:13:662:18 | +| C::C() -> void | 0 | 19 | Invoke | ir.cpp:662:9:662:19 | +| C::C() -> void | 0 | 20 | NoOp | ir.cpp:664:5:664:5 | +| C::C() -> void | 0 | 21 | ReturnVoid | ir.cpp:658:5:658:5 | +| C::C() -> void | 0 | 22 | UnmodeledUse | ir.cpp:658:5:658:5 | +| C::C() -> void | 0 | 23 | ExitFunction | ir.cpp:658:5:658:5 | +| C::FieldAccess() -> void | 0 | 0 | EnterFunction | ir.cpp:642:10:642:20 | +| C::FieldAccess() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:642:10:642:20 | +| C::FieldAccess() -> void | 0 | 2 | InitializeThis | ir.cpp:642:10:642:20 | +| C::FieldAccess() -> void | 0 | 3 | Constant[0] | ir.cpp:643:21:643:21 | +| C::FieldAccess() -> void | 0 | 4 | CopyValue | ir.cpp:643:9:643:12 | +| C::FieldAccess() -> void | 0 | 5 | FieldAddress[m_a] | ir.cpp:643:15:643:17 | +| C::FieldAccess() -> void | 0 | 6 | Store | ir.cpp:643:9:643:21 | +| C::FieldAccess() -> void | 0 | 7 | Constant[1] | ir.cpp:644:23:644:23 | +| C::FieldAccess() -> void | 0 | 8 | CopyValue | ir.cpp:644:11:644:14 | +| C::FieldAccess() -> void | 0 | 9 | FieldAddress[m_a] | ir.cpp:644:17:644:19 | +| C::FieldAccess() -> void | 0 | 10 | Store | ir.cpp:644:9:644:23 | +| C::FieldAccess() -> void | 0 | 11 | Constant[2] | ir.cpp:645:15:645:15 | +| C::FieldAccess() -> void | 0 | 12 | CopyValue | file://:0:0:0:0 | +| C::FieldAccess() -> void | 0 | 13 | FieldAddress[m_a] | ir.cpp:645:9:645:11 | +| C::FieldAccess() -> void | 0 | 14 | Store | ir.cpp:645:9:645:15 | +| C::FieldAccess() -> void | 0 | 15 | VariableAddress[x] | ir.cpp:646:13:646:13 | +| C::FieldAccess() -> void | 0 | 16 | Uninitialized | ir.cpp:646:13:646:13 | +| C::FieldAccess() -> void | 0 | 17 | Store | ir.cpp:646:13:646:13 | +| C::FieldAccess() -> void | 0 | 18 | CopyValue | ir.cpp:647:13:647:16 | +| C::FieldAccess() -> void | 0 | 19 | FieldAddress[m_a] | ir.cpp:647:19:647:21 | +| C::FieldAccess() -> void | 0 | 20 | Load | ir.cpp:647:19:647:21 | +| C::FieldAccess() -> void | 0 | 21 | VariableAddress[x] | ir.cpp:647:9:647:9 | +| C::FieldAccess() -> void | 0 | 22 | Store | ir.cpp:647:9:647:21 | +| C::FieldAccess() -> void | 0 | 23 | CopyValue | ir.cpp:648:15:648:18 | +| C::FieldAccess() -> void | 0 | 24 | FieldAddress[m_a] | ir.cpp:648:21:648:23 | +| C::FieldAccess() -> void | 0 | 25 | Load | ir.cpp:648:21:648:23 | +| C::FieldAccess() -> void | 0 | 26 | VariableAddress[x] | ir.cpp:648:9:648:9 | +| C::FieldAccess() -> void | 0 | 27 | Store | ir.cpp:648:9:648:23 | +| C::FieldAccess() -> void | 0 | 28 | CopyValue | file://:0:0:0:0 | +| C::FieldAccess() -> void | 0 | 29 | FieldAddress[m_a] | ir.cpp:649:13:649:15 | +| C::FieldAccess() -> void | 0 | 30 | Load | ir.cpp:649:13:649:15 | +| C::FieldAccess() -> void | 0 | 31 | VariableAddress[x] | ir.cpp:649:9:649:9 | +| C::FieldAccess() -> void | 0 | 32 | Store | ir.cpp:649:9:649:15 | +| C::FieldAccess() -> void | 0 | 33 | NoOp | ir.cpp:650:5:650:5 | +| C::FieldAccess() -> void | 0 | 34 | ReturnVoid | ir.cpp:642:10:642:20 | +| C::FieldAccess() -> void | 0 | 35 | UnmodeledUse | ir.cpp:642:10:642:20 | +| C::FieldAccess() -> void | 0 | 36 | ExitFunction | ir.cpp:642:10:642:20 | +| C::InstanceMemberFunction(int) -> int | 0 | 0 | EnterFunction | ir.cpp:634:9:634:30 | +| C::InstanceMemberFunction(int) -> int | 0 | 1 | UnmodeledDefinition | ir.cpp:634:9:634:30 | +| C::InstanceMemberFunction(int) -> int | 0 | 2 | InitializeThis | ir.cpp:634:9:634:30 | +| C::InstanceMemberFunction(int) -> int | 0 | 3 | InitializeParameter[x] | ir.cpp:634:36:634:36 | +| C::InstanceMemberFunction(int) -> int | 0 | 4 | VariableAddress[x] | ir.cpp:634:36:634:36 | +| C::InstanceMemberFunction(int) -> int | 0 | 5 | Store | ir.cpp:634:36:634:36 | +| C::InstanceMemberFunction(int) -> int | 0 | 6 | VariableAddress[#return] | ir.cpp:635:9:635:17 | +| C::InstanceMemberFunction(int) -> int | 0 | 7 | VariableAddress[x] | ir.cpp:635:16:635:16 | +| C::InstanceMemberFunction(int) -> int | 0 | 8 | Load | ir.cpp:635:16:635:16 | +| C::InstanceMemberFunction(int) -> int | 0 | 9 | Store | ir.cpp:635:16:635:16 | +| C::InstanceMemberFunction(int) -> int | 0 | 10 | VariableAddress[#return] | ir.cpp:634:9:634:30 | +| C::InstanceMemberFunction(int) -> int | 0 | 11 | ReturnValue | ir.cpp:634:9:634:30 | +| C::InstanceMemberFunction(int) -> int | 0 | 12 | UnmodeledUse | ir.cpp:634:9:634:30 | +| C::InstanceMemberFunction(int) -> int | 0 | 13 | ExitFunction | ir.cpp:634:9:634:30 | +| C::MethodCalls() -> void | 0 | 0 | EnterFunction | ir.cpp:652:10:652:20 | +| C::MethodCalls() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:652:10:652:20 | +| C::MethodCalls() -> void | 0 | 2 | InitializeThis | ir.cpp:652:10:652:20 | +| C::MethodCalls() -> void | 0 | 3 | CopyValue | ir.cpp:653:9:653:12 | +| C::MethodCalls() -> void | 0 | 4 | FunctionAddress[InstanceMemberFunction] | ir.cpp:653:15:653:36 | +| C::MethodCalls() -> void | 0 | 5 | Constant[0] | ir.cpp:653:38:653:38 | +| C::MethodCalls() -> void | 0 | 6 | Invoke | ir.cpp:653:15:653:36 | +| C::MethodCalls() -> void | 0 | 7 | CopyValue | ir.cpp:654:11:654:14 | +| C::MethodCalls() -> void | 0 | 8 | FunctionAddress[InstanceMemberFunction] | ir.cpp:654:17:654:38 | +| C::MethodCalls() -> void | 0 | 9 | Constant[1] | ir.cpp:654:40:654:40 | +| C::MethodCalls() -> void | 0 | 10 | Invoke | ir.cpp:654:17:654:38 | +| C::MethodCalls() -> void | 0 | 11 | CopyValue | file://:0:0:0:0 | +| C::MethodCalls() -> void | 0 | 12 | FunctionAddress[InstanceMemberFunction] | ir.cpp:655:9:655:30 | +| C::MethodCalls() -> void | 0 | 13 | Constant[2] | ir.cpp:655:32:655:32 | +| C::MethodCalls() -> void | 0 | 14 | Invoke | ir.cpp:655:9:655:30 | +| C::MethodCalls() -> void | 0 | 15 | NoOp | ir.cpp:656:5:656:5 | +| C::MethodCalls() -> void | 0 | 16 | ReturnVoid | ir.cpp:652:10:652:20 | +| C::MethodCalls() -> void | 0 | 17 | UnmodeledUse | ir.cpp:652:10:652:20 | +| C::MethodCalls() -> void | 0 | 18 | ExitFunction | ir.cpp:652:10:652:20 | +| C::StaticMemberFunction(int) -> int | 0 | 0 | EnterFunction | ir.cpp:630:16:630:35 | +| C::StaticMemberFunction(int) -> int | 0 | 1 | UnmodeledDefinition | ir.cpp:630:16:630:35 | +| C::StaticMemberFunction(int) -> int | 0 | 2 | InitializeParameter[x] | ir.cpp:630:41:630:41 | +| C::StaticMemberFunction(int) -> int | 0 | 3 | VariableAddress[x] | ir.cpp:630:41:630:41 | +| C::StaticMemberFunction(int) -> int | 0 | 4 | Store | ir.cpp:630:41:630:41 | +| C::StaticMemberFunction(int) -> int | 0 | 5 | VariableAddress[#return] | ir.cpp:631:9:631:17 | +| C::StaticMemberFunction(int) -> int | 0 | 6 | VariableAddress[x] | ir.cpp:631:16:631:16 | +| C::StaticMemberFunction(int) -> int | 0 | 7 | Load | ir.cpp:631:16:631:16 | +| C::StaticMemberFunction(int) -> int | 0 | 8 | Store | ir.cpp:631:16:631:16 | +| C::StaticMemberFunction(int) -> int | 0 | 9 | VariableAddress[#return] | ir.cpp:630:16:630:35 | +| C::StaticMemberFunction(int) -> int | 0 | 10 | ReturnValue | ir.cpp:630:16:630:35 | +| C::StaticMemberFunction(int) -> int | 0 | 11 | UnmodeledUse | ir.cpp:630:16:630:35 | +| C::StaticMemberFunction(int) -> int | 0 | 12 | ExitFunction | ir.cpp:630:16:630:35 | +| C::VirtualMemberFunction(int) -> int | 0 | 0 | EnterFunction | ir.cpp:638:17:638:37 | +| C::VirtualMemberFunction(int) -> int | 0 | 1 | UnmodeledDefinition | ir.cpp:638:17:638:37 | +| C::VirtualMemberFunction(int) -> int | 0 | 2 | InitializeThis | ir.cpp:638:17:638:37 | +| C::VirtualMemberFunction(int) -> int | 0 | 3 | InitializeParameter[x] | ir.cpp:638:43:638:43 | +| C::VirtualMemberFunction(int) -> int | 0 | 4 | VariableAddress[x] | ir.cpp:638:43:638:43 | +| C::VirtualMemberFunction(int) -> int | 0 | 5 | Store | ir.cpp:638:43:638:43 | +| C::VirtualMemberFunction(int) -> int | 0 | 6 | VariableAddress[#return] | ir.cpp:639:9:639:17 | +| C::VirtualMemberFunction(int) -> int | 0 | 7 | VariableAddress[x] | ir.cpp:639:16:639:16 | +| C::VirtualMemberFunction(int) -> int | 0 | 8 | Load | ir.cpp:639:16:639:16 | +| C::VirtualMemberFunction(int) -> int | 0 | 9 | Store | ir.cpp:639:16:639:16 | +| C::VirtualMemberFunction(int) -> int | 0 | 10 | VariableAddress[#return] | ir.cpp:638:17:638:37 | +| C::VirtualMemberFunction(int) -> int | 0 | 11 | ReturnValue | ir.cpp:638:17:638:37 | +| C::VirtualMemberFunction(int) -> int | 0 | 12 | UnmodeledUse | ir.cpp:638:17:638:37 | +| C::VirtualMemberFunction(int) -> int | 0 | 13 | ExitFunction | ir.cpp:638:17:638:37 | +| Call() -> void | 0 | 0 | EnterFunction | ir.cpp:372:6:372:9 | +| Call() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:372:6:372:9 | +| Call() -> void | 0 | 2 | FunctionAddress[VoidFunc] | ir.cpp:373:5:373:12 | +| Call() -> void | 0 | 3 | Invoke | ir.cpp:373:5:373:12 | +| Call() -> void | 0 | 4 | NoOp | ir.cpp:374:1:374:1 | +| Call() -> void | 0 | 5 | ReturnVoid | ir.cpp:372:6:372:9 | +| Call() -> void | 0 | 6 | UnmodeledUse | ir.cpp:372:6:372:9 | +| Call() -> void | 0 | 7 | ExitFunction | ir.cpp:372:6:372:9 | +| CallAdd(int, int) -> int | 0 | 0 | EnterFunction | ir.cpp:376:5:376:11 | +| CallAdd(int, int) -> int | 0 | 1 | UnmodeledDefinition | ir.cpp:376:5:376:11 | +| CallAdd(int, int) -> int | 0 | 2 | InitializeParameter[x] | ir.cpp:376:17:376:17 | +| CallAdd(int, int) -> int | 0 | 3 | VariableAddress[x] | ir.cpp:376:17:376:17 | +| CallAdd(int, int) -> int | 0 | 4 | Store | ir.cpp:376:17:376:17 | +| CallAdd(int, int) -> int | 0 | 5 | InitializeParameter[y] | ir.cpp:376:24:376:24 | +| CallAdd(int, int) -> int | 0 | 6 | VariableAddress[y] | ir.cpp:376:24:376:24 | +| CallAdd(int, int) -> int | 0 | 7 | Store | ir.cpp:376:24:376:24 | +| CallAdd(int, int) -> int | 0 | 8 | VariableAddress[#return] | ir.cpp:377:5:377:21 | +| CallAdd(int, int) -> int | 0 | 9 | FunctionAddress[Add] | ir.cpp:377:12:377:14 | +| CallAdd(int, int) -> int | 0 | 10 | VariableAddress[x] | ir.cpp:377:16:377:16 | +| CallAdd(int, int) -> int | 0 | 11 | Load | ir.cpp:377:16:377:16 | +| CallAdd(int, int) -> int | 0 | 12 | VariableAddress[y] | ir.cpp:377:19:377:19 | +| CallAdd(int, int) -> int | 0 | 13 | Load | ir.cpp:377:19:377:19 | +| CallAdd(int, int) -> int | 0 | 14 | Invoke | ir.cpp:377:12:377:14 | +| CallAdd(int, int) -> int | 0 | 15 | Store | ir.cpp:377:12:377:14 | +| CallAdd(int, int) -> int | 0 | 16 | VariableAddress[#return] | ir.cpp:376:5:376:11 | +| CallAdd(int, int) -> int | 0 | 17 | ReturnValue | ir.cpp:376:5:376:11 | +| CallAdd(int, int) -> int | 0 | 18 | UnmodeledUse | ir.cpp:376:5:376:11 | +| CallAdd(int, int) -> int | 0 | 19 | ExitFunction | ir.cpp:376:5:376:11 | +| CallMethods(String &, String *, String) -> void | 0 | 0 | EnterFunction | ir.cpp:622:6:622:16 | +| CallMethods(String &, String *, String) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:622:6:622:16 | +| CallMethods(String &, String *, String) -> void | 0 | 2 | InitializeParameter[r] | ir.cpp:622:26:622:26 | +| CallMethods(String &, String *, String) -> void | 0 | 3 | VariableAddress[r] | ir.cpp:622:26:622:26 | +| CallMethods(String &, String *, String) -> void | 0 | 4 | Store | ir.cpp:622:26:622:26 | +| CallMethods(String &, String *, String) -> void | 0 | 5 | InitializeParameter[p] | ir.cpp:622:37:622:37 | +| CallMethods(String &, String *, String) -> void | 0 | 6 | VariableAddress[p] | ir.cpp:622:37:622:37 | +| CallMethods(String &, String *, String) -> void | 0 | 7 | Store | ir.cpp:622:37:622:37 | +| CallMethods(String &, String *, String) -> void | 0 | 8 | InitializeParameter[s] | ir.cpp:622:47:622:47 | +| CallMethods(String &, String *, String) -> void | 0 | 9 | VariableAddress[s] | ir.cpp:622:47:622:47 | +| CallMethods(String &, String *, String) -> void | 0 | 10 | Store | ir.cpp:622:47:622:47 | +| CallMethods(String &, String *, String) -> void | 0 | 11 | VariableAddress[r] | ir.cpp:623:5:623:5 | +| CallMethods(String &, String *, String) -> void | 0 | 12 | Load | ir.cpp:623:5:623:5 | +| CallMethods(String &, String *, String) -> void | 0 | 13 | Convert | ir.cpp:623:5:623:5 | +| CallMethods(String &, String *, String) -> void | 0 | 14 | FunctionAddress[c_str] | ir.cpp:623:7:623:11 | +| CallMethods(String &, String *, String) -> void | 0 | 15 | Invoke | ir.cpp:623:7:623:11 | +| CallMethods(String &, String *, String) -> void | 0 | 16 | VariableAddress[p] | ir.cpp:624:5:624:5 | +| CallMethods(String &, String *, String) -> void | 0 | 17 | Load | ir.cpp:624:5:624:5 | +| CallMethods(String &, String *, String) -> void | 0 | 18 | Convert | ir.cpp:624:5:624:5 | +| CallMethods(String &, String *, String) -> void | 0 | 19 | FunctionAddress[c_str] | ir.cpp:624:8:624:12 | +| CallMethods(String &, String *, String) -> void | 0 | 20 | Invoke | ir.cpp:624:8:624:12 | +| CallMethods(String &, String *, String) -> void | 0 | 21 | VariableAddress[s] | ir.cpp:625:5:625:5 | +| CallMethods(String &, String *, String) -> void | 0 | 22 | Convert | ir.cpp:625:5:625:5 | +| CallMethods(String &, String *, String) -> void | 0 | 23 | FunctionAddress[c_str] | ir.cpp:625:7:625:11 | +| CallMethods(String &, String *, String) -> void | 0 | 24 | Invoke | ir.cpp:625:7:625:11 | +| CallMethods(String &, String *, String) -> void | 0 | 25 | NoOp | ir.cpp:626:1:626:1 | +| CallMethods(String &, String *, String) -> void | 0 | 26 | ReturnVoid | ir.cpp:622:6:622:16 | +| CallMethods(String &, String *, String) -> void | 0 | 27 | UnmodeledUse | ir.cpp:622:6:622:16 | +| CallMethods(String &, String *, String) -> void | 0 | 28 | ExitFunction | ir.cpp:622:6:622:16 | +| CallMin(int, int) -> int | 0 | 0 | EnterFunction | ir.cpp:708:5:708:11 | +| CallMin(int, int) -> int | 0 | 1 | UnmodeledDefinition | ir.cpp:708:5:708:11 | +| CallMin(int, int) -> int | 0 | 2 | InitializeParameter[x] | ir.cpp:708:17:708:17 | +| CallMin(int, int) -> int | 0 | 3 | VariableAddress[x] | ir.cpp:708:17:708:17 | +| CallMin(int, int) -> int | 0 | 4 | Store | ir.cpp:708:17:708:17 | +| CallMin(int, int) -> int | 0 | 5 | InitializeParameter[y] | ir.cpp:708:24:708:24 | +| CallMin(int, int) -> int | 0 | 6 | VariableAddress[y] | ir.cpp:708:24:708:24 | +| CallMin(int, int) -> int | 0 | 7 | Store | ir.cpp:708:24:708:24 | +| CallMin(int, int) -> int | 0 | 8 | VariableAddress[#return] | ir.cpp:709:3:709:19 | +| CallMin(int, int) -> int | 0 | 9 | FunctionAddress[min] | ir.cpp:709:10:709:12 | +| CallMin(int, int) -> int | 0 | 10 | VariableAddress[x] | ir.cpp:709:14:709:14 | +| CallMin(int, int) -> int | 0 | 11 | Load | ir.cpp:709:14:709:14 | +| CallMin(int, int) -> int | 0 | 12 | VariableAddress[y] | ir.cpp:709:17:709:17 | +| CallMin(int, int) -> int | 0 | 13 | Load | ir.cpp:709:17:709:17 | +| CallMin(int, int) -> int | 0 | 14 | Invoke | ir.cpp:709:10:709:12 | +| CallMin(int, int) -> int | 0 | 15 | Store | ir.cpp:709:10:709:12 | +| CallMin(int, int) -> int | 0 | 16 | VariableAddress[#return] | ir.cpp:708:5:708:11 | +| CallMin(int, int) -> int | 0 | 17 | ReturnValue | ir.cpp:708:5:708:11 | +| CallMin(int, int) -> int | 0 | 18 | UnmodeledUse | ir.cpp:708:5:708:11 | +| CallMin(int, int) -> int | 0 | 19 | ExitFunction | ir.cpp:708:5:708:11 | +| CallNestedTemplateFunc() -> double | 0 | 0 | EnterFunction | ir.cpp:720:8:720:29 | +| CallNestedTemplateFunc() -> double | 0 | 1 | UnmodeledDefinition | ir.cpp:720:8:720:29 | +| CallNestedTemplateFunc() -> double | 0 | 2 | VariableAddress[#return] | ir.cpp:721:3:721:54 | +| CallNestedTemplateFunc() -> double | 0 | 3 | FunctionAddress[Func] | ir.cpp:721:10:721:39 | +| CallNestedTemplateFunc() -> double | 0 | 4 | Constant[0] | ir.cpp:721:41:721:47 | +| CallNestedTemplateFunc() -> double | 0 | 5 | Constant[111] | ir.cpp:721:50:721:52 | +| CallNestedTemplateFunc() -> double | 0 | 6 | Invoke | ir.cpp:721:10:721:39 | +| CallNestedTemplateFunc() -> double | 0 | 7 | Convert | ir.cpp:721:10:721:53 | +| CallNestedTemplateFunc() -> double | 0 | 8 | Store | ir.cpp:721:10:721:53 | +| CallNestedTemplateFunc() -> double | 0 | 9 | VariableAddress[#return] | ir.cpp:720:8:720:29 | +| CallNestedTemplateFunc() -> double | 0 | 10 | ReturnValue | ir.cpp:720:8:720:29 | +| CallNestedTemplateFunc() -> double | 0 | 11 | UnmodeledUse | ir.cpp:720:8:720:29 | +| CallNestedTemplateFunc() -> double | 0 | 12 | ExitFunction | ir.cpp:720:8:720:29 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 0 | EnterFunction | ir.cpp:551:5:551:18 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 1 | UnmodeledDefinition | ir.cpp:551:5:551:18 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 2 | InitializeParameter[pfn] | ir.cpp:551:26:551:28 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 3 | VariableAddress[pfn] | ir.cpp:551:26:551:28 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 4 | Store | ir.cpp:551:26:551:28 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 5 | VariableAddress[#return] | ir.cpp:552:5:552:18 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 6 | VariableAddress[pfn] | ir.cpp:552:12:552:14 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 7 | Load | ir.cpp:552:12:552:14 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 8 | Constant[5] | ir.cpp:552:16:552:16 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 9 | Invoke | ir.cpp:552:12:552:17 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 10 | Store | ir.cpp:552:12:552:17 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 11 | VariableAddress[#return] | ir.cpp:551:5:551:18 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 12 | ReturnValue | ir.cpp:551:5:551:18 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 13 | UnmodeledUse | ir.cpp:551:5:551:18 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 14 | ExitFunction | ir.cpp:551:5:551:18 | +| Comma(int, int) -> int | 0 | 0 | EnterFunction | ir.cpp:380:5:380:9 | +| Comma(int, int) -> int | 0 | 1 | UnmodeledDefinition | ir.cpp:380:5:380:9 | +| Comma(int, int) -> int | 0 | 2 | InitializeParameter[x] | ir.cpp:380:15:380:15 | +| Comma(int, int) -> int | 0 | 3 | VariableAddress[x] | ir.cpp:380:15:380:15 | +| Comma(int, int) -> int | 0 | 4 | Store | ir.cpp:380:15:380:15 | +| Comma(int, int) -> int | 0 | 5 | InitializeParameter[y] | ir.cpp:380:22:380:22 | +| Comma(int, int) -> int | 0 | 6 | VariableAddress[y] | ir.cpp:380:22:380:22 | +| Comma(int, int) -> int | 0 | 7 | Store | ir.cpp:380:22:380:22 | +| Comma(int, int) -> int | 0 | 8 | VariableAddress[#return] | ir.cpp:381:5:381:37 | +| Comma(int, int) -> int | 0 | 9 | FunctionAddress[VoidFunc] | ir.cpp:381:12:381:19 | +| Comma(int, int) -> int | 0 | 10 | Invoke | ir.cpp:381:12:381:19 | +| Comma(int, int) -> int | 0 | 11 | FunctionAddress[CallAdd] | ir.cpp:381:24:381:30 | +| Comma(int, int) -> int | 0 | 12 | VariableAddress[x] | ir.cpp:381:32:381:32 | +| Comma(int, int) -> int | 0 | 13 | Load | ir.cpp:381:32:381:32 | +| Comma(int, int) -> int | 0 | 14 | VariableAddress[y] | ir.cpp:381:35:381:35 | +| Comma(int, int) -> int | 0 | 15 | Load | ir.cpp:381:35:381:35 | +| Comma(int, int) -> int | 0 | 16 | Invoke | ir.cpp:381:24:381:30 | +| Comma(int, int) -> int | 0 | 17 | Store | ir.cpp:381:12:381:36 | +| Comma(int, int) -> int | 0 | 18 | VariableAddress[#return] | ir.cpp:380:5:380:9 | +| Comma(int, int) -> int | 0 | 19 | ReturnValue | ir.cpp:380:5:380:9 | +| Comma(int, int) -> int | 0 | 20 | UnmodeledUse | ir.cpp:380:5:380:9 | +| Comma(int, int) -> int | 0 | 21 | ExitFunction | ir.cpp:380:5:380:9 | +| CompoundAssignment() -> void | 0 | 0 | EnterFunction | ir.cpp:213:6:213:23 | +| CompoundAssignment() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:213:6:213:23 | +| CompoundAssignment() -> void | 0 | 2 | VariableAddress[x] | ir.cpp:215:9:215:9 | +| CompoundAssignment() -> void | 0 | 3 | Constant[5] | ir.cpp:215:12:215:13 | +| CompoundAssignment() -> void | 0 | 4 | Store | ir.cpp:215:12:215:13 | +| CompoundAssignment() -> void | 0 | 5 | Constant[7] | ir.cpp:216:10:216:10 | +| CompoundAssignment() -> void | 0 | 6 | VariableAddress[x] | ir.cpp:216:5:216:5 | +| CompoundAssignment() -> void | 0 | 7 | Load | ir.cpp:216:5:216:10 | +| CompoundAssignment() -> void | 0 | 8 | Add | ir.cpp:216:5:216:10 | +| CompoundAssignment() -> void | 0 | 9 | Store | ir.cpp:216:5:216:10 | +| CompoundAssignment() -> void | 0 | 10 | VariableAddress[y] | ir.cpp:219:11:219:11 | +| CompoundAssignment() -> void | 0 | 11 | Constant[5] | ir.cpp:219:15:219:15 | +| CompoundAssignment() -> void | 0 | 12 | Store | ir.cpp:219:15:219:15 | +| CompoundAssignment() -> void | 0 | 13 | VariableAddress[x] | ir.cpp:220:10:220:10 | +| CompoundAssignment() -> void | 0 | 14 | Load | ir.cpp:220:10:220:10 | +| CompoundAssignment() -> void | 0 | 15 | VariableAddress[y] | ir.cpp:220:5:220:5 | +| CompoundAssignment() -> void | 0 | 16 | Load | ir.cpp:220:5:220:10 | +| CompoundAssignment() -> void | 0 | 17 | Convert | ir.cpp:220:5:220:10 | +| CompoundAssignment() -> void | 0 | 18 | Add | ir.cpp:220:5:220:10 | +| CompoundAssignment() -> void | 0 | 19 | Convert | ir.cpp:220:5:220:10 | +| CompoundAssignment() -> void | 0 | 20 | Store | ir.cpp:220:5:220:10 | +| CompoundAssignment() -> void | 0 | 21 | Constant[1] | ir.cpp:223:11:223:11 | +| CompoundAssignment() -> void | 0 | 22 | VariableAddress[y] | ir.cpp:223:5:223:5 | +| CompoundAssignment() -> void | 0 | 23 | Load | ir.cpp:223:5:223:11 | +| CompoundAssignment() -> void | 0 | 24 | ShiftLeft | ir.cpp:223:5:223:11 | +| CompoundAssignment() -> void | 0 | 25 | Store | ir.cpp:223:5:223:11 | +| CompoundAssignment() -> void | 0 | 26 | VariableAddress[z] | ir.cpp:226:10:226:10 | +| CompoundAssignment() -> void | 0 | 27 | Constant[7] | ir.cpp:226:14:226:14 | +| CompoundAssignment() -> void | 0 | 28 | Store | ir.cpp:226:14:226:14 | +| CompoundAssignment() -> void | 0 | 29 | Constant[2.0] | ir.cpp:227:10:227:13 | +| CompoundAssignment() -> void | 0 | 30 | VariableAddress[z] | ir.cpp:227:5:227:5 | +| CompoundAssignment() -> void | 0 | 31 | Load | ir.cpp:227:5:227:13 | +| CompoundAssignment() -> void | 0 | 32 | Convert | ir.cpp:227:5:227:13 | +| CompoundAssignment() -> void | 0 | 33 | Add | ir.cpp:227:5:227:13 | +| CompoundAssignment() -> void | 0 | 34 | Convert | ir.cpp:227:5:227:13 | +| CompoundAssignment() -> void | 0 | 35 | Store | ir.cpp:227:5:227:13 | +| CompoundAssignment() -> void | 0 | 36 | NoOp | ir.cpp:228:1:228:1 | +| CompoundAssignment() -> void | 0 | 37 | ReturnVoid | ir.cpp:213:6:213:23 | +| CompoundAssignment() -> void | 0 | 38 | UnmodeledUse | ir.cpp:213:6:213:23 | +| CompoundAssignment() -> void | 0 | 39 | ExitFunction | ir.cpp:213:6:213:23 | +| ConditionValues(bool, bool) -> void | 0 | 0 | EnterFunction | ir.cpp:475:6:475:20 | +| ConditionValues(bool, bool) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:475:6:475:20 | +| ConditionValues(bool, bool) -> void | 0 | 2 | InitializeParameter[a] | ir.cpp:475:27:475:27 | +| ConditionValues(bool, bool) -> void | 0 | 3 | VariableAddress[a] | ir.cpp:475:27:475:27 | +| ConditionValues(bool, bool) -> void | 0 | 4 | Store | ir.cpp:475:27:475:27 | +| ConditionValues(bool, bool) -> void | 0 | 5 | InitializeParameter[b] | ir.cpp:475:35:475:35 | +| ConditionValues(bool, bool) -> void | 0 | 6 | VariableAddress[b] | ir.cpp:475:35:475:35 | +| ConditionValues(bool, bool) -> void | 0 | 7 | Store | ir.cpp:475:35:475:35 | +| ConditionValues(bool, bool) -> void | 0 | 8 | VariableAddress[x] | ir.cpp:476:10:476:10 | +| ConditionValues(bool, bool) -> void | 0 | 9 | Uninitialized | ir.cpp:476:10:476:10 | +| ConditionValues(bool, bool) -> void | 0 | 10 | Store | ir.cpp:476:10:476:10 | +| ConditionValues(bool, bool) -> void | 0 | 11 | VariableAddress[a] | ir.cpp:477:9:477:9 | +| ConditionValues(bool, bool) -> void | 0 | 12 | Load | ir.cpp:477:9:477:9 | +| ConditionValues(bool, bool) -> void | 0 | 13 | ConditionalBranch | ir.cpp:477:9:477:9 | +| ConditionValues(bool, bool) -> void | 1 | 0 | VariableAddress[b] | ir.cpp:477:14:477:14 | +| ConditionValues(bool, bool) -> void | 1 | 1 | Load | ir.cpp:477:14:477:14 | +| ConditionValues(bool, bool) -> void | 1 | 2 | ConditionalBranch | ir.cpp:477:14:477:14 | +| ConditionValues(bool, bool) -> void | 2 | 0 | VariableAddress[#temp478:9] | ir.cpp:478:9:478:14 | +| ConditionValues(bool, bool) -> void | 2 | 1 | Constant[0] | ir.cpp:478:9:478:14 | +| ConditionValues(bool, bool) -> void | 2 | 2 | Store | ir.cpp:478:9:478:14 | +| ConditionValues(bool, bool) -> void | 3 | 0 | Phi | ir.cpp:478:9:478:14 | +| ConditionValues(bool, bool) -> void | 3 | 1 | VariableAddress[#temp478:9] | ir.cpp:478:9:478:14 | +| ConditionValues(bool, bool) -> void | 3 | 2 | Load | ir.cpp:478:9:478:14 | +| ConditionValues(bool, bool) -> void | 3 | 3 | VariableAddress[x] | ir.cpp:478:5:478:5 | +| ConditionValues(bool, bool) -> void | 3 | 4 | Store | ir.cpp:478:5:478:14 | +| ConditionValues(bool, bool) -> void | 3 | 5 | VariableAddress[a] | ir.cpp:479:11:479:11 | +| ConditionValues(bool, bool) -> void | 3 | 6 | Load | ir.cpp:479:11:479:11 | +| ConditionValues(bool, bool) -> void | 3 | 7 | ConditionalBranch | ir.cpp:479:11:479:11 | +| ConditionValues(bool, bool) -> void | 4 | 0 | VariableAddress[#temp478:9] | ir.cpp:478:9:478:14 | +| ConditionValues(bool, bool) -> void | 4 | 1 | Constant[1] | ir.cpp:478:9:478:14 | +| ConditionValues(bool, bool) -> void | 4 | 2 | Store | ir.cpp:478:9:478:14 | +| ConditionValues(bool, bool) -> void | 5 | 0 | VariableAddress[b] | ir.cpp:478:14:478:14 | +| ConditionValues(bool, bool) -> void | 5 | 1 | Load | ir.cpp:478:14:478:14 | +| ConditionValues(bool, bool) -> void | 5 | 2 | ConditionalBranch | ir.cpp:478:14:478:14 | +| ConditionValues(bool, bool) -> void | 6 | 0 | VariableAddress[#temp479:11] | ir.cpp:479:11:479:16 | +| ConditionValues(bool, bool) -> void | 6 | 1 | Constant[0] | ir.cpp:479:11:479:16 | +| ConditionValues(bool, bool) -> void | 6 | 2 | Store | ir.cpp:479:11:479:16 | +| ConditionValues(bool, bool) -> void | 7 | 0 | Phi | ir.cpp:479:11:479:16 | +| ConditionValues(bool, bool) -> void | 7 | 1 | VariableAddress[#temp479:11] | ir.cpp:479:11:479:16 | +| ConditionValues(bool, bool) -> void | 7 | 2 | Load | ir.cpp:479:11:479:16 | +| ConditionValues(bool, bool) -> void | 7 | 3 | LogicalNot | ir.cpp:479:9:479:17 | +| ConditionValues(bool, bool) -> void | 7 | 4 | VariableAddress[x] | ir.cpp:479:5:479:5 | +| ConditionValues(bool, bool) -> void | 7 | 5 | Store | ir.cpp:479:5:479:17 | +| ConditionValues(bool, bool) -> void | 7 | 6 | NoOp | ir.cpp:480:1:480:1 | +| ConditionValues(bool, bool) -> void | 7 | 7 | ReturnVoid | ir.cpp:475:6:475:20 | +| ConditionValues(bool, bool) -> void | 7 | 8 | UnmodeledUse | ir.cpp:475:6:475:20 | +| ConditionValues(bool, bool) -> void | 7 | 9 | ExitFunction | ir.cpp:475:6:475:20 | +| ConditionValues(bool, bool) -> void | 8 | 0 | VariableAddress[#temp479:11] | ir.cpp:479:11:479:16 | +| ConditionValues(bool, bool) -> void | 8 | 1 | Constant[1] | ir.cpp:479:11:479:16 | +| ConditionValues(bool, bool) -> void | 8 | 2 | Store | ir.cpp:479:11:479:16 | +| ConditionValues(bool, bool) -> void | 9 | 0 | VariableAddress[b] | ir.cpp:479:16:479:16 | +| ConditionValues(bool, bool) -> void | 9 | 1 | Load | ir.cpp:479:16:479:16 | +| ConditionValues(bool, bool) -> void | 9 | 2 | ConditionalBranch | ir.cpp:479:16:479:16 | +| ConditionValues(bool, bool) -> void | 10 | 0 | VariableAddress[#temp477:9] | ir.cpp:477:9:477:14 | +| ConditionValues(bool, bool) -> void | 10 | 1 | Constant[0] | ir.cpp:477:9:477:14 | +| ConditionValues(bool, bool) -> void | 10 | 2 | Store | ir.cpp:477:9:477:14 | +| ConditionValues(bool, bool) -> void | 11 | 0 | Phi | ir.cpp:477:9:477:14 | +| ConditionValues(bool, bool) -> void | 11 | 1 | VariableAddress[#temp477:9] | ir.cpp:477:9:477:14 | +| ConditionValues(bool, bool) -> void | 11 | 2 | Load | ir.cpp:477:9:477:14 | +| ConditionValues(bool, bool) -> void | 11 | 3 | VariableAddress[x] | ir.cpp:477:5:477:5 | +| ConditionValues(bool, bool) -> void | 11 | 4 | Store | ir.cpp:477:5:477:14 | +| ConditionValues(bool, bool) -> void | 11 | 5 | VariableAddress[a] | ir.cpp:478:9:478:9 | +| ConditionValues(bool, bool) -> void | 11 | 6 | Load | ir.cpp:478:9:478:9 | +| ConditionValues(bool, bool) -> void | 11 | 7 | ConditionalBranch | ir.cpp:478:9:478:9 | +| ConditionValues(bool, bool) -> void | 12 | 0 | VariableAddress[#temp477:9] | ir.cpp:477:9:477:14 | +| ConditionValues(bool, bool) -> void | 12 | 1 | Constant[1] | ir.cpp:477:9:477:14 | +| ConditionValues(bool, bool) -> void | 12 | 2 | Store | ir.cpp:477:9:477:14 | +| Conditional(bool, int, int) -> void | 0 | 0 | EnterFunction | ir.cpp:482:6:482:16 | +| Conditional(bool, int, int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:482:6:482:16 | +| Conditional(bool, int, int) -> void | 0 | 2 | InitializeParameter[a] | ir.cpp:482:23:482:23 | +| Conditional(bool, int, int) -> void | 0 | 3 | VariableAddress[a] | ir.cpp:482:23:482:23 | +| Conditional(bool, int, int) -> void | 0 | 4 | Store | ir.cpp:482:23:482:23 | +| Conditional(bool, int, int) -> void | 0 | 5 | InitializeParameter[x] | ir.cpp:482:30:482:30 | +| Conditional(bool, int, int) -> void | 0 | 6 | VariableAddress[x] | ir.cpp:482:30:482:30 | +| Conditional(bool, int, int) -> void | 0 | 7 | Store | ir.cpp:482:30:482:30 | +| Conditional(bool, int, int) -> void | 0 | 8 | InitializeParameter[y] | ir.cpp:482:37:482:37 | +| Conditional(bool, int, int) -> void | 0 | 9 | VariableAddress[y] | ir.cpp:482:37:482:37 | +| Conditional(bool, int, int) -> void | 0 | 10 | Store | ir.cpp:482:37:482:37 | +| Conditional(bool, int, int) -> void | 0 | 11 | VariableAddress[z] | ir.cpp:483:9:483:9 | +| Conditional(bool, int, int) -> void | 0 | 12 | VariableAddress[a] | ir.cpp:483:13:483:13 | +| Conditional(bool, int, int) -> void | 0 | 13 | Load | ir.cpp:483:13:483:13 | +| Conditional(bool, int, int) -> void | 0 | 14 | ConditionalBranch | ir.cpp:483:13:483:13 | +| Conditional(bool, int, int) -> void | 1 | 0 | VariableAddress[x] | ir.cpp:483:17:483:17 | +| Conditional(bool, int, int) -> void | 1 | 1 | Load | ir.cpp:483:17:483:17 | +| Conditional(bool, int, int) -> void | 1 | 2 | VariableAddress[#temp483:13] | ir.cpp:483:13:483:21 | +| Conditional(bool, int, int) -> void | 1 | 3 | Store | ir.cpp:483:13:483:21 | +| Conditional(bool, int, int) -> void | 2 | 0 | VariableAddress[y] | ir.cpp:483:21:483:21 | +| Conditional(bool, int, int) -> void | 2 | 1 | Load | ir.cpp:483:21:483:21 | +| Conditional(bool, int, int) -> void | 2 | 2 | VariableAddress[#temp483:13] | ir.cpp:483:13:483:21 | +| Conditional(bool, int, int) -> void | 2 | 3 | Store | ir.cpp:483:13:483:21 | +| Conditional(bool, int, int) -> void | 3 | 0 | Phi | ir.cpp:483:13:483:21 | +| Conditional(bool, int, int) -> void | 3 | 1 | VariableAddress[#temp483:13] | ir.cpp:483:13:483:21 | +| Conditional(bool, int, int) -> void | 3 | 2 | Load | ir.cpp:483:13:483:21 | +| Conditional(bool, int, int) -> void | 3 | 3 | Store | ir.cpp:483:13:483:21 | +| Conditional(bool, int, int) -> void | 3 | 4 | NoOp | ir.cpp:484:1:484:1 | +| Conditional(bool, int, int) -> void | 3 | 5 | ReturnVoid | ir.cpp:482:6:482:16 | +| Conditional(bool, int, int) -> void | 3 | 6 | UnmodeledUse | ir.cpp:482:6:482:16 | +| Conditional(bool, int, int) -> void | 3 | 7 | ExitFunction | ir.cpp:482:6:482:16 | +| Conditional_LValue(bool) -> void | 0 | 0 | EnterFunction | ir.cpp:486:6:486:23 | +| Conditional_LValue(bool) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:486:6:486:23 | +| Conditional_LValue(bool) -> void | 0 | 2 | InitializeParameter[a] | ir.cpp:486:30:486:30 | +| Conditional_LValue(bool) -> void | 0 | 3 | VariableAddress[a] | ir.cpp:486:30:486:30 | +| Conditional_LValue(bool) -> void | 0 | 4 | Store | ir.cpp:486:30:486:30 | +| Conditional_LValue(bool) -> void | 0 | 5 | VariableAddress[x] | ir.cpp:487:9:487:9 | +| Conditional_LValue(bool) -> void | 0 | 6 | Uninitialized | ir.cpp:487:9:487:9 | +| Conditional_LValue(bool) -> void | 0 | 7 | Store | ir.cpp:487:9:487:9 | +| Conditional_LValue(bool) -> void | 0 | 8 | VariableAddress[y] | ir.cpp:488:9:488:9 | +| Conditional_LValue(bool) -> void | 0 | 9 | Uninitialized | ir.cpp:488:9:488:9 | +| Conditional_LValue(bool) -> void | 0 | 10 | Store | ir.cpp:488:9:488:9 | +| Conditional_LValue(bool) -> void | 0 | 11 | Constant[5] | ir.cpp:489:19:489:19 | +| Conditional_LValue(bool) -> void | 0 | 12 | VariableAddress[a] | ir.cpp:489:6:489:6 | +| Conditional_LValue(bool) -> void | 0 | 13 | Load | ir.cpp:489:6:489:6 | +| Conditional_LValue(bool) -> void | 0 | 14 | ConditionalBranch | ir.cpp:489:6:489:6 | +| Conditional_LValue(bool) -> void | 1 | 0 | Phi | ir.cpp:489:6:489:14 | +| Conditional_LValue(bool) -> void | 1 | 1 | VariableAddress[#temp489:6] | ir.cpp:489:6:489:14 | +| Conditional_LValue(bool) -> void | 1 | 2 | Load | ir.cpp:489:6:489:14 | +| Conditional_LValue(bool) -> void | 1 | 3 | Store | ir.cpp:489:5:489:19 | +| Conditional_LValue(bool) -> void | 1 | 4 | NoOp | ir.cpp:490:1:490:1 | +| Conditional_LValue(bool) -> void | 1 | 5 | ReturnVoid | ir.cpp:486:6:486:23 | +| Conditional_LValue(bool) -> void | 1 | 6 | UnmodeledUse | ir.cpp:486:6:486:23 | +| Conditional_LValue(bool) -> void | 1 | 7 | ExitFunction | ir.cpp:486:6:486:23 | +| Conditional_LValue(bool) -> void | 2 | 0 | VariableAddress[x] | ir.cpp:489:10:489:10 | +| Conditional_LValue(bool) -> void | 2 | 1 | VariableAddress[#temp489:6] | ir.cpp:489:6:489:14 | +| Conditional_LValue(bool) -> void | 2 | 2 | Store | ir.cpp:489:6:489:14 | +| Conditional_LValue(bool) -> void | 3 | 0 | VariableAddress[y] | ir.cpp:489:14:489:14 | +| Conditional_LValue(bool) -> void | 3 | 1 | VariableAddress[#temp489:6] | ir.cpp:489:6:489:14 | +| Conditional_LValue(bool) -> void | 3 | 2 | Store | ir.cpp:489:6:489:14 | +| Conditional_Void(bool) -> void | 0 | 0 | EnterFunction | ir.cpp:492:6:492:21 | +| Conditional_Void(bool) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:492:6:492:21 | +| Conditional_Void(bool) -> void | 0 | 2 | InitializeParameter[a] | ir.cpp:492:28:492:28 | +| Conditional_Void(bool) -> void | 0 | 3 | VariableAddress[a] | ir.cpp:492:28:492:28 | +| Conditional_Void(bool) -> void | 0 | 4 | Store | ir.cpp:492:28:492:28 | +| Conditional_Void(bool) -> void | 0 | 5 | VariableAddress[a] | ir.cpp:493:5:493:5 | +| Conditional_Void(bool) -> void | 0 | 6 | Load | ir.cpp:493:5:493:5 | +| Conditional_Void(bool) -> void | 0 | 7 | ConditionalBranch | ir.cpp:493:5:493:5 | +| Conditional_Void(bool) -> void | 1 | 0 | NoOp | ir.cpp:494:1:494:1 | +| Conditional_Void(bool) -> void | 1 | 1 | ReturnVoid | ir.cpp:492:6:492:21 | +| Conditional_Void(bool) -> void | 1 | 2 | UnmodeledUse | ir.cpp:492:6:492:21 | +| Conditional_Void(bool) -> void | 1 | 3 | ExitFunction | ir.cpp:492:6:492:21 | +| Conditional_Void(bool) -> void | 2 | 0 | FunctionAddress[VoidFunc] | ir.cpp:493:9:493:16 | +| Conditional_Void(bool) -> void | 2 | 1 | Invoke | ir.cpp:493:9:493:16 | +| Conditional_Void(bool) -> void | 3 | 0 | FunctionAddress[VoidFunc] | ir.cpp:493:22:493:29 | +| Conditional_Void(bool) -> void | 3 | 1 | Invoke | ir.cpp:493:22:493:29 | +| Constants() -> void | 0 | 0 | EnterFunction | ir.cpp:1:6:1:14 | +| Constants() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:1:6:1:14 | +| Constants() -> void | 0 | 2 | VariableAddress[c_i] | ir.cpp:2:10:2:12 | +| Constants() -> void | 0 | 3 | Constant[1] | ir.cpp:2:16:2:16 | +| Constants() -> void | 0 | 4 | Store | ir.cpp:2:16:2:16 | +| Constants() -> void | 0 | 5 | VariableAddress[c_c] | ir.cpp:3:10:3:12 | +| Constants() -> void | 0 | 6 | Constant[65] | ir.cpp:3:15:3:18 | +| Constants() -> void | 0 | 7 | Store | ir.cpp:3:15:3:18 | +| Constants() -> void | 0 | 8 | VariableAddress[sc_i] | ir.cpp:5:17:5:20 | +| Constants() -> void | 0 | 9 | Constant[-1] | ir.cpp:5:24:5:25 | +| Constants() -> void | 0 | 10 | Store | ir.cpp:5:24:5:25 | +| Constants() -> void | 0 | 11 | VariableAddress[sc_c] | ir.cpp:6:17:6:20 | +| Constants() -> void | 0 | 12 | Constant[65] | ir.cpp:6:24:6:26 | +| Constants() -> void | 0 | 13 | Store | ir.cpp:6:24:6:26 | +| Constants() -> void | 0 | 14 | VariableAddress[uc_i] | ir.cpp:8:19:8:22 | +| Constants() -> void | 0 | 15 | Constant[5] | ir.cpp:8:26:8:26 | +| Constants() -> void | 0 | 16 | Store | ir.cpp:8:26:8:26 | +| Constants() -> void | 0 | 17 | VariableAddress[uc_c] | ir.cpp:9:19:9:22 | +| Constants() -> void | 0 | 18 | Constant[65] | ir.cpp:9:26:9:28 | +| Constants() -> void | 0 | 19 | Store | ir.cpp:9:26:9:28 | +| Constants() -> void | 0 | 20 | VariableAddress[s] | ir.cpp:11:11:11:11 | +| Constants() -> void | 0 | 21 | Constant[5] | ir.cpp:11:15:11:15 | +| Constants() -> void | 0 | 22 | Store | ir.cpp:11:15:11:15 | +| Constants() -> void | 0 | 23 | VariableAddress[us] | ir.cpp:12:20:12:21 | +| Constants() -> void | 0 | 24 | Constant[5] | ir.cpp:12:25:12:25 | +| Constants() -> void | 0 | 25 | Store | ir.cpp:12:25:12:25 | +| Constants() -> void | 0 | 26 | VariableAddress[i] | ir.cpp:14:9:14:9 | +| Constants() -> void | 0 | 27 | Constant[5] | ir.cpp:14:12:14:13 | +| Constants() -> void | 0 | 28 | Store | ir.cpp:14:12:14:13 | +| Constants() -> void | 0 | 29 | VariableAddress[ui] | ir.cpp:15:18:15:19 | +| Constants() -> void | 0 | 30 | Constant[5] | ir.cpp:15:23:15:23 | +| Constants() -> void | 0 | 31 | Store | ir.cpp:15:23:15:23 | +| Constants() -> void | 0 | 32 | VariableAddress[l] | ir.cpp:17:10:17:10 | +| Constants() -> void | 0 | 33 | Constant[5] | ir.cpp:17:14:17:14 | +| Constants() -> void | 0 | 34 | Store | ir.cpp:17:14:17:14 | +| Constants() -> void | 0 | 35 | VariableAddress[ul] | ir.cpp:18:19:18:20 | +| Constants() -> void | 0 | 36 | Constant[5] | ir.cpp:18:24:18:24 | +| Constants() -> void | 0 | 37 | Store | ir.cpp:18:24:18:24 | +| Constants() -> void | 0 | 38 | VariableAddress[ll_i] | ir.cpp:20:15:20:18 | +| Constants() -> void | 0 | 39 | Constant[5] | ir.cpp:20:22:20:22 | +| Constants() -> void | 0 | 40 | Store | ir.cpp:20:22:20:22 | +| Constants() -> void | 0 | 41 | VariableAddress[ll_ll] | ir.cpp:21:15:21:19 | +| Constants() -> void | 0 | 42 | Constant[5] | ir.cpp:21:22:21:25 | +| Constants() -> void | 0 | 43 | Store | ir.cpp:21:22:21:25 | +| Constants() -> void | 0 | 44 | VariableAddress[ull_i] | ir.cpp:22:24:22:28 | +| Constants() -> void | 0 | 45 | Constant[5] | ir.cpp:22:32:22:32 | +| Constants() -> void | 0 | 46 | Store | ir.cpp:22:32:22:32 | +| Constants() -> void | 0 | 47 | VariableAddress[ull_ull] | ir.cpp:23:24:23:30 | +| Constants() -> void | 0 | 48 | Constant[5] | ir.cpp:23:33:23:37 | +| Constants() -> void | 0 | 49 | Store | ir.cpp:23:33:23:37 | +| Constants() -> void | 0 | 50 | VariableAddress[b_t] | ir.cpp:25:10:25:12 | +| Constants() -> void | 0 | 51 | Constant[1] | ir.cpp:25:15:25:19 | +| Constants() -> void | 0 | 52 | Store | ir.cpp:25:15:25:19 | +| Constants() -> void | 0 | 53 | VariableAddress[b_f] | ir.cpp:26:10:26:12 | +| Constants() -> void | 0 | 54 | Constant[0] | ir.cpp:26:15:26:20 | +| Constants() -> void | 0 | 55 | Store | ir.cpp:26:15:26:20 | +| Constants() -> void | 0 | 56 | VariableAddress[wc_i] | ir.cpp:28:13:28:16 | +| Constants() -> void | 0 | 57 | Constant[5] | ir.cpp:28:20:28:20 | +| Constants() -> void | 0 | 58 | Store | ir.cpp:28:20:28:20 | +| Constants() -> void | 0 | 59 | VariableAddress[wc_c] | ir.cpp:29:13:29:16 | +| Constants() -> void | 0 | 60 | Constant[65] | ir.cpp:29:19:29:23 | +| Constants() -> void | 0 | 61 | Store | ir.cpp:29:19:29:23 | +| Constants() -> void | 0 | 62 | VariableAddress[c16] | ir.cpp:31:14:31:16 | +| Constants() -> void | 0 | 63 | Constant[65] | ir.cpp:31:19:31:23 | +| Constants() -> void | 0 | 64 | Store | ir.cpp:31:19:31:23 | +| Constants() -> void | 0 | 65 | VariableAddress[c32] | ir.cpp:32:14:32:16 | +| Constants() -> void | 0 | 66 | Constant[65] | ir.cpp:32:19:32:23 | +| Constants() -> void | 0 | 67 | Store | ir.cpp:32:19:32:23 | +| Constants() -> void | 0 | 68 | VariableAddress[f_i] | ir.cpp:34:11:34:13 | +| Constants() -> void | 0 | 69 | Constant[1.0] | ir.cpp:34:17:34:17 | +| Constants() -> void | 0 | 70 | Store | ir.cpp:34:17:34:17 | +| Constants() -> void | 0 | 71 | VariableAddress[f_f] | ir.cpp:35:11:35:13 | +| Constants() -> void | 0 | 72 | Constant[1.0] | ir.cpp:35:16:35:20 | +| Constants() -> void | 0 | 73 | Store | ir.cpp:35:16:35:20 | +| Constants() -> void | 0 | 74 | VariableAddress[f_d] | ir.cpp:36:11:36:13 | +| Constants() -> void | 0 | 75 | Constant[1.0] | ir.cpp:36:17:36:19 | +| Constants() -> void | 0 | 76 | Store | ir.cpp:36:17:36:19 | +| Constants() -> void | 0 | 77 | VariableAddress[d_i] | ir.cpp:38:12:38:14 | +| Constants() -> void | 0 | 78 | Constant[1.0] | ir.cpp:38:18:38:18 | +| Constants() -> void | 0 | 79 | Store | ir.cpp:38:18:38:18 | +| Constants() -> void | 0 | 80 | VariableAddress[d_f] | ir.cpp:39:12:39:14 | +| Constants() -> void | 0 | 81 | Constant[1.0] | ir.cpp:39:18:39:21 | +| Constants() -> void | 0 | 82 | Store | ir.cpp:39:18:39:21 | +| Constants() -> void | 0 | 83 | VariableAddress[d_d] | ir.cpp:40:12:40:14 | +| Constants() -> void | 0 | 84 | Constant[1.0] | ir.cpp:40:17:40:20 | +| Constants() -> void | 0 | 85 | Store | ir.cpp:40:17:40:20 | +| Constants() -> void | 0 | 86 | NoOp | ir.cpp:41:1:41:1 | +| Constants() -> void | 0 | 87 | ReturnVoid | ir.cpp:1:6:1:14 | +| Constants() -> void | 0 | 88 | UnmodeledUse | ir.cpp:1:6:1:14 | +| Constants() -> void | 0 | 89 | ExitFunction | ir.cpp:1:6:1:14 | +| Continue(int) -> void | 0 | 0 | EnterFunction | ir.cpp:360:6:360:13 | +| Continue(int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:360:6:360:13 | +| Continue(int) -> void | 0 | 2 | InitializeParameter[n] | ir.cpp:360:19:360:19 | +| Continue(int) -> void | 0 | 3 | VariableAddress[n] | ir.cpp:360:19:360:19 | +| Continue(int) -> void | 0 | 4 | Store | ir.cpp:360:19:360:19 | +| Continue(int) -> void | 1 | 0 | Phi | ir.cpp:362:13:362:13 | +| Continue(int) -> void | 1 | 1 | VariableAddress[n] | ir.cpp:362:13:362:13 | +| Continue(int) -> void | 1 | 2 | Load | ir.cpp:362:13:362:13 | +| Continue(int) -> void | 1 | 3 | Constant[1] | ir.cpp:362:18:362:18 | +| Continue(int) -> void | 1 | 4 | CompareEQ | ir.cpp:362:13:362:18 | +| Continue(int) -> void | 1 | 5 | ConditionalBranch | ir.cpp:362:13:362:18 | +| Continue(int) -> void | 2 | 0 | NoOp | ir.cpp:363:13:363:21 | +| Continue(int) -> void | 3 | 0 | Constant[1] | ir.cpp:365:14:365:14 | +| Continue(int) -> void | 3 | 1 | VariableAddress[n] | ir.cpp:365:9:365:9 | +| Continue(int) -> void | 3 | 2 | Load | ir.cpp:365:9:365:14 | +| Continue(int) -> void | 3 | 3 | Sub | ir.cpp:365:9:365:14 | +| Continue(int) -> void | 3 | 4 | Store | ir.cpp:365:9:365:14 | +| Continue(int) -> void | 4 | 0 | Phi | ir.cpp:361:5:361:5 | +| Continue(int) -> void | 4 | 1 | NoOp | ir.cpp:361:5:361:5 | +| Continue(int) -> void | 4 | 2 | VariableAddress[n] | ir.cpp:366:14:366:14 | +| Continue(int) -> void | 4 | 3 | Load | ir.cpp:366:14:366:14 | +| Continue(int) -> void | 4 | 4 | Constant[0] | ir.cpp:366:18:366:18 | +| Continue(int) -> void | 4 | 5 | CompareGT | ir.cpp:366:14:366:18 | +| Continue(int) -> void | 4 | 6 | ConditionalBranch | ir.cpp:366:14:366:18 | +| Continue(int) -> void | 5 | 0 | NoOp | ir.cpp:367:1:367:1 | +| Continue(int) -> void | 5 | 1 | ReturnVoid | ir.cpp:360:6:360:13 | +| Continue(int) -> void | 5 | 2 | UnmodeledUse | ir.cpp:360:6:360:13 | +| Continue(int) -> void | 5 | 3 | ExitFunction | ir.cpp:360:6:360:13 | +| DeclareObject() -> void | 0 | 0 | EnterFunction | ir.cpp:615:6:615:18 | +| DeclareObject() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:615:6:615:18 | +| DeclareObject() -> void | 0 | 2 | VariableAddress[s1] | ir.cpp:616:12:616:13 | +| DeclareObject() -> void | 0 | 3 | FunctionAddress[String] | ir.cpp:616:12:616:13 | +| DeclareObject() -> void | 0 | 4 | Invoke | ir.cpp:616:12:616:13 | +| DeclareObject() -> void | 0 | 5 | VariableAddress[s2] | ir.cpp:617:12:617:13 | +| DeclareObject() -> void | 0 | 6 | FunctionAddress[String] | ir.cpp:617:15:617:22 | +| DeclareObject() -> void | 0 | 7 | StringConstant["hello"] | ir.cpp:617:15:617:21 | +| DeclareObject() -> void | 0 | 8 | Convert | ir.cpp:617:15:617:21 | +| DeclareObject() -> void | 0 | 9 | Invoke | ir.cpp:617:15:617:22 | +| DeclareObject() -> void | 0 | 10 | VariableAddress[s3] | ir.cpp:618:12:618:13 | +| DeclareObject() -> void | 0 | 11 | FunctionAddress[ReturnObject] | ir.cpp:618:17:618:28 | +| DeclareObject() -> void | 0 | 12 | Invoke | ir.cpp:618:17:618:28 | +| DeclareObject() -> void | 0 | 13 | Store | ir.cpp:618:17:618:28 | +| DeclareObject() -> void | 0 | 14 | VariableAddress[s4] | ir.cpp:619:12:619:13 | +| DeclareObject() -> void | 0 | 15 | FunctionAddress[String] | ir.cpp:619:16:619:30 | +| DeclareObject() -> void | 0 | 16 | StringConstant["test"] | ir.cpp:619:24:619:29 | +| DeclareObject() -> void | 0 | 17 | Convert | ir.cpp:619:24:619:29 | +| DeclareObject() -> void | 0 | 18 | Invoke | ir.cpp:619:16:619:30 | +| DeclareObject() -> void | 0 | 19 | NoOp | ir.cpp:620:1:620:1 | +| DeclareObject() -> void | 0 | 20 | ReturnVoid | ir.cpp:615:6:615:18 | +| DeclareObject() -> void | 0 | 21 | UnmodeledUse | ir.cpp:615:6:615:18 | +| DeclareObject() -> void | 0 | 22 | ExitFunction | ir.cpp:615:6:615:18 | +| DerefReference(int &) -> int | 0 | 0 | EnterFunction | ir.cpp:675:5:675:18 | +| DerefReference(int &) -> int | 0 | 1 | UnmodeledDefinition | ir.cpp:675:5:675:18 | +| DerefReference(int &) -> int | 0 | 2 | InitializeParameter[r] | ir.cpp:675:25:675:25 | +| DerefReference(int &) -> int | 0 | 3 | VariableAddress[r] | ir.cpp:675:25:675:25 | +| DerefReference(int &) -> int | 0 | 4 | Store | ir.cpp:675:25:675:25 | +| DerefReference(int &) -> int | 0 | 5 | VariableAddress[#return] | ir.cpp:676:5:676:13 | +| DerefReference(int &) -> int | 0 | 6 | VariableAddress[r] | ir.cpp:676:12:676:12 | +| DerefReference(int &) -> int | 0 | 7 | Load | ir.cpp:676:12:676:12 | +| DerefReference(int &) -> int | 0 | 8 | Load | ir.cpp:676:12:676:12 | +| DerefReference(int &) -> int | 0 | 9 | Store | ir.cpp:676:12:676:12 | +| DerefReference(int &) -> int | 0 | 10 | VariableAddress[#return] | ir.cpp:675:5:675:18 | +| DerefReference(int &) -> int | 0 | 11 | ReturnValue | ir.cpp:675:5:675:18 | +| DerefReference(int &) -> int | 0 | 12 | UnmodeledUse | ir.cpp:675:5:675:18 | +| DerefReference(int &) -> int | 0 | 13 | ExitFunction | ir.cpp:675:5:675:18 | +| Dereference(int *) -> int | 0 | 0 | EnterFunction | ir.cpp:341:5:341:15 | +| Dereference(int *) -> int | 0 | 1 | UnmodeledDefinition | ir.cpp:341:5:341:15 | +| Dereference(int *) -> int | 0 | 2 | InitializeParameter[p] | ir.cpp:341:22:341:22 | +| Dereference(int *) -> int | 0 | 3 | VariableAddress[p] | ir.cpp:341:22:341:22 | +| Dereference(int *) -> int | 0 | 4 | Store | ir.cpp:341:22:341:22 | +| Dereference(int *) -> int | 0 | 5 | Constant[1] | ir.cpp:342:10:342:10 | +| Dereference(int *) -> int | 0 | 6 | VariableAddress[p] | ir.cpp:342:6:342:6 | +| Dereference(int *) -> int | 0 | 7 | Load | ir.cpp:342:6:342:6 | +| Dereference(int *) -> int | 0 | 8 | Store | ir.cpp:342:5:342:10 | +| Dereference(int *) -> int | 0 | 9 | VariableAddress[#return] | ir.cpp:343:5:343:14 | +| Dereference(int *) -> int | 0 | 10 | VariableAddress[p] | ir.cpp:343:13:343:13 | +| Dereference(int *) -> int | 0 | 11 | Load | ir.cpp:343:13:343:13 | +| Dereference(int *) -> int | 0 | 12 | Load | ir.cpp:343:12:343:13 | +| Dereference(int *) -> int | 0 | 13 | Store | ir.cpp:343:12:343:13 | +| Dereference(int *) -> int | 0 | 14 | VariableAddress[#return] | ir.cpp:341:5:341:15 | +| Dereference(int *) -> int | 0 | 15 | ReturnValue | ir.cpp:341:5:341:15 | +| Dereference(int *) -> int | 0 | 16 | UnmodeledUse | ir.cpp:341:5:341:15 | +| Dereference(int *) -> int | 0 | 17 | ExitFunction | ir.cpp:341:5:341:15 | +| Derived::Derived() -> void | 0 | 0 | EnterFunction | ir.cpp:766:3:766:9 | +| Derived::Derived() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:766:3:766:9 | +| Derived::Derived() -> void | 0 | 2 | InitializeThis | ir.cpp:766:3:766:9 | +| Derived::Derived() -> void | 0 | 3 | ConvertToBase[Derived : Middle] | ir.cpp:766:13:766:13 | +| Derived::Derived() -> void | 0 | 4 | FunctionAddress[Middle] | ir.cpp:766:13:766:13 | +| Derived::Derived() -> void | 0 | 5 | Invoke | ir.cpp:766:13:766:13 | +| Derived::Derived() -> void | 0 | 6 | FieldAddress[derived_s] | ir.cpp:766:13:766:13 | +| Derived::Derived() -> void | 0 | 7 | FunctionAddress[String] | ir.cpp:766:13:766:13 | +| Derived::Derived() -> void | 0 | 8 | Invoke | ir.cpp:766:13:766:13 | +| Derived::Derived() -> void | 0 | 9 | NoOp | ir.cpp:767:3:767:3 | +| Derived::Derived() -> void | 0 | 10 | ReturnVoid | ir.cpp:766:3:766:9 | +| Derived::Derived() -> void | 0 | 11 | UnmodeledUse | ir.cpp:766:3:766:9 | +| Derived::Derived() -> void | 0 | 12 | ExitFunction | ir.cpp:766:3:766:9 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 0 | EnterFunction | ir.cpp:763:8:763:8 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 1 | UnmodeledDefinition | ir.cpp:763:8:763:8 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 2 | InitializeThis | ir.cpp:763:8:763:8 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 3 | InitializeParameter[p#0] | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 4 | VariableAddress[p#0] | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 5 | Store | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 6 | CopyValue | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 7 | ConvertToBase[Derived : Middle] | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 8 | FunctionAddress[operator=] | ir.cpp:763:8:763:8 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 9 | VariableAddress[p#0] | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 10 | Load | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 11 | ConvertToBase[Derived : Middle] | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 12 | Invoke | ir.cpp:763:8:763:8 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 13 | CopyValue | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 14 | FieldAddress[derived_s] | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 15 | FunctionAddress[operator=] | ir.cpp:763:8:763:8 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 16 | VariableAddress[p#0] | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 17 | Load | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 18 | FieldAddress[derived_s] | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 19 | Invoke | ir.cpp:763:8:763:8 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 20 | VariableAddress[#return] | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 21 | CopyValue | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 22 | Store | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 23 | VariableAddress[#return] | ir.cpp:763:8:763:8 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 24 | ReturnValue | ir.cpp:763:8:763:8 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 25 | UnmodeledUse | ir.cpp:763:8:763:8 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 26 | ExitFunction | ir.cpp:763:8:763:8 | +| Derived::~Derived() -> void | 0 | 0 | EnterFunction | ir.cpp:768:3:768:10 | +| Derived::~Derived() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:768:3:768:10 | +| Derived::~Derived() -> void | 0 | 2 | InitializeThis | ir.cpp:768:3:768:10 | +| Derived::~Derived() -> void | 0 | 3 | NoOp | ir.cpp:769:3:769:3 | +| Derived::~Derived() -> void | 0 | 4 | FieldAddress[derived_s] | ir.cpp:769:3:769:3 | +| Derived::~Derived() -> void | 0 | 5 | FunctionAddress[~String] | ir.cpp:769:3:769:3 | +| Derived::~Derived() -> void | 0 | 6 | Invoke | ir.cpp:769:3:769:3 | +| Derived::~Derived() -> void | 0 | 7 | ConvertToBase[Derived : Middle] | ir.cpp:769:3:769:3 | +| Derived::~Derived() -> void | 0 | 8 | FunctionAddress[~Middle] | ir.cpp:769:3:769:3 | +| Derived::~Derived() -> void | 0 | 9 | Invoke | ir.cpp:769:3:769:3 | +| Derived::~Derived() -> void | 0 | 10 | ReturnVoid | ir.cpp:768:3:768:10 | +| Derived::~Derived() -> void | 0 | 11 | UnmodeledUse | ir.cpp:768:3:768:10 | +| Derived::~Derived() -> void | 0 | 12 | ExitFunction | ir.cpp:768:3:768:10 | +| DerivedVB::DerivedVB() -> void | 0 | 0 | EnterFunction | ir.cpp:793:3:793:11 | +| DerivedVB::DerivedVB() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:793:3:793:11 | +| DerivedVB::DerivedVB() -> void | 0 | 2 | InitializeThis | ir.cpp:793:3:793:11 | +| DerivedVB::DerivedVB() -> void | 0 | 3 | ConvertToBase[DerivedVB : Base] | ir.cpp:793:15:793:15 | +| DerivedVB::DerivedVB() -> void | 0 | 4 | FunctionAddress[Base] | ir.cpp:793:15:793:15 | +| DerivedVB::DerivedVB() -> void | 0 | 5 | Invoke | ir.cpp:793:15:793:15 | +| DerivedVB::DerivedVB() -> void | 0 | 6 | ConvertToBase[DerivedVB : MiddleVB1] | ir.cpp:793:15:793:15 | +| DerivedVB::DerivedVB() -> void | 0 | 7 | FunctionAddress[MiddleVB1] | ir.cpp:793:15:793:15 | +| DerivedVB::DerivedVB() -> void | 0 | 8 | Invoke | ir.cpp:793:15:793:15 | +| DerivedVB::DerivedVB() -> void | 0 | 9 | ConvertToBase[DerivedVB : MiddleVB2] | ir.cpp:793:15:793:15 | +| DerivedVB::DerivedVB() -> void | 0 | 10 | FunctionAddress[MiddleVB2] | ir.cpp:793:15:793:15 | +| DerivedVB::DerivedVB() -> void | 0 | 11 | Invoke | ir.cpp:793:15:793:15 | +| DerivedVB::DerivedVB() -> void | 0 | 12 | FieldAddress[derivedvb_s] | ir.cpp:793:15:793:15 | +| DerivedVB::DerivedVB() -> void | 0 | 13 | FunctionAddress[String] | ir.cpp:793:15:793:15 | +| DerivedVB::DerivedVB() -> void | 0 | 14 | Invoke | ir.cpp:793:15:793:15 | +| DerivedVB::DerivedVB() -> void | 0 | 15 | NoOp | ir.cpp:794:3:794:3 | +| DerivedVB::DerivedVB() -> void | 0 | 16 | ReturnVoid | ir.cpp:793:3:793:11 | +| DerivedVB::DerivedVB() -> void | 0 | 17 | UnmodeledUse | ir.cpp:793:3:793:11 | +| DerivedVB::DerivedVB() -> void | 0 | 18 | ExitFunction | ir.cpp:793:3:793:11 | +| DerivedVB::~DerivedVB() -> void | 0 | 0 | EnterFunction | ir.cpp:795:3:795:12 | +| DerivedVB::~DerivedVB() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:795:3:795:12 | +| DerivedVB::~DerivedVB() -> void | 0 | 2 | InitializeThis | ir.cpp:795:3:795:12 | +| DerivedVB::~DerivedVB() -> void | 0 | 3 | NoOp | ir.cpp:796:3:796:3 | +| DerivedVB::~DerivedVB() -> void | 0 | 4 | FieldAddress[derivedvb_s] | ir.cpp:796:3:796:3 | +| DerivedVB::~DerivedVB() -> void | 0 | 5 | FunctionAddress[~String] | ir.cpp:796:3:796:3 | +| DerivedVB::~DerivedVB() -> void | 0 | 6 | Invoke | ir.cpp:796:3:796:3 | +| DerivedVB::~DerivedVB() -> void | 0 | 7 | ConvertToBase[DerivedVB : MiddleVB2] | ir.cpp:796:3:796:3 | +| DerivedVB::~DerivedVB() -> void | 0 | 8 | FunctionAddress[~MiddleVB2] | ir.cpp:796:3:796:3 | +| DerivedVB::~DerivedVB() -> void | 0 | 9 | Invoke | ir.cpp:796:3:796:3 | +| DerivedVB::~DerivedVB() -> void | 0 | 10 | ConvertToBase[DerivedVB : MiddleVB1] | ir.cpp:796:3:796:3 | +| DerivedVB::~DerivedVB() -> void | 0 | 11 | FunctionAddress[~MiddleVB1] | ir.cpp:796:3:796:3 | +| DerivedVB::~DerivedVB() -> void | 0 | 12 | Invoke | ir.cpp:796:3:796:3 | +| DerivedVB::~DerivedVB() -> void | 0 | 13 | ConvertToBase[DerivedVB : Base] | ir.cpp:796:3:796:3 | +| DerivedVB::~DerivedVB() -> void | 0 | 14 | FunctionAddress[~Base] | ir.cpp:796:3:796:3 | +| DerivedVB::~DerivedVB() -> void | 0 | 15 | Invoke | ir.cpp:796:3:796:3 | +| DerivedVB::~DerivedVB() -> void | 0 | 16 | ReturnVoid | ir.cpp:795:3:795:12 | +| DerivedVB::~DerivedVB() -> void | 0 | 17 | UnmodeledUse | ir.cpp:795:3:795:12 | +| DerivedVB::~DerivedVB() -> void | 0 | 18 | ExitFunction | ir.cpp:795:3:795:12 | +| DoStatements(int) -> void | 0 | 0 | EnterFunction | ir.cpp:259:6:259:17 | +| DoStatements(int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:259:6:259:17 | +| DoStatements(int) -> void | 0 | 2 | InitializeParameter[n] | ir.cpp:259:23:259:23 | +| DoStatements(int) -> void | 0 | 3 | VariableAddress[n] | ir.cpp:259:23:259:23 | +| DoStatements(int) -> void | 0 | 4 | Store | ir.cpp:259:23:259:23 | +| DoStatements(int) -> void | 1 | 0 | Phi | ir.cpp:261:14:261:14 | +| DoStatements(int) -> void | 1 | 1 | Constant[1] | ir.cpp:261:14:261:14 | +| DoStatements(int) -> void | 1 | 2 | VariableAddress[n] | ir.cpp:261:9:261:9 | +| DoStatements(int) -> void | 1 | 3 | Load | ir.cpp:261:9:261:14 | +| DoStatements(int) -> void | 1 | 4 | Sub | ir.cpp:261:9:261:14 | +| DoStatements(int) -> void | 1 | 5 | Store | ir.cpp:261:9:261:14 | +| DoStatements(int) -> void | 1 | 6 | VariableAddress[n] | ir.cpp:262:14:262:14 | +| DoStatements(int) -> void | 1 | 7 | Load | ir.cpp:262:14:262:14 | +| DoStatements(int) -> void | 1 | 8 | Constant[0] | ir.cpp:262:18:262:18 | +| DoStatements(int) -> void | 1 | 9 | CompareGT | ir.cpp:262:14:262:18 | +| DoStatements(int) -> void | 1 | 10 | ConditionalBranch | ir.cpp:262:14:262:18 | +| DoStatements(int) -> void | 2 | 0 | NoOp | ir.cpp:263:1:263:1 | +| DoStatements(int) -> void | 2 | 1 | ReturnVoid | ir.cpp:259:6:259:17 | +| DoStatements(int) -> void | 2 | 2 | UnmodeledUse | ir.cpp:259:6:259:17 | +| DoStatements(int) -> void | 2 | 3 | ExitFunction | ir.cpp:259:6:259:17 | +| DynamicCast() -> void | 0 | 0 | EnterFunction | ir.cpp:849:6:849:16 | +| DynamicCast() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:849:6:849:16 | +| DynamicCast() -> void | 0 | 2 | VariableAddress[b] | ir.cpp:850:19:850:19 | +| DynamicCast() -> void | 0 | 3 | FunctionAddress[PolymorphicBase] | file://:0:0:0:0 | +| DynamicCast() -> void | 0 | 4 | Invoke | file://:0:0:0:0 | +| DynamicCast() -> void | 0 | 5 | VariableAddress[d] | ir.cpp:851:22:851:22 | +| DynamicCast() -> void | 0 | 6 | FunctionAddress[PolymorphicDerived] | ir.cpp:851:22:851:22 | +| DynamicCast() -> void | 0 | 7 | Invoke | ir.cpp:851:22:851:22 | +| DynamicCast() -> void | 0 | 8 | VariableAddress[pb] | ir.cpp:853:20:853:21 | +| DynamicCast() -> void | 0 | 9 | VariableAddress[b] | ir.cpp:853:26:853:26 | +| DynamicCast() -> void | 0 | 10 | Store | ir.cpp:853:25:853:26 | +| DynamicCast() -> void | 0 | 11 | VariableAddress[pd] | ir.cpp:854:23:854:24 | +| DynamicCast() -> void | 0 | 12 | VariableAddress[d] | ir.cpp:854:29:854:29 | +| DynamicCast() -> void | 0 | 13 | Store | ir.cpp:854:28:854:29 | +| DynamicCast() -> void | 0 | 14 | VariableAddress[pd] | ir.cpp:857:39:857:40 | +| DynamicCast() -> void | 0 | 15 | Load | ir.cpp:857:39:857:40 | +| DynamicCast() -> void | 0 | 16 | ConvertToBase[PolymorphicDerived : PolymorphicBase] | ir.cpp:857:8:857:41 | +| DynamicCast() -> void | 0 | 17 | VariableAddress[pb] | ir.cpp:857:3:857:4 | +| DynamicCast() -> void | 0 | 18 | Store | ir.cpp:857:3:857:41 | +| DynamicCast() -> void | 0 | 19 | VariableAddress[rb] | ir.cpp:858:20:858:21 | +| DynamicCast() -> void | 0 | 20 | VariableAddress[d] | ir.cpp:858:56:858:56 | +| DynamicCast() -> void | 0 | 21 | ConvertToBase[PolymorphicDerived : PolymorphicBase] | ir.cpp:858:25:858:57 | +| DynamicCast() -> void | 0 | 22 | Store | ir.cpp:858:25:858:57 | +| DynamicCast() -> void | 0 | 23 | VariableAddress[pb] | ir.cpp:860:42:860:43 | +| DynamicCast() -> void | 0 | 24 | Load | ir.cpp:860:42:860:43 | +| DynamicCast() -> void | 0 | 25 | CheckedConvertOrNull | ir.cpp:860:8:860:44 | +| DynamicCast() -> void | 0 | 26 | VariableAddress[pd] | ir.cpp:860:3:860:4 | +| DynamicCast() -> void | 0 | 27 | Store | ir.cpp:860:3:860:44 | +| DynamicCast() -> void | 0 | 28 | VariableAddress[rd] | ir.cpp:861:23:861:24 | +| DynamicCast() -> void | 0 | 29 | VariableAddress[b] | ir.cpp:861:62:861:62 | +| DynamicCast() -> void | 0 | 30 | CheckedConvertOrThrow | ir.cpp:861:28:861:63 | +| DynamicCast() -> void | 0 | 31 | Store | ir.cpp:861:28:861:63 | +| DynamicCast() -> void | 0 | 32 | VariableAddress[pv] | ir.cpp:863:9:863:10 | +| DynamicCast() -> void | 0 | 33 | VariableAddress[pb] | ir.cpp:863:34:863:35 | +| DynamicCast() -> void | 0 | 34 | Load | ir.cpp:863:34:863:35 | +| DynamicCast() -> void | 0 | 35 | DynamicCastToVoid | ir.cpp:863:14:863:36 | +| DynamicCast() -> void | 0 | 36 | Store | ir.cpp:863:14:863:36 | +| DynamicCast() -> void | 0 | 37 | VariableAddress[pcv] | ir.cpp:864:15:864:17 | +| DynamicCast() -> void | 0 | 38 | VariableAddress[pd] | ir.cpp:864:47:864:48 | +| DynamicCast() -> void | 0 | 39 | Load | ir.cpp:864:47:864:48 | +| DynamicCast() -> void | 0 | 40 | DynamicCastToVoid | ir.cpp:864:21:864:49 | +| DynamicCast() -> void | 0 | 41 | Store | ir.cpp:864:21:864:49 | +| DynamicCast() -> void | 0 | 42 | NoOp | ir.cpp:865:1:865:1 | +| DynamicCast() -> void | 0 | 43 | ReturnVoid | ir.cpp:849:6:849:16 | +| DynamicCast() -> void | 0 | 44 | UnmodeledUse | ir.cpp:849:6:849:16 | +| DynamicCast() -> void | 0 | 45 | ExitFunction | ir.cpp:849:6:849:16 | +| EarlyReturn(int, int) -> void | 0 | 0 | EnterFunction | ir.cpp:535:6:535:16 | +| EarlyReturn(int, int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:535:6:535:16 | +| EarlyReturn(int, int) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:535:22:535:22 | +| EarlyReturn(int, int) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:535:22:535:22 | +| EarlyReturn(int, int) -> void | 0 | 4 | Store | ir.cpp:535:22:535:22 | +| EarlyReturn(int, int) -> void | 0 | 5 | InitializeParameter[y] | ir.cpp:535:29:535:29 | +| EarlyReturn(int, int) -> void | 0 | 6 | VariableAddress[y] | ir.cpp:535:29:535:29 | +| EarlyReturn(int, int) -> void | 0 | 7 | Store | ir.cpp:535:29:535:29 | +| EarlyReturn(int, int) -> void | 0 | 8 | VariableAddress[x] | ir.cpp:536:9:536:9 | +| EarlyReturn(int, int) -> void | 0 | 9 | Load | ir.cpp:536:9:536:9 | +| EarlyReturn(int, int) -> void | 0 | 10 | VariableAddress[y] | ir.cpp:536:13:536:13 | +| EarlyReturn(int, int) -> void | 0 | 11 | Load | ir.cpp:536:13:536:13 | +| EarlyReturn(int, int) -> void | 0 | 12 | CompareLT | ir.cpp:536:9:536:13 | +| EarlyReturn(int, int) -> void | 0 | 13 | ConditionalBranch | ir.cpp:536:9:536:13 | +| EarlyReturn(int, int) -> void | 1 | 0 | ReturnVoid | ir.cpp:535:6:535:16 | +| EarlyReturn(int, int) -> void | 1 | 1 | UnmodeledUse | ir.cpp:535:6:535:16 | +| EarlyReturn(int, int) -> void | 1 | 2 | ExitFunction | ir.cpp:535:6:535:16 | +| EarlyReturn(int, int) -> void | 2 | 0 | NoOp | ir.cpp:537:9:537:15 | +| EarlyReturn(int, int) -> void | 3 | 0 | VariableAddress[x] | ir.cpp:540:9:540:9 | +| EarlyReturn(int, int) -> void | 3 | 1 | Load | ir.cpp:540:9:540:9 | +| EarlyReturn(int, int) -> void | 3 | 2 | VariableAddress[y] | ir.cpp:540:5:540:5 | +| EarlyReturn(int, int) -> void | 3 | 3 | Store | ir.cpp:540:5:540:9 | +| EarlyReturn(int, int) -> void | 3 | 4 | NoOp | ir.cpp:541:1:541:1 | +| EarlyReturnValue(int, int) -> int | 0 | 0 | EnterFunction | ir.cpp:543:5:543:20 | +| EarlyReturnValue(int, int) -> int | 0 | 1 | UnmodeledDefinition | ir.cpp:543:5:543:20 | +| EarlyReturnValue(int, int) -> int | 0 | 2 | InitializeParameter[x] | ir.cpp:543:26:543:26 | +| EarlyReturnValue(int, int) -> int | 0 | 3 | VariableAddress[x] | ir.cpp:543:26:543:26 | +| EarlyReturnValue(int, int) -> int | 0 | 4 | Store | ir.cpp:543:26:543:26 | +| EarlyReturnValue(int, int) -> int | 0 | 5 | InitializeParameter[y] | ir.cpp:543:33:543:33 | +| EarlyReturnValue(int, int) -> int | 0 | 6 | VariableAddress[y] | ir.cpp:543:33:543:33 | +| EarlyReturnValue(int, int) -> int | 0 | 7 | Store | ir.cpp:543:33:543:33 | +| EarlyReturnValue(int, int) -> int | 0 | 8 | VariableAddress[x] | ir.cpp:544:9:544:9 | +| EarlyReturnValue(int, int) -> int | 0 | 9 | Load | ir.cpp:544:9:544:9 | +| EarlyReturnValue(int, int) -> int | 0 | 10 | VariableAddress[y] | ir.cpp:544:13:544:13 | +| EarlyReturnValue(int, int) -> int | 0 | 11 | Load | ir.cpp:544:13:544:13 | +| EarlyReturnValue(int, int) -> int | 0 | 12 | CompareLT | ir.cpp:544:9:544:13 | +| EarlyReturnValue(int, int) -> int | 0 | 13 | ConditionalBranch | ir.cpp:544:9:544:13 | +| EarlyReturnValue(int, int) -> int | 1 | 0 | Phi | ir.cpp:543:5:543:20 | +| EarlyReturnValue(int, int) -> int | 1 | 1 | VariableAddress[#return] | ir.cpp:543:5:543:20 | +| EarlyReturnValue(int, int) -> int | 1 | 2 | ReturnValue | ir.cpp:543:5:543:20 | +| EarlyReturnValue(int, int) -> int | 1 | 3 | UnmodeledUse | ir.cpp:543:5:543:20 | +| EarlyReturnValue(int, int) -> int | 1 | 4 | ExitFunction | ir.cpp:543:5:543:20 | +| EarlyReturnValue(int, int) -> int | 2 | 0 | VariableAddress[#return] | ir.cpp:545:9:545:17 | +| EarlyReturnValue(int, int) -> int | 2 | 1 | VariableAddress[x] | ir.cpp:545:16:545:16 | +| EarlyReturnValue(int, int) -> int | 2 | 2 | Load | ir.cpp:545:16:545:16 | +| EarlyReturnValue(int, int) -> int | 2 | 3 | Store | ir.cpp:545:16:545:16 | +| EarlyReturnValue(int, int) -> int | 3 | 0 | VariableAddress[#return] | ir.cpp:548:5:548:17 | +| EarlyReturnValue(int, int) -> int | 3 | 1 | VariableAddress[x] | ir.cpp:548:12:548:12 | +| EarlyReturnValue(int, int) -> int | 3 | 2 | Load | ir.cpp:548:12:548:12 | +| EarlyReturnValue(int, int) -> int | 3 | 3 | VariableAddress[y] | ir.cpp:548:16:548:16 | +| EarlyReturnValue(int, int) -> int | 3 | 4 | Load | ir.cpp:548:16:548:16 | +| EarlyReturnValue(int, int) -> int | 3 | 5 | Add | ir.cpp:548:12:548:16 | +| EarlyReturnValue(int, int) -> int | 3 | 6 | Store | ir.cpp:548:12:548:16 | +| EnumSwitch(E) -> int | 0 | 0 | EnterFunction | ir.cpp:560:5:560:14 | +| EnumSwitch(E) -> int | 0 | 1 | UnmodeledDefinition | ir.cpp:560:5:560:14 | +| EnumSwitch(E) -> int | 0 | 2 | InitializeParameter[e] | ir.cpp:560:18:560:18 | +| EnumSwitch(E) -> int | 0 | 3 | VariableAddress[e] | ir.cpp:560:18:560:18 | +| EnumSwitch(E) -> int | 0 | 4 | Store | ir.cpp:560:18:560:18 | +| EnumSwitch(E) -> int | 0 | 5 | VariableAddress[e] | ir.cpp:561:13:561:13 | +| EnumSwitch(E) -> int | 0 | 6 | Load | ir.cpp:561:13:561:13 | +| EnumSwitch(E) -> int | 0 | 7 | Convert | ir.cpp:561:13:561:13 | +| EnumSwitch(E) -> int | 0 | 8 | Switch | ir.cpp:561:5:568:5 | +| EnumSwitch(E) -> int | 1 | 0 | Phi | ir.cpp:560:5:560:14 | +| EnumSwitch(E) -> int | 1 | 1 | VariableAddress[#return] | ir.cpp:560:5:560:14 | +| EnumSwitch(E) -> int | 1 | 2 | ReturnValue | ir.cpp:560:5:560:14 | +| EnumSwitch(E) -> int | 1 | 3 | UnmodeledUse | ir.cpp:560:5:560:14 | +| EnumSwitch(E) -> int | 1 | 4 | ExitFunction | ir.cpp:560:5:560:14 | +| EnumSwitch(E) -> int | 2 | 0 | NoOp | ir.cpp:564:9:564:17 | +| EnumSwitch(E) -> int | 2 | 1 | VariableAddress[#return] | ir.cpp:565:13:565:21 | +| EnumSwitch(E) -> int | 2 | 2 | Constant[1] | ir.cpp:565:20:565:20 | +| EnumSwitch(E) -> int | 2 | 3 | Store | ir.cpp:565:20:565:20 | +| EnumSwitch(E) -> int | 3 | 0 | NoOp | ir.cpp:566:9:566:16 | +| EnumSwitch(E) -> int | 3 | 1 | VariableAddress[#return] | ir.cpp:567:13:567:22 | +| EnumSwitch(E) -> int | 3 | 2 | Constant[-1] | ir.cpp:567:20:567:21 | +| EnumSwitch(E) -> int | 3 | 3 | Store | ir.cpp:567:20:567:21 | +| EnumSwitch(E) -> int | 4 | 0 | NoOp | ir.cpp:562:9:562:17 | +| EnumSwitch(E) -> int | 4 | 1 | VariableAddress[#return] | ir.cpp:563:13:563:21 | +| EnumSwitch(E) -> int | 4 | 2 | Constant[0] | ir.cpp:563:20:563:20 | +| EnumSwitch(E) -> int | 4 | 3 | Store | ir.cpp:563:20:563:20 | +| FieldAccess() -> void | 0 | 0 | EnterFunction | ir.cpp:426:6:426:16 | +| FieldAccess() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:426:6:426:16 | +| FieldAccess() -> void | 0 | 2 | VariableAddress[pt] | ir.cpp:427:11:427:12 | +| FieldAccess() -> void | 0 | 3 | Uninitialized | ir.cpp:427:11:427:12 | +| FieldAccess() -> void | 0 | 4 | Store | ir.cpp:427:11:427:12 | +| FieldAccess() -> void | 0 | 5 | Constant[5] | ir.cpp:428:12:428:12 | +| FieldAccess() -> void | 0 | 6 | VariableAddress[pt] | ir.cpp:428:5:428:6 | +| FieldAccess() -> void | 0 | 7 | FieldAddress[x] | ir.cpp:428:8:428:8 | +| FieldAccess() -> void | 0 | 8 | Store | ir.cpp:428:5:428:12 | +| FieldAccess() -> void | 0 | 9 | VariableAddress[pt] | ir.cpp:429:12:429:13 | +| FieldAccess() -> void | 0 | 10 | FieldAddress[x] | ir.cpp:429:15:429:15 | +| FieldAccess() -> void | 0 | 11 | Load | ir.cpp:429:15:429:15 | +| FieldAccess() -> void | 0 | 12 | VariableAddress[pt] | ir.cpp:429:5:429:6 | +| FieldAccess() -> void | 0 | 13 | FieldAddress[y] | ir.cpp:429:8:429:8 | +| FieldAccess() -> void | 0 | 14 | Store | ir.cpp:429:5:429:15 | +| FieldAccess() -> void | 0 | 15 | VariableAddress[p] | ir.cpp:430:10:430:10 | +| FieldAccess() -> void | 0 | 16 | VariableAddress[pt] | ir.cpp:430:15:430:16 | +| FieldAccess() -> void | 0 | 17 | FieldAddress[y] | ir.cpp:430:18:430:18 | +| FieldAccess() -> void | 0 | 18 | Store | ir.cpp:430:14:430:18 | +| FieldAccess() -> void | 0 | 19 | NoOp | ir.cpp:431:1:431:1 | +| FieldAccess() -> void | 0 | 20 | ReturnVoid | ir.cpp:426:6:426:16 | +| FieldAccess() -> void | 0 | 21 | UnmodeledUse | ir.cpp:426:6:426:16 | +| FieldAccess() -> void | 0 | 22 | ExitFunction | ir.cpp:426:6:426:16 | +| FloatCompare(double, double) -> void | 0 | 0 | EnterFunction | ir.cpp:133:6:133:17 | +| FloatCompare(double, double) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:133:6:133:17 | +| FloatCompare(double, double) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:133:26:133:26 | +| FloatCompare(double, double) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:133:26:133:26 | +| FloatCompare(double, double) -> void | 0 | 4 | Store | ir.cpp:133:26:133:26 | +| FloatCompare(double, double) -> void | 0 | 5 | InitializeParameter[y] | ir.cpp:133:36:133:36 | +| FloatCompare(double, double) -> void | 0 | 6 | VariableAddress[y] | ir.cpp:133:36:133:36 | +| FloatCompare(double, double) -> void | 0 | 7 | Store | ir.cpp:133:36:133:36 | +| FloatCompare(double, double) -> void | 0 | 8 | VariableAddress[b] | ir.cpp:134:10:134:10 | +| FloatCompare(double, double) -> void | 0 | 9 | Uninitialized | ir.cpp:134:10:134:10 | +| FloatCompare(double, double) -> void | 0 | 10 | Store | ir.cpp:134:10:134:10 | +| FloatCompare(double, double) -> void | 0 | 11 | VariableAddress[x] | ir.cpp:136:9:136:9 | +| FloatCompare(double, double) -> void | 0 | 12 | Load | ir.cpp:136:9:136:9 | +| FloatCompare(double, double) -> void | 0 | 13 | VariableAddress[y] | ir.cpp:136:14:136:14 | +| FloatCompare(double, double) -> void | 0 | 14 | Load | ir.cpp:136:14:136:14 | +| FloatCompare(double, double) -> void | 0 | 15 | CompareEQ | ir.cpp:136:9:136:14 | +| FloatCompare(double, double) -> void | 0 | 16 | VariableAddress[b] | ir.cpp:136:5:136:5 | +| FloatCompare(double, double) -> void | 0 | 17 | Store | ir.cpp:136:5:136:14 | +| FloatCompare(double, double) -> void | 0 | 18 | VariableAddress[x] | ir.cpp:137:9:137:9 | +| FloatCompare(double, double) -> void | 0 | 19 | Load | ir.cpp:137:9:137:9 | +| FloatCompare(double, double) -> void | 0 | 20 | VariableAddress[y] | ir.cpp:137:14:137:14 | +| FloatCompare(double, double) -> void | 0 | 21 | Load | ir.cpp:137:14:137:14 | +| FloatCompare(double, double) -> void | 0 | 22 | CompareNE | ir.cpp:137:9:137:14 | +| FloatCompare(double, double) -> void | 0 | 23 | VariableAddress[b] | ir.cpp:137:5:137:5 | +| FloatCompare(double, double) -> void | 0 | 24 | Store | ir.cpp:137:5:137:14 | +| FloatCompare(double, double) -> void | 0 | 25 | VariableAddress[x] | ir.cpp:138:9:138:9 | +| FloatCompare(double, double) -> void | 0 | 26 | Load | ir.cpp:138:9:138:9 | +| FloatCompare(double, double) -> void | 0 | 27 | VariableAddress[y] | ir.cpp:138:13:138:13 | +| FloatCompare(double, double) -> void | 0 | 28 | Load | ir.cpp:138:13:138:13 | +| FloatCompare(double, double) -> void | 0 | 29 | CompareLT | ir.cpp:138:9:138:13 | +| FloatCompare(double, double) -> void | 0 | 30 | VariableAddress[b] | ir.cpp:138:5:138:5 | +| FloatCompare(double, double) -> void | 0 | 31 | Store | ir.cpp:138:5:138:13 | +| FloatCompare(double, double) -> void | 0 | 32 | VariableAddress[x] | ir.cpp:139:9:139:9 | +| FloatCompare(double, double) -> void | 0 | 33 | Load | ir.cpp:139:9:139:9 | +| FloatCompare(double, double) -> void | 0 | 34 | VariableAddress[y] | ir.cpp:139:13:139:13 | +| FloatCompare(double, double) -> void | 0 | 35 | Load | ir.cpp:139:13:139:13 | +| FloatCompare(double, double) -> void | 0 | 36 | CompareGT | ir.cpp:139:9:139:13 | +| FloatCompare(double, double) -> void | 0 | 37 | VariableAddress[b] | ir.cpp:139:5:139:5 | +| FloatCompare(double, double) -> void | 0 | 38 | Store | ir.cpp:139:5:139:13 | +| FloatCompare(double, double) -> void | 0 | 39 | VariableAddress[x] | ir.cpp:140:9:140:9 | +| FloatCompare(double, double) -> void | 0 | 40 | Load | ir.cpp:140:9:140:9 | +| FloatCompare(double, double) -> void | 0 | 41 | VariableAddress[y] | ir.cpp:140:14:140:14 | +| FloatCompare(double, double) -> void | 0 | 42 | Load | ir.cpp:140:14:140:14 | +| FloatCompare(double, double) -> void | 0 | 43 | CompareLE | ir.cpp:140:9:140:14 | +| FloatCompare(double, double) -> void | 0 | 44 | VariableAddress[b] | ir.cpp:140:5:140:5 | +| FloatCompare(double, double) -> void | 0 | 45 | Store | ir.cpp:140:5:140:14 | +| FloatCompare(double, double) -> void | 0 | 46 | VariableAddress[x] | ir.cpp:141:9:141:9 | +| FloatCompare(double, double) -> void | 0 | 47 | Load | ir.cpp:141:9:141:9 | +| FloatCompare(double, double) -> void | 0 | 48 | VariableAddress[y] | ir.cpp:141:14:141:14 | +| FloatCompare(double, double) -> void | 0 | 49 | Load | ir.cpp:141:14:141:14 | +| FloatCompare(double, double) -> void | 0 | 50 | CompareGE | ir.cpp:141:9:141:14 | +| FloatCompare(double, double) -> void | 0 | 51 | VariableAddress[b] | ir.cpp:141:5:141:5 | +| FloatCompare(double, double) -> void | 0 | 52 | Store | ir.cpp:141:5:141:14 | +| FloatCompare(double, double) -> void | 0 | 53 | NoOp | ir.cpp:142:1:142:1 | +| FloatCompare(double, double) -> void | 0 | 54 | ReturnVoid | ir.cpp:133:6:133:17 | +| FloatCompare(double, double) -> void | 0 | 55 | UnmodeledUse | ir.cpp:133:6:133:17 | +| FloatCompare(double, double) -> void | 0 | 56 | ExitFunction | ir.cpp:133:6:133:17 | +| FloatCrement(float) -> void | 0 | 0 | EnterFunction | ir.cpp:144:6:144:17 | +| FloatCrement(float) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:144:6:144:17 | +| FloatCrement(float) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:144:25:144:25 | +| FloatCrement(float) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:144:25:144:25 | +| FloatCrement(float) -> void | 0 | 4 | Store | ir.cpp:144:25:144:25 | +| FloatCrement(float) -> void | 0 | 5 | VariableAddress[y] | ir.cpp:145:11:145:11 | +| FloatCrement(float) -> void | 0 | 6 | Uninitialized | ir.cpp:145:11:145:11 | +| FloatCrement(float) -> void | 0 | 7 | Store | ir.cpp:145:11:145:11 | +| FloatCrement(float) -> void | 0 | 8 | VariableAddress[x] | ir.cpp:147:11:147:11 | +| FloatCrement(float) -> void | 0 | 9 | Load | ir.cpp:147:9:147:11 | +| FloatCrement(float) -> void | 0 | 10 | Constant[1.0] | ir.cpp:147:9:147:11 | +| FloatCrement(float) -> void | 0 | 11 | Add | ir.cpp:147:9:147:11 | +| FloatCrement(float) -> void | 0 | 12 | Store | ir.cpp:147:9:147:11 | +| FloatCrement(float) -> void | 0 | 13 | VariableAddress[y] | ir.cpp:147:5:147:5 | +| FloatCrement(float) -> void | 0 | 14 | Store | ir.cpp:147:5:147:11 | +| FloatCrement(float) -> void | 0 | 15 | VariableAddress[x] | ir.cpp:148:11:148:11 | +| FloatCrement(float) -> void | 0 | 16 | Load | ir.cpp:148:9:148:11 | +| FloatCrement(float) -> void | 0 | 17 | Constant[1.0] | ir.cpp:148:9:148:11 | +| FloatCrement(float) -> void | 0 | 18 | Sub | ir.cpp:148:9:148:11 | +| FloatCrement(float) -> void | 0 | 19 | Store | ir.cpp:148:9:148:11 | +| FloatCrement(float) -> void | 0 | 20 | VariableAddress[y] | ir.cpp:148:5:148:5 | +| FloatCrement(float) -> void | 0 | 21 | Store | ir.cpp:148:5:148:11 | +| FloatCrement(float) -> void | 0 | 22 | VariableAddress[x] | ir.cpp:149:9:149:9 | +| FloatCrement(float) -> void | 0 | 23 | Load | ir.cpp:149:9:149:11 | +| FloatCrement(float) -> void | 0 | 24 | Constant[1.0] | ir.cpp:149:9:149:11 | +| FloatCrement(float) -> void | 0 | 25 | Add | ir.cpp:149:9:149:11 | +| FloatCrement(float) -> void | 0 | 26 | Store | ir.cpp:149:9:149:11 | +| FloatCrement(float) -> void | 0 | 27 | VariableAddress[y] | ir.cpp:149:5:149:5 | +| FloatCrement(float) -> void | 0 | 28 | Store | ir.cpp:149:5:149:11 | +| FloatCrement(float) -> void | 0 | 29 | VariableAddress[x] | ir.cpp:150:9:150:9 | +| FloatCrement(float) -> void | 0 | 30 | Load | ir.cpp:150:9:150:11 | +| FloatCrement(float) -> void | 0 | 31 | Constant[1.0] | ir.cpp:150:9:150:11 | +| FloatCrement(float) -> void | 0 | 32 | Sub | ir.cpp:150:9:150:11 | +| FloatCrement(float) -> void | 0 | 33 | Store | ir.cpp:150:9:150:11 | +| FloatCrement(float) -> void | 0 | 34 | VariableAddress[y] | ir.cpp:150:5:150:5 | +| FloatCrement(float) -> void | 0 | 35 | Store | ir.cpp:150:5:150:11 | +| FloatCrement(float) -> void | 0 | 36 | NoOp | ir.cpp:151:1:151:1 | +| FloatCrement(float) -> void | 0 | 37 | ReturnVoid | ir.cpp:144:6:144:17 | +| FloatCrement(float) -> void | 0 | 38 | UnmodeledUse | ir.cpp:144:6:144:17 | +| FloatCrement(float) -> void | 0 | 39 | ExitFunction | ir.cpp:144:6:144:17 | +| FloatOps(double, double) -> void | 0 | 0 | EnterFunction | ir.cpp:114:6:114:13 | +| FloatOps(double, double) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:114:6:114:13 | +| FloatOps(double, double) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:114:22:114:22 | +| FloatOps(double, double) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:114:22:114:22 | +| FloatOps(double, double) -> void | 0 | 4 | Store | ir.cpp:114:22:114:22 | +| FloatOps(double, double) -> void | 0 | 5 | InitializeParameter[y] | ir.cpp:114:32:114:32 | +| FloatOps(double, double) -> void | 0 | 6 | VariableAddress[y] | ir.cpp:114:32:114:32 | +| FloatOps(double, double) -> void | 0 | 7 | Store | ir.cpp:114:32:114:32 | +| FloatOps(double, double) -> void | 0 | 8 | VariableAddress[z] | ir.cpp:115:12:115:12 | +| FloatOps(double, double) -> void | 0 | 9 | Uninitialized | ir.cpp:115:12:115:12 | +| FloatOps(double, double) -> void | 0 | 10 | Store | ir.cpp:115:12:115:12 | +| FloatOps(double, double) -> void | 0 | 11 | VariableAddress[x] | ir.cpp:117:9:117:9 | +| FloatOps(double, double) -> void | 0 | 12 | Load | ir.cpp:117:9:117:9 | +| FloatOps(double, double) -> void | 0 | 13 | VariableAddress[y] | ir.cpp:117:13:117:13 | +| FloatOps(double, double) -> void | 0 | 14 | Load | ir.cpp:117:13:117:13 | +| FloatOps(double, double) -> void | 0 | 15 | Add | ir.cpp:117:9:117:13 | +| FloatOps(double, double) -> void | 0 | 16 | VariableAddress[z] | ir.cpp:117:5:117:5 | +| FloatOps(double, double) -> void | 0 | 17 | Store | ir.cpp:117:5:117:13 | +| FloatOps(double, double) -> void | 0 | 18 | VariableAddress[x] | ir.cpp:118:9:118:9 | +| FloatOps(double, double) -> void | 0 | 19 | Load | ir.cpp:118:9:118:9 | +| FloatOps(double, double) -> void | 0 | 20 | VariableAddress[y] | ir.cpp:118:13:118:13 | +| FloatOps(double, double) -> void | 0 | 21 | Load | ir.cpp:118:13:118:13 | +| FloatOps(double, double) -> void | 0 | 22 | Sub | ir.cpp:118:9:118:13 | +| FloatOps(double, double) -> void | 0 | 23 | VariableAddress[z] | ir.cpp:118:5:118:5 | +| FloatOps(double, double) -> void | 0 | 24 | Store | ir.cpp:118:5:118:13 | +| FloatOps(double, double) -> void | 0 | 25 | VariableAddress[x] | ir.cpp:119:9:119:9 | +| FloatOps(double, double) -> void | 0 | 26 | Load | ir.cpp:119:9:119:9 | +| FloatOps(double, double) -> void | 0 | 27 | VariableAddress[y] | ir.cpp:119:13:119:13 | +| FloatOps(double, double) -> void | 0 | 28 | Load | ir.cpp:119:13:119:13 | +| FloatOps(double, double) -> void | 0 | 29 | Mul | ir.cpp:119:9:119:13 | +| FloatOps(double, double) -> void | 0 | 30 | VariableAddress[z] | ir.cpp:119:5:119:5 | +| FloatOps(double, double) -> void | 0 | 31 | Store | ir.cpp:119:5:119:13 | +| FloatOps(double, double) -> void | 0 | 32 | VariableAddress[x] | ir.cpp:120:9:120:9 | +| FloatOps(double, double) -> void | 0 | 33 | Load | ir.cpp:120:9:120:9 | +| FloatOps(double, double) -> void | 0 | 34 | VariableAddress[y] | ir.cpp:120:13:120:13 | +| FloatOps(double, double) -> void | 0 | 35 | Load | ir.cpp:120:13:120:13 | +| FloatOps(double, double) -> void | 0 | 36 | Div | ir.cpp:120:9:120:13 | +| FloatOps(double, double) -> void | 0 | 37 | VariableAddress[z] | ir.cpp:120:5:120:5 | +| FloatOps(double, double) -> void | 0 | 38 | Store | ir.cpp:120:5:120:13 | +| FloatOps(double, double) -> void | 0 | 39 | VariableAddress[x] | ir.cpp:122:9:122:9 | +| FloatOps(double, double) -> void | 0 | 40 | Load | ir.cpp:122:9:122:9 | +| FloatOps(double, double) -> void | 0 | 41 | VariableAddress[z] | ir.cpp:122:5:122:5 | +| FloatOps(double, double) -> void | 0 | 42 | Store | ir.cpp:122:5:122:9 | +| FloatOps(double, double) -> void | 0 | 43 | VariableAddress[x] | ir.cpp:124:10:124:10 | +| FloatOps(double, double) -> void | 0 | 44 | Load | ir.cpp:124:10:124:10 | +| FloatOps(double, double) -> void | 0 | 45 | VariableAddress[z] | ir.cpp:124:5:124:5 | +| FloatOps(double, double) -> void | 0 | 46 | Load | ir.cpp:124:5:124:10 | +| FloatOps(double, double) -> void | 0 | 47 | Add | ir.cpp:124:5:124:10 | +| FloatOps(double, double) -> void | 0 | 48 | Store | ir.cpp:124:5:124:10 | +| FloatOps(double, double) -> void | 0 | 49 | VariableAddress[x] | ir.cpp:125:10:125:10 | +| FloatOps(double, double) -> void | 0 | 50 | Load | ir.cpp:125:10:125:10 | +| FloatOps(double, double) -> void | 0 | 51 | VariableAddress[z] | ir.cpp:125:5:125:5 | +| FloatOps(double, double) -> void | 0 | 52 | Load | ir.cpp:125:5:125:10 | +| FloatOps(double, double) -> void | 0 | 53 | Sub | ir.cpp:125:5:125:10 | +| FloatOps(double, double) -> void | 0 | 54 | Store | ir.cpp:125:5:125:10 | +| FloatOps(double, double) -> void | 0 | 55 | VariableAddress[x] | ir.cpp:126:10:126:10 | +| FloatOps(double, double) -> void | 0 | 56 | Load | ir.cpp:126:10:126:10 | +| FloatOps(double, double) -> void | 0 | 57 | VariableAddress[z] | ir.cpp:126:5:126:5 | +| FloatOps(double, double) -> void | 0 | 58 | Load | ir.cpp:126:5:126:10 | +| FloatOps(double, double) -> void | 0 | 59 | Mul | ir.cpp:126:5:126:10 | +| FloatOps(double, double) -> void | 0 | 60 | Store | ir.cpp:126:5:126:10 | +| FloatOps(double, double) -> void | 0 | 61 | VariableAddress[x] | ir.cpp:127:10:127:10 | +| FloatOps(double, double) -> void | 0 | 62 | Load | ir.cpp:127:10:127:10 | +| FloatOps(double, double) -> void | 0 | 63 | VariableAddress[z] | ir.cpp:127:5:127:5 | +| FloatOps(double, double) -> void | 0 | 64 | Load | ir.cpp:127:5:127:10 | +| FloatOps(double, double) -> void | 0 | 65 | Div | ir.cpp:127:5:127:10 | +| FloatOps(double, double) -> void | 0 | 66 | Store | ir.cpp:127:5:127:10 | +| FloatOps(double, double) -> void | 0 | 67 | VariableAddress[x] | ir.cpp:129:10:129:10 | +| FloatOps(double, double) -> void | 0 | 68 | Load | ir.cpp:129:10:129:10 | +| FloatOps(double, double) -> void | 0 | 69 | CopyValue | ir.cpp:129:9:129:10 | +| FloatOps(double, double) -> void | 0 | 70 | VariableAddress[z] | ir.cpp:129:5:129:5 | +| FloatOps(double, double) -> void | 0 | 71 | Store | ir.cpp:129:5:129:10 | +| FloatOps(double, double) -> void | 0 | 72 | VariableAddress[x] | ir.cpp:130:10:130:10 | +| FloatOps(double, double) -> void | 0 | 73 | Load | ir.cpp:130:10:130:10 | +| FloatOps(double, double) -> void | 0 | 74 | Negate | ir.cpp:130:9:130:10 | +| FloatOps(double, double) -> void | 0 | 75 | VariableAddress[z] | ir.cpp:130:5:130:5 | +| FloatOps(double, double) -> void | 0 | 76 | Store | ir.cpp:130:5:130:10 | +| FloatOps(double, double) -> void | 0 | 77 | NoOp | ir.cpp:131:1:131:1 | +| FloatOps(double, double) -> void | 0 | 78 | ReturnVoid | ir.cpp:114:6:114:13 | +| FloatOps(double, double) -> void | 0 | 79 | UnmodeledUse | ir.cpp:114:6:114:13 | +| FloatOps(double, double) -> void | 0 | 80 | ExitFunction | ir.cpp:114:6:114:13 | +| Foo() -> void | 0 | 0 | EnterFunction | ir.cpp:43:6:43:8 | +| Foo() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:43:6:43:8 | +| Foo() -> void | 0 | 2 | VariableAddress[x] | ir.cpp:44:9:44:9 | +| Foo() -> void | 0 | 3 | Constant[17] | ir.cpp:44:13:44:18 | +| Foo() -> void | 0 | 4 | Store | ir.cpp:44:13:44:18 | +| Foo() -> void | 0 | 5 | VariableAddress[y] | ir.cpp:45:11:45:11 | +| Foo() -> void | 0 | 6 | Constant[7] | ir.cpp:45:15:45:15 | +| Foo() -> void | 0 | 7 | Store | ir.cpp:45:15:45:15 | +| Foo() -> void | 0 | 8 | VariableAddress[x] | ir.cpp:46:9:46:9 | +| Foo() -> void | 0 | 9 | Load | ir.cpp:46:9:46:9 | +| Foo() -> void | 0 | 10 | VariableAddress[y] | ir.cpp:46:13:46:13 | +| Foo() -> void | 0 | 11 | Load | ir.cpp:46:13:46:13 | +| Foo() -> void | 0 | 12 | Convert | ir.cpp:46:13:46:13 | +| Foo() -> void | 0 | 13 | Add | ir.cpp:46:9:46:13 | +| Foo() -> void | 0 | 14 | Convert | ir.cpp:46:9:46:13 | +| Foo() -> void | 0 | 15 | VariableAddress[y] | ir.cpp:46:5:46:5 | +| Foo() -> void | 0 | 16 | Store | ir.cpp:46:5:46:13 | +| Foo() -> void | 0 | 17 | VariableAddress[x] | ir.cpp:47:9:47:9 | +| Foo() -> void | 0 | 18 | Load | ir.cpp:47:9:47:9 | +| Foo() -> void | 0 | 19 | VariableAddress[y] | ir.cpp:47:13:47:13 | +| Foo() -> void | 0 | 20 | Load | ir.cpp:47:13:47:13 | +| Foo() -> void | 0 | 21 | Convert | ir.cpp:47:13:47:13 | +| Foo() -> void | 0 | 22 | Mul | ir.cpp:47:9:47:13 | +| Foo() -> void | 0 | 23 | VariableAddress[x] | ir.cpp:47:5:47:5 | +| Foo() -> void | 0 | 24 | Store | ir.cpp:47:5:47:13 | +| Foo() -> void | 0 | 25 | NoOp | ir.cpp:48:1:48:1 | +| Foo() -> void | 0 | 26 | ReturnVoid | ir.cpp:43:6:43:8 | +| Foo() -> void | 0 | 27 | UnmodeledUse | ir.cpp:43:6:43:8 | +| Foo() -> void | 0 | 28 | ExitFunction | ir.cpp:43:6:43:8 | +| For_Break() -> void | 0 | 0 | EnterFunction | ir.cpp:317:6:317:14 | +| For_Break() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:317:6:317:14 | +| For_Break() -> void | 0 | 2 | VariableAddress[i] | ir.cpp:318:14:318:14 | +| For_Break() -> void | 0 | 3 | Constant[0] | ir.cpp:318:17:318:18 | +| For_Break() -> void | 0 | 4 | Store | ir.cpp:318:17:318:18 | +| For_Break() -> void | 1 | 0 | Phi | ir.cpp:318:21:318:21 | +| For_Break() -> void | 1 | 1 | VariableAddress[i] | ir.cpp:318:21:318:21 | +| For_Break() -> void | 1 | 2 | Load | ir.cpp:318:21:318:21 | +| For_Break() -> void | 1 | 3 | Constant[10] | ir.cpp:318:25:318:26 | +| For_Break() -> void | 1 | 4 | CompareLT | ir.cpp:318:21:318:26 | +| For_Break() -> void | 1 | 5 | ConditionalBranch | ir.cpp:318:21:318:26 | +| For_Break() -> void | 2 | 0 | Constant[1] | ir.cpp:318:34:318:34 | +| For_Break() -> void | 2 | 1 | VariableAddress[i] | ir.cpp:318:29:318:29 | +| For_Break() -> void | 2 | 2 | Load | ir.cpp:318:29:318:34 | +| For_Break() -> void | 2 | 3 | Add | ir.cpp:318:29:318:34 | +| For_Break() -> void | 2 | 4 | Store | ir.cpp:318:29:318:34 | +| For_Break() -> void | 3 | 0 | VariableAddress[i] | ir.cpp:319:13:319:13 | +| For_Break() -> void | 3 | 1 | Load | ir.cpp:319:13:319:13 | +| For_Break() -> void | 3 | 2 | Constant[5] | ir.cpp:319:18:319:18 | +| For_Break() -> void | 3 | 3 | CompareEQ | ir.cpp:319:13:319:18 | +| For_Break() -> void | 3 | 4 | ConditionalBranch | ir.cpp:319:13:319:18 | +| For_Break() -> void | 4 | 0 | NoOp | ir.cpp:320:13:320:18 | +| For_Break() -> void | 5 | 0 | NoOp | ir.cpp:322:5:322:5 | +| For_Break() -> void | 5 | 1 | NoOp | ir.cpp:323:1:323:1 | +| For_Break() -> void | 5 | 2 | ReturnVoid | ir.cpp:317:6:317:14 | +| For_Break() -> void | 5 | 3 | UnmodeledUse | ir.cpp:317:6:317:14 | +| For_Break() -> void | 5 | 4 | ExitFunction | ir.cpp:317:6:317:14 | +| For_Condition() -> void | 0 | 0 | EnterFunction | ir.cpp:278:6:278:18 | +| For_Condition() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:278:6:278:18 | +| For_Condition() -> void | 0 | 2 | VariableAddress[i] | ir.cpp:279:9:279:9 | +| For_Condition() -> void | 0 | 3 | Constant[0] | ir.cpp:279:12:279:13 | +| For_Condition() -> void | 0 | 4 | Store | ir.cpp:279:12:279:13 | +| For_Condition() -> void | 1 | 0 | VariableAddress[i] | ir.cpp:280:12:280:12 | +| For_Condition() -> void | 1 | 1 | Load | ir.cpp:280:12:280:12 | +| For_Condition() -> void | 1 | 2 | Constant[10] | ir.cpp:280:16:280:17 | +| For_Condition() -> void | 1 | 3 | CompareLT | ir.cpp:280:12:280:17 | +| For_Condition() -> void | 1 | 4 | ConditionalBranch | ir.cpp:280:12:280:17 | +| For_Condition() -> void | 2 | 0 | NoOp | ir.cpp:281:9:281:9 | +| For_Condition() -> void | 3 | 0 | NoOp | ir.cpp:283:1:283:1 | +| For_Condition() -> void | 3 | 1 | ReturnVoid | ir.cpp:278:6:278:18 | +| For_Condition() -> void | 3 | 2 | UnmodeledUse | ir.cpp:278:6:278:18 | +| For_Condition() -> void | 3 | 3 | ExitFunction | ir.cpp:278:6:278:18 | +| For_ConditionUpdate() -> void | 0 | 0 | EnterFunction | ir.cpp:304:6:304:24 | +| For_ConditionUpdate() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:304:6:304:24 | +| For_ConditionUpdate() -> void | 0 | 2 | VariableAddress[i] | ir.cpp:305:9:305:9 | +| For_ConditionUpdate() -> void | 0 | 3 | Constant[0] | ir.cpp:305:12:305:13 | +| For_ConditionUpdate() -> void | 0 | 4 | Store | ir.cpp:305:12:305:13 | +| For_ConditionUpdate() -> void | 1 | 0 | Phi | ir.cpp:306:12:306:12 | +| For_ConditionUpdate() -> void | 1 | 1 | VariableAddress[i] | ir.cpp:306:12:306:12 | +| For_ConditionUpdate() -> void | 1 | 2 | Load | ir.cpp:306:12:306:12 | +| For_ConditionUpdate() -> void | 1 | 3 | Constant[10] | ir.cpp:306:16:306:17 | +| For_ConditionUpdate() -> void | 1 | 4 | CompareLT | ir.cpp:306:12:306:17 | +| For_ConditionUpdate() -> void | 1 | 5 | ConditionalBranch | ir.cpp:306:12:306:17 | +| For_ConditionUpdate() -> void | 2 | 0 | NoOp | ir.cpp:307:9:307:9 | +| For_ConditionUpdate() -> void | 2 | 1 | Constant[1] | ir.cpp:306:25:306:25 | +| For_ConditionUpdate() -> void | 2 | 2 | VariableAddress[i] | ir.cpp:306:20:306:20 | +| For_ConditionUpdate() -> void | 2 | 3 | Load | ir.cpp:306:20:306:25 | +| For_ConditionUpdate() -> void | 2 | 4 | Add | ir.cpp:306:20:306:25 | +| For_ConditionUpdate() -> void | 2 | 5 | Store | ir.cpp:306:20:306:25 | +| For_ConditionUpdate() -> void | 3 | 0 | NoOp | ir.cpp:309:1:309:1 | +| For_ConditionUpdate() -> void | 3 | 1 | ReturnVoid | ir.cpp:304:6:304:24 | +| For_ConditionUpdate() -> void | 3 | 2 | UnmodeledUse | ir.cpp:304:6:304:24 | +| For_ConditionUpdate() -> void | 3 | 3 | ExitFunction | ir.cpp:304:6:304:24 | +| For_Continue_NoUpdate() -> void | 0 | 0 | EnterFunction | ir.cpp:333:6:333:26 | +| For_Continue_NoUpdate() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:333:6:333:26 | +| For_Continue_NoUpdate() -> void | 0 | 2 | VariableAddress[i] | ir.cpp:334:14:334:14 | +| For_Continue_NoUpdate() -> void | 0 | 3 | Constant[0] | ir.cpp:334:17:334:18 | +| For_Continue_NoUpdate() -> void | 0 | 4 | Store | ir.cpp:334:17:334:18 | +| For_Continue_NoUpdate() -> void | 1 | 0 | VariableAddress[i] | ir.cpp:334:21:334:21 | +| For_Continue_NoUpdate() -> void | 1 | 1 | Load | ir.cpp:334:21:334:21 | +| For_Continue_NoUpdate() -> void | 1 | 2 | Constant[10] | ir.cpp:334:25:334:26 | +| For_Continue_NoUpdate() -> void | 1 | 3 | CompareLT | ir.cpp:334:21:334:26 | +| For_Continue_NoUpdate() -> void | 1 | 4 | ConditionalBranch | ir.cpp:334:21:334:26 | +| For_Continue_NoUpdate() -> void | 2 | 0 | VariableAddress[i] | ir.cpp:335:13:335:13 | +| For_Continue_NoUpdate() -> void | 2 | 1 | Load | ir.cpp:335:13:335:13 | +| For_Continue_NoUpdate() -> void | 2 | 2 | Constant[5] | ir.cpp:335:18:335:18 | +| For_Continue_NoUpdate() -> void | 2 | 3 | CompareEQ | ir.cpp:335:13:335:18 | +| For_Continue_NoUpdate() -> void | 2 | 4 | ConditionalBranch | ir.cpp:335:13:335:18 | +| For_Continue_NoUpdate() -> void | 3 | 0 | NoOp | ir.cpp:336:13:336:21 | +| For_Continue_NoUpdate() -> void | 4 | 0 | NoOp | ir.cpp:334:5:334:5 | +| For_Continue_NoUpdate() -> void | 5 | 0 | NoOp | ir.cpp:339:1:339:1 | +| For_Continue_NoUpdate() -> void | 5 | 1 | ReturnVoid | ir.cpp:333:6:333:26 | +| For_Continue_NoUpdate() -> void | 5 | 2 | UnmodeledUse | ir.cpp:333:6:333:26 | +| For_Continue_NoUpdate() -> void | 5 | 3 | ExitFunction | ir.cpp:333:6:333:26 | +| For_Continue_Update() -> void | 0 | 0 | EnterFunction | ir.cpp:325:6:325:24 | +| For_Continue_Update() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:325:6:325:24 | +| For_Continue_Update() -> void | 0 | 2 | VariableAddress[i] | ir.cpp:326:14:326:14 | +| For_Continue_Update() -> void | 0 | 3 | Constant[0] | ir.cpp:326:17:326:18 | +| For_Continue_Update() -> void | 0 | 4 | Store | ir.cpp:326:17:326:18 | +| For_Continue_Update() -> void | 1 | 0 | Phi | ir.cpp:326:21:326:21 | +| For_Continue_Update() -> void | 1 | 1 | VariableAddress[i] | ir.cpp:326:21:326:21 | +| For_Continue_Update() -> void | 1 | 2 | Load | ir.cpp:326:21:326:21 | +| For_Continue_Update() -> void | 1 | 3 | Constant[10] | ir.cpp:326:25:326:26 | +| For_Continue_Update() -> void | 1 | 4 | CompareLT | ir.cpp:326:21:326:26 | +| For_Continue_Update() -> void | 1 | 5 | ConditionalBranch | ir.cpp:326:21:326:26 | +| For_Continue_Update() -> void | 2 | 0 | VariableAddress[i] | ir.cpp:327:13:327:13 | +| For_Continue_Update() -> void | 2 | 1 | Load | ir.cpp:327:13:327:13 | +| For_Continue_Update() -> void | 2 | 2 | Constant[5] | ir.cpp:327:18:327:18 | +| For_Continue_Update() -> void | 2 | 3 | CompareEQ | ir.cpp:327:13:327:18 | +| For_Continue_Update() -> void | 2 | 4 | ConditionalBranch | ir.cpp:327:13:327:18 | +| For_Continue_Update() -> void | 3 | 0 | NoOp | ir.cpp:328:13:328:21 | +| For_Continue_Update() -> void | 4 | 0 | NoOp | ir.cpp:326:5:326:5 | +| For_Continue_Update() -> void | 4 | 1 | Constant[1] | ir.cpp:326:34:326:34 | +| For_Continue_Update() -> void | 4 | 2 | VariableAddress[i] | ir.cpp:326:29:326:29 | +| For_Continue_Update() -> void | 4 | 3 | Load | ir.cpp:326:29:326:34 | +| For_Continue_Update() -> void | 4 | 4 | Add | ir.cpp:326:29:326:34 | +| For_Continue_Update() -> void | 4 | 5 | Store | ir.cpp:326:29:326:34 | +| For_Continue_Update() -> void | 5 | 0 | NoOp | ir.cpp:331:1:331:1 | +| For_Continue_Update() -> void | 5 | 1 | ReturnVoid | ir.cpp:325:6:325:24 | +| For_Continue_Update() -> void | 5 | 2 | UnmodeledUse | ir.cpp:325:6:325:24 | +| For_Continue_Update() -> void | 5 | 3 | ExitFunction | ir.cpp:325:6:325:24 | +| For_Empty() -> void | 0 | 0 | EnterFunction | ir.cpp:265:6:265:14 | +| For_Empty() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:265:6:265:14 | +| For_Empty() -> void | 0 | 2 | VariableAddress[j] | ir.cpp:266:9:266:9 | +| For_Empty() -> void | 0 | 3 | Uninitialized | ir.cpp:266:9:266:9 | +| For_Empty() -> void | 0 | 4 | Store | ir.cpp:266:9:266:9 | +| For_Empty() -> void | 1 | 0 | ReturnVoid | ir.cpp:265:6:265:14 | +| For_Empty() -> void | 1 | 1 | UnmodeledUse | ir.cpp:265:6:265:14 | +| For_Empty() -> void | 1 | 2 | ExitFunction | ir.cpp:265:6:265:14 | +| For_Empty() -> void | 2 | 0 | NoOp | ir.cpp:268:9:268:9 | +| For_Init() -> void | 0 | 0 | EnterFunction | ir.cpp:272:6:272:13 | +| For_Init() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:272:6:272:13 | +| For_Init() -> void | 0 | 2 | VariableAddress[i] | ir.cpp:273:14:273:14 | +| For_Init() -> void | 0 | 3 | Constant[0] | ir.cpp:273:17:273:18 | +| For_Init() -> void | 0 | 4 | Store | ir.cpp:273:17:273:18 | +| For_Init() -> void | 1 | 0 | ReturnVoid | ir.cpp:272:6:272:13 | +| For_Init() -> void | 1 | 1 | UnmodeledUse | ir.cpp:272:6:272:13 | +| For_Init() -> void | 1 | 2 | ExitFunction | ir.cpp:272:6:272:13 | +| For_Init() -> void | 2 | 0 | NoOp | ir.cpp:274:9:274:9 | +| For_InitCondition() -> void | 0 | 0 | EnterFunction | ir.cpp:292:6:292:22 | +| For_InitCondition() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:292:6:292:22 | +| For_InitCondition() -> void | 0 | 2 | VariableAddress[i] | ir.cpp:293:14:293:14 | +| For_InitCondition() -> void | 0 | 3 | Constant[0] | ir.cpp:293:17:293:18 | +| For_InitCondition() -> void | 0 | 4 | Store | ir.cpp:293:17:293:18 | +| For_InitCondition() -> void | 1 | 0 | VariableAddress[i] | ir.cpp:293:21:293:21 | +| For_InitCondition() -> void | 1 | 1 | Load | ir.cpp:293:21:293:21 | +| For_InitCondition() -> void | 1 | 2 | Constant[10] | ir.cpp:293:25:293:26 | +| For_InitCondition() -> void | 1 | 3 | CompareLT | ir.cpp:293:21:293:26 | +| For_InitCondition() -> void | 1 | 4 | ConditionalBranch | ir.cpp:293:21:293:26 | +| For_InitCondition() -> void | 2 | 0 | NoOp | ir.cpp:294:9:294:9 | +| For_InitCondition() -> void | 3 | 0 | NoOp | ir.cpp:296:1:296:1 | +| For_InitCondition() -> void | 3 | 1 | ReturnVoid | ir.cpp:292:6:292:22 | +| For_InitCondition() -> void | 3 | 2 | UnmodeledUse | ir.cpp:292:6:292:22 | +| For_InitCondition() -> void | 3 | 3 | ExitFunction | ir.cpp:292:6:292:22 | +| For_InitConditionUpdate() -> void | 0 | 0 | EnterFunction | ir.cpp:311:6:311:28 | +| For_InitConditionUpdate() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:311:6:311:28 | +| For_InitConditionUpdate() -> void | 0 | 2 | VariableAddress[i] | ir.cpp:312:14:312:14 | +| For_InitConditionUpdate() -> void | 0 | 3 | Constant[0] | ir.cpp:312:17:312:18 | +| For_InitConditionUpdate() -> void | 0 | 4 | Store | ir.cpp:312:17:312:18 | +| For_InitConditionUpdate() -> void | 1 | 0 | Phi | ir.cpp:312:21:312:21 | +| For_InitConditionUpdate() -> void | 1 | 1 | VariableAddress[i] | ir.cpp:312:21:312:21 | +| For_InitConditionUpdate() -> void | 1 | 2 | Load | ir.cpp:312:21:312:21 | +| For_InitConditionUpdate() -> void | 1 | 3 | Constant[10] | ir.cpp:312:25:312:26 | +| For_InitConditionUpdate() -> void | 1 | 4 | CompareLT | ir.cpp:312:21:312:26 | +| For_InitConditionUpdate() -> void | 1 | 5 | ConditionalBranch | ir.cpp:312:21:312:26 | +| For_InitConditionUpdate() -> void | 2 | 0 | NoOp | ir.cpp:313:9:313:9 | +| For_InitConditionUpdate() -> void | 2 | 1 | Constant[1] | ir.cpp:312:34:312:34 | +| For_InitConditionUpdate() -> void | 2 | 2 | VariableAddress[i] | ir.cpp:312:29:312:29 | +| For_InitConditionUpdate() -> void | 2 | 3 | Load | ir.cpp:312:29:312:34 | +| For_InitConditionUpdate() -> void | 2 | 4 | Add | ir.cpp:312:29:312:34 | +| For_InitConditionUpdate() -> void | 2 | 5 | Store | ir.cpp:312:29:312:34 | +| For_InitConditionUpdate() -> void | 3 | 0 | NoOp | ir.cpp:315:1:315:1 | +| For_InitConditionUpdate() -> void | 3 | 1 | ReturnVoid | ir.cpp:311:6:311:28 | +| For_InitConditionUpdate() -> void | 3 | 2 | UnmodeledUse | ir.cpp:311:6:311:28 | +| For_InitConditionUpdate() -> void | 3 | 3 | ExitFunction | ir.cpp:311:6:311:28 | +| For_InitUpdate() -> void | 0 | 0 | EnterFunction | ir.cpp:298:6:298:19 | +| For_InitUpdate() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:298:6:298:19 | +| For_InitUpdate() -> void | 0 | 2 | VariableAddress[i] | ir.cpp:299:14:299:14 | +| For_InitUpdate() -> void | 0 | 3 | Constant[0] | ir.cpp:299:17:299:18 | +| For_InitUpdate() -> void | 0 | 4 | Store | ir.cpp:299:17:299:18 | +| For_InitUpdate() -> void | 1 | 0 | ReturnVoid | ir.cpp:298:6:298:19 | +| For_InitUpdate() -> void | 1 | 1 | UnmodeledUse | ir.cpp:298:6:298:19 | +| For_InitUpdate() -> void | 1 | 2 | ExitFunction | ir.cpp:298:6:298:19 | +| For_InitUpdate() -> void | 2 | 0 | Phi | ir.cpp:300:9:300:9 | +| For_InitUpdate() -> void | 2 | 1 | NoOp | ir.cpp:300:9:300:9 | +| For_InitUpdate() -> void | 2 | 2 | Constant[1] | ir.cpp:299:27:299:27 | +| For_InitUpdate() -> void | 2 | 3 | VariableAddress[i] | ir.cpp:299:22:299:22 | +| For_InitUpdate() -> void | 2 | 4 | Load | ir.cpp:299:22:299:27 | +| For_InitUpdate() -> void | 2 | 5 | Add | ir.cpp:299:22:299:27 | +| For_InitUpdate() -> void | 2 | 6 | Store | ir.cpp:299:22:299:27 | +| For_Update() -> void | 0 | 0 | EnterFunction | ir.cpp:285:6:285:15 | +| For_Update() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:285:6:285:15 | +| For_Update() -> void | 0 | 2 | VariableAddress[i] | ir.cpp:286:9:286:9 | +| For_Update() -> void | 0 | 3 | Constant[0] | ir.cpp:286:12:286:13 | +| For_Update() -> void | 0 | 4 | Store | ir.cpp:286:12:286:13 | +| For_Update() -> void | 1 | 0 | ReturnVoid | ir.cpp:285:6:285:15 | +| For_Update() -> void | 1 | 1 | UnmodeledUse | ir.cpp:285:6:285:15 | +| For_Update() -> void | 1 | 2 | ExitFunction | ir.cpp:285:6:285:15 | +| For_Update() -> void | 2 | 0 | Phi | ir.cpp:288:9:288:9 | +| For_Update() -> void | 2 | 1 | NoOp | ir.cpp:288:9:288:9 | +| For_Update() -> void | 2 | 2 | Constant[1] | ir.cpp:287:18:287:18 | +| For_Update() -> void | 2 | 3 | VariableAddress[i] | ir.cpp:287:13:287:13 | +| For_Update() -> void | 2 | 4 | Load | ir.cpp:287:13:287:18 | +| For_Update() -> void | 2 | 5 | Add | ir.cpp:287:13:287:18 | +| For_Update() -> void | 2 | 6 | Store | ir.cpp:287:13:287:18 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 0 | EnterFunction | ir.cpp:883:6:883:23 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:883:6:883:23 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 2 | InitializeParameter[pfn] | ir.cpp:883:30:883:32 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 3 | VariableAddress[pfn] | ir.cpp:883:30:883:32 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 4 | Store | ir.cpp:883:30:883:32 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 5 | InitializeParameter[p] | ir.cpp:883:47:883:47 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 6 | VariableAddress[p] | ir.cpp:883:47:883:47 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 7 | Store | ir.cpp:883:47:883:47 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 8 | VariableAddress[pfn] | ir.cpp:884:14:884:16 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 9 | Load | ir.cpp:884:14:884:16 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 10 | Convert | ir.cpp:884:7:884:16 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 11 | VariableAddress[p] | ir.cpp:884:3:884:3 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 12 | Store | ir.cpp:884:3:884:16 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 13 | VariableAddress[p] | ir.cpp:885:22:885:22 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 14 | Load | ir.cpp:885:22:885:22 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 15 | Convert | ir.cpp:885:9:885:22 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 16 | VariableAddress[pfn] | ir.cpp:885:3:885:5 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 17 | Store | ir.cpp:885:3:885:22 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 18 | NoOp | ir.cpp:886:1:886:1 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 19 | ReturnVoid | ir.cpp:883:6:883:23 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 20 | UnmodeledUse | ir.cpp:883:6:883:23 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 21 | ExitFunction | ir.cpp:883:6:883:23 | +| FunctionReferences() -> void | 0 | 0 | EnterFunction | ir.cpp:697:6:697:23 | +| FunctionReferences() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:697:6:697:23 | +| FunctionReferences() -> void | 0 | 2 | VariableAddress[rfn] | ir.cpp:698:8:698:10 | +| FunctionReferences() -> void | 0 | 3 | FunctionAddress[FuncPtrTarget] | ir.cpp:698:20:698:32 | +| FunctionReferences() -> void | 0 | 4 | Store | ir.cpp:698:20:698:32 | +| FunctionReferences() -> void | 0 | 5 | VariableAddress[pfn] | ir.cpp:699:8:699:10 | +| FunctionReferences() -> void | 0 | 6 | VariableAddress[rfn] | ir.cpp:699:20:699:22 | +| FunctionReferences() -> void | 0 | 7 | Load | ir.cpp:699:20:699:22 | +| FunctionReferences() -> void | 0 | 8 | Store | ir.cpp:699:20:699:22 | +| FunctionReferences() -> void | 0 | 9 | VariableAddress[rfn] | ir.cpp:700:3:700:5 | +| FunctionReferences() -> void | 0 | 10 | Load | ir.cpp:700:3:700:5 | +| FunctionReferences() -> void | 0 | 11 | Constant[5] | ir.cpp:700:7:700:7 | +| FunctionReferences() -> void | 0 | 12 | Invoke | ir.cpp:700:3:700:8 | +| FunctionReferences() -> void | 0 | 13 | NoOp | ir.cpp:701:1:701:1 | +| FunctionReferences() -> void | 0 | 14 | ReturnVoid | ir.cpp:697:6:697:23 | +| FunctionReferences() -> void | 0 | 15 | UnmodeledUse | ir.cpp:697:6:697:23 | +| FunctionReferences() -> void | 0 | 16 | ExitFunction | ir.cpp:697:6:697:23 | +| HierarchyConversions() -> void | 0 | 0 | EnterFunction | ir.cpp:799:6:799:25 | +| HierarchyConversions() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:799:6:799:25 | +| HierarchyConversions() -> void | 0 | 2 | VariableAddress[b] | ir.cpp:800:8:800:8 | +| HierarchyConversions() -> void | 0 | 3 | FunctionAddress[Base] | ir.cpp:800:8:800:8 | +| HierarchyConversions() -> void | 0 | 4 | Invoke | ir.cpp:800:8:800:8 | +| HierarchyConversions() -> void | 0 | 5 | VariableAddress[m] | ir.cpp:801:10:801:10 | +| HierarchyConversions() -> void | 0 | 6 | FunctionAddress[Middle] | ir.cpp:801:10:801:10 | +| HierarchyConversions() -> void | 0 | 7 | Invoke | ir.cpp:801:10:801:10 | +| HierarchyConversions() -> void | 0 | 8 | VariableAddress[d] | ir.cpp:802:11:802:11 | +| HierarchyConversions() -> void | 0 | 9 | FunctionAddress[Derived] | ir.cpp:802:11:802:11 | +| HierarchyConversions() -> void | 0 | 10 | Invoke | ir.cpp:802:11:802:11 | +| HierarchyConversions() -> void | 0 | 11 | VariableAddress[pb] | ir.cpp:804:9:804:10 | +| HierarchyConversions() -> void | 0 | 12 | VariableAddress[b] | ir.cpp:804:15:804:15 | +| HierarchyConversions() -> void | 0 | 13 | Store | ir.cpp:804:14:804:15 | +| HierarchyConversions() -> void | 0 | 14 | VariableAddress[pm] | ir.cpp:805:11:805:12 | +| HierarchyConversions() -> void | 0 | 15 | VariableAddress[m] | ir.cpp:805:17:805:17 | +| HierarchyConversions() -> void | 0 | 16 | Store | ir.cpp:805:16:805:17 | +| HierarchyConversions() -> void | 0 | 17 | VariableAddress[pd] | ir.cpp:806:12:806:13 | +| HierarchyConversions() -> void | 0 | 18 | VariableAddress[d] | ir.cpp:806:18:806:18 | +| HierarchyConversions() -> void | 0 | 19 | Store | ir.cpp:806:17:806:18 | +| HierarchyConversions() -> void | 0 | 20 | VariableAddress[b] | ir.cpp:808:3:808:3 | +| HierarchyConversions() -> void | 0 | 21 | FunctionAddress[operator=] | ir.cpp:808:5:808:5 | +| HierarchyConversions() -> void | 0 | 22 | VariableAddress[m] | ir.cpp:808:7:808:7 | +| HierarchyConversions() -> void | 0 | 23 | ConvertToBase[Middle : Base] | ir.cpp:808:7:808:7 | +| HierarchyConversions() -> void | 0 | 24 | Invoke | ir.cpp:808:5:808:5 | +| HierarchyConversions() -> void | 0 | 25 | VariableAddress[b] | ir.cpp:809:3:809:3 | +| HierarchyConversions() -> void | 0 | 26 | FunctionAddress[operator=] | ir.cpp:809:5:809:5 | +| HierarchyConversions() -> void | 0 | 27 | FunctionAddress[Base] | ir.cpp:809:7:809:13 | +| HierarchyConversions() -> void | 0 | 28 | VariableAddress[m] | ir.cpp:809:13:809:13 | +| HierarchyConversions() -> void | 0 | 29 | ConvertToBase[Middle : Base] | ir.cpp:809:13:809:13 | +| HierarchyConversions() -> void | 0 | 30 | Invoke | ir.cpp:809:7:809:13 | +| HierarchyConversions() -> void | 0 | 31 | Convert | ir.cpp:809:7:809:13 | +| HierarchyConversions() -> void | 0 | 32 | Invoke | ir.cpp:809:5:809:5 | +| HierarchyConversions() -> void | 0 | 33 | VariableAddress[b] | ir.cpp:810:3:810:3 | +| HierarchyConversions() -> void | 0 | 34 | FunctionAddress[operator=] | ir.cpp:810:5:810:5 | +| HierarchyConversions() -> void | 0 | 35 | FunctionAddress[Base] | ir.cpp:810:7:810:26 | +| HierarchyConversions() -> void | 0 | 36 | VariableAddress[m] | ir.cpp:810:25:810:25 | +| HierarchyConversions() -> void | 0 | 37 | ConvertToBase[Middle : Base] | ir.cpp:810:25:810:25 | +| HierarchyConversions() -> void | 0 | 38 | Invoke | ir.cpp:810:7:810:26 | +| HierarchyConversions() -> void | 0 | 39 | Convert | ir.cpp:810:7:810:26 | +| HierarchyConversions() -> void | 0 | 40 | Invoke | ir.cpp:810:5:810:5 | +| HierarchyConversions() -> void | 0 | 41 | VariableAddress[pm] | ir.cpp:811:8:811:9 | +| HierarchyConversions() -> void | 0 | 42 | Load | ir.cpp:811:8:811:9 | +| HierarchyConversions() -> void | 0 | 43 | ConvertToBase[Middle : Base] | ir.cpp:811:8:811:9 | +| HierarchyConversions() -> void | 0 | 44 | VariableAddress[pb] | ir.cpp:811:3:811:4 | +| HierarchyConversions() -> void | 0 | 45 | Store | ir.cpp:811:3:811:9 | +| HierarchyConversions() -> void | 0 | 46 | VariableAddress[pm] | ir.cpp:812:15:812:16 | +| HierarchyConversions() -> void | 0 | 47 | Load | ir.cpp:812:15:812:16 | +| HierarchyConversions() -> void | 0 | 48 | ConvertToBase[Middle : Base] | ir.cpp:812:8:812:16 | +| HierarchyConversions() -> void | 0 | 49 | VariableAddress[pb] | ir.cpp:812:3:812:4 | +| HierarchyConversions() -> void | 0 | 50 | Store | ir.cpp:812:3:812:16 | +| HierarchyConversions() -> void | 0 | 51 | VariableAddress[pm] | ir.cpp:813:27:813:28 | +| HierarchyConversions() -> void | 0 | 52 | Load | ir.cpp:813:27:813:28 | +| HierarchyConversions() -> void | 0 | 53 | ConvertToBase[Middle : Base] | ir.cpp:813:8:813:29 | +| HierarchyConversions() -> void | 0 | 54 | VariableAddress[pb] | ir.cpp:813:3:813:4 | +| HierarchyConversions() -> void | 0 | 55 | Store | ir.cpp:813:3:813:29 | +| HierarchyConversions() -> void | 0 | 56 | VariableAddress[pm] | ir.cpp:814:32:814:33 | +| HierarchyConversions() -> void | 0 | 57 | Load | ir.cpp:814:32:814:33 | +| HierarchyConversions() -> void | 0 | 58 | Convert | ir.cpp:814:8:814:34 | +| HierarchyConversions() -> void | 0 | 59 | VariableAddress[pb] | ir.cpp:814:3:814:4 | +| HierarchyConversions() -> void | 0 | 60 | Store | ir.cpp:814:3:814:34 | +| HierarchyConversions() -> void | 0 | 61 | VariableAddress[m] | ir.cpp:816:3:816:3 | +| HierarchyConversions() -> void | 0 | 62 | FunctionAddress[operator=] | ir.cpp:816:5:816:5 | +| HierarchyConversions() -> void | 0 | 63 | VariableAddress[b] | ir.cpp:816:16:816:16 | +| HierarchyConversions() -> void | 0 | 64 | ConvertToDerived[Middle : Base] | ir.cpp:816:7:816:16 | +| HierarchyConversions() -> void | 0 | 65 | Convert | ir.cpp:816:7:816:16 | +| HierarchyConversions() -> void | 0 | 66 | Invoke | ir.cpp:816:5:816:5 | +| HierarchyConversions() -> void | 0 | 67 | VariableAddress[m] | ir.cpp:817:3:817:3 | +| HierarchyConversions() -> void | 0 | 68 | FunctionAddress[operator=] | ir.cpp:817:5:817:5 | +| HierarchyConversions() -> void | 0 | 69 | VariableAddress[b] | ir.cpp:817:28:817:28 | +| HierarchyConversions() -> void | 0 | 70 | ConvertToDerived[Middle : Base] | ir.cpp:817:7:817:29 | +| HierarchyConversions() -> void | 0 | 71 | Convert | ir.cpp:817:7:817:29 | +| HierarchyConversions() -> void | 0 | 72 | Invoke | ir.cpp:817:5:817:5 | +| HierarchyConversions() -> void | 0 | 73 | VariableAddress[pb] | ir.cpp:818:17:818:18 | +| HierarchyConversions() -> void | 0 | 74 | Load | ir.cpp:818:17:818:18 | +| HierarchyConversions() -> void | 0 | 75 | ConvertToDerived[Middle : Base] | ir.cpp:818:8:818:18 | +| HierarchyConversions() -> void | 0 | 76 | VariableAddress[pm] | ir.cpp:818:3:818:4 | +| HierarchyConversions() -> void | 0 | 77 | Store | ir.cpp:818:3:818:18 | +| HierarchyConversions() -> void | 0 | 78 | VariableAddress[pb] | ir.cpp:819:29:819:30 | +| HierarchyConversions() -> void | 0 | 79 | Load | ir.cpp:819:29:819:30 | +| HierarchyConversions() -> void | 0 | 80 | ConvertToDerived[Middle : Base] | ir.cpp:819:8:819:31 | +| HierarchyConversions() -> void | 0 | 81 | VariableAddress[pm] | ir.cpp:819:3:819:4 | +| HierarchyConversions() -> void | 0 | 82 | Store | ir.cpp:819:3:819:31 | +| HierarchyConversions() -> void | 0 | 83 | VariableAddress[pb] | ir.cpp:820:34:820:35 | +| HierarchyConversions() -> void | 0 | 84 | Load | ir.cpp:820:34:820:35 | +| HierarchyConversions() -> void | 0 | 85 | Convert | ir.cpp:820:8:820:36 | +| HierarchyConversions() -> void | 0 | 86 | VariableAddress[pm] | ir.cpp:820:3:820:4 | +| HierarchyConversions() -> void | 0 | 87 | Store | ir.cpp:820:3:820:36 | +| HierarchyConversions() -> void | 0 | 88 | VariableAddress[b] | ir.cpp:822:3:822:3 | +| HierarchyConversions() -> void | 0 | 89 | FunctionAddress[operator=] | ir.cpp:822:5:822:5 | +| HierarchyConversions() -> void | 0 | 90 | VariableAddress[d] | ir.cpp:822:7:822:7 | +| HierarchyConversions() -> void | 0 | 91 | ConvertToBase[Derived : Middle] | ir.cpp:822:7:822:7 | +| HierarchyConversions() -> void | 0 | 92 | ConvertToBase[Middle : Base] | ir.cpp:822:7:822:7 | +| HierarchyConversions() -> void | 0 | 93 | Invoke | ir.cpp:822:5:822:5 | +| HierarchyConversions() -> void | 0 | 94 | VariableAddress[b] | ir.cpp:823:3:823:3 | +| HierarchyConversions() -> void | 0 | 95 | FunctionAddress[operator=] | ir.cpp:823:5:823:5 | +| HierarchyConversions() -> void | 0 | 96 | FunctionAddress[Base] | ir.cpp:823:7:823:13 | +| HierarchyConversions() -> void | 0 | 97 | VariableAddress[d] | ir.cpp:823:13:823:13 | +| HierarchyConversions() -> void | 0 | 98 | ConvertToBase[Derived : Middle] | ir.cpp:823:13:823:13 | +| HierarchyConversions() -> void | 0 | 99 | ConvertToBase[Middle : Base] | ir.cpp:823:13:823:13 | +| HierarchyConversions() -> void | 0 | 100 | Invoke | ir.cpp:823:7:823:13 | +| HierarchyConversions() -> void | 0 | 101 | Convert | ir.cpp:823:7:823:13 | +| HierarchyConversions() -> void | 0 | 102 | Invoke | ir.cpp:823:5:823:5 | +| HierarchyConversions() -> void | 0 | 103 | VariableAddress[b] | ir.cpp:824:3:824:3 | +| HierarchyConversions() -> void | 0 | 104 | FunctionAddress[operator=] | ir.cpp:824:5:824:5 | +| HierarchyConversions() -> void | 0 | 105 | FunctionAddress[Base] | ir.cpp:824:7:824:26 | +| HierarchyConversions() -> void | 0 | 106 | VariableAddress[d] | ir.cpp:824:25:824:25 | +| HierarchyConversions() -> void | 0 | 107 | ConvertToBase[Derived : Middle] | ir.cpp:824:25:824:25 | +| HierarchyConversions() -> void | 0 | 108 | ConvertToBase[Middle : Base] | ir.cpp:824:25:824:25 | +| HierarchyConversions() -> void | 0 | 109 | Invoke | ir.cpp:824:7:824:26 | +| HierarchyConversions() -> void | 0 | 110 | Convert | ir.cpp:824:7:824:26 | +| HierarchyConversions() -> void | 0 | 111 | Invoke | ir.cpp:824:5:824:5 | +| HierarchyConversions() -> void | 0 | 112 | VariableAddress[pd] | ir.cpp:825:8:825:9 | +| HierarchyConversions() -> void | 0 | 113 | Load | ir.cpp:825:8:825:9 | +| HierarchyConversions() -> void | 0 | 114 | ConvertToBase[Derived : Middle] | ir.cpp:825:8:825:9 | +| HierarchyConversions() -> void | 0 | 115 | ConvertToBase[Middle : Base] | ir.cpp:825:8:825:9 | +| HierarchyConversions() -> void | 0 | 116 | VariableAddress[pb] | ir.cpp:825:3:825:4 | +| HierarchyConversions() -> void | 0 | 117 | Store | ir.cpp:825:3:825:9 | +| HierarchyConversions() -> void | 0 | 118 | VariableAddress[pd] | ir.cpp:826:15:826:16 | +| HierarchyConversions() -> void | 0 | 119 | Load | ir.cpp:826:15:826:16 | +| HierarchyConversions() -> void | 0 | 120 | ConvertToBase[Derived : Middle] | ir.cpp:826:15:826:16 | +| HierarchyConversions() -> void | 0 | 121 | ConvertToBase[Middle : Base] | ir.cpp:826:8:826:16 | +| HierarchyConversions() -> void | 0 | 122 | VariableAddress[pb] | ir.cpp:826:3:826:4 | +| HierarchyConversions() -> void | 0 | 123 | Store | ir.cpp:826:3:826:16 | +| HierarchyConversions() -> void | 0 | 124 | VariableAddress[pd] | ir.cpp:827:27:827:28 | +| HierarchyConversions() -> void | 0 | 125 | Load | ir.cpp:827:27:827:28 | +| HierarchyConversions() -> void | 0 | 126 | ConvertToBase[Derived : Middle] | ir.cpp:827:27:827:28 | +| HierarchyConversions() -> void | 0 | 127 | ConvertToBase[Middle : Base] | ir.cpp:827:8:827:29 | +| HierarchyConversions() -> void | 0 | 128 | VariableAddress[pb] | ir.cpp:827:3:827:4 | +| HierarchyConversions() -> void | 0 | 129 | Store | ir.cpp:827:3:827:29 | +| HierarchyConversions() -> void | 0 | 130 | VariableAddress[pd] | ir.cpp:828:32:828:33 | +| HierarchyConversions() -> void | 0 | 131 | Load | ir.cpp:828:32:828:33 | +| HierarchyConversions() -> void | 0 | 132 | Convert | ir.cpp:828:8:828:34 | +| HierarchyConversions() -> void | 0 | 133 | VariableAddress[pb] | ir.cpp:828:3:828:4 | +| HierarchyConversions() -> void | 0 | 134 | Store | ir.cpp:828:3:828:34 | +| HierarchyConversions() -> void | 0 | 135 | VariableAddress[d] | ir.cpp:830:3:830:3 | +| HierarchyConversions() -> void | 0 | 136 | FunctionAddress[operator=] | ir.cpp:830:5:830:5 | +| HierarchyConversions() -> void | 0 | 137 | VariableAddress[b] | ir.cpp:830:17:830:17 | +| HierarchyConversions() -> void | 0 | 138 | ConvertToDerived[Middle : Base] | ir.cpp:830:17:830:17 | +| HierarchyConversions() -> void | 0 | 139 | ConvertToDerived[Derived : Middle] | ir.cpp:830:7:830:17 | +| HierarchyConversions() -> void | 0 | 140 | Convert | ir.cpp:830:7:830:17 | +| HierarchyConversions() -> void | 0 | 141 | Invoke | ir.cpp:830:5:830:5 | +| HierarchyConversions() -> void | 0 | 142 | VariableAddress[d] | ir.cpp:831:3:831:3 | +| HierarchyConversions() -> void | 0 | 143 | FunctionAddress[operator=] | ir.cpp:831:5:831:5 | +| HierarchyConversions() -> void | 0 | 144 | VariableAddress[b] | ir.cpp:831:29:831:29 | +| HierarchyConversions() -> void | 0 | 145 | ConvertToDerived[Middle : Base] | ir.cpp:831:29:831:29 | +| HierarchyConversions() -> void | 0 | 146 | ConvertToDerived[Derived : Middle] | ir.cpp:831:7:831:30 | +| HierarchyConversions() -> void | 0 | 147 | Convert | ir.cpp:831:7:831:30 | +| HierarchyConversions() -> void | 0 | 148 | Invoke | ir.cpp:831:5:831:5 | +| HierarchyConversions() -> void | 0 | 149 | VariableAddress[pb] | ir.cpp:832:18:832:19 | +| HierarchyConversions() -> void | 0 | 150 | Load | ir.cpp:832:18:832:19 | +| HierarchyConversions() -> void | 0 | 151 | ConvertToDerived[Middle : Base] | ir.cpp:832:18:832:19 | +| HierarchyConversions() -> void | 0 | 152 | ConvertToDerived[Derived : Middle] | ir.cpp:832:8:832:19 | +| HierarchyConversions() -> void | 0 | 153 | VariableAddress[pd] | ir.cpp:832:3:832:4 | +| HierarchyConversions() -> void | 0 | 154 | Store | ir.cpp:832:3:832:19 | +| HierarchyConversions() -> void | 0 | 155 | VariableAddress[pb] | ir.cpp:833:30:833:31 | +| HierarchyConversions() -> void | 0 | 156 | Load | ir.cpp:833:30:833:31 | +| HierarchyConversions() -> void | 0 | 157 | ConvertToDerived[Middle : Base] | ir.cpp:833:30:833:31 | +| HierarchyConversions() -> void | 0 | 158 | ConvertToDerived[Derived : Middle] | ir.cpp:833:8:833:32 | +| HierarchyConversions() -> void | 0 | 159 | VariableAddress[pd] | ir.cpp:833:3:833:4 | +| HierarchyConversions() -> void | 0 | 160 | Store | ir.cpp:833:3:833:32 | +| HierarchyConversions() -> void | 0 | 161 | VariableAddress[pb] | ir.cpp:834:35:834:36 | +| HierarchyConversions() -> void | 0 | 162 | Load | ir.cpp:834:35:834:36 | +| HierarchyConversions() -> void | 0 | 163 | Convert | ir.cpp:834:8:834:37 | +| HierarchyConversions() -> void | 0 | 164 | VariableAddress[pd] | ir.cpp:834:3:834:4 | +| HierarchyConversions() -> void | 0 | 165 | Store | ir.cpp:834:3:834:37 | +| HierarchyConversions() -> void | 0 | 166 | VariableAddress[pmv] | ir.cpp:836:14:836:16 | +| HierarchyConversions() -> void | 0 | 167 | Constant[0] | ir.cpp:836:20:836:26 | +| HierarchyConversions() -> void | 0 | 168 | Store | ir.cpp:836:20:836:26 | +| HierarchyConversions() -> void | 0 | 169 | VariableAddress[pdv] | ir.cpp:837:14:837:16 | +| HierarchyConversions() -> void | 0 | 170 | Constant[0] | ir.cpp:837:20:837:26 | +| HierarchyConversions() -> void | 0 | 171 | Store | ir.cpp:837:20:837:26 | +| HierarchyConversions() -> void | 0 | 172 | VariableAddress[pmv] | ir.cpp:838:8:838:10 | +| HierarchyConversions() -> void | 0 | 173 | Load | ir.cpp:838:8:838:10 | +| HierarchyConversions() -> void | 0 | 174 | ConvertToVirtualBase[MiddleVB1 : Base] | ir.cpp:838:8:838:10 | +| HierarchyConversions() -> void | 0 | 175 | VariableAddress[pb] | ir.cpp:838:3:838:4 | +| HierarchyConversions() -> void | 0 | 176 | Store | ir.cpp:838:3:838:10 | +| HierarchyConversions() -> void | 0 | 177 | VariableAddress[pdv] | ir.cpp:839:8:839:10 | +| HierarchyConversions() -> void | 0 | 178 | Load | ir.cpp:839:8:839:10 | +| HierarchyConversions() -> void | 0 | 179 | ConvertToVirtualBase[DerivedVB : Base] | ir.cpp:839:8:839:10 | +| HierarchyConversions() -> void | 0 | 180 | VariableAddress[pb] | ir.cpp:839:3:839:4 | +| HierarchyConversions() -> void | 0 | 181 | Store | ir.cpp:839:3:839:10 | +| HierarchyConversions() -> void | 0 | 182 | NoOp | ir.cpp:840:1:840:1 | +| HierarchyConversions() -> void | 0 | 183 | ReturnVoid | ir.cpp:799:6:799:25 | +| HierarchyConversions() -> void | 0 | 184 | UnmodeledUse | ir.cpp:799:6:799:25 | +| HierarchyConversions() -> void | 0 | 185 | ExitFunction | ir.cpp:799:6:799:25 | +| IfStatements(bool, int, int) -> void | 0 | 0 | EnterFunction | ir.cpp:239:6:239:17 | +| IfStatements(bool, int, int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:239:6:239:17 | +| IfStatements(bool, int, int) -> void | 0 | 2 | InitializeParameter[b] | ir.cpp:239:24:239:24 | +| IfStatements(bool, int, int) -> void | 0 | 3 | VariableAddress[b] | ir.cpp:239:24:239:24 | +| IfStatements(bool, int, int) -> void | 0 | 4 | Store | ir.cpp:239:24:239:24 | +| IfStatements(bool, int, int) -> void | 0 | 5 | InitializeParameter[x] | ir.cpp:239:31:239:31 | +| IfStatements(bool, int, int) -> void | 0 | 6 | VariableAddress[x] | ir.cpp:239:31:239:31 | +| IfStatements(bool, int, int) -> void | 0 | 7 | Store | ir.cpp:239:31:239:31 | +| IfStatements(bool, int, int) -> void | 0 | 8 | InitializeParameter[y] | ir.cpp:239:38:239:38 | +| IfStatements(bool, int, int) -> void | 0 | 9 | VariableAddress[y] | ir.cpp:239:38:239:38 | +| IfStatements(bool, int, int) -> void | 0 | 10 | Store | ir.cpp:239:38:239:38 | +| IfStatements(bool, int, int) -> void | 0 | 11 | VariableAddress[b] | ir.cpp:240:9:240:9 | +| IfStatements(bool, int, int) -> void | 0 | 12 | Load | ir.cpp:240:9:240:9 | +| IfStatements(bool, int, int) -> void | 0 | 13 | ConditionalBranch | ir.cpp:240:9:240:9 | +| IfStatements(bool, int, int) -> void | 1 | 0 | VariableAddress[b] | ir.cpp:243:9:243:9 | +| IfStatements(bool, int, int) -> void | 1 | 1 | Load | ir.cpp:243:9:243:9 | +| IfStatements(bool, int, int) -> void | 1 | 2 | ConditionalBranch | ir.cpp:243:9:243:9 | +| IfStatements(bool, int, int) -> void | 2 | 0 | VariableAddress[y] | ir.cpp:244:13:244:13 | +| IfStatements(bool, int, int) -> void | 2 | 1 | Load | ir.cpp:244:13:244:13 | +| IfStatements(bool, int, int) -> void | 2 | 2 | VariableAddress[x] | ir.cpp:244:9:244:9 | +| IfStatements(bool, int, int) -> void | 2 | 3 | Store | ir.cpp:244:9:244:13 | +| IfStatements(bool, int, int) -> void | 3 | 0 | Phi | ir.cpp:247:9:247:9 | +| IfStatements(bool, int, int) -> void | 3 | 1 | VariableAddress[x] | ir.cpp:247:9:247:9 | +| IfStatements(bool, int, int) -> void | 3 | 2 | Load | ir.cpp:247:9:247:9 | +| IfStatements(bool, int, int) -> void | 3 | 3 | Constant[7] | ir.cpp:247:13:247:13 | +| IfStatements(bool, int, int) -> void | 3 | 4 | CompareLT | ir.cpp:247:9:247:13 | +| IfStatements(bool, int, int) -> void | 3 | 5 | ConditionalBranch | ir.cpp:247:9:247:13 | +| IfStatements(bool, int, int) -> void | 4 | 0 | Constant[2] | ir.cpp:248:13:248:13 | +| IfStatements(bool, int, int) -> void | 4 | 1 | VariableAddress[x] | ir.cpp:248:9:248:9 | +| IfStatements(bool, int, int) -> void | 4 | 2 | Store | ir.cpp:248:9:248:13 | +| IfStatements(bool, int, int) -> void | 5 | 0 | Constant[7] | ir.cpp:250:13:250:13 | +| IfStatements(bool, int, int) -> void | 5 | 1 | VariableAddress[x] | ir.cpp:250:9:250:9 | +| IfStatements(bool, int, int) -> void | 5 | 2 | Store | ir.cpp:250:9:250:13 | +| IfStatements(bool, int, int) -> void | 6 | 0 | NoOp | ir.cpp:251:1:251:1 | +| IfStatements(bool, int, int) -> void | 6 | 1 | ReturnVoid | ir.cpp:239:6:239:17 | +| IfStatements(bool, int, int) -> void | 6 | 2 | UnmodeledUse | ir.cpp:239:6:239:17 | +| IfStatements(bool, int, int) -> void | 6 | 3 | ExitFunction | ir.cpp:239:6:239:17 | +| IfStatements(bool, int, int) -> void | 7 | 0 | NoOp | ir.cpp:240:12:241:5 | +| InitArray() -> void | 0 | 0 | EnterFunction | ir.cpp:571:6:571:14 | +| InitArray() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:571:6:571:14 | +| InitArray() -> void | 0 | 2 | VariableAddress[a_pad] | ir.cpp:572:10:572:14 | +| InitArray() -> void | 0 | 3 | StringConstant[""] | ir.cpp:572:22:572:23 | +| InitArray() -> void | 0 | 4 | Load | ir.cpp:572:22:572:23 | +| InitArray() -> void | 0 | 5 | Store | ir.cpp:572:22:572:23 | +| InitArray() -> void | 0 | 6 | Constant[0] | ir.cpp:572:22:572:23 | +| InitArray() -> void | 0 | 7 | Constant[1] | ir.cpp:572:22:572:23 | +| InitArray() -> void | 0 | 8 | PointerAdd | ir.cpp:572:22:572:23 | +| InitArray() -> void | 0 | 9 | Store | ir.cpp:572:22:572:23 | +| InitArray() -> void | 0 | 10 | VariableAddress[a_nopad] | ir.cpp:573:10:573:16 | +| InitArray() -> void | 0 | 11 | StringConstant["foo"] | ir.cpp:573:23:573:27 | +| InitArray() -> void | 0 | 12 | Load | ir.cpp:573:23:573:27 | +| InitArray() -> void | 0 | 13 | Store | ir.cpp:573:23:573:27 | +| InitArray() -> void | 0 | 14 | VariableAddress[a_infer] | ir.cpp:574:10:574:16 | +| InitArray() -> void | 0 | 15 | StringConstant["blah"] | ir.cpp:574:22:574:27 | +| InitArray() -> void | 0 | 16 | Load | ir.cpp:574:22:574:27 | +| InitArray() -> void | 0 | 17 | Store | ir.cpp:574:22:574:27 | +| InitArray() -> void | 0 | 18 | VariableAddress[b] | ir.cpp:575:10:575:10 | +| InitArray() -> void | 0 | 19 | Uninitialized | ir.cpp:575:10:575:10 | +| InitArray() -> void | 0 | 20 | Store | ir.cpp:575:10:575:10 | +| InitArray() -> void | 0 | 21 | VariableAddress[c] | ir.cpp:576:10:576:10 | +| InitArray() -> void | 0 | 22 | Constant[0] | ir.cpp:576:16:576:18 | +| InitArray() -> void | 0 | 23 | PointerAdd | ir.cpp:576:16:576:18 | +| InitArray() -> void | 0 | 24 | Constant[0] | ir.cpp:576:16:576:18 | +| InitArray() -> void | 0 | 25 | Store | ir.cpp:576:16:576:18 | +| InitArray() -> void | 0 | 26 | VariableAddress[d] | ir.cpp:577:10:577:10 | +| InitArray() -> void | 0 | 27 | Constant[0] | ir.cpp:577:16:577:21 | +| InitArray() -> void | 0 | 28 | PointerAdd | ir.cpp:577:16:577:21 | +| InitArray() -> void | 0 | 29 | Constant[0] | ir.cpp:577:19:577:19 | +| InitArray() -> void | 0 | 30 | Store | ir.cpp:577:19:577:19 | +| InitArray() -> void | 0 | 31 | Constant[1] | ir.cpp:577:16:577:21 | +| InitArray() -> void | 0 | 32 | PointerAdd | ir.cpp:577:16:577:21 | +| InitArray() -> void | 0 | 33 | Constant[0] | ir.cpp:577:16:577:21 | +| InitArray() -> void | 0 | 34 | Store | ir.cpp:577:16:577:21 | +| InitArray() -> void | 0 | 35 | VariableAddress[e] | ir.cpp:578:10:578:10 | +| InitArray() -> void | 0 | 36 | Constant[0] | ir.cpp:578:16:578:24 | +| InitArray() -> void | 0 | 37 | PointerAdd | ir.cpp:578:16:578:24 | +| InitArray() -> void | 0 | 38 | Constant[0] | ir.cpp:578:19:578:19 | +| InitArray() -> void | 0 | 39 | Store | ir.cpp:578:19:578:19 | +| InitArray() -> void | 0 | 40 | Constant[1] | ir.cpp:578:16:578:24 | +| InitArray() -> void | 0 | 41 | PointerAdd | ir.cpp:578:16:578:24 | +| InitArray() -> void | 0 | 42 | Constant[1] | ir.cpp:578:22:578:22 | +| InitArray() -> void | 0 | 43 | Store | ir.cpp:578:22:578:22 | +| InitArray() -> void | 0 | 44 | VariableAddress[f] | ir.cpp:579:10:579:10 | +| InitArray() -> void | 0 | 45 | Constant[0] | ir.cpp:579:16:579:21 | +| InitArray() -> void | 0 | 46 | PointerAdd | ir.cpp:579:16:579:21 | +| InitArray() -> void | 0 | 47 | Constant[0] | ir.cpp:579:19:579:19 | +| InitArray() -> void | 0 | 48 | Store | ir.cpp:579:19:579:19 | +| InitArray() -> void | 0 | 49 | Constant[1] | ir.cpp:579:16:579:21 | +| InitArray() -> void | 0 | 50 | PointerAdd | ir.cpp:579:16:579:21 | +| InitArray() -> void | 0 | 51 | Constant[0] | ir.cpp:579:16:579:21 | +| InitArray() -> void | 0 | 52 | Store | ir.cpp:579:16:579:21 | +| InitArray() -> void | 0 | 53 | NoOp | ir.cpp:580:1:580:1 | +| InitArray() -> void | 0 | 54 | ReturnVoid | ir.cpp:571:6:571:14 | +| InitArray() -> void | 0 | 55 | UnmodeledUse | ir.cpp:571:6:571:14 | +| InitArray() -> void | 0 | 56 | ExitFunction | ir.cpp:571:6:571:14 | +| InitList(int, float) -> void | 0 | 0 | EnterFunction | ir.cpp:503:6:503:13 | +| InitList(int, float) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:503:6:503:13 | +| InitList(int, float) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:503:19:503:19 | +| InitList(int, float) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:503:19:503:19 | +| InitList(int, float) -> void | 0 | 4 | Store | ir.cpp:503:19:503:19 | +| InitList(int, float) -> void | 0 | 5 | InitializeParameter[f] | ir.cpp:503:28:503:28 | +| InitList(int, float) -> void | 0 | 6 | VariableAddress[f] | ir.cpp:503:28:503:28 | +| InitList(int, float) -> void | 0 | 7 | Store | ir.cpp:503:28:503:28 | +| InitList(int, float) -> void | 0 | 8 | VariableAddress[pt1] | ir.cpp:504:11:504:13 | +| InitList(int, float) -> void | 0 | 9 | FieldAddress[x] | ir.cpp:504:16:504:24 | +| InitList(int, float) -> void | 0 | 10 | VariableAddress[x] | ir.cpp:504:19:504:19 | +| InitList(int, float) -> void | 0 | 11 | Load | ir.cpp:504:19:504:19 | +| InitList(int, float) -> void | 0 | 12 | Store | ir.cpp:504:19:504:19 | +| InitList(int, float) -> void | 0 | 13 | FieldAddress[y] | ir.cpp:504:16:504:24 | +| InitList(int, float) -> void | 0 | 14 | VariableAddress[f] | ir.cpp:504:22:504:22 | +| InitList(int, float) -> void | 0 | 15 | Load | ir.cpp:504:22:504:22 | +| InitList(int, float) -> void | 0 | 16 | Convert | ir.cpp:504:22:504:22 | +| InitList(int, float) -> void | 0 | 17 | Store | ir.cpp:504:22:504:22 | +| InitList(int, float) -> void | 0 | 18 | VariableAddress[pt2] | ir.cpp:505:11:505:13 | +| InitList(int, float) -> void | 0 | 19 | FieldAddress[x] | ir.cpp:505:16:505:21 | +| InitList(int, float) -> void | 0 | 20 | VariableAddress[x] | ir.cpp:505:19:505:19 | +| InitList(int, float) -> void | 0 | 21 | Load | ir.cpp:505:19:505:19 | +| InitList(int, float) -> void | 0 | 22 | Store | ir.cpp:505:19:505:19 | +| InitList(int, float) -> void | 0 | 23 | FieldAddress[y] | ir.cpp:505:16:505:21 | +| InitList(int, float) -> void | 0 | 24 | Constant[0] | ir.cpp:505:16:505:21 | +| InitList(int, float) -> void | 0 | 25 | Store | ir.cpp:505:16:505:21 | +| InitList(int, float) -> void | 0 | 26 | VariableAddress[pt3] | ir.cpp:506:11:506:13 | +| InitList(int, float) -> void | 0 | 27 | FieldAddress[x] | ir.cpp:506:16:506:18 | +| InitList(int, float) -> void | 0 | 28 | Constant[0] | ir.cpp:506:16:506:18 | +| InitList(int, float) -> void | 0 | 29 | Store | ir.cpp:506:16:506:18 | +| InitList(int, float) -> void | 0 | 30 | FieldAddress[y] | ir.cpp:506:16:506:18 | +| InitList(int, float) -> void | 0 | 31 | Constant[0] | ir.cpp:506:16:506:18 | +| InitList(int, float) -> void | 0 | 32 | Store | ir.cpp:506:16:506:18 | +| InitList(int, float) -> void | 0 | 33 | VariableAddress[x1] | ir.cpp:508:9:508:10 | +| InitList(int, float) -> void | 0 | 34 | Constant[1] | ir.cpp:508:13:508:18 | +| InitList(int, float) -> void | 0 | 35 | Store | ir.cpp:508:13:508:18 | +| InitList(int, float) -> void | 0 | 36 | VariableAddress[x2] | ir.cpp:509:9:509:10 | +| InitList(int, float) -> void | 0 | 37 | Constant[0] | ir.cpp:509:13:509:15 | +| InitList(int, float) -> void | 0 | 38 | Store | ir.cpp:509:13:509:15 | +| InitList(int, float) -> void | 0 | 39 | NoOp | ir.cpp:510:1:510:1 | +| InitList(int, float) -> void | 0 | 40 | ReturnVoid | ir.cpp:503:6:503:13 | +| InitList(int, float) -> void | 0 | 41 | UnmodeledUse | ir.cpp:503:6:503:13 | +| InitList(int, float) -> void | 0 | 42 | ExitFunction | ir.cpp:503:6:503:13 | +| InitReference(int) -> void | 0 | 0 | EnterFunction | ir.cpp:685:6:685:18 | +| InitReference(int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:685:6:685:18 | +| InitReference(int) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:685:24:685:24 | +| InitReference(int) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:685:24:685:24 | +| InitReference(int) -> void | 0 | 4 | Store | ir.cpp:685:24:685:24 | +| InitReference(int) -> void | 0 | 5 | VariableAddress[r] | ir.cpp:686:10:686:10 | +| InitReference(int) -> void | 0 | 6 | VariableAddress[x] | ir.cpp:686:14:686:14 | +| InitReference(int) -> void | 0 | 7 | Store | ir.cpp:686:14:686:14 | +| InitReference(int) -> void | 0 | 8 | VariableAddress[r2] | ir.cpp:687:10:687:11 | +| InitReference(int) -> void | 0 | 9 | VariableAddress[r] | ir.cpp:687:15:687:15 | +| InitReference(int) -> void | 0 | 10 | Load | ir.cpp:687:15:687:15 | +| InitReference(int) -> void | 0 | 11 | Store | ir.cpp:687:15:687:15 | +| InitReference(int) -> void | 0 | 12 | VariableAddress[r3] | ir.cpp:688:19:688:20 | +| InitReference(int) -> void | 0 | 13 | FunctionAddress[ReturnReference] | ir.cpp:688:24:688:38 | +| InitReference(int) -> void | 0 | 14 | Invoke | ir.cpp:688:24:688:38 | +| InitReference(int) -> void | 0 | 15 | Convert | ir.cpp:688:24:688:41 | +| InitReference(int) -> void | 0 | 16 | Store | ir.cpp:688:24:688:41 | +| InitReference(int) -> void | 0 | 17 | NoOp | ir.cpp:689:1:689:1 | +| InitReference(int) -> void | 0 | 18 | ReturnVoid | ir.cpp:685:6:685:18 | +| InitReference(int) -> void | 0 | 19 | UnmodeledUse | ir.cpp:685:6:685:18 | +| InitReference(int) -> void | 0 | 20 | ExitFunction | ir.cpp:685:6:685:18 | +| IntegerCompare(int, int) -> void | 0 | 0 | EnterFunction | ir.cpp:87:6:87:19 | +| IntegerCompare(int, int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:87:6:87:19 | +| IntegerCompare(int, int) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:87:25:87:25 | +| IntegerCompare(int, int) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:87:25:87:25 | +| IntegerCompare(int, int) -> void | 0 | 4 | Store | ir.cpp:87:25:87:25 | +| IntegerCompare(int, int) -> void | 0 | 5 | InitializeParameter[y] | ir.cpp:87:32:87:32 | +| IntegerCompare(int, int) -> void | 0 | 6 | VariableAddress[y] | ir.cpp:87:32:87:32 | +| IntegerCompare(int, int) -> void | 0 | 7 | Store | ir.cpp:87:32:87:32 | +| IntegerCompare(int, int) -> void | 0 | 8 | VariableAddress[b] | ir.cpp:88:10:88:10 | +| IntegerCompare(int, int) -> void | 0 | 9 | Uninitialized | ir.cpp:88:10:88:10 | +| IntegerCompare(int, int) -> void | 0 | 10 | Store | ir.cpp:88:10:88:10 | +| IntegerCompare(int, int) -> void | 0 | 11 | VariableAddress[x] | ir.cpp:90:9:90:9 | +| IntegerCompare(int, int) -> void | 0 | 12 | Load | ir.cpp:90:9:90:9 | +| IntegerCompare(int, int) -> void | 0 | 13 | VariableAddress[y] | ir.cpp:90:14:90:14 | +| IntegerCompare(int, int) -> void | 0 | 14 | Load | ir.cpp:90:14:90:14 | +| IntegerCompare(int, int) -> void | 0 | 15 | CompareEQ | ir.cpp:90:9:90:14 | +| IntegerCompare(int, int) -> void | 0 | 16 | VariableAddress[b] | ir.cpp:90:5:90:5 | +| IntegerCompare(int, int) -> void | 0 | 17 | Store | ir.cpp:90:5:90:14 | +| IntegerCompare(int, int) -> void | 0 | 18 | VariableAddress[x] | ir.cpp:91:9:91:9 | +| IntegerCompare(int, int) -> void | 0 | 19 | Load | ir.cpp:91:9:91:9 | +| IntegerCompare(int, int) -> void | 0 | 20 | VariableAddress[y] | ir.cpp:91:14:91:14 | +| IntegerCompare(int, int) -> void | 0 | 21 | Load | ir.cpp:91:14:91:14 | +| IntegerCompare(int, int) -> void | 0 | 22 | CompareNE | ir.cpp:91:9:91:14 | +| IntegerCompare(int, int) -> void | 0 | 23 | VariableAddress[b] | ir.cpp:91:5:91:5 | +| IntegerCompare(int, int) -> void | 0 | 24 | Store | ir.cpp:91:5:91:14 | +| IntegerCompare(int, int) -> void | 0 | 25 | VariableAddress[x] | ir.cpp:92:9:92:9 | +| IntegerCompare(int, int) -> void | 0 | 26 | Load | ir.cpp:92:9:92:9 | +| IntegerCompare(int, int) -> void | 0 | 27 | VariableAddress[y] | ir.cpp:92:13:92:13 | +| IntegerCompare(int, int) -> void | 0 | 28 | Load | ir.cpp:92:13:92:13 | +| IntegerCompare(int, int) -> void | 0 | 29 | CompareLT | ir.cpp:92:9:92:13 | +| IntegerCompare(int, int) -> void | 0 | 30 | VariableAddress[b] | ir.cpp:92:5:92:5 | +| IntegerCompare(int, int) -> void | 0 | 31 | Store | ir.cpp:92:5:92:13 | +| IntegerCompare(int, int) -> void | 0 | 32 | VariableAddress[x] | ir.cpp:93:9:93:9 | +| IntegerCompare(int, int) -> void | 0 | 33 | Load | ir.cpp:93:9:93:9 | +| IntegerCompare(int, int) -> void | 0 | 34 | VariableAddress[y] | ir.cpp:93:13:93:13 | +| IntegerCompare(int, int) -> void | 0 | 35 | Load | ir.cpp:93:13:93:13 | +| IntegerCompare(int, int) -> void | 0 | 36 | CompareGT | ir.cpp:93:9:93:13 | +| IntegerCompare(int, int) -> void | 0 | 37 | VariableAddress[b] | ir.cpp:93:5:93:5 | +| IntegerCompare(int, int) -> void | 0 | 38 | Store | ir.cpp:93:5:93:13 | +| IntegerCompare(int, int) -> void | 0 | 39 | VariableAddress[x] | ir.cpp:94:9:94:9 | +| IntegerCompare(int, int) -> void | 0 | 40 | Load | ir.cpp:94:9:94:9 | +| IntegerCompare(int, int) -> void | 0 | 41 | VariableAddress[y] | ir.cpp:94:14:94:14 | +| IntegerCompare(int, int) -> void | 0 | 42 | Load | ir.cpp:94:14:94:14 | +| IntegerCompare(int, int) -> void | 0 | 43 | CompareLE | ir.cpp:94:9:94:14 | +| IntegerCompare(int, int) -> void | 0 | 44 | VariableAddress[b] | ir.cpp:94:5:94:5 | +| IntegerCompare(int, int) -> void | 0 | 45 | Store | ir.cpp:94:5:94:14 | +| IntegerCompare(int, int) -> void | 0 | 46 | VariableAddress[x] | ir.cpp:95:9:95:9 | +| IntegerCompare(int, int) -> void | 0 | 47 | Load | ir.cpp:95:9:95:9 | +| IntegerCompare(int, int) -> void | 0 | 48 | VariableAddress[y] | ir.cpp:95:14:95:14 | +| IntegerCompare(int, int) -> void | 0 | 49 | Load | ir.cpp:95:14:95:14 | +| IntegerCompare(int, int) -> void | 0 | 50 | CompareGE | ir.cpp:95:9:95:14 | +| IntegerCompare(int, int) -> void | 0 | 51 | VariableAddress[b] | ir.cpp:95:5:95:5 | +| IntegerCompare(int, int) -> void | 0 | 52 | Store | ir.cpp:95:5:95:14 | +| IntegerCompare(int, int) -> void | 0 | 53 | NoOp | ir.cpp:96:1:96:1 | +| IntegerCompare(int, int) -> void | 0 | 54 | ReturnVoid | ir.cpp:87:6:87:19 | +| IntegerCompare(int, int) -> void | 0 | 55 | UnmodeledUse | ir.cpp:87:6:87:19 | +| IntegerCompare(int, int) -> void | 0 | 56 | ExitFunction | ir.cpp:87:6:87:19 | +| IntegerCrement(int) -> void | 0 | 0 | EnterFunction | ir.cpp:98:6:98:19 | +| IntegerCrement(int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:98:6:98:19 | +| IntegerCrement(int) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:98:25:98:25 | +| IntegerCrement(int) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:98:25:98:25 | +| IntegerCrement(int) -> void | 0 | 4 | Store | ir.cpp:98:25:98:25 | +| IntegerCrement(int) -> void | 0 | 5 | VariableAddress[y] | ir.cpp:99:9:99:9 | +| IntegerCrement(int) -> void | 0 | 6 | Uninitialized | ir.cpp:99:9:99:9 | +| IntegerCrement(int) -> void | 0 | 7 | Store | ir.cpp:99:9:99:9 | +| IntegerCrement(int) -> void | 0 | 8 | VariableAddress[x] | ir.cpp:101:11:101:11 | +| IntegerCrement(int) -> void | 0 | 9 | Load | ir.cpp:101:9:101:11 | +| IntegerCrement(int) -> void | 0 | 10 | Constant[1] | ir.cpp:101:9:101:11 | +| IntegerCrement(int) -> void | 0 | 11 | Add | ir.cpp:101:9:101:11 | +| IntegerCrement(int) -> void | 0 | 12 | Store | ir.cpp:101:9:101:11 | +| IntegerCrement(int) -> void | 0 | 13 | VariableAddress[y] | ir.cpp:101:5:101:5 | +| IntegerCrement(int) -> void | 0 | 14 | Store | ir.cpp:101:5:101:11 | +| IntegerCrement(int) -> void | 0 | 15 | VariableAddress[x] | ir.cpp:102:11:102:11 | +| IntegerCrement(int) -> void | 0 | 16 | Load | ir.cpp:102:9:102:11 | +| IntegerCrement(int) -> void | 0 | 17 | Constant[1] | ir.cpp:102:9:102:11 | +| IntegerCrement(int) -> void | 0 | 18 | Sub | ir.cpp:102:9:102:11 | +| IntegerCrement(int) -> void | 0 | 19 | Store | ir.cpp:102:9:102:11 | +| IntegerCrement(int) -> void | 0 | 20 | VariableAddress[y] | ir.cpp:102:5:102:5 | +| IntegerCrement(int) -> void | 0 | 21 | Store | ir.cpp:102:5:102:11 | +| IntegerCrement(int) -> void | 0 | 22 | VariableAddress[x] | ir.cpp:103:9:103:9 | +| IntegerCrement(int) -> void | 0 | 23 | Load | ir.cpp:103:9:103:11 | +| IntegerCrement(int) -> void | 0 | 24 | Constant[1] | ir.cpp:103:9:103:11 | +| IntegerCrement(int) -> void | 0 | 25 | Add | ir.cpp:103:9:103:11 | +| IntegerCrement(int) -> void | 0 | 26 | Store | ir.cpp:103:9:103:11 | +| IntegerCrement(int) -> void | 0 | 27 | VariableAddress[y] | ir.cpp:103:5:103:5 | +| IntegerCrement(int) -> void | 0 | 28 | Store | ir.cpp:103:5:103:11 | +| IntegerCrement(int) -> void | 0 | 29 | VariableAddress[x] | ir.cpp:104:9:104:9 | +| IntegerCrement(int) -> void | 0 | 30 | Load | ir.cpp:104:9:104:11 | +| IntegerCrement(int) -> void | 0 | 31 | Constant[1] | ir.cpp:104:9:104:11 | +| IntegerCrement(int) -> void | 0 | 32 | Sub | ir.cpp:104:9:104:11 | +| IntegerCrement(int) -> void | 0 | 33 | Store | ir.cpp:104:9:104:11 | +| IntegerCrement(int) -> void | 0 | 34 | VariableAddress[y] | ir.cpp:104:5:104:5 | +| IntegerCrement(int) -> void | 0 | 35 | Store | ir.cpp:104:5:104:11 | +| IntegerCrement(int) -> void | 0 | 36 | NoOp | ir.cpp:105:1:105:1 | +| IntegerCrement(int) -> void | 0 | 37 | ReturnVoid | ir.cpp:98:6:98:19 | +| IntegerCrement(int) -> void | 0 | 38 | UnmodeledUse | ir.cpp:98:6:98:19 | +| IntegerCrement(int) -> void | 0 | 39 | ExitFunction | ir.cpp:98:6:98:19 | +| IntegerCrement_LValue(int) -> void | 0 | 0 | EnterFunction | ir.cpp:107:6:107:26 | +| IntegerCrement_LValue(int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:107:6:107:26 | +| IntegerCrement_LValue(int) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:107:32:107:32 | +| IntegerCrement_LValue(int) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:107:32:107:32 | +| IntegerCrement_LValue(int) -> void | 0 | 4 | Store | ir.cpp:107:32:107:32 | +| IntegerCrement_LValue(int) -> void | 0 | 5 | VariableAddress[p] | ir.cpp:108:10:108:10 | +| IntegerCrement_LValue(int) -> void | 0 | 6 | Uninitialized | ir.cpp:108:10:108:10 | +| IntegerCrement_LValue(int) -> void | 0 | 7 | Store | ir.cpp:108:10:108:10 | +| IntegerCrement_LValue(int) -> void | 0 | 8 | VariableAddress[x] | ir.cpp:110:13:110:13 | +| IntegerCrement_LValue(int) -> void | 0 | 9 | Load | ir.cpp:110:11:110:13 | +| IntegerCrement_LValue(int) -> void | 0 | 10 | Constant[1] | ir.cpp:110:11:110:13 | +| IntegerCrement_LValue(int) -> void | 0 | 11 | Add | ir.cpp:110:11:110:13 | +| IntegerCrement_LValue(int) -> void | 0 | 12 | Store | ir.cpp:110:11:110:13 | +| IntegerCrement_LValue(int) -> void | 0 | 13 | VariableAddress[p] | ir.cpp:110:5:110:5 | +| IntegerCrement_LValue(int) -> void | 0 | 14 | Store | ir.cpp:110:5:110:14 | +| IntegerCrement_LValue(int) -> void | 0 | 15 | VariableAddress[x] | ir.cpp:111:13:111:13 | +| IntegerCrement_LValue(int) -> void | 0 | 16 | Load | ir.cpp:111:11:111:13 | +| IntegerCrement_LValue(int) -> void | 0 | 17 | Constant[1] | ir.cpp:111:11:111:13 | +| IntegerCrement_LValue(int) -> void | 0 | 18 | Sub | ir.cpp:111:11:111:13 | +| IntegerCrement_LValue(int) -> void | 0 | 19 | Store | ir.cpp:111:11:111:13 | +| IntegerCrement_LValue(int) -> void | 0 | 20 | VariableAddress[p] | ir.cpp:111:5:111:5 | +| IntegerCrement_LValue(int) -> void | 0 | 21 | Store | ir.cpp:111:5:111:14 | +| IntegerCrement_LValue(int) -> void | 0 | 22 | NoOp | ir.cpp:112:1:112:1 | +| IntegerCrement_LValue(int) -> void | 0 | 23 | ReturnVoid | ir.cpp:107:6:107:26 | +| IntegerCrement_LValue(int) -> void | 0 | 24 | UnmodeledUse | ir.cpp:107:6:107:26 | +| IntegerCrement_LValue(int) -> void | 0 | 25 | ExitFunction | ir.cpp:107:6:107:26 | +| IntegerOps(int, int) -> void | 0 | 0 | EnterFunction | ir.cpp:50:6:50:15 | +| IntegerOps(int, int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:50:6:50:15 | +| IntegerOps(int, int) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:50:21:50:21 | +| IntegerOps(int, int) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:50:21:50:21 | +| IntegerOps(int, int) -> void | 0 | 4 | Store | ir.cpp:50:21:50:21 | +| IntegerOps(int, int) -> void | 0 | 5 | InitializeParameter[y] | ir.cpp:50:28:50:28 | +| IntegerOps(int, int) -> void | 0 | 6 | VariableAddress[y] | ir.cpp:50:28:50:28 | +| IntegerOps(int, int) -> void | 0 | 7 | Store | ir.cpp:50:28:50:28 | +| IntegerOps(int, int) -> void | 0 | 8 | VariableAddress[z] | ir.cpp:51:9:51:9 | +| IntegerOps(int, int) -> void | 0 | 9 | Uninitialized | ir.cpp:51:9:51:9 | +| IntegerOps(int, int) -> void | 0 | 10 | Store | ir.cpp:51:9:51:9 | +| IntegerOps(int, int) -> void | 0 | 11 | VariableAddress[x] | ir.cpp:53:9:53:9 | +| IntegerOps(int, int) -> void | 0 | 12 | Load | ir.cpp:53:9:53:9 | +| IntegerOps(int, int) -> void | 0 | 13 | VariableAddress[y] | ir.cpp:53:13:53:13 | +| IntegerOps(int, int) -> void | 0 | 14 | Load | ir.cpp:53:13:53:13 | +| IntegerOps(int, int) -> void | 0 | 15 | Add | ir.cpp:53:9:53:13 | +| IntegerOps(int, int) -> void | 0 | 16 | VariableAddress[z] | ir.cpp:53:5:53:5 | +| IntegerOps(int, int) -> void | 0 | 17 | Store | ir.cpp:53:5:53:13 | +| IntegerOps(int, int) -> void | 0 | 18 | VariableAddress[x] | ir.cpp:54:9:54:9 | +| IntegerOps(int, int) -> void | 0 | 19 | Load | ir.cpp:54:9:54:9 | +| IntegerOps(int, int) -> void | 0 | 20 | VariableAddress[y] | ir.cpp:54:13:54:13 | +| IntegerOps(int, int) -> void | 0 | 21 | Load | ir.cpp:54:13:54:13 | +| IntegerOps(int, int) -> void | 0 | 22 | Sub | ir.cpp:54:9:54:13 | +| IntegerOps(int, int) -> void | 0 | 23 | VariableAddress[z] | ir.cpp:54:5:54:5 | +| IntegerOps(int, int) -> void | 0 | 24 | Store | ir.cpp:54:5:54:13 | +| IntegerOps(int, int) -> void | 0 | 25 | VariableAddress[x] | ir.cpp:55:9:55:9 | +| IntegerOps(int, int) -> void | 0 | 26 | Load | ir.cpp:55:9:55:9 | +| IntegerOps(int, int) -> void | 0 | 27 | VariableAddress[y] | ir.cpp:55:13:55:13 | +| IntegerOps(int, int) -> void | 0 | 28 | Load | ir.cpp:55:13:55:13 | +| IntegerOps(int, int) -> void | 0 | 29 | Mul | ir.cpp:55:9:55:13 | +| IntegerOps(int, int) -> void | 0 | 30 | VariableAddress[z] | ir.cpp:55:5:55:5 | +| IntegerOps(int, int) -> void | 0 | 31 | Store | ir.cpp:55:5:55:13 | +| IntegerOps(int, int) -> void | 0 | 32 | VariableAddress[x] | ir.cpp:56:9:56:9 | +| IntegerOps(int, int) -> void | 0 | 33 | Load | ir.cpp:56:9:56:9 | +| IntegerOps(int, int) -> void | 0 | 34 | VariableAddress[y] | ir.cpp:56:13:56:13 | +| IntegerOps(int, int) -> void | 0 | 35 | Load | ir.cpp:56:13:56:13 | +| IntegerOps(int, int) -> void | 0 | 36 | Div | ir.cpp:56:9:56:13 | +| IntegerOps(int, int) -> void | 0 | 37 | VariableAddress[z] | ir.cpp:56:5:56:5 | +| IntegerOps(int, int) -> void | 0 | 38 | Store | ir.cpp:56:5:56:13 | +| IntegerOps(int, int) -> void | 0 | 39 | VariableAddress[x] | ir.cpp:57:9:57:9 | +| IntegerOps(int, int) -> void | 0 | 40 | Load | ir.cpp:57:9:57:9 | +| IntegerOps(int, int) -> void | 0 | 41 | VariableAddress[y] | ir.cpp:57:13:57:13 | +| IntegerOps(int, int) -> void | 0 | 42 | Load | ir.cpp:57:13:57:13 | +| IntegerOps(int, int) -> void | 0 | 43 | Rem | ir.cpp:57:9:57:13 | +| IntegerOps(int, int) -> void | 0 | 44 | VariableAddress[z] | ir.cpp:57:5:57:5 | +| IntegerOps(int, int) -> void | 0 | 45 | Store | ir.cpp:57:5:57:13 | +| IntegerOps(int, int) -> void | 0 | 46 | VariableAddress[x] | ir.cpp:59:9:59:9 | +| IntegerOps(int, int) -> void | 0 | 47 | Load | ir.cpp:59:9:59:9 | +| IntegerOps(int, int) -> void | 0 | 48 | VariableAddress[y] | ir.cpp:59:13:59:13 | +| IntegerOps(int, int) -> void | 0 | 49 | Load | ir.cpp:59:13:59:13 | +| IntegerOps(int, int) -> void | 0 | 50 | BitAnd | ir.cpp:59:9:59:13 | +| IntegerOps(int, int) -> void | 0 | 51 | VariableAddress[z] | ir.cpp:59:5:59:5 | +| IntegerOps(int, int) -> void | 0 | 52 | Store | ir.cpp:59:5:59:13 | +| IntegerOps(int, int) -> void | 0 | 53 | VariableAddress[x] | ir.cpp:60:9:60:9 | +| IntegerOps(int, int) -> void | 0 | 54 | Load | ir.cpp:60:9:60:9 | +| IntegerOps(int, int) -> void | 0 | 55 | VariableAddress[y] | ir.cpp:60:13:60:13 | +| IntegerOps(int, int) -> void | 0 | 56 | Load | ir.cpp:60:13:60:13 | +| IntegerOps(int, int) -> void | 0 | 57 | BitOr | ir.cpp:60:9:60:13 | +| IntegerOps(int, int) -> void | 0 | 58 | VariableAddress[z] | ir.cpp:60:5:60:5 | +| IntegerOps(int, int) -> void | 0 | 59 | Store | ir.cpp:60:5:60:13 | +| IntegerOps(int, int) -> void | 0 | 60 | VariableAddress[x] | ir.cpp:61:9:61:9 | +| IntegerOps(int, int) -> void | 0 | 61 | Load | ir.cpp:61:9:61:9 | +| IntegerOps(int, int) -> void | 0 | 62 | VariableAddress[y] | ir.cpp:61:13:61:13 | +| IntegerOps(int, int) -> void | 0 | 63 | Load | ir.cpp:61:13:61:13 | +| IntegerOps(int, int) -> void | 0 | 64 | BitXor | ir.cpp:61:9:61:13 | +| IntegerOps(int, int) -> void | 0 | 65 | VariableAddress[z] | ir.cpp:61:5:61:5 | +| IntegerOps(int, int) -> void | 0 | 66 | Store | ir.cpp:61:5:61:13 | +| IntegerOps(int, int) -> void | 0 | 67 | VariableAddress[x] | ir.cpp:63:9:63:9 | +| IntegerOps(int, int) -> void | 0 | 68 | Load | ir.cpp:63:9:63:9 | +| IntegerOps(int, int) -> void | 0 | 69 | VariableAddress[y] | ir.cpp:63:14:63:14 | +| IntegerOps(int, int) -> void | 0 | 70 | Load | ir.cpp:63:14:63:14 | +| IntegerOps(int, int) -> void | 0 | 71 | ShiftLeft | ir.cpp:63:9:63:14 | +| IntegerOps(int, int) -> void | 0 | 72 | VariableAddress[z] | ir.cpp:63:5:63:5 | +| IntegerOps(int, int) -> void | 0 | 73 | Store | ir.cpp:63:5:63:14 | +| IntegerOps(int, int) -> void | 0 | 74 | VariableAddress[x] | ir.cpp:64:9:64:9 | +| IntegerOps(int, int) -> void | 0 | 75 | Load | ir.cpp:64:9:64:9 | +| IntegerOps(int, int) -> void | 0 | 76 | VariableAddress[y] | ir.cpp:64:14:64:14 | +| IntegerOps(int, int) -> void | 0 | 77 | Load | ir.cpp:64:14:64:14 | +| IntegerOps(int, int) -> void | 0 | 78 | ShiftRight | ir.cpp:64:9:64:14 | +| IntegerOps(int, int) -> void | 0 | 79 | VariableAddress[z] | ir.cpp:64:5:64:5 | +| IntegerOps(int, int) -> void | 0 | 80 | Store | ir.cpp:64:5:64:14 | +| IntegerOps(int, int) -> void | 0 | 81 | VariableAddress[x] | ir.cpp:66:9:66:9 | +| IntegerOps(int, int) -> void | 0 | 82 | Load | ir.cpp:66:9:66:9 | +| IntegerOps(int, int) -> void | 0 | 83 | VariableAddress[z] | ir.cpp:66:5:66:5 | +| IntegerOps(int, int) -> void | 0 | 84 | Store | ir.cpp:66:5:66:9 | +| IntegerOps(int, int) -> void | 0 | 85 | VariableAddress[x] | ir.cpp:68:10:68:10 | +| IntegerOps(int, int) -> void | 0 | 86 | Load | ir.cpp:68:10:68:10 | +| IntegerOps(int, int) -> void | 0 | 87 | VariableAddress[z] | ir.cpp:68:5:68:5 | +| IntegerOps(int, int) -> void | 0 | 88 | Load | ir.cpp:68:5:68:10 | +| IntegerOps(int, int) -> void | 0 | 89 | Add | ir.cpp:68:5:68:10 | +| IntegerOps(int, int) -> void | 0 | 90 | Store | ir.cpp:68:5:68:10 | +| IntegerOps(int, int) -> void | 0 | 91 | VariableAddress[x] | ir.cpp:69:10:69:10 | +| IntegerOps(int, int) -> void | 0 | 92 | Load | ir.cpp:69:10:69:10 | +| IntegerOps(int, int) -> void | 0 | 93 | VariableAddress[z] | ir.cpp:69:5:69:5 | +| IntegerOps(int, int) -> void | 0 | 94 | Load | ir.cpp:69:5:69:10 | +| IntegerOps(int, int) -> void | 0 | 95 | Sub | ir.cpp:69:5:69:10 | +| IntegerOps(int, int) -> void | 0 | 96 | Store | ir.cpp:69:5:69:10 | +| IntegerOps(int, int) -> void | 0 | 97 | VariableAddress[x] | ir.cpp:70:10:70:10 | +| IntegerOps(int, int) -> void | 0 | 98 | Load | ir.cpp:70:10:70:10 | +| IntegerOps(int, int) -> void | 0 | 99 | VariableAddress[z] | ir.cpp:70:5:70:5 | +| IntegerOps(int, int) -> void | 0 | 100 | Load | ir.cpp:70:5:70:10 | +| IntegerOps(int, int) -> void | 0 | 101 | Mul | ir.cpp:70:5:70:10 | +| IntegerOps(int, int) -> void | 0 | 102 | Store | ir.cpp:70:5:70:10 | +| IntegerOps(int, int) -> void | 0 | 103 | VariableAddress[x] | ir.cpp:71:10:71:10 | +| IntegerOps(int, int) -> void | 0 | 104 | Load | ir.cpp:71:10:71:10 | +| IntegerOps(int, int) -> void | 0 | 105 | VariableAddress[z] | ir.cpp:71:5:71:5 | +| IntegerOps(int, int) -> void | 0 | 106 | Load | ir.cpp:71:5:71:10 | +| IntegerOps(int, int) -> void | 0 | 107 | Div | ir.cpp:71:5:71:10 | +| IntegerOps(int, int) -> void | 0 | 108 | Store | ir.cpp:71:5:71:10 | +| IntegerOps(int, int) -> void | 0 | 109 | VariableAddress[x] | ir.cpp:72:10:72:10 | +| IntegerOps(int, int) -> void | 0 | 110 | Load | ir.cpp:72:10:72:10 | +| IntegerOps(int, int) -> void | 0 | 111 | VariableAddress[z] | ir.cpp:72:5:72:5 | +| IntegerOps(int, int) -> void | 0 | 112 | Load | ir.cpp:72:5:72:10 | +| IntegerOps(int, int) -> void | 0 | 113 | Rem | ir.cpp:72:5:72:10 | +| IntegerOps(int, int) -> void | 0 | 114 | Store | ir.cpp:72:5:72:10 | +| IntegerOps(int, int) -> void | 0 | 115 | VariableAddress[x] | ir.cpp:74:10:74:10 | +| IntegerOps(int, int) -> void | 0 | 116 | Load | ir.cpp:74:10:74:10 | +| IntegerOps(int, int) -> void | 0 | 117 | VariableAddress[z] | ir.cpp:74:5:74:5 | +| IntegerOps(int, int) -> void | 0 | 118 | Load | ir.cpp:74:5:74:10 | +| IntegerOps(int, int) -> void | 0 | 119 | BitAnd | ir.cpp:74:5:74:10 | +| IntegerOps(int, int) -> void | 0 | 120 | Store | ir.cpp:74:5:74:10 | +| IntegerOps(int, int) -> void | 0 | 121 | VariableAddress[x] | ir.cpp:75:10:75:10 | +| IntegerOps(int, int) -> void | 0 | 122 | Load | ir.cpp:75:10:75:10 | +| IntegerOps(int, int) -> void | 0 | 123 | VariableAddress[z] | ir.cpp:75:5:75:5 | +| IntegerOps(int, int) -> void | 0 | 124 | Load | ir.cpp:75:5:75:10 | +| IntegerOps(int, int) -> void | 0 | 125 | BitOr | ir.cpp:75:5:75:10 | +| IntegerOps(int, int) -> void | 0 | 126 | Store | ir.cpp:75:5:75:10 | +| IntegerOps(int, int) -> void | 0 | 127 | VariableAddress[x] | ir.cpp:76:10:76:10 | +| IntegerOps(int, int) -> void | 0 | 128 | Load | ir.cpp:76:10:76:10 | +| IntegerOps(int, int) -> void | 0 | 129 | VariableAddress[z] | ir.cpp:76:5:76:5 | +| IntegerOps(int, int) -> void | 0 | 130 | Load | ir.cpp:76:5:76:10 | +| IntegerOps(int, int) -> void | 0 | 131 | BitXor | ir.cpp:76:5:76:10 | +| IntegerOps(int, int) -> void | 0 | 132 | Store | ir.cpp:76:5:76:10 | +| IntegerOps(int, int) -> void | 0 | 133 | VariableAddress[x] | ir.cpp:78:11:78:11 | +| IntegerOps(int, int) -> void | 0 | 134 | Load | ir.cpp:78:11:78:11 | +| IntegerOps(int, int) -> void | 0 | 135 | VariableAddress[z] | ir.cpp:78:5:78:5 | +| IntegerOps(int, int) -> void | 0 | 136 | Load | ir.cpp:78:5:78:11 | +| IntegerOps(int, int) -> void | 0 | 137 | ShiftLeft | ir.cpp:78:5:78:11 | +| IntegerOps(int, int) -> void | 0 | 138 | Store | ir.cpp:78:5:78:11 | +| IntegerOps(int, int) -> void | 0 | 139 | VariableAddress[x] | ir.cpp:79:11:79:11 | +| IntegerOps(int, int) -> void | 0 | 140 | Load | ir.cpp:79:11:79:11 | +| IntegerOps(int, int) -> void | 0 | 141 | VariableAddress[z] | ir.cpp:79:5:79:5 | +| IntegerOps(int, int) -> void | 0 | 142 | Load | ir.cpp:79:5:79:11 | +| IntegerOps(int, int) -> void | 0 | 143 | ShiftRight | ir.cpp:79:5:79:11 | +| IntegerOps(int, int) -> void | 0 | 144 | Store | ir.cpp:79:5:79:11 | +| IntegerOps(int, int) -> void | 0 | 145 | VariableAddress[x] | ir.cpp:81:10:81:10 | +| IntegerOps(int, int) -> void | 0 | 146 | Load | ir.cpp:81:10:81:10 | +| IntegerOps(int, int) -> void | 0 | 147 | CopyValue | ir.cpp:81:9:81:10 | +| IntegerOps(int, int) -> void | 0 | 148 | VariableAddress[z] | ir.cpp:81:5:81:5 | +| IntegerOps(int, int) -> void | 0 | 149 | Store | ir.cpp:81:5:81:10 | +| IntegerOps(int, int) -> void | 0 | 150 | VariableAddress[x] | ir.cpp:82:10:82:10 | +| IntegerOps(int, int) -> void | 0 | 151 | Load | ir.cpp:82:10:82:10 | +| IntegerOps(int, int) -> void | 0 | 152 | Negate | ir.cpp:82:9:82:10 | +| IntegerOps(int, int) -> void | 0 | 153 | VariableAddress[z] | ir.cpp:82:5:82:5 | +| IntegerOps(int, int) -> void | 0 | 154 | Store | ir.cpp:82:5:82:10 | +| IntegerOps(int, int) -> void | 0 | 155 | VariableAddress[x] | ir.cpp:83:10:83:10 | +| IntegerOps(int, int) -> void | 0 | 156 | Load | ir.cpp:83:10:83:10 | +| IntegerOps(int, int) -> void | 0 | 157 | BitComplement | ir.cpp:83:9:83:10 | +| IntegerOps(int, int) -> void | 0 | 158 | VariableAddress[z] | ir.cpp:83:5:83:5 | +| IntegerOps(int, int) -> void | 0 | 159 | Store | ir.cpp:83:5:83:10 | +| IntegerOps(int, int) -> void | 0 | 160 | VariableAddress[x] | ir.cpp:84:10:84:10 | +| IntegerOps(int, int) -> void | 0 | 161 | Load | ir.cpp:84:10:84:10 | +| IntegerOps(int, int) -> void | 0 | 162 | Constant[0] | ir.cpp:84:10:84:10 | +| IntegerOps(int, int) -> void | 0 | 163 | CompareNE | ir.cpp:84:10:84:10 | +| IntegerOps(int, int) -> void | 0 | 164 | LogicalNot | ir.cpp:84:9:84:10 | +| IntegerOps(int, int) -> void | 0 | 165 | Convert | ir.cpp:84:9:84:10 | +| IntegerOps(int, int) -> void | 0 | 166 | VariableAddress[z] | ir.cpp:84:5:84:5 | +| IntegerOps(int, int) -> void | 0 | 167 | Store | ir.cpp:84:5:84:10 | +| IntegerOps(int, int) -> void | 0 | 168 | NoOp | ir.cpp:85:1:85:1 | +| IntegerOps(int, int) -> void | 0 | 169 | ReturnVoid | ir.cpp:50:6:50:15 | +| IntegerOps(int, int) -> void | 0 | 170 | UnmodeledUse | ir.cpp:50:6:50:15 | +| IntegerOps(int, int) -> void | 0 | 171 | ExitFunction | ir.cpp:50:6:50:15 | +| LogicalAnd(bool, bool) -> void | 0 | 0 | EnterFunction | ir.cpp:447:6:447:15 | +| LogicalAnd(bool, bool) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:447:6:447:15 | +| LogicalAnd(bool, bool) -> void | 0 | 2 | InitializeParameter[a] | ir.cpp:447:22:447:22 | +| LogicalAnd(bool, bool) -> void | 0 | 3 | VariableAddress[a] | ir.cpp:447:22:447:22 | +| LogicalAnd(bool, bool) -> void | 0 | 4 | Store | ir.cpp:447:22:447:22 | +| LogicalAnd(bool, bool) -> void | 0 | 5 | InitializeParameter[b] | ir.cpp:447:30:447:30 | +| LogicalAnd(bool, bool) -> void | 0 | 6 | VariableAddress[b] | ir.cpp:447:30:447:30 | +| LogicalAnd(bool, bool) -> void | 0 | 7 | Store | ir.cpp:447:30:447:30 | +| LogicalAnd(bool, bool) -> void | 0 | 8 | VariableAddress[x] | ir.cpp:448:9:448:9 | +| LogicalAnd(bool, bool) -> void | 0 | 9 | Uninitialized | ir.cpp:448:9:448:9 | +| LogicalAnd(bool, bool) -> void | 0 | 10 | Store | ir.cpp:448:9:448:9 | +| LogicalAnd(bool, bool) -> void | 0 | 11 | VariableAddress[a] | ir.cpp:449:9:449:9 | +| LogicalAnd(bool, bool) -> void | 0 | 12 | Load | ir.cpp:449:9:449:9 | +| LogicalAnd(bool, bool) -> void | 0 | 13 | ConditionalBranch | ir.cpp:449:9:449:9 | +| LogicalAnd(bool, bool) -> void | 1 | 0 | VariableAddress[b] | ir.cpp:449:14:449:14 | +| LogicalAnd(bool, bool) -> void | 1 | 1 | Load | ir.cpp:449:14:449:14 | +| LogicalAnd(bool, bool) -> void | 1 | 2 | ConditionalBranch | ir.cpp:449:14:449:14 | +| LogicalAnd(bool, bool) -> void | 2 | 0 | Constant[7] | ir.cpp:450:13:450:13 | +| LogicalAnd(bool, bool) -> void | 2 | 1 | VariableAddress[x] | ir.cpp:450:9:450:9 | +| LogicalAnd(bool, bool) -> void | 2 | 2 | Store | ir.cpp:450:9:450:13 | +| LogicalAnd(bool, bool) -> void | 3 | 0 | VariableAddress[a] | ir.cpp:453:9:453:9 | +| LogicalAnd(bool, bool) -> void | 3 | 1 | Load | ir.cpp:453:9:453:9 | +| LogicalAnd(bool, bool) -> void | 3 | 2 | ConditionalBranch | ir.cpp:453:9:453:9 | +| LogicalAnd(bool, bool) -> void | 4 | 0 | VariableAddress[b] | ir.cpp:453:14:453:14 | +| LogicalAnd(bool, bool) -> void | 4 | 1 | Load | ir.cpp:453:14:453:14 | +| LogicalAnd(bool, bool) -> void | 4 | 2 | ConditionalBranch | ir.cpp:453:14:453:14 | +| LogicalAnd(bool, bool) -> void | 5 | 0 | Constant[1] | ir.cpp:454:13:454:13 | +| LogicalAnd(bool, bool) -> void | 5 | 1 | VariableAddress[x] | ir.cpp:454:9:454:9 | +| LogicalAnd(bool, bool) -> void | 5 | 2 | Store | ir.cpp:454:9:454:13 | +| LogicalAnd(bool, bool) -> void | 6 | 0 | Constant[5] | ir.cpp:457:13:457:13 | +| LogicalAnd(bool, bool) -> void | 6 | 1 | VariableAddress[x] | ir.cpp:457:9:457:9 | +| LogicalAnd(bool, bool) -> void | 6 | 2 | Store | ir.cpp:457:9:457:13 | +| LogicalAnd(bool, bool) -> void | 7 | 0 | NoOp | ir.cpp:459:1:459:1 | +| LogicalAnd(bool, bool) -> void | 7 | 1 | ReturnVoid | ir.cpp:447:6:447:15 | +| LogicalAnd(bool, bool) -> void | 7 | 2 | UnmodeledUse | ir.cpp:447:6:447:15 | +| LogicalAnd(bool, bool) -> void | 7 | 3 | ExitFunction | ir.cpp:447:6:447:15 | +| LogicalNot(bool, bool) -> void | 0 | 0 | EnterFunction | ir.cpp:461:6:461:15 | +| LogicalNot(bool, bool) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:461:6:461:15 | +| LogicalNot(bool, bool) -> void | 0 | 2 | InitializeParameter[a] | ir.cpp:461:22:461:22 | +| LogicalNot(bool, bool) -> void | 0 | 3 | VariableAddress[a] | ir.cpp:461:22:461:22 | +| LogicalNot(bool, bool) -> void | 0 | 4 | Store | ir.cpp:461:22:461:22 | +| LogicalNot(bool, bool) -> void | 0 | 5 | InitializeParameter[b] | ir.cpp:461:30:461:30 | +| LogicalNot(bool, bool) -> void | 0 | 6 | VariableAddress[b] | ir.cpp:461:30:461:30 | +| LogicalNot(bool, bool) -> void | 0 | 7 | Store | ir.cpp:461:30:461:30 | +| LogicalNot(bool, bool) -> void | 0 | 8 | VariableAddress[x] | ir.cpp:462:9:462:9 | +| LogicalNot(bool, bool) -> void | 0 | 9 | Uninitialized | ir.cpp:462:9:462:9 | +| LogicalNot(bool, bool) -> void | 0 | 10 | Store | ir.cpp:462:9:462:9 | +| LogicalNot(bool, bool) -> void | 0 | 11 | VariableAddress[a] | ir.cpp:463:10:463:10 | +| LogicalNot(bool, bool) -> void | 0 | 12 | Load | ir.cpp:463:10:463:10 | +| LogicalNot(bool, bool) -> void | 0 | 13 | ConditionalBranch | ir.cpp:463:10:463:10 | +| LogicalNot(bool, bool) -> void | 1 | 0 | Constant[1] | ir.cpp:464:13:464:13 | +| LogicalNot(bool, bool) -> void | 1 | 1 | VariableAddress[x] | ir.cpp:464:9:464:9 | +| LogicalNot(bool, bool) -> void | 1 | 2 | Store | ir.cpp:464:9:464:13 | +| LogicalNot(bool, bool) -> void | 2 | 0 | VariableAddress[a] | ir.cpp:467:11:467:11 | +| LogicalNot(bool, bool) -> void | 2 | 1 | Load | ir.cpp:467:11:467:11 | +| LogicalNot(bool, bool) -> void | 2 | 2 | ConditionalBranch | ir.cpp:467:11:467:11 | +| LogicalNot(bool, bool) -> void | 3 | 0 | VariableAddress[b] | ir.cpp:467:16:467:16 | +| LogicalNot(bool, bool) -> void | 3 | 1 | Load | ir.cpp:467:16:467:16 | +| LogicalNot(bool, bool) -> void | 3 | 2 | ConditionalBranch | ir.cpp:467:16:467:16 | +| LogicalNot(bool, bool) -> void | 4 | 0 | Constant[2] | ir.cpp:468:13:468:13 | +| LogicalNot(bool, bool) -> void | 4 | 1 | VariableAddress[x] | ir.cpp:468:9:468:9 | +| LogicalNot(bool, bool) -> void | 4 | 2 | Store | ir.cpp:468:9:468:13 | +| LogicalNot(bool, bool) -> void | 5 | 0 | Constant[3] | ir.cpp:471:13:471:13 | +| LogicalNot(bool, bool) -> void | 5 | 1 | VariableAddress[x] | ir.cpp:471:9:471:9 | +| LogicalNot(bool, bool) -> void | 5 | 2 | Store | ir.cpp:471:9:471:13 | +| LogicalNot(bool, bool) -> void | 6 | 0 | NoOp | ir.cpp:473:1:473:1 | +| LogicalNot(bool, bool) -> void | 6 | 1 | ReturnVoid | ir.cpp:461:6:461:15 | +| LogicalNot(bool, bool) -> void | 6 | 2 | UnmodeledUse | ir.cpp:461:6:461:15 | +| LogicalNot(bool, bool) -> void | 6 | 3 | ExitFunction | ir.cpp:461:6:461:15 | +| LogicalOr(bool, bool) -> void | 0 | 0 | EnterFunction | ir.cpp:433:6:433:14 | +| LogicalOr(bool, bool) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:433:6:433:14 | +| LogicalOr(bool, bool) -> void | 0 | 2 | InitializeParameter[a] | ir.cpp:433:21:433:21 | +| LogicalOr(bool, bool) -> void | 0 | 3 | VariableAddress[a] | ir.cpp:433:21:433:21 | +| LogicalOr(bool, bool) -> void | 0 | 4 | Store | ir.cpp:433:21:433:21 | +| LogicalOr(bool, bool) -> void | 0 | 5 | InitializeParameter[b] | ir.cpp:433:29:433:29 | +| LogicalOr(bool, bool) -> void | 0 | 6 | VariableAddress[b] | ir.cpp:433:29:433:29 | +| LogicalOr(bool, bool) -> void | 0 | 7 | Store | ir.cpp:433:29:433:29 | +| LogicalOr(bool, bool) -> void | 0 | 8 | VariableAddress[x] | ir.cpp:434:9:434:9 | +| LogicalOr(bool, bool) -> void | 0 | 9 | Uninitialized | ir.cpp:434:9:434:9 | +| LogicalOr(bool, bool) -> void | 0 | 10 | Store | ir.cpp:434:9:434:9 | +| LogicalOr(bool, bool) -> void | 0 | 11 | VariableAddress[a] | ir.cpp:435:9:435:9 | +| LogicalOr(bool, bool) -> void | 0 | 12 | Load | ir.cpp:435:9:435:9 | +| LogicalOr(bool, bool) -> void | 0 | 13 | ConditionalBranch | ir.cpp:435:9:435:9 | +| LogicalOr(bool, bool) -> void | 1 | 0 | VariableAddress[b] | ir.cpp:435:14:435:14 | +| LogicalOr(bool, bool) -> void | 1 | 1 | Load | ir.cpp:435:14:435:14 | +| LogicalOr(bool, bool) -> void | 1 | 2 | ConditionalBranch | ir.cpp:435:14:435:14 | +| LogicalOr(bool, bool) -> void | 2 | 0 | Constant[7] | ir.cpp:436:13:436:13 | +| LogicalOr(bool, bool) -> void | 2 | 1 | VariableAddress[x] | ir.cpp:436:9:436:9 | +| LogicalOr(bool, bool) -> void | 2 | 2 | Store | ir.cpp:436:9:436:13 | +| LogicalOr(bool, bool) -> void | 3 | 0 | VariableAddress[a] | ir.cpp:439:9:439:9 | +| LogicalOr(bool, bool) -> void | 3 | 1 | Load | ir.cpp:439:9:439:9 | +| LogicalOr(bool, bool) -> void | 3 | 2 | ConditionalBranch | ir.cpp:439:9:439:9 | +| LogicalOr(bool, bool) -> void | 4 | 0 | VariableAddress[b] | ir.cpp:439:14:439:14 | +| LogicalOr(bool, bool) -> void | 4 | 1 | Load | ir.cpp:439:14:439:14 | +| LogicalOr(bool, bool) -> void | 4 | 2 | ConditionalBranch | ir.cpp:439:14:439:14 | +| LogicalOr(bool, bool) -> void | 5 | 0 | Constant[1] | ir.cpp:440:13:440:13 | +| LogicalOr(bool, bool) -> void | 5 | 1 | VariableAddress[x] | ir.cpp:440:9:440:9 | +| LogicalOr(bool, bool) -> void | 5 | 2 | Store | ir.cpp:440:9:440:13 | +| LogicalOr(bool, bool) -> void | 6 | 0 | Constant[5] | ir.cpp:443:13:443:13 | +| LogicalOr(bool, bool) -> void | 6 | 1 | VariableAddress[x] | ir.cpp:443:9:443:9 | +| LogicalOr(bool, bool) -> void | 6 | 2 | Store | ir.cpp:443:9:443:13 | +| LogicalOr(bool, bool) -> void | 7 | 0 | NoOp | ir.cpp:445:1:445:1 | +| LogicalOr(bool, bool) -> void | 7 | 1 | ReturnVoid | ir.cpp:433:6:433:14 | +| LogicalOr(bool, bool) -> void | 7 | 2 | UnmodeledUse | ir.cpp:433:6:433:14 | +| LogicalOr(bool, bool) -> void | 7 | 3 | ExitFunction | ir.cpp:433:6:433:14 | +| Middle::Middle() -> void | 0 | 0 | EnterFunction | ir.cpp:757:3:757:8 | +| Middle::Middle() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:757:3:757:8 | +| Middle::Middle() -> void | 0 | 2 | InitializeThis | ir.cpp:757:3:757:8 | +| Middle::Middle() -> void | 0 | 3 | ConvertToBase[Middle : Base] | ir.cpp:757:12:757:12 | +| Middle::Middle() -> void | 0 | 4 | FunctionAddress[Base] | ir.cpp:757:12:757:12 | +| Middle::Middle() -> void | 0 | 5 | Invoke | ir.cpp:757:12:757:12 | +| Middle::Middle() -> void | 0 | 6 | FieldAddress[middle_s] | ir.cpp:757:12:757:12 | +| Middle::Middle() -> void | 0 | 7 | FunctionAddress[String] | ir.cpp:757:12:757:12 | +| Middle::Middle() -> void | 0 | 8 | Invoke | ir.cpp:757:12:757:12 | +| Middle::Middle() -> void | 0 | 9 | NoOp | ir.cpp:758:3:758:3 | +| Middle::Middle() -> void | 0 | 10 | ReturnVoid | ir.cpp:757:3:757:8 | +| Middle::Middle() -> void | 0 | 11 | UnmodeledUse | ir.cpp:757:3:757:8 | +| Middle::Middle() -> void | 0 | 12 | ExitFunction | ir.cpp:757:3:757:8 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 0 | EnterFunction | ir.cpp:754:8:754:8 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 1 | UnmodeledDefinition | ir.cpp:754:8:754:8 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 2 | InitializeThis | ir.cpp:754:8:754:8 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 3 | InitializeParameter[p#0] | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 4 | VariableAddress[p#0] | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 5 | Store | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 6 | CopyValue | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 7 | ConvertToBase[Middle : Base] | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 8 | FunctionAddress[operator=] | ir.cpp:754:8:754:8 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 9 | VariableAddress[p#0] | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 10 | Load | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 11 | ConvertToBase[Middle : Base] | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 12 | Invoke | ir.cpp:754:8:754:8 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 13 | CopyValue | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 14 | FieldAddress[middle_s] | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 15 | FunctionAddress[operator=] | ir.cpp:754:8:754:8 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 16 | VariableAddress[p#0] | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 17 | Load | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 18 | FieldAddress[middle_s] | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 19 | Invoke | ir.cpp:754:8:754:8 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 20 | VariableAddress[#return] | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 21 | CopyValue | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 22 | Store | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 23 | VariableAddress[#return] | ir.cpp:754:8:754:8 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 24 | ReturnValue | ir.cpp:754:8:754:8 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 25 | UnmodeledUse | ir.cpp:754:8:754:8 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 26 | ExitFunction | ir.cpp:754:8:754:8 | +| Middle::~Middle() -> void | 0 | 0 | EnterFunction | ir.cpp:759:3:759:9 | +| Middle::~Middle() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:759:3:759:9 | +| Middle::~Middle() -> void | 0 | 2 | InitializeThis | ir.cpp:759:3:759:9 | +| Middle::~Middle() -> void | 0 | 3 | NoOp | ir.cpp:760:3:760:3 | +| Middle::~Middle() -> void | 0 | 4 | FieldAddress[middle_s] | ir.cpp:760:3:760:3 | +| Middle::~Middle() -> void | 0 | 5 | FunctionAddress[~String] | ir.cpp:760:3:760:3 | +| Middle::~Middle() -> void | 0 | 6 | Invoke | ir.cpp:760:3:760:3 | +| Middle::~Middle() -> void | 0 | 7 | ConvertToBase[Middle : Base] | ir.cpp:760:3:760:3 | +| Middle::~Middle() -> void | 0 | 8 | FunctionAddress[~Base] | ir.cpp:760:3:760:3 | +| Middle::~Middle() -> void | 0 | 9 | Invoke | ir.cpp:760:3:760:3 | +| Middle::~Middle() -> void | 0 | 10 | ReturnVoid | ir.cpp:759:3:759:9 | +| Middle::~Middle() -> void | 0 | 11 | UnmodeledUse | ir.cpp:759:3:759:9 | +| Middle::~Middle() -> void | 0 | 12 | ExitFunction | ir.cpp:759:3:759:9 | +| MiddleVB1::MiddleVB1() -> void | 0 | 0 | EnterFunction | ir.cpp:775:3:775:11 | +| MiddleVB1::MiddleVB1() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:775:3:775:11 | +| MiddleVB1::MiddleVB1() -> void | 0 | 2 | InitializeThis | ir.cpp:775:3:775:11 | +| MiddleVB1::MiddleVB1() -> void | 0 | 3 | ConvertToBase[MiddleVB1 : Base] | ir.cpp:775:15:775:15 | +| MiddleVB1::MiddleVB1() -> void | 0 | 4 | FunctionAddress[Base] | ir.cpp:775:15:775:15 | +| MiddleVB1::MiddleVB1() -> void | 0 | 5 | Invoke | ir.cpp:775:15:775:15 | +| MiddleVB1::MiddleVB1() -> void | 0 | 6 | FieldAddress[middlevb1_s] | ir.cpp:775:15:775:15 | +| MiddleVB1::MiddleVB1() -> void | 0 | 7 | FunctionAddress[String] | ir.cpp:775:15:775:15 | +| MiddleVB1::MiddleVB1() -> void | 0 | 8 | Invoke | ir.cpp:775:15:775:15 | +| MiddleVB1::MiddleVB1() -> void | 0 | 9 | NoOp | ir.cpp:776:3:776:3 | +| MiddleVB1::MiddleVB1() -> void | 0 | 10 | ReturnVoid | ir.cpp:775:3:775:11 | +| MiddleVB1::MiddleVB1() -> void | 0 | 11 | UnmodeledUse | ir.cpp:775:3:775:11 | +| MiddleVB1::MiddleVB1() -> void | 0 | 12 | ExitFunction | ir.cpp:775:3:775:11 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 0 | EnterFunction | ir.cpp:777:3:777:12 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:777:3:777:12 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 2 | InitializeThis | ir.cpp:777:3:777:12 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 3 | NoOp | ir.cpp:778:3:778:3 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 4 | FieldAddress[middlevb1_s] | ir.cpp:778:3:778:3 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 5 | FunctionAddress[~String] | ir.cpp:778:3:778:3 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 6 | Invoke | ir.cpp:778:3:778:3 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 7 | ConvertToBase[MiddleVB1 : Base] | ir.cpp:778:3:778:3 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 8 | FunctionAddress[~Base] | ir.cpp:778:3:778:3 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 9 | Invoke | ir.cpp:778:3:778:3 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 10 | ReturnVoid | ir.cpp:777:3:777:12 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 11 | UnmodeledUse | ir.cpp:777:3:777:12 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 12 | ExitFunction | ir.cpp:777:3:777:12 | +| MiddleVB2::MiddleVB2() -> void | 0 | 0 | EnterFunction | ir.cpp:784:3:784:11 | +| MiddleVB2::MiddleVB2() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:784:3:784:11 | +| MiddleVB2::MiddleVB2() -> void | 0 | 2 | InitializeThis | ir.cpp:784:3:784:11 | +| MiddleVB2::MiddleVB2() -> void | 0 | 3 | ConvertToBase[MiddleVB2 : Base] | ir.cpp:784:15:784:15 | +| MiddleVB2::MiddleVB2() -> void | 0 | 4 | FunctionAddress[Base] | ir.cpp:784:15:784:15 | +| MiddleVB2::MiddleVB2() -> void | 0 | 5 | Invoke | ir.cpp:784:15:784:15 | +| MiddleVB2::MiddleVB2() -> void | 0 | 6 | FieldAddress[middlevb2_s] | ir.cpp:784:15:784:15 | +| MiddleVB2::MiddleVB2() -> void | 0 | 7 | FunctionAddress[String] | ir.cpp:784:15:784:15 | +| MiddleVB2::MiddleVB2() -> void | 0 | 8 | Invoke | ir.cpp:784:15:784:15 | +| MiddleVB2::MiddleVB2() -> void | 0 | 9 | NoOp | ir.cpp:785:3:785:3 | +| MiddleVB2::MiddleVB2() -> void | 0 | 10 | ReturnVoid | ir.cpp:784:3:784:11 | +| MiddleVB2::MiddleVB2() -> void | 0 | 11 | UnmodeledUse | ir.cpp:784:3:784:11 | +| MiddleVB2::MiddleVB2() -> void | 0 | 12 | ExitFunction | ir.cpp:784:3:784:11 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 0 | EnterFunction | ir.cpp:786:3:786:12 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:786:3:786:12 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 2 | InitializeThis | ir.cpp:786:3:786:12 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 3 | NoOp | ir.cpp:787:3:787:3 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 4 | FieldAddress[middlevb2_s] | ir.cpp:787:3:787:3 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 5 | FunctionAddress[~String] | ir.cpp:787:3:787:3 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 6 | Invoke | ir.cpp:787:3:787:3 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 7 | ConvertToBase[MiddleVB2 : Base] | ir.cpp:787:3:787:3 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 8 | FunctionAddress[~Base] | ir.cpp:787:3:787:3 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 9 | Invoke | ir.cpp:787:3:787:3 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 10 | ReturnVoid | ir.cpp:786:3:786:12 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 11 | UnmodeledUse | ir.cpp:786:3:786:12 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 12 | ExitFunction | ir.cpp:786:3:786:12 | +| NestedInitList(int, float) -> void | 0 | 0 | EnterFunction | ir.cpp:512:6:512:19 | +| NestedInitList(int, float) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:512:6:512:19 | +| NestedInitList(int, float) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:512:25:512:25 | +| NestedInitList(int, float) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:512:25:512:25 | +| NestedInitList(int, float) -> void | 0 | 4 | Store | ir.cpp:512:25:512:25 | +| NestedInitList(int, float) -> void | 0 | 5 | InitializeParameter[f] | ir.cpp:512:34:512:34 | +| NestedInitList(int, float) -> void | 0 | 6 | VariableAddress[f] | ir.cpp:512:34:512:34 | +| NestedInitList(int, float) -> void | 0 | 7 | Store | ir.cpp:512:34:512:34 | +| NestedInitList(int, float) -> void | 0 | 8 | VariableAddress[r1] | ir.cpp:513:10:513:11 | +| NestedInitList(int, float) -> void | 0 | 9 | FieldAddress[topLeft] | ir.cpp:513:14:513:16 | +| NestedInitList(int, float) -> void | 0 | 10 | Constant[0] | ir.cpp:513:14:513:16 | +| NestedInitList(int, float) -> void | 0 | 11 | Store | ir.cpp:513:14:513:16 | +| NestedInitList(int, float) -> void | 0 | 12 | FieldAddress[bottomRight] | ir.cpp:513:14:513:16 | +| NestedInitList(int, float) -> void | 0 | 13 | Constant[0] | ir.cpp:513:14:513:16 | +| NestedInitList(int, float) -> void | 0 | 14 | Store | ir.cpp:513:14:513:16 | +| NestedInitList(int, float) -> void | 0 | 15 | VariableAddress[r2] | ir.cpp:514:10:514:11 | +| NestedInitList(int, float) -> void | 0 | 16 | FieldAddress[topLeft] | ir.cpp:514:14:514:26 | +| NestedInitList(int, float) -> void | 0 | 17 | FieldAddress[x] | ir.cpp:514:17:514:24 | +| NestedInitList(int, float) -> void | 0 | 18 | VariableAddress[x] | ir.cpp:514:19:514:19 | +| NestedInitList(int, float) -> void | 0 | 19 | Load | ir.cpp:514:19:514:19 | +| NestedInitList(int, float) -> void | 0 | 20 | Store | ir.cpp:514:19:514:19 | +| NestedInitList(int, float) -> void | 0 | 21 | FieldAddress[y] | ir.cpp:514:17:514:24 | +| NestedInitList(int, float) -> void | 0 | 22 | VariableAddress[f] | ir.cpp:514:22:514:22 | +| NestedInitList(int, float) -> void | 0 | 23 | Load | ir.cpp:514:22:514:22 | +| NestedInitList(int, float) -> void | 0 | 24 | Convert | ir.cpp:514:22:514:22 | +| NestedInitList(int, float) -> void | 0 | 25 | Store | ir.cpp:514:22:514:22 | +| NestedInitList(int, float) -> void | 0 | 26 | FieldAddress[bottomRight] | ir.cpp:514:14:514:26 | +| NestedInitList(int, float) -> void | 0 | 27 | Constant[0] | ir.cpp:514:14:514:26 | +| NestedInitList(int, float) -> void | 0 | 28 | Store | ir.cpp:514:14:514:26 | +| NestedInitList(int, float) -> void | 0 | 29 | VariableAddress[r3] | ir.cpp:515:10:515:11 | +| NestedInitList(int, float) -> void | 0 | 30 | FieldAddress[topLeft] | ir.cpp:515:14:515:36 | +| NestedInitList(int, float) -> void | 0 | 31 | FieldAddress[x] | ir.cpp:515:17:515:24 | +| NestedInitList(int, float) -> void | 0 | 32 | VariableAddress[x] | ir.cpp:515:19:515:19 | +| NestedInitList(int, float) -> void | 0 | 33 | Load | ir.cpp:515:19:515:19 | +| NestedInitList(int, float) -> void | 0 | 34 | Store | ir.cpp:515:19:515:19 | +| NestedInitList(int, float) -> void | 0 | 35 | FieldAddress[y] | ir.cpp:515:17:515:24 | +| NestedInitList(int, float) -> void | 0 | 36 | VariableAddress[f] | ir.cpp:515:22:515:22 | +| NestedInitList(int, float) -> void | 0 | 37 | Load | ir.cpp:515:22:515:22 | +| NestedInitList(int, float) -> void | 0 | 38 | Convert | ir.cpp:515:22:515:22 | +| NestedInitList(int, float) -> void | 0 | 39 | Store | ir.cpp:515:22:515:22 | +| NestedInitList(int, float) -> void | 0 | 40 | FieldAddress[bottomRight] | ir.cpp:515:14:515:36 | +| NestedInitList(int, float) -> void | 0 | 41 | FieldAddress[x] | ir.cpp:515:27:515:34 | +| NestedInitList(int, float) -> void | 0 | 42 | VariableAddress[x] | ir.cpp:515:29:515:29 | +| NestedInitList(int, float) -> void | 0 | 43 | Load | ir.cpp:515:29:515:29 | +| NestedInitList(int, float) -> void | 0 | 44 | Store | ir.cpp:515:29:515:29 | +| NestedInitList(int, float) -> void | 0 | 45 | FieldAddress[y] | ir.cpp:515:27:515:34 | +| NestedInitList(int, float) -> void | 0 | 46 | VariableAddress[f] | ir.cpp:515:32:515:32 | +| NestedInitList(int, float) -> void | 0 | 47 | Load | ir.cpp:515:32:515:32 | +| NestedInitList(int, float) -> void | 0 | 48 | Convert | ir.cpp:515:32:515:32 | +| NestedInitList(int, float) -> void | 0 | 49 | Store | ir.cpp:515:32:515:32 | +| NestedInitList(int, float) -> void | 0 | 50 | VariableAddress[r4] | ir.cpp:516:10:516:11 | +| NestedInitList(int, float) -> void | 0 | 51 | FieldAddress[topLeft] | ir.cpp:516:14:516:30 | +| NestedInitList(int, float) -> void | 0 | 52 | FieldAddress[x] | ir.cpp:516:17:516:21 | +| NestedInitList(int, float) -> void | 0 | 53 | VariableAddress[x] | ir.cpp:516:19:516:19 | +| NestedInitList(int, float) -> void | 0 | 54 | Load | ir.cpp:516:19:516:19 | +| NestedInitList(int, float) -> void | 0 | 55 | Store | ir.cpp:516:19:516:19 | +| NestedInitList(int, float) -> void | 0 | 56 | FieldAddress[y] | ir.cpp:516:17:516:21 | +| NestedInitList(int, float) -> void | 0 | 57 | Constant[0] | ir.cpp:516:17:516:21 | +| NestedInitList(int, float) -> void | 0 | 58 | Store | ir.cpp:516:17:516:21 | +| NestedInitList(int, float) -> void | 0 | 59 | FieldAddress[bottomRight] | ir.cpp:516:14:516:30 | +| NestedInitList(int, float) -> void | 0 | 60 | FieldAddress[x] | ir.cpp:516:24:516:28 | +| NestedInitList(int, float) -> void | 0 | 61 | VariableAddress[x] | ir.cpp:516:26:516:26 | +| NestedInitList(int, float) -> void | 0 | 62 | Load | ir.cpp:516:26:516:26 | +| NestedInitList(int, float) -> void | 0 | 63 | Store | ir.cpp:516:26:516:26 | +| NestedInitList(int, float) -> void | 0 | 64 | FieldAddress[y] | ir.cpp:516:24:516:28 | +| NestedInitList(int, float) -> void | 0 | 65 | Constant[0] | ir.cpp:516:24:516:28 | +| NestedInitList(int, float) -> void | 0 | 66 | Store | ir.cpp:516:24:516:28 | +| NestedInitList(int, float) -> void | 0 | 67 | NoOp | ir.cpp:517:1:517:1 | +| NestedInitList(int, float) -> void | 0 | 68 | ReturnVoid | ir.cpp:512:6:512:19 | +| NestedInitList(int, float) -> void | 0 | 69 | UnmodeledUse | ir.cpp:512:6:512:19 | +| NestedInitList(int, float) -> void | 0 | 70 | ExitFunction | ir.cpp:512:6:512:19 | +| Nullptr() -> void | 0 | 0 | EnterFunction | ir.cpp:496:6:496:12 | +| Nullptr() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:496:6:496:12 | +| Nullptr() -> void | 0 | 2 | VariableAddress[p] | ir.cpp:497:10:497:10 | +| Nullptr() -> void | 0 | 3 | Constant[0] | ir.cpp:497:14:497:20 | +| Nullptr() -> void | 0 | 4 | Store | ir.cpp:497:14:497:20 | +| Nullptr() -> void | 0 | 5 | VariableAddress[q] | ir.cpp:498:10:498:10 | +| Nullptr() -> void | 0 | 6 | Constant[0] | ir.cpp:498:14:498:14 | +| Nullptr() -> void | 0 | 7 | Store | ir.cpp:498:14:498:14 | +| Nullptr() -> void | 0 | 8 | Constant[0] | ir.cpp:499:9:499:15 | +| Nullptr() -> void | 0 | 9 | VariableAddress[p] | ir.cpp:499:5:499:5 | +| Nullptr() -> void | 0 | 10 | Store | ir.cpp:499:5:499:15 | +| Nullptr() -> void | 0 | 11 | Constant[0] | ir.cpp:500:9:500:9 | +| Nullptr() -> void | 0 | 12 | VariableAddress[q] | ir.cpp:500:5:500:5 | +| Nullptr() -> void | 0 | 13 | Store | ir.cpp:500:5:500:9 | +| Nullptr() -> void | 0 | 14 | NoOp | ir.cpp:501:1:501:1 | +| Nullptr() -> void | 0 | 15 | ReturnVoid | ir.cpp:496:6:496:12 | +| Nullptr() -> void | 0 | 16 | UnmodeledUse | ir.cpp:496:6:496:12 | +| Nullptr() -> void | 0 | 17 | ExitFunction | ir.cpp:496:6:496:12 | +| Outer::Func(void *, char) -> long | 0 | 0 | EnterFunction | ir.cpp:715:12:715:15 | +| Outer::Func(void *, char) -> long | 0 | 1 | UnmodeledDefinition | ir.cpp:715:12:715:15 | +| Outer::Func(void *, char) -> long | 0 | 2 | InitializeParameter[x] | ir.cpp:715:19:715:19 | +| Outer::Func(void *, char) -> long | 0 | 3 | VariableAddress[x] | ir.cpp:715:19:715:19 | +| Outer::Func(void *, char) -> long | 0 | 4 | Store | ir.cpp:715:19:715:19 | +| Outer::Func(void *, char) -> long | 0 | 5 | InitializeParameter[y] | ir.cpp:715:24:715:24 | +| Outer::Func(void *, char) -> long | 0 | 6 | VariableAddress[y] | ir.cpp:715:24:715:24 | +| Outer::Func(void *, char) -> long | 0 | 7 | Store | ir.cpp:715:24:715:24 | +| Outer::Func(void *, char) -> long | 0 | 8 | VariableAddress[#return] | ir.cpp:716:5:716:15 | +| Outer::Func(void *, char) -> long | 0 | 9 | Constant[0] | ir.cpp:716:12:716:14 | +| Outer::Func(void *, char) -> long | 0 | 10 | Store | ir.cpp:716:12:716:14 | +| Outer::Func(void *, char) -> long | 0 | 11 | VariableAddress[#return] | ir.cpp:715:12:715:15 | +| Outer::Func(void *, char) -> long | 0 | 12 | ReturnValue | ir.cpp:715:12:715:15 | +| Outer::Func(void *, char) -> long | 0 | 13 | UnmodeledUse | ir.cpp:715:12:715:15 | +| Outer::Func(void *, char) -> long | 0 | 14 | ExitFunction | ir.cpp:715:12:715:15 | +| Parameters(int, int) -> int | 0 | 0 | EnterFunction | ir.cpp:235:5:235:14 | +| Parameters(int, int) -> int | 0 | 1 | UnmodeledDefinition | ir.cpp:235:5:235:14 | +| Parameters(int, int) -> int | 0 | 2 | InitializeParameter[x] | ir.cpp:235:20:235:20 | +| Parameters(int, int) -> int | 0 | 3 | VariableAddress[x] | ir.cpp:235:20:235:20 | +| Parameters(int, int) -> int | 0 | 4 | Store | ir.cpp:235:20:235:20 | +| Parameters(int, int) -> int | 0 | 5 | InitializeParameter[y] | ir.cpp:235:27:235:27 | +| Parameters(int, int) -> int | 0 | 6 | VariableAddress[y] | ir.cpp:235:27:235:27 | +| Parameters(int, int) -> int | 0 | 7 | Store | ir.cpp:235:27:235:27 | +| Parameters(int, int) -> int | 0 | 8 | VariableAddress[#return] | ir.cpp:236:5:236:17 | +| Parameters(int, int) -> int | 0 | 9 | VariableAddress[x] | ir.cpp:236:12:236:12 | +| Parameters(int, int) -> int | 0 | 10 | Load | ir.cpp:236:12:236:12 | +| Parameters(int, int) -> int | 0 | 11 | VariableAddress[y] | ir.cpp:236:16:236:16 | +| Parameters(int, int) -> int | 0 | 12 | Load | ir.cpp:236:16:236:16 | +| Parameters(int, int) -> int | 0 | 13 | Rem | ir.cpp:236:12:236:16 | +| Parameters(int, int) -> int | 0 | 14 | Store | ir.cpp:236:12:236:16 | +| Parameters(int, int) -> int | 0 | 15 | VariableAddress[#return] | ir.cpp:235:5:235:14 | +| Parameters(int, int) -> int | 0 | 16 | ReturnValue | ir.cpp:235:5:235:14 | +| Parameters(int, int) -> int | 0 | 17 | UnmodeledUse | ir.cpp:235:5:235:14 | +| Parameters(int, int) -> int | 0 | 18 | ExitFunction | ir.cpp:235:5:235:14 | +| PointerCompare(int *, int *) -> void | 0 | 0 | EnterFunction | ir.cpp:193:6:193:19 | +| PointerCompare(int *, int *) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:193:6:193:19 | +| PointerCompare(int *, int *) -> void | 0 | 2 | InitializeParameter[p] | ir.cpp:193:26:193:26 | +| PointerCompare(int *, int *) -> void | 0 | 3 | VariableAddress[p] | ir.cpp:193:26:193:26 | +| PointerCompare(int *, int *) -> void | 0 | 4 | Store | ir.cpp:193:26:193:26 | +| PointerCompare(int *, int *) -> void | 0 | 5 | InitializeParameter[q] | ir.cpp:193:34:193:34 | +| PointerCompare(int *, int *) -> void | 0 | 6 | VariableAddress[q] | ir.cpp:193:34:193:34 | +| PointerCompare(int *, int *) -> void | 0 | 7 | Store | ir.cpp:193:34:193:34 | +| PointerCompare(int *, int *) -> void | 0 | 8 | VariableAddress[b] | ir.cpp:194:10:194:10 | +| PointerCompare(int *, int *) -> void | 0 | 9 | Uninitialized | ir.cpp:194:10:194:10 | +| PointerCompare(int *, int *) -> void | 0 | 10 | Store | ir.cpp:194:10:194:10 | +| PointerCompare(int *, int *) -> void | 0 | 11 | VariableAddress[p] | ir.cpp:196:9:196:9 | +| PointerCompare(int *, int *) -> void | 0 | 12 | Load | ir.cpp:196:9:196:9 | +| PointerCompare(int *, int *) -> void | 0 | 13 | VariableAddress[q] | ir.cpp:196:14:196:14 | +| PointerCompare(int *, int *) -> void | 0 | 14 | Load | ir.cpp:196:14:196:14 | +| PointerCompare(int *, int *) -> void | 0 | 15 | CompareEQ | ir.cpp:196:9:196:14 | +| PointerCompare(int *, int *) -> void | 0 | 16 | VariableAddress[b] | ir.cpp:196:5:196:5 | +| PointerCompare(int *, int *) -> void | 0 | 17 | Store | ir.cpp:196:5:196:14 | +| PointerCompare(int *, int *) -> void | 0 | 18 | VariableAddress[p] | ir.cpp:197:9:197:9 | +| PointerCompare(int *, int *) -> void | 0 | 19 | Load | ir.cpp:197:9:197:9 | +| PointerCompare(int *, int *) -> void | 0 | 20 | VariableAddress[q] | ir.cpp:197:14:197:14 | +| PointerCompare(int *, int *) -> void | 0 | 21 | Load | ir.cpp:197:14:197:14 | +| PointerCompare(int *, int *) -> void | 0 | 22 | CompareNE | ir.cpp:197:9:197:14 | +| PointerCompare(int *, int *) -> void | 0 | 23 | VariableAddress[b] | ir.cpp:197:5:197:5 | +| PointerCompare(int *, int *) -> void | 0 | 24 | Store | ir.cpp:197:5:197:14 | +| PointerCompare(int *, int *) -> void | 0 | 25 | VariableAddress[p] | ir.cpp:198:9:198:9 | +| PointerCompare(int *, int *) -> void | 0 | 26 | Load | ir.cpp:198:9:198:9 | +| PointerCompare(int *, int *) -> void | 0 | 27 | VariableAddress[q] | ir.cpp:198:13:198:13 | +| PointerCompare(int *, int *) -> void | 0 | 28 | Load | ir.cpp:198:13:198:13 | +| PointerCompare(int *, int *) -> void | 0 | 29 | CompareLT | ir.cpp:198:9:198:13 | +| PointerCompare(int *, int *) -> void | 0 | 30 | VariableAddress[b] | ir.cpp:198:5:198:5 | +| PointerCompare(int *, int *) -> void | 0 | 31 | Store | ir.cpp:198:5:198:13 | +| PointerCompare(int *, int *) -> void | 0 | 32 | VariableAddress[p] | ir.cpp:199:9:199:9 | +| PointerCompare(int *, int *) -> void | 0 | 33 | Load | ir.cpp:199:9:199:9 | +| PointerCompare(int *, int *) -> void | 0 | 34 | VariableAddress[q] | ir.cpp:199:13:199:13 | +| PointerCompare(int *, int *) -> void | 0 | 35 | Load | ir.cpp:199:13:199:13 | +| PointerCompare(int *, int *) -> void | 0 | 36 | CompareGT | ir.cpp:199:9:199:13 | +| PointerCompare(int *, int *) -> void | 0 | 37 | VariableAddress[b] | ir.cpp:199:5:199:5 | +| PointerCompare(int *, int *) -> void | 0 | 38 | Store | ir.cpp:199:5:199:13 | +| PointerCompare(int *, int *) -> void | 0 | 39 | VariableAddress[p] | ir.cpp:200:9:200:9 | +| PointerCompare(int *, int *) -> void | 0 | 40 | Load | ir.cpp:200:9:200:9 | +| PointerCompare(int *, int *) -> void | 0 | 41 | VariableAddress[q] | ir.cpp:200:14:200:14 | +| PointerCompare(int *, int *) -> void | 0 | 42 | Load | ir.cpp:200:14:200:14 | +| PointerCompare(int *, int *) -> void | 0 | 43 | CompareLE | ir.cpp:200:9:200:14 | +| PointerCompare(int *, int *) -> void | 0 | 44 | VariableAddress[b] | ir.cpp:200:5:200:5 | +| PointerCompare(int *, int *) -> void | 0 | 45 | Store | ir.cpp:200:5:200:14 | +| PointerCompare(int *, int *) -> void | 0 | 46 | VariableAddress[p] | ir.cpp:201:9:201:9 | +| PointerCompare(int *, int *) -> void | 0 | 47 | Load | ir.cpp:201:9:201:9 | +| PointerCompare(int *, int *) -> void | 0 | 48 | VariableAddress[q] | ir.cpp:201:14:201:14 | +| PointerCompare(int *, int *) -> void | 0 | 49 | Load | ir.cpp:201:14:201:14 | +| PointerCompare(int *, int *) -> void | 0 | 50 | CompareGE | ir.cpp:201:9:201:14 | +| PointerCompare(int *, int *) -> void | 0 | 51 | VariableAddress[b] | ir.cpp:201:5:201:5 | +| PointerCompare(int *, int *) -> void | 0 | 52 | Store | ir.cpp:201:5:201:14 | +| PointerCompare(int *, int *) -> void | 0 | 53 | NoOp | ir.cpp:202:1:202:1 | +| PointerCompare(int *, int *) -> void | 0 | 54 | ReturnVoid | ir.cpp:193:6:193:19 | +| PointerCompare(int *, int *) -> void | 0 | 55 | UnmodeledUse | ir.cpp:193:6:193:19 | +| PointerCompare(int *, int *) -> void | 0 | 56 | ExitFunction | ir.cpp:193:6:193:19 | +| PointerCrement(int *) -> void | 0 | 0 | EnterFunction | ir.cpp:204:6:204:19 | +| PointerCrement(int *) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:204:6:204:19 | +| PointerCrement(int *) -> void | 0 | 2 | InitializeParameter[p] | ir.cpp:204:26:204:26 | +| PointerCrement(int *) -> void | 0 | 3 | VariableAddress[p] | ir.cpp:204:26:204:26 | +| PointerCrement(int *) -> void | 0 | 4 | Store | ir.cpp:204:26:204:26 | +| PointerCrement(int *) -> void | 0 | 5 | VariableAddress[q] | ir.cpp:205:10:205:10 | +| PointerCrement(int *) -> void | 0 | 6 | Uninitialized | ir.cpp:205:10:205:10 | +| PointerCrement(int *) -> void | 0 | 7 | Store | ir.cpp:205:10:205:10 | +| PointerCrement(int *) -> void | 0 | 8 | VariableAddress[p] | ir.cpp:207:11:207:11 | +| PointerCrement(int *) -> void | 0 | 9 | Load | ir.cpp:207:9:207:11 | +| PointerCrement(int *) -> void | 0 | 10 | Constant[1] | ir.cpp:207:9:207:11 | +| PointerCrement(int *) -> void | 0 | 11 | PointerAdd[4] | ir.cpp:207:9:207:11 | +| PointerCrement(int *) -> void | 0 | 12 | Store | ir.cpp:207:9:207:11 | +| PointerCrement(int *) -> void | 0 | 13 | VariableAddress[q] | ir.cpp:207:5:207:5 | +| PointerCrement(int *) -> void | 0 | 14 | Store | ir.cpp:207:5:207:11 | +| PointerCrement(int *) -> void | 0 | 15 | VariableAddress[p] | ir.cpp:208:11:208:11 | +| PointerCrement(int *) -> void | 0 | 16 | Load | ir.cpp:208:9:208:11 | +| PointerCrement(int *) -> void | 0 | 17 | Constant[1] | ir.cpp:208:9:208:11 | +| PointerCrement(int *) -> void | 0 | 18 | PointerSub[4] | ir.cpp:208:9:208:11 | +| PointerCrement(int *) -> void | 0 | 19 | Store | ir.cpp:208:9:208:11 | +| PointerCrement(int *) -> void | 0 | 20 | VariableAddress[q] | ir.cpp:208:5:208:5 | +| PointerCrement(int *) -> void | 0 | 21 | Store | ir.cpp:208:5:208:11 | +| PointerCrement(int *) -> void | 0 | 22 | VariableAddress[p] | ir.cpp:209:9:209:9 | +| PointerCrement(int *) -> void | 0 | 23 | Load | ir.cpp:209:9:209:11 | +| PointerCrement(int *) -> void | 0 | 24 | Constant[1] | ir.cpp:209:9:209:11 | +| PointerCrement(int *) -> void | 0 | 25 | PointerAdd[4] | ir.cpp:209:9:209:11 | +| PointerCrement(int *) -> void | 0 | 26 | Store | ir.cpp:209:9:209:11 | +| PointerCrement(int *) -> void | 0 | 27 | VariableAddress[q] | ir.cpp:209:5:209:5 | +| PointerCrement(int *) -> void | 0 | 28 | Store | ir.cpp:209:5:209:11 | +| PointerCrement(int *) -> void | 0 | 29 | VariableAddress[p] | ir.cpp:210:9:210:9 | +| PointerCrement(int *) -> void | 0 | 30 | Load | ir.cpp:210:9:210:11 | +| PointerCrement(int *) -> void | 0 | 31 | Constant[1] | ir.cpp:210:9:210:11 | +| PointerCrement(int *) -> void | 0 | 32 | PointerSub[4] | ir.cpp:210:9:210:11 | +| PointerCrement(int *) -> void | 0 | 33 | Store | ir.cpp:210:9:210:11 | +| PointerCrement(int *) -> void | 0 | 34 | VariableAddress[q] | ir.cpp:210:5:210:5 | +| PointerCrement(int *) -> void | 0 | 35 | Store | ir.cpp:210:5:210:11 | +| PointerCrement(int *) -> void | 0 | 36 | NoOp | ir.cpp:211:1:211:1 | +| PointerCrement(int *) -> void | 0 | 37 | ReturnVoid | ir.cpp:204:6:204:19 | +| PointerCrement(int *) -> void | 0 | 38 | UnmodeledUse | ir.cpp:204:6:204:19 | +| PointerCrement(int *) -> void | 0 | 39 | ExitFunction | ir.cpp:204:6:204:19 | +| PointerOps(int *, int) -> void | 0 | 0 | EnterFunction | ir.cpp:153:6:153:15 | +| PointerOps(int *, int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:153:6:153:15 | +| PointerOps(int *, int) -> void | 0 | 2 | InitializeParameter[p] | ir.cpp:153:22:153:22 | +| PointerOps(int *, int) -> void | 0 | 3 | VariableAddress[p] | ir.cpp:153:22:153:22 | +| PointerOps(int *, int) -> void | 0 | 4 | Store | ir.cpp:153:22:153:22 | +| PointerOps(int *, int) -> void | 0 | 5 | InitializeParameter[i] | ir.cpp:153:29:153:29 | +| PointerOps(int *, int) -> void | 0 | 6 | VariableAddress[i] | ir.cpp:153:29:153:29 | +| PointerOps(int *, int) -> void | 0 | 7 | Store | ir.cpp:153:29:153:29 | +| PointerOps(int *, int) -> void | 0 | 8 | VariableAddress[q] | ir.cpp:154:10:154:10 | +| PointerOps(int *, int) -> void | 0 | 9 | Uninitialized | ir.cpp:154:10:154:10 | +| PointerOps(int *, int) -> void | 0 | 10 | Store | ir.cpp:154:10:154:10 | +| PointerOps(int *, int) -> void | 0 | 11 | VariableAddress[b] | ir.cpp:155:10:155:10 | +| PointerOps(int *, int) -> void | 0 | 12 | Uninitialized | ir.cpp:155:10:155:10 | +| PointerOps(int *, int) -> void | 0 | 13 | Store | ir.cpp:155:10:155:10 | +| PointerOps(int *, int) -> void | 0 | 14 | VariableAddress[p] | ir.cpp:157:9:157:9 | +| PointerOps(int *, int) -> void | 0 | 15 | Load | ir.cpp:157:9:157:9 | +| PointerOps(int *, int) -> void | 0 | 16 | VariableAddress[i] | ir.cpp:157:13:157:13 | +| PointerOps(int *, int) -> void | 0 | 17 | Load | ir.cpp:157:13:157:13 | +| PointerOps(int *, int) -> void | 0 | 18 | PointerAdd[4] | ir.cpp:157:9:157:13 | +| PointerOps(int *, int) -> void | 0 | 19 | VariableAddress[q] | ir.cpp:157:5:157:5 | +| PointerOps(int *, int) -> void | 0 | 20 | Store | ir.cpp:157:5:157:13 | +| PointerOps(int *, int) -> void | 0 | 21 | VariableAddress[i] | ir.cpp:158:9:158:9 | +| PointerOps(int *, int) -> void | 0 | 22 | Load | ir.cpp:158:9:158:9 | +| PointerOps(int *, int) -> void | 0 | 23 | VariableAddress[p] | ir.cpp:158:13:158:13 | +| PointerOps(int *, int) -> void | 0 | 24 | Load | ir.cpp:158:13:158:13 | +| PointerOps(int *, int) -> void | 0 | 25 | PointerAdd[4] | ir.cpp:158:9:158:13 | +| PointerOps(int *, int) -> void | 0 | 26 | VariableAddress[q] | ir.cpp:158:5:158:5 | +| PointerOps(int *, int) -> void | 0 | 27 | Store | ir.cpp:158:5:158:13 | +| PointerOps(int *, int) -> void | 0 | 28 | VariableAddress[p] | ir.cpp:159:9:159:9 | +| PointerOps(int *, int) -> void | 0 | 29 | Load | ir.cpp:159:9:159:9 | +| PointerOps(int *, int) -> void | 0 | 30 | VariableAddress[i] | ir.cpp:159:13:159:13 | +| PointerOps(int *, int) -> void | 0 | 31 | Load | ir.cpp:159:13:159:13 | +| PointerOps(int *, int) -> void | 0 | 32 | PointerSub[4] | ir.cpp:159:9:159:13 | +| PointerOps(int *, int) -> void | 0 | 33 | VariableAddress[q] | ir.cpp:159:5:159:5 | +| PointerOps(int *, int) -> void | 0 | 34 | Store | ir.cpp:159:5:159:13 | +| PointerOps(int *, int) -> void | 0 | 35 | VariableAddress[p] | ir.cpp:160:9:160:9 | +| PointerOps(int *, int) -> void | 0 | 36 | Load | ir.cpp:160:9:160:9 | +| PointerOps(int *, int) -> void | 0 | 37 | VariableAddress[q] | ir.cpp:160:13:160:13 | +| PointerOps(int *, int) -> void | 0 | 38 | Load | ir.cpp:160:13:160:13 | +| PointerOps(int *, int) -> void | 0 | 39 | PointerDiff[4] | ir.cpp:160:9:160:13 | +| PointerOps(int *, int) -> void | 0 | 40 | Convert | ir.cpp:160:9:160:13 | +| PointerOps(int *, int) -> void | 0 | 41 | VariableAddress[i] | ir.cpp:160:5:160:5 | +| PointerOps(int *, int) -> void | 0 | 42 | Store | ir.cpp:160:5:160:13 | +| PointerOps(int *, int) -> void | 0 | 43 | VariableAddress[p] | ir.cpp:162:9:162:9 | +| PointerOps(int *, int) -> void | 0 | 44 | Load | ir.cpp:162:9:162:9 | +| PointerOps(int *, int) -> void | 0 | 45 | VariableAddress[q] | ir.cpp:162:5:162:5 | +| PointerOps(int *, int) -> void | 0 | 46 | Store | ir.cpp:162:5:162:9 | +| PointerOps(int *, int) -> void | 0 | 47 | VariableAddress[i] | ir.cpp:164:10:164:10 | +| PointerOps(int *, int) -> void | 0 | 48 | Load | ir.cpp:164:10:164:10 | +| PointerOps(int *, int) -> void | 0 | 49 | VariableAddress[q] | ir.cpp:164:5:164:5 | +| PointerOps(int *, int) -> void | 0 | 50 | Load | ir.cpp:164:5:164:10 | +| PointerOps(int *, int) -> void | 0 | 51 | PointerAdd[4] | ir.cpp:164:5:164:10 | +| PointerOps(int *, int) -> void | 0 | 52 | Store | ir.cpp:164:5:164:10 | +| PointerOps(int *, int) -> void | 0 | 53 | VariableAddress[i] | ir.cpp:165:10:165:10 | +| PointerOps(int *, int) -> void | 0 | 54 | Load | ir.cpp:165:10:165:10 | +| PointerOps(int *, int) -> void | 0 | 55 | VariableAddress[q] | ir.cpp:165:5:165:5 | +| PointerOps(int *, int) -> void | 0 | 56 | Load | ir.cpp:165:5:165:10 | +| PointerOps(int *, int) -> void | 0 | 57 | PointerSub[4] | ir.cpp:165:5:165:10 | +| PointerOps(int *, int) -> void | 0 | 58 | Store | ir.cpp:165:5:165:10 | +| PointerOps(int *, int) -> void | 0 | 59 | VariableAddress[p] | ir.cpp:167:9:167:9 | +| PointerOps(int *, int) -> void | 0 | 60 | Load | ir.cpp:167:9:167:9 | +| PointerOps(int *, int) -> void | 0 | 61 | Constant[0] | ir.cpp:167:9:167:9 | +| PointerOps(int *, int) -> void | 0 | 62 | CompareNE | ir.cpp:167:9:167:9 | +| PointerOps(int *, int) -> void | 0 | 63 | VariableAddress[b] | ir.cpp:167:5:167:5 | +| PointerOps(int *, int) -> void | 0 | 64 | Store | ir.cpp:167:5:167:9 | +| PointerOps(int *, int) -> void | 0 | 65 | VariableAddress[p] | ir.cpp:168:10:168:10 | +| PointerOps(int *, int) -> void | 0 | 66 | Load | ir.cpp:168:10:168:10 | +| PointerOps(int *, int) -> void | 0 | 67 | Constant[0] | ir.cpp:168:10:168:10 | +| PointerOps(int *, int) -> void | 0 | 68 | CompareNE | ir.cpp:168:10:168:10 | +| PointerOps(int *, int) -> void | 0 | 69 | LogicalNot | ir.cpp:168:9:168:10 | +| PointerOps(int *, int) -> void | 0 | 70 | VariableAddress[b] | ir.cpp:168:5:168:5 | +| PointerOps(int *, int) -> void | 0 | 71 | Store | ir.cpp:168:5:168:10 | +| PointerOps(int *, int) -> void | 0 | 72 | NoOp | ir.cpp:169:1:169:1 | +| PointerOps(int *, int) -> void | 0 | 73 | ReturnVoid | ir.cpp:153:6:153:15 | +| PointerOps(int *, int) -> void | 0 | 74 | UnmodeledUse | ir.cpp:153:6:153:15 | +| PointerOps(int *, int) -> void | 0 | 75 | ExitFunction | ir.cpp:153:6:153:15 | +| PolymorphicBase::PolymorphicBase() -> void | 0 | 0 | EnterFunction | ir.cpp:842:8:842:8 | +| PolymorphicBase::PolymorphicBase() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:842:8:842:8 | +| PolymorphicBase::PolymorphicBase() -> void | 0 | 2 | InitializeThis | ir.cpp:842:8:842:8 | +| PolymorphicBase::PolymorphicBase() -> void | 0 | 3 | NoOp | ir.cpp:842:8:842:8 | +| PolymorphicBase::PolymorphicBase() -> void | 0 | 4 | ReturnVoid | ir.cpp:842:8:842:8 | +| PolymorphicBase::PolymorphicBase() -> void | 0 | 5 | UnmodeledUse | ir.cpp:842:8:842:8 | +| PolymorphicBase::PolymorphicBase() -> void | 0 | 6 | ExitFunction | ir.cpp:842:8:842:8 | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 0 | EnterFunction | ir.cpp:846:8:846:8 | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:846:8:846:8 | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 2 | InitializeThis | ir.cpp:846:8:846:8 | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 3 | ConvertToBase[PolymorphicDerived : PolymorphicBase] | ir.cpp:846:8:846:8 | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 4 | FunctionAddress[PolymorphicBase] | ir.cpp:846:8:846:8 | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 5 | Invoke | ir.cpp:846:8:846:8 | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 6 | NoOp | ir.cpp:846:8:846:8 | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 7 | ReturnVoid | ir.cpp:846:8:846:8 | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 8 | UnmodeledUse | ir.cpp:846:8:846:8 | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 9 | ExitFunction | ir.cpp:846:8:846:8 | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 0 | EnterFunction | ir.cpp:846:8:846:8 | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:846:8:846:8 | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 2 | InitializeThis | ir.cpp:846:8:846:8 | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 3 | NoOp | file://:0:0:0:0 | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 4 | ConvertToBase[PolymorphicDerived : PolymorphicBase] | ir.cpp:846:8:846:8 | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 5 | FunctionAddress[~PolymorphicBase] | ir.cpp:846:8:846:8 | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 6 | Invoke | ir.cpp:846:8:846:8 | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 7 | ReturnVoid | ir.cpp:846:8:846:8 | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 8 | UnmodeledUse | ir.cpp:846:8:846:8 | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 9 | ExitFunction | ir.cpp:846:8:846:8 | +| ReturnStruct(Point) -> Point | 0 | 0 | EnterFunction | ir.cpp:422:7:422:18 | +| ReturnStruct(Point) -> Point | 0 | 1 | UnmodeledDefinition | ir.cpp:422:7:422:18 | +| ReturnStruct(Point) -> Point | 0 | 2 | InitializeParameter[pt] | ir.cpp:422:26:422:27 | +| ReturnStruct(Point) -> Point | 0 | 3 | VariableAddress[pt] | ir.cpp:422:26:422:27 | +| ReturnStruct(Point) -> Point | 0 | 4 | Store | ir.cpp:422:26:422:27 | +| ReturnStruct(Point) -> Point | 0 | 5 | VariableAddress[#return] | ir.cpp:423:5:423:14 | +| ReturnStruct(Point) -> Point | 0 | 6 | VariableAddress[pt] | ir.cpp:423:12:423:13 | +| ReturnStruct(Point) -> Point | 0 | 7 | Load | ir.cpp:423:12:423:13 | +| ReturnStruct(Point) -> Point | 0 | 8 | Store | ir.cpp:423:12:423:13 | +| ReturnStruct(Point) -> Point | 0 | 9 | VariableAddress[#return] | ir.cpp:422:7:422:18 | +| ReturnStruct(Point) -> Point | 0 | 10 | ReturnValue | ir.cpp:422:7:422:18 | +| ReturnStruct(Point) -> Point | 0 | 11 | UnmodeledUse | ir.cpp:422:7:422:18 | +| ReturnStruct(Point) -> Point | 0 | 12 | ExitFunction | ir.cpp:422:7:422:18 | +| SetFuncPtr() -> void | 0 | 0 | EnterFunction | ir.cpp:590:6:590:15 | +| SetFuncPtr() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:590:6:590:15 | +| SetFuncPtr() -> void | 0 | 2 | VariableAddress[pfn] | ir.cpp:591:11:591:13 | +| SetFuncPtr() -> void | 0 | 3 | FunctionAddress[FuncPtrTarget] | ir.cpp:591:23:591:35 | +| SetFuncPtr() -> void | 0 | 4 | Store | ir.cpp:591:23:591:35 | +| SetFuncPtr() -> void | 0 | 5 | FunctionAddress[FuncPtrTarget] | ir.cpp:592:12:592:24 | +| SetFuncPtr() -> void | 0 | 6 | VariableAddress[pfn] | ir.cpp:592:5:592:7 | +| SetFuncPtr() -> void | 0 | 7 | Store | ir.cpp:592:5:592:24 | +| SetFuncPtr() -> void | 0 | 8 | FunctionAddress[FuncPtrTarget] | ir.cpp:593:12:593:24 | +| SetFuncPtr() -> void | 0 | 9 | VariableAddress[pfn] | ir.cpp:593:5:593:7 | +| SetFuncPtr() -> void | 0 | 10 | Store | ir.cpp:593:5:593:24 | +| SetFuncPtr() -> void | 0 | 11 | FunctionAddress[FuncPtrTarget] | ir.cpp:594:15:594:27 | +| SetFuncPtr() -> void | 0 | 12 | VariableAddress[pfn] | ir.cpp:594:5:594:7 | +| SetFuncPtr() -> void | 0 | 13 | Store | ir.cpp:594:5:594:27 | +| SetFuncPtr() -> void | 0 | 14 | NoOp | ir.cpp:595:1:595:1 | +| SetFuncPtr() -> void | 0 | 15 | ReturnVoid | ir.cpp:590:6:590:15 | +| SetFuncPtr() -> void | 0 | 16 | UnmodeledUse | ir.cpp:590:6:590:15 | +| SetFuncPtr() -> void | 0 | 17 | ExitFunction | ir.cpp:590:6:590:15 | +| String::String() -> void | 0 | 0 | EnterFunction | ir.cpp:867:1:867:14 | +| String::String() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:867:1:867:14 | +| String::String() -> void | 0 | 2 | InitializeThis | ir.cpp:867:1:867:14 | +| String::String() -> void | 0 | 3 | FunctionAddress[String] | ir.cpp:868:3:868:12 | +| String::String() -> void | 0 | 4 | StringConstant[""] | ir.cpp:868:10:868:11 | +| String::String() -> void | 0 | 5 | Convert | ir.cpp:868:10:868:11 | +| String::String() -> void | 0 | 6 | Invoke | ir.cpp:868:3:868:12 | +| String::String() -> void | 0 | 7 | NoOp | ir.cpp:869:1:869:1 | +| String::String() -> void | 0 | 8 | ReturnVoid | ir.cpp:867:1:867:14 | +| String::String() -> void | 0 | 9 | UnmodeledUse | ir.cpp:867:1:867:14 | +| String::String() -> void | 0 | 10 | ExitFunction | ir.cpp:867:1:867:14 | +| StringLiteral(int) -> void | 0 | 0 | EnterFunction | ir.cpp:187:6:187:18 | +| StringLiteral(int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:187:6:187:18 | +| StringLiteral(int) -> void | 0 | 2 | InitializeParameter[i] | ir.cpp:187:24:187:24 | +| StringLiteral(int) -> void | 0 | 3 | VariableAddress[i] | ir.cpp:187:24:187:24 | +| StringLiteral(int) -> void | 0 | 4 | Store | ir.cpp:187:24:187:24 | +| StringLiteral(int) -> void | 0 | 5 | VariableAddress[c] | ir.cpp:188:10:188:10 | +| StringLiteral(int) -> void | 0 | 6 | StringConstant["Foo"] | ir.cpp:188:14:188:18 | +| StringLiteral(int) -> void | 0 | 7 | Convert | ir.cpp:188:14:188:18 | +| StringLiteral(int) -> void | 0 | 8 | VariableAddress[i] | ir.cpp:188:20:188:20 | +| StringLiteral(int) -> void | 0 | 9 | Load | ir.cpp:188:20:188:20 | +| StringLiteral(int) -> void | 0 | 10 | PointerAdd[1] | ir.cpp:188:14:188:21 | +| StringLiteral(int) -> void | 0 | 11 | Load | ir.cpp:188:14:188:21 | +| StringLiteral(int) -> void | 0 | 12 | Store | ir.cpp:188:14:188:21 | +| StringLiteral(int) -> void | 0 | 13 | VariableAddress[pwc] | ir.cpp:189:14:189:16 | +| StringLiteral(int) -> void | 0 | 14 | StringConstant[L"Bar"] | ir.cpp:189:20:189:25 | +| StringLiteral(int) -> void | 0 | 15 | Convert | ir.cpp:189:20:189:25 | +| StringLiteral(int) -> void | 0 | 16 | Convert | ir.cpp:189:20:189:25 | +| StringLiteral(int) -> void | 0 | 17 | Store | ir.cpp:189:20:189:25 | +| StringLiteral(int) -> void | 0 | 18 | VariableAddress[wc] | ir.cpp:190:13:190:14 | +| StringLiteral(int) -> void | 0 | 19 | VariableAddress[pwc] | ir.cpp:190:18:190:20 | +| StringLiteral(int) -> void | 0 | 20 | Load | ir.cpp:190:18:190:20 | +| StringLiteral(int) -> void | 0 | 21 | VariableAddress[i] | ir.cpp:190:22:190:22 | +| StringLiteral(int) -> void | 0 | 22 | Load | ir.cpp:190:22:190:22 | +| StringLiteral(int) -> void | 0 | 23 | PointerAdd[4] | ir.cpp:190:18:190:23 | +| StringLiteral(int) -> void | 0 | 24 | Load | ir.cpp:190:18:190:23 | +| StringLiteral(int) -> void | 0 | 25 | Store | ir.cpp:190:18:190:23 | +| StringLiteral(int) -> void | 0 | 26 | NoOp | ir.cpp:191:1:191:1 | +| StringLiteral(int) -> void | 0 | 27 | ReturnVoid | ir.cpp:187:6:187:18 | +| StringLiteral(int) -> void | 0 | 28 | UnmodeledUse | ir.cpp:187:6:187:18 | +| StringLiteral(int) -> void | 0 | 29 | ExitFunction | ir.cpp:187:6:187:18 | +| Switch(int) -> void | 0 | 0 | EnterFunction | ir.cpp:384:6:384:11 | +| Switch(int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:384:6:384:11 | +| Switch(int) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:384:17:384:17 | +| Switch(int) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:384:17:384:17 | +| Switch(int) -> void | 0 | 4 | Store | ir.cpp:384:17:384:17 | +| Switch(int) -> void | 0 | 5 | VariableAddress[y] | ir.cpp:385:9:385:9 | +| Switch(int) -> void | 0 | 6 | Uninitialized | ir.cpp:385:9:385:9 | +| Switch(int) -> void | 0 | 7 | Store | ir.cpp:385:9:385:9 | +| Switch(int) -> void | 0 | 8 | VariableAddress[x] | ir.cpp:386:13:386:13 | +| Switch(int) -> void | 0 | 9 | Load | ir.cpp:386:13:386:13 | +| Switch(int) -> void | 0 | 10 | Switch | ir.cpp:386:5:409:5 | +| Switch(int) -> void | 1 | 0 | Constant[1234] | ir.cpp:387:13:387:16 | +| Switch(int) -> void | 1 | 1 | VariableAddress[y] | ir.cpp:387:9:387:9 | +| Switch(int) -> void | 1 | 2 | Store | ir.cpp:387:9:387:16 | +| Switch(int) -> void | 2 | 0 | NoOp | ir.cpp:389:9:389:16 | +| Switch(int) -> void | 2 | 1 | Constant[-1] | ir.cpp:390:17:390:18 | +| Switch(int) -> void | 2 | 2 | VariableAddress[y] | ir.cpp:390:13:390:13 | +| Switch(int) -> void | 2 | 3 | Store | ir.cpp:390:13:390:18 | +| Switch(int) -> void | 2 | 4 | NoOp | ir.cpp:391:13:391:18 | +| Switch(int) -> void | 3 | 0 | NoOp | ir.cpp:393:9:393:15 | +| Switch(int) -> void | 4 | 0 | NoOp | ir.cpp:394:9:394:15 | +| Switch(int) -> void | 4 | 1 | Constant[1] | ir.cpp:395:17:395:17 | +| Switch(int) -> void | 4 | 2 | VariableAddress[y] | ir.cpp:395:13:395:13 | +| Switch(int) -> void | 4 | 3 | Store | ir.cpp:395:13:395:17 | +| Switch(int) -> void | 4 | 4 | NoOp | ir.cpp:396:13:396:18 | +| Switch(int) -> void | 5 | 0 | NoOp | ir.cpp:398:9:398:15 | +| Switch(int) -> void | 5 | 1 | Constant[3] | ir.cpp:399:17:399:17 | +| Switch(int) -> void | 5 | 2 | VariableAddress[y] | ir.cpp:399:13:399:13 | +| Switch(int) -> void | 5 | 3 | Store | ir.cpp:399:13:399:17 | +| Switch(int) -> void | 6 | 0 | NoOp | ir.cpp:400:9:400:15 | +| Switch(int) -> void | 6 | 1 | Constant[4] | ir.cpp:401:17:401:17 | +| Switch(int) -> void | 6 | 2 | VariableAddress[y] | ir.cpp:401:13:401:13 | +| Switch(int) -> void | 6 | 3 | Store | ir.cpp:401:13:401:17 | +| Switch(int) -> void | 6 | 4 | NoOp | ir.cpp:402:13:402:18 | +| Switch(int) -> void | 7 | 0 | NoOp | ir.cpp:404:9:404:16 | +| Switch(int) -> void | 7 | 1 | Constant[0] | ir.cpp:405:17:405:17 | +| Switch(int) -> void | 7 | 2 | VariableAddress[y] | ir.cpp:405:13:405:13 | +| Switch(int) -> void | 7 | 3 | Store | ir.cpp:405:13:405:17 | +| Switch(int) -> void | 7 | 4 | NoOp | ir.cpp:406:13:406:18 | +| Switch(int) -> void | 8 | 0 | Constant[5678] | ir.cpp:408:13:408:16 | +| Switch(int) -> void | 8 | 1 | VariableAddress[y] | ir.cpp:408:9:408:9 | +| Switch(int) -> void | 8 | 2 | Store | ir.cpp:408:9:408:16 | +| Switch(int) -> void | 9 | 0 | NoOp | ir.cpp:409:5:409:5 | +| Switch(int) -> void | 9 | 1 | NoOp | ir.cpp:410:1:410:1 | +| Switch(int) -> void | 9 | 2 | ReturnVoid | ir.cpp:384:6:384:11 | +| Switch(int) -> void | 9 | 3 | UnmodeledUse | ir.cpp:384:6:384:11 | +| Switch(int) -> void | 9 | 4 | ExitFunction | ir.cpp:384:6:384:11 | +| TakeReference() -> int & | 0 | 0 | EnterFunction | ir.cpp:679:6:679:18 | +| TakeReference() -> int & | 0 | 1 | UnmodeledDefinition | ir.cpp:679:6:679:18 | +| TakeReference() -> int & | 0 | 2 | VariableAddress[#return] | ir.cpp:680:5:680:13 | +| TakeReference() -> int & | 0 | 3 | VariableAddress[g] | ir.cpp:680:12:680:12 | +| TakeReference() -> int & | 0 | 4 | Store | ir.cpp:680:12:680:12 | +| TakeReference() -> int & | 0 | 5 | VariableAddress[#return] | ir.cpp:679:6:679:18 | +| TakeReference() -> int & | 0 | 6 | ReturnValue | ir.cpp:679:6:679:18 | +| TakeReference() -> int & | 0 | 7 | UnmodeledUse | ir.cpp:679:6:679:18 | +| TakeReference() -> int & | 0 | 8 | ExitFunction | ir.cpp:679:6:679:18 | +| TryCatch(bool) -> void | 0 | 0 | EnterFunction | ir.cpp:724:6:724:13 | +| TryCatch(bool) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:724:6:724:13 | +| TryCatch(bool) -> void | 0 | 2 | InitializeParameter[b] | ir.cpp:724:20:724:20 | +| TryCatch(bool) -> void | 0 | 3 | VariableAddress[b] | ir.cpp:724:20:724:20 | +| TryCatch(bool) -> void | 0 | 4 | Store | ir.cpp:724:20:724:20 | +| TryCatch(bool) -> void | 0 | 5 | VariableAddress[x] | ir.cpp:726:9:726:9 | +| TryCatch(bool) -> void | 0 | 6 | Constant[5] | ir.cpp:726:12:726:13 | +| TryCatch(bool) -> void | 0 | 7 | Store | ir.cpp:726:12:726:13 | +| TryCatch(bool) -> void | 0 | 8 | VariableAddress[b] | ir.cpp:727:9:727:9 | +| TryCatch(bool) -> void | 0 | 9 | Load | ir.cpp:727:9:727:9 | +| TryCatch(bool) -> void | 0 | 10 | ConditionalBranch | ir.cpp:727:9:727:9 | +| TryCatch(bool) -> void | 1 | 0 | UnmodeledUse | ir.cpp:724:6:724:13 | +| TryCatch(bool) -> void | 1 | 1 | ExitFunction | ir.cpp:724:6:724:13 | +| TryCatch(bool) -> void | 2 | 0 | Unwind | ir.cpp:724:6:724:13 | +| TryCatch(bool) -> void | 3 | 0 | VariableAddress[#throw728:7] | ir.cpp:728:7:728:28 | +| TryCatch(bool) -> void | 3 | 1 | StringConstant["string literal"] | ir.cpp:728:13:728:28 | +| TryCatch(bool) -> void | 3 | 2 | Convert | ir.cpp:728:13:728:28 | +| TryCatch(bool) -> void | 3 | 3 | Store | ir.cpp:728:13:728:28 | +| TryCatch(bool) -> void | 3 | 4 | ThrowValue | ir.cpp:728:7:728:28 | +| TryCatch(bool) -> void | 4 | 0 | VariableAddress[x] | ir.cpp:730:14:730:14 | +| TryCatch(bool) -> void | 4 | 1 | Load | ir.cpp:730:14:730:14 | +| TryCatch(bool) -> void | 4 | 2 | Constant[2] | ir.cpp:730:18:730:18 | +| TryCatch(bool) -> void | 4 | 3 | CompareLT | ir.cpp:730:14:730:18 | +| TryCatch(bool) -> void | 4 | 4 | ConditionalBranch | ir.cpp:730:14:730:18 | +| TryCatch(bool) -> void | 5 | 0 | VariableAddress[b] | ir.cpp:731:11:731:11 | +| TryCatch(bool) -> void | 5 | 1 | Load | ir.cpp:731:11:731:11 | +| TryCatch(bool) -> void | 5 | 2 | ConditionalBranch | ir.cpp:731:11:731:11 | +| TryCatch(bool) -> void | 6 | 0 | Constant[7] | ir.cpp:731:15:731:15 | +| TryCatch(bool) -> void | 6 | 1 | VariableAddress[#temp731:11] | ir.cpp:731:11:731:47 | +| TryCatch(bool) -> void | 6 | 2 | Store | ir.cpp:731:11:731:47 | +| TryCatch(bool) -> void | 6 | 3 | VariableAddress[#temp731:11] | ir.cpp:731:11:731:47 | +| TryCatch(bool) -> void | 6 | 4 | Load | ir.cpp:731:11:731:47 | +| TryCatch(bool) -> void | 6 | 5 | VariableAddress[x] | ir.cpp:731:7:731:7 | +| TryCatch(bool) -> void | 6 | 6 | Store | ir.cpp:731:7:731:47 | +| TryCatch(bool) -> void | 7 | 0 | VariableAddress[#throw731:19] | ir.cpp:731:19:731:47 | +| TryCatch(bool) -> void | 7 | 1 | FunctionAddress[String] | ir.cpp:731:19:731:47 | +| TryCatch(bool) -> void | 7 | 2 | StringConstant["String object"] | ir.cpp:731:32:731:46 | +| TryCatch(bool) -> void | 7 | 3 | Convert | ir.cpp:731:32:731:46 | +| TryCatch(bool) -> void | 7 | 4 | Invoke | ir.cpp:731:19:731:47 | +| TryCatch(bool) -> void | 7 | 5 | ThrowValue | ir.cpp:731:19:731:47 | +| TryCatch(bool) -> void | 8 | 0 | Constant[7] | ir.cpp:733:9:733:9 | +| TryCatch(bool) -> void | 8 | 1 | VariableAddress[x] | ir.cpp:733:5:733:5 | +| TryCatch(bool) -> void | 8 | 2 | Store | ir.cpp:733:5:733:9 | +| TryCatch(bool) -> void | 9 | 0 | CatchByType[const char *] | ir.cpp:735:25:737:3 | +| TryCatch(bool) -> void | 10 | 0 | InitializeParameter[s] | ir.cpp:735:22:735:22 | +| TryCatch(bool) -> void | 10 | 1 | VariableAddress[s] | ir.cpp:735:22:735:22 | +| TryCatch(bool) -> void | 10 | 2 | Store | ir.cpp:735:22:735:22 | +| TryCatch(bool) -> void | 10 | 3 | VariableAddress[#throw736:5] | ir.cpp:736:5:736:19 | +| TryCatch(bool) -> void | 10 | 4 | FunctionAddress[String] | ir.cpp:736:5:736:19 | +| TryCatch(bool) -> void | 10 | 5 | VariableAddress[s] | ir.cpp:736:18:736:18 | +| TryCatch(bool) -> void | 10 | 6 | Load | ir.cpp:736:18:736:18 | +| TryCatch(bool) -> void | 10 | 7 | Invoke | ir.cpp:736:5:736:19 | +| TryCatch(bool) -> void | 10 | 8 | ThrowValue | ir.cpp:736:5:736:19 | +| TryCatch(bool) -> void | 11 | 0 | CatchByType[const String &] | ir.cpp:738:27:739:3 | +| TryCatch(bool) -> void | 12 | 0 | InitializeParameter[e] | ir.cpp:738:24:738:24 | +| TryCatch(bool) -> void | 12 | 1 | VariableAddress[e] | ir.cpp:738:24:738:24 | +| TryCatch(bool) -> void | 12 | 2 | Store | ir.cpp:738:24:738:24 | +| TryCatch(bool) -> void | 12 | 3 | NoOp | ir.cpp:738:27:739:3 | +| TryCatch(bool) -> void | 13 | 0 | CatchAny | ir.cpp:740:15:742:3 | +| TryCatch(bool) -> void | 13 | 1 | ReThrow | ir.cpp:741:5:741:9 | +| TryCatch(bool) -> void | 14 | 0 | NoOp | ir.cpp:743:1:743:1 | +| TryCatch(bool) -> void | 14 | 1 | ReturnVoid | ir.cpp:724:6:724:13 | +| UninitializedVariables() -> void | 0 | 0 | EnterFunction | ir.cpp:230:6:230:27 | +| UninitializedVariables() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:230:6:230:27 | +| UninitializedVariables() -> void | 0 | 2 | VariableAddress[x] | ir.cpp:231:9:231:9 | +| UninitializedVariables() -> void | 0 | 3 | Uninitialized | ir.cpp:231:9:231:9 | +| UninitializedVariables() -> void | 0 | 4 | Store | ir.cpp:231:9:231:9 | +| UninitializedVariables() -> void | 0 | 5 | VariableAddress[y] | ir.cpp:232:9:232:9 | +| UninitializedVariables() -> void | 0 | 6 | VariableAddress[x] | ir.cpp:232:13:232:13 | +| UninitializedVariables() -> void | 0 | 7 | Load | ir.cpp:232:13:232:13 | +| UninitializedVariables() -> void | 0 | 8 | Store | ir.cpp:232:13:232:13 | +| UninitializedVariables() -> void | 0 | 9 | NoOp | ir.cpp:233:1:233:1 | +| UninitializedVariables() -> void | 0 | 10 | ReturnVoid | ir.cpp:230:6:230:27 | +| UninitializedVariables() -> void | 0 | 11 | UnmodeledUse | ir.cpp:230:6:230:27 | +| UninitializedVariables() -> void | 0 | 12 | ExitFunction | ir.cpp:230:6:230:27 | +| UnionInit(int, float) -> void | 0 | 0 | EnterFunction | ir.cpp:530:6:530:14 | +| UnionInit(int, float) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:530:6:530:14 | +| UnionInit(int, float) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:530:20:530:20 | +| UnionInit(int, float) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:530:20:530:20 | +| UnionInit(int, float) -> void | 0 | 4 | Store | ir.cpp:530:20:530:20 | +| UnionInit(int, float) -> void | 0 | 5 | InitializeParameter[f] | ir.cpp:530:29:530:29 | +| UnionInit(int, float) -> void | 0 | 6 | VariableAddress[f] | ir.cpp:530:29:530:29 | +| UnionInit(int, float) -> void | 0 | 7 | Store | ir.cpp:530:29:530:29 | +| UnionInit(int, float) -> void | 0 | 8 | VariableAddress[u1] | ir.cpp:531:7:531:8 | +| UnionInit(int, float) -> void | 0 | 9 | FieldAddress[d] | ir.cpp:531:11:531:16 | +| UnionInit(int, float) -> void | 0 | 10 | VariableAddress[f] | ir.cpp:531:14:531:14 | +| UnionInit(int, float) -> void | 0 | 11 | Load | ir.cpp:531:14:531:14 | +| UnionInit(int, float) -> void | 0 | 12 | Convert | ir.cpp:531:14:531:14 | +| UnionInit(int, float) -> void | 0 | 13 | Store | ir.cpp:531:14:531:14 | +| UnionInit(int, float) -> void | 0 | 14 | NoOp | ir.cpp:533:1:533:1 | +| UnionInit(int, float) -> void | 0 | 15 | ReturnVoid | ir.cpp:530:6:530:14 | +| UnionInit(int, float) -> void | 0 | 16 | UnmodeledUse | ir.cpp:530:6:530:14 | +| UnionInit(int, float) -> void | 0 | 17 | ExitFunction | ir.cpp:530:6:530:14 | +| VarArgUsage(int) -> void | 0 | 0 | EnterFunction | ir.cpp:888:6:888:16 | +| VarArgUsage(int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:888:6:888:16 | +| VarArgUsage(int) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:888:22:888:22 | +| VarArgUsage(int) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:888:22:888:22 | +| VarArgUsage(int) -> void | 0 | 4 | Store | ir.cpp:888:22:888:22 | +| VarArgUsage(int) -> void | 0 | 5 | VariableAddress[args] | ir.cpp:889:21:889:24 | +| VarArgUsage(int) -> void | 0 | 6 | Uninitialized | ir.cpp:889:21:889:24 | +| VarArgUsage(int) -> void | 0 | 7 | Store | ir.cpp:889:21:889:24 | +| VarArgUsage(int) -> void | 0 | 8 | VariableAddress[args] | ir.cpp:891:22:891:25 | +| VarArgUsage(int) -> void | 0 | 9 | Convert | ir.cpp:891:22:891:25 | +| VarArgUsage(int) -> void | 0 | 10 | VariableAddress[x] | ir.cpp:891:28:891:28 | +| VarArgUsage(int) -> void | 0 | 11 | VarArgsStart | ir.cpp:891:3:891:29 | +| VarArgUsage(int) -> void | 0 | 12 | VariableAddress[args2] | ir.cpp:892:21:892:25 | +| VarArgUsage(int) -> void | 0 | 13 | Uninitialized | ir.cpp:892:21:892:25 | +| VarArgUsage(int) -> void | 0 | 14 | Store | ir.cpp:892:21:892:25 | +| VarArgUsage(int) -> void | 0 | 15 | VariableAddress[args2] | ir.cpp:893:22:893:26 | +| VarArgUsage(int) -> void | 0 | 16 | Convert | ir.cpp:893:22:893:26 | +| VarArgUsage(int) -> void | 0 | 17 | VariableAddress[args] | ir.cpp:893:29:893:32 | +| VarArgUsage(int) -> void | 0 | 18 | Convert | ir.cpp:893:29:893:32 | +| VarArgUsage(int) -> void | 0 | 19 | VarArgsStart | ir.cpp:893:3:893:33 | +| VarArgUsage(int) -> void | 0 | 20 | VariableAddress[d] | ir.cpp:894:10:894:10 | +| VarArgUsage(int) -> void | 0 | 21 | VariableAddress[args] | ir.cpp:894:31:894:34 | +| VarArgUsage(int) -> void | 0 | 22 | Convert | ir.cpp:894:31:894:34 | +| VarArgUsage(int) -> void | 0 | 23 | VarArg | ir.cpp:894:14:894:43 | +| VarArgUsage(int) -> void | 0 | 24 | Load | ir.cpp:894:14:894:43 | +| VarArgUsage(int) -> void | 0 | 25 | Store | ir.cpp:894:14:894:43 | +| VarArgUsage(int) -> void | 0 | 26 | VariableAddress[f] | ir.cpp:895:9:895:9 | +| VarArgUsage(int) -> void | 0 | 27 | VariableAddress[args] | ir.cpp:895:30:895:33 | +| VarArgUsage(int) -> void | 0 | 28 | Convert | ir.cpp:895:30:895:33 | +| VarArgUsage(int) -> void | 0 | 29 | VarArg | ir.cpp:895:30:895:33 | +| VarArgUsage(int) -> void | 0 | 30 | Load | ir.cpp:895:30:895:33 | +| VarArgUsage(int) -> void | 0 | 31 | Convert | ir.cpp:895:13:895:41 | +| VarArgUsage(int) -> void | 0 | 32 | Store | ir.cpp:895:13:895:41 | +| VarArgUsage(int) -> void | 0 | 33 | VariableAddress[args] | ir.cpp:896:20:896:23 | +| VarArgUsage(int) -> void | 0 | 34 | Convert | ir.cpp:896:20:896:23 | +| VarArgUsage(int) -> void | 0 | 35 | VarArgsEnd | ir.cpp:896:3:896:24 | +| VarArgUsage(int) -> void | 0 | 36 | VariableAddress[args2] | ir.cpp:897:20:897:24 | +| VarArgUsage(int) -> void | 0 | 37 | Convert | ir.cpp:897:20:897:24 | +| VarArgUsage(int) -> void | 0 | 38 | VarArgsEnd | ir.cpp:897:3:897:25 | +| VarArgUsage(int) -> void | 0 | 39 | NoOp | ir.cpp:898:1:898:1 | +| VarArgUsage(int) -> void | 0 | 40 | ReturnVoid | ir.cpp:888:6:888:16 | +| VarArgUsage(int) -> void | 0 | 41 | UnmodeledUse | ir.cpp:888:6:888:16 | +| VarArgUsage(int) -> void | 0 | 42 | ExitFunction | ir.cpp:888:6:888:16 | +| VarArgs() -> void | 0 | 0 | EnterFunction | ir.cpp:584:6:584:12 | +| VarArgs() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:584:6:584:12 | +| VarArgs() -> void | 0 | 2 | FunctionAddress[VarArgFunction] | ir.cpp:585:5:585:18 | +| VarArgs() -> void | 0 | 3 | StringConstant["%d %s"] | ir.cpp:585:20:585:26 | +| VarArgs() -> void | 0 | 4 | Convert | ir.cpp:585:20:585:26 | +| VarArgs() -> void | 0 | 5 | Constant[1] | ir.cpp:585:29:585:29 | +| VarArgs() -> void | 0 | 6 | StringConstant["string"] | ir.cpp:585:32:585:39 | +| VarArgs() -> void | 0 | 7 | Convert | ir.cpp:585:32:585:39 | +| VarArgs() -> void | 0 | 8 | Invoke | ir.cpp:585:5:585:18 | +| VarArgs() -> void | 0 | 9 | NoOp | ir.cpp:586:1:586:1 | +| VarArgs() -> void | 0 | 10 | ReturnVoid | ir.cpp:584:6:584:12 | +| VarArgs() -> void | 0 | 11 | UnmodeledUse | ir.cpp:584:6:584:12 | +| VarArgs() -> void | 0 | 12 | ExitFunction | ir.cpp:584:6:584:12 | +| WhileStatements(int) -> void | 0 | 0 | EnterFunction | ir.cpp:253:6:253:20 | +| WhileStatements(int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:253:6:253:20 | +| WhileStatements(int) -> void | 0 | 2 | InitializeParameter[n] | ir.cpp:253:26:253:26 | +| WhileStatements(int) -> void | 0 | 3 | VariableAddress[n] | ir.cpp:253:26:253:26 | +| WhileStatements(int) -> void | 0 | 4 | Store | ir.cpp:253:26:253:26 | +| WhileStatements(int) -> void | 1 | 0 | Constant[1] | ir.cpp:255:14:255:14 | +| WhileStatements(int) -> void | 1 | 1 | VariableAddress[n] | ir.cpp:255:9:255:9 | +| WhileStatements(int) -> void | 1 | 2 | Load | ir.cpp:255:9:255:14 | +| WhileStatements(int) -> void | 1 | 3 | Sub | ir.cpp:255:9:255:14 | +| WhileStatements(int) -> void | 1 | 4 | Store | ir.cpp:255:9:255:14 | +| WhileStatements(int) -> void | 2 | 0 | NoOp | ir.cpp:257:1:257:1 | +| WhileStatements(int) -> void | 2 | 1 | ReturnVoid | ir.cpp:253:6:253:20 | +| WhileStatements(int) -> void | 2 | 2 | UnmodeledUse | ir.cpp:253:6:253:20 | +| WhileStatements(int) -> void | 2 | 3 | ExitFunction | ir.cpp:253:6:253:20 | +| WhileStatements(int) -> void | 3 | 0 | Phi | ir.cpp:254:12:254:12 | +| WhileStatements(int) -> void | 3 | 1 | VariableAddress[n] | ir.cpp:254:12:254:12 | +| WhileStatements(int) -> void | 3 | 2 | Load | ir.cpp:254:12:254:12 | +| WhileStatements(int) -> void | 3 | 3 | Constant[0] | ir.cpp:254:16:254:16 | +| WhileStatements(int) -> void | 3 | 4 | CompareGT | ir.cpp:254:12:254:16 | +| WhileStatements(int) -> void | 3 | 5 | ConditionalBranch | ir.cpp:254:12:254:16 | +| min(int, int) -> int | 0 | 0 | EnterFunction | ir.cpp:704:3:704:5 | +| min(int, int) -> int | 0 | 1 | UnmodeledDefinition | ir.cpp:704:3:704:5 | +| min(int, int) -> int | 0 | 2 | InitializeParameter[x] | ir.cpp:704:9:704:9 | +| min(int, int) -> int | 0 | 3 | VariableAddress[x] | ir.cpp:704:9:704:9 | +| min(int, int) -> int | 0 | 4 | Store | ir.cpp:704:9:704:9 | +| min(int, int) -> int | 0 | 5 | InitializeParameter[y] | ir.cpp:704:14:704:14 | +| min(int, int) -> int | 0 | 6 | VariableAddress[y] | ir.cpp:704:14:704:14 | +| min(int, int) -> int | 0 | 7 | Store | ir.cpp:704:14:704:14 | +| min(int, int) -> int | 0 | 8 | VariableAddress[#return] | ir.cpp:705:3:705:25 | +| min(int, int) -> int | 0 | 9 | VariableAddress[x] | ir.cpp:705:11:705:11 | +| min(int, int) -> int | 0 | 10 | Load | ir.cpp:705:11:705:11 | +| min(int, int) -> int | 0 | 11 | VariableAddress[y] | ir.cpp:705:15:705:15 | +| min(int, int) -> int | 0 | 12 | Load | ir.cpp:705:15:705:15 | +| min(int, int) -> int | 0 | 13 | CompareLT | ir.cpp:705:11:705:15 | +| min(int, int) -> int | 0 | 14 | ConditionalBranch | ir.cpp:705:11:705:15 | +| min(int, int) -> int | 1 | 0 | VariableAddress[x] | ir.cpp:705:20:705:20 | +| min(int, int) -> int | 1 | 1 | Load | ir.cpp:705:20:705:20 | +| min(int, int) -> int | 1 | 2 | VariableAddress[#temp705:10] | ir.cpp:705:10:705:24 | +| min(int, int) -> int | 1 | 3 | Store | ir.cpp:705:10:705:24 | +| min(int, int) -> int | 2 | 0 | VariableAddress[y] | ir.cpp:705:24:705:24 | +| min(int, int) -> int | 2 | 1 | Load | ir.cpp:705:24:705:24 | +| min(int, int) -> int | 2 | 2 | VariableAddress[#temp705:10] | ir.cpp:705:10:705:24 | +| min(int, int) -> int | 2 | 3 | Store | ir.cpp:705:10:705:24 | +| min(int, int) -> int | 3 | 0 | Phi | ir.cpp:705:10:705:24 | +| min(int, int) -> int | 3 | 1 | VariableAddress[#temp705:10] | ir.cpp:705:10:705:24 | +| min(int, int) -> int | 3 | 2 | Load | ir.cpp:705:10:705:24 | +| min(int, int) -> int | 3 | 3 | Store | ir.cpp:705:10:705:24 | +| min(int, int) -> int | 3 | 4 | VariableAddress[#return] | ir.cpp:704:3:704:5 | +| min(int, int) -> int | 3 | 5 | ReturnValue | ir.cpp:704:3:704:5 | +| min(int, int) -> int | 3 | 6 | UnmodeledUse | ir.cpp:704:3:704:5 | +| min(int, int) -> int | 3 | 7 | ExitFunction | ir.cpp:704:3:704:5 | +printIRGraphEdges +| Break(int) -> void | 0 | 5 | Goto | +| Break(int) -> void | 1 | 2 | True | +| Break(int) -> void | 1 | 3 | False | +| Break(int) -> void | 2 | 4 | Goto | +| Break(int) -> void | 3 | 5 | Goto | +| Break(int) -> void | 5 | 1 | True | +| Break(int) -> void | 5 | 4 | False | +| ConditionValues(bool, bool) -> void | 0 | 1 | True | +| ConditionValues(bool, bool) -> void | 0 | 10 | False | +| ConditionValues(bool, bool) -> void | 1 | 10 | False | +| ConditionValues(bool, bool) -> void | 1 | 12 | True | +| ConditionValues(bool, bool) -> void | 2 | 3 | Goto | +| ConditionValues(bool, bool) -> void | 3 | 8 | True | +| ConditionValues(bool, bool) -> void | 3 | 9 | False | +| ConditionValues(bool, bool) -> void | 4 | 3 | Goto | +| ConditionValues(bool, bool) -> void | 5 | 2 | False | +| ConditionValues(bool, bool) -> void | 5 | 4 | True | +| ConditionValues(bool, bool) -> void | 6 | 7 | Goto | +| ConditionValues(bool, bool) -> void | 8 | 7 | Goto | +| ConditionValues(bool, bool) -> void | 9 | 6 | False | +| ConditionValues(bool, bool) -> void | 9 | 8 | True | +| ConditionValues(bool, bool) -> void | 10 | 11 | Goto | +| ConditionValues(bool, bool) -> void | 11 | 4 | True | +| ConditionValues(bool, bool) -> void | 11 | 5 | False | +| ConditionValues(bool, bool) -> void | 12 | 11 | Goto | +| Conditional(bool, int, int) -> void | 0 | 1 | True | +| Conditional(bool, int, int) -> void | 0 | 2 | False | +| Conditional(bool, int, int) -> void | 1 | 3 | Goto | +| Conditional(bool, int, int) -> void | 2 | 3 | Goto | +| Conditional_LValue(bool) -> void | 0 | 2 | True | +| Conditional_LValue(bool) -> void | 0 | 3 | False | +| Conditional_LValue(bool) -> void | 2 | 1 | Goto | +| Conditional_LValue(bool) -> void | 3 | 1 | Goto | +| Conditional_Void(bool) -> void | 0 | 2 | True | +| Conditional_Void(bool) -> void | 0 | 3 | False | +| Conditional_Void(bool) -> void | 2 | 1 | Goto | +| Conditional_Void(bool) -> void | 3 | 1 | Goto | +| Continue(int) -> void | 0 | 1 | Goto | +| Continue(int) -> void | 1 | 2 | True | +| Continue(int) -> void | 1 | 3 | False | +| Continue(int) -> void | 2 | 4 | Goto | +| Continue(int) -> void | 3 | 4 | Goto | +| Continue(int) -> void | 4 | 1 | True | +| Continue(int) -> void | 4 | 5 | False | +| DoStatements(int) -> void | 0 | 1 | Goto | +| DoStatements(int) -> void | 1 | 1 | True | +| DoStatements(int) -> void | 1 | 2 | False | +| EarlyReturn(int, int) -> void | 0 | 2 | True | +| EarlyReturn(int, int) -> void | 0 | 3 | False | +| EarlyReturn(int, int) -> void | 2 | 1 | Goto | +| EarlyReturn(int, int) -> void | 3 | 1 | Goto | +| EarlyReturnValue(int, int) -> int | 0 | 2 | True | +| EarlyReturnValue(int, int) -> int | 0 | 3 | False | +| EarlyReturnValue(int, int) -> int | 2 | 1 | Goto | +| EarlyReturnValue(int, int) -> int | 3 | 1 | Goto | +| EnumSwitch(E) -> int | 0 | 2 | Case[1] | +| EnumSwitch(E) -> int | 0 | 3 | Default | +| EnumSwitch(E) -> int | 0 | 4 | Case[0] | +| EnumSwitch(E) -> int | 2 | 1 | Goto | +| EnumSwitch(E) -> int | 3 | 1 | Goto | +| EnumSwitch(E) -> int | 4 | 1 | Goto | +| For_Break() -> void | 0 | 1 | Goto | +| For_Break() -> void | 1 | 3 | True | +| For_Break() -> void | 1 | 5 | False | +| For_Break() -> void | 2 | 1 | Goto | +| For_Break() -> void | 3 | 2 | False | +| For_Break() -> void | 3 | 4 | True | +| For_Break() -> void | 4 | 5 | Goto | +| For_Condition() -> void | 0 | 1 | Goto | +| For_Condition() -> void | 1 | 2 | True | +| For_Condition() -> void | 1 | 3 | False | +| For_Condition() -> void | 2 | 1 | Goto | +| For_ConditionUpdate() -> void | 0 | 1 | Goto | +| For_ConditionUpdate() -> void | 1 | 2 | True | +| For_ConditionUpdate() -> void | 1 | 3 | False | +| For_ConditionUpdate() -> void | 2 | 1 | Goto | +| For_Continue_NoUpdate() -> void | 0 | 1 | Goto | +| For_Continue_NoUpdate() -> void | 1 | 2 | True | +| For_Continue_NoUpdate() -> void | 1 | 5 | False | +| For_Continue_NoUpdate() -> void | 2 | 3 | True | +| For_Continue_NoUpdate() -> void | 2 | 4 | False | +| For_Continue_NoUpdate() -> void | 3 | 4 | Goto | +| For_Continue_NoUpdate() -> void | 4 | 1 | Goto | +| For_Continue_Update() -> void | 0 | 1 | Goto | +| For_Continue_Update() -> void | 1 | 2 | True | +| For_Continue_Update() -> void | 1 | 5 | False | +| For_Continue_Update() -> void | 2 | 3 | True | +| For_Continue_Update() -> void | 2 | 4 | False | +| For_Continue_Update() -> void | 3 | 4 | Goto | +| For_Continue_Update() -> void | 4 | 1 | Goto | +| For_Empty() -> void | 0 | 2 | Goto | +| For_Empty() -> void | 2 | 2 | Goto | +| For_Init() -> void | 0 | 2 | Goto | +| For_Init() -> void | 2 | 2 | Goto | +| For_InitCondition() -> void | 0 | 1 | Goto | +| For_InitCondition() -> void | 1 | 2 | True | +| For_InitCondition() -> void | 1 | 3 | False | +| For_InitCondition() -> void | 2 | 1 | Goto | +| For_InitConditionUpdate() -> void | 0 | 1 | Goto | +| For_InitConditionUpdate() -> void | 1 | 2 | True | +| For_InitConditionUpdate() -> void | 1 | 3 | False | +| For_InitConditionUpdate() -> void | 2 | 1 | Goto | +| For_InitUpdate() -> void | 0 | 2 | Goto | +| For_InitUpdate() -> void | 2 | 2 | Goto | +| For_Update() -> void | 0 | 2 | Goto | +| For_Update() -> void | 2 | 2 | Goto | +| IfStatements(bool, int, int) -> void | 0 | 1 | False | +| IfStatements(bool, int, int) -> void | 0 | 7 | True | +| IfStatements(bool, int, int) -> void | 1 | 2 | True | +| IfStatements(bool, int, int) -> void | 1 | 3 | False | +| IfStatements(bool, int, int) -> void | 2 | 3 | Goto | +| IfStatements(bool, int, int) -> void | 3 | 4 | True | +| IfStatements(bool, int, int) -> void | 3 | 5 | False | +| IfStatements(bool, int, int) -> void | 4 | 6 | Goto | +| IfStatements(bool, int, int) -> void | 5 | 6 | Goto | +| IfStatements(bool, int, int) -> void | 7 | 1 | Goto | +| LogicalAnd(bool, bool) -> void | 0 | 1 | True | +| LogicalAnd(bool, bool) -> void | 0 | 3 | False | +| LogicalAnd(bool, bool) -> void | 1 | 2 | True | +| LogicalAnd(bool, bool) -> void | 1 | 3 | False | +| LogicalAnd(bool, bool) -> void | 2 | 3 | Goto | +| LogicalAnd(bool, bool) -> void | 3 | 4 | True | +| LogicalAnd(bool, bool) -> void | 3 | 6 | False | +| LogicalAnd(bool, bool) -> void | 4 | 5 | True | +| LogicalAnd(bool, bool) -> void | 4 | 6 | False | +| LogicalAnd(bool, bool) -> void | 5 | 7 | Goto | +| LogicalAnd(bool, bool) -> void | 6 | 7 | Goto | +| LogicalNot(bool, bool) -> void | 0 | 1 | False | +| LogicalNot(bool, bool) -> void | 0 | 2 | True | +| LogicalNot(bool, bool) -> void | 1 | 2 | Goto | +| LogicalNot(bool, bool) -> void | 2 | 3 | True | +| LogicalNot(bool, bool) -> void | 2 | 4 | False | +| LogicalNot(bool, bool) -> void | 3 | 4 | False | +| LogicalNot(bool, bool) -> void | 3 | 5 | True | +| LogicalNot(bool, bool) -> void | 4 | 6 | Goto | +| LogicalNot(bool, bool) -> void | 5 | 6 | Goto | +| LogicalOr(bool, bool) -> void | 0 | 1 | False | +| LogicalOr(bool, bool) -> void | 0 | 2 | True | +| LogicalOr(bool, bool) -> void | 1 | 2 | True | +| LogicalOr(bool, bool) -> void | 1 | 3 | False | +| LogicalOr(bool, bool) -> void | 2 | 3 | Goto | +| LogicalOr(bool, bool) -> void | 3 | 4 | False | +| LogicalOr(bool, bool) -> void | 3 | 5 | True | +| LogicalOr(bool, bool) -> void | 4 | 5 | True | +| LogicalOr(bool, bool) -> void | 4 | 6 | False | +| LogicalOr(bool, bool) -> void | 5 | 7 | Goto | +| LogicalOr(bool, bool) -> void | 6 | 7 | Goto | +| Switch(int) -> void | 0 | 2 | Case[-1] | +| Switch(int) -> void | 0 | 3 | Case[1] | +| Switch(int) -> void | 0 | 4 | Case[2] | +| Switch(int) -> void | 0 | 5 | Case[3] | +| Switch(int) -> void | 0 | 6 | Case[4] | +| Switch(int) -> void | 0 | 7 | Default | +| Switch(int) -> void | 1 | 2 | Goto | +| Switch(int) -> void | 2 | 9 | Goto | +| Switch(int) -> void | 3 | 4 | Goto | +| Switch(int) -> void | 4 | 9 | Goto | +| Switch(int) -> void | 5 | 6 | Goto | +| Switch(int) -> void | 6 | 9 | Goto | +| Switch(int) -> void | 7 | 9 | Goto | +| Switch(int) -> void | 8 | 9 | Goto | +| TryCatch(bool) -> void | 0 | 3 | True | +| TryCatch(bool) -> void | 0 | 4 | False | +| TryCatch(bool) -> void | 2 | 1 | Goto | +| TryCatch(bool) -> void | 3 | 9 | Exception | +| TryCatch(bool) -> void | 4 | 5 | True | +| TryCatch(bool) -> void | 4 | 8 | False | +| TryCatch(bool) -> void | 5 | 6 | True | +| TryCatch(bool) -> void | 5 | 7 | False | +| TryCatch(bool) -> void | 6 | 8 | Goto | +| TryCatch(bool) -> void | 7 | 9 | Exception | +| TryCatch(bool) -> void | 8 | 14 | Goto | +| TryCatch(bool) -> void | 9 | 10 | Goto | +| TryCatch(bool) -> void | 9 | 11 | Exception | +| TryCatch(bool) -> void | 10 | 2 | Exception | +| TryCatch(bool) -> void | 11 | 12 | Goto | +| TryCatch(bool) -> void | 11 | 13 | Exception | +| TryCatch(bool) -> void | 12 | 14 | Goto | +| TryCatch(bool) -> void | 13 | 2 | Exception | +| TryCatch(bool) -> void | 14 | 1 | Goto | +| WhileStatements(int) -> void | 0 | 3 | Goto | +| WhileStatements(int) -> void | 1 | 3 | Goto | +| WhileStatements(int) -> void | 3 | 1 | True | +| WhileStatements(int) -> void | 3 | 2 | False | +| min(int, int) -> int | 0 | 1 | True | +| min(int, int) -> int | 0 | 2 | False | +| min(int, int) -> int | 1 | 3 | Goto | +| min(int, int) -> int | 2 | 3 | Goto | +printIRGraphDestinationOperands +| AddressOf() -> int * | 0 | 1 | 0 | @mu0_1(unknown) | +| AddressOf() -> int * | 0 | 2 | 0 | @r0_2(glval:int *) | +| AddressOf() -> int * | 0 | 3 | 0 | @r0_3(glval:int) | +| AddressOf() -> int * | 0 | 4 | 0 | @m0_4(int *) | +| AddressOf() -> int * | 0 | 5 | 0 | @r0_5(glval:int *) | +| ArrayAccess(int *, int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| ArrayAccess(int *, int) -> void | 0 | 2 | 0 | @r0_2(int *) | +| ArrayAccess(int *, int) -> void | 0 | 3 | 0 | @r0_3(glval:int *) | +| ArrayAccess(int *, int) -> void | 0 | 4 | 0 | @m0_4(int *) | +| ArrayAccess(int *, int) -> void | 0 | 5 | 0 | @r0_5(int) | +| ArrayAccess(int *, int) -> void | 0 | 6 | 0 | @r0_6(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 7 | 0 | @m0_7(int) | +| ArrayAccess(int *, int) -> void | 0 | 8 | 0 | @r0_8(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 9 | 0 | @r0_9(int) | +| ArrayAccess(int *, int) -> void | 0 | 10 | 0 | @m0_10(int) | +| ArrayAccess(int *, int) -> void | 0 | 11 | 0 | @r0_11(glval:int *) | +| ArrayAccess(int *, int) -> void | 0 | 12 | 0 | @r0_12(int *) | +| ArrayAccess(int *, int) -> void | 0 | 13 | 0 | @r0_13(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 14 | 0 | @r0_14(int) | +| ArrayAccess(int *, int) -> void | 0 | 15 | 0 | @r0_15(int *) | +| ArrayAccess(int *, int) -> void | 0 | 16 | 0 | @r0_16(int) | +| ArrayAccess(int *, int) -> void | 0 | 17 | 0 | @r0_17(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 18 | 0 | @m0_18(int) | +| ArrayAccess(int *, int) -> void | 0 | 19 | 0 | @r0_19(glval:int *) | +| ArrayAccess(int *, int) -> void | 0 | 20 | 0 | @r0_20(int *) | +| ArrayAccess(int *, int) -> void | 0 | 21 | 0 | @r0_21(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 22 | 0 | @r0_22(int) | +| ArrayAccess(int *, int) -> void | 0 | 23 | 0 | @r0_23(int *) | +| ArrayAccess(int *, int) -> void | 0 | 24 | 0 | @r0_24(int) | +| ArrayAccess(int *, int) -> void | 0 | 25 | 0 | @r0_25(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 26 | 0 | @m0_26(int) | +| ArrayAccess(int *, int) -> void | 0 | 27 | 0 | @r0_27(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 28 | 0 | @r0_28(int) | +| ArrayAccess(int *, int) -> void | 0 | 29 | 0 | @r0_29(glval:int *) | +| ArrayAccess(int *, int) -> void | 0 | 30 | 0 | @r0_30(int *) | +| ArrayAccess(int *, int) -> void | 0 | 31 | 0 | @r0_31(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 32 | 0 | @r0_32(int) | +| ArrayAccess(int *, int) -> void | 0 | 33 | 0 | @r0_33(int *) | +| ArrayAccess(int *, int) -> void | 0 | 34 | 0 | @mu0_34(int) | +| ArrayAccess(int *, int) -> void | 0 | 35 | 0 | @r0_35(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 36 | 0 | @r0_36(int) | +| ArrayAccess(int *, int) -> void | 0 | 37 | 0 | @r0_37(glval:int *) | +| ArrayAccess(int *, int) -> void | 0 | 38 | 0 | @r0_38(int *) | +| ArrayAccess(int *, int) -> void | 0 | 39 | 0 | @r0_39(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 40 | 0 | @r0_40(int) | +| ArrayAccess(int *, int) -> void | 0 | 41 | 0 | @r0_41(int *) | +| ArrayAccess(int *, int) -> void | 0 | 42 | 0 | @mu0_42(int) | +| ArrayAccess(int *, int) -> void | 0 | 43 | 0 | @r0_43(glval:int[10]) | +| ArrayAccess(int *, int) -> void | 0 | 44 | 0 | @r0_44(int[10]) | +| ArrayAccess(int *, int) -> void | 0 | 45 | 0 | @m0_45(int[10]) | +| ArrayAccess(int *, int) -> void | 0 | 46 | 0 | @r0_46(glval:int[10]) | +| ArrayAccess(int *, int) -> void | 0 | 47 | 0 | @r0_47(int *) | +| ArrayAccess(int *, int) -> void | 0 | 48 | 0 | @r0_48(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 49 | 0 | @r0_49(int) | +| ArrayAccess(int *, int) -> void | 0 | 50 | 0 | @r0_50(int *) | +| ArrayAccess(int *, int) -> void | 0 | 51 | 0 | @r0_51(int) | +| ArrayAccess(int *, int) -> void | 0 | 52 | 0 | @r0_52(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 53 | 0 | @m0_53(int) | +| ArrayAccess(int *, int) -> void | 0 | 54 | 0 | @r0_54(glval:int[10]) | +| ArrayAccess(int *, int) -> void | 0 | 55 | 0 | @r0_55(int *) | +| ArrayAccess(int *, int) -> void | 0 | 56 | 0 | @r0_56(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 57 | 0 | @r0_57(int) | +| ArrayAccess(int *, int) -> void | 0 | 58 | 0 | @r0_58(int *) | +| ArrayAccess(int *, int) -> void | 0 | 59 | 0 | @r0_59(int) | +| ArrayAccess(int *, int) -> void | 0 | 60 | 0 | @r0_60(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 61 | 0 | @m0_61(int) | +| ArrayAccess(int *, int) -> void | 0 | 62 | 0 | @r0_62(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 63 | 0 | @r0_63(int) | +| ArrayAccess(int *, int) -> void | 0 | 64 | 0 | @r0_64(glval:int[10]) | +| ArrayAccess(int *, int) -> void | 0 | 65 | 0 | @r0_65(int *) | +| ArrayAccess(int *, int) -> void | 0 | 66 | 0 | @r0_66(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 67 | 0 | @r0_67(int) | +| ArrayAccess(int *, int) -> void | 0 | 68 | 0 | @r0_68(int *) | +| ArrayAccess(int *, int) -> void | 0 | 69 | 0 | @mu0_69(int) | +| ArrayAccess(int *, int) -> void | 0 | 70 | 0 | @r0_70(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 71 | 0 | @r0_71(int) | +| ArrayAccess(int *, int) -> void | 0 | 72 | 0 | @r0_72(glval:int[10]) | +| ArrayAccess(int *, int) -> void | 0 | 73 | 0 | @r0_73(int *) | +| ArrayAccess(int *, int) -> void | 0 | 74 | 0 | @r0_74(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 75 | 0 | @r0_75(int) | +| ArrayAccess(int *, int) -> void | 0 | 76 | 0 | @r0_76(int *) | +| ArrayAccess(int *, int) -> void | 0 | 77 | 0 | @mu0_77(int) | +| ArrayConversions() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| ArrayConversions() -> void | 0 | 2 | 0 | @r0_2(glval:char[5]) | +| ArrayConversions() -> void | 0 | 3 | 0 | @r0_3(char[5]) | +| ArrayConversions() -> void | 0 | 4 | 0 | @m0_4(char[5]) | +| ArrayConversions() -> void | 0 | 5 | 0 | @r0_5(glval:char *) | +| ArrayConversions() -> void | 0 | 6 | 0 | @r0_6(glval:char[5]) | +| ArrayConversions() -> void | 0 | 7 | 0 | @r0_7(char *) | +| ArrayConversions() -> void | 0 | 8 | 0 | @r0_8(char *) | +| ArrayConversions() -> void | 0 | 9 | 0 | @m0_9(char *) | +| ArrayConversions() -> void | 0 | 10 | 0 | @r0_10(glval:char[5]) | +| ArrayConversions() -> void | 0 | 11 | 0 | @r0_11(char *) | +| ArrayConversions() -> void | 0 | 12 | 0 | @r0_12(glval:char *) | +| ArrayConversions() -> void | 0 | 13 | 0 | @m0_13(char *) | +| ArrayConversions() -> void | 0 | 14 | 0 | @r0_14(glval:char[5]) | +| ArrayConversions() -> void | 0 | 15 | 0 | @r0_15(char *) | +| ArrayConversions() -> void | 0 | 16 | 0 | @r0_16(int) | +| ArrayConversions() -> void | 0 | 17 | 0 | @r0_17(char *) | +| ArrayConversions() -> void | 0 | 18 | 0 | @r0_18(char *) | +| ArrayConversions() -> void | 0 | 19 | 0 | @r0_19(glval:char *) | +| ArrayConversions() -> void | 0 | 20 | 0 | @m0_20(char *) | +| ArrayConversions() -> void | 0 | 21 | 0 | @r0_21(glval:char[5]) | +| ArrayConversions() -> void | 0 | 22 | 0 | @r0_22(char *) | +| ArrayConversions() -> void | 0 | 23 | 0 | @r0_23(int) | +| ArrayConversions() -> void | 0 | 24 | 0 | @r0_24(char *) | +| ArrayConversions() -> void | 0 | 25 | 0 | @r0_25(glval:char *) | +| ArrayConversions() -> void | 0 | 26 | 0 | @m0_26(char *) | +| ArrayConversions() -> void | 0 | 27 | 0 | @r0_27(glval:char(&)[5]) | +| ArrayConversions() -> void | 0 | 28 | 0 | @r0_28(glval:char[5]) | +| ArrayConversions() -> void | 0 | 29 | 0 | @m0_29(char(&)[5]) | +| ArrayConversions() -> void | 0 | 30 | 0 | @r0_30(glval:char(&)[5]) | +| ArrayConversions() -> void | 0 | 31 | 0 | @r0_31(glval:char[5]) | +| ArrayConversions() -> void | 0 | 32 | 0 | @m0_32(char(&)[5]) | +| ArrayConversions() -> void | 0 | 33 | 0 | @r0_33(glval:char(*)[5]) | +| ArrayConversions() -> void | 0 | 34 | 0 | @r0_34(glval:char[5]) | +| ArrayConversions() -> void | 0 | 35 | 0 | @r0_35(char(*)[5]) | +| ArrayConversions() -> void | 0 | 36 | 0 | @m0_36(char(*)[5]) | +| ArrayConversions() -> void | 0 | 37 | 0 | @r0_37(glval:char[5]) | +| ArrayConversions() -> void | 0 | 38 | 0 | @r0_38(glval:char(*)[5]) | +| ArrayConversions() -> void | 0 | 39 | 0 | @m0_39(char(*)[5]) | +| ArrayInit(int, float) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| ArrayInit(int, float) -> void | 0 | 2 | 0 | @r0_2(int) | +| ArrayInit(int, float) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| ArrayInit(int, float) -> void | 0 | 4 | 0 | @m0_4(int) | +| ArrayInit(int, float) -> void | 0 | 5 | 0 | @r0_5(float) | +| ArrayInit(int, float) -> void | 0 | 6 | 0 | @r0_6(glval:float) | +| ArrayInit(int, float) -> void | 0 | 7 | 0 | @m0_7(float) | +| ArrayInit(int, float) -> void | 0 | 8 | 0 | @r0_8(glval:int[3]) | +| ArrayInit(int, float) -> void | 0 | 9 | 0 | @r0_9(int) | +| ArrayInit(int, float) -> void | 0 | 10 | 0 | @r0_10(glval:int) | +| ArrayInit(int, float) -> void | 0 | 11 | 0 | @r0_11(unknown[12]) | +| ArrayInit(int, float) -> void | 0 | 12 | 0 | @mu0_12(unknown[12]) | +| ArrayInit(int, float) -> void | 0 | 13 | 0 | @r0_13(glval:int[3]) | +| ArrayInit(int, float) -> void | 0 | 14 | 0 | @r0_14(int) | +| ArrayInit(int, float) -> void | 0 | 15 | 0 | @r0_15(glval:int) | +| ArrayInit(int, float) -> void | 0 | 16 | 0 | @r0_16(glval:int) | +| ArrayInit(int, float) -> void | 0 | 17 | 0 | @r0_17(int) | +| ArrayInit(int, float) -> void | 0 | 18 | 0 | @mu0_18(int) | +| ArrayInit(int, float) -> void | 0 | 19 | 0 | @r0_19(int) | +| ArrayInit(int, float) -> void | 0 | 20 | 0 | @r0_20(glval:int) | +| ArrayInit(int, float) -> void | 0 | 21 | 0 | @r0_21(glval:float) | +| ArrayInit(int, float) -> void | 0 | 22 | 0 | @r0_22(float) | +| ArrayInit(int, float) -> void | 0 | 23 | 0 | @r0_23(int) | +| ArrayInit(int, float) -> void | 0 | 24 | 0 | @mu0_24(int) | +| ArrayInit(int, float) -> void | 0 | 25 | 0 | @r0_25(int) | +| ArrayInit(int, float) -> void | 0 | 26 | 0 | @r0_26(glval:int) | +| ArrayInit(int, float) -> void | 0 | 27 | 0 | @r0_27(int) | +| ArrayInit(int, float) -> void | 0 | 28 | 0 | @mu0_28(int) | +| ArrayInit(int, float) -> void | 0 | 29 | 0 | @r0_29(glval:int[3]) | +| ArrayInit(int, float) -> void | 0 | 30 | 0 | @r0_30(int) | +| ArrayInit(int, float) -> void | 0 | 31 | 0 | @r0_31(glval:int) | +| ArrayInit(int, float) -> void | 0 | 32 | 0 | @r0_32(glval:int) | +| ArrayInit(int, float) -> void | 0 | 33 | 0 | @r0_33(int) | +| ArrayInit(int, float) -> void | 0 | 34 | 0 | @mu0_34(int) | +| ArrayInit(int, float) -> void | 0 | 35 | 0 | @r0_35(int) | +| ArrayInit(int, float) -> void | 0 | 36 | 0 | @r0_36(glval:int) | +| ArrayInit(int, float) -> void | 0 | 37 | 0 | @r0_37(unknown[8]) | +| ArrayInit(int, float) -> void | 0 | 38 | 0 | @mu0_38(unknown[8]) | +| ArrayReferences() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| ArrayReferences() -> void | 0 | 2 | 0 | @r0_2(glval:int[10]) | +| ArrayReferences() -> void | 0 | 3 | 0 | @r0_3(int[10]) | +| ArrayReferences() -> void | 0 | 4 | 0 | @m0_4(int[10]) | +| ArrayReferences() -> void | 0 | 5 | 0 | @r0_5(glval:int(&)[10]) | +| ArrayReferences() -> void | 0 | 6 | 0 | @r0_6(glval:int[10]) | +| ArrayReferences() -> void | 0 | 7 | 0 | @m0_7(int(&)[10]) | +| ArrayReferences() -> void | 0 | 8 | 0 | @r0_8(glval:int) | +| ArrayReferences() -> void | 0 | 9 | 0 | @r0_9(glval:int(&)[10]) | +| ArrayReferences() -> void | 0 | 10 | 0 | @r0_10(int(&)[10]) | +| ArrayReferences() -> void | 0 | 11 | 0 | @r0_11(int *) | +| ArrayReferences() -> void | 0 | 12 | 0 | @r0_12(int) | +| ArrayReferences() -> void | 0 | 13 | 0 | @r0_13(int *) | +| ArrayReferences() -> void | 0 | 14 | 0 | @r0_14(int) | +| ArrayReferences() -> void | 0 | 15 | 0 | @m0_15(int) | +| Base::Base() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Base::Base() -> void | 0 | 2 | 0 | @r0_2(glval:Base) | +| Base::Base() -> void | 0 | 3 | 0 | @r0_3(glval:String) | +| Base::Base() -> void | 0 | 4 | 0 | @r0_4(bool) | +| Base::Base(const Base &) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Base::Base(const Base &) -> void | 0 | 2 | 0 | @r0_2(glval:Base) | +| Base::Base(const Base &) -> void | 0 | 3 | 0 | @r0_3(Base &) | +| Base::Base(const Base &) -> void | 0 | 4 | 0 | @r0_4(glval:Base &) | +| Base::Base(const Base &) -> void | 0 | 5 | 0 | @m0_5(Base &) | +| Base::Base(const Base &) -> void | 0 | 6 | 0 | @r0_6(glval:String) | +| Base::Base(const Base &) -> void | 0 | 7 | 0 | @r0_7(bool) | +| Base::operator=(const Base &) -> Base & | 0 | 1 | 0 | @mu0_1(unknown) | +| Base::operator=(const Base &) -> Base & | 0 | 2 | 0 | @r0_2(glval:Base) | +| Base::operator=(const Base &) -> Base & | 0 | 3 | 0 | @r0_3(Base &) | +| Base::operator=(const Base &) -> Base & | 0 | 4 | 0 | @r0_4(glval:Base &) | +| Base::operator=(const Base &) -> Base & | 0 | 5 | 0 | @m0_5(Base &) | +| Base::operator=(const Base &) -> Base & | 0 | 6 | 0 | @r0_6(Base *) | +| Base::operator=(const Base &) -> Base & | 0 | 7 | 0 | @r0_7(glval:String) | +| Base::operator=(const Base &) -> Base & | 0 | 8 | 0 | @r0_8(bool) | +| Base::operator=(const Base &) -> Base & | 0 | 9 | 0 | @r0_9(glval:Base &) | +| Base::operator=(const Base &) -> Base & | 0 | 10 | 0 | @r0_10(Base &) | +| Base::operator=(const Base &) -> Base & | 0 | 11 | 0 | @r0_11(glval:String) | +| Base::operator=(const Base &) -> Base & | 0 | 12 | 0 | @r0_12(String &) | +| Base::operator=(const Base &) -> Base & | 0 | 13 | 0 | @r0_13(glval:Base &) | +| Base::operator=(const Base &) -> Base & | 0 | 14 | 0 | @r0_14(Base *) | +| Base::operator=(const Base &) -> Base & | 0 | 15 | 0 | @m0_15(Base &) | +| Base::operator=(const Base &) -> Base & | 0 | 16 | 0 | @r0_16(glval:Base &) | +| Base::~Base() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Base::~Base() -> void | 0 | 2 | 0 | @r0_2(glval:Base) | +| Base::~Base() -> void | 0 | 4 | 0 | @r0_4(glval:String) | +| Base::~Base() -> void | 0 | 5 | 0 | @r0_5(bool) | +| Break(int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Break(int) -> void | 0 | 2 | 0 | @r0_2(int) | +| Break(int) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| Break(int) -> void | 0 | 4 | 0 | @m0_4(int) | +| Break(int) -> void | 1 | 0 | 0 | @r1_0(glval:int) | +| Break(int) -> void | 1 | 1 | 0 | @r1_1(int) | +| Break(int) -> void | 1 | 2 | 0 | @r1_2(int) | +| Break(int) -> void | 1 | 3 | 0 | @r1_3(bool) | +| Break(int) -> void | 3 | 0 | 0 | @r3_0(int) | +| Break(int) -> void | 3 | 1 | 0 | @r3_1(glval:int) | +| Break(int) -> void | 3 | 2 | 0 | @r3_2(int) | +| Break(int) -> void | 3 | 3 | 0 | @r3_3(int) | +| Break(int) -> void | 3 | 4 | 0 | @m3_4(int) | +| Break(int) -> void | 5 | 0 | 0 | @m5_0(int) | +| Break(int) -> void | 5 | 1 | 0 | @r5_1(glval:int) | +| Break(int) -> void | 5 | 2 | 0 | @r5_2(int) | +| Break(int) -> void | 5 | 3 | 0 | @r5_3(int) | +| Break(int) -> void | 5 | 4 | 0 | @r5_4(bool) | +| C::C() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| C::C() -> void | 0 | 2 | 0 | @r0_2(glval:C) | +| C::C() -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| C::C() -> void | 0 | 4 | 0 | @r0_4(int) | +| C::C() -> void | 0 | 5 | 0 | @mu0_5(int) | +| C::C() -> void | 0 | 6 | 0 | @r0_6(glval:String) | +| C::C() -> void | 0 | 7 | 0 | @r0_7(bool) | +| C::C() -> void | 0 | 9 | 0 | @r0_9(glval:char) | +| C::C() -> void | 0 | 10 | 0 | @r0_10(char) | +| C::C() -> void | 0 | 11 | 0 | @mu0_11(char) | +| C::C() -> void | 0 | 12 | 0 | @r0_12(glval:void *) | +| C::C() -> void | 0 | 13 | 0 | @r0_13(void *) | +| C::C() -> void | 0 | 14 | 0 | @mu0_14(void *) | +| C::C() -> void | 0 | 15 | 0 | @r0_15(glval:String) | +| C::C() -> void | 0 | 16 | 0 | @r0_16(bool) | +| C::C() -> void | 0 | 17 | 0 | @r0_17(glval:char[5]) | +| C::C() -> void | 0 | 18 | 0 | @r0_18(char *) | +| C::FieldAccess() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| C::FieldAccess() -> void | 0 | 2 | 0 | @r0_2(glval:C) | +| C::FieldAccess() -> void | 0 | 3 | 0 | @r0_3(int) | +| C::FieldAccess() -> void | 0 | 4 | 0 | @r0_4(C *) | +| C::FieldAccess() -> void | 0 | 5 | 0 | @r0_5(glval:int) | +| C::FieldAccess() -> void | 0 | 6 | 0 | @mu0_6(int) | +| C::FieldAccess() -> void | 0 | 7 | 0 | @r0_7(int) | +| C::FieldAccess() -> void | 0 | 8 | 0 | @r0_8(C *) | +| C::FieldAccess() -> void | 0 | 9 | 0 | @r0_9(glval:int) | +| C::FieldAccess() -> void | 0 | 10 | 0 | @mu0_10(int) | +| C::FieldAccess() -> void | 0 | 11 | 0 | @r0_11(int) | +| C::FieldAccess() -> void | 0 | 12 | 0 | @r0_12(C *) | +| C::FieldAccess() -> void | 0 | 13 | 0 | @r0_13(glval:int) | +| C::FieldAccess() -> void | 0 | 14 | 0 | @mu0_14(int) | +| C::FieldAccess() -> void | 0 | 15 | 0 | @r0_15(glval:int) | +| C::FieldAccess() -> void | 0 | 16 | 0 | @r0_16(int) | +| C::FieldAccess() -> void | 0 | 17 | 0 | @m0_17(int) | +| C::FieldAccess() -> void | 0 | 18 | 0 | @r0_18(C *) | +| C::FieldAccess() -> void | 0 | 19 | 0 | @r0_19(glval:int) | +| C::FieldAccess() -> void | 0 | 20 | 0 | @r0_20(int) | +| C::FieldAccess() -> void | 0 | 21 | 0 | @r0_21(glval:int) | +| C::FieldAccess() -> void | 0 | 22 | 0 | @m0_22(int) | +| C::FieldAccess() -> void | 0 | 23 | 0 | @r0_23(C *) | +| C::FieldAccess() -> void | 0 | 24 | 0 | @r0_24(glval:int) | +| C::FieldAccess() -> void | 0 | 25 | 0 | @r0_25(int) | +| C::FieldAccess() -> void | 0 | 26 | 0 | @r0_26(glval:int) | +| C::FieldAccess() -> void | 0 | 27 | 0 | @m0_27(int) | +| C::FieldAccess() -> void | 0 | 28 | 0 | @r0_28(C *) | +| C::FieldAccess() -> void | 0 | 29 | 0 | @r0_29(glval:int) | +| C::FieldAccess() -> void | 0 | 30 | 0 | @r0_30(int) | +| C::FieldAccess() -> void | 0 | 31 | 0 | @r0_31(glval:int) | +| C::FieldAccess() -> void | 0 | 32 | 0 | @m0_32(int) | +| C::InstanceMemberFunction(int) -> int | 0 | 1 | 0 | @mu0_1(unknown) | +| C::InstanceMemberFunction(int) -> int | 0 | 2 | 0 | @r0_2(glval:C) | +| C::InstanceMemberFunction(int) -> int | 0 | 3 | 0 | @r0_3(int) | +| C::InstanceMemberFunction(int) -> int | 0 | 4 | 0 | @r0_4(glval:int) | +| C::InstanceMemberFunction(int) -> int | 0 | 5 | 0 | @m0_5(int) | +| C::InstanceMemberFunction(int) -> int | 0 | 6 | 0 | @r0_6(glval:int) | +| C::InstanceMemberFunction(int) -> int | 0 | 7 | 0 | @r0_7(glval:int) | +| C::InstanceMemberFunction(int) -> int | 0 | 8 | 0 | @r0_8(int) | +| C::InstanceMemberFunction(int) -> int | 0 | 9 | 0 | @m0_9(int) | +| C::InstanceMemberFunction(int) -> int | 0 | 10 | 0 | @r0_10(glval:int) | +| C::MethodCalls() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| C::MethodCalls() -> void | 0 | 2 | 0 | @r0_2(glval:C) | +| C::MethodCalls() -> void | 0 | 3 | 0 | @r0_3(C *) | +| C::MethodCalls() -> void | 0 | 4 | 0 | @r0_4(bool) | +| C::MethodCalls() -> void | 0 | 5 | 0 | @r0_5(int) | +| C::MethodCalls() -> void | 0 | 6 | 0 | @r0_6(int) | +| C::MethodCalls() -> void | 0 | 7 | 0 | @r0_7(C *) | +| C::MethodCalls() -> void | 0 | 8 | 0 | @r0_8(bool) | +| C::MethodCalls() -> void | 0 | 9 | 0 | @r0_9(int) | +| C::MethodCalls() -> void | 0 | 10 | 0 | @r0_10(int) | +| C::MethodCalls() -> void | 0 | 11 | 0 | @r0_11(C *) | +| C::MethodCalls() -> void | 0 | 12 | 0 | @r0_12(bool) | +| C::MethodCalls() -> void | 0 | 13 | 0 | @r0_13(int) | +| C::MethodCalls() -> void | 0 | 14 | 0 | @r0_14(int) | +| C::StaticMemberFunction(int) -> int | 0 | 1 | 0 | @mu0_1(unknown) | +| C::StaticMemberFunction(int) -> int | 0 | 2 | 0 | @r0_2(int) | +| C::StaticMemberFunction(int) -> int | 0 | 3 | 0 | @r0_3(glval:int) | +| C::StaticMemberFunction(int) -> int | 0 | 4 | 0 | @m0_4(int) | +| C::StaticMemberFunction(int) -> int | 0 | 5 | 0 | @r0_5(glval:int) | +| C::StaticMemberFunction(int) -> int | 0 | 6 | 0 | @r0_6(glval:int) | +| C::StaticMemberFunction(int) -> int | 0 | 7 | 0 | @r0_7(int) | +| C::StaticMemberFunction(int) -> int | 0 | 8 | 0 | @m0_8(int) | +| C::StaticMemberFunction(int) -> int | 0 | 9 | 0 | @r0_9(glval:int) | +| C::VirtualMemberFunction(int) -> int | 0 | 1 | 0 | @mu0_1(unknown) | +| C::VirtualMemberFunction(int) -> int | 0 | 2 | 0 | @r0_2(glval:C) | +| C::VirtualMemberFunction(int) -> int | 0 | 3 | 0 | @r0_3(int) | +| C::VirtualMemberFunction(int) -> int | 0 | 4 | 0 | @r0_4(glval:int) | +| C::VirtualMemberFunction(int) -> int | 0 | 5 | 0 | @m0_5(int) | +| C::VirtualMemberFunction(int) -> int | 0 | 6 | 0 | @r0_6(glval:int) | +| C::VirtualMemberFunction(int) -> int | 0 | 7 | 0 | @r0_7(glval:int) | +| C::VirtualMemberFunction(int) -> int | 0 | 8 | 0 | @r0_8(int) | +| C::VirtualMemberFunction(int) -> int | 0 | 9 | 0 | @m0_9(int) | +| C::VirtualMemberFunction(int) -> int | 0 | 10 | 0 | @r0_10(glval:int) | +| Call() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Call() -> void | 0 | 2 | 0 | @r0_2(bool) | +| CallAdd(int, int) -> int | 0 | 1 | 0 | @mu0_1(unknown) | +| CallAdd(int, int) -> int | 0 | 2 | 0 | @r0_2(int) | +| CallAdd(int, int) -> int | 0 | 3 | 0 | @r0_3(glval:int) | +| CallAdd(int, int) -> int | 0 | 4 | 0 | @m0_4(int) | +| CallAdd(int, int) -> int | 0 | 5 | 0 | @r0_5(int) | +| CallAdd(int, int) -> int | 0 | 6 | 0 | @r0_6(glval:int) | +| CallAdd(int, int) -> int | 0 | 7 | 0 | @m0_7(int) | +| CallAdd(int, int) -> int | 0 | 8 | 0 | @r0_8(glval:int) | +| CallAdd(int, int) -> int | 0 | 9 | 0 | @r0_9(bool) | +| CallAdd(int, int) -> int | 0 | 10 | 0 | @r0_10(glval:int) | +| CallAdd(int, int) -> int | 0 | 11 | 0 | @r0_11(int) | +| CallAdd(int, int) -> int | 0 | 12 | 0 | @r0_12(glval:int) | +| CallAdd(int, int) -> int | 0 | 13 | 0 | @r0_13(int) | +| CallAdd(int, int) -> int | 0 | 14 | 0 | @r0_14(int) | +| CallAdd(int, int) -> int | 0 | 15 | 0 | @m0_15(int) | +| CallAdd(int, int) -> int | 0 | 16 | 0 | @r0_16(glval:int) | +| CallMethods(String &, String *, String) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| CallMethods(String &, String *, String) -> void | 0 | 2 | 0 | @r0_2(String &) | +| CallMethods(String &, String *, String) -> void | 0 | 3 | 0 | @r0_3(glval:String &) | +| CallMethods(String &, String *, String) -> void | 0 | 4 | 0 | @m0_4(String &) | +| CallMethods(String &, String *, String) -> void | 0 | 5 | 0 | @r0_5(String *) | +| CallMethods(String &, String *, String) -> void | 0 | 6 | 0 | @r0_6(glval:String *) | +| CallMethods(String &, String *, String) -> void | 0 | 7 | 0 | @m0_7(String *) | +| CallMethods(String &, String *, String) -> void | 0 | 8 | 0 | @r0_8(String) | +| CallMethods(String &, String *, String) -> void | 0 | 9 | 0 | @r0_9(glval:String) | +| CallMethods(String &, String *, String) -> void | 0 | 10 | 0 | @mu0_10(String) | +| CallMethods(String &, String *, String) -> void | 0 | 11 | 0 | @r0_11(glval:String &) | +| CallMethods(String &, String *, String) -> void | 0 | 12 | 0 | @r0_12(String &) | +| CallMethods(String &, String *, String) -> void | 0 | 13 | 0 | @r0_13(glval:String) | +| CallMethods(String &, String *, String) -> void | 0 | 14 | 0 | @r0_14(bool) | +| CallMethods(String &, String *, String) -> void | 0 | 15 | 0 | @r0_15(char *) | +| CallMethods(String &, String *, String) -> void | 0 | 16 | 0 | @r0_16(glval:String *) | +| CallMethods(String &, String *, String) -> void | 0 | 17 | 0 | @r0_17(String *) | +| CallMethods(String &, String *, String) -> void | 0 | 18 | 0 | @r0_18(String *) | +| CallMethods(String &, String *, String) -> void | 0 | 19 | 0 | @r0_19(bool) | +| CallMethods(String &, String *, String) -> void | 0 | 20 | 0 | @r0_20(char *) | +| CallMethods(String &, String *, String) -> void | 0 | 21 | 0 | @r0_21(glval:String) | +| CallMethods(String &, String *, String) -> void | 0 | 22 | 0 | @r0_22(glval:String) | +| CallMethods(String &, String *, String) -> void | 0 | 23 | 0 | @r0_23(bool) | +| CallMethods(String &, String *, String) -> void | 0 | 24 | 0 | @r0_24(char *) | +| CallMin(int, int) -> int | 0 | 1 | 0 | @mu0_1(unknown) | +| CallMin(int, int) -> int | 0 | 2 | 0 | @r0_2(int) | +| CallMin(int, int) -> int | 0 | 3 | 0 | @r0_3(glval:int) | +| CallMin(int, int) -> int | 0 | 4 | 0 | @m0_4(int) | +| CallMin(int, int) -> int | 0 | 5 | 0 | @r0_5(int) | +| CallMin(int, int) -> int | 0 | 6 | 0 | @r0_6(glval:int) | +| CallMin(int, int) -> int | 0 | 7 | 0 | @m0_7(int) | +| CallMin(int, int) -> int | 0 | 8 | 0 | @r0_8(glval:int) | +| CallMin(int, int) -> int | 0 | 9 | 0 | @r0_9(bool) | +| CallMin(int, int) -> int | 0 | 10 | 0 | @r0_10(glval:int) | +| CallMin(int, int) -> int | 0 | 11 | 0 | @r0_11(int) | +| CallMin(int, int) -> int | 0 | 12 | 0 | @r0_12(glval:int) | +| CallMin(int, int) -> int | 0 | 13 | 0 | @r0_13(int) | +| CallMin(int, int) -> int | 0 | 14 | 0 | @r0_14(int) | +| CallMin(int, int) -> int | 0 | 15 | 0 | @m0_15(int) | +| CallMin(int, int) -> int | 0 | 16 | 0 | @r0_16(glval:int) | +| CallNestedTemplateFunc() -> double | 0 | 1 | 0 | @mu0_1(unknown) | +| CallNestedTemplateFunc() -> double | 0 | 2 | 0 | @r0_2(glval:double) | +| CallNestedTemplateFunc() -> double | 0 | 3 | 0 | @r0_3(bool) | +| CallNestedTemplateFunc() -> double | 0 | 4 | 0 | @r0_4(void *) | +| CallNestedTemplateFunc() -> double | 0 | 5 | 0 | @r0_5(char) | +| CallNestedTemplateFunc() -> double | 0 | 6 | 0 | @r0_6(long) | +| CallNestedTemplateFunc() -> double | 0 | 7 | 0 | @r0_7(double) | +| CallNestedTemplateFunc() -> double | 0 | 8 | 0 | @m0_8(double) | +| CallNestedTemplateFunc() -> double | 0 | 9 | 0 | @r0_9(glval:double) | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 1 | 0 | @mu0_1(unknown) | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 2 | 0 | @r0_2(..(*)(..)) | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 3 | 0 | @r0_3(glval:..(*)(..)) | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 4 | 0 | @m0_4(..(*)(..)) | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 5 | 0 | @r0_5(glval:int) | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 6 | 0 | @r0_6(glval:..(*)(..)) | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 7 | 0 | @r0_7(..(*)(..)) | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 8 | 0 | @r0_8(int) | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 9 | 0 | @r0_9(int) | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 10 | 0 | @m0_10(int) | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 11 | 0 | @r0_11(glval:int) | +| Comma(int, int) -> int | 0 | 1 | 0 | @mu0_1(unknown) | +| Comma(int, int) -> int | 0 | 2 | 0 | @r0_2(int) | +| Comma(int, int) -> int | 0 | 3 | 0 | @r0_3(glval:int) | +| Comma(int, int) -> int | 0 | 4 | 0 | @m0_4(int) | +| Comma(int, int) -> int | 0 | 5 | 0 | @r0_5(int) | +| Comma(int, int) -> int | 0 | 6 | 0 | @r0_6(glval:int) | +| Comma(int, int) -> int | 0 | 7 | 0 | @m0_7(int) | +| Comma(int, int) -> int | 0 | 8 | 0 | @r0_8(glval:int) | +| Comma(int, int) -> int | 0 | 9 | 0 | @r0_9(bool) | +| Comma(int, int) -> int | 0 | 11 | 0 | @r0_11(bool) | +| Comma(int, int) -> int | 0 | 12 | 0 | @r0_12(glval:int) | +| Comma(int, int) -> int | 0 | 13 | 0 | @r0_13(int) | +| Comma(int, int) -> int | 0 | 14 | 0 | @r0_14(glval:int) | +| Comma(int, int) -> int | 0 | 15 | 0 | @r0_15(int) | +| Comma(int, int) -> int | 0 | 16 | 0 | @r0_16(int) | +| Comma(int, int) -> int | 0 | 17 | 0 | @m0_17(int) | +| Comma(int, int) -> int | 0 | 18 | 0 | @r0_18(glval:int) | +| CompoundAssignment() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| CompoundAssignment() -> void | 0 | 2 | 0 | @r0_2(glval:int) | +| CompoundAssignment() -> void | 0 | 3 | 0 | @r0_3(int) | +| CompoundAssignment() -> void | 0 | 4 | 0 | @m0_4(int) | +| CompoundAssignment() -> void | 0 | 5 | 0 | @r0_5(int) | +| CompoundAssignment() -> void | 0 | 6 | 0 | @r0_6(glval:int) | +| CompoundAssignment() -> void | 0 | 7 | 0 | @r0_7(int) | +| CompoundAssignment() -> void | 0 | 8 | 0 | @r0_8(int) | +| CompoundAssignment() -> void | 0 | 9 | 0 | @m0_9(int) | +| CompoundAssignment() -> void | 0 | 10 | 0 | @r0_10(glval:short) | +| CompoundAssignment() -> void | 0 | 11 | 0 | @r0_11(short) | +| CompoundAssignment() -> void | 0 | 12 | 0 | @m0_12(short) | +| CompoundAssignment() -> void | 0 | 13 | 0 | @r0_13(glval:int) | +| CompoundAssignment() -> void | 0 | 14 | 0 | @r0_14(int) | +| CompoundAssignment() -> void | 0 | 15 | 0 | @r0_15(glval:short) | +| CompoundAssignment() -> void | 0 | 16 | 0 | @r0_16(short) | +| CompoundAssignment() -> void | 0 | 17 | 0 | @r0_17(int) | +| CompoundAssignment() -> void | 0 | 18 | 0 | @r0_18(int) | +| CompoundAssignment() -> void | 0 | 19 | 0 | @r0_19(short) | +| CompoundAssignment() -> void | 0 | 20 | 0 | @m0_20(short) | +| CompoundAssignment() -> void | 0 | 21 | 0 | @r0_21(int) | +| CompoundAssignment() -> void | 0 | 22 | 0 | @r0_22(glval:short) | +| CompoundAssignment() -> void | 0 | 23 | 0 | @r0_23(short) | +| CompoundAssignment() -> void | 0 | 24 | 0 | @r0_24(short) | +| CompoundAssignment() -> void | 0 | 25 | 0 | @m0_25(short) | +| CompoundAssignment() -> void | 0 | 26 | 0 | @r0_26(glval:long) | +| CompoundAssignment() -> void | 0 | 27 | 0 | @r0_27(long) | +| CompoundAssignment() -> void | 0 | 28 | 0 | @m0_28(long) | +| CompoundAssignment() -> void | 0 | 29 | 0 | @r0_29(float) | +| CompoundAssignment() -> void | 0 | 30 | 0 | @r0_30(glval:long) | +| CompoundAssignment() -> void | 0 | 31 | 0 | @r0_31(long) | +| CompoundAssignment() -> void | 0 | 32 | 0 | @r0_32(float) | +| CompoundAssignment() -> void | 0 | 33 | 0 | @r0_33(float) | +| CompoundAssignment() -> void | 0 | 34 | 0 | @r0_34(long) | +| CompoundAssignment() -> void | 0 | 35 | 0 | @m0_35(long) | +| ConditionValues(bool, bool) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| ConditionValues(bool, bool) -> void | 0 | 2 | 0 | @r0_2(bool) | +| ConditionValues(bool, bool) -> void | 0 | 3 | 0 | @r0_3(glval:bool) | +| ConditionValues(bool, bool) -> void | 0 | 4 | 0 | @m0_4(bool) | +| ConditionValues(bool, bool) -> void | 0 | 5 | 0 | @r0_5(bool) | +| ConditionValues(bool, bool) -> void | 0 | 6 | 0 | @r0_6(glval:bool) | +| ConditionValues(bool, bool) -> void | 0 | 7 | 0 | @m0_7(bool) | +| ConditionValues(bool, bool) -> void | 0 | 8 | 0 | @r0_8(glval:bool) | +| ConditionValues(bool, bool) -> void | 0 | 9 | 0 | @r0_9(bool) | +| ConditionValues(bool, bool) -> void | 0 | 10 | 0 | @m0_10(bool) | +| ConditionValues(bool, bool) -> void | 0 | 11 | 0 | @r0_11(glval:bool) | +| ConditionValues(bool, bool) -> void | 0 | 12 | 0 | @r0_12(bool) | +| ConditionValues(bool, bool) -> void | 1 | 0 | 0 | @r1_0(glval:bool) | +| ConditionValues(bool, bool) -> void | 1 | 1 | 0 | @r1_1(bool) | +| ConditionValues(bool, bool) -> void | 2 | 0 | 0 | @r2_0(glval:bool) | +| ConditionValues(bool, bool) -> void | 2 | 1 | 0 | @r2_1(bool) | +| ConditionValues(bool, bool) -> void | 2 | 2 | 0 | @m2_2(bool) | +| ConditionValues(bool, bool) -> void | 3 | 0 | 0 | @m3_0(bool) | +| ConditionValues(bool, bool) -> void | 3 | 1 | 0 | @r3_1(glval:bool) | +| ConditionValues(bool, bool) -> void | 3 | 2 | 0 | @r3_2(bool) | +| ConditionValues(bool, bool) -> void | 3 | 3 | 0 | @r3_3(glval:bool) | +| ConditionValues(bool, bool) -> void | 3 | 4 | 0 | @m3_4(bool) | +| ConditionValues(bool, bool) -> void | 3 | 5 | 0 | @r3_5(glval:bool) | +| ConditionValues(bool, bool) -> void | 3 | 6 | 0 | @r3_6(bool) | +| ConditionValues(bool, bool) -> void | 4 | 0 | 0 | @r4_0(glval:bool) | +| ConditionValues(bool, bool) -> void | 4 | 1 | 0 | @r4_1(bool) | +| ConditionValues(bool, bool) -> void | 4 | 2 | 0 | @m4_2(bool) | +| ConditionValues(bool, bool) -> void | 5 | 0 | 0 | @r5_0(glval:bool) | +| ConditionValues(bool, bool) -> void | 5 | 1 | 0 | @r5_1(bool) | +| ConditionValues(bool, bool) -> void | 6 | 0 | 0 | @r6_0(glval:bool) | +| ConditionValues(bool, bool) -> void | 6 | 1 | 0 | @r6_1(bool) | +| ConditionValues(bool, bool) -> void | 6 | 2 | 0 | @m6_2(bool) | +| ConditionValues(bool, bool) -> void | 7 | 0 | 0 | @m7_0(bool) | +| ConditionValues(bool, bool) -> void | 7 | 1 | 0 | @r7_1(glval:bool) | +| ConditionValues(bool, bool) -> void | 7 | 2 | 0 | @r7_2(bool) | +| ConditionValues(bool, bool) -> void | 7 | 3 | 0 | @r7_3(bool) | +| ConditionValues(bool, bool) -> void | 7 | 4 | 0 | @r7_4(glval:bool) | +| ConditionValues(bool, bool) -> void | 7 | 5 | 0 | @m7_5(bool) | +| ConditionValues(bool, bool) -> void | 8 | 0 | 0 | @r8_0(glval:bool) | +| ConditionValues(bool, bool) -> void | 8 | 1 | 0 | @r8_1(bool) | +| ConditionValues(bool, bool) -> void | 8 | 2 | 0 | @m8_2(bool) | +| ConditionValues(bool, bool) -> void | 9 | 0 | 0 | @r9_0(glval:bool) | +| ConditionValues(bool, bool) -> void | 9 | 1 | 0 | @r9_1(bool) | +| ConditionValues(bool, bool) -> void | 10 | 0 | 0 | @r10_0(glval:bool) | +| ConditionValues(bool, bool) -> void | 10 | 1 | 0 | @r10_1(bool) | +| ConditionValues(bool, bool) -> void | 10 | 2 | 0 | @m10_2(bool) | +| ConditionValues(bool, bool) -> void | 11 | 0 | 0 | @m11_0(bool) | +| ConditionValues(bool, bool) -> void | 11 | 1 | 0 | @r11_1(glval:bool) | +| ConditionValues(bool, bool) -> void | 11 | 2 | 0 | @r11_2(bool) | +| ConditionValues(bool, bool) -> void | 11 | 3 | 0 | @r11_3(glval:bool) | +| ConditionValues(bool, bool) -> void | 11 | 4 | 0 | @m11_4(bool) | +| ConditionValues(bool, bool) -> void | 11 | 5 | 0 | @r11_5(glval:bool) | +| ConditionValues(bool, bool) -> void | 11 | 6 | 0 | @r11_6(bool) | +| ConditionValues(bool, bool) -> void | 12 | 0 | 0 | @r12_0(glval:bool) | +| ConditionValues(bool, bool) -> void | 12 | 1 | 0 | @r12_1(bool) | +| ConditionValues(bool, bool) -> void | 12 | 2 | 0 | @m12_2(bool) | +| Conditional(bool, int, int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Conditional(bool, int, int) -> void | 0 | 2 | 0 | @r0_2(bool) | +| Conditional(bool, int, int) -> void | 0 | 3 | 0 | @r0_3(glval:bool) | +| Conditional(bool, int, int) -> void | 0 | 4 | 0 | @m0_4(bool) | +| Conditional(bool, int, int) -> void | 0 | 5 | 0 | @r0_5(int) | +| Conditional(bool, int, int) -> void | 0 | 6 | 0 | @r0_6(glval:int) | +| Conditional(bool, int, int) -> void | 0 | 7 | 0 | @m0_7(int) | +| Conditional(bool, int, int) -> void | 0 | 8 | 0 | @r0_8(int) | +| Conditional(bool, int, int) -> void | 0 | 9 | 0 | @r0_9(glval:int) | +| Conditional(bool, int, int) -> void | 0 | 10 | 0 | @m0_10(int) | +| Conditional(bool, int, int) -> void | 0 | 11 | 0 | @r0_11(glval:int) | +| Conditional(bool, int, int) -> void | 0 | 12 | 0 | @r0_12(glval:bool) | +| Conditional(bool, int, int) -> void | 0 | 13 | 0 | @r0_13(bool) | +| Conditional(bool, int, int) -> void | 1 | 0 | 0 | @r1_0(glval:int) | +| Conditional(bool, int, int) -> void | 1 | 1 | 0 | @r1_1(int) | +| Conditional(bool, int, int) -> void | 1 | 2 | 0 | @r1_2(glval:int) | +| Conditional(bool, int, int) -> void | 1 | 3 | 0 | @m1_3(int) | +| Conditional(bool, int, int) -> void | 2 | 0 | 0 | @r2_0(glval:int) | +| Conditional(bool, int, int) -> void | 2 | 1 | 0 | @r2_1(int) | +| Conditional(bool, int, int) -> void | 2 | 2 | 0 | @r2_2(glval:int) | +| Conditional(bool, int, int) -> void | 2 | 3 | 0 | @m2_3(int) | +| Conditional(bool, int, int) -> void | 3 | 0 | 0 | @m3_0(int) | +| Conditional(bool, int, int) -> void | 3 | 1 | 0 | @r3_1(glval:int) | +| Conditional(bool, int, int) -> void | 3 | 2 | 0 | @r3_2(int) | +| Conditional(bool, int, int) -> void | 3 | 3 | 0 | @m3_3(int) | +| Conditional_LValue(bool) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Conditional_LValue(bool) -> void | 0 | 2 | 0 | @r0_2(bool) | +| Conditional_LValue(bool) -> void | 0 | 3 | 0 | @r0_3(glval:bool) | +| Conditional_LValue(bool) -> void | 0 | 4 | 0 | @m0_4(bool) | +| Conditional_LValue(bool) -> void | 0 | 5 | 0 | @r0_5(glval:int) | +| Conditional_LValue(bool) -> void | 0 | 6 | 0 | @r0_6(int) | +| Conditional_LValue(bool) -> void | 0 | 7 | 0 | @mu0_7(int) | +| Conditional_LValue(bool) -> void | 0 | 8 | 0 | @r0_8(glval:int) | +| Conditional_LValue(bool) -> void | 0 | 9 | 0 | @r0_9(int) | +| Conditional_LValue(bool) -> void | 0 | 10 | 0 | @mu0_10(int) | +| Conditional_LValue(bool) -> void | 0 | 11 | 0 | @r0_11(int) | +| Conditional_LValue(bool) -> void | 0 | 12 | 0 | @r0_12(glval:bool) | +| Conditional_LValue(bool) -> void | 0 | 13 | 0 | @r0_13(bool) | +| Conditional_LValue(bool) -> void | 1 | 0 | 0 | @m1_0(int) | +| Conditional_LValue(bool) -> void | 1 | 1 | 0 | @r1_1(glval:int) | +| Conditional_LValue(bool) -> void | 1 | 2 | 0 | @r1_2(glval:int) | +| Conditional_LValue(bool) -> void | 1 | 3 | 0 | @mu1_3(int) | +| Conditional_LValue(bool) -> void | 2 | 0 | 0 | @r2_0(glval:int) | +| Conditional_LValue(bool) -> void | 2 | 1 | 0 | @r2_1(glval:int) | +| Conditional_LValue(bool) -> void | 2 | 2 | 0 | @m2_2(int) | +| Conditional_LValue(bool) -> void | 3 | 0 | 0 | @r3_0(glval:int) | +| Conditional_LValue(bool) -> void | 3 | 1 | 0 | @r3_1(glval:int) | +| Conditional_LValue(bool) -> void | 3 | 2 | 0 | @m3_2(int) | +| Conditional_Void(bool) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Conditional_Void(bool) -> void | 0 | 2 | 0 | @r0_2(bool) | +| Conditional_Void(bool) -> void | 0 | 3 | 0 | @r0_3(glval:bool) | +| Conditional_Void(bool) -> void | 0 | 4 | 0 | @m0_4(bool) | +| Conditional_Void(bool) -> void | 0 | 5 | 0 | @r0_5(glval:bool) | +| Conditional_Void(bool) -> void | 0 | 6 | 0 | @r0_6(bool) | +| Conditional_Void(bool) -> void | 2 | 0 | 0 | @r2_0(bool) | +| Conditional_Void(bool) -> void | 3 | 0 | 0 | @r3_0(bool) | +| Constants() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Constants() -> void | 0 | 2 | 0 | @r0_2(glval:char) | +| Constants() -> void | 0 | 3 | 0 | @r0_3(char) | +| Constants() -> void | 0 | 4 | 0 | @m0_4(char) | +| Constants() -> void | 0 | 5 | 0 | @r0_5(glval:char) | +| Constants() -> void | 0 | 6 | 0 | @r0_6(char) | +| Constants() -> void | 0 | 7 | 0 | @m0_7(char) | +| Constants() -> void | 0 | 8 | 0 | @r0_8(glval:signed char) | +| Constants() -> void | 0 | 9 | 0 | @r0_9(signed char) | +| Constants() -> void | 0 | 10 | 0 | @m0_10(signed char) | +| Constants() -> void | 0 | 11 | 0 | @r0_11(glval:signed char) | +| Constants() -> void | 0 | 12 | 0 | @r0_12(signed char) | +| Constants() -> void | 0 | 13 | 0 | @m0_13(signed char) | +| Constants() -> void | 0 | 14 | 0 | @r0_14(glval:unsigned char) | +| Constants() -> void | 0 | 15 | 0 | @r0_15(unsigned char) | +| Constants() -> void | 0 | 16 | 0 | @m0_16(unsigned char) | +| Constants() -> void | 0 | 17 | 0 | @r0_17(glval:unsigned char) | +| Constants() -> void | 0 | 18 | 0 | @r0_18(unsigned char) | +| Constants() -> void | 0 | 19 | 0 | @m0_19(unsigned char) | +| Constants() -> void | 0 | 20 | 0 | @r0_20(glval:short) | +| Constants() -> void | 0 | 21 | 0 | @r0_21(short) | +| Constants() -> void | 0 | 22 | 0 | @m0_22(short) | +| Constants() -> void | 0 | 23 | 0 | @r0_23(glval:unsigned short) | +| Constants() -> void | 0 | 24 | 0 | @r0_24(unsigned short) | +| Constants() -> void | 0 | 25 | 0 | @m0_25(unsigned short) | +| Constants() -> void | 0 | 26 | 0 | @r0_26(glval:int) | +| Constants() -> void | 0 | 27 | 0 | @r0_27(int) | +| Constants() -> void | 0 | 28 | 0 | @m0_28(int) | +| Constants() -> void | 0 | 29 | 0 | @r0_29(glval:unsigned int) | +| Constants() -> void | 0 | 30 | 0 | @r0_30(unsigned int) | +| Constants() -> void | 0 | 31 | 0 | @m0_31(unsigned int) | +| Constants() -> void | 0 | 32 | 0 | @r0_32(glval:long) | +| Constants() -> void | 0 | 33 | 0 | @r0_33(long) | +| Constants() -> void | 0 | 34 | 0 | @m0_34(long) | +| Constants() -> void | 0 | 35 | 0 | @r0_35(glval:unsigned long) | +| Constants() -> void | 0 | 36 | 0 | @r0_36(unsigned long) | +| Constants() -> void | 0 | 37 | 0 | @m0_37(unsigned long) | +| Constants() -> void | 0 | 38 | 0 | @r0_38(glval:long long) | +| Constants() -> void | 0 | 39 | 0 | @r0_39(long long) | +| Constants() -> void | 0 | 40 | 0 | @m0_40(long long) | +| Constants() -> void | 0 | 41 | 0 | @r0_41(glval:long long) | +| Constants() -> void | 0 | 42 | 0 | @r0_42(long long) | +| Constants() -> void | 0 | 43 | 0 | @m0_43(long long) | +| Constants() -> void | 0 | 44 | 0 | @r0_44(glval:unsigned long long) | +| Constants() -> void | 0 | 45 | 0 | @r0_45(unsigned long long) | +| Constants() -> void | 0 | 46 | 0 | @m0_46(unsigned long long) | +| Constants() -> void | 0 | 47 | 0 | @r0_47(glval:unsigned long long) | +| Constants() -> void | 0 | 48 | 0 | @r0_48(unsigned long long) | +| Constants() -> void | 0 | 49 | 0 | @m0_49(unsigned long long) | +| Constants() -> void | 0 | 50 | 0 | @r0_50(glval:bool) | +| Constants() -> void | 0 | 51 | 0 | @r0_51(bool) | +| Constants() -> void | 0 | 52 | 0 | @m0_52(bool) | +| Constants() -> void | 0 | 53 | 0 | @r0_53(glval:bool) | +| Constants() -> void | 0 | 54 | 0 | @r0_54(bool) | +| Constants() -> void | 0 | 55 | 0 | @m0_55(bool) | +| Constants() -> void | 0 | 56 | 0 | @r0_56(glval:wchar_t) | +| Constants() -> void | 0 | 57 | 0 | @r0_57(wchar_t) | +| Constants() -> void | 0 | 58 | 0 | @m0_58(wchar_t) | +| Constants() -> void | 0 | 59 | 0 | @r0_59(glval:wchar_t) | +| Constants() -> void | 0 | 60 | 0 | @r0_60(wchar_t) | +| Constants() -> void | 0 | 61 | 0 | @m0_61(wchar_t) | +| Constants() -> void | 0 | 62 | 0 | @r0_62(glval:char16_t) | +| Constants() -> void | 0 | 63 | 0 | @r0_63(char16_t) | +| Constants() -> void | 0 | 64 | 0 | @m0_64(char16_t) | +| Constants() -> void | 0 | 65 | 0 | @r0_65(glval:char32_t) | +| Constants() -> void | 0 | 66 | 0 | @r0_66(char32_t) | +| Constants() -> void | 0 | 67 | 0 | @m0_67(char32_t) | +| Constants() -> void | 0 | 68 | 0 | @r0_68(glval:float) | +| Constants() -> void | 0 | 69 | 0 | @r0_69(float) | +| Constants() -> void | 0 | 70 | 0 | @m0_70(float) | +| Constants() -> void | 0 | 71 | 0 | @r0_71(glval:float) | +| Constants() -> void | 0 | 72 | 0 | @r0_72(float) | +| Constants() -> void | 0 | 73 | 0 | @m0_73(float) | +| Constants() -> void | 0 | 74 | 0 | @r0_74(glval:float) | +| Constants() -> void | 0 | 75 | 0 | @r0_75(float) | +| Constants() -> void | 0 | 76 | 0 | @m0_76(float) | +| Constants() -> void | 0 | 77 | 0 | @r0_77(glval:double) | +| Constants() -> void | 0 | 78 | 0 | @r0_78(double) | +| Constants() -> void | 0 | 79 | 0 | @m0_79(double) | +| Constants() -> void | 0 | 80 | 0 | @r0_80(glval:double) | +| Constants() -> void | 0 | 81 | 0 | @r0_81(double) | +| Constants() -> void | 0 | 82 | 0 | @m0_82(double) | +| Constants() -> void | 0 | 83 | 0 | @r0_83(glval:double) | +| Constants() -> void | 0 | 84 | 0 | @r0_84(double) | +| Constants() -> void | 0 | 85 | 0 | @m0_85(double) | +| Continue(int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Continue(int) -> void | 0 | 2 | 0 | @r0_2(int) | +| Continue(int) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| Continue(int) -> void | 0 | 4 | 0 | @m0_4(int) | +| Continue(int) -> void | 1 | 0 | 0 | @m1_0(int) | +| Continue(int) -> void | 1 | 1 | 0 | @r1_1(glval:int) | +| Continue(int) -> void | 1 | 2 | 0 | @r1_2(int) | +| Continue(int) -> void | 1 | 3 | 0 | @r1_3(int) | +| Continue(int) -> void | 1 | 4 | 0 | @r1_4(bool) | +| Continue(int) -> void | 3 | 0 | 0 | @r3_0(int) | +| Continue(int) -> void | 3 | 1 | 0 | @r3_1(glval:int) | +| Continue(int) -> void | 3 | 2 | 0 | @r3_2(int) | +| Continue(int) -> void | 3 | 3 | 0 | @r3_3(int) | +| Continue(int) -> void | 3 | 4 | 0 | @m3_4(int) | +| Continue(int) -> void | 4 | 0 | 0 | @m4_0(int) | +| Continue(int) -> void | 4 | 2 | 0 | @r4_2(glval:int) | +| Continue(int) -> void | 4 | 3 | 0 | @r4_3(int) | +| Continue(int) -> void | 4 | 4 | 0 | @r4_4(int) | +| Continue(int) -> void | 4 | 5 | 0 | @r4_5(bool) | +| DeclareObject() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| DeclareObject() -> void | 0 | 2 | 0 | @r0_2(glval:String) | +| DeclareObject() -> void | 0 | 3 | 0 | @r0_3(bool) | +| DeclareObject() -> void | 0 | 5 | 0 | @r0_5(glval:String) | +| DeclareObject() -> void | 0 | 6 | 0 | @r0_6(bool) | +| DeclareObject() -> void | 0 | 7 | 0 | @r0_7(glval:char[6]) | +| DeclareObject() -> void | 0 | 8 | 0 | @r0_8(char *) | +| DeclareObject() -> void | 0 | 10 | 0 | @r0_10(glval:String) | +| DeclareObject() -> void | 0 | 11 | 0 | @r0_11(bool) | +| DeclareObject() -> void | 0 | 12 | 0 | @r0_12(String) | +| DeclareObject() -> void | 0 | 13 | 0 | @m0_13(String) | +| DeclareObject() -> void | 0 | 14 | 0 | @r0_14(glval:String) | +| DeclareObject() -> void | 0 | 15 | 0 | @r0_15(bool) | +| DeclareObject() -> void | 0 | 16 | 0 | @r0_16(glval:char[5]) | +| DeclareObject() -> void | 0 | 17 | 0 | @r0_17(char *) | +| DerefReference(int &) -> int | 0 | 1 | 0 | @mu0_1(unknown) | +| DerefReference(int &) -> int | 0 | 2 | 0 | @r0_2(int &) | +| DerefReference(int &) -> int | 0 | 3 | 0 | @r0_3(glval:int &) | +| DerefReference(int &) -> int | 0 | 4 | 0 | @m0_4(int &) | +| DerefReference(int &) -> int | 0 | 5 | 0 | @r0_5(glval:int) | +| DerefReference(int &) -> int | 0 | 6 | 0 | @r0_6(glval:int &) | +| DerefReference(int &) -> int | 0 | 7 | 0 | @r0_7(int &) | +| DerefReference(int &) -> int | 0 | 8 | 0 | @r0_8(int) | +| DerefReference(int &) -> int | 0 | 9 | 0 | @m0_9(int) | +| DerefReference(int &) -> int | 0 | 10 | 0 | @r0_10(glval:int) | +| Dereference(int *) -> int | 0 | 1 | 0 | @mu0_1(unknown) | +| Dereference(int *) -> int | 0 | 2 | 0 | @r0_2(int *) | +| Dereference(int *) -> int | 0 | 3 | 0 | @r0_3(glval:int *) | +| Dereference(int *) -> int | 0 | 4 | 0 | @m0_4(int *) | +| Dereference(int *) -> int | 0 | 5 | 0 | @r0_5(int) | +| Dereference(int *) -> int | 0 | 6 | 0 | @r0_6(glval:int *) | +| Dereference(int *) -> int | 0 | 7 | 0 | @r0_7(int *) | +| Dereference(int *) -> int | 0 | 8 | 0 | @mu0_8(int) | +| Dereference(int *) -> int | 0 | 9 | 0 | @r0_9(glval:int) | +| Dereference(int *) -> int | 0 | 10 | 0 | @r0_10(glval:int *) | +| Dereference(int *) -> int | 0 | 11 | 0 | @r0_11(int *) | +| Dereference(int *) -> int | 0 | 12 | 0 | @r0_12(int) | +| Dereference(int *) -> int | 0 | 13 | 0 | @m0_13(int) | +| Dereference(int *) -> int | 0 | 14 | 0 | @r0_14(glval:int) | +| Derived::Derived() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Derived::Derived() -> void | 0 | 2 | 0 | @r0_2(glval:Derived) | +| Derived::Derived() -> void | 0 | 3 | 0 | @r0_3(glval:Middle) | +| Derived::Derived() -> void | 0 | 4 | 0 | @r0_4(bool) | +| Derived::Derived() -> void | 0 | 6 | 0 | @r0_6(glval:String) | +| Derived::Derived() -> void | 0 | 7 | 0 | @r0_7(bool) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 1 | 0 | @mu0_1(unknown) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 2 | 0 | @r0_2(glval:Derived) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 3 | 0 | @r0_3(Derived &) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 4 | 0 | @r0_4(glval:Derived &) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 5 | 0 | @m0_5(Derived &) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 6 | 0 | @r0_6(Derived *) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 7 | 0 | @r0_7(Middle *) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 8 | 0 | @r0_8(bool) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 9 | 0 | @r0_9(glval:Derived &) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 10 | 0 | @r0_10(Derived &) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 11 | 0 | @r0_11(Middle *) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 12 | 0 | @r0_12(Middle &) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 13 | 0 | @r0_13(Derived *) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 14 | 0 | @r0_14(glval:String) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 15 | 0 | @r0_15(bool) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 16 | 0 | @r0_16(glval:Derived &) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 17 | 0 | @r0_17(Derived &) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 18 | 0 | @r0_18(glval:String) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 19 | 0 | @r0_19(String &) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 20 | 0 | @r0_20(glval:Derived &) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 21 | 0 | @r0_21(Derived *) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 22 | 0 | @m0_22(Derived &) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 23 | 0 | @r0_23(glval:Derived &) | +| Derived::~Derived() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Derived::~Derived() -> void | 0 | 2 | 0 | @r0_2(glval:Derived) | +| Derived::~Derived() -> void | 0 | 4 | 0 | @r0_4(glval:String) | +| Derived::~Derived() -> void | 0 | 5 | 0 | @r0_5(bool) | +| Derived::~Derived() -> void | 0 | 7 | 0 | @r0_7(glval:Middle) | +| Derived::~Derived() -> void | 0 | 8 | 0 | @r0_8(bool) | +| DerivedVB::DerivedVB() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| DerivedVB::DerivedVB() -> void | 0 | 2 | 0 | @r0_2(glval:DerivedVB) | +| DerivedVB::DerivedVB() -> void | 0 | 3 | 0 | @r0_3(glval:Base) | +| DerivedVB::DerivedVB() -> void | 0 | 4 | 0 | @r0_4(bool) | +| DerivedVB::DerivedVB() -> void | 0 | 6 | 0 | @r0_6(glval:MiddleVB1) | +| DerivedVB::DerivedVB() -> void | 0 | 7 | 0 | @r0_7(bool) | +| DerivedVB::DerivedVB() -> void | 0 | 9 | 0 | @r0_9(glval:MiddleVB2) | +| DerivedVB::DerivedVB() -> void | 0 | 10 | 0 | @r0_10(bool) | +| DerivedVB::DerivedVB() -> void | 0 | 12 | 0 | @r0_12(glval:String) | +| DerivedVB::DerivedVB() -> void | 0 | 13 | 0 | @r0_13(bool) | +| DerivedVB::~DerivedVB() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| DerivedVB::~DerivedVB() -> void | 0 | 2 | 0 | @r0_2(glval:DerivedVB) | +| DerivedVB::~DerivedVB() -> void | 0 | 4 | 0 | @r0_4(glval:String) | +| DerivedVB::~DerivedVB() -> void | 0 | 5 | 0 | @r0_5(bool) | +| DerivedVB::~DerivedVB() -> void | 0 | 7 | 0 | @r0_7(glval:MiddleVB2) | +| DerivedVB::~DerivedVB() -> void | 0 | 8 | 0 | @r0_8(bool) | +| DerivedVB::~DerivedVB() -> void | 0 | 10 | 0 | @r0_10(glval:MiddleVB1) | +| DerivedVB::~DerivedVB() -> void | 0 | 11 | 0 | @r0_11(bool) | +| DerivedVB::~DerivedVB() -> void | 0 | 13 | 0 | @r0_13(glval:Base) | +| DerivedVB::~DerivedVB() -> void | 0 | 14 | 0 | @r0_14(bool) | +| DoStatements(int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| DoStatements(int) -> void | 0 | 2 | 0 | @r0_2(int) | +| DoStatements(int) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| DoStatements(int) -> void | 0 | 4 | 0 | @m0_4(int) | +| DoStatements(int) -> void | 1 | 0 | 0 | @m1_0(int) | +| DoStatements(int) -> void | 1 | 1 | 0 | @r1_1(int) | +| DoStatements(int) -> void | 1 | 2 | 0 | @r1_2(glval:int) | +| DoStatements(int) -> void | 1 | 3 | 0 | @r1_3(int) | +| DoStatements(int) -> void | 1 | 4 | 0 | @r1_4(int) | +| DoStatements(int) -> void | 1 | 5 | 0 | @m1_5(int) | +| DoStatements(int) -> void | 1 | 6 | 0 | @r1_6(glval:int) | +| DoStatements(int) -> void | 1 | 7 | 0 | @r1_7(int) | +| DoStatements(int) -> void | 1 | 8 | 0 | @r1_8(int) | +| DoStatements(int) -> void | 1 | 9 | 0 | @r1_9(bool) | +| DynamicCast() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| DynamicCast() -> void | 0 | 2 | 0 | @r0_2(glval:PolymorphicBase) | +| DynamicCast() -> void | 0 | 3 | 0 | @r0_3(bool) | +| DynamicCast() -> void | 0 | 5 | 0 | @r0_5(glval:PolymorphicDerived) | +| DynamicCast() -> void | 0 | 6 | 0 | @r0_6(bool) | +| DynamicCast() -> void | 0 | 8 | 0 | @r0_8(glval:PolymorphicBase *) | +| DynamicCast() -> void | 0 | 9 | 0 | @r0_9(glval:PolymorphicBase) | +| DynamicCast() -> void | 0 | 10 | 0 | @m0_10(PolymorphicBase *) | +| DynamicCast() -> void | 0 | 11 | 0 | @r0_11(glval:PolymorphicDerived *) | +| DynamicCast() -> void | 0 | 12 | 0 | @r0_12(glval:PolymorphicDerived) | +| DynamicCast() -> void | 0 | 13 | 0 | @m0_13(PolymorphicDerived *) | +| DynamicCast() -> void | 0 | 14 | 0 | @r0_14(glval:PolymorphicDerived *) | +| DynamicCast() -> void | 0 | 15 | 0 | @r0_15(PolymorphicDerived *) | +| DynamicCast() -> void | 0 | 16 | 0 | @r0_16(PolymorphicBase *) | +| DynamicCast() -> void | 0 | 17 | 0 | @r0_17(glval:PolymorphicBase *) | +| DynamicCast() -> void | 0 | 18 | 0 | @m0_18(PolymorphicBase *) | +| DynamicCast() -> void | 0 | 19 | 0 | @r0_19(glval:PolymorphicBase &) | +| DynamicCast() -> void | 0 | 20 | 0 | @r0_20(glval:PolymorphicDerived) | +| DynamicCast() -> void | 0 | 21 | 0 | @r0_21(glval:PolymorphicBase) | +| DynamicCast() -> void | 0 | 22 | 0 | @m0_22(PolymorphicBase &) | +| DynamicCast() -> void | 0 | 23 | 0 | @r0_23(glval:PolymorphicBase *) | +| DynamicCast() -> void | 0 | 24 | 0 | @r0_24(PolymorphicBase *) | +| DynamicCast() -> void | 0 | 25 | 0 | @r0_25(PolymorphicDerived *) | +| DynamicCast() -> void | 0 | 26 | 0 | @r0_26(glval:PolymorphicDerived *) | +| DynamicCast() -> void | 0 | 27 | 0 | @m0_27(PolymorphicDerived *) | +| DynamicCast() -> void | 0 | 28 | 0 | @r0_28(glval:PolymorphicDerived &) | +| DynamicCast() -> void | 0 | 29 | 0 | @r0_29(glval:PolymorphicBase) | +| DynamicCast() -> void | 0 | 30 | 0 | @r0_30(glval:PolymorphicDerived) | +| DynamicCast() -> void | 0 | 31 | 0 | @m0_31(PolymorphicDerived &) | +| DynamicCast() -> void | 0 | 32 | 0 | @r0_32(glval:void *) | +| DynamicCast() -> void | 0 | 33 | 0 | @r0_33(glval:PolymorphicBase *) | +| DynamicCast() -> void | 0 | 34 | 0 | @r0_34(PolymorphicBase *) | +| DynamicCast() -> void | 0 | 35 | 0 | @r0_35(void *) | +| DynamicCast() -> void | 0 | 36 | 0 | @m0_36(void *) | +| DynamicCast() -> void | 0 | 37 | 0 | @r0_37(glval:void *) | +| DynamicCast() -> void | 0 | 38 | 0 | @r0_38(glval:PolymorphicDerived *) | +| DynamicCast() -> void | 0 | 39 | 0 | @r0_39(PolymorphicDerived *) | +| DynamicCast() -> void | 0 | 40 | 0 | @r0_40(void *) | +| DynamicCast() -> void | 0 | 41 | 0 | @m0_41(void *) | +| EarlyReturn(int, int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| EarlyReturn(int, int) -> void | 0 | 2 | 0 | @r0_2(int) | +| EarlyReturn(int, int) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| EarlyReturn(int, int) -> void | 0 | 4 | 0 | @m0_4(int) | +| EarlyReturn(int, int) -> void | 0 | 5 | 0 | @r0_5(int) | +| EarlyReturn(int, int) -> void | 0 | 6 | 0 | @r0_6(glval:int) | +| EarlyReturn(int, int) -> void | 0 | 7 | 0 | @m0_7(int) | +| EarlyReturn(int, int) -> void | 0 | 8 | 0 | @r0_8(glval:int) | +| EarlyReturn(int, int) -> void | 0 | 9 | 0 | @r0_9(int) | +| EarlyReturn(int, int) -> void | 0 | 10 | 0 | @r0_10(glval:int) | +| EarlyReturn(int, int) -> void | 0 | 11 | 0 | @r0_11(int) | +| EarlyReturn(int, int) -> void | 0 | 12 | 0 | @r0_12(bool) | +| EarlyReturn(int, int) -> void | 3 | 0 | 0 | @r3_0(glval:int) | +| EarlyReturn(int, int) -> void | 3 | 1 | 0 | @r3_1(int) | +| EarlyReturn(int, int) -> void | 3 | 2 | 0 | @r3_2(glval:int) | +| EarlyReturn(int, int) -> void | 3 | 3 | 0 | @m3_3(int) | +| EarlyReturnValue(int, int) -> int | 0 | 1 | 0 | @mu0_1(unknown) | +| EarlyReturnValue(int, int) -> int | 0 | 2 | 0 | @r0_2(int) | +| EarlyReturnValue(int, int) -> int | 0 | 3 | 0 | @r0_3(glval:int) | +| EarlyReturnValue(int, int) -> int | 0 | 4 | 0 | @m0_4(int) | +| EarlyReturnValue(int, int) -> int | 0 | 5 | 0 | @r0_5(int) | +| EarlyReturnValue(int, int) -> int | 0 | 6 | 0 | @r0_6(glval:int) | +| EarlyReturnValue(int, int) -> int | 0 | 7 | 0 | @m0_7(int) | +| EarlyReturnValue(int, int) -> int | 0 | 8 | 0 | @r0_8(glval:int) | +| EarlyReturnValue(int, int) -> int | 0 | 9 | 0 | @r0_9(int) | +| EarlyReturnValue(int, int) -> int | 0 | 10 | 0 | @r0_10(glval:int) | +| EarlyReturnValue(int, int) -> int | 0 | 11 | 0 | @r0_11(int) | +| EarlyReturnValue(int, int) -> int | 0 | 12 | 0 | @r0_12(bool) | +| EarlyReturnValue(int, int) -> int | 1 | 0 | 0 | @m1_0(int) | +| EarlyReturnValue(int, int) -> int | 1 | 1 | 0 | @r1_1(glval:int) | +| EarlyReturnValue(int, int) -> int | 2 | 0 | 0 | @r2_0(glval:int) | +| EarlyReturnValue(int, int) -> int | 2 | 1 | 0 | @r2_1(glval:int) | +| EarlyReturnValue(int, int) -> int | 2 | 2 | 0 | @r2_2(int) | +| EarlyReturnValue(int, int) -> int | 2 | 3 | 0 | @m2_3(int) | +| EarlyReturnValue(int, int) -> int | 3 | 0 | 0 | @r3_0(glval:int) | +| EarlyReturnValue(int, int) -> int | 3 | 1 | 0 | @r3_1(glval:int) | +| EarlyReturnValue(int, int) -> int | 3 | 2 | 0 | @r3_2(int) | +| EarlyReturnValue(int, int) -> int | 3 | 3 | 0 | @r3_3(glval:int) | +| EarlyReturnValue(int, int) -> int | 3 | 4 | 0 | @r3_4(int) | +| EarlyReturnValue(int, int) -> int | 3 | 5 | 0 | @r3_5(int) | +| EarlyReturnValue(int, int) -> int | 3 | 6 | 0 | @m3_6(int) | +| EnumSwitch(E) -> int | 0 | 1 | 0 | @mu0_1(unknown) | +| EnumSwitch(E) -> int | 0 | 2 | 0 | @r0_2(E) | +| EnumSwitch(E) -> int | 0 | 3 | 0 | @r0_3(glval:E) | +| EnumSwitch(E) -> int | 0 | 4 | 0 | @m0_4(E) | +| EnumSwitch(E) -> int | 0 | 5 | 0 | @r0_5(glval:E) | +| EnumSwitch(E) -> int | 0 | 6 | 0 | @r0_6(E) | +| EnumSwitch(E) -> int | 0 | 7 | 0 | @r0_7(int) | +| EnumSwitch(E) -> int | 1 | 0 | 0 | @m1_0(int) | +| EnumSwitch(E) -> int | 1 | 1 | 0 | @r1_1(glval:int) | +| EnumSwitch(E) -> int | 2 | 1 | 0 | @r2_1(glval:int) | +| EnumSwitch(E) -> int | 2 | 2 | 0 | @r2_2(int) | +| EnumSwitch(E) -> int | 2 | 3 | 0 | @m2_3(int) | +| EnumSwitch(E) -> int | 3 | 1 | 0 | @r3_1(glval:int) | +| EnumSwitch(E) -> int | 3 | 2 | 0 | @r3_2(int) | +| EnumSwitch(E) -> int | 3 | 3 | 0 | @m3_3(int) | +| EnumSwitch(E) -> int | 4 | 1 | 0 | @r4_1(glval:int) | +| EnumSwitch(E) -> int | 4 | 2 | 0 | @r4_2(int) | +| EnumSwitch(E) -> int | 4 | 3 | 0 | @m4_3(int) | +| FieldAccess() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| FieldAccess() -> void | 0 | 2 | 0 | @r0_2(glval:Point) | +| FieldAccess() -> void | 0 | 3 | 0 | @r0_3(Point) | +| FieldAccess() -> void | 0 | 4 | 0 | @m0_4(Point) | +| FieldAccess() -> void | 0 | 5 | 0 | @r0_5(int) | +| FieldAccess() -> void | 0 | 6 | 0 | @r0_6(glval:Point) | +| FieldAccess() -> void | 0 | 7 | 0 | @r0_7(glval:int) | +| FieldAccess() -> void | 0 | 8 | 0 | @m0_8(int) | +| FieldAccess() -> void | 0 | 9 | 0 | @r0_9(glval:Point) | +| FieldAccess() -> void | 0 | 10 | 0 | @r0_10(glval:int) | +| FieldAccess() -> void | 0 | 11 | 0 | @r0_11(int) | +| FieldAccess() -> void | 0 | 12 | 0 | @r0_12(glval:Point) | +| FieldAccess() -> void | 0 | 13 | 0 | @r0_13(glval:int) | +| FieldAccess() -> void | 0 | 14 | 0 | @mu0_14(int) | +| FieldAccess() -> void | 0 | 15 | 0 | @r0_15(glval:int *) | +| FieldAccess() -> void | 0 | 16 | 0 | @r0_16(glval:Point) | +| FieldAccess() -> void | 0 | 17 | 0 | @r0_17(glval:int) | +| FieldAccess() -> void | 0 | 18 | 0 | @m0_18(int *) | +| FloatCompare(double, double) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| FloatCompare(double, double) -> void | 0 | 2 | 0 | @r0_2(double) | +| FloatCompare(double, double) -> void | 0 | 3 | 0 | @r0_3(glval:double) | +| FloatCompare(double, double) -> void | 0 | 4 | 0 | @m0_4(double) | +| FloatCompare(double, double) -> void | 0 | 5 | 0 | @r0_5(double) | +| FloatCompare(double, double) -> void | 0 | 6 | 0 | @r0_6(glval:double) | +| FloatCompare(double, double) -> void | 0 | 7 | 0 | @m0_7(double) | +| FloatCompare(double, double) -> void | 0 | 8 | 0 | @r0_8(glval:bool) | +| FloatCompare(double, double) -> void | 0 | 9 | 0 | @r0_9(bool) | +| FloatCompare(double, double) -> void | 0 | 10 | 0 | @m0_10(bool) | +| FloatCompare(double, double) -> void | 0 | 11 | 0 | @r0_11(glval:double) | +| FloatCompare(double, double) -> void | 0 | 12 | 0 | @r0_12(double) | +| FloatCompare(double, double) -> void | 0 | 13 | 0 | @r0_13(glval:double) | +| FloatCompare(double, double) -> void | 0 | 14 | 0 | @r0_14(double) | +| FloatCompare(double, double) -> void | 0 | 15 | 0 | @r0_15(bool) | +| FloatCompare(double, double) -> void | 0 | 16 | 0 | @r0_16(glval:bool) | +| FloatCompare(double, double) -> void | 0 | 17 | 0 | @m0_17(bool) | +| FloatCompare(double, double) -> void | 0 | 18 | 0 | @r0_18(glval:double) | +| FloatCompare(double, double) -> void | 0 | 19 | 0 | @r0_19(double) | +| FloatCompare(double, double) -> void | 0 | 20 | 0 | @r0_20(glval:double) | +| FloatCompare(double, double) -> void | 0 | 21 | 0 | @r0_21(double) | +| FloatCompare(double, double) -> void | 0 | 22 | 0 | @r0_22(bool) | +| FloatCompare(double, double) -> void | 0 | 23 | 0 | @r0_23(glval:bool) | +| FloatCompare(double, double) -> void | 0 | 24 | 0 | @m0_24(bool) | +| FloatCompare(double, double) -> void | 0 | 25 | 0 | @r0_25(glval:double) | +| FloatCompare(double, double) -> void | 0 | 26 | 0 | @r0_26(double) | +| FloatCompare(double, double) -> void | 0 | 27 | 0 | @r0_27(glval:double) | +| FloatCompare(double, double) -> void | 0 | 28 | 0 | @r0_28(double) | +| FloatCompare(double, double) -> void | 0 | 29 | 0 | @r0_29(bool) | +| FloatCompare(double, double) -> void | 0 | 30 | 0 | @r0_30(glval:bool) | +| FloatCompare(double, double) -> void | 0 | 31 | 0 | @m0_31(bool) | +| FloatCompare(double, double) -> void | 0 | 32 | 0 | @r0_32(glval:double) | +| FloatCompare(double, double) -> void | 0 | 33 | 0 | @r0_33(double) | +| FloatCompare(double, double) -> void | 0 | 34 | 0 | @r0_34(glval:double) | +| FloatCompare(double, double) -> void | 0 | 35 | 0 | @r0_35(double) | +| FloatCompare(double, double) -> void | 0 | 36 | 0 | @r0_36(bool) | +| FloatCompare(double, double) -> void | 0 | 37 | 0 | @r0_37(glval:bool) | +| FloatCompare(double, double) -> void | 0 | 38 | 0 | @m0_38(bool) | +| FloatCompare(double, double) -> void | 0 | 39 | 0 | @r0_39(glval:double) | +| FloatCompare(double, double) -> void | 0 | 40 | 0 | @r0_40(double) | +| FloatCompare(double, double) -> void | 0 | 41 | 0 | @r0_41(glval:double) | +| FloatCompare(double, double) -> void | 0 | 42 | 0 | @r0_42(double) | +| FloatCompare(double, double) -> void | 0 | 43 | 0 | @r0_43(bool) | +| FloatCompare(double, double) -> void | 0 | 44 | 0 | @r0_44(glval:bool) | +| FloatCompare(double, double) -> void | 0 | 45 | 0 | @m0_45(bool) | +| FloatCompare(double, double) -> void | 0 | 46 | 0 | @r0_46(glval:double) | +| FloatCompare(double, double) -> void | 0 | 47 | 0 | @r0_47(double) | +| FloatCompare(double, double) -> void | 0 | 48 | 0 | @r0_48(glval:double) | +| FloatCompare(double, double) -> void | 0 | 49 | 0 | @r0_49(double) | +| FloatCompare(double, double) -> void | 0 | 50 | 0 | @r0_50(bool) | +| FloatCompare(double, double) -> void | 0 | 51 | 0 | @r0_51(glval:bool) | +| FloatCompare(double, double) -> void | 0 | 52 | 0 | @m0_52(bool) | +| FloatCrement(float) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| FloatCrement(float) -> void | 0 | 2 | 0 | @r0_2(float) | +| FloatCrement(float) -> void | 0 | 3 | 0 | @r0_3(glval:float) | +| FloatCrement(float) -> void | 0 | 4 | 0 | @m0_4(float) | +| FloatCrement(float) -> void | 0 | 5 | 0 | @r0_5(glval:float) | +| FloatCrement(float) -> void | 0 | 6 | 0 | @r0_6(float) | +| FloatCrement(float) -> void | 0 | 7 | 0 | @m0_7(float) | +| FloatCrement(float) -> void | 0 | 8 | 0 | @r0_8(glval:float) | +| FloatCrement(float) -> void | 0 | 9 | 0 | @r0_9(float) | +| FloatCrement(float) -> void | 0 | 10 | 0 | @r0_10(float) | +| FloatCrement(float) -> void | 0 | 11 | 0 | @r0_11(float) | +| FloatCrement(float) -> void | 0 | 12 | 0 | @m0_12(float) | +| FloatCrement(float) -> void | 0 | 13 | 0 | @r0_13(glval:float) | +| FloatCrement(float) -> void | 0 | 14 | 0 | @m0_14(float) | +| FloatCrement(float) -> void | 0 | 15 | 0 | @r0_15(glval:float) | +| FloatCrement(float) -> void | 0 | 16 | 0 | @r0_16(float) | +| FloatCrement(float) -> void | 0 | 17 | 0 | @r0_17(float) | +| FloatCrement(float) -> void | 0 | 18 | 0 | @r0_18(float) | +| FloatCrement(float) -> void | 0 | 19 | 0 | @m0_19(float) | +| FloatCrement(float) -> void | 0 | 20 | 0 | @r0_20(glval:float) | +| FloatCrement(float) -> void | 0 | 21 | 0 | @m0_21(float) | +| FloatCrement(float) -> void | 0 | 22 | 0 | @r0_22(glval:float) | +| FloatCrement(float) -> void | 0 | 23 | 0 | @r0_23(float) | +| FloatCrement(float) -> void | 0 | 24 | 0 | @r0_24(float) | +| FloatCrement(float) -> void | 0 | 25 | 0 | @r0_25(float) | +| FloatCrement(float) -> void | 0 | 26 | 0 | @m0_26(float) | +| FloatCrement(float) -> void | 0 | 27 | 0 | @r0_27(glval:float) | +| FloatCrement(float) -> void | 0 | 28 | 0 | @m0_28(float) | +| FloatCrement(float) -> void | 0 | 29 | 0 | @r0_29(glval:float) | +| FloatCrement(float) -> void | 0 | 30 | 0 | @r0_30(float) | +| FloatCrement(float) -> void | 0 | 31 | 0 | @r0_31(float) | +| FloatCrement(float) -> void | 0 | 32 | 0 | @r0_32(float) | +| FloatCrement(float) -> void | 0 | 33 | 0 | @m0_33(float) | +| FloatCrement(float) -> void | 0 | 34 | 0 | @r0_34(glval:float) | +| FloatCrement(float) -> void | 0 | 35 | 0 | @m0_35(float) | +| FloatOps(double, double) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| FloatOps(double, double) -> void | 0 | 2 | 0 | @r0_2(double) | +| FloatOps(double, double) -> void | 0 | 3 | 0 | @r0_3(glval:double) | +| FloatOps(double, double) -> void | 0 | 4 | 0 | @m0_4(double) | +| FloatOps(double, double) -> void | 0 | 5 | 0 | @r0_5(double) | +| FloatOps(double, double) -> void | 0 | 6 | 0 | @r0_6(glval:double) | +| FloatOps(double, double) -> void | 0 | 7 | 0 | @m0_7(double) | +| FloatOps(double, double) -> void | 0 | 8 | 0 | @r0_8(glval:double) | +| FloatOps(double, double) -> void | 0 | 9 | 0 | @r0_9(double) | +| FloatOps(double, double) -> void | 0 | 10 | 0 | @m0_10(double) | +| FloatOps(double, double) -> void | 0 | 11 | 0 | @r0_11(glval:double) | +| FloatOps(double, double) -> void | 0 | 12 | 0 | @r0_12(double) | +| FloatOps(double, double) -> void | 0 | 13 | 0 | @r0_13(glval:double) | +| FloatOps(double, double) -> void | 0 | 14 | 0 | @r0_14(double) | +| FloatOps(double, double) -> void | 0 | 15 | 0 | @r0_15(double) | +| FloatOps(double, double) -> void | 0 | 16 | 0 | @r0_16(glval:double) | +| FloatOps(double, double) -> void | 0 | 17 | 0 | @m0_17(double) | +| FloatOps(double, double) -> void | 0 | 18 | 0 | @r0_18(glval:double) | +| FloatOps(double, double) -> void | 0 | 19 | 0 | @r0_19(double) | +| FloatOps(double, double) -> void | 0 | 20 | 0 | @r0_20(glval:double) | +| FloatOps(double, double) -> void | 0 | 21 | 0 | @r0_21(double) | +| FloatOps(double, double) -> void | 0 | 22 | 0 | @r0_22(double) | +| FloatOps(double, double) -> void | 0 | 23 | 0 | @r0_23(glval:double) | +| FloatOps(double, double) -> void | 0 | 24 | 0 | @m0_24(double) | +| FloatOps(double, double) -> void | 0 | 25 | 0 | @r0_25(glval:double) | +| FloatOps(double, double) -> void | 0 | 26 | 0 | @r0_26(double) | +| FloatOps(double, double) -> void | 0 | 27 | 0 | @r0_27(glval:double) | +| FloatOps(double, double) -> void | 0 | 28 | 0 | @r0_28(double) | +| FloatOps(double, double) -> void | 0 | 29 | 0 | @r0_29(double) | +| FloatOps(double, double) -> void | 0 | 30 | 0 | @r0_30(glval:double) | +| FloatOps(double, double) -> void | 0 | 31 | 0 | @m0_31(double) | +| FloatOps(double, double) -> void | 0 | 32 | 0 | @r0_32(glval:double) | +| FloatOps(double, double) -> void | 0 | 33 | 0 | @r0_33(double) | +| FloatOps(double, double) -> void | 0 | 34 | 0 | @r0_34(glval:double) | +| FloatOps(double, double) -> void | 0 | 35 | 0 | @r0_35(double) | +| FloatOps(double, double) -> void | 0 | 36 | 0 | @r0_36(double) | +| FloatOps(double, double) -> void | 0 | 37 | 0 | @r0_37(glval:double) | +| FloatOps(double, double) -> void | 0 | 38 | 0 | @m0_38(double) | +| FloatOps(double, double) -> void | 0 | 39 | 0 | @r0_39(glval:double) | +| FloatOps(double, double) -> void | 0 | 40 | 0 | @r0_40(double) | +| FloatOps(double, double) -> void | 0 | 41 | 0 | @r0_41(glval:double) | +| FloatOps(double, double) -> void | 0 | 42 | 0 | @m0_42(double) | +| FloatOps(double, double) -> void | 0 | 43 | 0 | @r0_43(glval:double) | +| FloatOps(double, double) -> void | 0 | 44 | 0 | @r0_44(double) | +| FloatOps(double, double) -> void | 0 | 45 | 0 | @r0_45(glval:double) | +| FloatOps(double, double) -> void | 0 | 46 | 0 | @r0_46(double) | +| FloatOps(double, double) -> void | 0 | 47 | 0 | @r0_47(double) | +| FloatOps(double, double) -> void | 0 | 48 | 0 | @m0_48(double) | +| FloatOps(double, double) -> void | 0 | 49 | 0 | @r0_49(glval:double) | +| FloatOps(double, double) -> void | 0 | 50 | 0 | @r0_50(double) | +| FloatOps(double, double) -> void | 0 | 51 | 0 | @r0_51(glval:double) | +| FloatOps(double, double) -> void | 0 | 52 | 0 | @r0_52(double) | +| FloatOps(double, double) -> void | 0 | 53 | 0 | @r0_53(double) | +| FloatOps(double, double) -> void | 0 | 54 | 0 | @m0_54(double) | +| FloatOps(double, double) -> void | 0 | 55 | 0 | @r0_55(glval:double) | +| FloatOps(double, double) -> void | 0 | 56 | 0 | @r0_56(double) | +| FloatOps(double, double) -> void | 0 | 57 | 0 | @r0_57(glval:double) | +| FloatOps(double, double) -> void | 0 | 58 | 0 | @r0_58(double) | +| FloatOps(double, double) -> void | 0 | 59 | 0 | @r0_59(double) | +| FloatOps(double, double) -> void | 0 | 60 | 0 | @m0_60(double) | +| FloatOps(double, double) -> void | 0 | 61 | 0 | @r0_61(glval:double) | +| FloatOps(double, double) -> void | 0 | 62 | 0 | @r0_62(double) | +| FloatOps(double, double) -> void | 0 | 63 | 0 | @r0_63(glval:double) | +| FloatOps(double, double) -> void | 0 | 64 | 0 | @r0_64(double) | +| FloatOps(double, double) -> void | 0 | 65 | 0 | @r0_65(double) | +| FloatOps(double, double) -> void | 0 | 66 | 0 | @m0_66(double) | +| FloatOps(double, double) -> void | 0 | 67 | 0 | @r0_67(glval:double) | +| FloatOps(double, double) -> void | 0 | 68 | 0 | @r0_68(double) | +| FloatOps(double, double) -> void | 0 | 69 | 0 | @r0_69(double) | +| FloatOps(double, double) -> void | 0 | 70 | 0 | @r0_70(glval:double) | +| FloatOps(double, double) -> void | 0 | 71 | 0 | @m0_71(double) | +| FloatOps(double, double) -> void | 0 | 72 | 0 | @r0_72(glval:double) | +| FloatOps(double, double) -> void | 0 | 73 | 0 | @r0_73(double) | +| FloatOps(double, double) -> void | 0 | 74 | 0 | @r0_74(double) | +| FloatOps(double, double) -> void | 0 | 75 | 0 | @r0_75(glval:double) | +| FloatOps(double, double) -> void | 0 | 76 | 0 | @m0_76(double) | +| Foo() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Foo() -> void | 0 | 2 | 0 | @r0_2(glval:int) | +| Foo() -> void | 0 | 3 | 0 | @r0_3(int) | +| Foo() -> void | 0 | 4 | 0 | @m0_4(int) | +| Foo() -> void | 0 | 5 | 0 | @r0_5(glval:short) | +| Foo() -> void | 0 | 6 | 0 | @r0_6(short) | +| Foo() -> void | 0 | 7 | 0 | @m0_7(short) | +| Foo() -> void | 0 | 8 | 0 | @r0_8(glval:int) | +| Foo() -> void | 0 | 9 | 0 | @r0_9(int) | +| Foo() -> void | 0 | 10 | 0 | @r0_10(glval:short) | +| Foo() -> void | 0 | 11 | 0 | @r0_11(short) | +| Foo() -> void | 0 | 12 | 0 | @r0_12(int) | +| Foo() -> void | 0 | 13 | 0 | @r0_13(int) | +| Foo() -> void | 0 | 14 | 0 | @r0_14(short) | +| Foo() -> void | 0 | 15 | 0 | @r0_15(glval:short) | +| Foo() -> void | 0 | 16 | 0 | @m0_16(short) | +| Foo() -> void | 0 | 17 | 0 | @r0_17(glval:int) | +| Foo() -> void | 0 | 18 | 0 | @r0_18(int) | +| Foo() -> void | 0 | 19 | 0 | @r0_19(glval:short) | +| Foo() -> void | 0 | 20 | 0 | @r0_20(short) | +| Foo() -> void | 0 | 21 | 0 | @r0_21(int) | +| Foo() -> void | 0 | 22 | 0 | @r0_22(int) | +| Foo() -> void | 0 | 23 | 0 | @r0_23(glval:int) | +| Foo() -> void | 0 | 24 | 0 | @m0_24(int) | +| For_Break() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| For_Break() -> void | 0 | 2 | 0 | @r0_2(glval:int) | +| For_Break() -> void | 0 | 3 | 0 | @r0_3(int) | +| For_Break() -> void | 0 | 4 | 0 | @m0_4(int) | +| For_Break() -> void | 1 | 0 | 0 | @m1_0(int) | +| For_Break() -> void | 1 | 1 | 0 | @r1_1(glval:int) | +| For_Break() -> void | 1 | 2 | 0 | @r1_2(int) | +| For_Break() -> void | 1 | 3 | 0 | @r1_3(int) | +| For_Break() -> void | 1 | 4 | 0 | @r1_4(bool) | +| For_Break() -> void | 2 | 0 | 0 | @r2_0(int) | +| For_Break() -> void | 2 | 1 | 0 | @r2_1(glval:int) | +| For_Break() -> void | 2 | 2 | 0 | @r2_2(int) | +| For_Break() -> void | 2 | 3 | 0 | @r2_3(int) | +| For_Break() -> void | 2 | 4 | 0 | @m2_4(int) | +| For_Break() -> void | 3 | 0 | 0 | @r3_0(glval:int) | +| For_Break() -> void | 3 | 1 | 0 | @r3_1(int) | +| For_Break() -> void | 3 | 2 | 0 | @r3_2(int) | +| For_Break() -> void | 3 | 3 | 0 | @r3_3(bool) | +| For_Condition() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| For_Condition() -> void | 0 | 2 | 0 | @r0_2(glval:int) | +| For_Condition() -> void | 0 | 3 | 0 | @r0_3(int) | +| For_Condition() -> void | 0 | 4 | 0 | @m0_4(int) | +| For_Condition() -> void | 1 | 0 | 0 | @r1_0(glval:int) | +| For_Condition() -> void | 1 | 1 | 0 | @r1_1(int) | +| For_Condition() -> void | 1 | 2 | 0 | @r1_2(int) | +| For_Condition() -> void | 1 | 3 | 0 | @r1_3(bool) | +| For_ConditionUpdate() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| For_ConditionUpdate() -> void | 0 | 2 | 0 | @r0_2(glval:int) | +| For_ConditionUpdate() -> void | 0 | 3 | 0 | @r0_3(int) | +| For_ConditionUpdate() -> void | 0 | 4 | 0 | @m0_4(int) | +| For_ConditionUpdate() -> void | 1 | 0 | 0 | @m1_0(int) | +| For_ConditionUpdate() -> void | 1 | 1 | 0 | @r1_1(glval:int) | +| For_ConditionUpdate() -> void | 1 | 2 | 0 | @r1_2(int) | +| For_ConditionUpdate() -> void | 1 | 3 | 0 | @r1_3(int) | +| For_ConditionUpdate() -> void | 1 | 4 | 0 | @r1_4(bool) | +| For_ConditionUpdate() -> void | 2 | 1 | 0 | @r2_1(int) | +| For_ConditionUpdate() -> void | 2 | 2 | 0 | @r2_2(glval:int) | +| For_ConditionUpdate() -> void | 2 | 3 | 0 | @r2_3(int) | +| For_ConditionUpdate() -> void | 2 | 4 | 0 | @r2_4(int) | +| For_ConditionUpdate() -> void | 2 | 5 | 0 | @m2_5(int) | +| For_Continue_NoUpdate() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| For_Continue_NoUpdate() -> void | 0 | 2 | 0 | @r0_2(glval:int) | +| For_Continue_NoUpdate() -> void | 0 | 3 | 0 | @r0_3(int) | +| For_Continue_NoUpdate() -> void | 0 | 4 | 0 | @m0_4(int) | +| For_Continue_NoUpdate() -> void | 1 | 0 | 0 | @r1_0(glval:int) | +| For_Continue_NoUpdate() -> void | 1 | 1 | 0 | @r1_1(int) | +| For_Continue_NoUpdate() -> void | 1 | 2 | 0 | @r1_2(int) | +| For_Continue_NoUpdate() -> void | 1 | 3 | 0 | @r1_3(bool) | +| For_Continue_NoUpdate() -> void | 2 | 0 | 0 | @r2_0(glval:int) | +| For_Continue_NoUpdate() -> void | 2 | 1 | 0 | @r2_1(int) | +| For_Continue_NoUpdate() -> void | 2 | 2 | 0 | @r2_2(int) | +| For_Continue_NoUpdate() -> void | 2 | 3 | 0 | @r2_3(bool) | +| For_Continue_Update() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| For_Continue_Update() -> void | 0 | 2 | 0 | @r0_2(glval:int) | +| For_Continue_Update() -> void | 0 | 3 | 0 | @r0_3(int) | +| For_Continue_Update() -> void | 0 | 4 | 0 | @m0_4(int) | +| For_Continue_Update() -> void | 1 | 0 | 0 | @m1_0(int) | +| For_Continue_Update() -> void | 1 | 1 | 0 | @r1_1(glval:int) | +| For_Continue_Update() -> void | 1 | 2 | 0 | @r1_2(int) | +| For_Continue_Update() -> void | 1 | 3 | 0 | @r1_3(int) | +| For_Continue_Update() -> void | 1 | 4 | 0 | @r1_4(bool) | +| For_Continue_Update() -> void | 2 | 0 | 0 | @r2_0(glval:int) | +| For_Continue_Update() -> void | 2 | 1 | 0 | @r2_1(int) | +| For_Continue_Update() -> void | 2 | 2 | 0 | @r2_2(int) | +| For_Continue_Update() -> void | 2 | 3 | 0 | @r2_3(bool) | +| For_Continue_Update() -> void | 4 | 1 | 0 | @r4_1(int) | +| For_Continue_Update() -> void | 4 | 2 | 0 | @r4_2(glval:int) | +| For_Continue_Update() -> void | 4 | 3 | 0 | @r4_3(int) | +| For_Continue_Update() -> void | 4 | 4 | 0 | @r4_4(int) | +| For_Continue_Update() -> void | 4 | 5 | 0 | @m4_5(int) | +| For_Empty() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| For_Empty() -> void | 0 | 2 | 0 | @r0_2(glval:int) | +| For_Empty() -> void | 0 | 3 | 0 | @r0_3(int) | +| For_Empty() -> void | 0 | 4 | 0 | @m0_4(int) | +| For_Init() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| For_Init() -> void | 0 | 2 | 0 | @r0_2(glval:int) | +| For_Init() -> void | 0 | 3 | 0 | @r0_3(int) | +| For_Init() -> void | 0 | 4 | 0 | @m0_4(int) | +| For_InitCondition() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| For_InitCondition() -> void | 0 | 2 | 0 | @r0_2(glval:int) | +| For_InitCondition() -> void | 0 | 3 | 0 | @r0_3(int) | +| For_InitCondition() -> void | 0 | 4 | 0 | @m0_4(int) | +| For_InitCondition() -> void | 1 | 0 | 0 | @r1_0(glval:int) | +| For_InitCondition() -> void | 1 | 1 | 0 | @r1_1(int) | +| For_InitCondition() -> void | 1 | 2 | 0 | @r1_2(int) | +| For_InitCondition() -> void | 1 | 3 | 0 | @r1_3(bool) | +| For_InitConditionUpdate() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| For_InitConditionUpdate() -> void | 0 | 2 | 0 | @r0_2(glval:int) | +| For_InitConditionUpdate() -> void | 0 | 3 | 0 | @r0_3(int) | +| For_InitConditionUpdate() -> void | 0 | 4 | 0 | @m0_4(int) | +| For_InitConditionUpdate() -> void | 1 | 0 | 0 | @m1_0(int) | +| For_InitConditionUpdate() -> void | 1 | 1 | 0 | @r1_1(glval:int) | +| For_InitConditionUpdate() -> void | 1 | 2 | 0 | @r1_2(int) | +| For_InitConditionUpdate() -> void | 1 | 3 | 0 | @r1_3(int) | +| For_InitConditionUpdate() -> void | 1 | 4 | 0 | @r1_4(bool) | +| For_InitConditionUpdate() -> void | 2 | 1 | 0 | @r2_1(int) | +| For_InitConditionUpdate() -> void | 2 | 2 | 0 | @r2_2(glval:int) | +| For_InitConditionUpdate() -> void | 2 | 3 | 0 | @r2_3(int) | +| For_InitConditionUpdate() -> void | 2 | 4 | 0 | @r2_4(int) | +| For_InitConditionUpdate() -> void | 2 | 5 | 0 | @m2_5(int) | +| For_InitUpdate() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| For_InitUpdate() -> void | 0 | 2 | 0 | @r0_2(glval:int) | +| For_InitUpdate() -> void | 0 | 3 | 0 | @r0_3(int) | +| For_InitUpdate() -> void | 0 | 4 | 0 | @m0_4(int) | +| For_InitUpdate() -> void | 2 | 0 | 0 | @m2_0(int) | +| For_InitUpdate() -> void | 2 | 2 | 0 | @r2_2(int) | +| For_InitUpdate() -> void | 2 | 3 | 0 | @r2_3(glval:int) | +| For_InitUpdate() -> void | 2 | 4 | 0 | @r2_4(int) | +| For_InitUpdate() -> void | 2 | 5 | 0 | @r2_5(int) | +| For_InitUpdate() -> void | 2 | 6 | 0 | @m2_6(int) | +| For_Update() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| For_Update() -> void | 0 | 2 | 0 | @r0_2(glval:int) | +| For_Update() -> void | 0 | 3 | 0 | @r0_3(int) | +| For_Update() -> void | 0 | 4 | 0 | @m0_4(int) | +| For_Update() -> void | 2 | 0 | 0 | @m2_0(int) | +| For_Update() -> void | 2 | 2 | 0 | @r2_2(int) | +| For_Update() -> void | 2 | 3 | 0 | @r2_3(glval:int) | +| For_Update() -> void | 2 | 4 | 0 | @r2_4(int) | +| For_Update() -> void | 2 | 5 | 0 | @r2_5(int) | +| For_Update() -> void | 2 | 6 | 0 | @m2_6(int) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 2 | 0 | @r0_2(..(*)(..)) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 3 | 0 | @r0_3(glval:..(*)(..)) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 4 | 0 | @m0_4(..(*)(..)) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 5 | 0 | @r0_5(void *) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 6 | 0 | @r0_6(glval:void *) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 7 | 0 | @m0_7(void *) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 8 | 0 | @r0_8(glval:..(*)(..)) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 9 | 0 | @r0_9(..(*)(..)) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 10 | 0 | @r0_10(void *) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 11 | 0 | @r0_11(glval:void *) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 12 | 0 | @m0_12(void *) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 13 | 0 | @r0_13(glval:void *) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 14 | 0 | @r0_14(void *) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 15 | 0 | @r0_15(..(*)(..)) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 16 | 0 | @r0_16(glval:..(*)(..)) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 17 | 0 | @m0_17(..(*)(..)) | +| FunctionReferences() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| FunctionReferences() -> void | 0 | 2 | 0 | @r0_2(glval:..(&)(..)) | +| FunctionReferences() -> void | 0 | 3 | 0 | @r0_3(glval:..()(..)) | +| FunctionReferences() -> void | 0 | 4 | 0 | @m0_4(..(&)(..)) | +| FunctionReferences() -> void | 0 | 5 | 0 | @r0_5(glval:..(*)(..)) | +| FunctionReferences() -> void | 0 | 6 | 0 | @r0_6(glval:..(&)(..)) | +| FunctionReferences() -> void | 0 | 7 | 0 | @r0_7(..(&)(..)) | +| FunctionReferences() -> void | 0 | 8 | 0 | @m0_8(..(*)(..)) | +| FunctionReferences() -> void | 0 | 9 | 0 | @r0_9(glval:..(&)(..)) | +| FunctionReferences() -> void | 0 | 10 | 0 | @r0_10(..(&)(..)) | +| FunctionReferences() -> void | 0 | 11 | 0 | @r0_11(int) | +| FunctionReferences() -> void | 0 | 12 | 0 | @r0_12(int) | +| HierarchyConversions() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| HierarchyConversions() -> void | 0 | 2 | 0 | @r0_2(glval:Base) | +| HierarchyConversions() -> void | 0 | 3 | 0 | @r0_3(bool) | +| HierarchyConversions() -> void | 0 | 5 | 0 | @r0_5(glval:Middle) | +| HierarchyConversions() -> void | 0 | 6 | 0 | @r0_6(bool) | +| HierarchyConversions() -> void | 0 | 8 | 0 | @r0_8(glval:Derived) | +| HierarchyConversions() -> void | 0 | 9 | 0 | @r0_9(bool) | +| HierarchyConversions() -> void | 0 | 11 | 0 | @r0_11(glval:Base *) | +| HierarchyConversions() -> void | 0 | 12 | 0 | @r0_12(glval:Base) | +| HierarchyConversions() -> void | 0 | 13 | 0 | @m0_13(Base *) | +| HierarchyConversions() -> void | 0 | 14 | 0 | @r0_14(glval:Middle *) | +| HierarchyConversions() -> void | 0 | 15 | 0 | @r0_15(glval:Middle) | +| HierarchyConversions() -> void | 0 | 16 | 0 | @m0_16(Middle *) | +| HierarchyConversions() -> void | 0 | 17 | 0 | @r0_17(glval:Derived *) | +| HierarchyConversions() -> void | 0 | 18 | 0 | @r0_18(glval:Derived) | +| HierarchyConversions() -> void | 0 | 19 | 0 | @m0_19(Derived *) | +| HierarchyConversions() -> void | 0 | 20 | 0 | @r0_20(glval:Base) | +| HierarchyConversions() -> void | 0 | 21 | 0 | @r0_21(bool) | +| HierarchyConversions() -> void | 0 | 22 | 0 | @r0_22(glval:Middle) | +| HierarchyConversions() -> void | 0 | 23 | 0 | @r0_23(glval:Base) | +| HierarchyConversions() -> void | 0 | 24 | 0 | @r0_24(Base &) | +| HierarchyConversions() -> void | 0 | 25 | 0 | @r0_25(glval:Base) | +| HierarchyConversions() -> void | 0 | 26 | 0 | @r0_26(bool) | +| HierarchyConversions() -> void | 0 | 27 | 0 | @r0_27(bool) | +| HierarchyConversions() -> void | 0 | 28 | 0 | @r0_28(glval:Middle) | +| HierarchyConversions() -> void | 0 | 29 | 0 | @r0_29(glval:Base) | +| HierarchyConversions() -> void | 0 | 31 | 0 | @r0_31(Base) | +| HierarchyConversions() -> void | 0 | 32 | 0 | @r0_32(Base &) | +| HierarchyConversions() -> void | 0 | 33 | 0 | @r0_33(glval:Base) | +| HierarchyConversions() -> void | 0 | 34 | 0 | @r0_34(bool) | +| HierarchyConversions() -> void | 0 | 35 | 0 | @r0_35(bool) | +| HierarchyConversions() -> void | 0 | 36 | 0 | @r0_36(glval:Middle) | +| HierarchyConversions() -> void | 0 | 37 | 0 | @r0_37(glval:Base) | +| HierarchyConversions() -> void | 0 | 39 | 0 | @r0_39(Base) | +| HierarchyConversions() -> void | 0 | 40 | 0 | @r0_40(Base &) | +| HierarchyConversions() -> void | 0 | 41 | 0 | @r0_41(glval:Middle *) | +| HierarchyConversions() -> void | 0 | 42 | 0 | @r0_42(Middle *) | +| HierarchyConversions() -> void | 0 | 43 | 0 | @r0_43(Base *) | +| HierarchyConversions() -> void | 0 | 44 | 0 | @r0_44(glval:Base *) | +| HierarchyConversions() -> void | 0 | 45 | 0 | @m0_45(Base *) | +| HierarchyConversions() -> void | 0 | 46 | 0 | @r0_46(glval:Middle *) | +| HierarchyConversions() -> void | 0 | 47 | 0 | @r0_47(Middle *) | +| HierarchyConversions() -> void | 0 | 48 | 0 | @r0_48(Base *) | +| HierarchyConversions() -> void | 0 | 49 | 0 | @r0_49(glval:Base *) | +| HierarchyConversions() -> void | 0 | 50 | 0 | @m0_50(Base *) | +| HierarchyConversions() -> void | 0 | 51 | 0 | @r0_51(glval:Middle *) | +| HierarchyConversions() -> void | 0 | 52 | 0 | @r0_52(Middle *) | +| HierarchyConversions() -> void | 0 | 53 | 0 | @r0_53(Base *) | +| HierarchyConversions() -> void | 0 | 54 | 0 | @r0_54(glval:Base *) | +| HierarchyConversions() -> void | 0 | 55 | 0 | @m0_55(Base *) | +| HierarchyConversions() -> void | 0 | 56 | 0 | @r0_56(glval:Middle *) | +| HierarchyConversions() -> void | 0 | 57 | 0 | @r0_57(Middle *) | +| HierarchyConversions() -> void | 0 | 58 | 0 | @r0_58(Base *) | +| HierarchyConversions() -> void | 0 | 59 | 0 | @r0_59(glval:Base *) | +| HierarchyConversions() -> void | 0 | 60 | 0 | @m0_60(Base *) | +| HierarchyConversions() -> void | 0 | 61 | 0 | @r0_61(glval:Middle) | +| HierarchyConversions() -> void | 0 | 62 | 0 | @r0_62(bool) | +| HierarchyConversions() -> void | 0 | 63 | 0 | @r0_63(glval:Base) | +| HierarchyConversions() -> void | 0 | 64 | 0 | @r0_64(glval:Middle) | +| HierarchyConversions() -> void | 0 | 65 | 0 | @r0_65(glval:Middle) | +| HierarchyConversions() -> void | 0 | 66 | 0 | @r0_66(Middle &) | +| HierarchyConversions() -> void | 0 | 67 | 0 | @r0_67(glval:Middle) | +| HierarchyConversions() -> void | 0 | 68 | 0 | @r0_68(bool) | +| HierarchyConversions() -> void | 0 | 69 | 0 | @r0_69(glval:Base) | +| HierarchyConversions() -> void | 0 | 70 | 0 | @r0_70(glval:Middle) | +| HierarchyConversions() -> void | 0 | 71 | 0 | @r0_71(glval:Middle) | +| HierarchyConversions() -> void | 0 | 72 | 0 | @r0_72(Middle &) | +| HierarchyConversions() -> void | 0 | 73 | 0 | @r0_73(glval:Base *) | +| HierarchyConversions() -> void | 0 | 74 | 0 | @r0_74(Base *) | +| HierarchyConversions() -> void | 0 | 75 | 0 | @r0_75(Middle *) | +| HierarchyConversions() -> void | 0 | 76 | 0 | @r0_76(glval:Middle *) | +| HierarchyConversions() -> void | 0 | 77 | 0 | @m0_77(Middle *) | +| HierarchyConversions() -> void | 0 | 78 | 0 | @r0_78(glval:Base *) | +| HierarchyConversions() -> void | 0 | 79 | 0 | @r0_79(Base *) | +| HierarchyConversions() -> void | 0 | 80 | 0 | @r0_80(Middle *) | +| HierarchyConversions() -> void | 0 | 81 | 0 | @r0_81(glval:Middle *) | +| HierarchyConversions() -> void | 0 | 82 | 0 | @m0_82(Middle *) | +| HierarchyConversions() -> void | 0 | 83 | 0 | @r0_83(glval:Base *) | +| HierarchyConversions() -> void | 0 | 84 | 0 | @r0_84(Base *) | +| HierarchyConversions() -> void | 0 | 85 | 0 | @r0_85(Middle *) | +| HierarchyConversions() -> void | 0 | 86 | 0 | @r0_86(glval:Middle *) | +| HierarchyConversions() -> void | 0 | 87 | 0 | @m0_87(Middle *) | +| HierarchyConversions() -> void | 0 | 88 | 0 | @r0_88(glval:Base) | +| HierarchyConversions() -> void | 0 | 89 | 0 | @r0_89(bool) | +| HierarchyConversions() -> void | 0 | 90 | 0 | @r0_90(glval:Derived) | +| HierarchyConversions() -> void | 0 | 91 | 0 | @r0_91(glval:Middle) | +| HierarchyConversions() -> void | 0 | 92 | 0 | @r0_92(glval:Base) | +| HierarchyConversions() -> void | 0 | 93 | 0 | @r0_93(Base &) | +| HierarchyConversions() -> void | 0 | 94 | 0 | @r0_94(glval:Base) | +| HierarchyConversions() -> void | 0 | 95 | 0 | @r0_95(bool) | +| HierarchyConversions() -> void | 0 | 96 | 0 | @r0_96(bool) | +| HierarchyConversions() -> void | 0 | 97 | 0 | @r0_97(glval:Derived) | +| HierarchyConversions() -> void | 0 | 98 | 0 | @r0_98(glval:Middle) | +| HierarchyConversions() -> void | 0 | 99 | 0 | @r0_99(glval:Base) | +| HierarchyConversions() -> void | 0 | 101 | 0 | @r0_101(Base) | +| HierarchyConversions() -> void | 0 | 102 | 0 | @r0_102(Base &) | +| HierarchyConversions() -> void | 0 | 103 | 0 | @r0_103(glval:Base) | +| HierarchyConversions() -> void | 0 | 104 | 0 | @r0_104(bool) | +| HierarchyConversions() -> void | 0 | 105 | 0 | @r0_105(bool) | +| HierarchyConversions() -> void | 0 | 106 | 0 | @r0_106(glval:Derived) | +| HierarchyConversions() -> void | 0 | 107 | 0 | @r0_107(glval:Middle) | +| HierarchyConversions() -> void | 0 | 108 | 0 | @r0_108(glval:Base) | +| HierarchyConversions() -> void | 0 | 110 | 0 | @r0_110(Base) | +| HierarchyConversions() -> void | 0 | 111 | 0 | @r0_111(Base &) | +| HierarchyConversions() -> void | 0 | 112 | 0 | @r0_112(glval:Derived *) | +| HierarchyConversions() -> void | 0 | 113 | 0 | @r0_113(Derived *) | +| HierarchyConversions() -> void | 0 | 114 | 0 | @r0_114(Middle *) | +| HierarchyConversions() -> void | 0 | 115 | 0 | @r0_115(Base *) | +| HierarchyConversions() -> void | 0 | 116 | 0 | @r0_116(glval:Base *) | +| HierarchyConversions() -> void | 0 | 117 | 0 | @m0_117(Base *) | +| HierarchyConversions() -> void | 0 | 118 | 0 | @r0_118(glval:Derived *) | +| HierarchyConversions() -> void | 0 | 119 | 0 | @r0_119(Derived *) | +| HierarchyConversions() -> void | 0 | 120 | 0 | @r0_120(Middle *) | +| HierarchyConversions() -> void | 0 | 121 | 0 | @r0_121(Base *) | +| HierarchyConversions() -> void | 0 | 122 | 0 | @r0_122(glval:Base *) | +| HierarchyConversions() -> void | 0 | 123 | 0 | @m0_123(Base *) | +| HierarchyConversions() -> void | 0 | 124 | 0 | @r0_124(glval:Derived *) | +| HierarchyConversions() -> void | 0 | 125 | 0 | @r0_125(Derived *) | +| HierarchyConversions() -> void | 0 | 126 | 0 | @r0_126(Middle *) | +| HierarchyConversions() -> void | 0 | 127 | 0 | @r0_127(Base *) | +| HierarchyConversions() -> void | 0 | 128 | 0 | @r0_128(glval:Base *) | +| HierarchyConversions() -> void | 0 | 129 | 0 | @m0_129(Base *) | +| HierarchyConversions() -> void | 0 | 130 | 0 | @r0_130(glval:Derived *) | +| HierarchyConversions() -> void | 0 | 131 | 0 | @r0_131(Derived *) | +| HierarchyConversions() -> void | 0 | 132 | 0 | @r0_132(Base *) | +| HierarchyConversions() -> void | 0 | 133 | 0 | @r0_133(glval:Base *) | +| HierarchyConversions() -> void | 0 | 134 | 0 | @m0_134(Base *) | +| HierarchyConversions() -> void | 0 | 135 | 0 | @r0_135(glval:Derived) | +| HierarchyConversions() -> void | 0 | 136 | 0 | @r0_136(bool) | +| HierarchyConversions() -> void | 0 | 137 | 0 | @r0_137(glval:Base) | +| HierarchyConversions() -> void | 0 | 138 | 0 | @r0_138(glval:Middle) | +| HierarchyConversions() -> void | 0 | 139 | 0 | @r0_139(glval:Derived) | +| HierarchyConversions() -> void | 0 | 140 | 0 | @r0_140(glval:Derived) | +| HierarchyConversions() -> void | 0 | 141 | 0 | @r0_141(Derived &) | +| HierarchyConversions() -> void | 0 | 142 | 0 | @r0_142(glval:Derived) | +| HierarchyConversions() -> void | 0 | 143 | 0 | @r0_143(bool) | +| HierarchyConversions() -> void | 0 | 144 | 0 | @r0_144(glval:Base) | +| HierarchyConversions() -> void | 0 | 145 | 0 | @r0_145(glval:Middle) | +| HierarchyConversions() -> void | 0 | 146 | 0 | @r0_146(glval:Derived) | +| HierarchyConversions() -> void | 0 | 147 | 0 | @r0_147(glval:Derived) | +| HierarchyConversions() -> void | 0 | 148 | 0 | @r0_148(Derived &) | +| HierarchyConversions() -> void | 0 | 149 | 0 | @r0_149(glval:Base *) | +| HierarchyConversions() -> void | 0 | 150 | 0 | @r0_150(Base *) | +| HierarchyConversions() -> void | 0 | 151 | 0 | @r0_151(Middle *) | +| HierarchyConversions() -> void | 0 | 152 | 0 | @r0_152(Derived *) | +| HierarchyConversions() -> void | 0 | 153 | 0 | @r0_153(glval:Derived *) | +| HierarchyConversions() -> void | 0 | 154 | 0 | @m0_154(Derived *) | +| HierarchyConversions() -> void | 0 | 155 | 0 | @r0_155(glval:Base *) | +| HierarchyConversions() -> void | 0 | 156 | 0 | @r0_156(Base *) | +| HierarchyConversions() -> void | 0 | 157 | 0 | @r0_157(Middle *) | +| HierarchyConversions() -> void | 0 | 158 | 0 | @r0_158(Derived *) | +| HierarchyConversions() -> void | 0 | 159 | 0 | @r0_159(glval:Derived *) | +| HierarchyConversions() -> void | 0 | 160 | 0 | @m0_160(Derived *) | +| HierarchyConversions() -> void | 0 | 161 | 0 | @r0_161(glval:Base *) | +| HierarchyConversions() -> void | 0 | 162 | 0 | @r0_162(Base *) | +| HierarchyConversions() -> void | 0 | 163 | 0 | @r0_163(Derived *) | +| HierarchyConversions() -> void | 0 | 164 | 0 | @r0_164(glval:Derived *) | +| HierarchyConversions() -> void | 0 | 165 | 0 | @m0_165(Derived *) | +| HierarchyConversions() -> void | 0 | 166 | 0 | @r0_166(glval:MiddleVB1 *) | +| HierarchyConversions() -> void | 0 | 167 | 0 | @r0_167(MiddleVB1 *) | +| HierarchyConversions() -> void | 0 | 168 | 0 | @m0_168(MiddleVB1 *) | +| HierarchyConversions() -> void | 0 | 169 | 0 | @r0_169(glval:DerivedVB *) | +| HierarchyConversions() -> void | 0 | 170 | 0 | @r0_170(DerivedVB *) | +| HierarchyConversions() -> void | 0 | 171 | 0 | @m0_171(DerivedVB *) | +| HierarchyConversions() -> void | 0 | 172 | 0 | @r0_172(glval:MiddleVB1 *) | +| HierarchyConversions() -> void | 0 | 173 | 0 | @r0_173(MiddleVB1 *) | +| HierarchyConversions() -> void | 0 | 174 | 0 | @r0_174(Base *) | +| HierarchyConversions() -> void | 0 | 175 | 0 | @r0_175(glval:Base *) | +| HierarchyConversions() -> void | 0 | 176 | 0 | @m0_176(Base *) | +| HierarchyConversions() -> void | 0 | 177 | 0 | @r0_177(glval:DerivedVB *) | +| HierarchyConversions() -> void | 0 | 178 | 0 | @r0_178(DerivedVB *) | +| HierarchyConversions() -> void | 0 | 179 | 0 | @r0_179(Base *) | +| HierarchyConversions() -> void | 0 | 180 | 0 | @r0_180(glval:Base *) | +| HierarchyConversions() -> void | 0 | 181 | 0 | @m0_181(Base *) | +| IfStatements(bool, int, int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| IfStatements(bool, int, int) -> void | 0 | 2 | 0 | @r0_2(bool) | +| IfStatements(bool, int, int) -> void | 0 | 3 | 0 | @r0_3(glval:bool) | +| IfStatements(bool, int, int) -> void | 0 | 4 | 0 | @m0_4(bool) | +| IfStatements(bool, int, int) -> void | 0 | 5 | 0 | @r0_5(int) | +| IfStatements(bool, int, int) -> void | 0 | 6 | 0 | @r0_6(glval:int) | +| IfStatements(bool, int, int) -> void | 0 | 7 | 0 | @m0_7(int) | +| IfStatements(bool, int, int) -> void | 0 | 8 | 0 | @r0_8(int) | +| IfStatements(bool, int, int) -> void | 0 | 9 | 0 | @r0_9(glval:int) | +| IfStatements(bool, int, int) -> void | 0 | 10 | 0 | @m0_10(int) | +| IfStatements(bool, int, int) -> void | 0 | 11 | 0 | @r0_11(glval:bool) | +| IfStatements(bool, int, int) -> void | 0 | 12 | 0 | @r0_12(bool) | +| IfStatements(bool, int, int) -> void | 1 | 0 | 0 | @r1_0(glval:bool) | +| IfStatements(bool, int, int) -> void | 1 | 1 | 0 | @r1_1(bool) | +| IfStatements(bool, int, int) -> void | 2 | 0 | 0 | @r2_0(glval:int) | +| IfStatements(bool, int, int) -> void | 2 | 1 | 0 | @r2_1(int) | +| IfStatements(bool, int, int) -> void | 2 | 2 | 0 | @r2_2(glval:int) | +| IfStatements(bool, int, int) -> void | 2 | 3 | 0 | @m2_3(int) | +| IfStatements(bool, int, int) -> void | 3 | 0 | 0 | @m3_0(int) | +| IfStatements(bool, int, int) -> void | 3 | 1 | 0 | @r3_1(glval:int) | +| IfStatements(bool, int, int) -> void | 3 | 2 | 0 | @r3_2(int) | +| IfStatements(bool, int, int) -> void | 3 | 3 | 0 | @r3_3(int) | +| IfStatements(bool, int, int) -> void | 3 | 4 | 0 | @r3_4(bool) | +| IfStatements(bool, int, int) -> void | 4 | 0 | 0 | @r4_0(int) | +| IfStatements(bool, int, int) -> void | 4 | 1 | 0 | @r4_1(glval:int) | +| IfStatements(bool, int, int) -> void | 4 | 2 | 0 | @m4_2(int) | +| IfStatements(bool, int, int) -> void | 5 | 0 | 0 | @r5_0(int) | +| IfStatements(bool, int, int) -> void | 5 | 1 | 0 | @r5_1(glval:int) | +| IfStatements(bool, int, int) -> void | 5 | 2 | 0 | @m5_2(int) | +| InitArray() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| InitArray() -> void | 0 | 2 | 0 | @r0_2(glval:char[32]) | +| InitArray() -> void | 0 | 3 | 0 | @r0_3(glval:char[1]) | +| InitArray() -> void | 0 | 4 | 0 | @r0_4(char[1]) | +| InitArray() -> void | 0 | 5 | 0 | @mu0_5(char[1]) | +| InitArray() -> void | 0 | 6 | 0 | @r0_6(unknown[31]) | +| InitArray() -> void | 0 | 7 | 0 | @r0_7(int) | +| InitArray() -> void | 0 | 8 | 0 | @r0_8(glval:char) | +| InitArray() -> void | 0 | 9 | 0 | @mu0_9(unknown[31]) | +| InitArray() -> void | 0 | 10 | 0 | @r0_10(glval:char[4]) | +| InitArray() -> void | 0 | 11 | 0 | @r0_11(glval:char[4]) | +| InitArray() -> void | 0 | 12 | 0 | @r0_12(char[4]) | +| InitArray() -> void | 0 | 13 | 0 | @m0_13(char[4]) | +| InitArray() -> void | 0 | 14 | 0 | @r0_14(glval:char[]) | +| InitArray() -> void | 0 | 15 | 0 | @r0_15(glval:char[5]) | +| InitArray() -> void | 0 | 16 | 0 | @r0_16(char[5]) | +| InitArray() -> void | 0 | 17 | 0 | @m0_17(char[5]) | +| InitArray() -> void | 0 | 18 | 0 | @r0_18(glval:char[2]) | +| InitArray() -> void | 0 | 19 | 0 | @r0_19(char[2]) | +| InitArray() -> void | 0 | 20 | 0 | @m0_20(char[2]) | +| InitArray() -> void | 0 | 21 | 0 | @r0_21(glval:char[2]) | +| InitArray() -> void | 0 | 22 | 0 | @r0_22(int) | +| InitArray() -> void | 0 | 23 | 0 | @r0_23(glval:char) | +| InitArray() -> void | 0 | 24 | 0 | @r0_24(unknown[2]) | +| InitArray() -> void | 0 | 25 | 0 | @mu0_25(unknown[2]) | +| InitArray() -> void | 0 | 26 | 0 | @r0_26(glval:char[2]) | +| InitArray() -> void | 0 | 27 | 0 | @r0_27(int) | +| InitArray() -> void | 0 | 28 | 0 | @r0_28(glval:char) | +| InitArray() -> void | 0 | 29 | 0 | @r0_29(char) | +| InitArray() -> void | 0 | 30 | 0 | @mu0_30(char) | +| InitArray() -> void | 0 | 31 | 0 | @r0_31(int) | +| InitArray() -> void | 0 | 32 | 0 | @r0_32(glval:char) | +| InitArray() -> void | 0 | 33 | 0 | @r0_33(char) | +| InitArray() -> void | 0 | 34 | 0 | @mu0_34(char) | +| InitArray() -> void | 0 | 35 | 0 | @r0_35(glval:char[2]) | +| InitArray() -> void | 0 | 36 | 0 | @r0_36(int) | +| InitArray() -> void | 0 | 37 | 0 | @r0_37(glval:char) | +| InitArray() -> void | 0 | 38 | 0 | @r0_38(char) | +| InitArray() -> void | 0 | 39 | 0 | @mu0_39(char) | +| InitArray() -> void | 0 | 40 | 0 | @r0_40(int) | +| InitArray() -> void | 0 | 41 | 0 | @r0_41(glval:char) | +| InitArray() -> void | 0 | 42 | 0 | @r0_42(char) | +| InitArray() -> void | 0 | 43 | 0 | @mu0_43(char) | +| InitArray() -> void | 0 | 44 | 0 | @r0_44(glval:char[3]) | +| InitArray() -> void | 0 | 45 | 0 | @r0_45(int) | +| InitArray() -> void | 0 | 46 | 0 | @r0_46(glval:char) | +| InitArray() -> void | 0 | 47 | 0 | @r0_47(char) | +| InitArray() -> void | 0 | 48 | 0 | @mu0_48(char) | +| InitArray() -> void | 0 | 49 | 0 | @r0_49(int) | +| InitArray() -> void | 0 | 50 | 0 | @r0_50(glval:char) | +| InitArray() -> void | 0 | 51 | 0 | @r0_51(unknown[2]) | +| InitArray() -> void | 0 | 52 | 0 | @mu0_52(unknown[2]) | +| InitList(int, float) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| InitList(int, float) -> void | 0 | 2 | 0 | @r0_2(int) | +| InitList(int, float) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| InitList(int, float) -> void | 0 | 4 | 0 | @m0_4(int) | +| InitList(int, float) -> void | 0 | 5 | 0 | @r0_5(float) | +| InitList(int, float) -> void | 0 | 6 | 0 | @r0_6(glval:float) | +| InitList(int, float) -> void | 0 | 7 | 0 | @m0_7(float) | +| InitList(int, float) -> void | 0 | 8 | 0 | @r0_8(glval:Point) | +| InitList(int, float) -> void | 0 | 9 | 0 | @r0_9(glval:int) | +| InitList(int, float) -> void | 0 | 10 | 0 | @r0_10(glval:int) | +| InitList(int, float) -> void | 0 | 11 | 0 | @r0_11(int) | +| InitList(int, float) -> void | 0 | 12 | 0 | @m0_12(int) | +| InitList(int, float) -> void | 0 | 13 | 0 | @r0_13(glval:int) | +| InitList(int, float) -> void | 0 | 14 | 0 | @r0_14(glval:float) | +| InitList(int, float) -> void | 0 | 15 | 0 | @r0_15(float) | +| InitList(int, float) -> void | 0 | 16 | 0 | @r0_16(int) | +| InitList(int, float) -> void | 0 | 17 | 0 | @mu0_17(int) | +| InitList(int, float) -> void | 0 | 18 | 0 | @r0_18(glval:Point) | +| InitList(int, float) -> void | 0 | 19 | 0 | @r0_19(glval:int) | +| InitList(int, float) -> void | 0 | 20 | 0 | @r0_20(glval:int) | +| InitList(int, float) -> void | 0 | 21 | 0 | @r0_21(int) | +| InitList(int, float) -> void | 0 | 22 | 0 | @m0_22(int) | +| InitList(int, float) -> void | 0 | 23 | 0 | @r0_23(glval:int) | +| InitList(int, float) -> void | 0 | 24 | 0 | @r0_24(int) | +| InitList(int, float) -> void | 0 | 25 | 0 | @mu0_25(int) | +| InitList(int, float) -> void | 0 | 26 | 0 | @r0_26(glval:Point) | +| InitList(int, float) -> void | 0 | 27 | 0 | @r0_27(glval:int) | +| InitList(int, float) -> void | 0 | 28 | 0 | @r0_28(int) | +| InitList(int, float) -> void | 0 | 29 | 0 | @m0_29(int) | +| InitList(int, float) -> void | 0 | 30 | 0 | @r0_30(glval:int) | +| InitList(int, float) -> void | 0 | 31 | 0 | @r0_31(int) | +| InitList(int, float) -> void | 0 | 32 | 0 | @mu0_32(int) | +| InitList(int, float) -> void | 0 | 33 | 0 | @r0_33(glval:int) | +| InitList(int, float) -> void | 0 | 34 | 0 | @r0_34(int) | +| InitList(int, float) -> void | 0 | 35 | 0 | @m0_35(int) | +| InitList(int, float) -> void | 0 | 36 | 0 | @r0_36(glval:int) | +| InitList(int, float) -> void | 0 | 37 | 0 | @r0_37(int) | +| InitList(int, float) -> void | 0 | 38 | 0 | @m0_38(int) | +| InitReference(int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| InitReference(int) -> void | 0 | 2 | 0 | @r0_2(int) | +| InitReference(int) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| InitReference(int) -> void | 0 | 4 | 0 | @m0_4(int) | +| InitReference(int) -> void | 0 | 5 | 0 | @r0_5(glval:int &) | +| InitReference(int) -> void | 0 | 6 | 0 | @r0_6(glval:int) | +| InitReference(int) -> void | 0 | 7 | 0 | @m0_7(int &) | +| InitReference(int) -> void | 0 | 8 | 0 | @r0_8(glval:int &) | +| InitReference(int) -> void | 0 | 9 | 0 | @r0_9(glval:int &) | +| InitReference(int) -> void | 0 | 10 | 0 | @r0_10(int &) | +| InitReference(int) -> void | 0 | 11 | 0 | @m0_11(int &) | +| InitReference(int) -> void | 0 | 12 | 0 | @r0_12(glval:String &) | +| InitReference(int) -> void | 0 | 13 | 0 | @r0_13(bool) | +| InitReference(int) -> void | 0 | 14 | 0 | @r0_14(String &) | +| InitReference(int) -> void | 0 | 15 | 0 | @r0_15(glval:String) | +| InitReference(int) -> void | 0 | 16 | 0 | @m0_16(String &) | +| IntegerCompare(int, int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| IntegerCompare(int, int) -> void | 0 | 2 | 0 | @r0_2(int) | +| IntegerCompare(int, int) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| IntegerCompare(int, int) -> void | 0 | 4 | 0 | @m0_4(int) | +| IntegerCompare(int, int) -> void | 0 | 5 | 0 | @r0_5(int) | +| IntegerCompare(int, int) -> void | 0 | 6 | 0 | @r0_6(glval:int) | +| IntegerCompare(int, int) -> void | 0 | 7 | 0 | @m0_7(int) | +| IntegerCompare(int, int) -> void | 0 | 8 | 0 | @r0_8(glval:bool) | +| IntegerCompare(int, int) -> void | 0 | 9 | 0 | @r0_9(bool) | +| IntegerCompare(int, int) -> void | 0 | 10 | 0 | @m0_10(bool) | +| IntegerCompare(int, int) -> void | 0 | 11 | 0 | @r0_11(glval:int) | +| IntegerCompare(int, int) -> void | 0 | 12 | 0 | @r0_12(int) | +| IntegerCompare(int, int) -> void | 0 | 13 | 0 | @r0_13(glval:int) | +| IntegerCompare(int, int) -> void | 0 | 14 | 0 | @r0_14(int) | +| IntegerCompare(int, int) -> void | 0 | 15 | 0 | @r0_15(bool) | +| IntegerCompare(int, int) -> void | 0 | 16 | 0 | @r0_16(glval:bool) | +| IntegerCompare(int, int) -> void | 0 | 17 | 0 | @m0_17(bool) | +| IntegerCompare(int, int) -> void | 0 | 18 | 0 | @r0_18(glval:int) | +| IntegerCompare(int, int) -> void | 0 | 19 | 0 | @r0_19(int) | +| IntegerCompare(int, int) -> void | 0 | 20 | 0 | @r0_20(glval:int) | +| IntegerCompare(int, int) -> void | 0 | 21 | 0 | @r0_21(int) | +| IntegerCompare(int, int) -> void | 0 | 22 | 0 | @r0_22(bool) | +| IntegerCompare(int, int) -> void | 0 | 23 | 0 | @r0_23(glval:bool) | +| IntegerCompare(int, int) -> void | 0 | 24 | 0 | @m0_24(bool) | +| IntegerCompare(int, int) -> void | 0 | 25 | 0 | @r0_25(glval:int) | +| IntegerCompare(int, int) -> void | 0 | 26 | 0 | @r0_26(int) | +| IntegerCompare(int, int) -> void | 0 | 27 | 0 | @r0_27(glval:int) | +| IntegerCompare(int, int) -> void | 0 | 28 | 0 | @r0_28(int) | +| IntegerCompare(int, int) -> void | 0 | 29 | 0 | @r0_29(bool) | +| IntegerCompare(int, int) -> void | 0 | 30 | 0 | @r0_30(glval:bool) | +| IntegerCompare(int, int) -> void | 0 | 31 | 0 | @m0_31(bool) | +| IntegerCompare(int, int) -> void | 0 | 32 | 0 | @r0_32(glval:int) | +| IntegerCompare(int, int) -> void | 0 | 33 | 0 | @r0_33(int) | +| IntegerCompare(int, int) -> void | 0 | 34 | 0 | @r0_34(glval:int) | +| IntegerCompare(int, int) -> void | 0 | 35 | 0 | @r0_35(int) | +| IntegerCompare(int, int) -> void | 0 | 36 | 0 | @r0_36(bool) | +| IntegerCompare(int, int) -> void | 0 | 37 | 0 | @r0_37(glval:bool) | +| IntegerCompare(int, int) -> void | 0 | 38 | 0 | @m0_38(bool) | +| IntegerCompare(int, int) -> void | 0 | 39 | 0 | @r0_39(glval:int) | +| IntegerCompare(int, int) -> void | 0 | 40 | 0 | @r0_40(int) | +| IntegerCompare(int, int) -> void | 0 | 41 | 0 | @r0_41(glval:int) | +| IntegerCompare(int, int) -> void | 0 | 42 | 0 | @r0_42(int) | +| IntegerCompare(int, int) -> void | 0 | 43 | 0 | @r0_43(bool) | +| IntegerCompare(int, int) -> void | 0 | 44 | 0 | @r0_44(glval:bool) | +| IntegerCompare(int, int) -> void | 0 | 45 | 0 | @m0_45(bool) | +| IntegerCompare(int, int) -> void | 0 | 46 | 0 | @r0_46(glval:int) | +| IntegerCompare(int, int) -> void | 0 | 47 | 0 | @r0_47(int) | +| IntegerCompare(int, int) -> void | 0 | 48 | 0 | @r0_48(glval:int) | +| IntegerCompare(int, int) -> void | 0 | 49 | 0 | @r0_49(int) | +| IntegerCompare(int, int) -> void | 0 | 50 | 0 | @r0_50(bool) | +| IntegerCompare(int, int) -> void | 0 | 51 | 0 | @r0_51(glval:bool) | +| IntegerCompare(int, int) -> void | 0 | 52 | 0 | @m0_52(bool) | +| IntegerCrement(int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| IntegerCrement(int) -> void | 0 | 2 | 0 | @r0_2(int) | +| IntegerCrement(int) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| IntegerCrement(int) -> void | 0 | 4 | 0 | @m0_4(int) | +| IntegerCrement(int) -> void | 0 | 5 | 0 | @r0_5(glval:int) | +| IntegerCrement(int) -> void | 0 | 6 | 0 | @r0_6(int) | +| IntegerCrement(int) -> void | 0 | 7 | 0 | @m0_7(int) | +| IntegerCrement(int) -> void | 0 | 8 | 0 | @r0_8(glval:int) | +| IntegerCrement(int) -> void | 0 | 9 | 0 | @r0_9(int) | +| IntegerCrement(int) -> void | 0 | 10 | 0 | @r0_10(int) | +| IntegerCrement(int) -> void | 0 | 11 | 0 | @r0_11(int) | +| IntegerCrement(int) -> void | 0 | 12 | 0 | @m0_12(int) | +| IntegerCrement(int) -> void | 0 | 13 | 0 | @r0_13(glval:int) | +| IntegerCrement(int) -> void | 0 | 14 | 0 | @m0_14(int) | +| IntegerCrement(int) -> void | 0 | 15 | 0 | @r0_15(glval:int) | +| IntegerCrement(int) -> void | 0 | 16 | 0 | @r0_16(int) | +| IntegerCrement(int) -> void | 0 | 17 | 0 | @r0_17(int) | +| IntegerCrement(int) -> void | 0 | 18 | 0 | @r0_18(int) | +| IntegerCrement(int) -> void | 0 | 19 | 0 | @m0_19(int) | +| IntegerCrement(int) -> void | 0 | 20 | 0 | @r0_20(glval:int) | +| IntegerCrement(int) -> void | 0 | 21 | 0 | @m0_21(int) | +| IntegerCrement(int) -> void | 0 | 22 | 0 | @r0_22(glval:int) | +| IntegerCrement(int) -> void | 0 | 23 | 0 | @r0_23(int) | +| IntegerCrement(int) -> void | 0 | 24 | 0 | @r0_24(int) | +| IntegerCrement(int) -> void | 0 | 25 | 0 | @r0_25(int) | +| IntegerCrement(int) -> void | 0 | 26 | 0 | @m0_26(int) | +| IntegerCrement(int) -> void | 0 | 27 | 0 | @r0_27(glval:int) | +| IntegerCrement(int) -> void | 0 | 28 | 0 | @m0_28(int) | +| IntegerCrement(int) -> void | 0 | 29 | 0 | @r0_29(glval:int) | +| IntegerCrement(int) -> void | 0 | 30 | 0 | @r0_30(int) | +| IntegerCrement(int) -> void | 0 | 31 | 0 | @r0_31(int) | +| IntegerCrement(int) -> void | 0 | 32 | 0 | @r0_32(int) | +| IntegerCrement(int) -> void | 0 | 33 | 0 | @m0_33(int) | +| IntegerCrement(int) -> void | 0 | 34 | 0 | @r0_34(glval:int) | +| IntegerCrement(int) -> void | 0 | 35 | 0 | @m0_35(int) | +| IntegerCrement_LValue(int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| IntegerCrement_LValue(int) -> void | 0 | 2 | 0 | @r0_2(int) | +| IntegerCrement_LValue(int) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| IntegerCrement_LValue(int) -> void | 0 | 4 | 0 | @m0_4(int) | +| IntegerCrement_LValue(int) -> void | 0 | 5 | 0 | @r0_5(glval:int *) | +| IntegerCrement_LValue(int) -> void | 0 | 6 | 0 | @r0_6(int *) | +| IntegerCrement_LValue(int) -> void | 0 | 7 | 0 | @m0_7(int *) | +| IntegerCrement_LValue(int) -> void | 0 | 8 | 0 | @r0_8(glval:int) | +| IntegerCrement_LValue(int) -> void | 0 | 9 | 0 | @r0_9(int) | +| IntegerCrement_LValue(int) -> void | 0 | 10 | 0 | @r0_10(int) | +| IntegerCrement_LValue(int) -> void | 0 | 11 | 0 | @r0_11(int) | +| IntegerCrement_LValue(int) -> void | 0 | 12 | 0 | @m0_12(int) | +| IntegerCrement_LValue(int) -> void | 0 | 13 | 0 | @r0_13(glval:int *) | +| IntegerCrement_LValue(int) -> void | 0 | 14 | 0 | @m0_14(int *) | +| IntegerCrement_LValue(int) -> void | 0 | 15 | 0 | @r0_15(glval:int) | +| IntegerCrement_LValue(int) -> void | 0 | 16 | 0 | @r0_16(int) | +| IntegerCrement_LValue(int) -> void | 0 | 17 | 0 | @r0_17(int) | +| IntegerCrement_LValue(int) -> void | 0 | 18 | 0 | @r0_18(int) | +| IntegerCrement_LValue(int) -> void | 0 | 19 | 0 | @m0_19(int) | +| IntegerCrement_LValue(int) -> void | 0 | 20 | 0 | @r0_20(glval:int *) | +| IntegerCrement_LValue(int) -> void | 0 | 21 | 0 | @m0_21(int *) | +| IntegerOps(int, int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| IntegerOps(int, int) -> void | 0 | 2 | 0 | @r0_2(int) | +| IntegerOps(int, int) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| IntegerOps(int, int) -> void | 0 | 4 | 0 | @m0_4(int) | +| IntegerOps(int, int) -> void | 0 | 5 | 0 | @r0_5(int) | +| IntegerOps(int, int) -> void | 0 | 6 | 0 | @r0_6(glval:int) | +| IntegerOps(int, int) -> void | 0 | 7 | 0 | @m0_7(int) | +| IntegerOps(int, int) -> void | 0 | 8 | 0 | @r0_8(glval:int) | +| IntegerOps(int, int) -> void | 0 | 9 | 0 | @r0_9(int) | +| IntegerOps(int, int) -> void | 0 | 10 | 0 | @m0_10(int) | +| IntegerOps(int, int) -> void | 0 | 11 | 0 | @r0_11(glval:int) | +| IntegerOps(int, int) -> void | 0 | 12 | 0 | @r0_12(int) | +| IntegerOps(int, int) -> void | 0 | 13 | 0 | @r0_13(glval:int) | +| IntegerOps(int, int) -> void | 0 | 14 | 0 | @r0_14(int) | +| IntegerOps(int, int) -> void | 0 | 15 | 0 | @r0_15(int) | +| IntegerOps(int, int) -> void | 0 | 16 | 0 | @r0_16(glval:int) | +| IntegerOps(int, int) -> void | 0 | 17 | 0 | @m0_17(int) | +| IntegerOps(int, int) -> void | 0 | 18 | 0 | @r0_18(glval:int) | +| IntegerOps(int, int) -> void | 0 | 19 | 0 | @r0_19(int) | +| IntegerOps(int, int) -> void | 0 | 20 | 0 | @r0_20(glval:int) | +| IntegerOps(int, int) -> void | 0 | 21 | 0 | @r0_21(int) | +| IntegerOps(int, int) -> void | 0 | 22 | 0 | @r0_22(int) | +| IntegerOps(int, int) -> void | 0 | 23 | 0 | @r0_23(glval:int) | +| IntegerOps(int, int) -> void | 0 | 24 | 0 | @m0_24(int) | +| IntegerOps(int, int) -> void | 0 | 25 | 0 | @r0_25(glval:int) | +| IntegerOps(int, int) -> void | 0 | 26 | 0 | @r0_26(int) | +| IntegerOps(int, int) -> void | 0 | 27 | 0 | @r0_27(glval:int) | +| IntegerOps(int, int) -> void | 0 | 28 | 0 | @r0_28(int) | +| IntegerOps(int, int) -> void | 0 | 29 | 0 | @r0_29(int) | +| IntegerOps(int, int) -> void | 0 | 30 | 0 | @r0_30(glval:int) | +| IntegerOps(int, int) -> void | 0 | 31 | 0 | @m0_31(int) | +| IntegerOps(int, int) -> void | 0 | 32 | 0 | @r0_32(glval:int) | +| IntegerOps(int, int) -> void | 0 | 33 | 0 | @r0_33(int) | +| IntegerOps(int, int) -> void | 0 | 34 | 0 | @r0_34(glval:int) | +| IntegerOps(int, int) -> void | 0 | 35 | 0 | @r0_35(int) | +| IntegerOps(int, int) -> void | 0 | 36 | 0 | @r0_36(int) | +| IntegerOps(int, int) -> void | 0 | 37 | 0 | @r0_37(glval:int) | +| IntegerOps(int, int) -> void | 0 | 38 | 0 | @m0_38(int) | +| IntegerOps(int, int) -> void | 0 | 39 | 0 | @r0_39(glval:int) | +| IntegerOps(int, int) -> void | 0 | 40 | 0 | @r0_40(int) | +| IntegerOps(int, int) -> void | 0 | 41 | 0 | @r0_41(glval:int) | +| IntegerOps(int, int) -> void | 0 | 42 | 0 | @r0_42(int) | +| IntegerOps(int, int) -> void | 0 | 43 | 0 | @r0_43(int) | +| IntegerOps(int, int) -> void | 0 | 44 | 0 | @r0_44(glval:int) | +| IntegerOps(int, int) -> void | 0 | 45 | 0 | @m0_45(int) | +| IntegerOps(int, int) -> void | 0 | 46 | 0 | @r0_46(glval:int) | +| IntegerOps(int, int) -> void | 0 | 47 | 0 | @r0_47(int) | +| IntegerOps(int, int) -> void | 0 | 48 | 0 | @r0_48(glval:int) | +| IntegerOps(int, int) -> void | 0 | 49 | 0 | @r0_49(int) | +| IntegerOps(int, int) -> void | 0 | 50 | 0 | @r0_50(int) | +| IntegerOps(int, int) -> void | 0 | 51 | 0 | @r0_51(glval:int) | +| IntegerOps(int, int) -> void | 0 | 52 | 0 | @m0_52(int) | +| IntegerOps(int, int) -> void | 0 | 53 | 0 | @r0_53(glval:int) | +| IntegerOps(int, int) -> void | 0 | 54 | 0 | @r0_54(int) | +| IntegerOps(int, int) -> void | 0 | 55 | 0 | @r0_55(glval:int) | +| IntegerOps(int, int) -> void | 0 | 56 | 0 | @r0_56(int) | +| IntegerOps(int, int) -> void | 0 | 57 | 0 | @r0_57(int) | +| IntegerOps(int, int) -> void | 0 | 58 | 0 | @r0_58(glval:int) | +| IntegerOps(int, int) -> void | 0 | 59 | 0 | @m0_59(int) | +| IntegerOps(int, int) -> void | 0 | 60 | 0 | @r0_60(glval:int) | +| IntegerOps(int, int) -> void | 0 | 61 | 0 | @r0_61(int) | +| IntegerOps(int, int) -> void | 0 | 62 | 0 | @r0_62(glval:int) | +| IntegerOps(int, int) -> void | 0 | 63 | 0 | @r0_63(int) | +| IntegerOps(int, int) -> void | 0 | 64 | 0 | @r0_64(int) | +| IntegerOps(int, int) -> void | 0 | 65 | 0 | @r0_65(glval:int) | +| IntegerOps(int, int) -> void | 0 | 66 | 0 | @m0_66(int) | +| IntegerOps(int, int) -> void | 0 | 67 | 0 | @r0_67(glval:int) | +| IntegerOps(int, int) -> void | 0 | 68 | 0 | @r0_68(int) | +| IntegerOps(int, int) -> void | 0 | 69 | 0 | @r0_69(glval:int) | +| IntegerOps(int, int) -> void | 0 | 70 | 0 | @r0_70(int) | +| IntegerOps(int, int) -> void | 0 | 71 | 0 | @r0_71(int) | +| IntegerOps(int, int) -> void | 0 | 72 | 0 | @r0_72(glval:int) | +| IntegerOps(int, int) -> void | 0 | 73 | 0 | @m0_73(int) | +| IntegerOps(int, int) -> void | 0 | 74 | 0 | @r0_74(glval:int) | +| IntegerOps(int, int) -> void | 0 | 75 | 0 | @r0_75(int) | +| IntegerOps(int, int) -> void | 0 | 76 | 0 | @r0_76(glval:int) | +| IntegerOps(int, int) -> void | 0 | 77 | 0 | @r0_77(int) | +| IntegerOps(int, int) -> void | 0 | 78 | 0 | @r0_78(int) | +| IntegerOps(int, int) -> void | 0 | 79 | 0 | @r0_79(glval:int) | +| IntegerOps(int, int) -> void | 0 | 80 | 0 | @m0_80(int) | +| IntegerOps(int, int) -> void | 0 | 81 | 0 | @r0_81(glval:int) | +| IntegerOps(int, int) -> void | 0 | 82 | 0 | @r0_82(int) | +| IntegerOps(int, int) -> void | 0 | 83 | 0 | @r0_83(glval:int) | +| IntegerOps(int, int) -> void | 0 | 84 | 0 | @m0_84(int) | +| IntegerOps(int, int) -> void | 0 | 85 | 0 | @r0_85(glval:int) | +| IntegerOps(int, int) -> void | 0 | 86 | 0 | @r0_86(int) | +| IntegerOps(int, int) -> void | 0 | 87 | 0 | @r0_87(glval:int) | +| IntegerOps(int, int) -> void | 0 | 88 | 0 | @r0_88(int) | +| IntegerOps(int, int) -> void | 0 | 89 | 0 | @r0_89(int) | +| IntegerOps(int, int) -> void | 0 | 90 | 0 | @m0_90(int) | +| IntegerOps(int, int) -> void | 0 | 91 | 0 | @r0_91(glval:int) | +| IntegerOps(int, int) -> void | 0 | 92 | 0 | @r0_92(int) | +| IntegerOps(int, int) -> void | 0 | 93 | 0 | @r0_93(glval:int) | +| IntegerOps(int, int) -> void | 0 | 94 | 0 | @r0_94(int) | +| IntegerOps(int, int) -> void | 0 | 95 | 0 | @r0_95(int) | +| IntegerOps(int, int) -> void | 0 | 96 | 0 | @m0_96(int) | +| IntegerOps(int, int) -> void | 0 | 97 | 0 | @r0_97(glval:int) | +| IntegerOps(int, int) -> void | 0 | 98 | 0 | @r0_98(int) | +| IntegerOps(int, int) -> void | 0 | 99 | 0 | @r0_99(glval:int) | +| IntegerOps(int, int) -> void | 0 | 100 | 0 | @r0_100(int) | +| IntegerOps(int, int) -> void | 0 | 101 | 0 | @r0_101(int) | +| IntegerOps(int, int) -> void | 0 | 102 | 0 | @m0_102(int) | +| IntegerOps(int, int) -> void | 0 | 103 | 0 | @r0_103(glval:int) | +| IntegerOps(int, int) -> void | 0 | 104 | 0 | @r0_104(int) | +| IntegerOps(int, int) -> void | 0 | 105 | 0 | @r0_105(glval:int) | +| IntegerOps(int, int) -> void | 0 | 106 | 0 | @r0_106(int) | +| IntegerOps(int, int) -> void | 0 | 107 | 0 | @r0_107(int) | +| IntegerOps(int, int) -> void | 0 | 108 | 0 | @m0_108(int) | +| IntegerOps(int, int) -> void | 0 | 109 | 0 | @r0_109(glval:int) | +| IntegerOps(int, int) -> void | 0 | 110 | 0 | @r0_110(int) | +| IntegerOps(int, int) -> void | 0 | 111 | 0 | @r0_111(glval:int) | +| IntegerOps(int, int) -> void | 0 | 112 | 0 | @r0_112(int) | +| IntegerOps(int, int) -> void | 0 | 113 | 0 | @r0_113(int) | +| IntegerOps(int, int) -> void | 0 | 114 | 0 | @m0_114(int) | +| IntegerOps(int, int) -> void | 0 | 115 | 0 | @r0_115(glval:int) | +| IntegerOps(int, int) -> void | 0 | 116 | 0 | @r0_116(int) | +| IntegerOps(int, int) -> void | 0 | 117 | 0 | @r0_117(glval:int) | +| IntegerOps(int, int) -> void | 0 | 118 | 0 | @r0_118(int) | +| IntegerOps(int, int) -> void | 0 | 119 | 0 | @r0_119(int) | +| IntegerOps(int, int) -> void | 0 | 120 | 0 | @m0_120(int) | +| IntegerOps(int, int) -> void | 0 | 121 | 0 | @r0_121(glval:int) | +| IntegerOps(int, int) -> void | 0 | 122 | 0 | @r0_122(int) | +| IntegerOps(int, int) -> void | 0 | 123 | 0 | @r0_123(glval:int) | +| IntegerOps(int, int) -> void | 0 | 124 | 0 | @r0_124(int) | +| IntegerOps(int, int) -> void | 0 | 125 | 0 | @r0_125(int) | +| IntegerOps(int, int) -> void | 0 | 126 | 0 | @m0_126(int) | +| IntegerOps(int, int) -> void | 0 | 127 | 0 | @r0_127(glval:int) | +| IntegerOps(int, int) -> void | 0 | 128 | 0 | @r0_128(int) | +| IntegerOps(int, int) -> void | 0 | 129 | 0 | @r0_129(glval:int) | +| IntegerOps(int, int) -> void | 0 | 130 | 0 | @r0_130(int) | +| IntegerOps(int, int) -> void | 0 | 131 | 0 | @r0_131(int) | +| IntegerOps(int, int) -> void | 0 | 132 | 0 | @m0_132(int) | +| IntegerOps(int, int) -> void | 0 | 133 | 0 | @r0_133(glval:int) | +| IntegerOps(int, int) -> void | 0 | 134 | 0 | @r0_134(int) | +| IntegerOps(int, int) -> void | 0 | 135 | 0 | @r0_135(glval:int) | +| IntegerOps(int, int) -> void | 0 | 136 | 0 | @r0_136(int) | +| IntegerOps(int, int) -> void | 0 | 137 | 0 | @r0_137(int) | +| IntegerOps(int, int) -> void | 0 | 138 | 0 | @m0_138(int) | +| IntegerOps(int, int) -> void | 0 | 139 | 0 | @r0_139(glval:int) | +| IntegerOps(int, int) -> void | 0 | 140 | 0 | @r0_140(int) | +| IntegerOps(int, int) -> void | 0 | 141 | 0 | @r0_141(glval:int) | +| IntegerOps(int, int) -> void | 0 | 142 | 0 | @r0_142(int) | +| IntegerOps(int, int) -> void | 0 | 143 | 0 | @r0_143(int) | +| IntegerOps(int, int) -> void | 0 | 144 | 0 | @m0_144(int) | +| IntegerOps(int, int) -> void | 0 | 145 | 0 | @r0_145(glval:int) | +| IntegerOps(int, int) -> void | 0 | 146 | 0 | @r0_146(int) | +| IntegerOps(int, int) -> void | 0 | 147 | 0 | @r0_147(int) | +| IntegerOps(int, int) -> void | 0 | 148 | 0 | @r0_148(glval:int) | +| IntegerOps(int, int) -> void | 0 | 149 | 0 | @m0_149(int) | +| IntegerOps(int, int) -> void | 0 | 150 | 0 | @r0_150(glval:int) | +| IntegerOps(int, int) -> void | 0 | 151 | 0 | @r0_151(int) | +| IntegerOps(int, int) -> void | 0 | 152 | 0 | @r0_152(int) | +| IntegerOps(int, int) -> void | 0 | 153 | 0 | @r0_153(glval:int) | +| IntegerOps(int, int) -> void | 0 | 154 | 0 | @m0_154(int) | +| IntegerOps(int, int) -> void | 0 | 155 | 0 | @r0_155(glval:int) | +| IntegerOps(int, int) -> void | 0 | 156 | 0 | @r0_156(int) | +| IntegerOps(int, int) -> void | 0 | 157 | 0 | @r0_157(int) | +| IntegerOps(int, int) -> void | 0 | 158 | 0 | @r0_158(glval:int) | +| IntegerOps(int, int) -> void | 0 | 159 | 0 | @m0_159(int) | +| IntegerOps(int, int) -> void | 0 | 160 | 0 | @r0_160(glval:int) | +| IntegerOps(int, int) -> void | 0 | 161 | 0 | @r0_161(int) | +| IntegerOps(int, int) -> void | 0 | 162 | 0 | @r0_162(int) | +| IntegerOps(int, int) -> void | 0 | 163 | 0 | @r0_163(bool) | +| IntegerOps(int, int) -> void | 0 | 164 | 0 | @r0_164(bool) | +| IntegerOps(int, int) -> void | 0 | 165 | 0 | @r0_165(int) | +| IntegerOps(int, int) -> void | 0 | 166 | 0 | @r0_166(glval:int) | +| IntegerOps(int, int) -> void | 0 | 167 | 0 | @m0_167(int) | +| LogicalAnd(bool, bool) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| LogicalAnd(bool, bool) -> void | 0 | 2 | 0 | @r0_2(bool) | +| LogicalAnd(bool, bool) -> void | 0 | 3 | 0 | @r0_3(glval:bool) | +| LogicalAnd(bool, bool) -> void | 0 | 4 | 0 | @m0_4(bool) | +| LogicalAnd(bool, bool) -> void | 0 | 5 | 0 | @r0_5(bool) | +| LogicalAnd(bool, bool) -> void | 0 | 6 | 0 | @r0_6(glval:bool) | +| LogicalAnd(bool, bool) -> void | 0 | 7 | 0 | @m0_7(bool) | +| LogicalAnd(bool, bool) -> void | 0 | 8 | 0 | @r0_8(glval:int) | +| LogicalAnd(bool, bool) -> void | 0 | 9 | 0 | @r0_9(int) | +| LogicalAnd(bool, bool) -> void | 0 | 10 | 0 | @m0_10(int) | +| LogicalAnd(bool, bool) -> void | 0 | 11 | 0 | @r0_11(glval:bool) | +| LogicalAnd(bool, bool) -> void | 0 | 12 | 0 | @r0_12(bool) | +| LogicalAnd(bool, bool) -> void | 1 | 0 | 0 | @r1_0(glval:bool) | +| LogicalAnd(bool, bool) -> void | 1 | 1 | 0 | @r1_1(bool) | +| LogicalAnd(bool, bool) -> void | 2 | 0 | 0 | @r2_0(int) | +| LogicalAnd(bool, bool) -> void | 2 | 1 | 0 | @r2_1(glval:int) | +| LogicalAnd(bool, bool) -> void | 2 | 2 | 0 | @m2_2(int) | +| LogicalAnd(bool, bool) -> void | 3 | 0 | 0 | @r3_0(glval:bool) | +| LogicalAnd(bool, bool) -> void | 3 | 1 | 0 | @r3_1(bool) | +| LogicalAnd(bool, bool) -> void | 4 | 0 | 0 | @r4_0(glval:bool) | +| LogicalAnd(bool, bool) -> void | 4 | 1 | 0 | @r4_1(bool) | +| LogicalAnd(bool, bool) -> void | 5 | 0 | 0 | @r5_0(int) | +| LogicalAnd(bool, bool) -> void | 5 | 1 | 0 | @r5_1(glval:int) | +| LogicalAnd(bool, bool) -> void | 5 | 2 | 0 | @m5_2(int) | +| LogicalAnd(bool, bool) -> void | 6 | 0 | 0 | @r6_0(int) | +| LogicalAnd(bool, bool) -> void | 6 | 1 | 0 | @r6_1(glval:int) | +| LogicalAnd(bool, bool) -> void | 6 | 2 | 0 | @m6_2(int) | +| LogicalNot(bool, bool) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| LogicalNot(bool, bool) -> void | 0 | 2 | 0 | @r0_2(bool) | +| LogicalNot(bool, bool) -> void | 0 | 3 | 0 | @r0_3(glval:bool) | +| LogicalNot(bool, bool) -> void | 0 | 4 | 0 | @m0_4(bool) | +| LogicalNot(bool, bool) -> void | 0 | 5 | 0 | @r0_5(bool) | +| LogicalNot(bool, bool) -> void | 0 | 6 | 0 | @r0_6(glval:bool) | +| LogicalNot(bool, bool) -> void | 0 | 7 | 0 | @m0_7(bool) | +| LogicalNot(bool, bool) -> void | 0 | 8 | 0 | @r0_8(glval:int) | +| LogicalNot(bool, bool) -> void | 0 | 9 | 0 | @r0_9(int) | +| LogicalNot(bool, bool) -> void | 0 | 10 | 0 | @m0_10(int) | +| LogicalNot(bool, bool) -> void | 0 | 11 | 0 | @r0_11(glval:bool) | +| LogicalNot(bool, bool) -> void | 0 | 12 | 0 | @r0_12(bool) | +| LogicalNot(bool, bool) -> void | 1 | 0 | 0 | @r1_0(int) | +| LogicalNot(bool, bool) -> void | 1 | 1 | 0 | @r1_1(glval:int) | +| LogicalNot(bool, bool) -> void | 1 | 2 | 0 | @m1_2(int) | +| LogicalNot(bool, bool) -> void | 2 | 0 | 0 | @r2_0(glval:bool) | +| LogicalNot(bool, bool) -> void | 2 | 1 | 0 | @r2_1(bool) | +| LogicalNot(bool, bool) -> void | 3 | 0 | 0 | @r3_0(glval:bool) | +| LogicalNot(bool, bool) -> void | 3 | 1 | 0 | @r3_1(bool) | +| LogicalNot(bool, bool) -> void | 4 | 0 | 0 | @r4_0(int) | +| LogicalNot(bool, bool) -> void | 4 | 1 | 0 | @r4_1(glval:int) | +| LogicalNot(bool, bool) -> void | 4 | 2 | 0 | @m4_2(int) | +| LogicalNot(bool, bool) -> void | 5 | 0 | 0 | @r5_0(int) | +| LogicalNot(bool, bool) -> void | 5 | 1 | 0 | @r5_1(glval:int) | +| LogicalNot(bool, bool) -> void | 5 | 2 | 0 | @m5_2(int) | +| LogicalOr(bool, bool) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| LogicalOr(bool, bool) -> void | 0 | 2 | 0 | @r0_2(bool) | +| LogicalOr(bool, bool) -> void | 0 | 3 | 0 | @r0_3(glval:bool) | +| LogicalOr(bool, bool) -> void | 0 | 4 | 0 | @m0_4(bool) | +| LogicalOr(bool, bool) -> void | 0 | 5 | 0 | @r0_5(bool) | +| LogicalOr(bool, bool) -> void | 0 | 6 | 0 | @r0_6(glval:bool) | +| LogicalOr(bool, bool) -> void | 0 | 7 | 0 | @m0_7(bool) | +| LogicalOr(bool, bool) -> void | 0 | 8 | 0 | @r0_8(glval:int) | +| LogicalOr(bool, bool) -> void | 0 | 9 | 0 | @r0_9(int) | +| LogicalOr(bool, bool) -> void | 0 | 10 | 0 | @m0_10(int) | +| LogicalOr(bool, bool) -> void | 0 | 11 | 0 | @r0_11(glval:bool) | +| LogicalOr(bool, bool) -> void | 0 | 12 | 0 | @r0_12(bool) | +| LogicalOr(bool, bool) -> void | 1 | 0 | 0 | @r1_0(glval:bool) | +| LogicalOr(bool, bool) -> void | 1 | 1 | 0 | @r1_1(bool) | +| LogicalOr(bool, bool) -> void | 2 | 0 | 0 | @r2_0(int) | +| LogicalOr(bool, bool) -> void | 2 | 1 | 0 | @r2_1(glval:int) | +| LogicalOr(bool, bool) -> void | 2 | 2 | 0 | @m2_2(int) | +| LogicalOr(bool, bool) -> void | 3 | 0 | 0 | @r3_0(glval:bool) | +| LogicalOr(bool, bool) -> void | 3 | 1 | 0 | @r3_1(bool) | +| LogicalOr(bool, bool) -> void | 4 | 0 | 0 | @r4_0(glval:bool) | +| LogicalOr(bool, bool) -> void | 4 | 1 | 0 | @r4_1(bool) | +| LogicalOr(bool, bool) -> void | 5 | 0 | 0 | @r5_0(int) | +| LogicalOr(bool, bool) -> void | 5 | 1 | 0 | @r5_1(glval:int) | +| LogicalOr(bool, bool) -> void | 5 | 2 | 0 | @m5_2(int) | +| LogicalOr(bool, bool) -> void | 6 | 0 | 0 | @r6_0(int) | +| LogicalOr(bool, bool) -> void | 6 | 1 | 0 | @r6_1(glval:int) | +| LogicalOr(bool, bool) -> void | 6 | 2 | 0 | @m6_2(int) | +| Middle::Middle() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Middle::Middle() -> void | 0 | 2 | 0 | @r0_2(glval:Middle) | +| Middle::Middle() -> void | 0 | 3 | 0 | @r0_3(glval:Base) | +| Middle::Middle() -> void | 0 | 4 | 0 | @r0_4(bool) | +| Middle::Middle() -> void | 0 | 6 | 0 | @r0_6(glval:String) | +| Middle::Middle() -> void | 0 | 7 | 0 | @r0_7(bool) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 1 | 0 | @mu0_1(unknown) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 2 | 0 | @r0_2(glval:Middle) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 3 | 0 | @r0_3(Middle &) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 4 | 0 | @r0_4(glval:Middle &) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 5 | 0 | @m0_5(Middle &) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 6 | 0 | @r0_6(Middle *) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 7 | 0 | @r0_7(Base *) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 8 | 0 | @r0_8(bool) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 9 | 0 | @r0_9(glval:Middle &) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 10 | 0 | @r0_10(Middle &) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 11 | 0 | @r0_11(Base *) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 12 | 0 | @r0_12(Base &) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 13 | 0 | @r0_13(Middle *) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 14 | 0 | @r0_14(glval:String) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 15 | 0 | @r0_15(bool) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 16 | 0 | @r0_16(glval:Middle &) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 17 | 0 | @r0_17(Middle &) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 18 | 0 | @r0_18(glval:String) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 19 | 0 | @r0_19(String &) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 20 | 0 | @r0_20(glval:Middle &) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 21 | 0 | @r0_21(Middle *) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 22 | 0 | @m0_22(Middle &) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 23 | 0 | @r0_23(glval:Middle &) | +| Middle::~Middle() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Middle::~Middle() -> void | 0 | 2 | 0 | @r0_2(glval:Middle) | +| Middle::~Middle() -> void | 0 | 4 | 0 | @r0_4(glval:String) | +| Middle::~Middle() -> void | 0 | 5 | 0 | @r0_5(bool) | +| Middle::~Middle() -> void | 0 | 7 | 0 | @r0_7(glval:Base) | +| Middle::~Middle() -> void | 0 | 8 | 0 | @r0_8(bool) | +| MiddleVB1::MiddleVB1() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| MiddleVB1::MiddleVB1() -> void | 0 | 2 | 0 | @r0_2(glval:MiddleVB1) | +| MiddleVB1::MiddleVB1() -> void | 0 | 3 | 0 | @r0_3(glval:Base) | +| MiddleVB1::MiddleVB1() -> void | 0 | 4 | 0 | @r0_4(bool) | +| MiddleVB1::MiddleVB1() -> void | 0 | 6 | 0 | @r0_6(glval:String) | +| MiddleVB1::MiddleVB1() -> void | 0 | 7 | 0 | @r0_7(bool) | +| MiddleVB1::~MiddleVB1() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| MiddleVB1::~MiddleVB1() -> void | 0 | 2 | 0 | @r0_2(glval:MiddleVB1) | +| MiddleVB1::~MiddleVB1() -> void | 0 | 4 | 0 | @r0_4(glval:String) | +| MiddleVB1::~MiddleVB1() -> void | 0 | 5 | 0 | @r0_5(bool) | +| MiddleVB1::~MiddleVB1() -> void | 0 | 7 | 0 | @r0_7(glval:Base) | +| MiddleVB1::~MiddleVB1() -> void | 0 | 8 | 0 | @r0_8(bool) | +| MiddleVB2::MiddleVB2() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| MiddleVB2::MiddleVB2() -> void | 0 | 2 | 0 | @r0_2(glval:MiddleVB2) | +| MiddleVB2::MiddleVB2() -> void | 0 | 3 | 0 | @r0_3(glval:Base) | +| MiddleVB2::MiddleVB2() -> void | 0 | 4 | 0 | @r0_4(bool) | +| MiddleVB2::MiddleVB2() -> void | 0 | 6 | 0 | @r0_6(glval:String) | +| MiddleVB2::MiddleVB2() -> void | 0 | 7 | 0 | @r0_7(bool) | +| MiddleVB2::~MiddleVB2() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| MiddleVB2::~MiddleVB2() -> void | 0 | 2 | 0 | @r0_2(glval:MiddleVB2) | +| MiddleVB2::~MiddleVB2() -> void | 0 | 4 | 0 | @r0_4(glval:String) | +| MiddleVB2::~MiddleVB2() -> void | 0 | 5 | 0 | @r0_5(bool) | +| MiddleVB2::~MiddleVB2() -> void | 0 | 7 | 0 | @r0_7(glval:Base) | +| MiddleVB2::~MiddleVB2() -> void | 0 | 8 | 0 | @r0_8(bool) | +| NestedInitList(int, float) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| NestedInitList(int, float) -> void | 0 | 2 | 0 | @r0_2(int) | +| NestedInitList(int, float) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| NestedInitList(int, float) -> void | 0 | 4 | 0 | @m0_4(int) | +| NestedInitList(int, float) -> void | 0 | 5 | 0 | @r0_5(float) | +| NestedInitList(int, float) -> void | 0 | 6 | 0 | @r0_6(glval:float) | +| NestedInitList(int, float) -> void | 0 | 7 | 0 | @m0_7(float) | +| NestedInitList(int, float) -> void | 0 | 8 | 0 | @r0_8(glval:Rect) | +| NestedInitList(int, float) -> void | 0 | 9 | 0 | @r0_9(glval:Point) | +| NestedInitList(int, float) -> void | 0 | 10 | 0 | @r0_10(Point) | +| NestedInitList(int, float) -> void | 0 | 11 | 0 | @m0_11(Point) | +| NestedInitList(int, float) -> void | 0 | 12 | 0 | @r0_12(glval:Point) | +| NestedInitList(int, float) -> void | 0 | 13 | 0 | @r0_13(Point) | +| NestedInitList(int, float) -> void | 0 | 14 | 0 | @mu0_14(Point) | +| NestedInitList(int, float) -> void | 0 | 15 | 0 | @r0_15(glval:Rect) | +| NestedInitList(int, float) -> void | 0 | 16 | 0 | @r0_16(glval:Point) | +| NestedInitList(int, float) -> void | 0 | 17 | 0 | @r0_17(glval:int) | +| NestedInitList(int, float) -> void | 0 | 18 | 0 | @r0_18(glval:int) | +| NestedInitList(int, float) -> void | 0 | 19 | 0 | @r0_19(int) | +| NestedInitList(int, float) -> void | 0 | 20 | 0 | @m0_20(int) | +| NestedInitList(int, float) -> void | 0 | 21 | 0 | @r0_21(glval:int) | +| NestedInitList(int, float) -> void | 0 | 22 | 0 | @r0_22(glval:float) | +| NestedInitList(int, float) -> void | 0 | 23 | 0 | @r0_23(float) | +| NestedInitList(int, float) -> void | 0 | 24 | 0 | @r0_24(int) | +| NestedInitList(int, float) -> void | 0 | 25 | 0 | @mu0_25(int) | +| NestedInitList(int, float) -> void | 0 | 26 | 0 | @r0_26(glval:Point) | +| NestedInitList(int, float) -> void | 0 | 27 | 0 | @r0_27(Point) | +| NestedInitList(int, float) -> void | 0 | 28 | 0 | @mu0_28(Point) | +| NestedInitList(int, float) -> void | 0 | 29 | 0 | @r0_29(glval:Rect) | +| NestedInitList(int, float) -> void | 0 | 30 | 0 | @r0_30(glval:Point) | +| NestedInitList(int, float) -> void | 0 | 31 | 0 | @r0_31(glval:int) | +| NestedInitList(int, float) -> void | 0 | 32 | 0 | @r0_32(glval:int) | +| NestedInitList(int, float) -> void | 0 | 33 | 0 | @r0_33(int) | +| NestedInitList(int, float) -> void | 0 | 34 | 0 | @m0_34(int) | +| NestedInitList(int, float) -> void | 0 | 35 | 0 | @r0_35(glval:int) | +| NestedInitList(int, float) -> void | 0 | 36 | 0 | @r0_36(glval:float) | +| NestedInitList(int, float) -> void | 0 | 37 | 0 | @r0_37(float) | +| NestedInitList(int, float) -> void | 0 | 38 | 0 | @r0_38(int) | +| NestedInitList(int, float) -> void | 0 | 39 | 0 | @mu0_39(int) | +| NestedInitList(int, float) -> void | 0 | 40 | 0 | @r0_40(glval:Point) | +| NestedInitList(int, float) -> void | 0 | 41 | 0 | @r0_41(glval:int) | +| NestedInitList(int, float) -> void | 0 | 42 | 0 | @r0_42(glval:int) | +| NestedInitList(int, float) -> void | 0 | 43 | 0 | @r0_43(int) | +| NestedInitList(int, float) -> void | 0 | 44 | 0 | @mu0_44(int) | +| NestedInitList(int, float) -> void | 0 | 45 | 0 | @r0_45(glval:int) | +| NestedInitList(int, float) -> void | 0 | 46 | 0 | @r0_46(glval:float) | +| NestedInitList(int, float) -> void | 0 | 47 | 0 | @r0_47(float) | +| NestedInitList(int, float) -> void | 0 | 48 | 0 | @r0_48(int) | +| NestedInitList(int, float) -> void | 0 | 49 | 0 | @mu0_49(int) | +| NestedInitList(int, float) -> void | 0 | 50 | 0 | @r0_50(glval:Rect) | +| NestedInitList(int, float) -> void | 0 | 51 | 0 | @r0_51(glval:Point) | +| NestedInitList(int, float) -> void | 0 | 52 | 0 | @r0_52(glval:int) | +| NestedInitList(int, float) -> void | 0 | 53 | 0 | @r0_53(glval:int) | +| NestedInitList(int, float) -> void | 0 | 54 | 0 | @r0_54(int) | +| NestedInitList(int, float) -> void | 0 | 55 | 0 | @m0_55(int) | +| NestedInitList(int, float) -> void | 0 | 56 | 0 | @r0_56(glval:int) | +| NestedInitList(int, float) -> void | 0 | 57 | 0 | @r0_57(int) | +| NestedInitList(int, float) -> void | 0 | 58 | 0 | @mu0_58(int) | +| NestedInitList(int, float) -> void | 0 | 59 | 0 | @r0_59(glval:Point) | +| NestedInitList(int, float) -> void | 0 | 60 | 0 | @r0_60(glval:int) | +| NestedInitList(int, float) -> void | 0 | 61 | 0 | @r0_61(glval:int) | +| NestedInitList(int, float) -> void | 0 | 62 | 0 | @r0_62(int) | +| NestedInitList(int, float) -> void | 0 | 63 | 0 | @mu0_63(int) | +| NestedInitList(int, float) -> void | 0 | 64 | 0 | @r0_64(glval:int) | +| NestedInitList(int, float) -> void | 0 | 65 | 0 | @r0_65(int) | +| NestedInitList(int, float) -> void | 0 | 66 | 0 | @mu0_66(int) | +| Nullptr() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Nullptr() -> void | 0 | 2 | 0 | @r0_2(glval:int *) | +| Nullptr() -> void | 0 | 3 | 0 | @r0_3(int *) | +| Nullptr() -> void | 0 | 4 | 0 | @m0_4(int *) | +| Nullptr() -> void | 0 | 5 | 0 | @r0_5(glval:int *) | +| Nullptr() -> void | 0 | 6 | 0 | @r0_6(int *) | +| Nullptr() -> void | 0 | 7 | 0 | @m0_7(int *) | +| Nullptr() -> void | 0 | 8 | 0 | @r0_8(int *) | +| Nullptr() -> void | 0 | 9 | 0 | @r0_9(glval:int *) | +| Nullptr() -> void | 0 | 10 | 0 | @m0_10(int *) | +| Nullptr() -> void | 0 | 11 | 0 | @r0_11(int *) | +| Nullptr() -> void | 0 | 12 | 0 | @r0_12(glval:int *) | +| Nullptr() -> void | 0 | 13 | 0 | @m0_13(int *) | +| Outer::Func(void *, char) -> long | 0 | 1 | 0 | @mu0_1(unknown) | +| Outer::Func(void *, char) -> long | 0 | 2 | 0 | @r0_2(void *) | +| Outer::Func(void *, char) -> long | 0 | 3 | 0 | @r0_3(glval:void *) | +| Outer::Func(void *, char) -> long | 0 | 4 | 0 | @m0_4(void *) | +| Outer::Func(void *, char) -> long | 0 | 5 | 0 | @r0_5(char) | +| Outer::Func(void *, char) -> long | 0 | 6 | 0 | @r0_6(glval:char) | +| Outer::Func(void *, char) -> long | 0 | 7 | 0 | @m0_7(char) | +| Outer::Func(void *, char) -> long | 0 | 8 | 0 | @r0_8(glval:long) | +| Outer::Func(void *, char) -> long | 0 | 9 | 0 | @r0_9(long) | +| Outer::Func(void *, char) -> long | 0 | 10 | 0 | @m0_10(long) | +| Outer::Func(void *, char) -> long | 0 | 11 | 0 | @r0_11(glval:long) | +| Parameters(int, int) -> int | 0 | 1 | 0 | @mu0_1(unknown) | +| Parameters(int, int) -> int | 0 | 2 | 0 | @r0_2(int) | +| Parameters(int, int) -> int | 0 | 3 | 0 | @r0_3(glval:int) | +| Parameters(int, int) -> int | 0 | 4 | 0 | @m0_4(int) | +| Parameters(int, int) -> int | 0 | 5 | 0 | @r0_5(int) | +| Parameters(int, int) -> int | 0 | 6 | 0 | @r0_6(glval:int) | +| Parameters(int, int) -> int | 0 | 7 | 0 | @m0_7(int) | +| Parameters(int, int) -> int | 0 | 8 | 0 | @r0_8(glval:int) | +| Parameters(int, int) -> int | 0 | 9 | 0 | @r0_9(glval:int) | +| Parameters(int, int) -> int | 0 | 10 | 0 | @r0_10(int) | +| Parameters(int, int) -> int | 0 | 11 | 0 | @r0_11(glval:int) | +| Parameters(int, int) -> int | 0 | 12 | 0 | @r0_12(int) | +| Parameters(int, int) -> int | 0 | 13 | 0 | @r0_13(int) | +| Parameters(int, int) -> int | 0 | 14 | 0 | @m0_14(int) | +| Parameters(int, int) -> int | 0 | 15 | 0 | @r0_15(glval:int) | +| PointerCompare(int *, int *) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| PointerCompare(int *, int *) -> void | 0 | 2 | 0 | @r0_2(int *) | +| PointerCompare(int *, int *) -> void | 0 | 3 | 0 | @r0_3(glval:int *) | +| PointerCompare(int *, int *) -> void | 0 | 4 | 0 | @m0_4(int *) | +| PointerCompare(int *, int *) -> void | 0 | 5 | 0 | @r0_5(int *) | +| PointerCompare(int *, int *) -> void | 0 | 6 | 0 | @r0_6(glval:int *) | +| PointerCompare(int *, int *) -> void | 0 | 7 | 0 | @m0_7(int *) | +| PointerCompare(int *, int *) -> void | 0 | 8 | 0 | @r0_8(glval:bool) | +| PointerCompare(int *, int *) -> void | 0 | 9 | 0 | @r0_9(bool) | +| PointerCompare(int *, int *) -> void | 0 | 10 | 0 | @m0_10(bool) | +| PointerCompare(int *, int *) -> void | 0 | 11 | 0 | @r0_11(glval:int *) | +| PointerCompare(int *, int *) -> void | 0 | 12 | 0 | @r0_12(int *) | +| PointerCompare(int *, int *) -> void | 0 | 13 | 0 | @r0_13(glval:int *) | +| PointerCompare(int *, int *) -> void | 0 | 14 | 0 | @r0_14(int *) | +| PointerCompare(int *, int *) -> void | 0 | 15 | 0 | @r0_15(bool) | +| PointerCompare(int *, int *) -> void | 0 | 16 | 0 | @r0_16(glval:bool) | +| PointerCompare(int *, int *) -> void | 0 | 17 | 0 | @m0_17(bool) | +| PointerCompare(int *, int *) -> void | 0 | 18 | 0 | @r0_18(glval:int *) | +| PointerCompare(int *, int *) -> void | 0 | 19 | 0 | @r0_19(int *) | +| PointerCompare(int *, int *) -> void | 0 | 20 | 0 | @r0_20(glval:int *) | +| PointerCompare(int *, int *) -> void | 0 | 21 | 0 | @r0_21(int *) | +| PointerCompare(int *, int *) -> void | 0 | 22 | 0 | @r0_22(bool) | +| PointerCompare(int *, int *) -> void | 0 | 23 | 0 | @r0_23(glval:bool) | +| PointerCompare(int *, int *) -> void | 0 | 24 | 0 | @m0_24(bool) | +| PointerCompare(int *, int *) -> void | 0 | 25 | 0 | @r0_25(glval:int *) | +| PointerCompare(int *, int *) -> void | 0 | 26 | 0 | @r0_26(int *) | +| PointerCompare(int *, int *) -> void | 0 | 27 | 0 | @r0_27(glval:int *) | +| PointerCompare(int *, int *) -> void | 0 | 28 | 0 | @r0_28(int *) | +| PointerCompare(int *, int *) -> void | 0 | 29 | 0 | @r0_29(bool) | +| PointerCompare(int *, int *) -> void | 0 | 30 | 0 | @r0_30(glval:bool) | +| PointerCompare(int *, int *) -> void | 0 | 31 | 0 | @m0_31(bool) | +| PointerCompare(int *, int *) -> void | 0 | 32 | 0 | @r0_32(glval:int *) | +| PointerCompare(int *, int *) -> void | 0 | 33 | 0 | @r0_33(int *) | +| PointerCompare(int *, int *) -> void | 0 | 34 | 0 | @r0_34(glval:int *) | +| PointerCompare(int *, int *) -> void | 0 | 35 | 0 | @r0_35(int *) | +| PointerCompare(int *, int *) -> void | 0 | 36 | 0 | @r0_36(bool) | +| PointerCompare(int *, int *) -> void | 0 | 37 | 0 | @r0_37(glval:bool) | +| PointerCompare(int *, int *) -> void | 0 | 38 | 0 | @m0_38(bool) | +| PointerCompare(int *, int *) -> void | 0 | 39 | 0 | @r0_39(glval:int *) | +| PointerCompare(int *, int *) -> void | 0 | 40 | 0 | @r0_40(int *) | +| PointerCompare(int *, int *) -> void | 0 | 41 | 0 | @r0_41(glval:int *) | +| PointerCompare(int *, int *) -> void | 0 | 42 | 0 | @r0_42(int *) | +| PointerCompare(int *, int *) -> void | 0 | 43 | 0 | @r0_43(bool) | +| PointerCompare(int *, int *) -> void | 0 | 44 | 0 | @r0_44(glval:bool) | +| PointerCompare(int *, int *) -> void | 0 | 45 | 0 | @m0_45(bool) | +| PointerCompare(int *, int *) -> void | 0 | 46 | 0 | @r0_46(glval:int *) | +| PointerCompare(int *, int *) -> void | 0 | 47 | 0 | @r0_47(int *) | +| PointerCompare(int *, int *) -> void | 0 | 48 | 0 | @r0_48(glval:int *) | +| PointerCompare(int *, int *) -> void | 0 | 49 | 0 | @r0_49(int *) | +| PointerCompare(int *, int *) -> void | 0 | 50 | 0 | @r0_50(bool) | +| PointerCompare(int *, int *) -> void | 0 | 51 | 0 | @r0_51(glval:bool) | +| PointerCompare(int *, int *) -> void | 0 | 52 | 0 | @m0_52(bool) | +| PointerCrement(int *) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| PointerCrement(int *) -> void | 0 | 2 | 0 | @r0_2(int *) | +| PointerCrement(int *) -> void | 0 | 3 | 0 | @r0_3(glval:int *) | +| PointerCrement(int *) -> void | 0 | 4 | 0 | @m0_4(int *) | +| PointerCrement(int *) -> void | 0 | 5 | 0 | @r0_5(glval:int *) | +| PointerCrement(int *) -> void | 0 | 6 | 0 | @r0_6(int *) | +| PointerCrement(int *) -> void | 0 | 7 | 0 | @m0_7(int *) | +| PointerCrement(int *) -> void | 0 | 8 | 0 | @r0_8(glval:int *) | +| PointerCrement(int *) -> void | 0 | 9 | 0 | @r0_9(int *) | +| PointerCrement(int *) -> void | 0 | 10 | 0 | @r0_10(int) | +| PointerCrement(int *) -> void | 0 | 11 | 0 | @r0_11(int *) | +| PointerCrement(int *) -> void | 0 | 12 | 0 | @m0_12(int *) | +| PointerCrement(int *) -> void | 0 | 13 | 0 | @r0_13(glval:int *) | +| PointerCrement(int *) -> void | 0 | 14 | 0 | @m0_14(int *) | +| PointerCrement(int *) -> void | 0 | 15 | 0 | @r0_15(glval:int *) | +| PointerCrement(int *) -> void | 0 | 16 | 0 | @r0_16(int *) | +| PointerCrement(int *) -> void | 0 | 17 | 0 | @r0_17(int) | +| PointerCrement(int *) -> void | 0 | 18 | 0 | @r0_18(int *) | +| PointerCrement(int *) -> void | 0 | 19 | 0 | @m0_19(int *) | +| PointerCrement(int *) -> void | 0 | 20 | 0 | @r0_20(glval:int *) | +| PointerCrement(int *) -> void | 0 | 21 | 0 | @m0_21(int *) | +| PointerCrement(int *) -> void | 0 | 22 | 0 | @r0_22(glval:int *) | +| PointerCrement(int *) -> void | 0 | 23 | 0 | @r0_23(int *) | +| PointerCrement(int *) -> void | 0 | 24 | 0 | @r0_24(int) | +| PointerCrement(int *) -> void | 0 | 25 | 0 | @r0_25(int *) | +| PointerCrement(int *) -> void | 0 | 26 | 0 | @m0_26(int *) | +| PointerCrement(int *) -> void | 0 | 27 | 0 | @r0_27(glval:int *) | +| PointerCrement(int *) -> void | 0 | 28 | 0 | @m0_28(int *) | +| PointerCrement(int *) -> void | 0 | 29 | 0 | @r0_29(glval:int *) | +| PointerCrement(int *) -> void | 0 | 30 | 0 | @r0_30(int *) | +| PointerCrement(int *) -> void | 0 | 31 | 0 | @r0_31(int) | +| PointerCrement(int *) -> void | 0 | 32 | 0 | @r0_32(int *) | +| PointerCrement(int *) -> void | 0 | 33 | 0 | @m0_33(int *) | +| PointerCrement(int *) -> void | 0 | 34 | 0 | @r0_34(glval:int *) | +| PointerCrement(int *) -> void | 0 | 35 | 0 | @m0_35(int *) | +| PointerOps(int *, int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| PointerOps(int *, int) -> void | 0 | 2 | 0 | @r0_2(int *) | +| PointerOps(int *, int) -> void | 0 | 3 | 0 | @r0_3(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 4 | 0 | @m0_4(int *) | +| PointerOps(int *, int) -> void | 0 | 5 | 0 | @r0_5(int) | +| PointerOps(int *, int) -> void | 0 | 6 | 0 | @r0_6(glval:int) | +| PointerOps(int *, int) -> void | 0 | 7 | 0 | @m0_7(int) | +| PointerOps(int *, int) -> void | 0 | 8 | 0 | @r0_8(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 9 | 0 | @r0_9(int *) | +| PointerOps(int *, int) -> void | 0 | 10 | 0 | @m0_10(int *) | +| PointerOps(int *, int) -> void | 0 | 11 | 0 | @r0_11(glval:bool) | +| PointerOps(int *, int) -> void | 0 | 12 | 0 | @r0_12(bool) | +| PointerOps(int *, int) -> void | 0 | 13 | 0 | @m0_13(bool) | +| PointerOps(int *, int) -> void | 0 | 14 | 0 | @r0_14(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 15 | 0 | @r0_15(int *) | +| PointerOps(int *, int) -> void | 0 | 16 | 0 | @r0_16(glval:int) | +| PointerOps(int *, int) -> void | 0 | 17 | 0 | @r0_17(int) | +| PointerOps(int *, int) -> void | 0 | 18 | 0 | @r0_18(int *) | +| PointerOps(int *, int) -> void | 0 | 19 | 0 | @r0_19(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 20 | 0 | @m0_20(int *) | +| PointerOps(int *, int) -> void | 0 | 21 | 0 | @r0_21(glval:int) | +| PointerOps(int *, int) -> void | 0 | 22 | 0 | @r0_22(int) | +| PointerOps(int *, int) -> void | 0 | 23 | 0 | @r0_23(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 24 | 0 | @r0_24(int *) | +| PointerOps(int *, int) -> void | 0 | 25 | 0 | @r0_25(int *) | +| PointerOps(int *, int) -> void | 0 | 26 | 0 | @r0_26(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 27 | 0 | @m0_27(int *) | +| PointerOps(int *, int) -> void | 0 | 28 | 0 | @r0_28(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 29 | 0 | @r0_29(int *) | +| PointerOps(int *, int) -> void | 0 | 30 | 0 | @r0_30(glval:int) | +| PointerOps(int *, int) -> void | 0 | 31 | 0 | @r0_31(int) | +| PointerOps(int *, int) -> void | 0 | 32 | 0 | @r0_32(int *) | +| PointerOps(int *, int) -> void | 0 | 33 | 0 | @r0_33(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 34 | 0 | @m0_34(int *) | +| PointerOps(int *, int) -> void | 0 | 35 | 0 | @r0_35(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 36 | 0 | @r0_36(int *) | +| PointerOps(int *, int) -> void | 0 | 37 | 0 | @r0_37(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 38 | 0 | @r0_38(int *) | +| PointerOps(int *, int) -> void | 0 | 39 | 0 | @r0_39(long) | +| PointerOps(int *, int) -> void | 0 | 40 | 0 | @r0_40(int) | +| PointerOps(int *, int) -> void | 0 | 41 | 0 | @r0_41(glval:int) | +| PointerOps(int *, int) -> void | 0 | 42 | 0 | @m0_42(int) | +| PointerOps(int *, int) -> void | 0 | 43 | 0 | @r0_43(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 44 | 0 | @r0_44(int *) | +| PointerOps(int *, int) -> void | 0 | 45 | 0 | @r0_45(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 46 | 0 | @m0_46(int *) | +| PointerOps(int *, int) -> void | 0 | 47 | 0 | @r0_47(glval:int) | +| PointerOps(int *, int) -> void | 0 | 48 | 0 | @r0_48(int) | +| PointerOps(int *, int) -> void | 0 | 49 | 0 | @r0_49(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 50 | 0 | @r0_50(int *) | +| PointerOps(int *, int) -> void | 0 | 51 | 0 | @r0_51(int *) | +| PointerOps(int *, int) -> void | 0 | 52 | 0 | @m0_52(int *) | +| PointerOps(int *, int) -> void | 0 | 53 | 0 | @r0_53(glval:int) | +| PointerOps(int *, int) -> void | 0 | 54 | 0 | @r0_54(int) | +| PointerOps(int *, int) -> void | 0 | 55 | 0 | @r0_55(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 56 | 0 | @r0_56(int *) | +| PointerOps(int *, int) -> void | 0 | 57 | 0 | @r0_57(int *) | +| PointerOps(int *, int) -> void | 0 | 58 | 0 | @m0_58(int *) | +| PointerOps(int *, int) -> void | 0 | 59 | 0 | @r0_59(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 60 | 0 | @r0_60(int *) | +| PointerOps(int *, int) -> void | 0 | 61 | 0 | @r0_61(int *) | +| PointerOps(int *, int) -> void | 0 | 62 | 0 | @r0_62(bool) | +| PointerOps(int *, int) -> void | 0 | 63 | 0 | @r0_63(glval:bool) | +| PointerOps(int *, int) -> void | 0 | 64 | 0 | @m0_64(bool) | +| PointerOps(int *, int) -> void | 0 | 65 | 0 | @r0_65(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 66 | 0 | @r0_66(int *) | +| PointerOps(int *, int) -> void | 0 | 67 | 0 | @r0_67(int *) | +| PointerOps(int *, int) -> void | 0 | 68 | 0 | @r0_68(bool) | +| PointerOps(int *, int) -> void | 0 | 69 | 0 | @r0_69(bool) | +| PointerOps(int *, int) -> void | 0 | 70 | 0 | @r0_70(glval:bool) | +| PointerOps(int *, int) -> void | 0 | 71 | 0 | @m0_71(bool) | +| PolymorphicBase::PolymorphicBase() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| PolymorphicBase::PolymorphicBase() -> void | 0 | 2 | 0 | @r0_2(glval:PolymorphicBase) | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 2 | 0 | @r0_2(glval:PolymorphicDerived) | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 3 | 0 | @r0_3(glval:PolymorphicBase) | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 4 | 0 | @r0_4(bool) | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 2 | 0 | @r0_2(glval:PolymorphicDerived) | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 4 | 0 | @r0_4(glval:PolymorphicBase) | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 5 | 0 | @r0_5(bool) | +| ReturnStruct(Point) -> Point | 0 | 1 | 0 | @mu0_1(unknown) | +| ReturnStruct(Point) -> Point | 0 | 2 | 0 | @r0_2(Point) | +| ReturnStruct(Point) -> Point | 0 | 3 | 0 | @r0_3(glval:Point) | +| ReturnStruct(Point) -> Point | 0 | 4 | 0 | @m0_4(Point) | +| ReturnStruct(Point) -> Point | 0 | 5 | 0 | @r0_5(glval:Point) | +| ReturnStruct(Point) -> Point | 0 | 6 | 0 | @r0_6(glval:Point) | +| ReturnStruct(Point) -> Point | 0 | 7 | 0 | @r0_7(Point) | +| ReturnStruct(Point) -> Point | 0 | 8 | 0 | @m0_8(Point) | +| ReturnStruct(Point) -> Point | 0 | 9 | 0 | @r0_9(glval:Point) | +| SetFuncPtr() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| SetFuncPtr() -> void | 0 | 2 | 0 | @r0_2(glval:..(*)(..)) | +| SetFuncPtr() -> void | 0 | 3 | 0 | @r0_3(glval:..(*)(..)) | +| SetFuncPtr() -> void | 0 | 4 | 0 | @m0_4(..(*)(..)) | +| SetFuncPtr() -> void | 0 | 5 | 0 | @r0_5(glval:..()(..)) | +| SetFuncPtr() -> void | 0 | 6 | 0 | @r0_6(glval:..(*)(..)) | +| SetFuncPtr() -> void | 0 | 7 | 0 | @m0_7(..(*)(..)) | +| SetFuncPtr() -> void | 0 | 8 | 0 | @r0_8(glval:..(*)(..)) | +| SetFuncPtr() -> void | 0 | 9 | 0 | @r0_9(glval:..(*)(..)) | +| SetFuncPtr() -> void | 0 | 10 | 0 | @m0_10(..(*)(..)) | +| SetFuncPtr() -> void | 0 | 11 | 0 | @r0_11(glval:..()(..)) | +| SetFuncPtr() -> void | 0 | 12 | 0 | @r0_12(glval:..(*)(..)) | +| SetFuncPtr() -> void | 0 | 13 | 0 | @m0_13(..(*)(..)) | +| String::String() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| String::String() -> void | 0 | 2 | 0 | @r0_2(glval:String) | +| String::String() -> void | 0 | 3 | 0 | @r0_3(bool) | +| String::String() -> void | 0 | 4 | 0 | @r0_4(glval:char[1]) | +| String::String() -> void | 0 | 5 | 0 | @r0_5(char *) | +| StringLiteral(int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| StringLiteral(int) -> void | 0 | 2 | 0 | @r0_2(int) | +| StringLiteral(int) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| StringLiteral(int) -> void | 0 | 4 | 0 | @m0_4(int) | +| StringLiteral(int) -> void | 0 | 5 | 0 | @r0_5(glval:char) | +| StringLiteral(int) -> void | 0 | 6 | 0 | @r0_6(glval:char[4]) | +| StringLiteral(int) -> void | 0 | 7 | 0 | @r0_7(char *) | +| StringLiteral(int) -> void | 0 | 8 | 0 | @r0_8(glval:int) | +| StringLiteral(int) -> void | 0 | 9 | 0 | @r0_9(int) | +| StringLiteral(int) -> void | 0 | 10 | 0 | @r0_10(char *) | +| StringLiteral(int) -> void | 0 | 11 | 0 | @r0_11(char) | +| StringLiteral(int) -> void | 0 | 12 | 0 | @m0_12(char) | +| StringLiteral(int) -> void | 0 | 13 | 0 | @r0_13(glval:wchar_t *) | +| StringLiteral(int) -> void | 0 | 14 | 0 | @r0_14(glval:wchar_t[4]) | +| StringLiteral(int) -> void | 0 | 15 | 0 | @r0_15(wchar_t *) | +| StringLiteral(int) -> void | 0 | 16 | 0 | @r0_16(wchar_t *) | +| StringLiteral(int) -> void | 0 | 17 | 0 | @m0_17(wchar_t *) | +| StringLiteral(int) -> void | 0 | 18 | 0 | @r0_18(glval:wchar_t) | +| StringLiteral(int) -> void | 0 | 19 | 0 | @r0_19(glval:wchar_t *) | +| StringLiteral(int) -> void | 0 | 20 | 0 | @r0_20(wchar_t *) | +| StringLiteral(int) -> void | 0 | 21 | 0 | @r0_21(glval:int) | +| StringLiteral(int) -> void | 0 | 22 | 0 | @r0_22(int) | +| StringLiteral(int) -> void | 0 | 23 | 0 | @r0_23(wchar_t *) | +| StringLiteral(int) -> void | 0 | 24 | 0 | @r0_24(wchar_t) | +| StringLiteral(int) -> void | 0 | 25 | 0 | @m0_25(wchar_t) | +| Switch(int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Switch(int) -> void | 0 | 2 | 0 | @r0_2(int) | +| Switch(int) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| Switch(int) -> void | 0 | 4 | 0 | @m0_4(int) | +| Switch(int) -> void | 0 | 5 | 0 | @r0_5(glval:int) | +| Switch(int) -> void | 0 | 6 | 0 | @r0_6(int) | +| Switch(int) -> void | 0 | 7 | 0 | @m0_7(int) | +| Switch(int) -> void | 0 | 8 | 0 | @r0_8(glval:int) | +| Switch(int) -> void | 0 | 9 | 0 | @r0_9(int) | +| Switch(int) -> void | 1 | 0 | 0 | @r1_0(int) | +| Switch(int) -> void | 1 | 1 | 0 | @r1_1(glval:int) | +| Switch(int) -> void | 1 | 2 | 0 | @m1_2(int) | +| Switch(int) -> void | 2 | 1 | 0 | @r2_1(int) | +| Switch(int) -> void | 2 | 2 | 0 | @r2_2(glval:int) | +| Switch(int) -> void | 2 | 3 | 0 | @m2_3(int) | +| Switch(int) -> void | 4 | 1 | 0 | @r4_1(int) | +| Switch(int) -> void | 4 | 2 | 0 | @r4_2(glval:int) | +| Switch(int) -> void | 4 | 3 | 0 | @m4_3(int) | +| Switch(int) -> void | 5 | 1 | 0 | @r5_1(int) | +| Switch(int) -> void | 5 | 2 | 0 | @r5_2(glval:int) | +| Switch(int) -> void | 5 | 3 | 0 | @m5_3(int) | +| Switch(int) -> void | 6 | 1 | 0 | @r6_1(int) | +| Switch(int) -> void | 6 | 2 | 0 | @r6_2(glval:int) | +| Switch(int) -> void | 6 | 3 | 0 | @m6_3(int) | +| Switch(int) -> void | 7 | 1 | 0 | @r7_1(int) | +| Switch(int) -> void | 7 | 2 | 0 | @r7_2(glval:int) | +| Switch(int) -> void | 7 | 3 | 0 | @m7_3(int) | +| Switch(int) -> void | 8 | 0 | 0 | @r8_0(int) | +| Switch(int) -> void | 8 | 1 | 0 | @r8_1(glval:int) | +| Switch(int) -> void | 8 | 2 | 0 | @m8_2(int) | +| TakeReference() -> int & | 0 | 1 | 0 | @mu0_1(unknown) | +| TakeReference() -> int & | 0 | 2 | 0 | @r0_2(glval:int &) | +| TakeReference() -> int & | 0 | 3 | 0 | @r0_3(glval:int) | +| TakeReference() -> int & | 0 | 4 | 0 | @m0_4(int &) | +| TakeReference() -> int & | 0 | 5 | 0 | @r0_5(glval:int &) | +| TryCatch(bool) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| TryCatch(bool) -> void | 0 | 2 | 0 | @r0_2(bool) | +| TryCatch(bool) -> void | 0 | 3 | 0 | @r0_3(glval:bool) | +| TryCatch(bool) -> void | 0 | 4 | 0 | @m0_4(bool) | +| TryCatch(bool) -> void | 0 | 5 | 0 | @r0_5(glval:int) | +| TryCatch(bool) -> void | 0 | 6 | 0 | @r0_6(int) | +| TryCatch(bool) -> void | 0 | 7 | 0 | @m0_7(int) | +| TryCatch(bool) -> void | 0 | 8 | 0 | @r0_8(glval:bool) | +| TryCatch(bool) -> void | 0 | 9 | 0 | @r0_9(bool) | +| TryCatch(bool) -> void | 3 | 0 | 0 | @r3_0(glval:char *) | +| TryCatch(bool) -> void | 3 | 1 | 0 | @r3_1(glval:char[15]) | +| TryCatch(bool) -> void | 3 | 2 | 0 | @r3_2(char *) | +| TryCatch(bool) -> void | 3 | 3 | 0 | @m3_3(char *) | +| TryCatch(bool) -> void | 4 | 0 | 0 | @r4_0(glval:int) | +| TryCatch(bool) -> void | 4 | 1 | 0 | @r4_1(int) | +| TryCatch(bool) -> void | 4 | 2 | 0 | @r4_2(int) | +| TryCatch(bool) -> void | 4 | 3 | 0 | @r4_3(bool) | +| TryCatch(bool) -> void | 5 | 0 | 0 | @r5_0(glval:bool) | +| TryCatch(bool) -> void | 5 | 1 | 0 | @r5_1(bool) | +| TryCatch(bool) -> void | 6 | 0 | 0 | @r6_0(int) | +| TryCatch(bool) -> void | 6 | 1 | 0 | @r6_1(glval:int) | +| TryCatch(bool) -> void | 6 | 2 | 0 | @m6_2(int) | +| TryCatch(bool) -> void | 6 | 3 | 0 | @r6_3(glval:int) | +| TryCatch(bool) -> void | 6 | 4 | 0 | @r6_4(int) | +| TryCatch(bool) -> void | 6 | 5 | 0 | @r6_5(glval:int) | +| TryCatch(bool) -> void | 6 | 6 | 0 | @m6_6(int) | +| TryCatch(bool) -> void | 7 | 0 | 0 | @r7_0(glval:String) | +| TryCatch(bool) -> void | 7 | 1 | 0 | @r7_1(bool) | +| TryCatch(bool) -> void | 7 | 2 | 0 | @r7_2(glval:char[14]) | +| TryCatch(bool) -> void | 7 | 3 | 0 | @r7_3(char *) | +| TryCatch(bool) -> void | 8 | 0 | 0 | @r8_0(int) | +| TryCatch(bool) -> void | 8 | 1 | 0 | @r8_1(glval:int) | +| TryCatch(bool) -> void | 8 | 2 | 0 | @m8_2(int) | +| TryCatch(bool) -> void | 10 | 0 | 0 | @r10_0(char *) | +| TryCatch(bool) -> void | 10 | 1 | 0 | @r10_1(glval:char *) | +| TryCatch(bool) -> void | 10 | 2 | 0 | @m10_2(char *) | +| TryCatch(bool) -> void | 10 | 3 | 0 | @r10_3(glval:String) | +| TryCatch(bool) -> void | 10 | 4 | 0 | @r10_4(bool) | +| TryCatch(bool) -> void | 10 | 5 | 0 | @r10_5(glval:char *) | +| TryCatch(bool) -> void | 10 | 6 | 0 | @r10_6(char *) | +| TryCatch(bool) -> void | 12 | 0 | 0 | @r12_0(String &) | +| TryCatch(bool) -> void | 12 | 1 | 0 | @r12_1(glval:String &) | +| TryCatch(bool) -> void | 12 | 2 | 0 | @m12_2(String &) | +| UninitializedVariables() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| UninitializedVariables() -> void | 0 | 2 | 0 | @r0_2(glval:int) | +| UninitializedVariables() -> void | 0 | 3 | 0 | @r0_3(int) | +| UninitializedVariables() -> void | 0 | 4 | 0 | @m0_4(int) | +| UninitializedVariables() -> void | 0 | 5 | 0 | @r0_5(glval:int) | +| UninitializedVariables() -> void | 0 | 6 | 0 | @r0_6(glval:int) | +| UninitializedVariables() -> void | 0 | 7 | 0 | @r0_7(int) | +| UninitializedVariables() -> void | 0 | 8 | 0 | @m0_8(int) | +| UnionInit(int, float) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| UnionInit(int, float) -> void | 0 | 2 | 0 | @r0_2(int) | +| UnionInit(int, float) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| UnionInit(int, float) -> void | 0 | 4 | 0 | @m0_4(int) | +| UnionInit(int, float) -> void | 0 | 5 | 0 | @r0_5(float) | +| UnionInit(int, float) -> void | 0 | 6 | 0 | @r0_6(glval:float) | +| UnionInit(int, float) -> void | 0 | 7 | 0 | @m0_7(float) | +| UnionInit(int, float) -> void | 0 | 8 | 0 | @r0_8(glval:U) | +| UnionInit(int, float) -> void | 0 | 9 | 0 | @r0_9(glval:double) | +| UnionInit(int, float) -> void | 0 | 10 | 0 | @r0_10(glval:float) | +| UnionInit(int, float) -> void | 0 | 11 | 0 | @r0_11(float) | +| UnionInit(int, float) -> void | 0 | 12 | 0 | @r0_12(double) | +| UnionInit(int, float) -> void | 0 | 13 | 0 | @m0_13(double) | +| VarArgUsage(int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| VarArgUsage(int) -> void | 0 | 2 | 0 | @r0_2(int) | +| VarArgUsage(int) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| VarArgUsage(int) -> void | 0 | 4 | 0 | @mu0_4(int) | +| VarArgUsage(int) -> void | 0 | 5 | 0 | @r0_5(glval:__va_list_tag[1]) | +| VarArgUsage(int) -> void | 0 | 6 | 0 | @r0_6(__va_list_tag[1]) | +| VarArgUsage(int) -> void | 0 | 7 | 0 | @mu0_7(__va_list_tag[1]) | +| VarArgUsage(int) -> void | 0 | 8 | 0 | @r0_8(glval:__va_list_tag[1]) | +| VarArgUsage(int) -> void | 0 | 9 | 0 | @r0_9(__va_list_tag *) | +| VarArgUsage(int) -> void | 0 | 10 | 0 | @r0_10(glval:int) | +| VarArgUsage(int) -> void | 0 | 12 | 0 | @r0_12(glval:__va_list_tag[1]) | +| VarArgUsage(int) -> void | 0 | 13 | 0 | @r0_13(__va_list_tag[1]) | +| VarArgUsage(int) -> void | 0 | 14 | 0 | @mu0_14(__va_list_tag[1]) | +| VarArgUsage(int) -> void | 0 | 15 | 0 | @r0_15(glval:__va_list_tag[1]) | +| VarArgUsage(int) -> void | 0 | 16 | 0 | @r0_16(__va_list_tag *) | +| VarArgUsage(int) -> void | 0 | 17 | 0 | @r0_17(glval:__va_list_tag[1]) | +| VarArgUsage(int) -> void | 0 | 18 | 0 | @r0_18(__va_list_tag *) | +| VarArgUsage(int) -> void | 0 | 20 | 0 | @r0_20(glval:double) | +| VarArgUsage(int) -> void | 0 | 21 | 0 | @r0_21(glval:__va_list_tag[1]) | +| VarArgUsage(int) -> void | 0 | 22 | 0 | @r0_22(__va_list_tag *) | +| VarArgUsage(int) -> void | 0 | 23 | 0 | @r0_23(glval:double) | +| VarArgUsage(int) -> void | 0 | 24 | 0 | @r0_24(double) | +| VarArgUsage(int) -> void | 0 | 25 | 0 | @m0_25(double) | +| VarArgUsage(int) -> void | 0 | 26 | 0 | @r0_26(glval:float) | +| VarArgUsage(int) -> void | 0 | 27 | 0 | @r0_27(glval:__va_list_tag[1]) | +| VarArgUsage(int) -> void | 0 | 28 | 0 | @r0_28(__va_list_tag *) | +| VarArgUsage(int) -> void | 0 | 29 | 0 | @r0_29(glval:double) | +| VarArgUsage(int) -> void | 0 | 30 | 0 | @r0_30(double) | +| VarArgUsage(int) -> void | 0 | 31 | 0 | @r0_31(float) | +| VarArgUsage(int) -> void | 0 | 32 | 0 | @m0_32(float) | +| VarArgUsage(int) -> void | 0 | 33 | 0 | @r0_33(glval:__va_list_tag[1]) | +| VarArgUsage(int) -> void | 0 | 34 | 0 | @r0_34(__va_list_tag *) | +| VarArgUsage(int) -> void | 0 | 36 | 0 | @r0_36(glval:__va_list_tag[1]) | +| VarArgUsage(int) -> void | 0 | 37 | 0 | @r0_37(__va_list_tag *) | +| VarArgs() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| VarArgs() -> void | 0 | 2 | 0 | @r0_2(bool) | +| VarArgs() -> void | 0 | 3 | 0 | @r0_3(glval:char[6]) | +| VarArgs() -> void | 0 | 4 | 0 | @r0_4(char *) | +| VarArgs() -> void | 0 | 5 | 0 | @r0_5(int) | +| VarArgs() -> void | 0 | 6 | 0 | @r0_6(glval:char[7]) | +| VarArgs() -> void | 0 | 7 | 0 | @r0_7(char *) | +| WhileStatements(int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| WhileStatements(int) -> void | 0 | 2 | 0 | @r0_2(int) | +| WhileStatements(int) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| WhileStatements(int) -> void | 0 | 4 | 0 | @m0_4(int) | +| WhileStatements(int) -> void | 1 | 0 | 0 | @r1_0(int) | +| WhileStatements(int) -> void | 1 | 1 | 0 | @r1_1(glval:int) | +| WhileStatements(int) -> void | 1 | 2 | 0 | @r1_2(int) | +| WhileStatements(int) -> void | 1 | 3 | 0 | @r1_3(int) | +| WhileStatements(int) -> void | 1 | 4 | 0 | @m1_4(int) | +| WhileStatements(int) -> void | 3 | 0 | 0 | @m3_0(int) | +| WhileStatements(int) -> void | 3 | 1 | 0 | @r3_1(glval:int) | +| WhileStatements(int) -> void | 3 | 2 | 0 | @r3_2(int) | +| WhileStatements(int) -> void | 3 | 3 | 0 | @r3_3(int) | +| WhileStatements(int) -> void | 3 | 4 | 0 | @r3_4(bool) | +| min(int, int) -> int | 0 | 1 | 0 | @mu0_1(unknown) | +| min(int, int) -> int | 0 | 2 | 0 | @r0_2(int) | +| min(int, int) -> int | 0 | 3 | 0 | @r0_3(glval:int) | +| min(int, int) -> int | 0 | 4 | 0 | @m0_4(int) | +| min(int, int) -> int | 0 | 5 | 0 | @r0_5(int) | +| min(int, int) -> int | 0 | 6 | 0 | @r0_6(glval:int) | +| min(int, int) -> int | 0 | 7 | 0 | @m0_7(int) | +| min(int, int) -> int | 0 | 8 | 0 | @r0_8(glval:int) | +| min(int, int) -> int | 0 | 9 | 0 | @r0_9(glval:int) | +| min(int, int) -> int | 0 | 10 | 0 | @r0_10(int) | +| min(int, int) -> int | 0 | 11 | 0 | @r0_11(glval:int) | +| min(int, int) -> int | 0 | 12 | 0 | @r0_12(int) | +| min(int, int) -> int | 0 | 13 | 0 | @r0_13(bool) | +| min(int, int) -> int | 1 | 0 | 0 | @r1_0(glval:int) | +| min(int, int) -> int | 1 | 1 | 0 | @r1_1(int) | +| min(int, int) -> int | 1 | 2 | 0 | @r1_2(glval:int) | +| min(int, int) -> int | 1 | 3 | 0 | @m1_3(int) | +| min(int, int) -> int | 2 | 0 | 0 | @r2_0(glval:int) | +| min(int, int) -> int | 2 | 1 | 0 | @r2_1(int) | +| min(int, int) -> int | 2 | 2 | 0 | @r2_2(glval:int) | +| min(int, int) -> int | 2 | 3 | 0 | @m2_3(int) | +| min(int, int) -> int | 3 | 0 | 0 | @m3_0(int) | +| min(int, int) -> int | 3 | 1 | 0 | @r3_1(glval:int) | +| min(int, int) -> int | 3 | 2 | 0 | @r3_2(int) | +| min(int, int) -> int | 3 | 3 | 0 | @m3_3(int) | +| min(int, int) -> int | 3 | 4 | 0 | @r3_4(glval:int) | +printIRGraphSourceOperands +| AddressOf() -> int * | 0 | 4 | 0 | @r0_2 | +| AddressOf() -> int * | 0 | 4 | 1 | @r0_3 | +| AddressOf() -> int * | 0 | 6 | 0 | @r0_5 | +| AddressOf() -> int * | 0 | 6 | 5 | @m0_4 | +| AddressOf() -> int * | 0 | 7 | 0 | @mu* | +| ArrayAccess(int *, int) -> void | 0 | 4 | 0 | @r0_3 | +| ArrayAccess(int *, int) -> void | 0 | 4 | 1 | @r0_2 | +| ArrayAccess(int *, int) -> void | 0 | 7 | 0 | @r0_6 | +| ArrayAccess(int *, int) -> void | 0 | 7 | 1 | @r0_5 | +| ArrayAccess(int *, int) -> void | 0 | 10 | 0 | @r0_8 | +| ArrayAccess(int *, int) -> void | 0 | 10 | 1 | @r0_9 | +| ArrayAccess(int *, int) -> void | 0 | 12 | 0 | @r0_11 | +| ArrayAccess(int *, int) -> void | 0 | 12 | 1 | @m0_4 | +| ArrayAccess(int *, int) -> void | 0 | 14 | 0 | @r0_13 | +| ArrayAccess(int *, int) -> void | 0 | 14 | 1 | @m0_7 | +| ArrayAccess(int *, int) -> void | 0 | 15 | 3 | @r0_12 | +| ArrayAccess(int *, int) -> void | 0 | 15 | 4 | @r0_14 | +| ArrayAccess(int *, int) -> void | 0 | 16 | 0 | @r0_15 | +| ArrayAccess(int *, int) -> void | 0 | 16 | 1 | @mu0_1 | +| ArrayAccess(int *, int) -> void | 0 | 18 | 0 | @r0_17 | +| ArrayAccess(int *, int) -> void | 0 | 18 | 1 | @r0_16 | +| ArrayAccess(int *, int) -> void | 0 | 20 | 0 | @r0_19 | +| ArrayAccess(int *, int) -> void | 0 | 20 | 1 | @m0_4 | +| ArrayAccess(int *, int) -> void | 0 | 22 | 0 | @r0_21 | +| ArrayAccess(int *, int) -> void | 0 | 22 | 1 | @m0_7 | +| ArrayAccess(int *, int) -> void | 0 | 23 | 3 | @r0_20 | +| ArrayAccess(int *, int) -> void | 0 | 23 | 4 | @r0_22 | +| ArrayAccess(int *, int) -> void | 0 | 24 | 0 | @r0_23 | +| ArrayAccess(int *, int) -> void | 0 | 24 | 1 | @mu0_1 | +| ArrayAccess(int *, int) -> void | 0 | 26 | 0 | @r0_25 | +| ArrayAccess(int *, int) -> void | 0 | 26 | 1 | @r0_24 | +| ArrayAccess(int *, int) -> void | 0 | 28 | 0 | @r0_27 | +| ArrayAccess(int *, int) -> void | 0 | 28 | 1 | @m0_26 | +| ArrayAccess(int *, int) -> void | 0 | 30 | 0 | @r0_29 | +| ArrayAccess(int *, int) -> void | 0 | 30 | 1 | @m0_4 | +| ArrayAccess(int *, int) -> void | 0 | 32 | 0 | @r0_31 | +| ArrayAccess(int *, int) -> void | 0 | 32 | 1 | @m0_7 | +| ArrayAccess(int *, int) -> void | 0 | 33 | 3 | @r0_30 | +| ArrayAccess(int *, int) -> void | 0 | 33 | 4 | @r0_32 | +| ArrayAccess(int *, int) -> void | 0 | 34 | 0 | @r0_33 | +| ArrayAccess(int *, int) -> void | 0 | 34 | 1 | @r0_28 | +| ArrayAccess(int *, int) -> void | 0 | 36 | 0 | @r0_35 | +| ArrayAccess(int *, int) -> void | 0 | 36 | 1 | @m0_26 | +| ArrayAccess(int *, int) -> void | 0 | 38 | 0 | @r0_37 | +| ArrayAccess(int *, int) -> void | 0 | 38 | 1 | @m0_4 | +| ArrayAccess(int *, int) -> void | 0 | 40 | 0 | @r0_39 | +| ArrayAccess(int *, int) -> void | 0 | 40 | 1 | @m0_7 | +| ArrayAccess(int *, int) -> void | 0 | 41 | 3 | @r0_38 | +| ArrayAccess(int *, int) -> void | 0 | 41 | 4 | @r0_40 | +| ArrayAccess(int *, int) -> void | 0 | 42 | 0 | @r0_41 | +| ArrayAccess(int *, int) -> void | 0 | 42 | 1 | @r0_36 | +| ArrayAccess(int *, int) -> void | 0 | 45 | 0 | @r0_43 | +| ArrayAccess(int *, int) -> void | 0 | 45 | 1 | @r0_44 | +| ArrayAccess(int *, int) -> void | 0 | 47 | 2 | @r0_46 | +| ArrayAccess(int *, int) -> void | 0 | 49 | 0 | @r0_48 | +| ArrayAccess(int *, int) -> void | 0 | 49 | 1 | @m0_7 | +| ArrayAccess(int *, int) -> void | 0 | 50 | 3 | @r0_47 | +| ArrayAccess(int *, int) -> void | 0 | 50 | 4 | @r0_49 | +| ArrayAccess(int *, int) -> void | 0 | 51 | 0 | @r0_50 | +| ArrayAccess(int *, int) -> void | 0 | 51 | 1 | @mu0_1 | +| ArrayAccess(int *, int) -> void | 0 | 53 | 0 | @r0_52 | +| ArrayAccess(int *, int) -> void | 0 | 53 | 1 | @r0_51 | +| ArrayAccess(int *, int) -> void | 0 | 55 | 2 | @r0_54 | +| ArrayAccess(int *, int) -> void | 0 | 57 | 0 | @r0_56 | +| ArrayAccess(int *, int) -> void | 0 | 57 | 1 | @m0_7 | +| ArrayAccess(int *, int) -> void | 0 | 58 | 3 | @r0_55 | +| ArrayAccess(int *, int) -> void | 0 | 58 | 4 | @r0_57 | +| ArrayAccess(int *, int) -> void | 0 | 59 | 0 | @r0_58 | +| ArrayAccess(int *, int) -> void | 0 | 59 | 1 | @mu0_1 | +| ArrayAccess(int *, int) -> void | 0 | 61 | 0 | @r0_60 | +| ArrayAccess(int *, int) -> void | 0 | 61 | 1 | @r0_59 | +| ArrayAccess(int *, int) -> void | 0 | 63 | 0 | @r0_62 | +| ArrayAccess(int *, int) -> void | 0 | 63 | 1 | @m0_61 | +| ArrayAccess(int *, int) -> void | 0 | 65 | 2 | @r0_64 | +| ArrayAccess(int *, int) -> void | 0 | 67 | 0 | @r0_66 | +| ArrayAccess(int *, int) -> void | 0 | 67 | 1 | @m0_7 | +| ArrayAccess(int *, int) -> void | 0 | 68 | 3 | @r0_65 | +| ArrayAccess(int *, int) -> void | 0 | 68 | 4 | @r0_67 | +| ArrayAccess(int *, int) -> void | 0 | 69 | 0 | @r0_68 | +| ArrayAccess(int *, int) -> void | 0 | 69 | 1 | @r0_63 | +| ArrayAccess(int *, int) -> void | 0 | 71 | 0 | @r0_70 | +| ArrayAccess(int *, int) -> void | 0 | 71 | 1 | @m0_61 | +| ArrayAccess(int *, int) -> void | 0 | 73 | 2 | @r0_72 | +| ArrayAccess(int *, int) -> void | 0 | 75 | 0 | @r0_74 | +| ArrayAccess(int *, int) -> void | 0 | 75 | 1 | @m0_7 | +| ArrayAccess(int *, int) -> void | 0 | 76 | 3 | @r0_73 | +| ArrayAccess(int *, int) -> void | 0 | 76 | 4 | @r0_75 | +| ArrayAccess(int *, int) -> void | 0 | 77 | 0 | @r0_76 | +| ArrayAccess(int *, int) -> void | 0 | 77 | 1 | @r0_71 | +| ArrayAccess(int *, int) -> void | 0 | 80 | 0 | @mu* | +| ArrayConversions() -> void | 0 | 4 | 0 | @r0_2 | +| ArrayConversions() -> void | 0 | 4 | 1 | @r0_3 | +| ArrayConversions() -> void | 0 | 7 | 2 | @r0_6 | +| ArrayConversions() -> void | 0 | 8 | 2 | @r0_7 | +| ArrayConversions() -> void | 0 | 9 | 0 | @r0_5 | +| ArrayConversions() -> void | 0 | 9 | 1 | @r0_8 | +| ArrayConversions() -> void | 0 | 11 | 2 | @r0_10 | +| ArrayConversions() -> void | 0 | 13 | 0 | @r0_12 | +| ArrayConversions() -> void | 0 | 13 | 1 | @r0_11 | +| ArrayConversions() -> void | 0 | 15 | 2 | @r0_14 | +| ArrayConversions() -> void | 0 | 17 | 3 | @r0_15 | +| ArrayConversions() -> void | 0 | 17 | 4 | @r0_16 | +| ArrayConversions() -> void | 0 | 18 | 2 | @r0_17 | +| ArrayConversions() -> void | 0 | 20 | 0 | @r0_19 | +| ArrayConversions() -> void | 0 | 20 | 1 | @r0_18 | +| ArrayConversions() -> void | 0 | 22 | 2 | @r0_21 | +| ArrayConversions() -> void | 0 | 24 | 3 | @r0_22 | +| ArrayConversions() -> void | 0 | 24 | 4 | @r0_23 | +| ArrayConversions() -> void | 0 | 26 | 0 | @r0_25 | +| ArrayConversions() -> void | 0 | 26 | 1 | @r0_24 | +| ArrayConversions() -> void | 0 | 29 | 0 | @r0_27 | +| ArrayConversions() -> void | 0 | 29 | 1 | @r0_28 | +| ArrayConversions() -> void | 0 | 32 | 0 | @r0_30 | +| ArrayConversions() -> void | 0 | 32 | 1 | @r0_31 | +| ArrayConversions() -> void | 0 | 35 | 2 | @r0_34 | +| ArrayConversions() -> void | 0 | 36 | 0 | @r0_33 | +| ArrayConversions() -> void | 0 | 36 | 1 | @r0_35 | +| ArrayConversions() -> void | 0 | 39 | 0 | @r0_38 | +| ArrayConversions() -> void | 0 | 39 | 1 | @r0_37 | +| ArrayConversions() -> void | 0 | 42 | 0 | @mu* | +| ArrayInit(int, float) -> void | 0 | 4 | 0 | @r0_3 | +| ArrayInit(int, float) -> void | 0 | 4 | 1 | @r0_2 | +| ArrayInit(int, float) -> void | 0 | 7 | 0 | @r0_6 | +| ArrayInit(int, float) -> void | 0 | 7 | 1 | @r0_5 | +| ArrayInit(int, float) -> void | 0 | 10 | 3 | @r0_8 | +| ArrayInit(int, float) -> void | 0 | 10 | 4 | @r0_9 | +| ArrayInit(int, float) -> void | 0 | 12 | 0 | @r0_10 | +| ArrayInit(int, float) -> void | 0 | 12 | 1 | @r0_11 | +| ArrayInit(int, float) -> void | 0 | 15 | 3 | @r0_13 | +| ArrayInit(int, float) -> void | 0 | 15 | 4 | @r0_14 | +| ArrayInit(int, float) -> void | 0 | 17 | 0 | @r0_16 | +| ArrayInit(int, float) -> void | 0 | 17 | 1 | @m0_4 | +| ArrayInit(int, float) -> void | 0 | 18 | 0 | @r0_15 | +| ArrayInit(int, float) -> void | 0 | 18 | 1 | @r0_17 | +| ArrayInit(int, float) -> void | 0 | 20 | 3 | @r0_13 | +| ArrayInit(int, float) -> void | 0 | 20 | 4 | @r0_19 | +| ArrayInit(int, float) -> void | 0 | 22 | 0 | @r0_21 | +| ArrayInit(int, float) -> void | 0 | 22 | 1 | @m0_7 | +| ArrayInit(int, float) -> void | 0 | 23 | 2 | @r0_22 | +| ArrayInit(int, float) -> void | 0 | 24 | 0 | @r0_20 | +| ArrayInit(int, float) -> void | 0 | 24 | 1 | @r0_23 | +| ArrayInit(int, float) -> void | 0 | 26 | 3 | @r0_13 | +| ArrayInit(int, float) -> void | 0 | 26 | 4 | @r0_25 | +| ArrayInit(int, float) -> void | 0 | 28 | 0 | @r0_26 | +| ArrayInit(int, float) -> void | 0 | 28 | 1 | @r0_27 | +| ArrayInit(int, float) -> void | 0 | 31 | 3 | @r0_29 | +| ArrayInit(int, float) -> void | 0 | 31 | 4 | @r0_30 | +| ArrayInit(int, float) -> void | 0 | 33 | 0 | @r0_32 | +| ArrayInit(int, float) -> void | 0 | 33 | 1 | @m0_4 | +| ArrayInit(int, float) -> void | 0 | 34 | 0 | @r0_31 | +| ArrayInit(int, float) -> void | 0 | 34 | 1 | @r0_33 | +| ArrayInit(int, float) -> void | 0 | 36 | 3 | @r0_29 | +| ArrayInit(int, float) -> void | 0 | 36 | 4 | @r0_35 | +| ArrayInit(int, float) -> void | 0 | 38 | 0 | @r0_36 | +| ArrayInit(int, float) -> void | 0 | 38 | 1 | @r0_37 | +| ArrayInit(int, float) -> void | 0 | 41 | 0 | @mu* | +| ArrayReferences() -> void | 0 | 4 | 0 | @r0_2 | +| ArrayReferences() -> void | 0 | 4 | 1 | @r0_3 | +| ArrayReferences() -> void | 0 | 7 | 0 | @r0_5 | +| ArrayReferences() -> void | 0 | 7 | 1 | @r0_6 | +| ArrayReferences() -> void | 0 | 10 | 0 | @r0_9 | +| ArrayReferences() -> void | 0 | 10 | 1 | @m0_7 | +| ArrayReferences() -> void | 0 | 11 | 2 | @r0_10 | +| ArrayReferences() -> void | 0 | 13 | 3 | @r0_11 | +| ArrayReferences() -> void | 0 | 13 | 4 | @r0_12 | +| ArrayReferences() -> void | 0 | 14 | 0 | @r0_13 | +| ArrayReferences() -> void | 0 | 14 | 1 | @mu0_1 | +| ArrayReferences() -> void | 0 | 15 | 0 | @r0_8 | +| ArrayReferences() -> void | 0 | 15 | 1 | @r0_14 | +| ArrayReferences() -> void | 0 | 18 | 0 | @mu* | +| Base::Base() -> void | 0 | 3 | 2 | @r0_2 | +| Base::Base() -> void | 0 | 5 | 9 | @r0_4 | +| Base::Base() -> void | 0 | 5 | 10 | this:@r0_3 | +| Base::Base() -> void | 0 | 8 | 0 | @mu* | +| Base::Base(const Base &) -> void | 0 | 5 | 0 | @r0_4 | +| Base::Base(const Base &) -> void | 0 | 5 | 1 | @r0_3 | +| Base::Base(const Base &) -> void | 0 | 6 | 2 | @r0_2 | +| Base::Base(const Base &) -> void | 0 | 8 | 9 | @r0_7 | +| Base::Base(const Base &) -> void | 0 | 8 | 10 | this:@r0_6 | +| Base::Base(const Base &) -> void | 0 | 11 | 0 | @mu* | +| Base::operator=(const Base &) -> Base & | 0 | 5 | 0 | @r0_4 | +| Base::operator=(const Base &) -> Base & | 0 | 5 | 1 | @r0_3 | +| Base::operator=(const Base &) -> Base & | 0 | 6 | 1 | @r0_2 | +| Base::operator=(const Base &) -> Base & | 0 | 7 | 2 | @r0_6 | +| Base::operator=(const Base &) -> Base & | 0 | 10 | 0 | @r0_9 | +| Base::operator=(const Base &) -> Base & | 0 | 10 | 1 | @m0_5 | +| Base::operator=(const Base &) -> Base & | 0 | 11 | 2 | @r0_10 | +| Base::operator=(const Base &) -> Base & | 0 | 12 | 9 | @r0_8 | +| Base::operator=(const Base &) -> Base & | 0 | 12 | 10 | this:@r0_7 | +| Base::operator=(const Base &) -> Base & | 0 | 12 | 11 | @r0_11 | +| Base::operator=(const Base &) -> Base & | 0 | 14 | 1 | @r0_2 | +| Base::operator=(const Base &) -> Base & | 0 | 15 | 0 | @r0_13 | +| Base::operator=(const Base &) -> Base & | 0 | 15 | 1 | @r0_14 | +| Base::operator=(const Base &) -> Base & | 0 | 17 | 0 | @r0_16 | +| Base::operator=(const Base &) -> Base & | 0 | 17 | 5 | @m0_15 | +| Base::operator=(const Base &) -> Base & | 0 | 18 | 0 | @mu* | +| Base::~Base() -> void | 0 | 4 | 2 | @r0_2 | +| Base::~Base() -> void | 0 | 6 | 9 | @r0_5 | +| Base::~Base() -> void | 0 | 6 | 10 | this:@r0_4 | +| Base::~Base() -> void | 0 | 8 | 0 | @mu* | +| Break(int) -> void | 0 | 4 | 0 | @r0_3 | +| Break(int) -> void | 0 | 4 | 1 | @r0_2 | +| Break(int) -> void | 1 | 1 | 0 | @r1_0 | +| Break(int) -> void | 1 | 1 | 1 | @m5_0 | +| Break(int) -> void | 1 | 3 | 3 | @r1_1 | +| Break(int) -> void | 1 | 3 | 4 | @r1_2 | +| Break(int) -> void | 1 | 4 | 7 | @r1_3 | +| Break(int) -> void | 3 | 2 | 0 | @r3_1 | +| Break(int) -> void | 3 | 2 | 1 | @m5_0 | +| Break(int) -> void | 3 | 3 | 3 | @r3_2 | +| Break(int) -> void | 3 | 3 | 4 | @r3_0 | +| Break(int) -> void | 3 | 4 | 0 | @r3_1 | +| Break(int) -> void | 3 | 4 | 1 | @r3_3 | +| Break(int) -> void | 4 | 3 | 0 | @mu* | +| Break(int) -> void | 5 | 0 | 11 | from 0: @m0_4 | +| Break(int) -> void | 5 | 0 | 11 | from 3: @m3_4 | +| Break(int) -> void | 5 | 2 | 0 | @r5_1 | +| Break(int) -> void | 5 | 2 | 1 | @m5_0 | +| Break(int) -> void | 5 | 4 | 3 | @r5_2 | +| Break(int) -> void | 5 | 4 | 4 | @r5_3 | +| Break(int) -> void | 5 | 5 | 7 | @r5_4 | +| C::C() -> void | 0 | 3 | 2 | @r0_2 | +| C::C() -> void | 0 | 5 | 0 | @r0_3 | +| C::C() -> void | 0 | 5 | 1 | @r0_4 | +| C::C() -> void | 0 | 6 | 2 | @r0_2 | +| C::C() -> void | 0 | 8 | 9 | @r0_7 | +| C::C() -> void | 0 | 8 | 10 | this:@r0_6 | +| C::C() -> void | 0 | 9 | 2 | @r0_2 | +| C::C() -> void | 0 | 11 | 0 | @r0_9 | +| C::C() -> void | 0 | 11 | 1 | @r0_10 | +| C::C() -> void | 0 | 12 | 2 | @r0_2 | +| C::C() -> void | 0 | 14 | 0 | @r0_12 | +| C::C() -> void | 0 | 14 | 1 | @r0_13 | +| C::C() -> void | 0 | 15 | 2 | @r0_2 | +| C::C() -> void | 0 | 18 | 2 | @r0_17 | +| C::C() -> void | 0 | 19 | 9 | @r0_16 | +| C::C() -> void | 0 | 19 | 10 | this:@r0_15 | +| C::C() -> void | 0 | 19 | 11 | @r0_18 | +| C::C() -> void | 0 | 22 | 0 | @mu* | +| C::FieldAccess() -> void | 0 | 4 | 1 | @r0_2 | +| C::FieldAccess() -> void | 0 | 5 | 2 | @r0_4 | +| C::FieldAccess() -> void | 0 | 6 | 0 | @r0_5 | +| C::FieldAccess() -> void | 0 | 6 | 1 | @r0_3 | +| C::FieldAccess() -> void | 0 | 8 | 1 | @r0_2 | +| C::FieldAccess() -> void | 0 | 9 | 2 | @r0_8 | +| C::FieldAccess() -> void | 0 | 10 | 0 | @r0_9 | +| C::FieldAccess() -> void | 0 | 10 | 1 | @r0_7 | +| C::FieldAccess() -> void | 0 | 12 | 1 | @r0_2 | +| C::FieldAccess() -> void | 0 | 13 | 2 | @r0_12 | +| C::FieldAccess() -> void | 0 | 14 | 0 | @r0_13 | +| C::FieldAccess() -> void | 0 | 14 | 1 | @r0_11 | +| C::FieldAccess() -> void | 0 | 17 | 0 | @r0_15 | +| C::FieldAccess() -> void | 0 | 17 | 1 | @r0_16 | +| C::FieldAccess() -> void | 0 | 18 | 1 | @r0_2 | +| C::FieldAccess() -> void | 0 | 19 | 2 | @r0_18 | +| C::FieldAccess() -> void | 0 | 20 | 0 | @r0_19 | +| C::FieldAccess() -> void | 0 | 20 | 1 | @mu0_1 | +| C::FieldAccess() -> void | 0 | 22 | 0 | @r0_21 | +| C::FieldAccess() -> void | 0 | 22 | 1 | @r0_20 | +| C::FieldAccess() -> void | 0 | 23 | 1 | @r0_2 | +| C::FieldAccess() -> void | 0 | 24 | 2 | @r0_23 | +| C::FieldAccess() -> void | 0 | 25 | 0 | @r0_24 | +| C::FieldAccess() -> void | 0 | 25 | 1 | @mu0_1 | +| C::FieldAccess() -> void | 0 | 27 | 0 | @r0_26 | +| C::FieldAccess() -> void | 0 | 27 | 1 | @r0_25 | +| C::FieldAccess() -> void | 0 | 28 | 1 | @r0_2 | +| C::FieldAccess() -> void | 0 | 29 | 2 | @r0_28 | +| C::FieldAccess() -> void | 0 | 30 | 0 | @r0_29 | +| C::FieldAccess() -> void | 0 | 30 | 1 | @mu0_1 | +| C::FieldAccess() -> void | 0 | 32 | 0 | @r0_31 | +| C::FieldAccess() -> void | 0 | 32 | 1 | @r0_30 | +| C::FieldAccess() -> void | 0 | 35 | 0 | @mu* | +| C::InstanceMemberFunction(int) -> int | 0 | 5 | 0 | @r0_4 | +| C::InstanceMemberFunction(int) -> int | 0 | 5 | 1 | @r0_3 | +| C::InstanceMemberFunction(int) -> int | 0 | 8 | 0 | @r0_7 | +| C::InstanceMemberFunction(int) -> int | 0 | 8 | 1 | @m0_5 | +| C::InstanceMemberFunction(int) -> int | 0 | 9 | 0 | @r0_6 | +| C::InstanceMemberFunction(int) -> int | 0 | 9 | 1 | @r0_8 | +| C::InstanceMemberFunction(int) -> int | 0 | 11 | 0 | @r0_10 | +| C::InstanceMemberFunction(int) -> int | 0 | 11 | 5 | @m0_9 | +| C::InstanceMemberFunction(int) -> int | 0 | 12 | 0 | @mu* | +| C::MethodCalls() -> void | 0 | 3 | 1 | @r0_2 | +| C::MethodCalls() -> void | 0 | 6 | 9 | @r0_4 | +| C::MethodCalls() -> void | 0 | 6 | 10 | this:@r0_3 | +| C::MethodCalls() -> void | 0 | 6 | 11 | @r0_5 | +| C::MethodCalls() -> void | 0 | 7 | 1 | @r0_2 | +| C::MethodCalls() -> void | 0 | 10 | 9 | @r0_8 | +| C::MethodCalls() -> void | 0 | 10 | 10 | this:@r0_7 | +| C::MethodCalls() -> void | 0 | 10 | 11 | @r0_9 | +| C::MethodCalls() -> void | 0 | 11 | 1 | @r0_2 | +| C::MethodCalls() -> void | 0 | 14 | 9 | @r0_12 | +| C::MethodCalls() -> void | 0 | 14 | 10 | this:@r0_11 | +| C::MethodCalls() -> void | 0 | 14 | 11 | @r0_13 | +| C::MethodCalls() -> void | 0 | 17 | 0 | @mu* | +| C::StaticMemberFunction(int) -> int | 0 | 4 | 0 | @r0_3 | +| C::StaticMemberFunction(int) -> int | 0 | 4 | 1 | @r0_2 | +| C::StaticMemberFunction(int) -> int | 0 | 7 | 0 | @r0_6 | +| C::StaticMemberFunction(int) -> int | 0 | 7 | 1 | @m0_4 | +| C::StaticMemberFunction(int) -> int | 0 | 8 | 0 | @r0_5 | +| C::StaticMemberFunction(int) -> int | 0 | 8 | 1 | @r0_7 | +| C::StaticMemberFunction(int) -> int | 0 | 10 | 0 | @r0_9 | +| C::StaticMemberFunction(int) -> int | 0 | 10 | 5 | @m0_8 | +| C::StaticMemberFunction(int) -> int | 0 | 11 | 0 | @mu* | +| C::VirtualMemberFunction(int) -> int | 0 | 5 | 0 | @r0_4 | +| C::VirtualMemberFunction(int) -> int | 0 | 5 | 1 | @r0_3 | +| C::VirtualMemberFunction(int) -> int | 0 | 8 | 0 | @r0_7 | +| C::VirtualMemberFunction(int) -> int | 0 | 8 | 1 | @m0_5 | +| C::VirtualMemberFunction(int) -> int | 0 | 9 | 0 | @r0_6 | +| C::VirtualMemberFunction(int) -> int | 0 | 9 | 1 | @r0_8 | +| C::VirtualMemberFunction(int) -> int | 0 | 11 | 0 | @r0_10 | +| C::VirtualMemberFunction(int) -> int | 0 | 11 | 5 | @m0_9 | +| C::VirtualMemberFunction(int) -> int | 0 | 12 | 0 | @mu* | +| Call() -> void | 0 | 3 | 9 | @r0_2 | +| Call() -> void | 0 | 6 | 0 | @mu* | +| CallAdd(int, int) -> int | 0 | 4 | 0 | @r0_3 | +| CallAdd(int, int) -> int | 0 | 4 | 1 | @r0_2 | +| CallAdd(int, int) -> int | 0 | 7 | 0 | @r0_6 | +| CallAdd(int, int) -> int | 0 | 7 | 1 | @r0_5 | +| CallAdd(int, int) -> int | 0 | 11 | 0 | @r0_10 | +| CallAdd(int, int) -> int | 0 | 11 | 1 | @m0_4 | +| CallAdd(int, int) -> int | 0 | 13 | 0 | @r0_12 | +| CallAdd(int, int) -> int | 0 | 13 | 1 | @m0_7 | +| CallAdd(int, int) -> int | 0 | 14 | 9 | @r0_9 | +| CallAdd(int, int) -> int | 0 | 14 | 11 | @r0_11 | +| CallAdd(int, int) -> int | 0 | 14 | 12 | @r0_13 | +| CallAdd(int, int) -> int | 0 | 15 | 0 | @r0_8 | +| CallAdd(int, int) -> int | 0 | 15 | 1 | @r0_14 | +| CallAdd(int, int) -> int | 0 | 17 | 0 | @r0_16 | +| CallAdd(int, int) -> int | 0 | 17 | 5 | @m0_15 | +| CallAdd(int, int) -> int | 0 | 18 | 0 | @mu* | +| CallMethods(String &, String *, String) -> void | 0 | 4 | 0 | @r0_3 | +| CallMethods(String &, String *, String) -> void | 0 | 4 | 1 | @r0_2 | +| CallMethods(String &, String *, String) -> void | 0 | 7 | 0 | @r0_6 | +| CallMethods(String &, String *, String) -> void | 0 | 7 | 1 | @r0_5 | +| CallMethods(String &, String *, String) -> void | 0 | 10 | 0 | @r0_9 | +| CallMethods(String &, String *, String) -> void | 0 | 10 | 1 | @r0_8 | +| CallMethods(String &, String *, String) -> void | 0 | 12 | 0 | @r0_11 | +| CallMethods(String &, String *, String) -> void | 0 | 12 | 1 | @m0_4 | +| CallMethods(String &, String *, String) -> void | 0 | 13 | 2 | @r0_12 | +| CallMethods(String &, String *, String) -> void | 0 | 15 | 9 | @r0_14 | +| CallMethods(String &, String *, String) -> void | 0 | 15 | 10 | this:@r0_13 | +| CallMethods(String &, String *, String) -> void | 0 | 17 | 0 | @r0_16 | +| CallMethods(String &, String *, String) -> void | 0 | 17 | 1 | @m0_7 | +| CallMethods(String &, String *, String) -> void | 0 | 18 | 2 | @r0_17 | +| CallMethods(String &, String *, String) -> void | 0 | 20 | 9 | @r0_19 | +| CallMethods(String &, String *, String) -> void | 0 | 20 | 10 | this:@r0_18 | +| CallMethods(String &, String *, String) -> void | 0 | 22 | 2 | @r0_21 | +| CallMethods(String &, String *, String) -> void | 0 | 24 | 9 | @r0_23 | +| CallMethods(String &, String *, String) -> void | 0 | 24 | 10 | this:@r0_22 | +| CallMethods(String &, String *, String) -> void | 0 | 27 | 0 | @mu* | +| CallMin(int, int) -> int | 0 | 4 | 0 | @r0_3 | +| CallMin(int, int) -> int | 0 | 4 | 1 | @r0_2 | +| CallMin(int, int) -> int | 0 | 7 | 0 | @r0_6 | +| CallMin(int, int) -> int | 0 | 7 | 1 | @r0_5 | +| CallMin(int, int) -> int | 0 | 11 | 0 | @r0_10 | +| CallMin(int, int) -> int | 0 | 11 | 1 | @m0_4 | +| CallMin(int, int) -> int | 0 | 13 | 0 | @r0_12 | +| CallMin(int, int) -> int | 0 | 13 | 1 | @m0_7 | +| CallMin(int, int) -> int | 0 | 14 | 9 | @r0_9 | +| CallMin(int, int) -> int | 0 | 14 | 11 | @r0_11 | +| CallMin(int, int) -> int | 0 | 14 | 12 | @r0_13 | +| CallMin(int, int) -> int | 0 | 15 | 0 | @r0_8 | +| CallMin(int, int) -> int | 0 | 15 | 1 | @r0_14 | +| CallMin(int, int) -> int | 0 | 17 | 0 | @r0_16 | +| CallMin(int, int) -> int | 0 | 17 | 5 | @m0_15 | +| CallMin(int, int) -> int | 0 | 18 | 0 | @mu* | +| CallNestedTemplateFunc() -> double | 0 | 6 | 9 | @r0_3 | +| CallNestedTemplateFunc() -> double | 0 | 6 | 11 | @r0_4 | +| CallNestedTemplateFunc() -> double | 0 | 6 | 12 | @r0_5 | +| CallNestedTemplateFunc() -> double | 0 | 7 | 2 | @r0_6 | +| CallNestedTemplateFunc() -> double | 0 | 8 | 0 | @r0_2 | +| CallNestedTemplateFunc() -> double | 0 | 8 | 1 | @r0_7 | +| CallNestedTemplateFunc() -> double | 0 | 10 | 0 | @r0_9 | +| CallNestedTemplateFunc() -> double | 0 | 10 | 5 | @m0_8 | +| CallNestedTemplateFunc() -> double | 0 | 11 | 0 | @mu* | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 4 | 0 | @r0_3 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 4 | 1 | @r0_2 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 7 | 0 | @r0_6 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 7 | 1 | @m0_4 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 9 | 9 | @r0_7 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 9 | 11 | @r0_8 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 10 | 0 | @r0_5 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 10 | 1 | @r0_9 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 12 | 0 | @r0_11 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 12 | 5 | @m0_10 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 13 | 0 | @mu* | +| Comma(int, int) -> int | 0 | 4 | 0 | @r0_3 | +| Comma(int, int) -> int | 0 | 4 | 1 | @r0_2 | +| Comma(int, int) -> int | 0 | 7 | 0 | @r0_6 | +| Comma(int, int) -> int | 0 | 7 | 1 | @r0_5 | +| Comma(int, int) -> int | 0 | 10 | 9 | @r0_9 | +| Comma(int, int) -> int | 0 | 13 | 0 | @r0_12 | +| Comma(int, int) -> int | 0 | 13 | 1 | @m0_4 | +| Comma(int, int) -> int | 0 | 15 | 0 | @r0_14 | +| Comma(int, int) -> int | 0 | 15 | 1 | @m0_7 | +| Comma(int, int) -> int | 0 | 16 | 9 | @r0_11 | +| Comma(int, int) -> int | 0 | 16 | 11 | @r0_13 | +| Comma(int, int) -> int | 0 | 16 | 12 | @r0_15 | +| Comma(int, int) -> int | 0 | 17 | 0 | @r0_8 | +| Comma(int, int) -> int | 0 | 17 | 1 | @r0_16 | +| Comma(int, int) -> int | 0 | 19 | 0 | @r0_18 | +| Comma(int, int) -> int | 0 | 19 | 5 | @m0_17 | +| Comma(int, int) -> int | 0 | 20 | 0 | @mu* | +| CompoundAssignment() -> void | 0 | 4 | 0 | @r0_2 | +| CompoundAssignment() -> void | 0 | 4 | 1 | @r0_3 | +| CompoundAssignment() -> void | 0 | 7 | 0 | @r0_6 | +| CompoundAssignment() -> void | 0 | 7 | 1 | @m0_4 | +| CompoundAssignment() -> void | 0 | 8 | 3 | @r0_7 | +| CompoundAssignment() -> void | 0 | 8 | 4 | @r0_5 | +| CompoundAssignment() -> void | 0 | 9 | 0 | @r0_6 | +| CompoundAssignment() -> void | 0 | 9 | 1 | @r0_8 | +| CompoundAssignment() -> void | 0 | 12 | 0 | @r0_10 | +| CompoundAssignment() -> void | 0 | 12 | 1 | @r0_11 | +| CompoundAssignment() -> void | 0 | 14 | 0 | @r0_13 | +| CompoundAssignment() -> void | 0 | 14 | 1 | @m0_9 | +| CompoundAssignment() -> void | 0 | 16 | 0 | @r0_15 | +| CompoundAssignment() -> void | 0 | 16 | 1 | @m0_12 | +| CompoundAssignment() -> void | 0 | 17 | 2 | @r0_16 | +| CompoundAssignment() -> void | 0 | 18 | 3 | @r0_17 | +| CompoundAssignment() -> void | 0 | 18 | 4 | @r0_14 | +| CompoundAssignment() -> void | 0 | 19 | 2 | @r0_18 | +| CompoundAssignment() -> void | 0 | 20 | 0 | @r0_15 | +| CompoundAssignment() -> void | 0 | 20 | 1 | @r0_19 | +| CompoundAssignment() -> void | 0 | 23 | 0 | @r0_22 | +| CompoundAssignment() -> void | 0 | 23 | 1 | @m0_20 | +| CompoundAssignment() -> void | 0 | 24 | 3 | @r0_23 | +| CompoundAssignment() -> void | 0 | 24 | 4 | @r0_21 | +| CompoundAssignment() -> void | 0 | 25 | 0 | @r0_22 | +| CompoundAssignment() -> void | 0 | 25 | 1 | @r0_24 | +| CompoundAssignment() -> void | 0 | 28 | 0 | @r0_26 | +| CompoundAssignment() -> void | 0 | 28 | 1 | @r0_27 | +| CompoundAssignment() -> void | 0 | 31 | 0 | @r0_30 | +| CompoundAssignment() -> void | 0 | 31 | 1 | @m0_28 | +| CompoundAssignment() -> void | 0 | 32 | 2 | @r0_31 | +| CompoundAssignment() -> void | 0 | 33 | 3 | @r0_32 | +| CompoundAssignment() -> void | 0 | 33 | 4 | @r0_29 | +| CompoundAssignment() -> void | 0 | 34 | 2 | @r0_33 | +| CompoundAssignment() -> void | 0 | 35 | 0 | @r0_30 | +| CompoundAssignment() -> void | 0 | 35 | 1 | @r0_34 | +| CompoundAssignment() -> void | 0 | 38 | 0 | @mu* | +| ConditionValues(bool, bool) -> void | 0 | 4 | 0 | @r0_3 | +| ConditionValues(bool, bool) -> void | 0 | 4 | 1 | @r0_2 | +| ConditionValues(bool, bool) -> void | 0 | 7 | 0 | @r0_6 | +| ConditionValues(bool, bool) -> void | 0 | 7 | 1 | @r0_5 | +| ConditionValues(bool, bool) -> void | 0 | 10 | 0 | @r0_8 | +| ConditionValues(bool, bool) -> void | 0 | 10 | 1 | @r0_9 | +| ConditionValues(bool, bool) -> void | 0 | 12 | 0 | @r0_11 | +| ConditionValues(bool, bool) -> void | 0 | 12 | 1 | @m0_4 | +| ConditionValues(bool, bool) -> void | 0 | 13 | 7 | @r0_12 | +| ConditionValues(bool, bool) -> void | 1 | 1 | 0 | @r1_0 | +| ConditionValues(bool, bool) -> void | 1 | 1 | 1 | @m0_7 | +| ConditionValues(bool, bool) -> void | 1 | 2 | 7 | @r1_1 | +| ConditionValues(bool, bool) -> void | 2 | 2 | 0 | @r2_0 | +| ConditionValues(bool, bool) -> void | 2 | 2 | 1 | @r2_1 | +| ConditionValues(bool, bool) -> void | 3 | 0 | 11 | from 2: @m2_2 | +| ConditionValues(bool, bool) -> void | 3 | 0 | 11 | from 4: @m4_2 | +| ConditionValues(bool, bool) -> void | 3 | 2 | 0 | @r3_1 | +| ConditionValues(bool, bool) -> void | 3 | 2 | 1 | @m3_0 | +| ConditionValues(bool, bool) -> void | 3 | 4 | 0 | @r3_3 | +| ConditionValues(bool, bool) -> void | 3 | 4 | 1 | @r3_2 | +| ConditionValues(bool, bool) -> void | 3 | 6 | 0 | @r3_5 | +| ConditionValues(bool, bool) -> void | 3 | 6 | 1 | @m0_4 | +| ConditionValues(bool, bool) -> void | 3 | 7 | 7 | @r3_6 | +| ConditionValues(bool, bool) -> void | 4 | 2 | 0 | @r4_0 | +| ConditionValues(bool, bool) -> void | 4 | 2 | 1 | @r4_1 | +| ConditionValues(bool, bool) -> void | 5 | 1 | 0 | @r5_0 | +| ConditionValues(bool, bool) -> void | 5 | 1 | 1 | @m0_7 | +| ConditionValues(bool, bool) -> void | 5 | 2 | 7 | @r5_1 | +| ConditionValues(bool, bool) -> void | 6 | 2 | 0 | @r6_0 | +| ConditionValues(bool, bool) -> void | 6 | 2 | 1 | @r6_1 | +| ConditionValues(bool, bool) -> void | 7 | 0 | 11 | from 6: @m6_2 | +| ConditionValues(bool, bool) -> void | 7 | 0 | 11 | from 8: @m8_2 | +| ConditionValues(bool, bool) -> void | 7 | 2 | 0 | @r7_1 | +| ConditionValues(bool, bool) -> void | 7 | 2 | 1 | @m7_0 | +| ConditionValues(bool, bool) -> void | 7 | 3 | 2 | @r7_2 | +| ConditionValues(bool, bool) -> void | 7 | 5 | 0 | @r7_4 | +| ConditionValues(bool, bool) -> void | 7 | 5 | 1 | @r7_3 | +| ConditionValues(bool, bool) -> void | 7 | 8 | 0 | @mu* | +| ConditionValues(bool, bool) -> void | 8 | 2 | 0 | @r8_0 | +| ConditionValues(bool, bool) -> void | 8 | 2 | 1 | @r8_1 | +| ConditionValues(bool, bool) -> void | 9 | 1 | 0 | @r9_0 | +| ConditionValues(bool, bool) -> void | 9 | 1 | 1 | @m0_7 | +| ConditionValues(bool, bool) -> void | 9 | 2 | 7 | @r9_1 | +| ConditionValues(bool, bool) -> void | 10 | 2 | 0 | @r10_0 | +| ConditionValues(bool, bool) -> void | 10 | 2 | 1 | @r10_1 | +| ConditionValues(bool, bool) -> void | 11 | 0 | 11 | from 10: @m10_2 | +| ConditionValues(bool, bool) -> void | 11 | 0 | 11 | from 12: @m12_2 | +| ConditionValues(bool, bool) -> void | 11 | 2 | 0 | @r11_1 | +| ConditionValues(bool, bool) -> void | 11 | 2 | 1 | @m11_0 | +| ConditionValues(bool, bool) -> void | 11 | 4 | 0 | @r11_3 | +| ConditionValues(bool, bool) -> void | 11 | 4 | 1 | @r11_2 | +| ConditionValues(bool, bool) -> void | 11 | 6 | 0 | @r11_5 | +| ConditionValues(bool, bool) -> void | 11 | 6 | 1 | @m0_4 | +| ConditionValues(bool, bool) -> void | 11 | 7 | 7 | @r11_6 | +| ConditionValues(bool, bool) -> void | 12 | 2 | 0 | @r12_0 | +| ConditionValues(bool, bool) -> void | 12 | 2 | 1 | @r12_1 | +| Conditional(bool, int, int) -> void | 0 | 4 | 0 | @r0_3 | +| Conditional(bool, int, int) -> void | 0 | 4 | 1 | @r0_2 | +| Conditional(bool, int, int) -> void | 0 | 7 | 0 | @r0_6 | +| Conditional(bool, int, int) -> void | 0 | 7 | 1 | @r0_5 | +| Conditional(bool, int, int) -> void | 0 | 10 | 0 | @r0_9 | +| Conditional(bool, int, int) -> void | 0 | 10 | 1 | @r0_8 | +| Conditional(bool, int, int) -> void | 0 | 13 | 0 | @r0_12 | +| Conditional(bool, int, int) -> void | 0 | 13 | 1 | @m0_4 | +| Conditional(bool, int, int) -> void | 0 | 14 | 7 | @r0_13 | +| Conditional(bool, int, int) -> void | 1 | 1 | 0 | @r1_0 | +| Conditional(bool, int, int) -> void | 1 | 1 | 1 | @m0_7 | +| Conditional(bool, int, int) -> void | 1 | 3 | 0 | @r1_2 | +| Conditional(bool, int, int) -> void | 1 | 3 | 1 | @r1_1 | +| Conditional(bool, int, int) -> void | 2 | 1 | 0 | @r2_0 | +| Conditional(bool, int, int) -> void | 2 | 1 | 1 | @m0_10 | +| Conditional(bool, int, int) -> void | 2 | 3 | 0 | @r2_2 | +| Conditional(bool, int, int) -> void | 2 | 3 | 1 | @r2_1 | +| Conditional(bool, int, int) -> void | 3 | 0 | 11 | from 1: @m1_3 | +| Conditional(bool, int, int) -> void | 3 | 0 | 11 | from 2: @m2_3 | +| Conditional(bool, int, int) -> void | 3 | 2 | 0 | @r3_1 | +| Conditional(bool, int, int) -> void | 3 | 2 | 1 | @m3_0 | +| Conditional(bool, int, int) -> void | 3 | 3 | 0 | @r0_11 | +| Conditional(bool, int, int) -> void | 3 | 3 | 1 | @r3_2 | +| Conditional(bool, int, int) -> void | 3 | 6 | 0 | @mu* | +| Conditional_LValue(bool) -> void | 0 | 4 | 0 | @r0_3 | +| Conditional_LValue(bool) -> void | 0 | 4 | 1 | @r0_2 | +| Conditional_LValue(bool) -> void | 0 | 7 | 0 | @r0_5 | +| Conditional_LValue(bool) -> void | 0 | 7 | 1 | @r0_6 | +| Conditional_LValue(bool) -> void | 0 | 10 | 0 | @r0_8 | +| Conditional_LValue(bool) -> void | 0 | 10 | 1 | @r0_9 | +| Conditional_LValue(bool) -> void | 0 | 13 | 0 | @r0_12 | +| Conditional_LValue(bool) -> void | 0 | 13 | 1 | @m0_4 | +| Conditional_LValue(bool) -> void | 0 | 14 | 7 | @r0_13 | +| Conditional_LValue(bool) -> void | 1 | 0 | 11 | from 2: @m2_2 | +| Conditional_LValue(bool) -> void | 1 | 0 | 11 | from 3: @m3_2 | +| Conditional_LValue(bool) -> void | 1 | 2 | 0 | @r1_1 | +| Conditional_LValue(bool) -> void | 1 | 2 | 1 | @m1_0 | +| Conditional_LValue(bool) -> void | 1 | 3 | 0 | @r1_2 | +| Conditional_LValue(bool) -> void | 1 | 3 | 1 | @r0_11 | +| Conditional_LValue(bool) -> void | 1 | 6 | 0 | @mu* | +| Conditional_LValue(bool) -> void | 2 | 2 | 0 | @r2_1 | +| Conditional_LValue(bool) -> void | 2 | 2 | 1 | @r2_0 | +| Conditional_LValue(bool) -> void | 3 | 2 | 0 | @r3_1 | +| Conditional_LValue(bool) -> void | 3 | 2 | 1 | @r3_0 | +| Conditional_Void(bool) -> void | 0 | 4 | 0 | @r0_3 | +| Conditional_Void(bool) -> void | 0 | 4 | 1 | @r0_2 | +| Conditional_Void(bool) -> void | 0 | 6 | 0 | @r0_5 | +| Conditional_Void(bool) -> void | 0 | 6 | 1 | @m0_4 | +| Conditional_Void(bool) -> void | 0 | 7 | 7 | @r0_6 | +| Conditional_Void(bool) -> void | 1 | 2 | 0 | @mu* | +| Conditional_Void(bool) -> void | 2 | 1 | 9 | @r2_0 | +| Conditional_Void(bool) -> void | 3 | 1 | 9 | @r3_0 | +| Constants() -> void | 0 | 4 | 0 | @r0_2 | +| Constants() -> void | 0 | 4 | 1 | @r0_3 | +| Constants() -> void | 0 | 7 | 0 | @r0_5 | +| Constants() -> void | 0 | 7 | 1 | @r0_6 | +| Constants() -> void | 0 | 10 | 0 | @r0_8 | +| Constants() -> void | 0 | 10 | 1 | @r0_9 | +| Constants() -> void | 0 | 13 | 0 | @r0_11 | +| Constants() -> void | 0 | 13 | 1 | @r0_12 | +| Constants() -> void | 0 | 16 | 0 | @r0_14 | +| Constants() -> void | 0 | 16 | 1 | @r0_15 | +| Constants() -> void | 0 | 19 | 0 | @r0_17 | +| Constants() -> void | 0 | 19 | 1 | @r0_18 | +| Constants() -> void | 0 | 22 | 0 | @r0_20 | +| Constants() -> void | 0 | 22 | 1 | @r0_21 | +| Constants() -> void | 0 | 25 | 0 | @r0_23 | +| Constants() -> void | 0 | 25 | 1 | @r0_24 | +| Constants() -> void | 0 | 28 | 0 | @r0_26 | +| Constants() -> void | 0 | 28 | 1 | @r0_27 | +| Constants() -> void | 0 | 31 | 0 | @r0_29 | +| Constants() -> void | 0 | 31 | 1 | @r0_30 | +| Constants() -> void | 0 | 34 | 0 | @r0_32 | +| Constants() -> void | 0 | 34 | 1 | @r0_33 | +| Constants() -> void | 0 | 37 | 0 | @r0_35 | +| Constants() -> void | 0 | 37 | 1 | @r0_36 | +| Constants() -> void | 0 | 40 | 0 | @r0_38 | +| Constants() -> void | 0 | 40 | 1 | @r0_39 | +| Constants() -> void | 0 | 43 | 0 | @r0_41 | +| Constants() -> void | 0 | 43 | 1 | @r0_42 | +| Constants() -> void | 0 | 46 | 0 | @r0_44 | +| Constants() -> void | 0 | 46 | 1 | @r0_45 | +| Constants() -> void | 0 | 49 | 0 | @r0_47 | +| Constants() -> void | 0 | 49 | 1 | @r0_48 | +| Constants() -> void | 0 | 52 | 0 | @r0_50 | +| Constants() -> void | 0 | 52 | 1 | @r0_51 | +| Constants() -> void | 0 | 55 | 0 | @r0_53 | +| Constants() -> void | 0 | 55 | 1 | @r0_54 | +| Constants() -> void | 0 | 58 | 0 | @r0_56 | +| Constants() -> void | 0 | 58 | 1 | @r0_57 | +| Constants() -> void | 0 | 61 | 0 | @r0_59 | +| Constants() -> void | 0 | 61 | 1 | @r0_60 | +| Constants() -> void | 0 | 64 | 0 | @r0_62 | +| Constants() -> void | 0 | 64 | 1 | @r0_63 | +| Constants() -> void | 0 | 67 | 0 | @r0_65 | +| Constants() -> void | 0 | 67 | 1 | @r0_66 | +| Constants() -> void | 0 | 70 | 0 | @r0_68 | +| Constants() -> void | 0 | 70 | 1 | @r0_69 | +| Constants() -> void | 0 | 73 | 0 | @r0_71 | +| Constants() -> void | 0 | 73 | 1 | @r0_72 | +| Constants() -> void | 0 | 76 | 0 | @r0_74 | +| Constants() -> void | 0 | 76 | 1 | @r0_75 | +| Constants() -> void | 0 | 79 | 0 | @r0_77 | +| Constants() -> void | 0 | 79 | 1 | @r0_78 | +| Constants() -> void | 0 | 82 | 0 | @r0_80 | +| Constants() -> void | 0 | 82 | 1 | @r0_81 | +| Constants() -> void | 0 | 85 | 0 | @r0_83 | +| Constants() -> void | 0 | 85 | 1 | @r0_84 | +| Constants() -> void | 0 | 88 | 0 | @mu* | +| Continue(int) -> void | 0 | 4 | 0 | @r0_3 | +| Continue(int) -> void | 0 | 4 | 1 | @r0_2 | +| Continue(int) -> void | 1 | 0 | 11 | from 0: @m0_4 | +| Continue(int) -> void | 1 | 0 | 11 | from 4: @m4_0 | +| Continue(int) -> void | 1 | 2 | 0 | @r1_1 | +| Continue(int) -> void | 1 | 2 | 1 | @m1_0 | +| Continue(int) -> void | 1 | 4 | 3 | @r1_2 | +| Continue(int) -> void | 1 | 4 | 4 | @r1_3 | +| Continue(int) -> void | 1 | 5 | 7 | @r1_4 | +| Continue(int) -> void | 3 | 2 | 0 | @r3_1 | +| Continue(int) -> void | 3 | 2 | 1 | @m1_0 | +| Continue(int) -> void | 3 | 3 | 3 | @r3_2 | +| Continue(int) -> void | 3 | 3 | 4 | @r3_0 | +| Continue(int) -> void | 3 | 4 | 0 | @r3_1 | +| Continue(int) -> void | 3 | 4 | 1 | @r3_3 | +| Continue(int) -> void | 4 | 0 | 11 | from 2: @m1_0 | +| Continue(int) -> void | 4 | 0 | 11 | from 3: @m3_4 | +| Continue(int) -> void | 4 | 3 | 0 | @r4_2 | +| Continue(int) -> void | 4 | 3 | 1 | @m4_0 | +| Continue(int) -> void | 4 | 5 | 3 | @r4_3 | +| Continue(int) -> void | 4 | 5 | 4 | @r4_4 | +| Continue(int) -> void | 4 | 6 | 7 | @r4_5 | +| Continue(int) -> void | 5 | 2 | 0 | @mu* | +| DeclareObject() -> void | 0 | 4 | 9 | @r0_3 | +| DeclareObject() -> void | 0 | 4 | 10 | this:@r0_2 | +| DeclareObject() -> void | 0 | 8 | 2 | @r0_7 | +| DeclareObject() -> void | 0 | 9 | 9 | @r0_6 | +| DeclareObject() -> void | 0 | 9 | 10 | this:@r0_5 | +| DeclareObject() -> void | 0 | 9 | 11 | @r0_8 | +| DeclareObject() -> void | 0 | 12 | 9 | @r0_11 | +| DeclareObject() -> void | 0 | 13 | 0 | @r0_10 | +| DeclareObject() -> void | 0 | 13 | 1 | @r0_12 | +| DeclareObject() -> void | 0 | 17 | 2 | @r0_16 | +| DeclareObject() -> void | 0 | 18 | 9 | @r0_15 | +| DeclareObject() -> void | 0 | 18 | 10 | this:@r0_14 | +| DeclareObject() -> void | 0 | 18 | 11 | @r0_17 | +| DeclareObject() -> void | 0 | 21 | 0 | @mu* | +| DerefReference(int &) -> int | 0 | 4 | 0 | @r0_3 | +| DerefReference(int &) -> int | 0 | 4 | 1 | @r0_2 | +| DerefReference(int &) -> int | 0 | 7 | 0 | @r0_6 | +| DerefReference(int &) -> int | 0 | 7 | 1 | @m0_4 | +| DerefReference(int &) -> int | 0 | 8 | 0 | @r0_7 | +| DerefReference(int &) -> int | 0 | 8 | 1 | @mu0_1 | +| DerefReference(int &) -> int | 0 | 9 | 0 | @r0_5 | +| DerefReference(int &) -> int | 0 | 9 | 1 | @r0_8 | +| DerefReference(int &) -> int | 0 | 11 | 0 | @r0_10 | +| DerefReference(int &) -> int | 0 | 11 | 5 | @m0_9 | +| DerefReference(int &) -> int | 0 | 12 | 0 | @mu* | +| Dereference(int *) -> int | 0 | 4 | 0 | @r0_3 | +| Dereference(int *) -> int | 0 | 4 | 1 | @r0_2 | +| Dereference(int *) -> int | 0 | 7 | 0 | @r0_6 | +| Dereference(int *) -> int | 0 | 7 | 1 | @m0_4 | +| Dereference(int *) -> int | 0 | 8 | 0 | @r0_7 | +| Dereference(int *) -> int | 0 | 8 | 1 | @r0_5 | +| Dereference(int *) -> int | 0 | 11 | 0 | @r0_10 | +| Dereference(int *) -> int | 0 | 11 | 1 | @m0_4 | +| Dereference(int *) -> int | 0 | 12 | 0 | @r0_11 | +| Dereference(int *) -> int | 0 | 12 | 1 | @mu0_1 | +| Dereference(int *) -> int | 0 | 13 | 0 | @r0_9 | +| Dereference(int *) -> int | 0 | 13 | 1 | @r0_12 | +| Dereference(int *) -> int | 0 | 15 | 0 | @r0_14 | +| Dereference(int *) -> int | 0 | 15 | 5 | @m0_13 | +| Dereference(int *) -> int | 0 | 16 | 0 | @mu* | +| Derived::Derived() -> void | 0 | 3 | 2 | @r0_2 | +| Derived::Derived() -> void | 0 | 5 | 9 | @r0_4 | +| Derived::Derived() -> void | 0 | 5 | 10 | this:@r0_3 | +| Derived::Derived() -> void | 0 | 6 | 2 | @r0_2 | +| Derived::Derived() -> void | 0 | 8 | 9 | @r0_7 | +| Derived::Derived() -> void | 0 | 8 | 10 | this:@r0_6 | +| Derived::Derived() -> void | 0 | 11 | 0 | @mu* | +| Derived::operator=(const Derived &) -> Derived & | 0 | 5 | 0 | @r0_4 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 5 | 1 | @r0_3 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 6 | 1 | @r0_2 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 7 | 2 | @r0_6 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 10 | 0 | @r0_9 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 10 | 1 | @m0_5 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 11 | 2 | @r0_10 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 12 | 9 | @r0_8 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 12 | 10 | this:@r0_7 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 12 | 11 | @r0_11 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 13 | 1 | @r0_2 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 14 | 2 | @r0_13 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 17 | 0 | @r0_16 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 17 | 1 | @m0_5 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 18 | 2 | @r0_17 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 19 | 9 | @r0_15 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 19 | 10 | this:@r0_14 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 19 | 11 | @r0_18 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 21 | 1 | @r0_2 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 22 | 0 | @r0_20 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 22 | 1 | @r0_21 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 24 | 0 | @r0_23 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 24 | 5 | @m0_22 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 25 | 0 | @mu* | +| Derived::~Derived() -> void | 0 | 4 | 2 | @r0_2 | +| Derived::~Derived() -> void | 0 | 6 | 9 | @r0_5 | +| Derived::~Derived() -> void | 0 | 6 | 10 | this:@r0_4 | +| Derived::~Derived() -> void | 0 | 7 | 2 | @r0_2 | +| Derived::~Derived() -> void | 0 | 9 | 9 | @r0_8 | +| Derived::~Derived() -> void | 0 | 9 | 10 | this:@r0_7 | +| Derived::~Derived() -> void | 0 | 11 | 0 | @mu* | +| DerivedVB::DerivedVB() -> void | 0 | 3 | 2 | @r0_2 | +| DerivedVB::DerivedVB() -> void | 0 | 5 | 9 | @r0_4 | +| DerivedVB::DerivedVB() -> void | 0 | 5 | 10 | this:@r0_3 | +| DerivedVB::DerivedVB() -> void | 0 | 6 | 2 | @r0_2 | +| DerivedVB::DerivedVB() -> void | 0 | 8 | 9 | @r0_7 | +| DerivedVB::DerivedVB() -> void | 0 | 8 | 10 | this:@r0_6 | +| DerivedVB::DerivedVB() -> void | 0 | 9 | 2 | @r0_2 | +| DerivedVB::DerivedVB() -> void | 0 | 11 | 9 | @r0_10 | +| DerivedVB::DerivedVB() -> void | 0 | 11 | 10 | this:@r0_9 | +| DerivedVB::DerivedVB() -> void | 0 | 12 | 2 | @r0_2 | +| DerivedVB::DerivedVB() -> void | 0 | 14 | 9 | @r0_13 | +| DerivedVB::DerivedVB() -> void | 0 | 14 | 10 | this:@r0_12 | +| DerivedVB::DerivedVB() -> void | 0 | 17 | 0 | @mu* | +| DerivedVB::~DerivedVB() -> void | 0 | 4 | 2 | @r0_2 | +| DerivedVB::~DerivedVB() -> void | 0 | 6 | 9 | @r0_5 | +| DerivedVB::~DerivedVB() -> void | 0 | 6 | 10 | this:@r0_4 | +| DerivedVB::~DerivedVB() -> void | 0 | 7 | 2 | @r0_2 | +| DerivedVB::~DerivedVB() -> void | 0 | 9 | 9 | @r0_8 | +| DerivedVB::~DerivedVB() -> void | 0 | 9 | 10 | this:@r0_7 | +| DerivedVB::~DerivedVB() -> void | 0 | 10 | 2 | @r0_2 | +| DerivedVB::~DerivedVB() -> void | 0 | 12 | 9 | @r0_11 | +| DerivedVB::~DerivedVB() -> void | 0 | 12 | 10 | this:@r0_10 | +| DerivedVB::~DerivedVB() -> void | 0 | 13 | 2 | @r0_2 | +| DerivedVB::~DerivedVB() -> void | 0 | 15 | 9 | @r0_14 | +| DerivedVB::~DerivedVB() -> void | 0 | 15 | 10 | this:@r0_13 | +| DerivedVB::~DerivedVB() -> void | 0 | 17 | 0 | @mu* | +| DoStatements(int) -> void | 0 | 4 | 0 | @r0_3 | +| DoStatements(int) -> void | 0 | 4 | 1 | @r0_2 | +| DoStatements(int) -> void | 1 | 0 | 11 | from 0: @m0_4 | +| DoStatements(int) -> void | 1 | 0 | 11 | from 1: @m1_5 | +| DoStatements(int) -> void | 1 | 3 | 0 | @r1_2 | +| DoStatements(int) -> void | 1 | 3 | 1 | @m1_0 | +| DoStatements(int) -> void | 1 | 4 | 3 | @r1_3 | +| DoStatements(int) -> void | 1 | 4 | 4 | @r1_1 | +| DoStatements(int) -> void | 1 | 5 | 0 | @r1_2 | +| DoStatements(int) -> void | 1 | 5 | 1 | @r1_4 | +| DoStatements(int) -> void | 1 | 7 | 0 | @r1_6 | +| DoStatements(int) -> void | 1 | 7 | 1 | @m1_5 | +| DoStatements(int) -> void | 1 | 9 | 3 | @r1_7 | +| DoStatements(int) -> void | 1 | 9 | 4 | @r1_8 | +| DoStatements(int) -> void | 1 | 10 | 7 | @r1_9 | +| DoStatements(int) -> void | 2 | 2 | 0 | @mu* | +| DynamicCast() -> void | 0 | 4 | 9 | @r0_3 | +| DynamicCast() -> void | 0 | 4 | 10 | this:@r0_2 | +| DynamicCast() -> void | 0 | 7 | 9 | @r0_6 | +| DynamicCast() -> void | 0 | 7 | 10 | this:@r0_5 | +| DynamicCast() -> void | 0 | 10 | 0 | @r0_8 | +| DynamicCast() -> void | 0 | 10 | 1 | @r0_9 | +| DynamicCast() -> void | 0 | 13 | 0 | @r0_11 | +| DynamicCast() -> void | 0 | 13 | 1 | @r0_12 | +| DynamicCast() -> void | 0 | 15 | 0 | @r0_14 | +| DynamicCast() -> void | 0 | 15 | 1 | @m0_13 | +| DynamicCast() -> void | 0 | 16 | 2 | @r0_15 | +| DynamicCast() -> void | 0 | 18 | 0 | @r0_17 | +| DynamicCast() -> void | 0 | 18 | 1 | @r0_16 | +| DynamicCast() -> void | 0 | 21 | 2 | @r0_20 | +| DynamicCast() -> void | 0 | 22 | 0 | @r0_19 | +| DynamicCast() -> void | 0 | 22 | 1 | @r0_21 | +| DynamicCast() -> void | 0 | 24 | 0 | @r0_23 | +| DynamicCast() -> void | 0 | 24 | 1 | @m0_18 | +| DynamicCast() -> void | 0 | 25 | 2 | @r0_24 | +| DynamicCast() -> void | 0 | 27 | 0 | @r0_26 | +| DynamicCast() -> void | 0 | 27 | 1 | @r0_25 | +| DynamicCast() -> void | 0 | 30 | 2 | @r0_29 | +| DynamicCast() -> void | 0 | 31 | 0 | @r0_28 | +| DynamicCast() -> void | 0 | 31 | 1 | @r0_30 | +| DynamicCast() -> void | 0 | 34 | 0 | @r0_33 | +| DynamicCast() -> void | 0 | 34 | 1 | @m0_18 | +| DynamicCast() -> void | 0 | 35 | 2 | @r0_34 | +| DynamicCast() -> void | 0 | 36 | 0 | @r0_32 | +| DynamicCast() -> void | 0 | 36 | 1 | @r0_35 | +| DynamicCast() -> void | 0 | 39 | 0 | @r0_38 | +| DynamicCast() -> void | 0 | 39 | 1 | @m0_27 | +| DynamicCast() -> void | 0 | 40 | 2 | @r0_39 | +| DynamicCast() -> void | 0 | 41 | 0 | @r0_37 | +| DynamicCast() -> void | 0 | 41 | 1 | @r0_40 | +| DynamicCast() -> void | 0 | 44 | 0 | @mu* | +| EarlyReturn(int, int) -> void | 0 | 4 | 0 | @r0_3 | +| EarlyReturn(int, int) -> void | 0 | 4 | 1 | @r0_2 | +| EarlyReturn(int, int) -> void | 0 | 7 | 0 | @r0_6 | +| EarlyReturn(int, int) -> void | 0 | 7 | 1 | @r0_5 | +| EarlyReturn(int, int) -> void | 0 | 9 | 0 | @r0_8 | +| EarlyReturn(int, int) -> void | 0 | 9 | 1 | @m0_4 | +| EarlyReturn(int, int) -> void | 0 | 11 | 0 | @r0_10 | +| EarlyReturn(int, int) -> void | 0 | 11 | 1 | @m0_7 | +| EarlyReturn(int, int) -> void | 0 | 12 | 3 | @r0_9 | +| EarlyReturn(int, int) -> void | 0 | 12 | 4 | @r0_11 | +| EarlyReturn(int, int) -> void | 0 | 13 | 7 | @r0_12 | +| EarlyReturn(int, int) -> void | 1 | 1 | 0 | @mu* | +| EarlyReturn(int, int) -> void | 3 | 1 | 0 | @r3_0 | +| EarlyReturn(int, int) -> void | 3 | 1 | 1 | @m0_4 | +| EarlyReturn(int, int) -> void | 3 | 3 | 0 | @r3_2 | +| EarlyReturn(int, int) -> void | 3 | 3 | 1 | @r3_1 | +| EarlyReturnValue(int, int) -> int | 0 | 4 | 0 | @r0_3 | +| EarlyReturnValue(int, int) -> int | 0 | 4 | 1 | @r0_2 | +| EarlyReturnValue(int, int) -> int | 0 | 7 | 0 | @r0_6 | +| EarlyReturnValue(int, int) -> int | 0 | 7 | 1 | @r0_5 | +| EarlyReturnValue(int, int) -> int | 0 | 9 | 0 | @r0_8 | +| EarlyReturnValue(int, int) -> int | 0 | 9 | 1 | @m0_4 | +| EarlyReturnValue(int, int) -> int | 0 | 11 | 0 | @r0_10 | +| EarlyReturnValue(int, int) -> int | 0 | 11 | 1 | @m0_7 | +| EarlyReturnValue(int, int) -> int | 0 | 12 | 3 | @r0_9 | +| EarlyReturnValue(int, int) -> int | 0 | 12 | 4 | @r0_11 | +| EarlyReturnValue(int, int) -> int | 0 | 13 | 7 | @r0_12 | +| EarlyReturnValue(int, int) -> int | 1 | 0 | 11 | from 2: @m2_3 | +| EarlyReturnValue(int, int) -> int | 1 | 0 | 11 | from 3: @m3_6 | +| EarlyReturnValue(int, int) -> int | 1 | 2 | 0 | @r1_1 | +| EarlyReturnValue(int, int) -> int | 1 | 2 | 5 | @m1_0 | +| EarlyReturnValue(int, int) -> int | 1 | 3 | 0 | @mu* | +| EarlyReturnValue(int, int) -> int | 2 | 2 | 0 | @r2_1 | +| EarlyReturnValue(int, int) -> int | 2 | 2 | 1 | @m0_4 | +| EarlyReturnValue(int, int) -> int | 2 | 3 | 0 | @r2_0 | +| EarlyReturnValue(int, int) -> int | 2 | 3 | 1 | @r2_2 | +| EarlyReturnValue(int, int) -> int | 3 | 2 | 0 | @r3_1 | +| EarlyReturnValue(int, int) -> int | 3 | 2 | 1 | @m0_4 | +| EarlyReturnValue(int, int) -> int | 3 | 4 | 0 | @r3_3 | +| EarlyReturnValue(int, int) -> int | 3 | 4 | 1 | @m0_7 | +| EarlyReturnValue(int, int) -> int | 3 | 5 | 3 | @r3_2 | +| EarlyReturnValue(int, int) -> int | 3 | 5 | 4 | @r3_4 | +| EarlyReturnValue(int, int) -> int | 3 | 6 | 0 | @r3_0 | +| EarlyReturnValue(int, int) -> int | 3 | 6 | 1 | @r3_5 | +| EnumSwitch(E) -> int | 0 | 4 | 0 | @r0_3 | +| EnumSwitch(E) -> int | 0 | 4 | 1 | @r0_2 | +| EnumSwitch(E) -> int | 0 | 6 | 0 | @r0_5 | +| EnumSwitch(E) -> int | 0 | 6 | 1 | @m0_4 | +| EnumSwitch(E) -> int | 0 | 7 | 2 | @r0_6 | +| EnumSwitch(E) -> int | 0 | 8 | 7 | @r0_7 | +| EnumSwitch(E) -> int | 1 | 0 | 11 | from 2: @m2_3 | +| EnumSwitch(E) -> int | 1 | 0 | 11 | from 3: @m3_3 | +| EnumSwitch(E) -> int | 1 | 0 | 11 | from 4: @m4_3 | +| EnumSwitch(E) -> int | 1 | 2 | 0 | @r1_1 | +| EnumSwitch(E) -> int | 1 | 2 | 5 | @m1_0 | +| EnumSwitch(E) -> int | 1 | 3 | 0 | @mu* | +| EnumSwitch(E) -> int | 2 | 3 | 0 | @r2_1 | +| EnumSwitch(E) -> int | 2 | 3 | 1 | @r2_2 | +| EnumSwitch(E) -> int | 3 | 3 | 0 | @r3_1 | +| EnumSwitch(E) -> int | 3 | 3 | 1 | @r3_2 | +| EnumSwitch(E) -> int | 4 | 3 | 0 | @r4_1 | +| EnumSwitch(E) -> int | 4 | 3 | 1 | @r4_2 | +| FieldAccess() -> void | 0 | 4 | 0 | @r0_2 | +| FieldAccess() -> void | 0 | 4 | 1 | @r0_3 | +| FieldAccess() -> void | 0 | 7 | 2 | @r0_6 | +| FieldAccess() -> void | 0 | 8 | 0 | @r0_7 | +| FieldAccess() -> void | 0 | 8 | 1 | @r0_5 | +| FieldAccess() -> void | 0 | 10 | 2 | @r0_9 | +| FieldAccess() -> void | 0 | 11 | 0 | @r0_10 | +| FieldAccess() -> void | 0 | 11 | 1 | @m0_8 | +| FieldAccess() -> void | 0 | 13 | 2 | @r0_12 | +| FieldAccess() -> void | 0 | 14 | 0 | @r0_13 | +| FieldAccess() -> void | 0 | 14 | 1 | @r0_11 | +| FieldAccess() -> void | 0 | 17 | 2 | @r0_16 | +| FieldAccess() -> void | 0 | 18 | 0 | @r0_15 | +| FieldAccess() -> void | 0 | 18 | 1 | @r0_17 | +| FieldAccess() -> void | 0 | 21 | 0 | @mu* | +| FloatCompare(double, double) -> void | 0 | 4 | 0 | @r0_3 | +| FloatCompare(double, double) -> void | 0 | 4 | 1 | @r0_2 | +| FloatCompare(double, double) -> void | 0 | 7 | 0 | @r0_6 | +| FloatCompare(double, double) -> void | 0 | 7 | 1 | @r0_5 | +| FloatCompare(double, double) -> void | 0 | 10 | 0 | @r0_8 | +| FloatCompare(double, double) -> void | 0 | 10 | 1 | @r0_9 | +| FloatCompare(double, double) -> void | 0 | 12 | 0 | @r0_11 | +| FloatCompare(double, double) -> void | 0 | 12 | 1 | @m0_4 | +| FloatCompare(double, double) -> void | 0 | 14 | 0 | @r0_13 | +| FloatCompare(double, double) -> void | 0 | 14 | 1 | @m0_7 | +| FloatCompare(double, double) -> void | 0 | 15 | 3 | @r0_12 | +| FloatCompare(double, double) -> void | 0 | 15 | 4 | @r0_14 | +| FloatCompare(double, double) -> void | 0 | 17 | 0 | @r0_16 | +| FloatCompare(double, double) -> void | 0 | 17 | 1 | @r0_15 | +| FloatCompare(double, double) -> void | 0 | 19 | 0 | @r0_18 | +| FloatCompare(double, double) -> void | 0 | 19 | 1 | @m0_4 | +| FloatCompare(double, double) -> void | 0 | 21 | 0 | @r0_20 | +| FloatCompare(double, double) -> void | 0 | 21 | 1 | @m0_7 | +| FloatCompare(double, double) -> void | 0 | 22 | 3 | @r0_19 | +| FloatCompare(double, double) -> void | 0 | 22 | 4 | @r0_21 | +| FloatCompare(double, double) -> void | 0 | 24 | 0 | @r0_23 | +| FloatCompare(double, double) -> void | 0 | 24 | 1 | @r0_22 | +| FloatCompare(double, double) -> void | 0 | 26 | 0 | @r0_25 | +| FloatCompare(double, double) -> void | 0 | 26 | 1 | @m0_4 | +| FloatCompare(double, double) -> void | 0 | 28 | 0 | @r0_27 | +| FloatCompare(double, double) -> void | 0 | 28 | 1 | @m0_7 | +| FloatCompare(double, double) -> void | 0 | 29 | 3 | @r0_26 | +| FloatCompare(double, double) -> void | 0 | 29 | 4 | @r0_28 | +| FloatCompare(double, double) -> void | 0 | 31 | 0 | @r0_30 | +| FloatCompare(double, double) -> void | 0 | 31 | 1 | @r0_29 | +| FloatCompare(double, double) -> void | 0 | 33 | 0 | @r0_32 | +| FloatCompare(double, double) -> void | 0 | 33 | 1 | @m0_4 | +| FloatCompare(double, double) -> void | 0 | 35 | 0 | @r0_34 | +| FloatCompare(double, double) -> void | 0 | 35 | 1 | @m0_7 | +| FloatCompare(double, double) -> void | 0 | 36 | 3 | @r0_33 | +| FloatCompare(double, double) -> void | 0 | 36 | 4 | @r0_35 | +| FloatCompare(double, double) -> void | 0 | 38 | 0 | @r0_37 | +| FloatCompare(double, double) -> void | 0 | 38 | 1 | @r0_36 | +| FloatCompare(double, double) -> void | 0 | 40 | 0 | @r0_39 | +| FloatCompare(double, double) -> void | 0 | 40 | 1 | @m0_4 | +| FloatCompare(double, double) -> void | 0 | 42 | 0 | @r0_41 | +| FloatCompare(double, double) -> void | 0 | 42 | 1 | @m0_7 | +| FloatCompare(double, double) -> void | 0 | 43 | 3 | @r0_40 | +| FloatCompare(double, double) -> void | 0 | 43 | 4 | @r0_42 | +| FloatCompare(double, double) -> void | 0 | 45 | 0 | @r0_44 | +| FloatCompare(double, double) -> void | 0 | 45 | 1 | @r0_43 | +| FloatCompare(double, double) -> void | 0 | 47 | 0 | @r0_46 | +| FloatCompare(double, double) -> void | 0 | 47 | 1 | @m0_4 | +| FloatCompare(double, double) -> void | 0 | 49 | 0 | @r0_48 | +| FloatCompare(double, double) -> void | 0 | 49 | 1 | @m0_7 | +| FloatCompare(double, double) -> void | 0 | 50 | 3 | @r0_47 | +| FloatCompare(double, double) -> void | 0 | 50 | 4 | @r0_49 | +| FloatCompare(double, double) -> void | 0 | 52 | 0 | @r0_51 | +| FloatCompare(double, double) -> void | 0 | 52 | 1 | @r0_50 | +| FloatCompare(double, double) -> void | 0 | 55 | 0 | @mu* | +| FloatCrement(float) -> void | 0 | 4 | 0 | @r0_3 | +| FloatCrement(float) -> void | 0 | 4 | 1 | @r0_2 | +| FloatCrement(float) -> void | 0 | 7 | 0 | @r0_5 | +| FloatCrement(float) -> void | 0 | 7 | 1 | @r0_6 | +| FloatCrement(float) -> void | 0 | 9 | 0 | @r0_8 | +| FloatCrement(float) -> void | 0 | 9 | 1 | @m0_4 | +| FloatCrement(float) -> void | 0 | 11 | 3 | @r0_9 | +| FloatCrement(float) -> void | 0 | 11 | 4 | @r0_10 | +| FloatCrement(float) -> void | 0 | 12 | 0 | @r0_8 | +| FloatCrement(float) -> void | 0 | 12 | 1 | @r0_11 | +| FloatCrement(float) -> void | 0 | 14 | 0 | @r0_13 | +| FloatCrement(float) -> void | 0 | 14 | 1 | @r0_11 | +| FloatCrement(float) -> void | 0 | 16 | 0 | @r0_15 | +| FloatCrement(float) -> void | 0 | 16 | 1 | @m0_12 | +| FloatCrement(float) -> void | 0 | 18 | 3 | @r0_16 | +| FloatCrement(float) -> void | 0 | 18 | 4 | @r0_17 | +| FloatCrement(float) -> void | 0 | 19 | 0 | @r0_15 | +| FloatCrement(float) -> void | 0 | 19 | 1 | @r0_18 | +| FloatCrement(float) -> void | 0 | 21 | 0 | @r0_20 | +| FloatCrement(float) -> void | 0 | 21 | 1 | @r0_18 | +| FloatCrement(float) -> void | 0 | 23 | 0 | @r0_22 | +| FloatCrement(float) -> void | 0 | 23 | 1 | @m0_19 | +| FloatCrement(float) -> void | 0 | 25 | 3 | @r0_23 | +| FloatCrement(float) -> void | 0 | 25 | 4 | @r0_24 | +| FloatCrement(float) -> void | 0 | 26 | 0 | @r0_22 | +| FloatCrement(float) -> void | 0 | 26 | 1 | @r0_25 | +| FloatCrement(float) -> void | 0 | 28 | 0 | @r0_27 | +| FloatCrement(float) -> void | 0 | 28 | 1 | @r0_23 | +| FloatCrement(float) -> void | 0 | 30 | 0 | @r0_29 | +| FloatCrement(float) -> void | 0 | 30 | 1 | @m0_26 | +| FloatCrement(float) -> void | 0 | 32 | 3 | @r0_30 | +| FloatCrement(float) -> void | 0 | 32 | 4 | @r0_31 | +| FloatCrement(float) -> void | 0 | 33 | 0 | @r0_29 | +| FloatCrement(float) -> void | 0 | 33 | 1 | @r0_32 | +| FloatCrement(float) -> void | 0 | 35 | 0 | @r0_34 | +| FloatCrement(float) -> void | 0 | 35 | 1 | @r0_30 | +| FloatCrement(float) -> void | 0 | 38 | 0 | @mu* | +| FloatOps(double, double) -> void | 0 | 4 | 0 | @r0_3 | +| FloatOps(double, double) -> void | 0 | 4 | 1 | @r0_2 | +| FloatOps(double, double) -> void | 0 | 7 | 0 | @r0_6 | +| FloatOps(double, double) -> void | 0 | 7 | 1 | @r0_5 | +| FloatOps(double, double) -> void | 0 | 10 | 0 | @r0_8 | +| FloatOps(double, double) -> void | 0 | 10 | 1 | @r0_9 | +| FloatOps(double, double) -> void | 0 | 12 | 0 | @r0_11 | +| FloatOps(double, double) -> void | 0 | 12 | 1 | @m0_4 | +| FloatOps(double, double) -> void | 0 | 14 | 0 | @r0_13 | +| FloatOps(double, double) -> void | 0 | 14 | 1 | @m0_7 | +| FloatOps(double, double) -> void | 0 | 15 | 3 | @r0_12 | +| FloatOps(double, double) -> void | 0 | 15 | 4 | @r0_14 | +| FloatOps(double, double) -> void | 0 | 17 | 0 | @r0_16 | +| FloatOps(double, double) -> void | 0 | 17 | 1 | @r0_15 | +| FloatOps(double, double) -> void | 0 | 19 | 0 | @r0_18 | +| FloatOps(double, double) -> void | 0 | 19 | 1 | @m0_4 | +| FloatOps(double, double) -> void | 0 | 21 | 0 | @r0_20 | +| FloatOps(double, double) -> void | 0 | 21 | 1 | @m0_7 | +| FloatOps(double, double) -> void | 0 | 22 | 3 | @r0_19 | +| FloatOps(double, double) -> void | 0 | 22 | 4 | @r0_21 | +| FloatOps(double, double) -> void | 0 | 24 | 0 | @r0_23 | +| FloatOps(double, double) -> void | 0 | 24 | 1 | @r0_22 | +| FloatOps(double, double) -> void | 0 | 26 | 0 | @r0_25 | +| FloatOps(double, double) -> void | 0 | 26 | 1 | @m0_4 | +| FloatOps(double, double) -> void | 0 | 28 | 0 | @r0_27 | +| FloatOps(double, double) -> void | 0 | 28 | 1 | @m0_7 | +| FloatOps(double, double) -> void | 0 | 29 | 3 | @r0_26 | +| FloatOps(double, double) -> void | 0 | 29 | 4 | @r0_28 | +| FloatOps(double, double) -> void | 0 | 31 | 0 | @r0_30 | +| FloatOps(double, double) -> void | 0 | 31 | 1 | @r0_29 | +| FloatOps(double, double) -> void | 0 | 33 | 0 | @r0_32 | +| FloatOps(double, double) -> void | 0 | 33 | 1 | @m0_4 | +| FloatOps(double, double) -> void | 0 | 35 | 0 | @r0_34 | +| FloatOps(double, double) -> void | 0 | 35 | 1 | @m0_7 | +| FloatOps(double, double) -> void | 0 | 36 | 3 | @r0_33 | +| FloatOps(double, double) -> void | 0 | 36 | 4 | @r0_35 | +| FloatOps(double, double) -> void | 0 | 38 | 0 | @r0_37 | +| FloatOps(double, double) -> void | 0 | 38 | 1 | @r0_36 | +| FloatOps(double, double) -> void | 0 | 40 | 0 | @r0_39 | +| FloatOps(double, double) -> void | 0 | 40 | 1 | @m0_4 | +| FloatOps(double, double) -> void | 0 | 42 | 0 | @r0_41 | +| FloatOps(double, double) -> void | 0 | 42 | 1 | @r0_40 | +| FloatOps(double, double) -> void | 0 | 44 | 0 | @r0_43 | +| FloatOps(double, double) -> void | 0 | 44 | 1 | @m0_4 | +| FloatOps(double, double) -> void | 0 | 46 | 0 | @r0_45 | +| FloatOps(double, double) -> void | 0 | 46 | 1 | @m0_42 | +| FloatOps(double, double) -> void | 0 | 47 | 3 | @r0_46 | +| FloatOps(double, double) -> void | 0 | 47 | 4 | @r0_44 | +| FloatOps(double, double) -> void | 0 | 48 | 0 | @r0_45 | +| FloatOps(double, double) -> void | 0 | 48 | 1 | @r0_47 | +| FloatOps(double, double) -> void | 0 | 50 | 0 | @r0_49 | +| FloatOps(double, double) -> void | 0 | 50 | 1 | @m0_4 | +| FloatOps(double, double) -> void | 0 | 52 | 0 | @r0_51 | +| FloatOps(double, double) -> void | 0 | 52 | 1 | @m0_48 | +| FloatOps(double, double) -> void | 0 | 53 | 3 | @r0_52 | +| FloatOps(double, double) -> void | 0 | 53 | 4 | @r0_50 | +| FloatOps(double, double) -> void | 0 | 54 | 0 | @r0_51 | +| FloatOps(double, double) -> void | 0 | 54 | 1 | @r0_53 | +| FloatOps(double, double) -> void | 0 | 56 | 0 | @r0_55 | +| FloatOps(double, double) -> void | 0 | 56 | 1 | @m0_4 | +| FloatOps(double, double) -> void | 0 | 58 | 0 | @r0_57 | +| FloatOps(double, double) -> void | 0 | 58 | 1 | @m0_54 | +| FloatOps(double, double) -> void | 0 | 59 | 3 | @r0_58 | +| FloatOps(double, double) -> void | 0 | 59 | 4 | @r0_56 | +| FloatOps(double, double) -> void | 0 | 60 | 0 | @r0_57 | +| FloatOps(double, double) -> void | 0 | 60 | 1 | @r0_59 | +| FloatOps(double, double) -> void | 0 | 62 | 0 | @r0_61 | +| FloatOps(double, double) -> void | 0 | 62 | 1 | @m0_4 | +| FloatOps(double, double) -> void | 0 | 64 | 0 | @r0_63 | +| FloatOps(double, double) -> void | 0 | 64 | 1 | @m0_60 | +| FloatOps(double, double) -> void | 0 | 65 | 3 | @r0_64 | +| FloatOps(double, double) -> void | 0 | 65 | 4 | @r0_62 | +| FloatOps(double, double) -> void | 0 | 66 | 0 | @r0_63 | +| FloatOps(double, double) -> void | 0 | 66 | 1 | @r0_65 | +| FloatOps(double, double) -> void | 0 | 68 | 0 | @r0_67 | +| FloatOps(double, double) -> void | 0 | 68 | 1 | @m0_4 | +| FloatOps(double, double) -> void | 0 | 69 | 1 | @r0_68 | +| FloatOps(double, double) -> void | 0 | 71 | 0 | @r0_70 | +| FloatOps(double, double) -> void | 0 | 71 | 1 | @r0_69 | +| FloatOps(double, double) -> void | 0 | 73 | 0 | @r0_72 | +| FloatOps(double, double) -> void | 0 | 73 | 1 | @m0_4 | +| FloatOps(double, double) -> void | 0 | 74 | 2 | @r0_73 | +| FloatOps(double, double) -> void | 0 | 76 | 0 | @r0_75 | +| FloatOps(double, double) -> void | 0 | 76 | 1 | @r0_74 | +| FloatOps(double, double) -> void | 0 | 79 | 0 | @mu* | +| Foo() -> void | 0 | 4 | 0 | @r0_2 | +| Foo() -> void | 0 | 4 | 1 | @r0_3 | +| Foo() -> void | 0 | 7 | 0 | @r0_5 | +| Foo() -> void | 0 | 7 | 1 | @r0_6 | +| Foo() -> void | 0 | 9 | 0 | @r0_8 | +| Foo() -> void | 0 | 9 | 1 | @m0_4 | +| Foo() -> void | 0 | 11 | 0 | @r0_10 | +| Foo() -> void | 0 | 11 | 1 | @m0_7 | +| Foo() -> void | 0 | 12 | 2 | @r0_11 | +| Foo() -> void | 0 | 13 | 3 | @r0_9 | +| Foo() -> void | 0 | 13 | 4 | @r0_12 | +| Foo() -> void | 0 | 14 | 2 | @r0_13 | +| Foo() -> void | 0 | 16 | 0 | @r0_15 | +| Foo() -> void | 0 | 16 | 1 | @r0_14 | +| Foo() -> void | 0 | 18 | 0 | @r0_17 | +| Foo() -> void | 0 | 18 | 1 | @m0_4 | +| Foo() -> void | 0 | 20 | 0 | @r0_19 | +| Foo() -> void | 0 | 20 | 1 | @m0_16 | +| Foo() -> void | 0 | 21 | 2 | @r0_20 | +| Foo() -> void | 0 | 22 | 3 | @r0_18 | +| Foo() -> void | 0 | 22 | 4 | @r0_21 | +| Foo() -> void | 0 | 24 | 0 | @r0_23 | +| Foo() -> void | 0 | 24 | 1 | @r0_22 | +| Foo() -> void | 0 | 27 | 0 | @mu* | +| For_Break() -> void | 0 | 4 | 0 | @r0_2 | +| For_Break() -> void | 0 | 4 | 1 | @r0_3 | +| For_Break() -> void | 1 | 0 | 11 | from 0: @m0_4 | +| For_Break() -> void | 1 | 0 | 11 | from 2: @m2_4 | +| For_Break() -> void | 1 | 2 | 0 | @r1_1 | +| For_Break() -> void | 1 | 2 | 1 | @m1_0 | +| For_Break() -> void | 1 | 4 | 3 | @r1_2 | +| For_Break() -> void | 1 | 4 | 4 | @r1_3 | +| For_Break() -> void | 1 | 5 | 7 | @r1_4 | +| For_Break() -> void | 2 | 2 | 0 | @r2_1 | +| For_Break() -> void | 2 | 2 | 1 | @m1_0 | +| For_Break() -> void | 2 | 3 | 3 | @r2_2 | +| For_Break() -> void | 2 | 3 | 4 | @r2_0 | +| For_Break() -> void | 2 | 4 | 0 | @r2_1 | +| For_Break() -> void | 2 | 4 | 1 | @r2_3 | +| For_Break() -> void | 3 | 1 | 0 | @r3_0 | +| For_Break() -> void | 3 | 1 | 1 | @m1_0 | +| For_Break() -> void | 3 | 3 | 3 | @r3_1 | +| For_Break() -> void | 3 | 3 | 4 | @r3_2 | +| For_Break() -> void | 3 | 4 | 7 | @r3_3 | +| For_Break() -> void | 5 | 3 | 0 | @mu* | +| For_Condition() -> void | 0 | 4 | 0 | @r0_2 | +| For_Condition() -> void | 0 | 4 | 1 | @r0_3 | +| For_Condition() -> void | 1 | 1 | 0 | @r1_0 | +| For_Condition() -> void | 1 | 1 | 1 | @m0_4 | +| For_Condition() -> void | 1 | 3 | 3 | @r1_1 | +| For_Condition() -> void | 1 | 3 | 4 | @r1_2 | +| For_Condition() -> void | 1 | 4 | 7 | @r1_3 | +| For_Condition() -> void | 3 | 2 | 0 | @mu* | +| For_ConditionUpdate() -> void | 0 | 4 | 0 | @r0_2 | +| For_ConditionUpdate() -> void | 0 | 4 | 1 | @r0_3 | +| For_ConditionUpdate() -> void | 1 | 0 | 11 | from 0: @m0_4 | +| For_ConditionUpdate() -> void | 1 | 0 | 11 | from 2: @m2_5 | +| For_ConditionUpdate() -> void | 1 | 2 | 0 | @r1_1 | +| For_ConditionUpdate() -> void | 1 | 2 | 1 | @m1_0 | +| For_ConditionUpdate() -> void | 1 | 4 | 3 | @r1_2 | +| For_ConditionUpdate() -> void | 1 | 4 | 4 | @r1_3 | +| For_ConditionUpdate() -> void | 1 | 5 | 7 | @r1_4 | +| For_ConditionUpdate() -> void | 2 | 3 | 0 | @r2_2 | +| For_ConditionUpdate() -> void | 2 | 3 | 1 | @m1_0 | +| For_ConditionUpdate() -> void | 2 | 4 | 3 | @r2_3 | +| For_ConditionUpdate() -> void | 2 | 4 | 4 | @r2_1 | +| For_ConditionUpdate() -> void | 2 | 5 | 0 | @r2_2 | +| For_ConditionUpdate() -> void | 2 | 5 | 1 | @r2_4 | +| For_ConditionUpdate() -> void | 3 | 2 | 0 | @mu* | +| For_Continue_NoUpdate() -> void | 0 | 4 | 0 | @r0_2 | +| For_Continue_NoUpdate() -> void | 0 | 4 | 1 | @r0_3 | +| For_Continue_NoUpdate() -> void | 1 | 1 | 0 | @r1_0 | +| For_Continue_NoUpdate() -> void | 1 | 1 | 1 | @m0_4 | +| For_Continue_NoUpdate() -> void | 1 | 3 | 3 | @r1_1 | +| For_Continue_NoUpdate() -> void | 1 | 3 | 4 | @r1_2 | +| For_Continue_NoUpdate() -> void | 1 | 4 | 7 | @r1_3 | +| For_Continue_NoUpdate() -> void | 2 | 1 | 0 | @r2_0 | +| For_Continue_NoUpdate() -> void | 2 | 1 | 1 | @m0_4 | +| For_Continue_NoUpdate() -> void | 2 | 3 | 3 | @r2_1 | +| For_Continue_NoUpdate() -> void | 2 | 3 | 4 | @r2_2 | +| For_Continue_NoUpdate() -> void | 2 | 4 | 7 | @r2_3 | +| For_Continue_NoUpdate() -> void | 5 | 2 | 0 | @mu* | +| For_Continue_Update() -> void | 0 | 4 | 0 | @r0_2 | +| For_Continue_Update() -> void | 0 | 4 | 1 | @r0_3 | +| For_Continue_Update() -> void | 1 | 0 | 11 | from 0: @m0_4 | +| For_Continue_Update() -> void | 1 | 0 | 11 | from 4: @m4_5 | +| For_Continue_Update() -> void | 1 | 2 | 0 | @r1_1 | +| For_Continue_Update() -> void | 1 | 2 | 1 | @m1_0 | +| For_Continue_Update() -> void | 1 | 4 | 3 | @r1_2 | +| For_Continue_Update() -> void | 1 | 4 | 4 | @r1_3 | +| For_Continue_Update() -> void | 1 | 5 | 7 | @r1_4 | +| For_Continue_Update() -> void | 2 | 1 | 0 | @r2_0 | +| For_Continue_Update() -> void | 2 | 1 | 1 | @m1_0 | +| For_Continue_Update() -> void | 2 | 3 | 3 | @r2_1 | +| For_Continue_Update() -> void | 2 | 3 | 4 | @r2_2 | +| For_Continue_Update() -> void | 2 | 4 | 7 | @r2_3 | +| For_Continue_Update() -> void | 4 | 3 | 0 | @r4_2 | +| For_Continue_Update() -> void | 4 | 3 | 1 | @m1_0 | +| For_Continue_Update() -> void | 4 | 4 | 3 | @r4_3 | +| For_Continue_Update() -> void | 4 | 4 | 4 | @r4_1 | +| For_Continue_Update() -> void | 4 | 5 | 0 | @r4_2 | +| For_Continue_Update() -> void | 4 | 5 | 1 | @r4_4 | +| For_Continue_Update() -> void | 5 | 2 | 0 | @mu* | +| For_Empty() -> void | 0 | 4 | 0 | @r0_2 | +| For_Empty() -> void | 0 | 4 | 1 | @r0_3 | +| For_Empty() -> void | 1 | 1 | 0 | @mu* | +| For_Init() -> void | 0 | 4 | 0 | @r0_2 | +| For_Init() -> void | 0 | 4 | 1 | @r0_3 | +| For_Init() -> void | 1 | 1 | 0 | @mu* | +| For_InitCondition() -> void | 0 | 4 | 0 | @r0_2 | +| For_InitCondition() -> void | 0 | 4 | 1 | @r0_3 | +| For_InitCondition() -> void | 1 | 1 | 0 | @r1_0 | +| For_InitCondition() -> void | 1 | 1 | 1 | @m0_4 | +| For_InitCondition() -> void | 1 | 3 | 3 | @r1_1 | +| For_InitCondition() -> void | 1 | 3 | 4 | @r1_2 | +| For_InitCondition() -> void | 1 | 4 | 7 | @r1_3 | +| For_InitCondition() -> void | 3 | 2 | 0 | @mu* | +| For_InitConditionUpdate() -> void | 0 | 4 | 0 | @r0_2 | +| For_InitConditionUpdate() -> void | 0 | 4 | 1 | @r0_3 | +| For_InitConditionUpdate() -> void | 1 | 0 | 11 | from 0: @m0_4 | +| For_InitConditionUpdate() -> void | 1 | 0 | 11 | from 2: @m2_5 | +| For_InitConditionUpdate() -> void | 1 | 2 | 0 | @r1_1 | +| For_InitConditionUpdate() -> void | 1 | 2 | 1 | @m1_0 | +| For_InitConditionUpdate() -> void | 1 | 4 | 3 | @r1_2 | +| For_InitConditionUpdate() -> void | 1 | 4 | 4 | @r1_3 | +| For_InitConditionUpdate() -> void | 1 | 5 | 7 | @r1_4 | +| For_InitConditionUpdate() -> void | 2 | 3 | 0 | @r2_2 | +| For_InitConditionUpdate() -> void | 2 | 3 | 1 | @m1_0 | +| For_InitConditionUpdate() -> void | 2 | 4 | 3 | @r2_3 | +| For_InitConditionUpdate() -> void | 2 | 4 | 4 | @r2_1 | +| For_InitConditionUpdate() -> void | 2 | 5 | 0 | @r2_2 | +| For_InitConditionUpdate() -> void | 2 | 5 | 1 | @r2_4 | +| For_InitConditionUpdate() -> void | 3 | 2 | 0 | @mu* | +| For_InitUpdate() -> void | 0 | 4 | 0 | @r0_2 | +| For_InitUpdate() -> void | 0 | 4 | 1 | @r0_3 | +| For_InitUpdate() -> void | 1 | 1 | 0 | @mu* | +| For_InitUpdate() -> void | 2 | 0 | 11 | from 0: @m0_4 | +| For_InitUpdate() -> void | 2 | 0 | 11 | from 2: @m2_6 | +| For_InitUpdate() -> void | 2 | 4 | 0 | @r2_3 | +| For_InitUpdate() -> void | 2 | 4 | 1 | @m2_0 | +| For_InitUpdate() -> void | 2 | 5 | 3 | @r2_4 | +| For_InitUpdate() -> void | 2 | 5 | 4 | @r2_2 | +| For_InitUpdate() -> void | 2 | 6 | 0 | @r2_3 | +| For_InitUpdate() -> void | 2 | 6 | 1 | @r2_5 | +| For_Update() -> void | 0 | 4 | 0 | @r0_2 | +| For_Update() -> void | 0 | 4 | 1 | @r0_3 | +| For_Update() -> void | 1 | 1 | 0 | @mu* | +| For_Update() -> void | 2 | 0 | 11 | from 0: @m0_4 | +| For_Update() -> void | 2 | 0 | 11 | from 2: @m2_6 | +| For_Update() -> void | 2 | 4 | 0 | @r2_3 | +| For_Update() -> void | 2 | 4 | 1 | @m2_0 | +| For_Update() -> void | 2 | 5 | 3 | @r2_4 | +| For_Update() -> void | 2 | 5 | 4 | @r2_2 | +| For_Update() -> void | 2 | 6 | 0 | @r2_3 | +| For_Update() -> void | 2 | 6 | 1 | @r2_5 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 4 | 0 | @r0_3 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 4 | 1 | @r0_2 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 7 | 0 | @r0_6 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 7 | 1 | @r0_5 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 9 | 0 | @r0_8 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 9 | 1 | @m0_4 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 10 | 2 | @r0_9 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 12 | 0 | @r0_11 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 12 | 1 | @r0_10 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 14 | 0 | @r0_13 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 14 | 1 | @m0_12 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 15 | 2 | @r0_14 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 17 | 0 | @r0_16 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 17 | 1 | @r0_15 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 20 | 0 | @mu* | +| FunctionReferences() -> void | 0 | 4 | 0 | @r0_2 | +| FunctionReferences() -> void | 0 | 4 | 1 | @r0_3 | +| FunctionReferences() -> void | 0 | 7 | 0 | @r0_6 | +| FunctionReferences() -> void | 0 | 7 | 1 | @m0_4 | +| FunctionReferences() -> void | 0 | 8 | 0 | @r0_5 | +| FunctionReferences() -> void | 0 | 8 | 1 | @r0_7 | +| FunctionReferences() -> void | 0 | 10 | 0 | @r0_9 | +| FunctionReferences() -> void | 0 | 10 | 1 | @m0_4 | +| FunctionReferences() -> void | 0 | 12 | 9 | @r0_10 | +| FunctionReferences() -> void | 0 | 12 | 11 | @r0_11 | +| FunctionReferences() -> void | 0 | 15 | 0 | @mu* | +| HierarchyConversions() -> void | 0 | 4 | 9 | @r0_3 | +| HierarchyConversions() -> void | 0 | 4 | 10 | this:@r0_2 | +| HierarchyConversions() -> void | 0 | 7 | 9 | @r0_6 | +| HierarchyConversions() -> void | 0 | 7 | 10 | this:@r0_5 | +| HierarchyConversions() -> void | 0 | 10 | 9 | @r0_9 | +| HierarchyConversions() -> void | 0 | 10 | 10 | this:@r0_8 | +| HierarchyConversions() -> void | 0 | 13 | 0 | @r0_11 | +| HierarchyConversions() -> void | 0 | 13 | 1 | @r0_12 | +| HierarchyConversions() -> void | 0 | 16 | 0 | @r0_14 | +| HierarchyConversions() -> void | 0 | 16 | 1 | @r0_15 | +| HierarchyConversions() -> void | 0 | 19 | 0 | @r0_17 | +| HierarchyConversions() -> void | 0 | 19 | 1 | @r0_18 | +| HierarchyConversions() -> void | 0 | 23 | 2 | @r0_22 | +| HierarchyConversions() -> void | 0 | 24 | 9 | @r0_21 | +| HierarchyConversions() -> void | 0 | 24 | 10 | this:@r0_20 | +| HierarchyConversions() -> void | 0 | 24 | 11 | @r0_23 | +| HierarchyConversions() -> void | 0 | 29 | 2 | @r0_28 | +| HierarchyConversions() -> void | 0 | 30 | 9 | @r0_27 | +| HierarchyConversions() -> void | 0 | 30 | 11 | @r0_29 | +| HierarchyConversions() -> void | 0 | 31 | 2 | @r0_30 | +| HierarchyConversions() -> void | 0 | 32 | 9 | @r0_26 | +| HierarchyConversions() -> void | 0 | 32 | 10 | this:@r0_25 | +| HierarchyConversions() -> void | 0 | 32 | 11 | @r0_31 | +| HierarchyConversions() -> void | 0 | 37 | 2 | @r0_36 | +| HierarchyConversions() -> void | 0 | 38 | 9 | @r0_35 | +| HierarchyConversions() -> void | 0 | 38 | 11 | @r0_37 | +| HierarchyConversions() -> void | 0 | 39 | 2 | @r0_38 | +| HierarchyConversions() -> void | 0 | 40 | 9 | @r0_34 | +| HierarchyConversions() -> void | 0 | 40 | 10 | this:@r0_33 | +| HierarchyConversions() -> void | 0 | 40 | 11 | @r0_39 | +| HierarchyConversions() -> void | 0 | 42 | 0 | @r0_41 | +| HierarchyConversions() -> void | 0 | 42 | 1 | @m0_16 | +| HierarchyConversions() -> void | 0 | 43 | 2 | @r0_42 | +| HierarchyConversions() -> void | 0 | 45 | 0 | @r0_44 | +| HierarchyConversions() -> void | 0 | 45 | 1 | @r0_43 | +| HierarchyConversions() -> void | 0 | 47 | 0 | @r0_46 | +| HierarchyConversions() -> void | 0 | 47 | 1 | @m0_16 | +| HierarchyConversions() -> void | 0 | 48 | 2 | @r0_47 | +| HierarchyConversions() -> void | 0 | 50 | 0 | @r0_49 | +| HierarchyConversions() -> void | 0 | 50 | 1 | @r0_48 | +| HierarchyConversions() -> void | 0 | 52 | 0 | @r0_51 | +| HierarchyConversions() -> void | 0 | 52 | 1 | @m0_16 | +| HierarchyConversions() -> void | 0 | 53 | 2 | @r0_52 | +| HierarchyConversions() -> void | 0 | 55 | 0 | @r0_54 | +| HierarchyConversions() -> void | 0 | 55 | 1 | @r0_53 | +| HierarchyConversions() -> void | 0 | 57 | 0 | @r0_56 | +| HierarchyConversions() -> void | 0 | 57 | 1 | @m0_16 | +| HierarchyConversions() -> void | 0 | 58 | 2 | @r0_57 | +| HierarchyConversions() -> void | 0 | 60 | 0 | @r0_59 | +| HierarchyConversions() -> void | 0 | 60 | 1 | @r0_58 | +| HierarchyConversions() -> void | 0 | 64 | 2 | @r0_63 | +| HierarchyConversions() -> void | 0 | 65 | 2 | @r0_64 | +| HierarchyConversions() -> void | 0 | 66 | 9 | @r0_62 | +| HierarchyConversions() -> void | 0 | 66 | 10 | this:@r0_61 | +| HierarchyConversions() -> void | 0 | 66 | 11 | @r0_65 | +| HierarchyConversions() -> void | 0 | 70 | 2 | @r0_69 | +| HierarchyConversions() -> void | 0 | 71 | 2 | @r0_70 | +| HierarchyConversions() -> void | 0 | 72 | 9 | @r0_68 | +| HierarchyConversions() -> void | 0 | 72 | 10 | this:@r0_67 | +| HierarchyConversions() -> void | 0 | 72 | 11 | @r0_71 | +| HierarchyConversions() -> void | 0 | 74 | 0 | @r0_73 | +| HierarchyConversions() -> void | 0 | 74 | 1 | @m0_60 | +| HierarchyConversions() -> void | 0 | 75 | 2 | @r0_74 | +| HierarchyConversions() -> void | 0 | 77 | 0 | @r0_76 | +| HierarchyConversions() -> void | 0 | 77 | 1 | @r0_75 | +| HierarchyConversions() -> void | 0 | 79 | 0 | @r0_78 | +| HierarchyConversions() -> void | 0 | 79 | 1 | @m0_60 | +| HierarchyConversions() -> void | 0 | 80 | 2 | @r0_79 | +| HierarchyConversions() -> void | 0 | 82 | 0 | @r0_81 | +| HierarchyConversions() -> void | 0 | 82 | 1 | @r0_80 | +| HierarchyConversions() -> void | 0 | 84 | 0 | @r0_83 | +| HierarchyConversions() -> void | 0 | 84 | 1 | @m0_60 | +| HierarchyConversions() -> void | 0 | 85 | 2 | @r0_84 | +| HierarchyConversions() -> void | 0 | 87 | 0 | @r0_86 | +| HierarchyConversions() -> void | 0 | 87 | 1 | @r0_85 | +| HierarchyConversions() -> void | 0 | 91 | 2 | @r0_90 | +| HierarchyConversions() -> void | 0 | 92 | 2 | @r0_91 | +| HierarchyConversions() -> void | 0 | 93 | 9 | @r0_89 | +| HierarchyConversions() -> void | 0 | 93 | 10 | this:@r0_88 | +| HierarchyConversions() -> void | 0 | 93 | 11 | @r0_92 | +| HierarchyConversions() -> void | 0 | 98 | 2 | @r0_97 | +| HierarchyConversions() -> void | 0 | 99 | 2 | @r0_98 | +| HierarchyConversions() -> void | 0 | 100 | 9 | @r0_96 | +| HierarchyConversions() -> void | 0 | 100 | 11 | @r0_99 | +| HierarchyConversions() -> void | 0 | 101 | 2 | @r0_100 | +| HierarchyConversions() -> void | 0 | 102 | 9 | @r0_95 | +| HierarchyConversions() -> void | 0 | 102 | 10 | this:@r0_94 | +| HierarchyConversions() -> void | 0 | 102 | 11 | @r0_101 | +| HierarchyConversions() -> void | 0 | 107 | 2 | @r0_106 | +| HierarchyConversions() -> void | 0 | 108 | 2 | @r0_107 | +| HierarchyConversions() -> void | 0 | 109 | 9 | @r0_105 | +| HierarchyConversions() -> void | 0 | 109 | 11 | @r0_108 | +| HierarchyConversions() -> void | 0 | 110 | 2 | @r0_109 | +| HierarchyConversions() -> void | 0 | 111 | 9 | @r0_104 | +| HierarchyConversions() -> void | 0 | 111 | 10 | this:@r0_103 | +| HierarchyConversions() -> void | 0 | 111 | 11 | @r0_110 | +| HierarchyConversions() -> void | 0 | 113 | 0 | @r0_112 | +| HierarchyConversions() -> void | 0 | 113 | 1 | @m0_19 | +| HierarchyConversions() -> void | 0 | 114 | 2 | @r0_113 | +| HierarchyConversions() -> void | 0 | 115 | 2 | @r0_114 | +| HierarchyConversions() -> void | 0 | 117 | 0 | @r0_116 | +| HierarchyConversions() -> void | 0 | 117 | 1 | @r0_115 | +| HierarchyConversions() -> void | 0 | 119 | 0 | @r0_118 | +| HierarchyConversions() -> void | 0 | 119 | 1 | @m0_19 | +| HierarchyConversions() -> void | 0 | 120 | 2 | @r0_119 | +| HierarchyConversions() -> void | 0 | 121 | 2 | @r0_120 | +| HierarchyConversions() -> void | 0 | 123 | 0 | @r0_122 | +| HierarchyConversions() -> void | 0 | 123 | 1 | @r0_121 | +| HierarchyConversions() -> void | 0 | 125 | 0 | @r0_124 | +| HierarchyConversions() -> void | 0 | 125 | 1 | @m0_19 | +| HierarchyConversions() -> void | 0 | 126 | 2 | @r0_125 | +| HierarchyConversions() -> void | 0 | 127 | 2 | @r0_126 | +| HierarchyConversions() -> void | 0 | 129 | 0 | @r0_128 | +| HierarchyConversions() -> void | 0 | 129 | 1 | @r0_127 | +| HierarchyConversions() -> void | 0 | 131 | 0 | @r0_130 | +| HierarchyConversions() -> void | 0 | 131 | 1 | @m0_19 | +| HierarchyConversions() -> void | 0 | 132 | 2 | @r0_131 | +| HierarchyConversions() -> void | 0 | 134 | 0 | @r0_133 | +| HierarchyConversions() -> void | 0 | 134 | 1 | @r0_132 | +| HierarchyConversions() -> void | 0 | 138 | 2 | @r0_137 | +| HierarchyConversions() -> void | 0 | 139 | 2 | @r0_138 | +| HierarchyConversions() -> void | 0 | 140 | 2 | @r0_139 | +| HierarchyConversions() -> void | 0 | 141 | 9 | @r0_136 | +| HierarchyConversions() -> void | 0 | 141 | 10 | this:@r0_135 | +| HierarchyConversions() -> void | 0 | 141 | 11 | @r0_140 | +| HierarchyConversions() -> void | 0 | 145 | 2 | @r0_144 | +| HierarchyConversions() -> void | 0 | 146 | 2 | @r0_145 | +| HierarchyConversions() -> void | 0 | 147 | 2 | @r0_146 | +| HierarchyConversions() -> void | 0 | 148 | 9 | @r0_143 | +| HierarchyConversions() -> void | 0 | 148 | 10 | this:@r0_142 | +| HierarchyConversions() -> void | 0 | 148 | 11 | @r0_147 | +| HierarchyConversions() -> void | 0 | 150 | 0 | @r0_149 | +| HierarchyConversions() -> void | 0 | 150 | 1 | @m0_134 | +| HierarchyConversions() -> void | 0 | 151 | 2 | @r0_150 | +| HierarchyConversions() -> void | 0 | 152 | 2 | @r0_151 | +| HierarchyConversions() -> void | 0 | 154 | 0 | @r0_153 | +| HierarchyConversions() -> void | 0 | 154 | 1 | @r0_152 | +| HierarchyConversions() -> void | 0 | 156 | 0 | @r0_155 | +| HierarchyConversions() -> void | 0 | 156 | 1 | @m0_134 | +| HierarchyConversions() -> void | 0 | 157 | 2 | @r0_156 | +| HierarchyConversions() -> void | 0 | 158 | 2 | @r0_157 | +| HierarchyConversions() -> void | 0 | 160 | 0 | @r0_159 | +| HierarchyConversions() -> void | 0 | 160 | 1 | @r0_158 | +| HierarchyConversions() -> void | 0 | 162 | 0 | @r0_161 | +| HierarchyConversions() -> void | 0 | 162 | 1 | @m0_134 | +| HierarchyConversions() -> void | 0 | 163 | 2 | @r0_162 | +| HierarchyConversions() -> void | 0 | 165 | 0 | @r0_164 | +| HierarchyConversions() -> void | 0 | 165 | 1 | @r0_163 | +| HierarchyConversions() -> void | 0 | 168 | 0 | @r0_166 | +| HierarchyConversions() -> void | 0 | 168 | 1 | @r0_167 | +| HierarchyConversions() -> void | 0 | 171 | 0 | @r0_169 | +| HierarchyConversions() -> void | 0 | 171 | 1 | @r0_170 | +| HierarchyConversions() -> void | 0 | 173 | 0 | @r0_172 | +| HierarchyConversions() -> void | 0 | 173 | 1 | @m0_168 | +| HierarchyConversions() -> void | 0 | 174 | 2 | @r0_173 | +| HierarchyConversions() -> void | 0 | 176 | 0 | @r0_175 | +| HierarchyConversions() -> void | 0 | 176 | 1 | @r0_174 | +| HierarchyConversions() -> void | 0 | 178 | 0 | @r0_177 | +| HierarchyConversions() -> void | 0 | 178 | 1 | @m0_171 | +| HierarchyConversions() -> void | 0 | 179 | 2 | @r0_178 | +| HierarchyConversions() -> void | 0 | 181 | 0 | @r0_180 | +| HierarchyConversions() -> void | 0 | 181 | 1 | @r0_179 | +| HierarchyConversions() -> void | 0 | 184 | 0 | @mu* | +| IfStatements(bool, int, int) -> void | 0 | 4 | 0 | @r0_3 | +| IfStatements(bool, int, int) -> void | 0 | 4 | 1 | @r0_2 | +| IfStatements(bool, int, int) -> void | 0 | 7 | 0 | @r0_6 | +| IfStatements(bool, int, int) -> void | 0 | 7 | 1 | @r0_5 | +| IfStatements(bool, int, int) -> void | 0 | 10 | 0 | @r0_9 | +| IfStatements(bool, int, int) -> void | 0 | 10 | 1 | @r0_8 | +| IfStatements(bool, int, int) -> void | 0 | 12 | 0 | @r0_11 | +| IfStatements(bool, int, int) -> void | 0 | 12 | 1 | @m0_4 | +| IfStatements(bool, int, int) -> void | 0 | 13 | 7 | @r0_12 | +| IfStatements(bool, int, int) -> void | 1 | 1 | 0 | @r1_0 | +| IfStatements(bool, int, int) -> void | 1 | 1 | 1 | @m0_4 | +| IfStatements(bool, int, int) -> void | 1 | 2 | 7 | @r1_1 | +| IfStatements(bool, int, int) -> void | 2 | 1 | 0 | @r2_0 | +| IfStatements(bool, int, int) -> void | 2 | 1 | 1 | @m0_10 | +| IfStatements(bool, int, int) -> void | 2 | 3 | 0 | @r2_2 | +| IfStatements(bool, int, int) -> void | 2 | 3 | 1 | @r2_1 | +| IfStatements(bool, int, int) -> void | 3 | 0 | 11 | from 1: @m0_7 | +| IfStatements(bool, int, int) -> void | 3 | 0 | 11 | from 2: @m2_3 | +| IfStatements(bool, int, int) -> void | 3 | 2 | 0 | @r3_1 | +| IfStatements(bool, int, int) -> void | 3 | 2 | 1 | @m3_0 | +| IfStatements(bool, int, int) -> void | 3 | 4 | 3 | @r3_2 | +| IfStatements(bool, int, int) -> void | 3 | 4 | 4 | @r3_3 | +| IfStatements(bool, int, int) -> void | 3 | 5 | 7 | @r3_4 | +| IfStatements(bool, int, int) -> void | 4 | 2 | 0 | @r4_1 | +| IfStatements(bool, int, int) -> void | 4 | 2 | 1 | @r4_0 | +| IfStatements(bool, int, int) -> void | 5 | 2 | 0 | @r5_1 | +| IfStatements(bool, int, int) -> void | 5 | 2 | 1 | @r5_0 | +| IfStatements(bool, int, int) -> void | 6 | 2 | 0 | @mu* | +| InitArray() -> void | 0 | 4 | 0 | @r0_3 | +| InitArray() -> void | 0 | 4 | 1 | @mu0_1 | +| InitArray() -> void | 0 | 5 | 0 | @r0_2 | +| InitArray() -> void | 0 | 5 | 1 | @r0_4 | +| InitArray() -> void | 0 | 8 | 3 | @r0_2 | +| InitArray() -> void | 0 | 8 | 4 | @r0_7 | +| InitArray() -> void | 0 | 9 | 0 | @r0_8 | +| InitArray() -> void | 0 | 9 | 1 | @r0_6 | +| InitArray() -> void | 0 | 12 | 0 | @r0_11 | +| InitArray() -> void | 0 | 12 | 1 | @mu0_1 | +| InitArray() -> void | 0 | 13 | 0 | @r0_10 | +| InitArray() -> void | 0 | 13 | 1 | @r0_12 | +| InitArray() -> void | 0 | 16 | 0 | @r0_15 | +| InitArray() -> void | 0 | 16 | 1 | @mu0_1 | +| InitArray() -> void | 0 | 17 | 0 | @r0_14 | +| InitArray() -> void | 0 | 17 | 1 | @r0_16 | +| InitArray() -> void | 0 | 20 | 0 | @r0_18 | +| InitArray() -> void | 0 | 20 | 1 | @r0_19 | +| InitArray() -> void | 0 | 23 | 3 | @r0_21 | +| InitArray() -> void | 0 | 23 | 4 | @r0_22 | +| InitArray() -> void | 0 | 25 | 0 | @r0_23 | +| InitArray() -> void | 0 | 25 | 1 | @r0_24 | +| InitArray() -> void | 0 | 28 | 3 | @r0_26 | +| InitArray() -> void | 0 | 28 | 4 | @r0_27 | +| InitArray() -> void | 0 | 30 | 0 | @r0_28 | +| InitArray() -> void | 0 | 30 | 1 | @r0_29 | +| InitArray() -> void | 0 | 32 | 3 | @r0_26 | +| InitArray() -> void | 0 | 32 | 4 | @r0_31 | +| InitArray() -> void | 0 | 34 | 0 | @r0_32 | +| InitArray() -> void | 0 | 34 | 1 | @r0_33 | +| InitArray() -> void | 0 | 37 | 3 | @r0_35 | +| InitArray() -> void | 0 | 37 | 4 | @r0_36 | +| InitArray() -> void | 0 | 39 | 0 | @r0_37 | +| InitArray() -> void | 0 | 39 | 1 | @r0_38 | +| InitArray() -> void | 0 | 41 | 3 | @r0_35 | +| InitArray() -> void | 0 | 41 | 4 | @r0_40 | +| InitArray() -> void | 0 | 43 | 0 | @r0_41 | +| InitArray() -> void | 0 | 43 | 1 | @r0_42 | +| InitArray() -> void | 0 | 46 | 3 | @r0_44 | +| InitArray() -> void | 0 | 46 | 4 | @r0_45 | +| InitArray() -> void | 0 | 48 | 0 | @r0_46 | +| InitArray() -> void | 0 | 48 | 1 | @r0_47 | +| InitArray() -> void | 0 | 50 | 3 | @r0_44 | +| InitArray() -> void | 0 | 50 | 4 | @r0_49 | +| InitArray() -> void | 0 | 52 | 0 | @r0_50 | +| InitArray() -> void | 0 | 52 | 1 | @r0_51 | +| InitArray() -> void | 0 | 55 | 0 | @mu* | +| InitList(int, float) -> void | 0 | 4 | 0 | @r0_3 | +| InitList(int, float) -> void | 0 | 4 | 1 | @r0_2 | +| InitList(int, float) -> void | 0 | 7 | 0 | @r0_6 | +| InitList(int, float) -> void | 0 | 7 | 1 | @r0_5 | +| InitList(int, float) -> void | 0 | 9 | 2 | @r0_8 | +| InitList(int, float) -> void | 0 | 11 | 0 | @r0_10 | +| InitList(int, float) -> void | 0 | 11 | 1 | @m0_4 | +| InitList(int, float) -> void | 0 | 12 | 0 | @r0_9 | +| InitList(int, float) -> void | 0 | 12 | 1 | @r0_11 | +| InitList(int, float) -> void | 0 | 13 | 2 | @r0_8 | +| InitList(int, float) -> void | 0 | 15 | 0 | @r0_14 | +| InitList(int, float) -> void | 0 | 15 | 1 | @m0_7 | +| InitList(int, float) -> void | 0 | 16 | 2 | @r0_15 | +| InitList(int, float) -> void | 0 | 17 | 0 | @r0_13 | +| InitList(int, float) -> void | 0 | 17 | 1 | @r0_16 | +| InitList(int, float) -> void | 0 | 19 | 2 | @r0_18 | +| InitList(int, float) -> void | 0 | 21 | 0 | @r0_20 | +| InitList(int, float) -> void | 0 | 21 | 1 | @m0_4 | +| InitList(int, float) -> void | 0 | 22 | 0 | @r0_19 | +| InitList(int, float) -> void | 0 | 22 | 1 | @r0_21 | +| InitList(int, float) -> void | 0 | 23 | 2 | @r0_18 | +| InitList(int, float) -> void | 0 | 25 | 0 | @r0_23 | +| InitList(int, float) -> void | 0 | 25 | 1 | @r0_24 | +| InitList(int, float) -> void | 0 | 27 | 2 | @r0_26 | +| InitList(int, float) -> void | 0 | 29 | 0 | @r0_27 | +| InitList(int, float) -> void | 0 | 29 | 1 | @r0_28 | +| InitList(int, float) -> void | 0 | 30 | 2 | @r0_26 | +| InitList(int, float) -> void | 0 | 32 | 0 | @r0_30 | +| InitList(int, float) -> void | 0 | 32 | 1 | @r0_31 | +| InitList(int, float) -> void | 0 | 35 | 0 | @r0_33 | +| InitList(int, float) -> void | 0 | 35 | 1 | @r0_34 | +| InitList(int, float) -> void | 0 | 38 | 0 | @r0_36 | +| InitList(int, float) -> void | 0 | 38 | 1 | @r0_37 | +| InitList(int, float) -> void | 0 | 41 | 0 | @mu* | +| InitReference(int) -> void | 0 | 4 | 0 | @r0_3 | +| InitReference(int) -> void | 0 | 4 | 1 | @r0_2 | +| InitReference(int) -> void | 0 | 7 | 0 | @r0_5 | +| InitReference(int) -> void | 0 | 7 | 1 | @r0_6 | +| InitReference(int) -> void | 0 | 10 | 0 | @r0_9 | +| InitReference(int) -> void | 0 | 10 | 1 | @m0_7 | +| InitReference(int) -> void | 0 | 11 | 0 | @r0_8 | +| InitReference(int) -> void | 0 | 11 | 1 | @r0_10 | +| InitReference(int) -> void | 0 | 14 | 9 | @r0_13 | +| InitReference(int) -> void | 0 | 15 | 2 | @r0_14 | +| InitReference(int) -> void | 0 | 16 | 0 | @r0_12 | +| InitReference(int) -> void | 0 | 16 | 1 | @r0_15 | +| InitReference(int) -> void | 0 | 19 | 0 | @mu* | +| IntegerCompare(int, int) -> void | 0 | 4 | 0 | @r0_3 | +| IntegerCompare(int, int) -> void | 0 | 4 | 1 | @r0_2 | +| IntegerCompare(int, int) -> void | 0 | 7 | 0 | @r0_6 | +| IntegerCompare(int, int) -> void | 0 | 7 | 1 | @r0_5 | +| IntegerCompare(int, int) -> void | 0 | 10 | 0 | @r0_8 | +| IntegerCompare(int, int) -> void | 0 | 10 | 1 | @r0_9 | +| IntegerCompare(int, int) -> void | 0 | 12 | 0 | @r0_11 | +| IntegerCompare(int, int) -> void | 0 | 12 | 1 | @m0_4 | +| IntegerCompare(int, int) -> void | 0 | 14 | 0 | @r0_13 | +| IntegerCompare(int, int) -> void | 0 | 14 | 1 | @m0_7 | +| IntegerCompare(int, int) -> void | 0 | 15 | 3 | @r0_12 | +| IntegerCompare(int, int) -> void | 0 | 15 | 4 | @r0_14 | +| IntegerCompare(int, int) -> void | 0 | 17 | 0 | @r0_16 | +| IntegerCompare(int, int) -> void | 0 | 17 | 1 | @r0_15 | +| IntegerCompare(int, int) -> void | 0 | 19 | 0 | @r0_18 | +| IntegerCompare(int, int) -> void | 0 | 19 | 1 | @m0_4 | +| IntegerCompare(int, int) -> void | 0 | 21 | 0 | @r0_20 | +| IntegerCompare(int, int) -> void | 0 | 21 | 1 | @m0_7 | +| IntegerCompare(int, int) -> void | 0 | 22 | 3 | @r0_19 | +| IntegerCompare(int, int) -> void | 0 | 22 | 4 | @r0_21 | +| IntegerCompare(int, int) -> void | 0 | 24 | 0 | @r0_23 | +| IntegerCompare(int, int) -> void | 0 | 24 | 1 | @r0_22 | +| IntegerCompare(int, int) -> void | 0 | 26 | 0 | @r0_25 | +| IntegerCompare(int, int) -> void | 0 | 26 | 1 | @m0_4 | +| IntegerCompare(int, int) -> void | 0 | 28 | 0 | @r0_27 | +| IntegerCompare(int, int) -> void | 0 | 28 | 1 | @m0_7 | +| IntegerCompare(int, int) -> void | 0 | 29 | 3 | @r0_26 | +| IntegerCompare(int, int) -> void | 0 | 29 | 4 | @r0_28 | +| IntegerCompare(int, int) -> void | 0 | 31 | 0 | @r0_30 | +| IntegerCompare(int, int) -> void | 0 | 31 | 1 | @r0_29 | +| IntegerCompare(int, int) -> void | 0 | 33 | 0 | @r0_32 | +| IntegerCompare(int, int) -> void | 0 | 33 | 1 | @m0_4 | +| IntegerCompare(int, int) -> void | 0 | 35 | 0 | @r0_34 | +| IntegerCompare(int, int) -> void | 0 | 35 | 1 | @m0_7 | +| IntegerCompare(int, int) -> void | 0 | 36 | 3 | @r0_33 | +| IntegerCompare(int, int) -> void | 0 | 36 | 4 | @r0_35 | +| IntegerCompare(int, int) -> void | 0 | 38 | 0 | @r0_37 | +| IntegerCompare(int, int) -> void | 0 | 38 | 1 | @r0_36 | +| IntegerCompare(int, int) -> void | 0 | 40 | 0 | @r0_39 | +| IntegerCompare(int, int) -> void | 0 | 40 | 1 | @m0_4 | +| IntegerCompare(int, int) -> void | 0 | 42 | 0 | @r0_41 | +| IntegerCompare(int, int) -> void | 0 | 42 | 1 | @m0_7 | +| IntegerCompare(int, int) -> void | 0 | 43 | 3 | @r0_40 | +| IntegerCompare(int, int) -> void | 0 | 43 | 4 | @r0_42 | +| IntegerCompare(int, int) -> void | 0 | 45 | 0 | @r0_44 | +| IntegerCompare(int, int) -> void | 0 | 45 | 1 | @r0_43 | +| IntegerCompare(int, int) -> void | 0 | 47 | 0 | @r0_46 | +| IntegerCompare(int, int) -> void | 0 | 47 | 1 | @m0_4 | +| IntegerCompare(int, int) -> void | 0 | 49 | 0 | @r0_48 | +| IntegerCompare(int, int) -> void | 0 | 49 | 1 | @m0_7 | +| IntegerCompare(int, int) -> void | 0 | 50 | 3 | @r0_47 | +| IntegerCompare(int, int) -> void | 0 | 50 | 4 | @r0_49 | +| IntegerCompare(int, int) -> void | 0 | 52 | 0 | @r0_51 | +| IntegerCompare(int, int) -> void | 0 | 52 | 1 | @r0_50 | +| IntegerCompare(int, int) -> void | 0 | 55 | 0 | @mu* | +| IntegerCrement(int) -> void | 0 | 4 | 0 | @r0_3 | +| IntegerCrement(int) -> void | 0 | 4 | 1 | @r0_2 | +| IntegerCrement(int) -> void | 0 | 7 | 0 | @r0_5 | +| IntegerCrement(int) -> void | 0 | 7 | 1 | @r0_6 | +| IntegerCrement(int) -> void | 0 | 9 | 0 | @r0_8 | +| IntegerCrement(int) -> void | 0 | 9 | 1 | @m0_4 | +| IntegerCrement(int) -> void | 0 | 11 | 3 | @r0_9 | +| IntegerCrement(int) -> void | 0 | 11 | 4 | @r0_10 | +| IntegerCrement(int) -> void | 0 | 12 | 0 | @r0_8 | +| IntegerCrement(int) -> void | 0 | 12 | 1 | @r0_11 | +| IntegerCrement(int) -> void | 0 | 14 | 0 | @r0_13 | +| IntegerCrement(int) -> void | 0 | 14 | 1 | @r0_11 | +| IntegerCrement(int) -> void | 0 | 16 | 0 | @r0_15 | +| IntegerCrement(int) -> void | 0 | 16 | 1 | @m0_12 | +| IntegerCrement(int) -> void | 0 | 18 | 3 | @r0_16 | +| IntegerCrement(int) -> void | 0 | 18 | 4 | @r0_17 | +| IntegerCrement(int) -> void | 0 | 19 | 0 | @r0_15 | +| IntegerCrement(int) -> void | 0 | 19 | 1 | @r0_18 | +| IntegerCrement(int) -> void | 0 | 21 | 0 | @r0_20 | +| IntegerCrement(int) -> void | 0 | 21 | 1 | @r0_18 | +| IntegerCrement(int) -> void | 0 | 23 | 0 | @r0_22 | +| IntegerCrement(int) -> void | 0 | 23 | 1 | @m0_19 | +| IntegerCrement(int) -> void | 0 | 25 | 3 | @r0_23 | +| IntegerCrement(int) -> void | 0 | 25 | 4 | @r0_24 | +| IntegerCrement(int) -> void | 0 | 26 | 0 | @r0_22 | +| IntegerCrement(int) -> void | 0 | 26 | 1 | @r0_25 | +| IntegerCrement(int) -> void | 0 | 28 | 0 | @r0_27 | +| IntegerCrement(int) -> void | 0 | 28 | 1 | @r0_23 | +| IntegerCrement(int) -> void | 0 | 30 | 0 | @r0_29 | +| IntegerCrement(int) -> void | 0 | 30 | 1 | @m0_26 | +| IntegerCrement(int) -> void | 0 | 32 | 3 | @r0_30 | +| IntegerCrement(int) -> void | 0 | 32 | 4 | @r0_31 | +| IntegerCrement(int) -> void | 0 | 33 | 0 | @r0_29 | +| IntegerCrement(int) -> void | 0 | 33 | 1 | @r0_32 | +| IntegerCrement(int) -> void | 0 | 35 | 0 | @r0_34 | +| IntegerCrement(int) -> void | 0 | 35 | 1 | @r0_30 | +| IntegerCrement(int) -> void | 0 | 38 | 0 | @mu* | +| IntegerCrement_LValue(int) -> void | 0 | 4 | 0 | @r0_3 | +| IntegerCrement_LValue(int) -> void | 0 | 4 | 1 | @r0_2 | +| IntegerCrement_LValue(int) -> void | 0 | 7 | 0 | @r0_5 | +| IntegerCrement_LValue(int) -> void | 0 | 7 | 1 | @r0_6 | +| IntegerCrement_LValue(int) -> void | 0 | 9 | 0 | @r0_8 | +| IntegerCrement_LValue(int) -> void | 0 | 9 | 1 | @m0_4 | +| IntegerCrement_LValue(int) -> void | 0 | 11 | 3 | @r0_9 | +| IntegerCrement_LValue(int) -> void | 0 | 11 | 4 | @r0_10 | +| IntegerCrement_LValue(int) -> void | 0 | 12 | 0 | @r0_8 | +| IntegerCrement_LValue(int) -> void | 0 | 12 | 1 | @r0_11 | +| IntegerCrement_LValue(int) -> void | 0 | 14 | 0 | @r0_13 | +| IntegerCrement_LValue(int) -> void | 0 | 14 | 1 | @r0_8 | +| IntegerCrement_LValue(int) -> void | 0 | 16 | 0 | @r0_15 | +| IntegerCrement_LValue(int) -> void | 0 | 16 | 1 | @m0_12 | +| IntegerCrement_LValue(int) -> void | 0 | 18 | 3 | @r0_16 | +| IntegerCrement_LValue(int) -> void | 0 | 18 | 4 | @r0_17 | +| IntegerCrement_LValue(int) -> void | 0 | 19 | 0 | @r0_15 | +| IntegerCrement_LValue(int) -> void | 0 | 19 | 1 | @r0_18 | +| IntegerCrement_LValue(int) -> void | 0 | 21 | 0 | @r0_20 | +| IntegerCrement_LValue(int) -> void | 0 | 21 | 1 | @r0_15 | +| IntegerCrement_LValue(int) -> void | 0 | 24 | 0 | @mu* | +| IntegerOps(int, int) -> void | 0 | 4 | 0 | @r0_3 | +| IntegerOps(int, int) -> void | 0 | 4 | 1 | @r0_2 | +| IntegerOps(int, int) -> void | 0 | 7 | 0 | @r0_6 | +| IntegerOps(int, int) -> void | 0 | 7 | 1 | @r0_5 | +| IntegerOps(int, int) -> void | 0 | 10 | 0 | @r0_8 | +| IntegerOps(int, int) -> void | 0 | 10 | 1 | @r0_9 | +| IntegerOps(int, int) -> void | 0 | 12 | 0 | @r0_11 | +| IntegerOps(int, int) -> void | 0 | 12 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 14 | 0 | @r0_13 | +| IntegerOps(int, int) -> void | 0 | 14 | 1 | @m0_7 | +| IntegerOps(int, int) -> void | 0 | 15 | 3 | @r0_12 | +| IntegerOps(int, int) -> void | 0 | 15 | 4 | @r0_14 | +| IntegerOps(int, int) -> void | 0 | 17 | 0 | @r0_16 | +| IntegerOps(int, int) -> void | 0 | 17 | 1 | @r0_15 | +| IntegerOps(int, int) -> void | 0 | 19 | 0 | @r0_18 | +| IntegerOps(int, int) -> void | 0 | 19 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 21 | 0 | @r0_20 | +| IntegerOps(int, int) -> void | 0 | 21 | 1 | @m0_7 | +| IntegerOps(int, int) -> void | 0 | 22 | 3 | @r0_19 | +| IntegerOps(int, int) -> void | 0 | 22 | 4 | @r0_21 | +| IntegerOps(int, int) -> void | 0 | 24 | 0 | @r0_23 | +| IntegerOps(int, int) -> void | 0 | 24 | 1 | @r0_22 | +| IntegerOps(int, int) -> void | 0 | 26 | 0 | @r0_25 | +| IntegerOps(int, int) -> void | 0 | 26 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 28 | 0 | @r0_27 | +| IntegerOps(int, int) -> void | 0 | 28 | 1 | @m0_7 | +| IntegerOps(int, int) -> void | 0 | 29 | 3 | @r0_26 | +| IntegerOps(int, int) -> void | 0 | 29 | 4 | @r0_28 | +| IntegerOps(int, int) -> void | 0 | 31 | 0 | @r0_30 | +| IntegerOps(int, int) -> void | 0 | 31 | 1 | @r0_29 | +| IntegerOps(int, int) -> void | 0 | 33 | 0 | @r0_32 | +| IntegerOps(int, int) -> void | 0 | 33 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 35 | 0 | @r0_34 | +| IntegerOps(int, int) -> void | 0 | 35 | 1 | @m0_7 | +| IntegerOps(int, int) -> void | 0 | 36 | 3 | @r0_33 | +| IntegerOps(int, int) -> void | 0 | 36 | 4 | @r0_35 | +| IntegerOps(int, int) -> void | 0 | 38 | 0 | @r0_37 | +| IntegerOps(int, int) -> void | 0 | 38 | 1 | @r0_36 | +| IntegerOps(int, int) -> void | 0 | 40 | 0 | @r0_39 | +| IntegerOps(int, int) -> void | 0 | 40 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 42 | 0 | @r0_41 | +| IntegerOps(int, int) -> void | 0 | 42 | 1 | @m0_7 | +| IntegerOps(int, int) -> void | 0 | 43 | 3 | @r0_40 | +| IntegerOps(int, int) -> void | 0 | 43 | 4 | @r0_42 | +| IntegerOps(int, int) -> void | 0 | 45 | 0 | @r0_44 | +| IntegerOps(int, int) -> void | 0 | 45 | 1 | @r0_43 | +| IntegerOps(int, int) -> void | 0 | 47 | 0 | @r0_46 | +| IntegerOps(int, int) -> void | 0 | 47 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 49 | 0 | @r0_48 | +| IntegerOps(int, int) -> void | 0 | 49 | 1 | @m0_7 | +| IntegerOps(int, int) -> void | 0 | 50 | 3 | @r0_47 | +| IntegerOps(int, int) -> void | 0 | 50 | 4 | @r0_49 | +| IntegerOps(int, int) -> void | 0 | 52 | 0 | @r0_51 | +| IntegerOps(int, int) -> void | 0 | 52 | 1 | @r0_50 | +| IntegerOps(int, int) -> void | 0 | 54 | 0 | @r0_53 | +| IntegerOps(int, int) -> void | 0 | 54 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 56 | 0 | @r0_55 | +| IntegerOps(int, int) -> void | 0 | 56 | 1 | @m0_7 | +| IntegerOps(int, int) -> void | 0 | 57 | 3 | @r0_54 | +| IntegerOps(int, int) -> void | 0 | 57 | 4 | @r0_56 | +| IntegerOps(int, int) -> void | 0 | 59 | 0 | @r0_58 | +| IntegerOps(int, int) -> void | 0 | 59 | 1 | @r0_57 | +| IntegerOps(int, int) -> void | 0 | 61 | 0 | @r0_60 | +| IntegerOps(int, int) -> void | 0 | 61 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 63 | 0 | @r0_62 | +| IntegerOps(int, int) -> void | 0 | 63 | 1 | @m0_7 | +| IntegerOps(int, int) -> void | 0 | 64 | 3 | @r0_61 | +| IntegerOps(int, int) -> void | 0 | 64 | 4 | @r0_63 | +| IntegerOps(int, int) -> void | 0 | 66 | 0 | @r0_65 | +| IntegerOps(int, int) -> void | 0 | 66 | 1 | @r0_64 | +| IntegerOps(int, int) -> void | 0 | 68 | 0 | @r0_67 | +| IntegerOps(int, int) -> void | 0 | 68 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 70 | 0 | @r0_69 | +| IntegerOps(int, int) -> void | 0 | 70 | 1 | @m0_7 | +| IntegerOps(int, int) -> void | 0 | 71 | 3 | @r0_68 | +| IntegerOps(int, int) -> void | 0 | 71 | 4 | @r0_70 | +| IntegerOps(int, int) -> void | 0 | 73 | 0 | @r0_72 | +| IntegerOps(int, int) -> void | 0 | 73 | 1 | @r0_71 | +| IntegerOps(int, int) -> void | 0 | 75 | 0 | @r0_74 | +| IntegerOps(int, int) -> void | 0 | 75 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 77 | 0 | @r0_76 | +| IntegerOps(int, int) -> void | 0 | 77 | 1 | @m0_7 | +| IntegerOps(int, int) -> void | 0 | 78 | 3 | @r0_75 | +| IntegerOps(int, int) -> void | 0 | 78 | 4 | @r0_77 | +| IntegerOps(int, int) -> void | 0 | 80 | 0 | @r0_79 | +| IntegerOps(int, int) -> void | 0 | 80 | 1 | @r0_78 | +| IntegerOps(int, int) -> void | 0 | 82 | 0 | @r0_81 | +| IntegerOps(int, int) -> void | 0 | 82 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 84 | 0 | @r0_83 | +| IntegerOps(int, int) -> void | 0 | 84 | 1 | @r0_82 | +| IntegerOps(int, int) -> void | 0 | 86 | 0 | @r0_85 | +| IntegerOps(int, int) -> void | 0 | 86 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 88 | 0 | @r0_87 | +| IntegerOps(int, int) -> void | 0 | 88 | 1 | @m0_84 | +| IntegerOps(int, int) -> void | 0 | 89 | 3 | @r0_88 | +| IntegerOps(int, int) -> void | 0 | 89 | 4 | @r0_86 | +| IntegerOps(int, int) -> void | 0 | 90 | 0 | @r0_87 | +| IntegerOps(int, int) -> void | 0 | 90 | 1 | @r0_89 | +| IntegerOps(int, int) -> void | 0 | 92 | 0 | @r0_91 | +| IntegerOps(int, int) -> void | 0 | 92 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 94 | 0 | @r0_93 | +| IntegerOps(int, int) -> void | 0 | 94 | 1 | @m0_90 | +| IntegerOps(int, int) -> void | 0 | 95 | 3 | @r0_94 | +| IntegerOps(int, int) -> void | 0 | 95 | 4 | @r0_92 | +| IntegerOps(int, int) -> void | 0 | 96 | 0 | @r0_93 | +| IntegerOps(int, int) -> void | 0 | 96 | 1 | @r0_95 | +| IntegerOps(int, int) -> void | 0 | 98 | 0 | @r0_97 | +| IntegerOps(int, int) -> void | 0 | 98 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 100 | 0 | @r0_99 | +| IntegerOps(int, int) -> void | 0 | 100 | 1 | @m0_96 | +| IntegerOps(int, int) -> void | 0 | 101 | 3 | @r0_100 | +| IntegerOps(int, int) -> void | 0 | 101 | 4 | @r0_98 | +| IntegerOps(int, int) -> void | 0 | 102 | 0 | @r0_99 | +| IntegerOps(int, int) -> void | 0 | 102 | 1 | @r0_101 | +| IntegerOps(int, int) -> void | 0 | 104 | 0 | @r0_103 | +| IntegerOps(int, int) -> void | 0 | 104 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 106 | 0 | @r0_105 | +| IntegerOps(int, int) -> void | 0 | 106 | 1 | @m0_102 | +| IntegerOps(int, int) -> void | 0 | 107 | 3 | @r0_106 | +| IntegerOps(int, int) -> void | 0 | 107 | 4 | @r0_104 | +| IntegerOps(int, int) -> void | 0 | 108 | 0 | @r0_105 | +| IntegerOps(int, int) -> void | 0 | 108 | 1 | @r0_107 | +| IntegerOps(int, int) -> void | 0 | 110 | 0 | @r0_109 | +| IntegerOps(int, int) -> void | 0 | 110 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 112 | 0 | @r0_111 | +| IntegerOps(int, int) -> void | 0 | 112 | 1 | @m0_108 | +| IntegerOps(int, int) -> void | 0 | 113 | 3 | @r0_112 | +| IntegerOps(int, int) -> void | 0 | 113 | 4 | @r0_110 | +| IntegerOps(int, int) -> void | 0 | 114 | 0 | @r0_111 | +| IntegerOps(int, int) -> void | 0 | 114 | 1 | @r0_113 | +| IntegerOps(int, int) -> void | 0 | 116 | 0 | @r0_115 | +| IntegerOps(int, int) -> void | 0 | 116 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 118 | 0 | @r0_117 | +| IntegerOps(int, int) -> void | 0 | 118 | 1 | @m0_114 | +| IntegerOps(int, int) -> void | 0 | 119 | 3 | @r0_118 | +| IntegerOps(int, int) -> void | 0 | 119 | 4 | @r0_116 | +| IntegerOps(int, int) -> void | 0 | 120 | 0 | @r0_117 | +| IntegerOps(int, int) -> void | 0 | 120 | 1 | @r0_119 | +| IntegerOps(int, int) -> void | 0 | 122 | 0 | @r0_121 | +| IntegerOps(int, int) -> void | 0 | 122 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 124 | 0 | @r0_123 | +| IntegerOps(int, int) -> void | 0 | 124 | 1 | @m0_120 | +| IntegerOps(int, int) -> void | 0 | 125 | 3 | @r0_124 | +| IntegerOps(int, int) -> void | 0 | 125 | 4 | @r0_122 | +| IntegerOps(int, int) -> void | 0 | 126 | 0 | @r0_123 | +| IntegerOps(int, int) -> void | 0 | 126 | 1 | @r0_125 | +| IntegerOps(int, int) -> void | 0 | 128 | 0 | @r0_127 | +| IntegerOps(int, int) -> void | 0 | 128 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 130 | 0 | @r0_129 | +| IntegerOps(int, int) -> void | 0 | 130 | 1 | @m0_126 | +| IntegerOps(int, int) -> void | 0 | 131 | 3 | @r0_130 | +| IntegerOps(int, int) -> void | 0 | 131 | 4 | @r0_128 | +| IntegerOps(int, int) -> void | 0 | 132 | 0 | @r0_129 | +| IntegerOps(int, int) -> void | 0 | 132 | 1 | @r0_131 | +| IntegerOps(int, int) -> void | 0 | 134 | 0 | @r0_133 | +| IntegerOps(int, int) -> void | 0 | 134 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 136 | 0 | @r0_135 | +| IntegerOps(int, int) -> void | 0 | 136 | 1 | @m0_132 | +| IntegerOps(int, int) -> void | 0 | 137 | 3 | @r0_136 | +| IntegerOps(int, int) -> void | 0 | 137 | 4 | @r0_134 | +| IntegerOps(int, int) -> void | 0 | 138 | 0 | @r0_135 | +| IntegerOps(int, int) -> void | 0 | 138 | 1 | @r0_137 | +| IntegerOps(int, int) -> void | 0 | 140 | 0 | @r0_139 | +| IntegerOps(int, int) -> void | 0 | 140 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 142 | 0 | @r0_141 | +| IntegerOps(int, int) -> void | 0 | 142 | 1 | @m0_138 | +| IntegerOps(int, int) -> void | 0 | 143 | 3 | @r0_142 | +| IntegerOps(int, int) -> void | 0 | 143 | 4 | @r0_140 | +| IntegerOps(int, int) -> void | 0 | 144 | 0 | @r0_141 | +| IntegerOps(int, int) -> void | 0 | 144 | 1 | @r0_143 | +| IntegerOps(int, int) -> void | 0 | 146 | 0 | @r0_145 | +| IntegerOps(int, int) -> void | 0 | 146 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 147 | 1 | @r0_146 | +| IntegerOps(int, int) -> void | 0 | 149 | 0 | @r0_148 | +| IntegerOps(int, int) -> void | 0 | 149 | 1 | @r0_147 | +| IntegerOps(int, int) -> void | 0 | 151 | 0 | @r0_150 | +| IntegerOps(int, int) -> void | 0 | 151 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 152 | 2 | @r0_151 | +| IntegerOps(int, int) -> void | 0 | 154 | 0 | @r0_153 | +| IntegerOps(int, int) -> void | 0 | 154 | 1 | @r0_152 | +| IntegerOps(int, int) -> void | 0 | 156 | 0 | @r0_155 | +| IntegerOps(int, int) -> void | 0 | 156 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 157 | 2 | @r0_156 | +| IntegerOps(int, int) -> void | 0 | 159 | 0 | @r0_158 | +| IntegerOps(int, int) -> void | 0 | 159 | 1 | @r0_157 | +| IntegerOps(int, int) -> void | 0 | 161 | 0 | @r0_160 | +| IntegerOps(int, int) -> void | 0 | 161 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 163 | 3 | @r0_161 | +| IntegerOps(int, int) -> void | 0 | 163 | 4 | @r0_162 | +| IntegerOps(int, int) -> void | 0 | 164 | 2 | @r0_163 | +| IntegerOps(int, int) -> void | 0 | 165 | 2 | @r0_164 | +| IntegerOps(int, int) -> void | 0 | 167 | 0 | @r0_166 | +| IntegerOps(int, int) -> void | 0 | 167 | 1 | @r0_165 | +| IntegerOps(int, int) -> void | 0 | 170 | 0 | @mu* | +| LogicalAnd(bool, bool) -> void | 0 | 4 | 0 | @r0_3 | +| LogicalAnd(bool, bool) -> void | 0 | 4 | 1 | @r0_2 | +| LogicalAnd(bool, bool) -> void | 0 | 7 | 0 | @r0_6 | +| LogicalAnd(bool, bool) -> void | 0 | 7 | 1 | @r0_5 | +| LogicalAnd(bool, bool) -> void | 0 | 10 | 0 | @r0_8 | +| LogicalAnd(bool, bool) -> void | 0 | 10 | 1 | @r0_9 | +| LogicalAnd(bool, bool) -> void | 0 | 12 | 0 | @r0_11 | +| LogicalAnd(bool, bool) -> void | 0 | 12 | 1 | @m0_4 | +| LogicalAnd(bool, bool) -> void | 0 | 13 | 7 | @r0_12 | +| LogicalAnd(bool, bool) -> void | 1 | 1 | 0 | @r1_0 | +| LogicalAnd(bool, bool) -> void | 1 | 1 | 1 | @m0_7 | +| LogicalAnd(bool, bool) -> void | 1 | 2 | 7 | @r1_1 | +| LogicalAnd(bool, bool) -> void | 2 | 2 | 0 | @r2_1 | +| LogicalAnd(bool, bool) -> void | 2 | 2 | 1 | @r2_0 | +| LogicalAnd(bool, bool) -> void | 3 | 1 | 0 | @r3_0 | +| LogicalAnd(bool, bool) -> void | 3 | 1 | 1 | @m0_4 | +| LogicalAnd(bool, bool) -> void | 3 | 2 | 7 | @r3_1 | +| LogicalAnd(bool, bool) -> void | 4 | 1 | 0 | @r4_0 | +| LogicalAnd(bool, bool) -> void | 4 | 1 | 1 | @m0_7 | +| LogicalAnd(bool, bool) -> void | 4 | 2 | 7 | @r4_1 | +| LogicalAnd(bool, bool) -> void | 5 | 2 | 0 | @r5_1 | +| LogicalAnd(bool, bool) -> void | 5 | 2 | 1 | @r5_0 | +| LogicalAnd(bool, bool) -> void | 6 | 2 | 0 | @r6_1 | +| LogicalAnd(bool, bool) -> void | 6 | 2 | 1 | @r6_0 | +| LogicalAnd(bool, bool) -> void | 7 | 2 | 0 | @mu* | +| LogicalNot(bool, bool) -> void | 0 | 4 | 0 | @r0_3 | +| LogicalNot(bool, bool) -> void | 0 | 4 | 1 | @r0_2 | +| LogicalNot(bool, bool) -> void | 0 | 7 | 0 | @r0_6 | +| LogicalNot(bool, bool) -> void | 0 | 7 | 1 | @r0_5 | +| LogicalNot(bool, bool) -> void | 0 | 10 | 0 | @r0_8 | +| LogicalNot(bool, bool) -> void | 0 | 10 | 1 | @r0_9 | +| LogicalNot(bool, bool) -> void | 0 | 12 | 0 | @r0_11 | +| LogicalNot(bool, bool) -> void | 0 | 12 | 1 | @m0_4 | +| LogicalNot(bool, bool) -> void | 0 | 13 | 7 | @r0_12 | +| LogicalNot(bool, bool) -> void | 1 | 2 | 0 | @r1_1 | +| LogicalNot(bool, bool) -> void | 1 | 2 | 1 | @r1_0 | +| LogicalNot(bool, bool) -> void | 2 | 1 | 0 | @r2_0 | +| LogicalNot(bool, bool) -> void | 2 | 1 | 1 | @m0_4 | +| LogicalNot(bool, bool) -> void | 2 | 2 | 7 | @r2_1 | +| LogicalNot(bool, bool) -> void | 3 | 1 | 0 | @r3_0 | +| LogicalNot(bool, bool) -> void | 3 | 1 | 1 | @m0_7 | +| LogicalNot(bool, bool) -> void | 3 | 2 | 7 | @r3_1 | +| LogicalNot(bool, bool) -> void | 4 | 2 | 0 | @r4_1 | +| LogicalNot(bool, bool) -> void | 4 | 2 | 1 | @r4_0 | +| LogicalNot(bool, bool) -> void | 5 | 2 | 0 | @r5_1 | +| LogicalNot(bool, bool) -> void | 5 | 2 | 1 | @r5_0 | +| LogicalNot(bool, bool) -> void | 6 | 2 | 0 | @mu* | +| LogicalOr(bool, bool) -> void | 0 | 4 | 0 | @r0_3 | +| LogicalOr(bool, bool) -> void | 0 | 4 | 1 | @r0_2 | +| LogicalOr(bool, bool) -> void | 0 | 7 | 0 | @r0_6 | +| LogicalOr(bool, bool) -> void | 0 | 7 | 1 | @r0_5 | +| LogicalOr(bool, bool) -> void | 0 | 10 | 0 | @r0_8 | +| LogicalOr(bool, bool) -> void | 0 | 10 | 1 | @r0_9 | +| LogicalOr(bool, bool) -> void | 0 | 12 | 0 | @r0_11 | +| LogicalOr(bool, bool) -> void | 0 | 12 | 1 | @m0_4 | +| LogicalOr(bool, bool) -> void | 0 | 13 | 7 | @r0_12 | +| LogicalOr(bool, bool) -> void | 1 | 1 | 0 | @r1_0 | +| LogicalOr(bool, bool) -> void | 1 | 1 | 1 | @m0_7 | +| LogicalOr(bool, bool) -> void | 1 | 2 | 7 | @r1_1 | +| LogicalOr(bool, bool) -> void | 2 | 2 | 0 | @r2_1 | +| LogicalOr(bool, bool) -> void | 2 | 2 | 1 | @r2_0 | +| LogicalOr(bool, bool) -> void | 3 | 1 | 0 | @r3_0 | +| LogicalOr(bool, bool) -> void | 3 | 1 | 1 | @m0_4 | +| LogicalOr(bool, bool) -> void | 3 | 2 | 7 | @r3_1 | +| LogicalOr(bool, bool) -> void | 4 | 1 | 0 | @r4_0 | +| LogicalOr(bool, bool) -> void | 4 | 1 | 1 | @m0_7 | +| LogicalOr(bool, bool) -> void | 4 | 2 | 7 | @r4_1 | +| LogicalOr(bool, bool) -> void | 5 | 2 | 0 | @r5_1 | +| LogicalOr(bool, bool) -> void | 5 | 2 | 1 | @r5_0 | +| LogicalOr(bool, bool) -> void | 6 | 2 | 0 | @r6_1 | +| LogicalOr(bool, bool) -> void | 6 | 2 | 1 | @r6_0 | +| LogicalOr(bool, bool) -> void | 7 | 2 | 0 | @mu* | +| Middle::Middle() -> void | 0 | 3 | 2 | @r0_2 | +| Middle::Middle() -> void | 0 | 5 | 9 | @r0_4 | +| Middle::Middle() -> void | 0 | 5 | 10 | this:@r0_3 | +| Middle::Middle() -> void | 0 | 6 | 2 | @r0_2 | +| Middle::Middle() -> void | 0 | 8 | 9 | @r0_7 | +| Middle::Middle() -> void | 0 | 8 | 10 | this:@r0_6 | +| Middle::Middle() -> void | 0 | 11 | 0 | @mu* | +| Middle::operator=(const Middle &) -> Middle & | 0 | 5 | 0 | @r0_4 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 5 | 1 | @r0_3 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 6 | 1 | @r0_2 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 7 | 2 | @r0_6 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 10 | 0 | @r0_9 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 10 | 1 | @m0_5 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 11 | 2 | @r0_10 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 12 | 9 | @r0_8 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 12 | 10 | this:@r0_7 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 12 | 11 | @r0_11 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 13 | 1 | @r0_2 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 14 | 2 | @r0_13 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 17 | 0 | @r0_16 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 17 | 1 | @m0_5 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 18 | 2 | @r0_17 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 19 | 9 | @r0_15 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 19 | 10 | this:@r0_14 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 19 | 11 | @r0_18 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 21 | 1 | @r0_2 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 22 | 0 | @r0_20 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 22 | 1 | @r0_21 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 24 | 0 | @r0_23 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 24 | 5 | @m0_22 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 25 | 0 | @mu* | +| Middle::~Middle() -> void | 0 | 4 | 2 | @r0_2 | +| Middle::~Middle() -> void | 0 | 6 | 9 | @r0_5 | +| Middle::~Middle() -> void | 0 | 6 | 10 | this:@r0_4 | +| Middle::~Middle() -> void | 0 | 7 | 2 | @r0_2 | +| Middle::~Middle() -> void | 0 | 9 | 9 | @r0_8 | +| Middle::~Middle() -> void | 0 | 9 | 10 | this:@r0_7 | +| Middle::~Middle() -> void | 0 | 11 | 0 | @mu* | +| MiddleVB1::MiddleVB1() -> void | 0 | 3 | 2 | @r0_2 | +| MiddleVB1::MiddleVB1() -> void | 0 | 5 | 9 | @r0_4 | +| MiddleVB1::MiddleVB1() -> void | 0 | 5 | 10 | this:@r0_3 | +| MiddleVB1::MiddleVB1() -> void | 0 | 6 | 2 | @r0_2 | +| MiddleVB1::MiddleVB1() -> void | 0 | 8 | 9 | @r0_7 | +| MiddleVB1::MiddleVB1() -> void | 0 | 8 | 10 | this:@r0_6 | +| MiddleVB1::MiddleVB1() -> void | 0 | 11 | 0 | @mu* | +| MiddleVB1::~MiddleVB1() -> void | 0 | 4 | 2 | @r0_2 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 6 | 9 | @r0_5 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 6 | 10 | this:@r0_4 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 7 | 2 | @r0_2 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 9 | 9 | @r0_8 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 9 | 10 | this:@r0_7 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 11 | 0 | @mu* | +| MiddleVB2::MiddleVB2() -> void | 0 | 3 | 2 | @r0_2 | +| MiddleVB2::MiddleVB2() -> void | 0 | 5 | 9 | @r0_4 | +| MiddleVB2::MiddleVB2() -> void | 0 | 5 | 10 | this:@r0_3 | +| MiddleVB2::MiddleVB2() -> void | 0 | 6 | 2 | @r0_2 | +| MiddleVB2::MiddleVB2() -> void | 0 | 8 | 9 | @r0_7 | +| MiddleVB2::MiddleVB2() -> void | 0 | 8 | 10 | this:@r0_6 | +| MiddleVB2::MiddleVB2() -> void | 0 | 11 | 0 | @mu* | +| MiddleVB2::~MiddleVB2() -> void | 0 | 4 | 2 | @r0_2 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 6 | 9 | @r0_5 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 6 | 10 | this:@r0_4 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 7 | 2 | @r0_2 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 9 | 9 | @r0_8 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 9 | 10 | this:@r0_7 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 11 | 0 | @mu* | +| NestedInitList(int, float) -> void | 0 | 4 | 0 | @r0_3 | +| NestedInitList(int, float) -> void | 0 | 4 | 1 | @r0_2 | +| NestedInitList(int, float) -> void | 0 | 7 | 0 | @r0_6 | +| NestedInitList(int, float) -> void | 0 | 7 | 1 | @r0_5 | +| NestedInitList(int, float) -> void | 0 | 9 | 2 | @r0_8 | +| NestedInitList(int, float) -> void | 0 | 11 | 0 | @r0_9 | +| NestedInitList(int, float) -> void | 0 | 11 | 1 | @r0_10 | +| NestedInitList(int, float) -> void | 0 | 12 | 2 | @r0_8 | +| NestedInitList(int, float) -> void | 0 | 14 | 0 | @r0_12 | +| NestedInitList(int, float) -> void | 0 | 14 | 1 | @r0_13 | +| NestedInitList(int, float) -> void | 0 | 16 | 2 | @r0_15 | +| NestedInitList(int, float) -> void | 0 | 17 | 2 | @r0_16 | +| NestedInitList(int, float) -> void | 0 | 19 | 0 | @r0_18 | +| NestedInitList(int, float) -> void | 0 | 19 | 1 | @m0_4 | +| NestedInitList(int, float) -> void | 0 | 20 | 0 | @r0_17 | +| NestedInitList(int, float) -> void | 0 | 20 | 1 | @r0_19 | +| NestedInitList(int, float) -> void | 0 | 21 | 2 | @r0_16 | +| NestedInitList(int, float) -> void | 0 | 23 | 0 | @r0_22 | +| NestedInitList(int, float) -> void | 0 | 23 | 1 | @m0_7 | +| NestedInitList(int, float) -> void | 0 | 24 | 2 | @r0_23 | +| NestedInitList(int, float) -> void | 0 | 25 | 0 | @r0_21 | +| NestedInitList(int, float) -> void | 0 | 25 | 1 | @r0_24 | +| NestedInitList(int, float) -> void | 0 | 26 | 2 | @r0_15 | +| NestedInitList(int, float) -> void | 0 | 28 | 0 | @r0_26 | +| NestedInitList(int, float) -> void | 0 | 28 | 1 | @r0_27 | +| NestedInitList(int, float) -> void | 0 | 30 | 2 | @r0_29 | +| NestedInitList(int, float) -> void | 0 | 31 | 2 | @r0_30 | +| NestedInitList(int, float) -> void | 0 | 33 | 0 | @r0_32 | +| NestedInitList(int, float) -> void | 0 | 33 | 1 | @m0_4 | +| NestedInitList(int, float) -> void | 0 | 34 | 0 | @r0_31 | +| NestedInitList(int, float) -> void | 0 | 34 | 1 | @r0_33 | +| NestedInitList(int, float) -> void | 0 | 35 | 2 | @r0_30 | +| NestedInitList(int, float) -> void | 0 | 37 | 0 | @r0_36 | +| NestedInitList(int, float) -> void | 0 | 37 | 1 | @m0_7 | +| NestedInitList(int, float) -> void | 0 | 38 | 2 | @r0_37 | +| NestedInitList(int, float) -> void | 0 | 39 | 0 | @r0_35 | +| NestedInitList(int, float) -> void | 0 | 39 | 1 | @r0_38 | +| NestedInitList(int, float) -> void | 0 | 40 | 2 | @r0_29 | +| NestedInitList(int, float) -> void | 0 | 41 | 2 | @r0_40 | +| NestedInitList(int, float) -> void | 0 | 43 | 0 | @r0_42 | +| NestedInitList(int, float) -> void | 0 | 43 | 1 | @m0_4 | +| NestedInitList(int, float) -> void | 0 | 44 | 0 | @r0_41 | +| NestedInitList(int, float) -> void | 0 | 44 | 1 | @r0_43 | +| NestedInitList(int, float) -> void | 0 | 45 | 2 | @r0_40 | +| NestedInitList(int, float) -> void | 0 | 47 | 0 | @r0_46 | +| NestedInitList(int, float) -> void | 0 | 47 | 1 | @m0_7 | +| NestedInitList(int, float) -> void | 0 | 48 | 2 | @r0_47 | +| NestedInitList(int, float) -> void | 0 | 49 | 0 | @r0_45 | +| NestedInitList(int, float) -> void | 0 | 49 | 1 | @r0_48 | +| NestedInitList(int, float) -> void | 0 | 51 | 2 | @r0_50 | +| NestedInitList(int, float) -> void | 0 | 52 | 2 | @r0_51 | +| NestedInitList(int, float) -> void | 0 | 54 | 0 | @r0_53 | +| NestedInitList(int, float) -> void | 0 | 54 | 1 | @m0_4 | +| NestedInitList(int, float) -> void | 0 | 55 | 0 | @r0_52 | +| NestedInitList(int, float) -> void | 0 | 55 | 1 | @r0_54 | +| NestedInitList(int, float) -> void | 0 | 56 | 2 | @r0_51 | +| NestedInitList(int, float) -> void | 0 | 58 | 0 | @r0_56 | +| NestedInitList(int, float) -> void | 0 | 58 | 1 | @r0_57 | +| NestedInitList(int, float) -> void | 0 | 59 | 2 | @r0_50 | +| NestedInitList(int, float) -> void | 0 | 60 | 2 | @r0_59 | +| NestedInitList(int, float) -> void | 0 | 62 | 0 | @r0_61 | +| NestedInitList(int, float) -> void | 0 | 62 | 1 | @m0_4 | +| NestedInitList(int, float) -> void | 0 | 63 | 0 | @r0_60 | +| NestedInitList(int, float) -> void | 0 | 63 | 1 | @r0_62 | +| NestedInitList(int, float) -> void | 0 | 64 | 2 | @r0_59 | +| NestedInitList(int, float) -> void | 0 | 66 | 0 | @r0_64 | +| NestedInitList(int, float) -> void | 0 | 66 | 1 | @r0_65 | +| NestedInitList(int, float) -> void | 0 | 69 | 0 | @mu* | +| Nullptr() -> void | 0 | 4 | 0 | @r0_2 | +| Nullptr() -> void | 0 | 4 | 1 | @r0_3 | +| Nullptr() -> void | 0 | 7 | 0 | @r0_5 | +| Nullptr() -> void | 0 | 7 | 1 | @r0_6 | +| Nullptr() -> void | 0 | 10 | 0 | @r0_9 | +| Nullptr() -> void | 0 | 10 | 1 | @r0_8 | +| Nullptr() -> void | 0 | 13 | 0 | @r0_12 | +| Nullptr() -> void | 0 | 13 | 1 | @r0_11 | +| Nullptr() -> void | 0 | 16 | 0 | @mu* | +| Outer::Func(void *, char) -> long | 0 | 4 | 0 | @r0_3 | +| Outer::Func(void *, char) -> long | 0 | 4 | 1 | @r0_2 | +| Outer::Func(void *, char) -> long | 0 | 7 | 0 | @r0_6 | +| Outer::Func(void *, char) -> long | 0 | 7 | 1 | @r0_5 | +| Outer::Func(void *, char) -> long | 0 | 10 | 0 | @r0_8 | +| Outer::Func(void *, char) -> long | 0 | 10 | 1 | @r0_9 | +| Outer::Func(void *, char) -> long | 0 | 12 | 0 | @r0_11 | +| Outer::Func(void *, char) -> long | 0 | 12 | 5 | @m0_10 | +| Outer::Func(void *, char) -> long | 0 | 13 | 0 | @mu* | +| Parameters(int, int) -> int | 0 | 4 | 0 | @r0_3 | +| Parameters(int, int) -> int | 0 | 4 | 1 | @r0_2 | +| Parameters(int, int) -> int | 0 | 7 | 0 | @r0_6 | +| Parameters(int, int) -> int | 0 | 7 | 1 | @r0_5 | +| Parameters(int, int) -> int | 0 | 10 | 0 | @r0_9 | +| Parameters(int, int) -> int | 0 | 10 | 1 | @m0_4 | +| Parameters(int, int) -> int | 0 | 12 | 0 | @r0_11 | +| Parameters(int, int) -> int | 0 | 12 | 1 | @m0_7 | +| Parameters(int, int) -> int | 0 | 13 | 3 | @r0_10 | +| Parameters(int, int) -> int | 0 | 13 | 4 | @r0_12 | +| Parameters(int, int) -> int | 0 | 14 | 0 | @r0_8 | +| Parameters(int, int) -> int | 0 | 14 | 1 | @r0_13 | +| Parameters(int, int) -> int | 0 | 16 | 0 | @r0_15 | +| Parameters(int, int) -> int | 0 | 16 | 5 | @m0_14 | +| Parameters(int, int) -> int | 0 | 17 | 0 | @mu* | +| PointerCompare(int *, int *) -> void | 0 | 4 | 0 | @r0_3 | +| PointerCompare(int *, int *) -> void | 0 | 4 | 1 | @r0_2 | +| PointerCompare(int *, int *) -> void | 0 | 7 | 0 | @r0_6 | +| PointerCompare(int *, int *) -> void | 0 | 7 | 1 | @r0_5 | +| PointerCompare(int *, int *) -> void | 0 | 10 | 0 | @r0_8 | +| PointerCompare(int *, int *) -> void | 0 | 10 | 1 | @r0_9 | +| PointerCompare(int *, int *) -> void | 0 | 12 | 0 | @r0_11 | +| PointerCompare(int *, int *) -> void | 0 | 12 | 1 | @m0_4 | +| PointerCompare(int *, int *) -> void | 0 | 14 | 0 | @r0_13 | +| PointerCompare(int *, int *) -> void | 0 | 14 | 1 | @m0_7 | +| PointerCompare(int *, int *) -> void | 0 | 15 | 3 | @r0_12 | +| PointerCompare(int *, int *) -> void | 0 | 15 | 4 | @r0_14 | +| PointerCompare(int *, int *) -> void | 0 | 17 | 0 | @r0_16 | +| PointerCompare(int *, int *) -> void | 0 | 17 | 1 | @r0_15 | +| PointerCompare(int *, int *) -> void | 0 | 19 | 0 | @r0_18 | +| PointerCompare(int *, int *) -> void | 0 | 19 | 1 | @m0_4 | +| PointerCompare(int *, int *) -> void | 0 | 21 | 0 | @r0_20 | +| PointerCompare(int *, int *) -> void | 0 | 21 | 1 | @m0_7 | +| PointerCompare(int *, int *) -> void | 0 | 22 | 3 | @r0_19 | +| PointerCompare(int *, int *) -> void | 0 | 22 | 4 | @r0_21 | +| PointerCompare(int *, int *) -> void | 0 | 24 | 0 | @r0_23 | +| PointerCompare(int *, int *) -> void | 0 | 24 | 1 | @r0_22 | +| PointerCompare(int *, int *) -> void | 0 | 26 | 0 | @r0_25 | +| PointerCompare(int *, int *) -> void | 0 | 26 | 1 | @m0_4 | +| PointerCompare(int *, int *) -> void | 0 | 28 | 0 | @r0_27 | +| PointerCompare(int *, int *) -> void | 0 | 28 | 1 | @m0_7 | +| PointerCompare(int *, int *) -> void | 0 | 29 | 3 | @r0_26 | +| PointerCompare(int *, int *) -> void | 0 | 29 | 4 | @r0_28 | +| PointerCompare(int *, int *) -> void | 0 | 31 | 0 | @r0_30 | +| PointerCompare(int *, int *) -> void | 0 | 31 | 1 | @r0_29 | +| PointerCompare(int *, int *) -> void | 0 | 33 | 0 | @r0_32 | +| PointerCompare(int *, int *) -> void | 0 | 33 | 1 | @m0_4 | +| PointerCompare(int *, int *) -> void | 0 | 35 | 0 | @r0_34 | +| PointerCompare(int *, int *) -> void | 0 | 35 | 1 | @m0_7 | +| PointerCompare(int *, int *) -> void | 0 | 36 | 3 | @r0_33 | +| PointerCompare(int *, int *) -> void | 0 | 36 | 4 | @r0_35 | +| PointerCompare(int *, int *) -> void | 0 | 38 | 0 | @r0_37 | +| PointerCompare(int *, int *) -> void | 0 | 38 | 1 | @r0_36 | +| PointerCompare(int *, int *) -> void | 0 | 40 | 0 | @r0_39 | +| PointerCompare(int *, int *) -> void | 0 | 40 | 1 | @m0_4 | +| PointerCompare(int *, int *) -> void | 0 | 42 | 0 | @r0_41 | +| PointerCompare(int *, int *) -> void | 0 | 42 | 1 | @m0_7 | +| PointerCompare(int *, int *) -> void | 0 | 43 | 3 | @r0_40 | +| PointerCompare(int *, int *) -> void | 0 | 43 | 4 | @r0_42 | +| PointerCompare(int *, int *) -> void | 0 | 45 | 0 | @r0_44 | +| PointerCompare(int *, int *) -> void | 0 | 45 | 1 | @r0_43 | +| PointerCompare(int *, int *) -> void | 0 | 47 | 0 | @r0_46 | +| PointerCompare(int *, int *) -> void | 0 | 47 | 1 | @m0_4 | +| PointerCompare(int *, int *) -> void | 0 | 49 | 0 | @r0_48 | +| PointerCompare(int *, int *) -> void | 0 | 49 | 1 | @m0_7 | +| PointerCompare(int *, int *) -> void | 0 | 50 | 3 | @r0_47 | +| PointerCompare(int *, int *) -> void | 0 | 50 | 4 | @r0_49 | +| PointerCompare(int *, int *) -> void | 0 | 52 | 0 | @r0_51 | +| PointerCompare(int *, int *) -> void | 0 | 52 | 1 | @r0_50 | +| PointerCompare(int *, int *) -> void | 0 | 55 | 0 | @mu* | +| PointerCrement(int *) -> void | 0 | 4 | 0 | @r0_3 | +| PointerCrement(int *) -> void | 0 | 4 | 1 | @r0_2 | +| PointerCrement(int *) -> void | 0 | 7 | 0 | @r0_5 | +| PointerCrement(int *) -> void | 0 | 7 | 1 | @r0_6 | +| PointerCrement(int *) -> void | 0 | 9 | 0 | @r0_8 | +| PointerCrement(int *) -> void | 0 | 9 | 1 | @m0_4 | +| PointerCrement(int *) -> void | 0 | 11 | 3 | @r0_9 | +| PointerCrement(int *) -> void | 0 | 11 | 4 | @r0_10 | +| PointerCrement(int *) -> void | 0 | 12 | 0 | @r0_8 | +| PointerCrement(int *) -> void | 0 | 12 | 1 | @r0_11 | +| PointerCrement(int *) -> void | 0 | 14 | 0 | @r0_13 | +| PointerCrement(int *) -> void | 0 | 14 | 1 | @r0_11 | +| PointerCrement(int *) -> void | 0 | 16 | 0 | @r0_15 | +| PointerCrement(int *) -> void | 0 | 16 | 1 | @m0_12 | +| PointerCrement(int *) -> void | 0 | 18 | 3 | @r0_16 | +| PointerCrement(int *) -> void | 0 | 18 | 4 | @r0_17 | +| PointerCrement(int *) -> void | 0 | 19 | 0 | @r0_15 | +| PointerCrement(int *) -> void | 0 | 19 | 1 | @r0_18 | +| PointerCrement(int *) -> void | 0 | 21 | 0 | @r0_20 | +| PointerCrement(int *) -> void | 0 | 21 | 1 | @r0_18 | +| PointerCrement(int *) -> void | 0 | 23 | 0 | @r0_22 | +| PointerCrement(int *) -> void | 0 | 23 | 1 | @m0_19 | +| PointerCrement(int *) -> void | 0 | 25 | 3 | @r0_23 | +| PointerCrement(int *) -> void | 0 | 25 | 4 | @r0_24 | +| PointerCrement(int *) -> void | 0 | 26 | 0 | @r0_22 | +| PointerCrement(int *) -> void | 0 | 26 | 1 | @r0_25 | +| PointerCrement(int *) -> void | 0 | 28 | 0 | @r0_27 | +| PointerCrement(int *) -> void | 0 | 28 | 1 | @r0_23 | +| PointerCrement(int *) -> void | 0 | 30 | 0 | @r0_29 | +| PointerCrement(int *) -> void | 0 | 30 | 1 | @m0_26 | +| PointerCrement(int *) -> void | 0 | 32 | 3 | @r0_30 | +| PointerCrement(int *) -> void | 0 | 32 | 4 | @r0_31 | +| PointerCrement(int *) -> void | 0 | 33 | 0 | @r0_29 | +| PointerCrement(int *) -> void | 0 | 33 | 1 | @r0_32 | +| PointerCrement(int *) -> void | 0 | 35 | 0 | @r0_34 | +| PointerCrement(int *) -> void | 0 | 35 | 1 | @r0_30 | +| PointerCrement(int *) -> void | 0 | 38 | 0 | @mu* | +| PointerOps(int *, int) -> void | 0 | 4 | 0 | @r0_3 | +| PointerOps(int *, int) -> void | 0 | 4 | 1 | @r0_2 | +| PointerOps(int *, int) -> void | 0 | 7 | 0 | @r0_6 | +| PointerOps(int *, int) -> void | 0 | 7 | 1 | @r0_5 | +| PointerOps(int *, int) -> void | 0 | 10 | 0 | @r0_8 | +| PointerOps(int *, int) -> void | 0 | 10 | 1 | @r0_9 | +| PointerOps(int *, int) -> void | 0 | 13 | 0 | @r0_11 | +| PointerOps(int *, int) -> void | 0 | 13 | 1 | @r0_12 | +| PointerOps(int *, int) -> void | 0 | 15 | 0 | @r0_14 | +| PointerOps(int *, int) -> void | 0 | 15 | 1 | @m0_4 | +| PointerOps(int *, int) -> void | 0 | 17 | 0 | @r0_16 | +| PointerOps(int *, int) -> void | 0 | 17 | 1 | @m0_7 | +| PointerOps(int *, int) -> void | 0 | 18 | 3 | @r0_15 | +| PointerOps(int *, int) -> void | 0 | 18 | 4 | @r0_17 | +| PointerOps(int *, int) -> void | 0 | 20 | 0 | @r0_19 | +| PointerOps(int *, int) -> void | 0 | 20 | 1 | @r0_18 | +| PointerOps(int *, int) -> void | 0 | 22 | 0 | @r0_21 | +| PointerOps(int *, int) -> void | 0 | 22 | 1 | @m0_7 | +| PointerOps(int *, int) -> void | 0 | 24 | 0 | @r0_23 | +| PointerOps(int *, int) -> void | 0 | 24 | 1 | @m0_4 | +| PointerOps(int *, int) -> void | 0 | 25 | 3 | @r0_24 | +| PointerOps(int *, int) -> void | 0 | 25 | 4 | @r0_22 | +| PointerOps(int *, int) -> void | 0 | 27 | 0 | @r0_26 | +| PointerOps(int *, int) -> void | 0 | 27 | 1 | @r0_25 | +| PointerOps(int *, int) -> void | 0 | 29 | 0 | @r0_28 | +| PointerOps(int *, int) -> void | 0 | 29 | 1 | @m0_4 | +| PointerOps(int *, int) -> void | 0 | 31 | 0 | @r0_30 | +| PointerOps(int *, int) -> void | 0 | 31 | 1 | @m0_7 | +| PointerOps(int *, int) -> void | 0 | 32 | 3 | @r0_29 | +| PointerOps(int *, int) -> void | 0 | 32 | 4 | @r0_31 | +| PointerOps(int *, int) -> void | 0 | 34 | 0 | @r0_33 | +| PointerOps(int *, int) -> void | 0 | 34 | 1 | @r0_32 | +| PointerOps(int *, int) -> void | 0 | 36 | 0 | @r0_35 | +| PointerOps(int *, int) -> void | 0 | 36 | 1 | @m0_4 | +| PointerOps(int *, int) -> void | 0 | 38 | 0 | @r0_37 | +| PointerOps(int *, int) -> void | 0 | 38 | 1 | @m0_34 | +| PointerOps(int *, int) -> void | 0 | 39 | 3 | @r0_36 | +| PointerOps(int *, int) -> void | 0 | 39 | 4 | @r0_38 | +| PointerOps(int *, int) -> void | 0 | 40 | 2 | @r0_39 | +| PointerOps(int *, int) -> void | 0 | 42 | 0 | @r0_41 | +| PointerOps(int *, int) -> void | 0 | 42 | 1 | @r0_40 | +| PointerOps(int *, int) -> void | 0 | 44 | 0 | @r0_43 | +| PointerOps(int *, int) -> void | 0 | 44 | 1 | @m0_4 | +| PointerOps(int *, int) -> void | 0 | 46 | 0 | @r0_45 | +| PointerOps(int *, int) -> void | 0 | 46 | 1 | @r0_44 | +| PointerOps(int *, int) -> void | 0 | 48 | 0 | @r0_47 | +| PointerOps(int *, int) -> void | 0 | 48 | 1 | @m0_42 | +| PointerOps(int *, int) -> void | 0 | 50 | 0 | @r0_49 | +| PointerOps(int *, int) -> void | 0 | 50 | 1 | @m0_46 | +| PointerOps(int *, int) -> void | 0 | 51 | 3 | @r0_50 | +| PointerOps(int *, int) -> void | 0 | 51 | 4 | @r0_48 | +| PointerOps(int *, int) -> void | 0 | 52 | 0 | @r0_49 | +| PointerOps(int *, int) -> void | 0 | 52 | 1 | @r0_51 | +| PointerOps(int *, int) -> void | 0 | 54 | 0 | @r0_53 | +| PointerOps(int *, int) -> void | 0 | 54 | 1 | @m0_42 | +| PointerOps(int *, int) -> void | 0 | 56 | 0 | @r0_55 | +| PointerOps(int *, int) -> void | 0 | 56 | 1 | @m0_52 | +| PointerOps(int *, int) -> void | 0 | 57 | 3 | @r0_56 | +| PointerOps(int *, int) -> void | 0 | 57 | 4 | @r0_54 | +| PointerOps(int *, int) -> void | 0 | 58 | 0 | @r0_55 | +| PointerOps(int *, int) -> void | 0 | 58 | 1 | @r0_57 | +| PointerOps(int *, int) -> void | 0 | 60 | 0 | @r0_59 | +| PointerOps(int *, int) -> void | 0 | 60 | 1 | @m0_4 | +| PointerOps(int *, int) -> void | 0 | 62 | 3 | @r0_60 | +| PointerOps(int *, int) -> void | 0 | 62 | 4 | @r0_61 | +| PointerOps(int *, int) -> void | 0 | 64 | 0 | @r0_63 | +| PointerOps(int *, int) -> void | 0 | 64 | 1 | @r0_62 | +| PointerOps(int *, int) -> void | 0 | 66 | 0 | @r0_65 | +| PointerOps(int *, int) -> void | 0 | 66 | 1 | @m0_4 | +| PointerOps(int *, int) -> void | 0 | 68 | 3 | @r0_66 | +| PointerOps(int *, int) -> void | 0 | 68 | 4 | @r0_67 | +| PointerOps(int *, int) -> void | 0 | 69 | 2 | @r0_68 | +| PointerOps(int *, int) -> void | 0 | 71 | 0 | @r0_70 | +| PointerOps(int *, int) -> void | 0 | 71 | 1 | @r0_69 | +| PointerOps(int *, int) -> void | 0 | 74 | 0 | @mu* | +| PolymorphicBase::PolymorphicBase() -> void | 0 | 5 | 0 | @mu* | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 3 | 2 | @r0_2 | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 5 | 9 | @r0_4 | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 5 | 10 | this:@r0_3 | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 8 | 0 | @mu* | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 4 | 2 | @r0_2 | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 6 | 9 | @r0_5 | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 6 | 10 | this:@r0_4 | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 8 | 0 | @mu* | +| ReturnStruct(Point) -> Point | 0 | 4 | 0 | @r0_3 | +| ReturnStruct(Point) -> Point | 0 | 4 | 1 | @r0_2 | +| ReturnStruct(Point) -> Point | 0 | 7 | 0 | @r0_6 | +| ReturnStruct(Point) -> Point | 0 | 7 | 1 | @m0_4 | +| ReturnStruct(Point) -> Point | 0 | 8 | 0 | @r0_5 | +| ReturnStruct(Point) -> Point | 0 | 8 | 1 | @r0_7 | +| ReturnStruct(Point) -> Point | 0 | 10 | 0 | @r0_9 | +| ReturnStruct(Point) -> Point | 0 | 10 | 5 | @m0_8 | +| ReturnStruct(Point) -> Point | 0 | 11 | 0 | @mu* | +| SetFuncPtr() -> void | 0 | 4 | 0 | @r0_2 | +| SetFuncPtr() -> void | 0 | 4 | 1 | @r0_3 | +| SetFuncPtr() -> void | 0 | 7 | 0 | @r0_6 | +| SetFuncPtr() -> void | 0 | 7 | 1 | @r0_5 | +| SetFuncPtr() -> void | 0 | 10 | 0 | @r0_9 | +| SetFuncPtr() -> void | 0 | 10 | 1 | @r0_8 | +| SetFuncPtr() -> void | 0 | 13 | 0 | @r0_12 | +| SetFuncPtr() -> void | 0 | 13 | 1 | @r0_11 | +| SetFuncPtr() -> void | 0 | 16 | 0 | @mu* | +| String::String() -> void | 0 | 5 | 2 | @r0_4 | +| String::String() -> void | 0 | 6 | 9 | @r0_3 | +| String::String() -> void | 0 | 6 | 10 | this:@r0_2 | +| String::String() -> void | 0 | 6 | 11 | @r0_5 | +| String::String() -> void | 0 | 9 | 0 | @mu* | +| StringLiteral(int) -> void | 0 | 4 | 0 | @r0_3 | +| StringLiteral(int) -> void | 0 | 4 | 1 | @r0_2 | +| StringLiteral(int) -> void | 0 | 7 | 2 | @r0_6 | +| StringLiteral(int) -> void | 0 | 9 | 0 | @r0_8 | +| StringLiteral(int) -> void | 0 | 9 | 1 | @m0_4 | +| StringLiteral(int) -> void | 0 | 10 | 3 | @r0_7 | +| StringLiteral(int) -> void | 0 | 10 | 4 | @r0_9 | +| StringLiteral(int) -> void | 0 | 11 | 0 | @r0_10 | +| StringLiteral(int) -> void | 0 | 11 | 1 | @mu0_1 | +| StringLiteral(int) -> void | 0 | 12 | 0 | @r0_5 | +| StringLiteral(int) -> void | 0 | 12 | 1 | @r0_11 | +| StringLiteral(int) -> void | 0 | 15 | 2 | @r0_14 | +| StringLiteral(int) -> void | 0 | 16 | 2 | @r0_15 | +| StringLiteral(int) -> void | 0 | 17 | 0 | @r0_13 | +| StringLiteral(int) -> void | 0 | 17 | 1 | @r0_16 | +| StringLiteral(int) -> void | 0 | 20 | 0 | @r0_19 | +| StringLiteral(int) -> void | 0 | 20 | 1 | @m0_17 | +| StringLiteral(int) -> void | 0 | 22 | 0 | @r0_21 | +| StringLiteral(int) -> void | 0 | 22 | 1 | @m0_4 | +| StringLiteral(int) -> void | 0 | 23 | 3 | @r0_20 | +| StringLiteral(int) -> void | 0 | 23 | 4 | @r0_22 | +| StringLiteral(int) -> void | 0 | 24 | 0 | @r0_23 | +| StringLiteral(int) -> void | 0 | 24 | 1 | @mu0_1 | +| StringLiteral(int) -> void | 0 | 25 | 0 | @r0_18 | +| StringLiteral(int) -> void | 0 | 25 | 1 | @r0_24 | +| StringLiteral(int) -> void | 0 | 28 | 0 | @mu* | +| Switch(int) -> void | 0 | 4 | 0 | @r0_3 | +| Switch(int) -> void | 0 | 4 | 1 | @r0_2 | +| Switch(int) -> void | 0 | 7 | 0 | @r0_5 | +| Switch(int) -> void | 0 | 7 | 1 | @r0_6 | +| Switch(int) -> void | 0 | 9 | 0 | @r0_8 | +| Switch(int) -> void | 0 | 9 | 1 | @m0_4 | +| Switch(int) -> void | 0 | 10 | 7 | @r0_9 | +| Switch(int) -> void | 1 | 2 | 0 | @r1_1 | +| Switch(int) -> void | 1 | 2 | 1 | @r1_0 | +| Switch(int) -> void | 2 | 3 | 0 | @r2_2 | +| Switch(int) -> void | 2 | 3 | 1 | @r2_1 | +| Switch(int) -> void | 4 | 3 | 0 | @r4_2 | +| Switch(int) -> void | 4 | 3 | 1 | @r4_1 | +| Switch(int) -> void | 5 | 3 | 0 | @r5_2 | +| Switch(int) -> void | 5 | 3 | 1 | @r5_1 | +| Switch(int) -> void | 6 | 3 | 0 | @r6_2 | +| Switch(int) -> void | 6 | 3 | 1 | @r6_1 | +| Switch(int) -> void | 7 | 3 | 0 | @r7_2 | +| Switch(int) -> void | 7 | 3 | 1 | @r7_1 | +| Switch(int) -> void | 8 | 2 | 0 | @r8_1 | +| Switch(int) -> void | 8 | 2 | 1 | @r8_0 | +| Switch(int) -> void | 9 | 3 | 0 | @mu* | +| TakeReference() -> int & | 0 | 4 | 0 | @r0_2 | +| TakeReference() -> int & | 0 | 4 | 1 | @r0_3 | +| TakeReference() -> int & | 0 | 6 | 0 | @r0_5 | +| TakeReference() -> int & | 0 | 6 | 5 | @m0_4 | +| TakeReference() -> int & | 0 | 7 | 0 | @mu* | +| TryCatch(bool) -> void | 0 | 4 | 0 | @r0_3 | +| TryCatch(bool) -> void | 0 | 4 | 1 | @r0_2 | +| TryCatch(bool) -> void | 0 | 7 | 0 | @r0_5 | +| TryCatch(bool) -> void | 0 | 7 | 1 | @r0_6 | +| TryCatch(bool) -> void | 0 | 9 | 0 | @r0_8 | +| TryCatch(bool) -> void | 0 | 9 | 1 | @m0_4 | +| TryCatch(bool) -> void | 0 | 10 | 7 | @r0_9 | +| TryCatch(bool) -> void | 1 | 0 | 0 | @mu* | +| TryCatch(bool) -> void | 3 | 2 | 2 | @r3_1 | +| TryCatch(bool) -> void | 3 | 3 | 0 | @r3_0 | +| TryCatch(bool) -> void | 3 | 3 | 1 | @r3_2 | +| TryCatch(bool) -> void | 3 | 4 | 0 | @r3_0 | +| TryCatch(bool) -> void | 3 | 4 | 6 | @m3_3 | +| TryCatch(bool) -> void | 4 | 1 | 0 | @r4_0 | +| TryCatch(bool) -> void | 4 | 1 | 1 | @m0_7 | +| TryCatch(bool) -> void | 4 | 3 | 3 | @r4_1 | +| TryCatch(bool) -> void | 4 | 3 | 4 | @r4_2 | +| TryCatch(bool) -> void | 4 | 4 | 7 | @r4_3 | +| TryCatch(bool) -> void | 5 | 1 | 0 | @r5_0 | +| TryCatch(bool) -> void | 5 | 1 | 1 | @m0_4 | +| TryCatch(bool) -> void | 5 | 2 | 7 | @r5_1 | +| TryCatch(bool) -> void | 6 | 2 | 0 | @r6_1 | +| TryCatch(bool) -> void | 6 | 2 | 1 | @r6_0 | +| TryCatch(bool) -> void | 6 | 4 | 0 | @r6_3 | +| TryCatch(bool) -> void | 6 | 4 | 1 | @m6_2 | +| TryCatch(bool) -> void | 6 | 6 | 0 | @r6_5 | +| TryCatch(bool) -> void | 6 | 6 | 1 | @r6_4 | +| TryCatch(bool) -> void | 7 | 3 | 2 | @r7_2 | +| TryCatch(bool) -> void | 7 | 4 | 9 | @r7_1 | +| TryCatch(bool) -> void | 7 | 4 | 10 | this:@r7_0 | +| TryCatch(bool) -> void | 7 | 4 | 11 | @r7_3 | +| TryCatch(bool) -> void | 7 | 5 | 0 | @r7_0 | +| TryCatch(bool) -> void | 7 | 5 | 6 | @mu0_1 | +| TryCatch(bool) -> void | 8 | 2 | 0 | @r8_1 | +| TryCatch(bool) -> void | 8 | 2 | 1 | @r8_0 | +| TryCatch(bool) -> void | 10 | 2 | 0 | @r10_1 | +| TryCatch(bool) -> void | 10 | 2 | 1 | @r10_0 | +| TryCatch(bool) -> void | 10 | 6 | 0 | @r10_5 | +| TryCatch(bool) -> void | 10 | 6 | 1 | @m10_2 | +| TryCatch(bool) -> void | 10 | 7 | 9 | @r10_4 | +| TryCatch(bool) -> void | 10 | 7 | 10 | this:@r10_3 | +| TryCatch(bool) -> void | 10 | 7 | 11 | @r10_6 | +| TryCatch(bool) -> void | 10 | 8 | 0 | @r10_3 | +| TryCatch(bool) -> void | 10 | 8 | 6 | @mu0_1 | +| TryCatch(bool) -> void | 12 | 2 | 0 | @r12_1 | +| TryCatch(bool) -> void | 12 | 2 | 1 | @r12_0 | +| UninitializedVariables() -> void | 0 | 4 | 0 | @r0_2 | +| UninitializedVariables() -> void | 0 | 4 | 1 | @r0_3 | +| UninitializedVariables() -> void | 0 | 7 | 0 | @r0_6 | +| UninitializedVariables() -> void | 0 | 7 | 1 | @m0_4 | +| UninitializedVariables() -> void | 0 | 8 | 0 | @r0_5 | +| UninitializedVariables() -> void | 0 | 8 | 1 | @r0_7 | +| UninitializedVariables() -> void | 0 | 11 | 0 | @mu* | +| UnionInit(int, float) -> void | 0 | 4 | 0 | @r0_3 | +| UnionInit(int, float) -> void | 0 | 4 | 1 | @r0_2 | +| UnionInit(int, float) -> void | 0 | 7 | 0 | @r0_6 | +| UnionInit(int, float) -> void | 0 | 7 | 1 | @r0_5 | +| UnionInit(int, float) -> void | 0 | 9 | 2 | @r0_8 | +| UnionInit(int, float) -> void | 0 | 11 | 0 | @r0_10 | +| UnionInit(int, float) -> void | 0 | 11 | 1 | @m0_7 | +| UnionInit(int, float) -> void | 0 | 12 | 2 | @r0_11 | +| UnionInit(int, float) -> void | 0 | 13 | 0 | @r0_9 | +| UnionInit(int, float) -> void | 0 | 13 | 1 | @r0_12 | +| UnionInit(int, float) -> void | 0 | 16 | 0 | @mu* | +| VarArgUsage(int) -> void | 0 | 4 | 0 | @r0_3 | +| VarArgUsage(int) -> void | 0 | 4 | 1 | @r0_2 | +| VarArgUsage(int) -> void | 0 | 7 | 0 | @r0_5 | +| VarArgUsage(int) -> void | 0 | 7 | 1 | @r0_6 | +| VarArgUsage(int) -> void | 0 | 9 | 2 | @r0_8 | +| VarArgUsage(int) -> void | 0 | 11 | 11 | @r0_9 | +| VarArgUsage(int) -> void | 0 | 11 | 12 | @r0_10 | +| VarArgUsage(int) -> void | 0 | 14 | 0 | @r0_12 | +| VarArgUsage(int) -> void | 0 | 14 | 1 | @r0_13 | +| VarArgUsage(int) -> void | 0 | 16 | 2 | @r0_15 | +| VarArgUsage(int) -> void | 0 | 18 | 2 | @r0_17 | +| VarArgUsage(int) -> void | 0 | 19 | 11 | @r0_16 | +| VarArgUsage(int) -> void | 0 | 19 | 12 | @r0_18 | +| VarArgUsage(int) -> void | 0 | 22 | 2 | @r0_21 | +| VarArgUsage(int) -> void | 0 | 23 | 11 | @r0_22 | +| VarArgUsage(int) -> void | 0 | 24 | 0 | @r0_23 | +| VarArgUsage(int) -> void | 0 | 24 | 1 | @mu0_1 | +| VarArgUsage(int) -> void | 0 | 25 | 0 | @r0_20 | +| VarArgUsage(int) -> void | 0 | 25 | 1 | @r0_24 | +| VarArgUsage(int) -> void | 0 | 28 | 2 | @r0_27 | +| VarArgUsage(int) -> void | 0 | 29 | 11 | @r0_28 | +| VarArgUsage(int) -> void | 0 | 30 | 0 | @r0_29 | +| VarArgUsage(int) -> void | 0 | 30 | 1 | @mu0_1 | +| VarArgUsage(int) -> void | 0 | 31 | 2 | @r0_30 | +| VarArgUsage(int) -> void | 0 | 32 | 0 | @r0_26 | +| VarArgUsage(int) -> void | 0 | 32 | 1 | @r0_31 | +| VarArgUsage(int) -> void | 0 | 34 | 2 | @r0_33 | +| VarArgUsage(int) -> void | 0 | 35 | 11 | @r0_34 | +| VarArgUsage(int) -> void | 0 | 37 | 2 | @r0_36 | +| VarArgUsage(int) -> void | 0 | 38 | 11 | @r0_37 | +| VarArgUsage(int) -> void | 0 | 41 | 0 | @mu* | +| VarArgs() -> void | 0 | 4 | 2 | @r0_3 | +| VarArgs() -> void | 0 | 7 | 2 | @r0_6 | +| VarArgs() -> void | 0 | 8 | 9 | @r0_2 | +| VarArgs() -> void | 0 | 8 | 11 | @r0_4 | +| VarArgs() -> void | 0 | 8 | 12 | @r0_5 | +| VarArgs() -> void | 0 | 8 | 13 | @r0_7 | +| VarArgs() -> void | 0 | 11 | 0 | @mu* | +| WhileStatements(int) -> void | 0 | 4 | 0 | @r0_3 | +| WhileStatements(int) -> void | 0 | 4 | 1 | @r0_2 | +| WhileStatements(int) -> void | 1 | 2 | 0 | @r1_1 | +| WhileStatements(int) -> void | 1 | 2 | 1 | @m3_0 | +| WhileStatements(int) -> void | 1 | 3 | 3 | @r1_2 | +| WhileStatements(int) -> void | 1 | 3 | 4 | @r1_0 | +| WhileStatements(int) -> void | 1 | 4 | 0 | @r1_1 | +| WhileStatements(int) -> void | 1 | 4 | 1 | @r1_3 | +| WhileStatements(int) -> void | 2 | 2 | 0 | @mu* | +| WhileStatements(int) -> void | 3 | 0 | 11 | from 0: @m0_4 | +| WhileStatements(int) -> void | 3 | 0 | 11 | from 1: @m1_4 | +| WhileStatements(int) -> void | 3 | 2 | 0 | @r3_1 | +| WhileStatements(int) -> void | 3 | 2 | 1 | @m3_0 | +| WhileStatements(int) -> void | 3 | 4 | 3 | @r3_2 | +| WhileStatements(int) -> void | 3 | 4 | 4 | @r3_3 | +| WhileStatements(int) -> void | 3 | 5 | 7 | @r3_4 | +| min(int, int) -> int | 0 | 4 | 0 | @r0_3 | +| min(int, int) -> int | 0 | 4 | 1 | @r0_2 | +| min(int, int) -> int | 0 | 7 | 0 | @r0_6 | +| min(int, int) -> int | 0 | 7 | 1 | @r0_5 | +| min(int, int) -> int | 0 | 10 | 0 | @r0_9 | +| min(int, int) -> int | 0 | 10 | 1 | @m0_4 | +| min(int, int) -> int | 0 | 12 | 0 | @r0_11 | +| min(int, int) -> int | 0 | 12 | 1 | @m0_7 | +| min(int, int) -> int | 0 | 13 | 3 | @r0_10 | +| min(int, int) -> int | 0 | 13 | 4 | @r0_12 | +| min(int, int) -> int | 0 | 14 | 7 | @r0_13 | +| min(int, int) -> int | 1 | 1 | 0 | @r1_0 | +| min(int, int) -> int | 1 | 1 | 1 | @m0_4 | +| min(int, int) -> int | 1 | 3 | 0 | @r1_2 | +| min(int, int) -> int | 1 | 3 | 1 | @r1_1 | +| min(int, int) -> int | 2 | 1 | 0 | @r2_0 | +| min(int, int) -> int | 2 | 1 | 1 | @m0_7 | +| min(int, int) -> int | 2 | 3 | 0 | @r2_2 | +| min(int, int) -> int | 2 | 3 | 1 | @r2_1 | +| min(int, int) -> int | 3 | 0 | 11 | from 1: @m1_3 | +| min(int, int) -> int | 3 | 0 | 11 | from 2: @m2_3 | +| min(int, int) -> int | 3 | 2 | 0 | @r3_1 | +| min(int, int) -> int | 3 | 2 | 1 | @m3_0 | +| min(int, int) -> int | 3 | 3 | 0 | @r0_8 | +| min(int, int) -> int | 3 | 3 | 1 | @r3_2 | +| min(int, int) -> int | 3 | 5 | 0 | @r3_4 | +| min(int, int) -> int | 3 | 5 | 5 | @m3_3 | +| min(int, int) -> int | 3 | 6 | 0 | @mu* | +#select diff --git a/cpp/ql/test/library-tests/ir/ir/aliased_ssa_ir.ql b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_ir.ql new file mode 100644 index 000000000000..daf3f3fd0fc8 --- /dev/null +++ b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_ir.ql @@ -0,0 +1,6 @@ +import semmle.code.cpp.ssa.AliasedSSAIR +import semmle.code.cpp.ssa.PrintAliasedSSAIR + +from Instruction instr +where none() +select instr diff --git a/cpp/ql/test/library-tests/ir/ir/ir.cpp b/cpp/ql/test/library-tests/ir/ir/ir.cpp new file mode 100644 index 000000000000..3460b095d495 --- /dev/null +++ b/cpp/ql/test/library-tests/ir/ir/ir.cpp @@ -0,0 +1,898 @@ +void Constants() { + char c_i = 1; + char c_c = 'A'; + + signed char sc_i = -1; + signed char sc_c = 'A'; + + unsigned char uc_i = 5; + unsigned char uc_c = 'A'; + + short s = 5; + unsigned short us = 5; + + int i = 5; + unsigned int ui = 5; + + long l = 5; + unsigned long ul = 5; + + long long ll_i = 5; + long long ll_ll = 5LL; + unsigned long long ull_i = 5; + unsigned long long ull_ull = 5ULL; + + bool b_t = true; + bool b_f = false; + + wchar_t wc_i = 5; + wchar_t wc_c = L'A'; + + char16_t c16 = u'A'; + char32_t c32 = U'A'; + + float f_i = 1; + float f_f = 1.0f; + float f_d = 1.0; + + double d_i = 1; + double d_f = 1.0f; + double d_d = 1.0; +} + +void Foo() { + int x = 5 + 12; + short y = 7; + y = x + y; + x = x * y; +} + +void IntegerOps(int x, int y) { + int z; + + z = x + y; + z = x - y; + z = x * y; + z = x / y; + z = x % y; + + z = x & y; + z = x | y; + z = x ^ y; + + z = x << y; + z = x >> y; + + z = x; + + z += x; + z -= x; + z *= x; + z /= x; + z %= x; + + z &= x; + z |= x; + z ^= x; + + z <<= x; + z >>= x; + + z = +x; + z = -x; + z = ~x; + z = !x; +} + +void IntegerCompare(int x, int y) { + bool b; + + b = x == y; + b = x != y; + b = x < y; + b = x > y; + b = x <= y; + b = x >= y; +} + +void IntegerCrement(int x) { + int y; + + y = ++x; + y = --x; + y = x++; + y = x--; +} + +void IntegerCrement_LValue(int x) { + int* p; + + p = &(++x); + p = &(--x); +} + +void FloatOps(double x, double y) { + double z; + + z = x + y; + z = x - y; + z = x * y; + z = x / y; + + z = x; + + z += x; + z -= x; + z *= x; + z /= x; + + z = +x; + z = -x; +} + +void FloatCompare(double x, double y) { + bool b; + + b = x == y; + b = x != y; + b = x < y; + b = x > y; + b = x <= y; + b = x >= y; +} + +void FloatCrement(float x) { + float y; + + y = ++x; + y = --x; + y = x++; + y = x--; +} + +void PointerOps(int* p, int i) { + int* q; + bool b; + + q = p + i; + q = i + p; + q = p - i; + i = p - q; + + q = p; + + q += i; + q -= i; + + b = p; + b = !p; +} + +void ArrayAccess(int* p, int i) { + int x; + + x = p[i]; + x = i[p]; + + p[i] = x; + i[p] = x; + + int a[10]; + x = a[i]; + x = i[a]; + a[i] = x; + i[a] = x; +} + +void StringLiteral(int i) { + char c = "Foo"[i]; + wchar_t* pwc = L"Bar"; + wchar_t wc = pwc[i]; +} + +void PointerCompare(int* p, int* q) { + bool b; + + b = p == q; + b = p != q; + b = p < q; + b = p > q; + b = p <= q; + b = p >= q; +} + +void PointerCrement(int* p) { + int* q; + + q = ++p; + q = --p; + q = p++; + q = p--; +} + +void CompoundAssignment() { + // No conversion necessary + int x = 5; + x += 7; + + // Left side is converted to 'int' + short y = 5; + y += x; + + // Technically the left side is promoted to int, but we don't model that + y <<= 1; + + // Left side is converted to 'float' + long z = 7; + z += 2.0f; +} + +void UninitializedVariables() { + int x; + int y = x; +} + +int Parameters(int x, int y) { + return x % y; +} + +void IfStatements(bool b, int x, int y) { + if (b) { + } + + if (b) { + x = y; + } + + if (x < 7) + x = 2; + else + x = 7; +} + +void WhileStatements(int n) { + while (n > 0) { + n -= 1; + } +} + +void DoStatements(int n) { + do { + n -= 1; + } while (n > 0); +} + +void For_Empty() { + int j; + for (;;) { + ; + } +} + +void For_Init() { + for (int i = 0;;) { + ; + } +} + +void For_Condition() { + int i = 0; + for (; i < 10;) { + ; + } +} + +void For_Update() { + int i = 0; + for (;; i += 1) { + ; + } +} + +void For_InitCondition() { + for (int i = 0; i < 10;) { + ; + } +} + +void For_InitUpdate() { + for (int i = 0;; i += 1) { + ; + } +} + +void For_ConditionUpdate() { + int i = 0; + for (; i < 10; i += 1) { + ; + } +} + +void For_InitConditionUpdate() { + for (int i = 0; i < 10; i += 1) { + ; + } +} + +void For_Break() { + for (int i = 0; i < 10; i += 1) { + if (i == 5) { + break; + } + } +} + +void For_Continue_Update() { + for (int i = 0; i < 10; i += 1) { + if (i == 5) { + continue; + } + } +} + +void For_Continue_NoUpdate() { + for (int i = 0; i < 10;) { + if (i == 5) { + continue; + } + } +} + +int Dereference(int* p) { + *p = 1; + return *p; +} + +int g; + +int* AddressOf() { + return &g; +} + +void Break(int n) { + while (n > 0) { + if (n == 1) + break; + n -= 1; + } +} + +void Continue(int n) { + do { + if (n == 1) { + continue; + } + n -= 1; + } while (n > 0); +} + +void VoidFunc(); +int Add(int x, int y); + +void Call() { + VoidFunc(); +} + +int CallAdd(int x, int y) { + return Add(x, y); +} + +int Comma(int x, int y) { + return VoidFunc(), CallAdd(x, y); +} + +void Switch(int x) { + int y; + switch (x) { + y = 1234; + + case -1: + y = -1; + break; + + case 1: + case 2: + y = 1; + break; + + case 3: + y = 3; + case 4: + y = 4; + break; + + default: + y = 0; + break; + + y = 5678; + } +} + +struct Point { + int x; + int y; +}; + +struct Rect { + Point topLeft; + Point bottomRight; +}; + +Point ReturnStruct(Point pt) { + return pt; +} + +void FieldAccess() { + Point pt; + pt.x = 5; + pt.y = pt.x; + int* p = &pt.y; +} + +void LogicalOr(bool a, bool b) { + int x; + if (a || b) { + x = 7; + } + + if (a || b) { + x = 1; + } + else { + x = 5; + } +} + +void LogicalAnd(bool a, bool b) { + int x; + if (a && b) { + x = 7; + } + + if (a && b) { + x = 1; + } + else { + x = 5; + } +} + +void LogicalNot(bool a, bool b) { + int x; + if (!a) { + x = 1; + } + + if (!(a && b)) { + x = 2; + } + else { + x = 3; + } +} + +void ConditionValues(bool a, bool b) { + bool x; + x = a && b; + x = a || b; + x = !(a || b); +} + +void Conditional(bool a, int x, int y) { + int z = a ? x : y; +} + +void Conditional_LValue(bool a) { + int x; + int y; + (a ? x : y) = 5; +} + +void Conditional_Void(bool a) { + a ? VoidFunc() : VoidFunc(); +} + +void Nullptr() { + int* p = nullptr; + int* q = 0; + p = nullptr; + q = 0; +} + +void InitList(int x, float f) { + Point pt1 = { x, f }; + Point pt2 = { x }; + Point pt3 = {}; + + int x1 = { 1 }; + int x2 = {}; +} + +void NestedInitList(int x, float f) { + Rect r1 = {}; + Rect r2 = { { x, f } }; + Rect r3 = { { x, f }, { x, f } }; + Rect r4 = { { x }, { x } }; +} + +void ArrayInit(int x, float f) { + int a1[3] = {}; + int a2[3] = { x, f, 0 }; + int a3[3] = { x }; +} + +union U { + double d; + int i; +}; + +void UnionInit(int x, float f) { + U u1 = { f }; +// U u2 = {}; Waiting for fix +} + +void EarlyReturn(int x, int y) { + if (x < y) { + return; + } + + y = x; +} + +int EarlyReturnValue(int x, int y) { + if (x < y) { + return x; + } + + return x + y; +} + +int CallViaFuncPtr(int (*pfn)(int)) { + return pfn(5); +} + +typedef enum { + E_0, + E_1 +} E; + +int EnumSwitch(E e) { + switch (e) { + case E_0: + return 0; + case E_1: + return 1; + default: + return -1; + } +} + +void InitArray() { + char a_pad[32] = ""; + char a_nopad[4] = "foo"; + char a_infer[] = "blah"; + char b[2]; + char c[2] = {}; + char d[2] = { 0 }; + char e[2] = { 0, 1 }; + char f[3] = { 0 }; +} + +void VarArgFunction(const char* s, ...); + +void VarArgs() { + VarArgFunction("%d %s", 1, "string"); +} + +int FuncPtrTarget(int); + +void SetFuncPtr() { + int (*pfn)(int) = FuncPtrTarget; + pfn = &FuncPtrTarget; + pfn = *FuncPtrTarget; + pfn = ***&FuncPtrTarget; +} + +struct String { + String(); + String(const String&); + String(String&&); + String(const char*); + ~String(); + + String& operator=(const String&); + String& operator=(String&&); + + const char* c_str() const; + +private: + const char* p; +}; + +String ReturnObject(); + +void DeclareObject() { + String s1; + String s2("hello"); + String s3 = ReturnObject(); + String s4 = String("test"); +} + +void CallMethods(String& r, String* p, String s) { + r.c_str(); + p->c_str(); + s.c_str(); +} + +class C { +public: + static int StaticMemberFunction(int x) { + return x; + } + + int InstanceMemberFunction(int x) { + return x; + } + + virtual int VirtualMemberFunction(int x) { + return x; + } + + void FieldAccess() { + this->m_a = 0; + (*this).m_a = 1; + m_a = 2; + int x; + x = this->m_a; + x = (*this).m_a; + x = m_a; + } + + void MethodCalls() { + this->InstanceMemberFunction(0); + (*this).InstanceMemberFunction(1); + InstanceMemberFunction(2); + } + + C() : + m_a(1), + m_c(3), + m_e{}, + m_f("test") + { + } + +private: + int m_a; + String m_b; + char m_c; + float m_d; + void* m_e; + String m_f; +}; + +int DerefReference(int& r) { + return r; +} + +int& TakeReference() { + return g; +} + +String& ReturnReference(); + +void InitReference(int x) { + int& r = x; + int& r2 = r; + const String& r3 = ReturnReference(); +} + +void ArrayReferences() { + int a[10]; + int (&ra)[10] = a; + int x = ra[5]; +} + +void FunctionReferences() { + int(&rfn)(int) = FuncPtrTarget; + int(*pfn)(int) = rfn; + rfn(5); +} + +template +T min(T x, T y) { + return (x < y) ? x : y; +} + +int CallMin(int x, int y) { + return min(x, y); +} + +template +struct Outer { + template + static T Func(U x, V y) { + return T(); + } +}; + +double CallNestedTemplateFunc() { + return Outer::Func(nullptr, 'o'); +} + +void TryCatch(bool b) { + try { + int x = 5; + if (b) { + throw "string literal"; + } + else if (x < 2) { + x = b ? 7 : throw String("String object"); + } + x = 7; + } + catch (const char* s) { + throw String(s); + } + catch (const String& e) { + } + catch (...) { + throw; + } +} + +struct Base { + String base_s; + + Base() { + } + ~Base() { + } +}; + +struct Middle : Base { + String middle_s; + + Middle() { + } + ~Middle() { + } +}; + +struct Derived : Middle { + String derived_s; + + Derived() { + } + ~Derived() { + } +}; + +struct MiddleVB1 : virtual Base { + String middlevb1_s; + + MiddleVB1() { + } + ~MiddleVB1() { + } +}; + +struct MiddleVB2 : virtual Base { + String middlevb2_s; + + MiddleVB2() { + } + ~MiddleVB2() { + } +}; + +struct DerivedVB : MiddleVB1, MiddleVB2 { + String derivedvb_s; + + DerivedVB() { + } + ~DerivedVB() { + } +}; + +void HierarchyConversions() { + Base b; + Middle m; + Derived d; + + Base* pb = &b; + Middle* pm = &m; + Derived* pd = &d; + + b = m; + b = (Base)m; + b = static_cast(m); + pb = pm; + pb = (Base*)pm; + pb = static_cast(pm); + pb = reinterpret_cast(pm); + + m = (Middle&)b; + m = static_cast(b); + pm = (Middle*)pb; + pm = static_cast(pb); + pm = reinterpret_cast(pb); + + b = d; + b = (Base)d; + b = static_cast(d); + pb = pd; + pb = (Base*)pd; + pb = static_cast(pd); + pb = reinterpret_cast(pd); + + d = (Derived&)b; + d = static_cast(b); + pd = (Derived*)pb; + pd = static_cast(pb); + pd = reinterpret_cast(pb); + + MiddleVB1* pmv = nullptr; + DerivedVB* pdv = nullptr; + pb = pmv; + pb = pdv; +} + +struct PolymorphicBase { + virtual ~PolymorphicBase(); +}; + +struct PolymorphicDerived : PolymorphicBase { +}; + +void DynamicCast() { + PolymorphicBase b; + PolymorphicDerived d; + + PolymorphicBase* pb = &b; + PolymorphicDerived* pd = &d; + + // These two casts are represented as BaseClassCasts because they can be resolved at compile time. + pb = dynamic_cast(pd); + PolymorphicBase& rb = dynamic_cast(d); + + pd = dynamic_cast(pb); + PolymorphicDerived& rd = dynamic_cast(b); + + void* pv = dynamic_cast(pb); + const void* pcv = dynamic_cast(pd); +} + +String::String() : + String("") { // Delegating constructor call +} + +void ArrayConversions() { + char a[5]; + const char* p = a; + p = "test"; + p = &a[0]; + p = &"test"[0]; + char (&ra)[5] = a; + const char (&rs)[5] = "test"; + const char (*pa)[5] = &a; + pa = &"test"; +} + +void FuncPtrConversions(int(*pfn)(int), void* p) { + p = (void*)pfn; + pfn = (int(*)(int))p; +} + +void VarArgUsage(int x, ...) { + __builtin_va_list args; + + __builtin_va_start(args, x); + __builtin_va_list args2; + __builtin_va_start(args2, args); + double d = __builtin_va_arg(args, double); + float f = __builtin_va_arg(args, float); + __builtin_va_end(args); + __builtin_va_end(args2); +} diff --git a/cpp/ql/test/library-tests/ir/ir/ir.expected b/cpp/ql/test/library-tests/ir/ir/ir.expected new file mode 100644 index 000000000000..204485ae5673 --- /dev/null +++ b/cpp/ql/test/library-tests/ir/ir/ir.expected @@ -0,0 +1,8432 @@ +printIRGraphScopes +| AddressOf() -> int * | AddressOf | +| ArrayAccess(int *, int) -> void | ArrayAccess | +| ArrayConversions() -> void | ArrayConversions | +| ArrayInit(int, float) -> void | ArrayInit | +| ArrayReferences() -> void | ArrayReferences | +| Base::Base() -> void | Base::Base | +| Base::Base(const Base &) -> void | Base::Base | +| Base::operator=(const Base &) -> Base & | Base::operator= | +| Base::~Base() -> void | Base::~Base | +| Break(int) -> void | Break | +| C::C() -> void | C::C | +| C::FieldAccess() -> void | C::FieldAccess | +| C::InstanceMemberFunction(int) -> int | C::InstanceMemberFunction | +| C::MethodCalls() -> void | C::MethodCalls | +| C::StaticMemberFunction(int) -> int | C::StaticMemberFunction | +| C::VirtualMemberFunction(int) -> int | C::VirtualMemberFunction | +| Call() -> void | Call | +| CallAdd(int, int) -> int | CallAdd | +| CallMethods(String &, String *, String) -> void | CallMethods | +| CallMin(int, int) -> int | CallMin | +| CallNestedTemplateFunc() -> double | CallNestedTemplateFunc | +| CallViaFuncPtr(..(*)(..)) -> int | CallViaFuncPtr | +| Comma(int, int) -> int | Comma | +| CompoundAssignment() -> void | CompoundAssignment | +| ConditionValues(bool, bool) -> void | ConditionValues | +| Conditional(bool, int, int) -> void | Conditional | +| Conditional_LValue(bool) -> void | Conditional_LValue | +| Conditional_Void(bool) -> void | Conditional_Void | +| Constants() -> void | Constants | +| Continue(int) -> void | Continue | +| DeclareObject() -> void | DeclareObject | +| DerefReference(int &) -> int | DerefReference | +| Dereference(int *) -> int | Dereference | +| Derived::Derived() -> void | Derived::Derived | +| Derived::operator=(const Derived &) -> Derived & | Derived::operator= | +| Derived::~Derived() -> void | Derived::~Derived | +| DerivedVB::DerivedVB() -> void | DerivedVB::DerivedVB | +| DerivedVB::~DerivedVB() -> void | DerivedVB::~DerivedVB | +| DoStatements(int) -> void | DoStatements | +| DynamicCast() -> void | DynamicCast | +| EarlyReturn(int, int) -> void | EarlyReturn | +| EarlyReturnValue(int, int) -> int | EarlyReturnValue | +| EnumSwitch(E) -> int | EnumSwitch | +| FieldAccess() -> void | FieldAccess | +| FloatCompare(double, double) -> void | FloatCompare | +| FloatCrement(float) -> void | FloatCrement | +| FloatOps(double, double) -> void | FloatOps | +| Foo() -> void | Foo | +| For_Break() -> void | For_Break | +| For_Condition() -> void | For_Condition | +| For_ConditionUpdate() -> void | For_ConditionUpdate | +| For_Continue_NoUpdate() -> void | For_Continue_NoUpdate | +| For_Continue_Update() -> void | For_Continue_Update | +| For_Empty() -> void | For_Empty | +| For_Init() -> void | For_Init | +| For_InitCondition() -> void | For_InitCondition | +| For_InitConditionUpdate() -> void | For_InitConditionUpdate | +| For_InitUpdate() -> void | For_InitUpdate | +| For_Update() -> void | For_Update | +| FuncPtrConversions(..(*)(..), void *) -> void | FuncPtrConversions | +| FunctionReferences() -> void | FunctionReferences | +| HierarchyConversions() -> void | HierarchyConversions | +| IfStatements(bool, int, int) -> void | IfStatements | +| InitArray() -> void | InitArray | +| InitList(int, float) -> void | InitList | +| InitReference(int) -> void | InitReference | +| IntegerCompare(int, int) -> void | IntegerCompare | +| IntegerCrement(int) -> void | IntegerCrement | +| IntegerCrement_LValue(int) -> void | IntegerCrement_LValue | +| IntegerOps(int, int) -> void | IntegerOps | +| LogicalAnd(bool, bool) -> void | LogicalAnd | +| LogicalNot(bool, bool) -> void | LogicalNot | +| LogicalOr(bool, bool) -> void | LogicalOr | +| Middle::Middle() -> void | Middle::Middle | +| Middle::operator=(const Middle &) -> Middle & | Middle::operator= | +| Middle::~Middle() -> void | Middle::~Middle | +| MiddleVB1::MiddleVB1() -> void | MiddleVB1::MiddleVB1 | +| MiddleVB1::~MiddleVB1() -> void | MiddleVB1::~MiddleVB1 | +| MiddleVB2::MiddleVB2() -> void | MiddleVB2::MiddleVB2 | +| MiddleVB2::~MiddleVB2() -> void | MiddleVB2::~MiddleVB2 | +| NestedInitList(int, float) -> void | NestedInitList | +| Nullptr() -> void | Nullptr | +| Outer::Func(void *, char) -> long | Outer::Func | +| Parameters(int, int) -> int | Parameters | +| PointerCompare(int *, int *) -> void | PointerCompare | +| PointerCrement(int *) -> void | PointerCrement | +| PointerOps(int *, int) -> void | PointerOps | +| PolymorphicBase::PolymorphicBase() -> void | PolymorphicBase::PolymorphicBase | +| PolymorphicDerived::PolymorphicDerived() -> void | PolymorphicDerived::PolymorphicDerived | +| PolymorphicDerived::~PolymorphicDerived() -> void | PolymorphicDerived::~PolymorphicDerived | +| ReturnStruct(Point) -> Point | ReturnStruct | +| SetFuncPtr() -> void | SetFuncPtr | +| String::String() -> void | String::String | +| StringLiteral(int) -> void | StringLiteral | +| Switch(int) -> void | Switch | +| TakeReference() -> int & | TakeReference | +| TryCatch(bool) -> void | TryCatch | +| UninitializedVariables() -> void | UninitializedVariables | +| UnionInit(int, float) -> void | UnionInit | +| VarArgUsage(int) -> void | VarArgUsage | +| VarArgs() -> void | VarArgs | +| WhileStatements(int) -> void | WhileStatements | +| min(int, int) -> int | min | +printIRGraphNodes +| AddressOf() -> int * | 0 | | | +| ArrayAccess(int *, int) -> void | 0 | | | +| ArrayConversions() -> void | 0 | | | +| ArrayInit(int, float) -> void | 0 | | | +| ArrayReferences() -> void | 0 | | | +| Base::Base() -> void | 0 | | | +| Base::Base(const Base &) -> void | 0 | | | +| Base::operator=(const Base &) -> Base & | 0 | | | +| Base::~Base() -> void | 0 | | | +| Break(int) -> void | 0 | | | +| Break(int) -> void | 1 | | | +| Break(int) -> void | 2 | | | +| Break(int) -> void | 3 | | | +| Break(int) -> void | 4 | | | +| Break(int) -> void | 5 | | | +| C::C() -> void | 0 | | | +| C::FieldAccess() -> void | 0 | | | +| C::InstanceMemberFunction(int) -> int | 0 | | | +| C::MethodCalls() -> void | 0 | | | +| C::StaticMemberFunction(int) -> int | 0 | | | +| C::VirtualMemberFunction(int) -> int | 0 | | | +| Call() -> void | 0 | | | +| CallAdd(int, int) -> int | 0 | | | +| CallMethods(String &, String *, String) -> void | 0 | | | +| CallMin(int, int) -> int | 0 | | | +| CallNestedTemplateFunc() -> double | 0 | | | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | | | +| Comma(int, int) -> int | 0 | | | +| CompoundAssignment() -> void | 0 | | | +| ConditionValues(bool, bool) -> void | 0 | | | +| ConditionValues(bool, bool) -> void | 1 | | | +| ConditionValues(bool, bool) -> void | 2 | | | +| ConditionValues(bool, bool) -> void | 3 | | | +| ConditionValues(bool, bool) -> void | 4 | | | +| ConditionValues(bool, bool) -> void | 5 | | | +| ConditionValues(bool, bool) -> void | 6 | | | +| ConditionValues(bool, bool) -> void | 7 | | | +| ConditionValues(bool, bool) -> void | 8 | | | +| ConditionValues(bool, bool) -> void | 9 | | | +| ConditionValues(bool, bool) -> void | 10 | | | +| ConditionValues(bool, bool) -> void | 11 | | | +| ConditionValues(bool, bool) -> void | 12 | | | +| Conditional(bool, int, int) -> void | 0 | | | +| Conditional(bool, int, int) -> void | 1 | | | +| Conditional(bool, int, int) -> void | 2 | | | +| Conditional(bool, int, int) -> void | 3 | | | +| Conditional_LValue(bool) -> void | 0 | | | +| Conditional_LValue(bool) -> void | 1 | | | +| Conditional_LValue(bool) -> void | 2 | | | +| Conditional_LValue(bool) -> void | 3 | | | +| Conditional_Void(bool) -> void | 0 | | | +| Conditional_Void(bool) -> void | 1 | | | +| Conditional_Void(bool) -> void | 2 | | | +| Conditional_Void(bool) -> void | 3 | | | +| Constants() -> void | 0 | | | +| Continue(int) -> void | 0 | | | +| Continue(int) -> void | 1 | | | +| Continue(int) -> void | 2 | | | +| Continue(int) -> void | 3 | | | +| Continue(int) -> void | 4 | | | +| Continue(int) -> void | 5 | | | +| DeclareObject() -> void | 0 | | | +| DerefReference(int &) -> int | 0 | | | +| Dereference(int *) -> int | 0 | | | +| Derived::Derived() -> void | 0 | | | +| Derived::operator=(const Derived &) -> Derived & | 0 | | | +| Derived::~Derived() -> void | 0 | | | +| DerivedVB::DerivedVB() -> void | 0 | | | +| DerivedVB::~DerivedVB() -> void | 0 | | | +| DoStatements(int) -> void | 0 | | | +| DoStatements(int) -> void | 1 | | | +| DoStatements(int) -> void | 2 | | | +| DynamicCast() -> void | 0 | | | +| EarlyReturn(int, int) -> void | 0 | | | +| EarlyReturn(int, int) -> void | 1 | | | +| EarlyReturn(int, int) -> void | 2 | | | +| EarlyReturn(int, int) -> void | 3 | | | +| EarlyReturnValue(int, int) -> int | 0 | | | +| EarlyReturnValue(int, int) -> int | 1 | | | +| EarlyReturnValue(int, int) -> int | 2 | | | +| EarlyReturnValue(int, int) -> int | 3 | | | +| EnumSwitch(E) -> int | 0 | | | +| EnumSwitch(E) -> int | 1 | | | +| EnumSwitch(E) -> int | 2 | | | +| EnumSwitch(E) -> int | 3 | | | +| EnumSwitch(E) -> int | 4 | | | +| FieldAccess() -> void | 0 | | | +| FloatCompare(double, double) -> void | 0 | | | +| FloatCrement(float) -> void | 0 | | | +| FloatOps(double, double) -> void | 0 | | | +| Foo() -> void | 0 | | | +| For_Break() -> void | 0 | | | +| For_Break() -> void | 1 | | | +| For_Break() -> void | 2 | | | +| For_Break() -> void | 3 | | | +| For_Break() -> void | 4 | | | +| For_Break() -> void | 5 | | | +| For_Condition() -> void | 0 | | | +| For_Condition() -> void | 1 | | | +| For_Condition() -> void | 2 | | | +| For_Condition() -> void | 3 | | | +| For_ConditionUpdate() -> void | 0 | | | +| For_ConditionUpdate() -> void | 1 | | | +| For_ConditionUpdate() -> void | 2 | | | +| For_ConditionUpdate() -> void | 3 | | | +| For_Continue_NoUpdate() -> void | 0 | | | +| For_Continue_NoUpdate() -> void | 1 | | | +| For_Continue_NoUpdate() -> void | 2 | | | +| For_Continue_NoUpdate() -> void | 3 | | | +| For_Continue_NoUpdate() -> void | 4 | | | +| For_Continue_NoUpdate() -> void | 5 | | | +| For_Continue_Update() -> void | 0 | | | +| For_Continue_Update() -> void | 1 | | | +| For_Continue_Update() -> void | 2 | | | +| For_Continue_Update() -> void | 3 | | | +| For_Continue_Update() -> void | 4 | | | +| For_Continue_Update() -> void | 5 | | | +| For_Empty() -> void | 0 | | | +| For_Empty() -> void | 1 | | | +| For_Empty() -> void | 2 | | | +| For_Init() -> void | 0 | | | +| For_Init() -> void | 1 | | | +| For_Init() -> void | 2 | | | +| For_InitCondition() -> void | 0 | | | +| For_InitCondition() -> void | 1 | | | +| For_InitCondition() -> void | 2 | | | +| For_InitCondition() -> void | 3 | | | +| For_InitConditionUpdate() -> void | 0 | | | +| For_InitConditionUpdate() -> void | 1 | | | +| For_InitConditionUpdate() -> void | 2 | | | +| For_InitConditionUpdate() -> void | 3 | | | +| For_InitUpdate() -> void | 0 | | | +| For_InitUpdate() -> void | 1 | | | +| For_InitUpdate() -> void | 2 | | | +| For_Update() -> void | 0 | | | +| For_Update() -> void | 1 | | | +| For_Update() -> void | 2 | | | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | | | +| FunctionReferences() -> void | 0 | | | +| HierarchyConversions() -> void | 0 | | | +| IfStatements(bool, int, int) -> void | 0 | | | +| IfStatements(bool, int, int) -> void | 1 | | | +| IfStatements(bool, int, int) -> void | 2 | | | +| IfStatements(bool, int, int) -> void | 3 | | | +| IfStatements(bool, int, int) -> void | 4 | | | +| IfStatements(bool, int, int) -> void | 5 | | | +| IfStatements(bool, int, int) -> void | 6 | | | +| IfStatements(bool, int, int) -> void | 7 | | | +| InitArray() -> void | 0 | | | +| InitList(int, float) -> void | 0 | | | +| InitReference(int) -> void | 0 | | | +| IntegerCompare(int, int) -> void | 0 | | | +| IntegerCrement(int) -> void | 0 | | | +| IntegerCrement_LValue(int) -> void | 0 | | | +| IntegerOps(int, int) -> void | 0 | | | +| LogicalAnd(bool, bool) -> void | 0 | | | +| LogicalAnd(bool, bool) -> void | 1 | | | +| LogicalAnd(bool, bool) -> void | 2 | | | +| LogicalAnd(bool, bool) -> void | 3 | | | +| LogicalAnd(bool, bool) -> void | 4 | | | +| LogicalAnd(bool, bool) -> void | 5 | | | +| LogicalAnd(bool, bool) -> void | 6 | | | +| LogicalAnd(bool, bool) -> void | 7 | | | +| LogicalNot(bool, bool) -> void | 0 | | | +| LogicalNot(bool, bool) -> void | 1 | | | +| LogicalNot(bool, bool) -> void | 2 | | | +| LogicalNot(bool, bool) -> void | 3 | | | +| LogicalNot(bool, bool) -> void | 4 | | | +| LogicalNot(bool, bool) -> void | 5 | | | +| LogicalNot(bool, bool) -> void | 6 | | | +| LogicalOr(bool, bool) -> void | 0 | | | +| LogicalOr(bool, bool) -> void | 1 | | | +| LogicalOr(bool, bool) -> void | 2 | | | +| LogicalOr(bool, bool) -> void | 3 | | | +| LogicalOr(bool, bool) -> void | 4 | | | +| LogicalOr(bool, bool) -> void | 5 | | | +| LogicalOr(bool, bool) -> void | 6 | | | +| LogicalOr(bool, bool) -> void | 7 | | | +| Middle::Middle() -> void | 0 | | | +| Middle::operator=(const Middle &) -> Middle & | 0 | | | +| Middle::~Middle() -> void | 0 | | | +| MiddleVB1::MiddleVB1() -> void | 0 | | | +| MiddleVB1::~MiddleVB1() -> void | 0 | | | +| MiddleVB2::MiddleVB2() -> void | 0 | | | +| MiddleVB2::~MiddleVB2() -> void | 0 | | | +| NestedInitList(int, float) -> void | 0 | | | +| Nullptr() -> void | 0 | | | +| Outer::Func(void *, char) -> long | 0 | | | +| Parameters(int, int) -> int | 0 | | | +| PointerCompare(int *, int *) -> void | 0 | | | +| PointerCrement(int *) -> void | 0 | | | +| PointerOps(int *, int) -> void | 0 | | | +| PolymorphicBase::PolymorphicBase() -> void | 0 | | | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | | | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | | | +| ReturnStruct(Point) -> Point | 0 | | | +| SetFuncPtr() -> void | 0 | | | +| String::String() -> void | 0 | | | +| StringLiteral(int) -> void | 0 | | | +| Switch(int) -> void | 0 | | | +| Switch(int) -> void | 1 | | | +| Switch(int) -> void | 2 | | | +| Switch(int) -> void | 3 | | | +| Switch(int) -> void | 4 | | | +| Switch(int) -> void | 5 | | | +| Switch(int) -> void | 6 | | | +| Switch(int) -> void | 7 | | | +| Switch(int) -> void | 8 | | | +| Switch(int) -> void | 9 | | | +| TakeReference() -> int & | 0 | | | +| TryCatch(bool) -> void | 0 | | | +| TryCatch(bool) -> void | 1 | | | +| TryCatch(bool) -> void | 2 | | | +| TryCatch(bool) -> void | 3 | | | +| TryCatch(bool) -> void | 4 | | | +| TryCatch(bool) -> void | 5 | | | +| TryCatch(bool) -> void | 6 | | | +| TryCatch(bool) -> void | 7 | | | +| TryCatch(bool) -> void | 8 | | | +| TryCatch(bool) -> void | 9 | | | +| TryCatch(bool) -> void | 10 | | | +| TryCatch(bool) -> void | 11 | | | +| TryCatch(bool) -> void | 12 | | | +| TryCatch(bool) -> void | 13 | | | +| TryCatch(bool) -> void | 14 | | | +| UninitializedVariables() -> void | 0 | | | +| UnionInit(int, float) -> void | 0 | | | +| VarArgUsage(int) -> void | 0 | | | +| VarArgs() -> void | 0 | | | +| WhileStatements(int) -> void | 0 | | | +| WhileStatements(int) -> void | 1 | | | +| WhileStatements(int) -> void | 2 | | | +| WhileStatements(int) -> void | 3 | | | +| min(int, int) -> int | 0 | | | +| min(int, int) -> int | 1 | | | +| min(int, int) -> int | 2 | | | +| min(int, int) -> int | 3 | | | +printIRGraphInstructions +| AddressOf() -> int * | 0 | 0 | EnterFunction | ir.cpp:348:6:348:14 | +| AddressOf() -> int * | 0 | 1 | UnmodeledDefinition | ir.cpp:348:6:348:14 | +| AddressOf() -> int * | 0 | 2 | VariableAddress[#return] | ir.cpp:349:5:349:14 | +| AddressOf() -> int * | 0 | 3 | VariableAddress[g] | ir.cpp:349:13:349:13 | +| AddressOf() -> int * | 0 | 4 | Store | ir.cpp:349:12:349:13 | +| AddressOf() -> int * | 0 | 5 | VariableAddress[#return] | ir.cpp:348:6:348:14 | +| AddressOf() -> int * | 0 | 6 | ReturnValue | ir.cpp:348:6:348:14 | +| AddressOf() -> int * | 0 | 7 | UnmodeledUse | ir.cpp:348:6:348:14 | +| AddressOf() -> int * | 0 | 8 | ExitFunction | ir.cpp:348:6:348:14 | +| ArrayAccess(int *, int) -> void | 0 | 0 | EnterFunction | ir.cpp:171:6:171:16 | +| ArrayAccess(int *, int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:171:6:171:16 | +| ArrayAccess(int *, int) -> void | 0 | 2 | InitializeParameter[p] | ir.cpp:171:23:171:23 | +| ArrayAccess(int *, int) -> void | 0 | 3 | VariableAddress[p] | ir.cpp:171:23:171:23 | +| ArrayAccess(int *, int) -> void | 0 | 4 | Store | ir.cpp:171:23:171:23 | +| ArrayAccess(int *, int) -> void | 0 | 5 | InitializeParameter[i] | ir.cpp:171:30:171:30 | +| ArrayAccess(int *, int) -> void | 0 | 6 | VariableAddress[i] | ir.cpp:171:30:171:30 | +| ArrayAccess(int *, int) -> void | 0 | 7 | Store | ir.cpp:171:30:171:30 | +| ArrayAccess(int *, int) -> void | 0 | 8 | VariableAddress[x] | ir.cpp:172:9:172:9 | +| ArrayAccess(int *, int) -> void | 0 | 9 | Uninitialized | ir.cpp:172:9:172:9 | +| ArrayAccess(int *, int) -> void | 0 | 10 | Store | ir.cpp:172:9:172:9 | +| ArrayAccess(int *, int) -> void | 0 | 11 | VariableAddress[p] | ir.cpp:174:9:174:9 | +| ArrayAccess(int *, int) -> void | 0 | 12 | Load | ir.cpp:174:9:174:9 | +| ArrayAccess(int *, int) -> void | 0 | 13 | VariableAddress[i] | ir.cpp:174:11:174:11 | +| ArrayAccess(int *, int) -> void | 0 | 14 | Load | ir.cpp:174:11:174:11 | +| ArrayAccess(int *, int) -> void | 0 | 15 | PointerAdd[4] | ir.cpp:174:9:174:12 | +| ArrayAccess(int *, int) -> void | 0 | 16 | Load | ir.cpp:174:9:174:12 | +| ArrayAccess(int *, int) -> void | 0 | 17 | VariableAddress[x] | ir.cpp:174:5:174:5 | +| ArrayAccess(int *, int) -> void | 0 | 18 | Store | ir.cpp:174:5:174:12 | +| ArrayAccess(int *, int) -> void | 0 | 19 | VariableAddress[p] | ir.cpp:175:11:175:11 | +| ArrayAccess(int *, int) -> void | 0 | 20 | Load | ir.cpp:175:11:175:11 | +| ArrayAccess(int *, int) -> void | 0 | 21 | VariableAddress[i] | ir.cpp:175:9:175:9 | +| ArrayAccess(int *, int) -> void | 0 | 22 | Load | ir.cpp:175:9:175:9 | +| ArrayAccess(int *, int) -> void | 0 | 23 | PointerAdd[4] | ir.cpp:175:9:175:12 | +| ArrayAccess(int *, int) -> void | 0 | 24 | Load | ir.cpp:175:9:175:12 | +| ArrayAccess(int *, int) -> void | 0 | 25 | VariableAddress[x] | ir.cpp:175:5:175:5 | +| ArrayAccess(int *, int) -> void | 0 | 26 | Store | ir.cpp:175:5:175:12 | +| ArrayAccess(int *, int) -> void | 0 | 27 | VariableAddress[x] | ir.cpp:177:12:177:12 | +| ArrayAccess(int *, int) -> void | 0 | 28 | Load | ir.cpp:177:12:177:12 | +| ArrayAccess(int *, int) -> void | 0 | 29 | VariableAddress[p] | ir.cpp:177:5:177:5 | +| ArrayAccess(int *, int) -> void | 0 | 30 | Load | ir.cpp:177:5:177:5 | +| ArrayAccess(int *, int) -> void | 0 | 31 | VariableAddress[i] | ir.cpp:177:7:177:7 | +| ArrayAccess(int *, int) -> void | 0 | 32 | Load | ir.cpp:177:7:177:7 | +| ArrayAccess(int *, int) -> void | 0 | 33 | PointerAdd[4] | ir.cpp:177:5:177:8 | +| ArrayAccess(int *, int) -> void | 0 | 34 | Store | ir.cpp:177:5:177:12 | +| ArrayAccess(int *, int) -> void | 0 | 35 | VariableAddress[x] | ir.cpp:178:12:178:12 | +| ArrayAccess(int *, int) -> void | 0 | 36 | Load | ir.cpp:178:12:178:12 | +| ArrayAccess(int *, int) -> void | 0 | 37 | VariableAddress[p] | ir.cpp:178:7:178:7 | +| ArrayAccess(int *, int) -> void | 0 | 38 | Load | ir.cpp:178:7:178:7 | +| ArrayAccess(int *, int) -> void | 0 | 39 | VariableAddress[i] | ir.cpp:178:5:178:5 | +| ArrayAccess(int *, int) -> void | 0 | 40 | Load | ir.cpp:178:5:178:5 | +| ArrayAccess(int *, int) -> void | 0 | 41 | PointerAdd[4] | ir.cpp:178:5:178:8 | +| ArrayAccess(int *, int) -> void | 0 | 42 | Store | ir.cpp:178:5:178:12 | +| ArrayAccess(int *, int) -> void | 0 | 43 | VariableAddress[a] | ir.cpp:180:9:180:9 | +| ArrayAccess(int *, int) -> void | 0 | 44 | Uninitialized | ir.cpp:180:9:180:9 | +| ArrayAccess(int *, int) -> void | 0 | 45 | Store | ir.cpp:180:9:180:9 | +| ArrayAccess(int *, int) -> void | 0 | 46 | VariableAddress[a] | ir.cpp:181:9:181:9 | +| ArrayAccess(int *, int) -> void | 0 | 47 | Convert | ir.cpp:181:9:181:9 | +| ArrayAccess(int *, int) -> void | 0 | 48 | VariableAddress[i] | ir.cpp:181:11:181:11 | +| ArrayAccess(int *, int) -> void | 0 | 49 | Load | ir.cpp:181:11:181:11 | +| ArrayAccess(int *, int) -> void | 0 | 50 | PointerAdd[4] | ir.cpp:181:9:181:12 | +| ArrayAccess(int *, int) -> void | 0 | 51 | Load | ir.cpp:181:9:181:12 | +| ArrayAccess(int *, int) -> void | 0 | 52 | VariableAddress[x] | ir.cpp:181:5:181:5 | +| ArrayAccess(int *, int) -> void | 0 | 53 | Store | ir.cpp:181:5:181:12 | +| ArrayAccess(int *, int) -> void | 0 | 54 | VariableAddress[a] | ir.cpp:182:11:182:11 | +| ArrayAccess(int *, int) -> void | 0 | 55 | Convert | ir.cpp:182:11:182:11 | +| ArrayAccess(int *, int) -> void | 0 | 56 | VariableAddress[i] | ir.cpp:182:9:182:9 | +| ArrayAccess(int *, int) -> void | 0 | 57 | Load | ir.cpp:182:9:182:9 | +| ArrayAccess(int *, int) -> void | 0 | 58 | PointerAdd[4] | ir.cpp:182:9:182:12 | +| ArrayAccess(int *, int) -> void | 0 | 59 | Load | ir.cpp:182:9:182:12 | +| ArrayAccess(int *, int) -> void | 0 | 60 | VariableAddress[x] | ir.cpp:182:5:182:5 | +| ArrayAccess(int *, int) -> void | 0 | 61 | Store | ir.cpp:182:5:182:12 | +| ArrayAccess(int *, int) -> void | 0 | 62 | VariableAddress[x] | ir.cpp:183:12:183:12 | +| ArrayAccess(int *, int) -> void | 0 | 63 | Load | ir.cpp:183:12:183:12 | +| ArrayAccess(int *, int) -> void | 0 | 64 | VariableAddress[a] | ir.cpp:183:5:183:5 | +| ArrayAccess(int *, int) -> void | 0 | 65 | Convert | ir.cpp:183:5:183:5 | +| ArrayAccess(int *, int) -> void | 0 | 66 | VariableAddress[i] | ir.cpp:183:7:183:7 | +| ArrayAccess(int *, int) -> void | 0 | 67 | Load | ir.cpp:183:7:183:7 | +| ArrayAccess(int *, int) -> void | 0 | 68 | PointerAdd[4] | ir.cpp:183:5:183:8 | +| ArrayAccess(int *, int) -> void | 0 | 69 | Store | ir.cpp:183:5:183:12 | +| ArrayAccess(int *, int) -> void | 0 | 70 | VariableAddress[x] | ir.cpp:184:12:184:12 | +| ArrayAccess(int *, int) -> void | 0 | 71 | Load | ir.cpp:184:12:184:12 | +| ArrayAccess(int *, int) -> void | 0 | 72 | VariableAddress[a] | ir.cpp:184:7:184:7 | +| ArrayAccess(int *, int) -> void | 0 | 73 | Convert | ir.cpp:184:7:184:7 | +| ArrayAccess(int *, int) -> void | 0 | 74 | VariableAddress[i] | ir.cpp:184:5:184:5 | +| ArrayAccess(int *, int) -> void | 0 | 75 | Load | ir.cpp:184:5:184:5 | +| ArrayAccess(int *, int) -> void | 0 | 76 | PointerAdd[4] | ir.cpp:184:5:184:8 | +| ArrayAccess(int *, int) -> void | 0 | 77 | Store | ir.cpp:184:5:184:12 | +| ArrayAccess(int *, int) -> void | 0 | 78 | NoOp | ir.cpp:185:1:185:1 | +| ArrayAccess(int *, int) -> void | 0 | 79 | ReturnVoid | ir.cpp:171:6:171:16 | +| ArrayAccess(int *, int) -> void | 0 | 80 | UnmodeledUse | ir.cpp:171:6:171:16 | +| ArrayAccess(int *, int) -> void | 0 | 81 | ExitFunction | ir.cpp:171:6:171:16 | +| ArrayConversions() -> void | 0 | 0 | EnterFunction | ir.cpp:871:6:871:21 | +| ArrayConversions() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:871:6:871:21 | +| ArrayConversions() -> void | 0 | 2 | VariableAddress[a] | ir.cpp:872:8:872:8 | +| ArrayConversions() -> void | 0 | 3 | Uninitialized | ir.cpp:872:8:872:8 | +| ArrayConversions() -> void | 0 | 4 | Store | ir.cpp:872:8:872:8 | +| ArrayConversions() -> void | 0 | 5 | VariableAddress[p] | ir.cpp:873:15:873:15 | +| ArrayConversions() -> void | 0 | 6 | VariableAddress[a] | ir.cpp:873:19:873:19 | +| ArrayConversions() -> void | 0 | 7 | Convert | ir.cpp:873:19:873:19 | +| ArrayConversions() -> void | 0 | 8 | Convert | ir.cpp:873:19:873:19 | +| ArrayConversions() -> void | 0 | 9 | Store | ir.cpp:873:19:873:19 | +| ArrayConversions() -> void | 0 | 10 | StringConstant["test"] | ir.cpp:874:7:874:12 | +| ArrayConversions() -> void | 0 | 11 | Convert | ir.cpp:874:7:874:12 | +| ArrayConversions() -> void | 0 | 12 | VariableAddress[p] | ir.cpp:874:3:874:3 | +| ArrayConversions() -> void | 0 | 13 | Store | ir.cpp:874:3:874:12 | +| ArrayConversions() -> void | 0 | 14 | VariableAddress[a] | ir.cpp:875:8:875:8 | +| ArrayConversions() -> void | 0 | 15 | Convert | ir.cpp:875:8:875:8 | +| ArrayConversions() -> void | 0 | 16 | Constant[0] | ir.cpp:875:10:875:10 | +| ArrayConversions() -> void | 0 | 17 | PointerAdd[1] | ir.cpp:875:8:875:11 | +| ArrayConversions() -> void | 0 | 18 | Convert | ir.cpp:875:7:875:11 | +| ArrayConversions() -> void | 0 | 19 | VariableAddress[p] | ir.cpp:875:3:875:3 | +| ArrayConversions() -> void | 0 | 20 | Store | ir.cpp:875:3:875:11 | +| ArrayConversions() -> void | 0 | 21 | StringConstant["test"] | ir.cpp:876:8:876:13 | +| ArrayConversions() -> void | 0 | 22 | Convert | ir.cpp:876:8:876:13 | +| ArrayConversions() -> void | 0 | 23 | Constant[0] | ir.cpp:876:15:876:15 | +| ArrayConversions() -> void | 0 | 24 | PointerAdd[1] | ir.cpp:876:8:876:16 | +| ArrayConversions() -> void | 0 | 25 | VariableAddress[p] | ir.cpp:876:3:876:3 | +| ArrayConversions() -> void | 0 | 26 | Store | ir.cpp:876:3:876:16 | +| ArrayConversions() -> void | 0 | 27 | VariableAddress[ra] | ir.cpp:877:10:877:11 | +| ArrayConversions() -> void | 0 | 28 | VariableAddress[a] | ir.cpp:877:19:877:19 | +| ArrayConversions() -> void | 0 | 29 | Store | ir.cpp:877:19:877:19 | +| ArrayConversions() -> void | 0 | 30 | VariableAddress[rs] | ir.cpp:878:16:878:17 | +| ArrayConversions() -> void | 0 | 31 | StringConstant["test"] | ir.cpp:878:25:878:30 | +| ArrayConversions() -> void | 0 | 32 | Store | ir.cpp:878:25:878:30 | +| ArrayConversions() -> void | 0 | 33 | VariableAddress[pa] | ir.cpp:879:16:879:17 | +| ArrayConversions() -> void | 0 | 34 | VariableAddress[a] | ir.cpp:879:26:879:26 | +| ArrayConversions() -> void | 0 | 35 | Convert | ir.cpp:879:25:879:26 | +| ArrayConversions() -> void | 0 | 36 | Store | ir.cpp:879:25:879:26 | +| ArrayConversions() -> void | 0 | 37 | StringConstant["test"] | ir.cpp:880:9:880:14 | +| ArrayConversions() -> void | 0 | 38 | VariableAddress[pa] | ir.cpp:880:3:880:4 | +| ArrayConversions() -> void | 0 | 39 | Store | ir.cpp:880:3:880:14 | +| ArrayConversions() -> void | 0 | 40 | NoOp | ir.cpp:881:1:881:1 | +| ArrayConversions() -> void | 0 | 41 | ReturnVoid | ir.cpp:871:6:871:21 | +| ArrayConversions() -> void | 0 | 42 | UnmodeledUse | ir.cpp:871:6:871:21 | +| ArrayConversions() -> void | 0 | 43 | ExitFunction | ir.cpp:871:6:871:21 | +| ArrayInit(int, float) -> void | 0 | 0 | EnterFunction | ir.cpp:519:6:519:14 | +| ArrayInit(int, float) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:519:6:519:14 | +| ArrayInit(int, float) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:519:20:519:20 | +| ArrayInit(int, float) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:519:20:519:20 | +| ArrayInit(int, float) -> void | 0 | 4 | Store | ir.cpp:519:20:519:20 | +| ArrayInit(int, float) -> void | 0 | 5 | InitializeParameter[f] | ir.cpp:519:29:519:29 | +| ArrayInit(int, float) -> void | 0 | 6 | VariableAddress[f] | ir.cpp:519:29:519:29 | +| ArrayInit(int, float) -> void | 0 | 7 | Store | ir.cpp:519:29:519:29 | +| ArrayInit(int, float) -> void | 0 | 8 | VariableAddress[a1] | ir.cpp:520:9:520:10 | +| ArrayInit(int, float) -> void | 0 | 9 | Constant[0] | ir.cpp:520:16:520:18 | +| ArrayInit(int, float) -> void | 0 | 10 | PointerAdd | ir.cpp:520:16:520:18 | +| ArrayInit(int, float) -> void | 0 | 11 | Constant[0] | ir.cpp:520:16:520:18 | +| ArrayInit(int, float) -> void | 0 | 12 | Store | ir.cpp:520:16:520:18 | +| ArrayInit(int, float) -> void | 0 | 13 | VariableAddress[a2] | ir.cpp:521:9:521:10 | +| ArrayInit(int, float) -> void | 0 | 14 | Constant[0] | ir.cpp:521:16:521:27 | +| ArrayInit(int, float) -> void | 0 | 15 | PointerAdd | ir.cpp:521:16:521:27 | +| ArrayInit(int, float) -> void | 0 | 16 | VariableAddress[x] | ir.cpp:521:19:521:19 | +| ArrayInit(int, float) -> void | 0 | 17 | Load | ir.cpp:521:19:521:19 | +| ArrayInit(int, float) -> void | 0 | 18 | Store | ir.cpp:521:19:521:19 | +| ArrayInit(int, float) -> void | 0 | 19 | Constant[1] | ir.cpp:521:16:521:27 | +| ArrayInit(int, float) -> void | 0 | 20 | PointerAdd | ir.cpp:521:16:521:27 | +| ArrayInit(int, float) -> void | 0 | 21 | VariableAddress[f] | ir.cpp:521:22:521:22 | +| ArrayInit(int, float) -> void | 0 | 22 | Load | ir.cpp:521:22:521:22 | +| ArrayInit(int, float) -> void | 0 | 23 | Convert | ir.cpp:521:22:521:22 | +| ArrayInit(int, float) -> void | 0 | 24 | Store | ir.cpp:521:22:521:22 | +| ArrayInit(int, float) -> void | 0 | 25 | Constant[2] | ir.cpp:521:16:521:27 | +| ArrayInit(int, float) -> void | 0 | 26 | PointerAdd | ir.cpp:521:16:521:27 | +| ArrayInit(int, float) -> void | 0 | 27 | Constant[0] | ir.cpp:521:25:521:25 | +| ArrayInit(int, float) -> void | 0 | 28 | Store | ir.cpp:521:25:521:25 | +| ArrayInit(int, float) -> void | 0 | 29 | VariableAddress[a3] | ir.cpp:522:9:522:10 | +| ArrayInit(int, float) -> void | 0 | 30 | Constant[0] | ir.cpp:522:16:522:21 | +| ArrayInit(int, float) -> void | 0 | 31 | PointerAdd | ir.cpp:522:16:522:21 | +| ArrayInit(int, float) -> void | 0 | 32 | VariableAddress[x] | ir.cpp:522:19:522:19 | +| ArrayInit(int, float) -> void | 0 | 33 | Load | ir.cpp:522:19:522:19 | +| ArrayInit(int, float) -> void | 0 | 34 | Store | ir.cpp:522:19:522:19 | +| ArrayInit(int, float) -> void | 0 | 35 | Constant[1] | ir.cpp:522:16:522:21 | +| ArrayInit(int, float) -> void | 0 | 36 | PointerAdd | ir.cpp:522:16:522:21 | +| ArrayInit(int, float) -> void | 0 | 37 | Constant[0] | ir.cpp:522:16:522:21 | +| ArrayInit(int, float) -> void | 0 | 38 | Store | ir.cpp:522:16:522:21 | +| ArrayInit(int, float) -> void | 0 | 39 | NoOp | ir.cpp:523:1:523:1 | +| ArrayInit(int, float) -> void | 0 | 40 | ReturnVoid | ir.cpp:519:6:519:14 | +| ArrayInit(int, float) -> void | 0 | 41 | UnmodeledUse | ir.cpp:519:6:519:14 | +| ArrayInit(int, float) -> void | 0 | 42 | ExitFunction | ir.cpp:519:6:519:14 | +| ArrayReferences() -> void | 0 | 0 | EnterFunction | ir.cpp:691:6:691:20 | +| ArrayReferences() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:691:6:691:20 | +| ArrayReferences() -> void | 0 | 2 | VariableAddress[a] | ir.cpp:692:7:692:7 | +| ArrayReferences() -> void | 0 | 3 | Uninitialized | ir.cpp:692:7:692:7 | +| ArrayReferences() -> void | 0 | 4 | Store | ir.cpp:692:7:692:7 | +| ArrayReferences() -> void | 0 | 5 | VariableAddress[ra] | ir.cpp:693:9:693:10 | +| ArrayReferences() -> void | 0 | 6 | VariableAddress[a] | ir.cpp:693:19:693:19 | +| ArrayReferences() -> void | 0 | 7 | Store | ir.cpp:693:19:693:19 | +| ArrayReferences() -> void | 0 | 8 | VariableAddress[x] | ir.cpp:694:7:694:7 | +| ArrayReferences() -> void | 0 | 9 | VariableAddress[ra] | ir.cpp:694:11:694:12 | +| ArrayReferences() -> void | 0 | 10 | Load | ir.cpp:694:11:694:12 | +| ArrayReferences() -> void | 0 | 11 | Convert | ir.cpp:694:11:694:12 | +| ArrayReferences() -> void | 0 | 12 | Constant[5] | ir.cpp:694:14:694:14 | +| ArrayReferences() -> void | 0 | 13 | PointerAdd[4] | ir.cpp:694:11:694:15 | +| ArrayReferences() -> void | 0 | 14 | Load | ir.cpp:694:11:694:15 | +| ArrayReferences() -> void | 0 | 15 | Store | ir.cpp:694:11:694:15 | +| ArrayReferences() -> void | 0 | 16 | NoOp | ir.cpp:695:1:695:1 | +| ArrayReferences() -> void | 0 | 17 | ReturnVoid | ir.cpp:691:6:691:20 | +| ArrayReferences() -> void | 0 | 18 | UnmodeledUse | ir.cpp:691:6:691:20 | +| ArrayReferences() -> void | 0 | 19 | ExitFunction | ir.cpp:691:6:691:20 | +| Base::Base() -> void | 0 | 0 | EnterFunction | ir.cpp:748:3:748:6 | +| Base::Base() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:748:3:748:6 | +| Base::Base() -> void | 0 | 2 | InitializeThis | ir.cpp:748:3:748:6 | +| Base::Base() -> void | 0 | 3 | FieldAddress[base_s] | ir.cpp:748:10:748:10 | +| Base::Base() -> void | 0 | 4 | FunctionAddress[String] | ir.cpp:748:10:748:10 | +| Base::Base() -> void | 0 | 5 | Invoke | ir.cpp:748:10:748:10 | +| Base::Base() -> void | 0 | 6 | NoOp | ir.cpp:749:3:749:3 | +| Base::Base() -> void | 0 | 7 | ReturnVoid | ir.cpp:748:3:748:6 | +| Base::Base() -> void | 0 | 8 | UnmodeledUse | ir.cpp:748:3:748:6 | +| Base::Base() -> void | 0 | 9 | ExitFunction | ir.cpp:748:3:748:6 | +| Base::Base(const Base &) -> void | 0 | 0 | EnterFunction | ir.cpp:745:8:745:8 | +| Base::Base(const Base &) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:745:8:745:8 | +| Base::Base(const Base &) -> void | 0 | 2 | InitializeThis | ir.cpp:745:8:745:8 | +| Base::Base(const Base &) -> void | 0 | 3 | InitializeParameter[p#0] | file://:0:0:0:0 | +| Base::Base(const Base &) -> void | 0 | 4 | VariableAddress[p#0] | file://:0:0:0:0 | +| Base::Base(const Base &) -> void | 0 | 5 | Store | file://:0:0:0:0 | +| Base::Base(const Base &) -> void | 0 | 6 | FieldAddress[base_s] | ir.cpp:745:8:745:8 | +| Base::Base(const Base &) -> void | 0 | 7 | FunctionAddress[String] | ir.cpp:745:8:745:8 | +| Base::Base(const Base &) -> void | 0 | 8 | Invoke | ir.cpp:745:8:745:8 | +| Base::Base(const Base &) -> void | 0 | 9 | NoOp | ir.cpp:745:8:745:8 | +| Base::Base(const Base &) -> void | 0 | 10 | ReturnVoid | ir.cpp:745:8:745:8 | +| Base::Base(const Base &) -> void | 0 | 11 | UnmodeledUse | ir.cpp:745:8:745:8 | +| Base::Base(const Base &) -> void | 0 | 12 | ExitFunction | ir.cpp:745:8:745:8 | +| Base::operator=(const Base &) -> Base & | 0 | 0 | EnterFunction | ir.cpp:745:8:745:8 | +| Base::operator=(const Base &) -> Base & | 0 | 1 | UnmodeledDefinition | ir.cpp:745:8:745:8 | +| Base::operator=(const Base &) -> Base & | 0 | 2 | InitializeThis | ir.cpp:745:8:745:8 | +| Base::operator=(const Base &) -> Base & | 0 | 3 | InitializeParameter[p#0] | file://:0:0:0:0 | +| Base::operator=(const Base &) -> Base & | 0 | 4 | VariableAddress[p#0] | file://:0:0:0:0 | +| Base::operator=(const Base &) -> Base & | 0 | 5 | Store | file://:0:0:0:0 | +| Base::operator=(const Base &) -> Base & | 0 | 6 | CopyValue | file://:0:0:0:0 | +| Base::operator=(const Base &) -> Base & | 0 | 7 | FieldAddress[base_s] | file://:0:0:0:0 | +| Base::operator=(const Base &) -> Base & | 0 | 8 | FunctionAddress[operator=] | ir.cpp:745:8:745:8 | +| Base::operator=(const Base &) -> Base & | 0 | 9 | VariableAddress[p#0] | file://:0:0:0:0 | +| Base::operator=(const Base &) -> Base & | 0 | 10 | Load | file://:0:0:0:0 | +| Base::operator=(const Base &) -> Base & | 0 | 11 | FieldAddress[base_s] | file://:0:0:0:0 | +| Base::operator=(const Base &) -> Base & | 0 | 12 | Invoke | ir.cpp:745:8:745:8 | +| Base::operator=(const Base &) -> Base & | 0 | 13 | VariableAddress[#return] | file://:0:0:0:0 | +| Base::operator=(const Base &) -> Base & | 0 | 14 | CopyValue | file://:0:0:0:0 | +| Base::operator=(const Base &) -> Base & | 0 | 15 | Store | file://:0:0:0:0 | +| Base::operator=(const Base &) -> Base & | 0 | 16 | VariableAddress[#return] | ir.cpp:745:8:745:8 | +| Base::operator=(const Base &) -> Base & | 0 | 17 | ReturnValue | ir.cpp:745:8:745:8 | +| Base::operator=(const Base &) -> Base & | 0 | 18 | UnmodeledUse | ir.cpp:745:8:745:8 | +| Base::operator=(const Base &) -> Base & | 0 | 19 | ExitFunction | ir.cpp:745:8:745:8 | +| Base::~Base() -> void | 0 | 0 | EnterFunction | ir.cpp:750:3:750:7 | +| Base::~Base() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:750:3:750:7 | +| Base::~Base() -> void | 0 | 2 | InitializeThis | ir.cpp:750:3:750:7 | +| Base::~Base() -> void | 0 | 3 | NoOp | ir.cpp:751:3:751:3 | +| Base::~Base() -> void | 0 | 4 | FieldAddress[base_s] | ir.cpp:751:3:751:3 | +| Base::~Base() -> void | 0 | 5 | FunctionAddress[~String] | ir.cpp:751:3:751:3 | +| Base::~Base() -> void | 0 | 6 | Invoke | ir.cpp:751:3:751:3 | +| Base::~Base() -> void | 0 | 7 | ReturnVoid | ir.cpp:750:3:750:7 | +| Base::~Base() -> void | 0 | 8 | UnmodeledUse | ir.cpp:750:3:750:7 | +| Base::~Base() -> void | 0 | 9 | ExitFunction | ir.cpp:750:3:750:7 | +| Break(int) -> void | 0 | 0 | EnterFunction | ir.cpp:352:6:352:10 | +| Break(int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:352:6:352:10 | +| Break(int) -> void | 0 | 2 | InitializeParameter[n] | ir.cpp:352:16:352:16 | +| Break(int) -> void | 0 | 3 | VariableAddress[n] | ir.cpp:352:16:352:16 | +| Break(int) -> void | 0 | 4 | Store | ir.cpp:352:16:352:16 | +| Break(int) -> void | 1 | 0 | VariableAddress[n] | ir.cpp:354:13:354:13 | +| Break(int) -> void | 1 | 1 | Load | ir.cpp:354:13:354:13 | +| Break(int) -> void | 1 | 2 | Constant[1] | ir.cpp:354:18:354:18 | +| Break(int) -> void | 1 | 3 | CompareEQ | ir.cpp:354:13:354:18 | +| Break(int) -> void | 1 | 4 | ConditionalBranch | ir.cpp:354:13:354:18 | +| Break(int) -> void | 2 | 0 | NoOp | ir.cpp:355:13:355:18 | +| Break(int) -> void | 3 | 0 | Constant[1] | ir.cpp:356:14:356:14 | +| Break(int) -> void | 3 | 1 | VariableAddress[n] | ir.cpp:356:9:356:9 | +| Break(int) -> void | 3 | 2 | Load | ir.cpp:356:9:356:14 | +| Break(int) -> void | 3 | 3 | Sub | ir.cpp:356:9:356:14 | +| Break(int) -> void | 3 | 4 | Store | ir.cpp:356:9:356:14 | +| Break(int) -> void | 4 | 0 | NoOp | ir.cpp:357:5:357:5 | +| Break(int) -> void | 4 | 1 | NoOp | ir.cpp:358:1:358:1 | +| Break(int) -> void | 4 | 2 | ReturnVoid | ir.cpp:352:6:352:10 | +| Break(int) -> void | 4 | 3 | UnmodeledUse | ir.cpp:352:6:352:10 | +| Break(int) -> void | 4 | 4 | ExitFunction | ir.cpp:352:6:352:10 | +| Break(int) -> void | 5 | 0 | VariableAddress[n] | ir.cpp:353:12:353:12 | +| Break(int) -> void | 5 | 1 | Load | ir.cpp:353:12:353:12 | +| Break(int) -> void | 5 | 2 | Constant[0] | ir.cpp:353:16:353:16 | +| Break(int) -> void | 5 | 3 | CompareGT | ir.cpp:353:12:353:16 | +| Break(int) -> void | 5 | 4 | ConditionalBranch | ir.cpp:353:12:353:16 | +| C::C() -> void | 0 | 0 | EnterFunction | ir.cpp:658:5:658:5 | +| C::C() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:658:5:658:5 | +| C::C() -> void | 0 | 2 | InitializeThis | ir.cpp:658:5:658:5 | +| C::C() -> void | 0 | 3 | FieldAddress[m_a] | ir.cpp:659:9:659:14 | +| C::C() -> void | 0 | 4 | Constant[1] | ir.cpp:659:9:659:14 | +| C::C() -> void | 0 | 5 | Store | ir.cpp:659:9:659:14 | +| C::C() -> void | 0 | 6 | FieldAddress[m_b] | ir.cpp:663:5:663:5 | +| C::C() -> void | 0 | 7 | FunctionAddress[String] | ir.cpp:663:5:663:5 | +| C::C() -> void | 0 | 8 | Invoke | ir.cpp:663:5:663:5 | +| C::C() -> void | 0 | 9 | FieldAddress[m_c] | ir.cpp:660:9:660:14 | +| C::C() -> void | 0 | 10 | Constant[3] | ir.cpp:660:13:660:13 | +| C::C() -> void | 0 | 11 | Store | ir.cpp:660:13:660:13 | +| C::C() -> void | 0 | 12 | FieldAddress[m_e] | ir.cpp:661:9:661:13 | +| C::C() -> void | 0 | 13 | Constant[0] | ir.cpp:661:9:661:13 | +| C::C() -> void | 0 | 14 | Store | ir.cpp:661:9:661:13 | +| C::C() -> void | 0 | 15 | FieldAddress[m_f] | ir.cpp:662:9:662:19 | +| C::C() -> void | 0 | 16 | FunctionAddress[String] | ir.cpp:662:9:662:19 | +| C::C() -> void | 0 | 17 | StringConstant["test"] | ir.cpp:662:13:662:18 | +| C::C() -> void | 0 | 18 | Convert | ir.cpp:662:13:662:18 | +| C::C() -> void | 0 | 19 | Invoke | ir.cpp:662:9:662:19 | +| C::C() -> void | 0 | 20 | NoOp | ir.cpp:664:5:664:5 | +| C::C() -> void | 0 | 21 | ReturnVoid | ir.cpp:658:5:658:5 | +| C::C() -> void | 0 | 22 | UnmodeledUse | ir.cpp:658:5:658:5 | +| C::C() -> void | 0 | 23 | ExitFunction | ir.cpp:658:5:658:5 | +| C::FieldAccess() -> void | 0 | 0 | EnterFunction | ir.cpp:642:10:642:20 | +| C::FieldAccess() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:642:10:642:20 | +| C::FieldAccess() -> void | 0 | 2 | InitializeThis | ir.cpp:642:10:642:20 | +| C::FieldAccess() -> void | 0 | 3 | Constant[0] | ir.cpp:643:21:643:21 | +| C::FieldAccess() -> void | 0 | 4 | CopyValue | ir.cpp:643:9:643:12 | +| C::FieldAccess() -> void | 0 | 5 | FieldAddress[m_a] | ir.cpp:643:15:643:17 | +| C::FieldAccess() -> void | 0 | 6 | Store | ir.cpp:643:9:643:21 | +| C::FieldAccess() -> void | 0 | 7 | Constant[1] | ir.cpp:644:23:644:23 | +| C::FieldAccess() -> void | 0 | 8 | CopyValue | ir.cpp:644:11:644:14 | +| C::FieldAccess() -> void | 0 | 9 | FieldAddress[m_a] | ir.cpp:644:17:644:19 | +| C::FieldAccess() -> void | 0 | 10 | Store | ir.cpp:644:9:644:23 | +| C::FieldAccess() -> void | 0 | 11 | Constant[2] | ir.cpp:645:15:645:15 | +| C::FieldAccess() -> void | 0 | 12 | CopyValue | file://:0:0:0:0 | +| C::FieldAccess() -> void | 0 | 13 | FieldAddress[m_a] | ir.cpp:645:9:645:11 | +| C::FieldAccess() -> void | 0 | 14 | Store | ir.cpp:645:9:645:15 | +| C::FieldAccess() -> void | 0 | 15 | VariableAddress[x] | ir.cpp:646:13:646:13 | +| C::FieldAccess() -> void | 0 | 16 | Uninitialized | ir.cpp:646:13:646:13 | +| C::FieldAccess() -> void | 0 | 17 | Store | ir.cpp:646:13:646:13 | +| C::FieldAccess() -> void | 0 | 18 | CopyValue | ir.cpp:647:13:647:16 | +| C::FieldAccess() -> void | 0 | 19 | FieldAddress[m_a] | ir.cpp:647:19:647:21 | +| C::FieldAccess() -> void | 0 | 20 | Load | ir.cpp:647:19:647:21 | +| C::FieldAccess() -> void | 0 | 21 | VariableAddress[x] | ir.cpp:647:9:647:9 | +| C::FieldAccess() -> void | 0 | 22 | Store | ir.cpp:647:9:647:21 | +| C::FieldAccess() -> void | 0 | 23 | CopyValue | ir.cpp:648:15:648:18 | +| C::FieldAccess() -> void | 0 | 24 | FieldAddress[m_a] | ir.cpp:648:21:648:23 | +| C::FieldAccess() -> void | 0 | 25 | Load | ir.cpp:648:21:648:23 | +| C::FieldAccess() -> void | 0 | 26 | VariableAddress[x] | ir.cpp:648:9:648:9 | +| C::FieldAccess() -> void | 0 | 27 | Store | ir.cpp:648:9:648:23 | +| C::FieldAccess() -> void | 0 | 28 | CopyValue | file://:0:0:0:0 | +| C::FieldAccess() -> void | 0 | 29 | FieldAddress[m_a] | ir.cpp:649:13:649:15 | +| C::FieldAccess() -> void | 0 | 30 | Load | ir.cpp:649:13:649:15 | +| C::FieldAccess() -> void | 0 | 31 | VariableAddress[x] | ir.cpp:649:9:649:9 | +| C::FieldAccess() -> void | 0 | 32 | Store | ir.cpp:649:9:649:15 | +| C::FieldAccess() -> void | 0 | 33 | NoOp | ir.cpp:650:5:650:5 | +| C::FieldAccess() -> void | 0 | 34 | ReturnVoid | ir.cpp:642:10:642:20 | +| C::FieldAccess() -> void | 0 | 35 | UnmodeledUse | ir.cpp:642:10:642:20 | +| C::FieldAccess() -> void | 0 | 36 | ExitFunction | ir.cpp:642:10:642:20 | +| C::InstanceMemberFunction(int) -> int | 0 | 0 | EnterFunction | ir.cpp:634:9:634:30 | +| C::InstanceMemberFunction(int) -> int | 0 | 1 | UnmodeledDefinition | ir.cpp:634:9:634:30 | +| C::InstanceMemberFunction(int) -> int | 0 | 2 | InitializeThis | ir.cpp:634:9:634:30 | +| C::InstanceMemberFunction(int) -> int | 0 | 3 | InitializeParameter[x] | ir.cpp:634:36:634:36 | +| C::InstanceMemberFunction(int) -> int | 0 | 4 | VariableAddress[x] | ir.cpp:634:36:634:36 | +| C::InstanceMemberFunction(int) -> int | 0 | 5 | Store | ir.cpp:634:36:634:36 | +| C::InstanceMemberFunction(int) -> int | 0 | 6 | VariableAddress[#return] | ir.cpp:635:9:635:17 | +| C::InstanceMemberFunction(int) -> int | 0 | 7 | VariableAddress[x] | ir.cpp:635:16:635:16 | +| C::InstanceMemberFunction(int) -> int | 0 | 8 | Load | ir.cpp:635:16:635:16 | +| C::InstanceMemberFunction(int) -> int | 0 | 9 | Store | ir.cpp:635:16:635:16 | +| C::InstanceMemberFunction(int) -> int | 0 | 10 | VariableAddress[#return] | ir.cpp:634:9:634:30 | +| C::InstanceMemberFunction(int) -> int | 0 | 11 | ReturnValue | ir.cpp:634:9:634:30 | +| C::InstanceMemberFunction(int) -> int | 0 | 12 | UnmodeledUse | ir.cpp:634:9:634:30 | +| C::InstanceMemberFunction(int) -> int | 0 | 13 | ExitFunction | ir.cpp:634:9:634:30 | +| C::MethodCalls() -> void | 0 | 0 | EnterFunction | ir.cpp:652:10:652:20 | +| C::MethodCalls() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:652:10:652:20 | +| C::MethodCalls() -> void | 0 | 2 | InitializeThis | ir.cpp:652:10:652:20 | +| C::MethodCalls() -> void | 0 | 3 | CopyValue | ir.cpp:653:9:653:12 | +| C::MethodCalls() -> void | 0 | 4 | FunctionAddress[InstanceMemberFunction] | ir.cpp:653:15:653:36 | +| C::MethodCalls() -> void | 0 | 5 | Constant[0] | ir.cpp:653:38:653:38 | +| C::MethodCalls() -> void | 0 | 6 | Invoke | ir.cpp:653:15:653:36 | +| C::MethodCalls() -> void | 0 | 7 | CopyValue | ir.cpp:654:11:654:14 | +| C::MethodCalls() -> void | 0 | 8 | FunctionAddress[InstanceMemberFunction] | ir.cpp:654:17:654:38 | +| C::MethodCalls() -> void | 0 | 9 | Constant[1] | ir.cpp:654:40:654:40 | +| C::MethodCalls() -> void | 0 | 10 | Invoke | ir.cpp:654:17:654:38 | +| C::MethodCalls() -> void | 0 | 11 | CopyValue | file://:0:0:0:0 | +| C::MethodCalls() -> void | 0 | 12 | FunctionAddress[InstanceMemberFunction] | ir.cpp:655:9:655:30 | +| C::MethodCalls() -> void | 0 | 13 | Constant[2] | ir.cpp:655:32:655:32 | +| C::MethodCalls() -> void | 0 | 14 | Invoke | ir.cpp:655:9:655:30 | +| C::MethodCalls() -> void | 0 | 15 | NoOp | ir.cpp:656:5:656:5 | +| C::MethodCalls() -> void | 0 | 16 | ReturnVoid | ir.cpp:652:10:652:20 | +| C::MethodCalls() -> void | 0 | 17 | UnmodeledUse | ir.cpp:652:10:652:20 | +| C::MethodCalls() -> void | 0 | 18 | ExitFunction | ir.cpp:652:10:652:20 | +| C::StaticMemberFunction(int) -> int | 0 | 0 | EnterFunction | ir.cpp:630:16:630:35 | +| C::StaticMemberFunction(int) -> int | 0 | 1 | UnmodeledDefinition | ir.cpp:630:16:630:35 | +| C::StaticMemberFunction(int) -> int | 0 | 2 | InitializeParameter[x] | ir.cpp:630:41:630:41 | +| C::StaticMemberFunction(int) -> int | 0 | 3 | VariableAddress[x] | ir.cpp:630:41:630:41 | +| C::StaticMemberFunction(int) -> int | 0 | 4 | Store | ir.cpp:630:41:630:41 | +| C::StaticMemberFunction(int) -> int | 0 | 5 | VariableAddress[#return] | ir.cpp:631:9:631:17 | +| C::StaticMemberFunction(int) -> int | 0 | 6 | VariableAddress[x] | ir.cpp:631:16:631:16 | +| C::StaticMemberFunction(int) -> int | 0 | 7 | Load | ir.cpp:631:16:631:16 | +| C::StaticMemberFunction(int) -> int | 0 | 8 | Store | ir.cpp:631:16:631:16 | +| C::StaticMemberFunction(int) -> int | 0 | 9 | VariableAddress[#return] | ir.cpp:630:16:630:35 | +| C::StaticMemberFunction(int) -> int | 0 | 10 | ReturnValue | ir.cpp:630:16:630:35 | +| C::StaticMemberFunction(int) -> int | 0 | 11 | UnmodeledUse | ir.cpp:630:16:630:35 | +| C::StaticMemberFunction(int) -> int | 0 | 12 | ExitFunction | ir.cpp:630:16:630:35 | +| C::VirtualMemberFunction(int) -> int | 0 | 0 | EnterFunction | ir.cpp:638:17:638:37 | +| C::VirtualMemberFunction(int) -> int | 0 | 1 | UnmodeledDefinition | ir.cpp:638:17:638:37 | +| C::VirtualMemberFunction(int) -> int | 0 | 2 | InitializeThis | ir.cpp:638:17:638:37 | +| C::VirtualMemberFunction(int) -> int | 0 | 3 | InitializeParameter[x] | ir.cpp:638:43:638:43 | +| C::VirtualMemberFunction(int) -> int | 0 | 4 | VariableAddress[x] | ir.cpp:638:43:638:43 | +| C::VirtualMemberFunction(int) -> int | 0 | 5 | Store | ir.cpp:638:43:638:43 | +| C::VirtualMemberFunction(int) -> int | 0 | 6 | VariableAddress[#return] | ir.cpp:639:9:639:17 | +| C::VirtualMemberFunction(int) -> int | 0 | 7 | VariableAddress[x] | ir.cpp:639:16:639:16 | +| C::VirtualMemberFunction(int) -> int | 0 | 8 | Load | ir.cpp:639:16:639:16 | +| C::VirtualMemberFunction(int) -> int | 0 | 9 | Store | ir.cpp:639:16:639:16 | +| C::VirtualMemberFunction(int) -> int | 0 | 10 | VariableAddress[#return] | ir.cpp:638:17:638:37 | +| C::VirtualMemberFunction(int) -> int | 0 | 11 | ReturnValue | ir.cpp:638:17:638:37 | +| C::VirtualMemberFunction(int) -> int | 0 | 12 | UnmodeledUse | ir.cpp:638:17:638:37 | +| C::VirtualMemberFunction(int) -> int | 0 | 13 | ExitFunction | ir.cpp:638:17:638:37 | +| Call() -> void | 0 | 0 | EnterFunction | ir.cpp:372:6:372:9 | +| Call() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:372:6:372:9 | +| Call() -> void | 0 | 2 | FunctionAddress[VoidFunc] | ir.cpp:373:5:373:12 | +| Call() -> void | 0 | 3 | Invoke | ir.cpp:373:5:373:12 | +| Call() -> void | 0 | 4 | NoOp | ir.cpp:374:1:374:1 | +| Call() -> void | 0 | 5 | ReturnVoid | ir.cpp:372:6:372:9 | +| Call() -> void | 0 | 6 | UnmodeledUse | ir.cpp:372:6:372:9 | +| Call() -> void | 0 | 7 | ExitFunction | ir.cpp:372:6:372:9 | +| CallAdd(int, int) -> int | 0 | 0 | EnterFunction | ir.cpp:376:5:376:11 | +| CallAdd(int, int) -> int | 0 | 1 | UnmodeledDefinition | ir.cpp:376:5:376:11 | +| CallAdd(int, int) -> int | 0 | 2 | InitializeParameter[x] | ir.cpp:376:17:376:17 | +| CallAdd(int, int) -> int | 0 | 3 | VariableAddress[x] | ir.cpp:376:17:376:17 | +| CallAdd(int, int) -> int | 0 | 4 | Store | ir.cpp:376:17:376:17 | +| CallAdd(int, int) -> int | 0 | 5 | InitializeParameter[y] | ir.cpp:376:24:376:24 | +| CallAdd(int, int) -> int | 0 | 6 | VariableAddress[y] | ir.cpp:376:24:376:24 | +| CallAdd(int, int) -> int | 0 | 7 | Store | ir.cpp:376:24:376:24 | +| CallAdd(int, int) -> int | 0 | 8 | VariableAddress[#return] | ir.cpp:377:5:377:21 | +| CallAdd(int, int) -> int | 0 | 9 | FunctionAddress[Add] | ir.cpp:377:12:377:14 | +| CallAdd(int, int) -> int | 0 | 10 | VariableAddress[x] | ir.cpp:377:16:377:16 | +| CallAdd(int, int) -> int | 0 | 11 | Load | ir.cpp:377:16:377:16 | +| CallAdd(int, int) -> int | 0 | 12 | VariableAddress[y] | ir.cpp:377:19:377:19 | +| CallAdd(int, int) -> int | 0 | 13 | Load | ir.cpp:377:19:377:19 | +| CallAdd(int, int) -> int | 0 | 14 | Invoke | ir.cpp:377:12:377:14 | +| CallAdd(int, int) -> int | 0 | 15 | Store | ir.cpp:377:12:377:14 | +| CallAdd(int, int) -> int | 0 | 16 | VariableAddress[#return] | ir.cpp:376:5:376:11 | +| CallAdd(int, int) -> int | 0 | 17 | ReturnValue | ir.cpp:376:5:376:11 | +| CallAdd(int, int) -> int | 0 | 18 | UnmodeledUse | ir.cpp:376:5:376:11 | +| CallAdd(int, int) -> int | 0 | 19 | ExitFunction | ir.cpp:376:5:376:11 | +| CallMethods(String &, String *, String) -> void | 0 | 0 | EnterFunction | ir.cpp:622:6:622:16 | +| CallMethods(String &, String *, String) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:622:6:622:16 | +| CallMethods(String &, String *, String) -> void | 0 | 2 | InitializeParameter[r] | ir.cpp:622:26:622:26 | +| CallMethods(String &, String *, String) -> void | 0 | 3 | VariableAddress[r] | ir.cpp:622:26:622:26 | +| CallMethods(String &, String *, String) -> void | 0 | 4 | Store | ir.cpp:622:26:622:26 | +| CallMethods(String &, String *, String) -> void | 0 | 5 | InitializeParameter[p] | ir.cpp:622:37:622:37 | +| CallMethods(String &, String *, String) -> void | 0 | 6 | VariableAddress[p] | ir.cpp:622:37:622:37 | +| CallMethods(String &, String *, String) -> void | 0 | 7 | Store | ir.cpp:622:37:622:37 | +| CallMethods(String &, String *, String) -> void | 0 | 8 | InitializeParameter[s] | ir.cpp:622:47:622:47 | +| CallMethods(String &, String *, String) -> void | 0 | 9 | VariableAddress[s] | ir.cpp:622:47:622:47 | +| CallMethods(String &, String *, String) -> void | 0 | 10 | Store | ir.cpp:622:47:622:47 | +| CallMethods(String &, String *, String) -> void | 0 | 11 | VariableAddress[r] | ir.cpp:623:5:623:5 | +| CallMethods(String &, String *, String) -> void | 0 | 12 | Load | ir.cpp:623:5:623:5 | +| CallMethods(String &, String *, String) -> void | 0 | 13 | Convert | ir.cpp:623:5:623:5 | +| CallMethods(String &, String *, String) -> void | 0 | 14 | FunctionAddress[c_str] | ir.cpp:623:7:623:11 | +| CallMethods(String &, String *, String) -> void | 0 | 15 | Invoke | ir.cpp:623:7:623:11 | +| CallMethods(String &, String *, String) -> void | 0 | 16 | VariableAddress[p] | ir.cpp:624:5:624:5 | +| CallMethods(String &, String *, String) -> void | 0 | 17 | Load | ir.cpp:624:5:624:5 | +| CallMethods(String &, String *, String) -> void | 0 | 18 | Convert | ir.cpp:624:5:624:5 | +| CallMethods(String &, String *, String) -> void | 0 | 19 | FunctionAddress[c_str] | ir.cpp:624:8:624:12 | +| CallMethods(String &, String *, String) -> void | 0 | 20 | Invoke | ir.cpp:624:8:624:12 | +| CallMethods(String &, String *, String) -> void | 0 | 21 | VariableAddress[s] | ir.cpp:625:5:625:5 | +| CallMethods(String &, String *, String) -> void | 0 | 22 | Convert | ir.cpp:625:5:625:5 | +| CallMethods(String &, String *, String) -> void | 0 | 23 | FunctionAddress[c_str] | ir.cpp:625:7:625:11 | +| CallMethods(String &, String *, String) -> void | 0 | 24 | Invoke | ir.cpp:625:7:625:11 | +| CallMethods(String &, String *, String) -> void | 0 | 25 | NoOp | ir.cpp:626:1:626:1 | +| CallMethods(String &, String *, String) -> void | 0 | 26 | ReturnVoid | ir.cpp:622:6:622:16 | +| CallMethods(String &, String *, String) -> void | 0 | 27 | UnmodeledUse | ir.cpp:622:6:622:16 | +| CallMethods(String &, String *, String) -> void | 0 | 28 | ExitFunction | ir.cpp:622:6:622:16 | +| CallMin(int, int) -> int | 0 | 0 | EnterFunction | ir.cpp:708:5:708:11 | +| CallMin(int, int) -> int | 0 | 1 | UnmodeledDefinition | ir.cpp:708:5:708:11 | +| CallMin(int, int) -> int | 0 | 2 | InitializeParameter[x] | ir.cpp:708:17:708:17 | +| CallMin(int, int) -> int | 0 | 3 | VariableAddress[x] | ir.cpp:708:17:708:17 | +| CallMin(int, int) -> int | 0 | 4 | Store | ir.cpp:708:17:708:17 | +| CallMin(int, int) -> int | 0 | 5 | InitializeParameter[y] | ir.cpp:708:24:708:24 | +| CallMin(int, int) -> int | 0 | 6 | VariableAddress[y] | ir.cpp:708:24:708:24 | +| CallMin(int, int) -> int | 0 | 7 | Store | ir.cpp:708:24:708:24 | +| CallMin(int, int) -> int | 0 | 8 | VariableAddress[#return] | ir.cpp:709:3:709:19 | +| CallMin(int, int) -> int | 0 | 9 | FunctionAddress[min] | ir.cpp:709:10:709:12 | +| CallMin(int, int) -> int | 0 | 10 | VariableAddress[x] | ir.cpp:709:14:709:14 | +| CallMin(int, int) -> int | 0 | 11 | Load | ir.cpp:709:14:709:14 | +| CallMin(int, int) -> int | 0 | 12 | VariableAddress[y] | ir.cpp:709:17:709:17 | +| CallMin(int, int) -> int | 0 | 13 | Load | ir.cpp:709:17:709:17 | +| CallMin(int, int) -> int | 0 | 14 | Invoke | ir.cpp:709:10:709:12 | +| CallMin(int, int) -> int | 0 | 15 | Store | ir.cpp:709:10:709:12 | +| CallMin(int, int) -> int | 0 | 16 | VariableAddress[#return] | ir.cpp:708:5:708:11 | +| CallMin(int, int) -> int | 0 | 17 | ReturnValue | ir.cpp:708:5:708:11 | +| CallMin(int, int) -> int | 0 | 18 | UnmodeledUse | ir.cpp:708:5:708:11 | +| CallMin(int, int) -> int | 0 | 19 | ExitFunction | ir.cpp:708:5:708:11 | +| CallNestedTemplateFunc() -> double | 0 | 0 | EnterFunction | ir.cpp:720:8:720:29 | +| CallNestedTemplateFunc() -> double | 0 | 1 | UnmodeledDefinition | ir.cpp:720:8:720:29 | +| CallNestedTemplateFunc() -> double | 0 | 2 | VariableAddress[#return] | ir.cpp:721:3:721:54 | +| CallNestedTemplateFunc() -> double | 0 | 3 | FunctionAddress[Func] | ir.cpp:721:10:721:39 | +| CallNestedTemplateFunc() -> double | 0 | 4 | Constant[0] | ir.cpp:721:41:721:47 | +| CallNestedTemplateFunc() -> double | 0 | 5 | Constant[111] | ir.cpp:721:50:721:52 | +| CallNestedTemplateFunc() -> double | 0 | 6 | Invoke | ir.cpp:721:10:721:39 | +| CallNestedTemplateFunc() -> double | 0 | 7 | Convert | ir.cpp:721:10:721:53 | +| CallNestedTemplateFunc() -> double | 0 | 8 | Store | ir.cpp:721:10:721:53 | +| CallNestedTemplateFunc() -> double | 0 | 9 | VariableAddress[#return] | ir.cpp:720:8:720:29 | +| CallNestedTemplateFunc() -> double | 0 | 10 | ReturnValue | ir.cpp:720:8:720:29 | +| CallNestedTemplateFunc() -> double | 0 | 11 | UnmodeledUse | ir.cpp:720:8:720:29 | +| CallNestedTemplateFunc() -> double | 0 | 12 | ExitFunction | ir.cpp:720:8:720:29 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 0 | EnterFunction | ir.cpp:551:5:551:18 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 1 | UnmodeledDefinition | ir.cpp:551:5:551:18 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 2 | InitializeParameter[pfn] | ir.cpp:551:26:551:28 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 3 | VariableAddress[pfn] | ir.cpp:551:26:551:28 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 4 | Store | ir.cpp:551:26:551:28 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 5 | VariableAddress[#return] | ir.cpp:552:5:552:18 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 6 | VariableAddress[pfn] | ir.cpp:552:12:552:14 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 7 | Load | ir.cpp:552:12:552:14 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 8 | Constant[5] | ir.cpp:552:16:552:16 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 9 | Invoke | ir.cpp:552:12:552:17 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 10 | Store | ir.cpp:552:12:552:17 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 11 | VariableAddress[#return] | ir.cpp:551:5:551:18 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 12 | ReturnValue | ir.cpp:551:5:551:18 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 13 | UnmodeledUse | ir.cpp:551:5:551:18 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 14 | ExitFunction | ir.cpp:551:5:551:18 | +| Comma(int, int) -> int | 0 | 0 | EnterFunction | ir.cpp:380:5:380:9 | +| Comma(int, int) -> int | 0 | 1 | UnmodeledDefinition | ir.cpp:380:5:380:9 | +| Comma(int, int) -> int | 0 | 2 | InitializeParameter[x] | ir.cpp:380:15:380:15 | +| Comma(int, int) -> int | 0 | 3 | VariableAddress[x] | ir.cpp:380:15:380:15 | +| Comma(int, int) -> int | 0 | 4 | Store | ir.cpp:380:15:380:15 | +| Comma(int, int) -> int | 0 | 5 | InitializeParameter[y] | ir.cpp:380:22:380:22 | +| Comma(int, int) -> int | 0 | 6 | VariableAddress[y] | ir.cpp:380:22:380:22 | +| Comma(int, int) -> int | 0 | 7 | Store | ir.cpp:380:22:380:22 | +| Comma(int, int) -> int | 0 | 8 | VariableAddress[#return] | ir.cpp:381:5:381:37 | +| Comma(int, int) -> int | 0 | 9 | FunctionAddress[VoidFunc] | ir.cpp:381:12:381:19 | +| Comma(int, int) -> int | 0 | 10 | Invoke | ir.cpp:381:12:381:19 | +| Comma(int, int) -> int | 0 | 11 | FunctionAddress[CallAdd] | ir.cpp:381:24:381:30 | +| Comma(int, int) -> int | 0 | 12 | VariableAddress[x] | ir.cpp:381:32:381:32 | +| Comma(int, int) -> int | 0 | 13 | Load | ir.cpp:381:32:381:32 | +| Comma(int, int) -> int | 0 | 14 | VariableAddress[y] | ir.cpp:381:35:381:35 | +| Comma(int, int) -> int | 0 | 15 | Load | ir.cpp:381:35:381:35 | +| Comma(int, int) -> int | 0 | 16 | Invoke | ir.cpp:381:24:381:30 | +| Comma(int, int) -> int | 0 | 17 | Store | ir.cpp:381:12:381:36 | +| Comma(int, int) -> int | 0 | 18 | VariableAddress[#return] | ir.cpp:380:5:380:9 | +| Comma(int, int) -> int | 0 | 19 | ReturnValue | ir.cpp:380:5:380:9 | +| Comma(int, int) -> int | 0 | 20 | UnmodeledUse | ir.cpp:380:5:380:9 | +| Comma(int, int) -> int | 0 | 21 | ExitFunction | ir.cpp:380:5:380:9 | +| CompoundAssignment() -> void | 0 | 0 | EnterFunction | ir.cpp:213:6:213:23 | +| CompoundAssignment() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:213:6:213:23 | +| CompoundAssignment() -> void | 0 | 2 | VariableAddress[x] | ir.cpp:215:9:215:9 | +| CompoundAssignment() -> void | 0 | 3 | Constant[5] | ir.cpp:215:12:215:13 | +| CompoundAssignment() -> void | 0 | 4 | Store | ir.cpp:215:12:215:13 | +| CompoundAssignment() -> void | 0 | 5 | Constant[7] | ir.cpp:216:10:216:10 | +| CompoundAssignment() -> void | 0 | 6 | VariableAddress[x] | ir.cpp:216:5:216:5 | +| CompoundAssignment() -> void | 0 | 7 | Load | ir.cpp:216:5:216:10 | +| CompoundAssignment() -> void | 0 | 8 | Add | ir.cpp:216:5:216:10 | +| CompoundAssignment() -> void | 0 | 9 | Store | ir.cpp:216:5:216:10 | +| CompoundAssignment() -> void | 0 | 10 | VariableAddress[y] | ir.cpp:219:11:219:11 | +| CompoundAssignment() -> void | 0 | 11 | Constant[5] | ir.cpp:219:15:219:15 | +| CompoundAssignment() -> void | 0 | 12 | Store | ir.cpp:219:15:219:15 | +| CompoundAssignment() -> void | 0 | 13 | VariableAddress[x] | ir.cpp:220:10:220:10 | +| CompoundAssignment() -> void | 0 | 14 | Load | ir.cpp:220:10:220:10 | +| CompoundAssignment() -> void | 0 | 15 | VariableAddress[y] | ir.cpp:220:5:220:5 | +| CompoundAssignment() -> void | 0 | 16 | Load | ir.cpp:220:5:220:10 | +| CompoundAssignment() -> void | 0 | 17 | Convert | ir.cpp:220:5:220:10 | +| CompoundAssignment() -> void | 0 | 18 | Add | ir.cpp:220:5:220:10 | +| CompoundAssignment() -> void | 0 | 19 | Convert | ir.cpp:220:5:220:10 | +| CompoundAssignment() -> void | 0 | 20 | Store | ir.cpp:220:5:220:10 | +| CompoundAssignment() -> void | 0 | 21 | Constant[1] | ir.cpp:223:11:223:11 | +| CompoundAssignment() -> void | 0 | 22 | VariableAddress[y] | ir.cpp:223:5:223:5 | +| CompoundAssignment() -> void | 0 | 23 | Load | ir.cpp:223:5:223:11 | +| CompoundAssignment() -> void | 0 | 24 | ShiftLeft | ir.cpp:223:5:223:11 | +| CompoundAssignment() -> void | 0 | 25 | Store | ir.cpp:223:5:223:11 | +| CompoundAssignment() -> void | 0 | 26 | VariableAddress[z] | ir.cpp:226:10:226:10 | +| CompoundAssignment() -> void | 0 | 27 | Constant[7] | ir.cpp:226:14:226:14 | +| CompoundAssignment() -> void | 0 | 28 | Store | ir.cpp:226:14:226:14 | +| CompoundAssignment() -> void | 0 | 29 | Constant[2.0] | ir.cpp:227:10:227:13 | +| CompoundAssignment() -> void | 0 | 30 | VariableAddress[z] | ir.cpp:227:5:227:5 | +| CompoundAssignment() -> void | 0 | 31 | Load | ir.cpp:227:5:227:13 | +| CompoundAssignment() -> void | 0 | 32 | Convert | ir.cpp:227:5:227:13 | +| CompoundAssignment() -> void | 0 | 33 | Add | ir.cpp:227:5:227:13 | +| CompoundAssignment() -> void | 0 | 34 | Convert | ir.cpp:227:5:227:13 | +| CompoundAssignment() -> void | 0 | 35 | Store | ir.cpp:227:5:227:13 | +| CompoundAssignment() -> void | 0 | 36 | NoOp | ir.cpp:228:1:228:1 | +| CompoundAssignment() -> void | 0 | 37 | ReturnVoid | ir.cpp:213:6:213:23 | +| CompoundAssignment() -> void | 0 | 38 | UnmodeledUse | ir.cpp:213:6:213:23 | +| CompoundAssignment() -> void | 0 | 39 | ExitFunction | ir.cpp:213:6:213:23 | +| ConditionValues(bool, bool) -> void | 0 | 0 | EnterFunction | ir.cpp:475:6:475:20 | +| ConditionValues(bool, bool) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:475:6:475:20 | +| ConditionValues(bool, bool) -> void | 0 | 2 | InitializeParameter[a] | ir.cpp:475:27:475:27 | +| ConditionValues(bool, bool) -> void | 0 | 3 | VariableAddress[a] | ir.cpp:475:27:475:27 | +| ConditionValues(bool, bool) -> void | 0 | 4 | Store | ir.cpp:475:27:475:27 | +| ConditionValues(bool, bool) -> void | 0 | 5 | InitializeParameter[b] | ir.cpp:475:35:475:35 | +| ConditionValues(bool, bool) -> void | 0 | 6 | VariableAddress[b] | ir.cpp:475:35:475:35 | +| ConditionValues(bool, bool) -> void | 0 | 7 | Store | ir.cpp:475:35:475:35 | +| ConditionValues(bool, bool) -> void | 0 | 8 | VariableAddress[x] | ir.cpp:476:10:476:10 | +| ConditionValues(bool, bool) -> void | 0 | 9 | Uninitialized | ir.cpp:476:10:476:10 | +| ConditionValues(bool, bool) -> void | 0 | 10 | Store | ir.cpp:476:10:476:10 | +| ConditionValues(bool, bool) -> void | 0 | 11 | VariableAddress[a] | ir.cpp:477:9:477:9 | +| ConditionValues(bool, bool) -> void | 0 | 12 | Load | ir.cpp:477:9:477:9 | +| ConditionValues(bool, bool) -> void | 0 | 13 | ConditionalBranch | ir.cpp:477:9:477:9 | +| ConditionValues(bool, bool) -> void | 1 | 0 | VariableAddress[b] | ir.cpp:477:14:477:14 | +| ConditionValues(bool, bool) -> void | 1 | 1 | Load | ir.cpp:477:14:477:14 | +| ConditionValues(bool, bool) -> void | 1 | 2 | ConditionalBranch | ir.cpp:477:14:477:14 | +| ConditionValues(bool, bool) -> void | 2 | 0 | VariableAddress[#temp478:9] | ir.cpp:478:9:478:14 | +| ConditionValues(bool, bool) -> void | 2 | 1 | Constant[0] | ir.cpp:478:9:478:14 | +| ConditionValues(bool, bool) -> void | 2 | 2 | Store | ir.cpp:478:9:478:14 | +| ConditionValues(bool, bool) -> void | 3 | 0 | VariableAddress[#temp478:9] | ir.cpp:478:9:478:14 | +| ConditionValues(bool, bool) -> void | 3 | 1 | Load | ir.cpp:478:9:478:14 | +| ConditionValues(bool, bool) -> void | 3 | 2 | VariableAddress[x] | ir.cpp:478:5:478:5 | +| ConditionValues(bool, bool) -> void | 3 | 3 | Store | ir.cpp:478:5:478:14 | +| ConditionValues(bool, bool) -> void | 3 | 4 | VariableAddress[a] | ir.cpp:479:11:479:11 | +| ConditionValues(bool, bool) -> void | 3 | 5 | Load | ir.cpp:479:11:479:11 | +| ConditionValues(bool, bool) -> void | 3 | 6 | ConditionalBranch | ir.cpp:479:11:479:11 | +| ConditionValues(bool, bool) -> void | 4 | 0 | VariableAddress[#temp478:9] | ir.cpp:478:9:478:14 | +| ConditionValues(bool, bool) -> void | 4 | 1 | Constant[1] | ir.cpp:478:9:478:14 | +| ConditionValues(bool, bool) -> void | 4 | 2 | Store | ir.cpp:478:9:478:14 | +| ConditionValues(bool, bool) -> void | 5 | 0 | VariableAddress[b] | ir.cpp:478:14:478:14 | +| ConditionValues(bool, bool) -> void | 5 | 1 | Load | ir.cpp:478:14:478:14 | +| ConditionValues(bool, bool) -> void | 5 | 2 | ConditionalBranch | ir.cpp:478:14:478:14 | +| ConditionValues(bool, bool) -> void | 6 | 0 | VariableAddress[#temp479:11] | ir.cpp:479:11:479:16 | +| ConditionValues(bool, bool) -> void | 6 | 1 | Constant[0] | ir.cpp:479:11:479:16 | +| ConditionValues(bool, bool) -> void | 6 | 2 | Store | ir.cpp:479:11:479:16 | +| ConditionValues(bool, bool) -> void | 7 | 0 | VariableAddress[#temp479:11] | ir.cpp:479:11:479:16 | +| ConditionValues(bool, bool) -> void | 7 | 1 | Load | ir.cpp:479:11:479:16 | +| ConditionValues(bool, bool) -> void | 7 | 2 | LogicalNot | ir.cpp:479:9:479:17 | +| ConditionValues(bool, bool) -> void | 7 | 3 | VariableAddress[x] | ir.cpp:479:5:479:5 | +| ConditionValues(bool, bool) -> void | 7 | 4 | Store | ir.cpp:479:5:479:17 | +| ConditionValues(bool, bool) -> void | 7 | 5 | NoOp | ir.cpp:480:1:480:1 | +| ConditionValues(bool, bool) -> void | 7 | 6 | ReturnVoid | ir.cpp:475:6:475:20 | +| ConditionValues(bool, bool) -> void | 7 | 7 | UnmodeledUse | ir.cpp:475:6:475:20 | +| ConditionValues(bool, bool) -> void | 7 | 8 | ExitFunction | ir.cpp:475:6:475:20 | +| ConditionValues(bool, bool) -> void | 8 | 0 | VariableAddress[#temp479:11] | ir.cpp:479:11:479:16 | +| ConditionValues(bool, bool) -> void | 8 | 1 | Constant[1] | ir.cpp:479:11:479:16 | +| ConditionValues(bool, bool) -> void | 8 | 2 | Store | ir.cpp:479:11:479:16 | +| ConditionValues(bool, bool) -> void | 9 | 0 | VariableAddress[b] | ir.cpp:479:16:479:16 | +| ConditionValues(bool, bool) -> void | 9 | 1 | Load | ir.cpp:479:16:479:16 | +| ConditionValues(bool, bool) -> void | 9 | 2 | ConditionalBranch | ir.cpp:479:16:479:16 | +| ConditionValues(bool, bool) -> void | 10 | 0 | VariableAddress[#temp477:9] | ir.cpp:477:9:477:14 | +| ConditionValues(bool, bool) -> void | 10 | 1 | Constant[0] | ir.cpp:477:9:477:14 | +| ConditionValues(bool, bool) -> void | 10 | 2 | Store | ir.cpp:477:9:477:14 | +| ConditionValues(bool, bool) -> void | 11 | 0 | VariableAddress[#temp477:9] | ir.cpp:477:9:477:14 | +| ConditionValues(bool, bool) -> void | 11 | 1 | Load | ir.cpp:477:9:477:14 | +| ConditionValues(bool, bool) -> void | 11 | 2 | VariableAddress[x] | ir.cpp:477:5:477:5 | +| ConditionValues(bool, bool) -> void | 11 | 3 | Store | ir.cpp:477:5:477:14 | +| ConditionValues(bool, bool) -> void | 11 | 4 | VariableAddress[a] | ir.cpp:478:9:478:9 | +| ConditionValues(bool, bool) -> void | 11 | 5 | Load | ir.cpp:478:9:478:9 | +| ConditionValues(bool, bool) -> void | 11 | 6 | ConditionalBranch | ir.cpp:478:9:478:9 | +| ConditionValues(bool, bool) -> void | 12 | 0 | VariableAddress[#temp477:9] | ir.cpp:477:9:477:14 | +| ConditionValues(bool, bool) -> void | 12 | 1 | Constant[1] | ir.cpp:477:9:477:14 | +| ConditionValues(bool, bool) -> void | 12 | 2 | Store | ir.cpp:477:9:477:14 | +| Conditional(bool, int, int) -> void | 0 | 0 | EnterFunction | ir.cpp:482:6:482:16 | +| Conditional(bool, int, int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:482:6:482:16 | +| Conditional(bool, int, int) -> void | 0 | 2 | InitializeParameter[a] | ir.cpp:482:23:482:23 | +| Conditional(bool, int, int) -> void | 0 | 3 | VariableAddress[a] | ir.cpp:482:23:482:23 | +| Conditional(bool, int, int) -> void | 0 | 4 | Store | ir.cpp:482:23:482:23 | +| Conditional(bool, int, int) -> void | 0 | 5 | InitializeParameter[x] | ir.cpp:482:30:482:30 | +| Conditional(bool, int, int) -> void | 0 | 6 | VariableAddress[x] | ir.cpp:482:30:482:30 | +| Conditional(bool, int, int) -> void | 0 | 7 | Store | ir.cpp:482:30:482:30 | +| Conditional(bool, int, int) -> void | 0 | 8 | InitializeParameter[y] | ir.cpp:482:37:482:37 | +| Conditional(bool, int, int) -> void | 0 | 9 | VariableAddress[y] | ir.cpp:482:37:482:37 | +| Conditional(bool, int, int) -> void | 0 | 10 | Store | ir.cpp:482:37:482:37 | +| Conditional(bool, int, int) -> void | 0 | 11 | VariableAddress[z] | ir.cpp:483:9:483:9 | +| Conditional(bool, int, int) -> void | 0 | 12 | VariableAddress[a] | ir.cpp:483:13:483:13 | +| Conditional(bool, int, int) -> void | 0 | 13 | Load | ir.cpp:483:13:483:13 | +| Conditional(bool, int, int) -> void | 0 | 14 | ConditionalBranch | ir.cpp:483:13:483:13 | +| Conditional(bool, int, int) -> void | 1 | 0 | VariableAddress[x] | ir.cpp:483:17:483:17 | +| Conditional(bool, int, int) -> void | 1 | 1 | Load | ir.cpp:483:17:483:17 | +| Conditional(bool, int, int) -> void | 1 | 2 | VariableAddress[#temp483:13] | ir.cpp:483:13:483:21 | +| Conditional(bool, int, int) -> void | 1 | 3 | Store | ir.cpp:483:13:483:21 | +| Conditional(bool, int, int) -> void | 2 | 0 | VariableAddress[y] | ir.cpp:483:21:483:21 | +| Conditional(bool, int, int) -> void | 2 | 1 | Load | ir.cpp:483:21:483:21 | +| Conditional(bool, int, int) -> void | 2 | 2 | VariableAddress[#temp483:13] | ir.cpp:483:13:483:21 | +| Conditional(bool, int, int) -> void | 2 | 3 | Store | ir.cpp:483:13:483:21 | +| Conditional(bool, int, int) -> void | 3 | 0 | VariableAddress[#temp483:13] | ir.cpp:483:13:483:21 | +| Conditional(bool, int, int) -> void | 3 | 1 | Load | ir.cpp:483:13:483:21 | +| Conditional(bool, int, int) -> void | 3 | 2 | Store | ir.cpp:483:13:483:21 | +| Conditional(bool, int, int) -> void | 3 | 3 | NoOp | ir.cpp:484:1:484:1 | +| Conditional(bool, int, int) -> void | 3 | 4 | ReturnVoid | ir.cpp:482:6:482:16 | +| Conditional(bool, int, int) -> void | 3 | 5 | UnmodeledUse | ir.cpp:482:6:482:16 | +| Conditional(bool, int, int) -> void | 3 | 6 | ExitFunction | ir.cpp:482:6:482:16 | +| Conditional_LValue(bool) -> void | 0 | 0 | EnterFunction | ir.cpp:486:6:486:23 | +| Conditional_LValue(bool) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:486:6:486:23 | +| Conditional_LValue(bool) -> void | 0 | 2 | InitializeParameter[a] | ir.cpp:486:30:486:30 | +| Conditional_LValue(bool) -> void | 0 | 3 | VariableAddress[a] | ir.cpp:486:30:486:30 | +| Conditional_LValue(bool) -> void | 0 | 4 | Store | ir.cpp:486:30:486:30 | +| Conditional_LValue(bool) -> void | 0 | 5 | VariableAddress[x] | ir.cpp:487:9:487:9 | +| Conditional_LValue(bool) -> void | 0 | 6 | Uninitialized | ir.cpp:487:9:487:9 | +| Conditional_LValue(bool) -> void | 0 | 7 | Store | ir.cpp:487:9:487:9 | +| Conditional_LValue(bool) -> void | 0 | 8 | VariableAddress[y] | ir.cpp:488:9:488:9 | +| Conditional_LValue(bool) -> void | 0 | 9 | Uninitialized | ir.cpp:488:9:488:9 | +| Conditional_LValue(bool) -> void | 0 | 10 | Store | ir.cpp:488:9:488:9 | +| Conditional_LValue(bool) -> void | 0 | 11 | Constant[5] | ir.cpp:489:19:489:19 | +| Conditional_LValue(bool) -> void | 0 | 12 | VariableAddress[a] | ir.cpp:489:6:489:6 | +| Conditional_LValue(bool) -> void | 0 | 13 | Load | ir.cpp:489:6:489:6 | +| Conditional_LValue(bool) -> void | 0 | 14 | ConditionalBranch | ir.cpp:489:6:489:6 | +| Conditional_LValue(bool) -> void | 1 | 0 | VariableAddress[#temp489:6] | ir.cpp:489:6:489:14 | +| Conditional_LValue(bool) -> void | 1 | 1 | Load | ir.cpp:489:6:489:14 | +| Conditional_LValue(bool) -> void | 1 | 2 | Store | ir.cpp:489:5:489:19 | +| Conditional_LValue(bool) -> void | 1 | 3 | NoOp | ir.cpp:490:1:490:1 | +| Conditional_LValue(bool) -> void | 1 | 4 | ReturnVoid | ir.cpp:486:6:486:23 | +| Conditional_LValue(bool) -> void | 1 | 5 | UnmodeledUse | ir.cpp:486:6:486:23 | +| Conditional_LValue(bool) -> void | 1 | 6 | ExitFunction | ir.cpp:486:6:486:23 | +| Conditional_LValue(bool) -> void | 2 | 0 | VariableAddress[x] | ir.cpp:489:10:489:10 | +| Conditional_LValue(bool) -> void | 2 | 1 | VariableAddress[#temp489:6] | ir.cpp:489:6:489:14 | +| Conditional_LValue(bool) -> void | 2 | 2 | Store | ir.cpp:489:6:489:14 | +| Conditional_LValue(bool) -> void | 3 | 0 | VariableAddress[y] | ir.cpp:489:14:489:14 | +| Conditional_LValue(bool) -> void | 3 | 1 | VariableAddress[#temp489:6] | ir.cpp:489:6:489:14 | +| Conditional_LValue(bool) -> void | 3 | 2 | Store | ir.cpp:489:6:489:14 | +| Conditional_Void(bool) -> void | 0 | 0 | EnterFunction | ir.cpp:492:6:492:21 | +| Conditional_Void(bool) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:492:6:492:21 | +| Conditional_Void(bool) -> void | 0 | 2 | InitializeParameter[a] | ir.cpp:492:28:492:28 | +| Conditional_Void(bool) -> void | 0 | 3 | VariableAddress[a] | ir.cpp:492:28:492:28 | +| Conditional_Void(bool) -> void | 0 | 4 | Store | ir.cpp:492:28:492:28 | +| Conditional_Void(bool) -> void | 0 | 5 | VariableAddress[a] | ir.cpp:493:5:493:5 | +| Conditional_Void(bool) -> void | 0 | 6 | Load | ir.cpp:493:5:493:5 | +| Conditional_Void(bool) -> void | 0 | 7 | ConditionalBranch | ir.cpp:493:5:493:5 | +| Conditional_Void(bool) -> void | 1 | 0 | NoOp | ir.cpp:494:1:494:1 | +| Conditional_Void(bool) -> void | 1 | 1 | ReturnVoid | ir.cpp:492:6:492:21 | +| Conditional_Void(bool) -> void | 1 | 2 | UnmodeledUse | ir.cpp:492:6:492:21 | +| Conditional_Void(bool) -> void | 1 | 3 | ExitFunction | ir.cpp:492:6:492:21 | +| Conditional_Void(bool) -> void | 2 | 0 | FunctionAddress[VoidFunc] | ir.cpp:493:9:493:16 | +| Conditional_Void(bool) -> void | 2 | 1 | Invoke | ir.cpp:493:9:493:16 | +| Conditional_Void(bool) -> void | 3 | 0 | FunctionAddress[VoidFunc] | ir.cpp:493:22:493:29 | +| Conditional_Void(bool) -> void | 3 | 1 | Invoke | ir.cpp:493:22:493:29 | +| Constants() -> void | 0 | 0 | EnterFunction | ir.cpp:1:6:1:14 | +| Constants() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:1:6:1:14 | +| Constants() -> void | 0 | 2 | VariableAddress[c_i] | ir.cpp:2:10:2:12 | +| Constants() -> void | 0 | 3 | Constant[1] | ir.cpp:2:16:2:16 | +| Constants() -> void | 0 | 4 | Store | ir.cpp:2:16:2:16 | +| Constants() -> void | 0 | 5 | VariableAddress[c_c] | ir.cpp:3:10:3:12 | +| Constants() -> void | 0 | 6 | Constant[65] | ir.cpp:3:15:3:18 | +| Constants() -> void | 0 | 7 | Store | ir.cpp:3:15:3:18 | +| Constants() -> void | 0 | 8 | VariableAddress[sc_i] | ir.cpp:5:17:5:20 | +| Constants() -> void | 0 | 9 | Constant[-1] | ir.cpp:5:24:5:25 | +| Constants() -> void | 0 | 10 | Store | ir.cpp:5:24:5:25 | +| Constants() -> void | 0 | 11 | VariableAddress[sc_c] | ir.cpp:6:17:6:20 | +| Constants() -> void | 0 | 12 | Constant[65] | ir.cpp:6:24:6:26 | +| Constants() -> void | 0 | 13 | Store | ir.cpp:6:24:6:26 | +| Constants() -> void | 0 | 14 | VariableAddress[uc_i] | ir.cpp:8:19:8:22 | +| Constants() -> void | 0 | 15 | Constant[5] | ir.cpp:8:26:8:26 | +| Constants() -> void | 0 | 16 | Store | ir.cpp:8:26:8:26 | +| Constants() -> void | 0 | 17 | VariableAddress[uc_c] | ir.cpp:9:19:9:22 | +| Constants() -> void | 0 | 18 | Constant[65] | ir.cpp:9:26:9:28 | +| Constants() -> void | 0 | 19 | Store | ir.cpp:9:26:9:28 | +| Constants() -> void | 0 | 20 | VariableAddress[s] | ir.cpp:11:11:11:11 | +| Constants() -> void | 0 | 21 | Constant[5] | ir.cpp:11:15:11:15 | +| Constants() -> void | 0 | 22 | Store | ir.cpp:11:15:11:15 | +| Constants() -> void | 0 | 23 | VariableAddress[us] | ir.cpp:12:20:12:21 | +| Constants() -> void | 0 | 24 | Constant[5] | ir.cpp:12:25:12:25 | +| Constants() -> void | 0 | 25 | Store | ir.cpp:12:25:12:25 | +| Constants() -> void | 0 | 26 | VariableAddress[i] | ir.cpp:14:9:14:9 | +| Constants() -> void | 0 | 27 | Constant[5] | ir.cpp:14:12:14:13 | +| Constants() -> void | 0 | 28 | Store | ir.cpp:14:12:14:13 | +| Constants() -> void | 0 | 29 | VariableAddress[ui] | ir.cpp:15:18:15:19 | +| Constants() -> void | 0 | 30 | Constant[5] | ir.cpp:15:23:15:23 | +| Constants() -> void | 0 | 31 | Store | ir.cpp:15:23:15:23 | +| Constants() -> void | 0 | 32 | VariableAddress[l] | ir.cpp:17:10:17:10 | +| Constants() -> void | 0 | 33 | Constant[5] | ir.cpp:17:14:17:14 | +| Constants() -> void | 0 | 34 | Store | ir.cpp:17:14:17:14 | +| Constants() -> void | 0 | 35 | VariableAddress[ul] | ir.cpp:18:19:18:20 | +| Constants() -> void | 0 | 36 | Constant[5] | ir.cpp:18:24:18:24 | +| Constants() -> void | 0 | 37 | Store | ir.cpp:18:24:18:24 | +| Constants() -> void | 0 | 38 | VariableAddress[ll_i] | ir.cpp:20:15:20:18 | +| Constants() -> void | 0 | 39 | Constant[5] | ir.cpp:20:22:20:22 | +| Constants() -> void | 0 | 40 | Store | ir.cpp:20:22:20:22 | +| Constants() -> void | 0 | 41 | VariableAddress[ll_ll] | ir.cpp:21:15:21:19 | +| Constants() -> void | 0 | 42 | Constant[5] | ir.cpp:21:22:21:25 | +| Constants() -> void | 0 | 43 | Store | ir.cpp:21:22:21:25 | +| Constants() -> void | 0 | 44 | VariableAddress[ull_i] | ir.cpp:22:24:22:28 | +| Constants() -> void | 0 | 45 | Constant[5] | ir.cpp:22:32:22:32 | +| Constants() -> void | 0 | 46 | Store | ir.cpp:22:32:22:32 | +| Constants() -> void | 0 | 47 | VariableAddress[ull_ull] | ir.cpp:23:24:23:30 | +| Constants() -> void | 0 | 48 | Constant[5] | ir.cpp:23:33:23:37 | +| Constants() -> void | 0 | 49 | Store | ir.cpp:23:33:23:37 | +| Constants() -> void | 0 | 50 | VariableAddress[b_t] | ir.cpp:25:10:25:12 | +| Constants() -> void | 0 | 51 | Constant[1] | ir.cpp:25:15:25:19 | +| Constants() -> void | 0 | 52 | Store | ir.cpp:25:15:25:19 | +| Constants() -> void | 0 | 53 | VariableAddress[b_f] | ir.cpp:26:10:26:12 | +| Constants() -> void | 0 | 54 | Constant[0] | ir.cpp:26:15:26:20 | +| Constants() -> void | 0 | 55 | Store | ir.cpp:26:15:26:20 | +| Constants() -> void | 0 | 56 | VariableAddress[wc_i] | ir.cpp:28:13:28:16 | +| Constants() -> void | 0 | 57 | Constant[5] | ir.cpp:28:20:28:20 | +| Constants() -> void | 0 | 58 | Store | ir.cpp:28:20:28:20 | +| Constants() -> void | 0 | 59 | VariableAddress[wc_c] | ir.cpp:29:13:29:16 | +| Constants() -> void | 0 | 60 | Constant[65] | ir.cpp:29:19:29:23 | +| Constants() -> void | 0 | 61 | Store | ir.cpp:29:19:29:23 | +| Constants() -> void | 0 | 62 | VariableAddress[c16] | ir.cpp:31:14:31:16 | +| Constants() -> void | 0 | 63 | Constant[65] | ir.cpp:31:19:31:23 | +| Constants() -> void | 0 | 64 | Store | ir.cpp:31:19:31:23 | +| Constants() -> void | 0 | 65 | VariableAddress[c32] | ir.cpp:32:14:32:16 | +| Constants() -> void | 0 | 66 | Constant[65] | ir.cpp:32:19:32:23 | +| Constants() -> void | 0 | 67 | Store | ir.cpp:32:19:32:23 | +| Constants() -> void | 0 | 68 | VariableAddress[f_i] | ir.cpp:34:11:34:13 | +| Constants() -> void | 0 | 69 | Constant[1.0] | ir.cpp:34:17:34:17 | +| Constants() -> void | 0 | 70 | Store | ir.cpp:34:17:34:17 | +| Constants() -> void | 0 | 71 | VariableAddress[f_f] | ir.cpp:35:11:35:13 | +| Constants() -> void | 0 | 72 | Constant[1.0] | ir.cpp:35:16:35:20 | +| Constants() -> void | 0 | 73 | Store | ir.cpp:35:16:35:20 | +| Constants() -> void | 0 | 74 | VariableAddress[f_d] | ir.cpp:36:11:36:13 | +| Constants() -> void | 0 | 75 | Constant[1.0] | ir.cpp:36:17:36:19 | +| Constants() -> void | 0 | 76 | Store | ir.cpp:36:17:36:19 | +| Constants() -> void | 0 | 77 | VariableAddress[d_i] | ir.cpp:38:12:38:14 | +| Constants() -> void | 0 | 78 | Constant[1.0] | ir.cpp:38:18:38:18 | +| Constants() -> void | 0 | 79 | Store | ir.cpp:38:18:38:18 | +| Constants() -> void | 0 | 80 | VariableAddress[d_f] | ir.cpp:39:12:39:14 | +| Constants() -> void | 0 | 81 | Constant[1.0] | ir.cpp:39:18:39:21 | +| Constants() -> void | 0 | 82 | Store | ir.cpp:39:18:39:21 | +| Constants() -> void | 0 | 83 | VariableAddress[d_d] | ir.cpp:40:12:40:14 | +| Constants() -> void | 0 | 84 | Constant[1.0] | ir.cpp:40:17:40:20 | +| Constants() -> void | 0 | 85 | Store | ir.cpp:40:17:40:20 | +| Constants() -> void | 0 | 86 | NoOp | ir.cpp:41:1:41:1 | +| Constants() -> void | 0 | 87 | ReturnVoid | ir.cpp:1:6:1:14 | +| Constants() -> void | 0 | 88 | UnmodeledUse | ir.cpp:1:6:1:14 | +| Constants() -> void | 0 | 89 | ExitFunction | ir.cpp:1:6:1:14 | +| Continue(int) -> void | 0 | 0 | EnterFunction | ir.cpp:360:6:360:13 | +| Continue(int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:360:6:360:13 | +| Continue(int) -> void | 0 | 2 | InitializeParameter[n] | ir.cpp:360:19:360:19 | +| Continue(int) -> void | 0 | 3 | VariableAddress[n] | ir.cpp:360:19:360:19 | +| Continue(int) -> void | 0 | 4 | Store | ir.cpp:360:19:360:19 | +| Continue(int) -> void | 1 | 0 | VariableAddress[n] | ir.cpp:362:13:362:13 | +| Continue(int) -> void | 1 | 1 | Load | ir.cpp:362:13:362:13 | +| Continue(int) -> void | 1 | 2 | Constant[1] | ir.cpp:362:18:362:18 | +| Continue(int) -> void | 1 | 3 | CompareEQ | ir.cpp:362:13:362:18 | +| Continue(int) -> void | 1 | 4 | ConditionalBranch | ir.cpp:362:13:362:18 | +| Continue(int) -> void | 2 | 0 | NoOp | ir.cpp:363:13:363:21 | +| Continue(int) -> void | 3 | 0 | Constant[1] | ir.cpp:365:14:365:14 | +| Continue(int) -> void | 3 | 1 | VariableAddress[n] | ir.cpp:365:9:365:9 | +| Continue(int) -> void | 3 | 2 | Load | ir.cpp:365:9:365:14 | +| Continue(int) -> void | 3 | 3 | Sub | ir.cpp:365:9:365:14 | +| Continue(int) -> void | 3 | 4 | Store | ir.cpp:365:9:365:14 | +| Continue(int) -> void | 4 | 0 | NoOp | ir.cpp:361:5:361:5 | +| Continue(int) -> void | 4 | 1 | VariableAddress[n] | ir.cpp:366:14:366:14 | +| Continue(int) -> void | 4 | 2 | Load | ir.cpp:366:14:366:14 | +| Continue(int) -> void | 4 | 3 | Constant[0] | ir.cpp:366:18:366:18 | +| Continue(int) -> void | 4 | 4 | CompareGT | ir.cpp:366:14:366:18 | +| Continue(int) -> void | 4 | 5 | ConditionalBranch | ir.cpp:366:14:366:18 | +| Continue(int) -> void | 5 | 0 | NoOp | ir.cpp:367:1:367:1 | +| Continue(int) -> void | 5 | 1 | ReturnVoid | ir.cpp:360:6:360:13 | +| Continue(int) -> void | 5 | 2 | UnmodeledUse | ir.cpp:360:6:360:13 | +| Continue(int) -> void | 5 | 3 | ExitFunction | ir.cpp:360:6:360:13 | +| DeclareObject() -> void | 0 | 0 | EnterFunction | ir.cpp:615:6:615:18 | +| DeclareObject() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:615:6:615:18 | +| DeclareObject() -> void | 0 | 2 | VariableAddress[s1] | ir.cpp:616:12:616:13 | +| DeclareObject() -> void | 0 | 3 | FunctionAddress[String] | ir.cpp:616:12:616:13 | +| DeclareObject() -> void | 0 | 4 | Invoke | ir.cpp:616:12:616:13 | +| DeclareObject() -> void | 0 | 5 | VariableAddress[s2] | ir.cpp:617:12:617:13 | +| DeclareObject() -> void | 0 | 6 | FunctionAddress[String] | ir.cpp:617:15:617:22 | +| DeclareObject() -> void | 0 | 7 | StringConstant["hello"] | ir.cpp:617:15:617:21 | +| DeclareObject() -> void | 0 | 8 | Convert | ir.cpp:617:15:617:21 | +| DeclareObject() -> void | 0 | 9 | Invoke | ir.cpp:617:15:617:22 | +| DeclareObject() -> void | 0 | 10 | VariableAddress[s3] | ir.cpp:618:12:618:13 | +| DeclareObject() -> void | 0 | 11 | FunctionAddress[ReturnObject] | ir.cpp:618:17:618:28 | +| DeclareObject() -> void | 0 | 12 | Invoke | ir.cpp:618:17:618:28 | +| DeclareObject() -> void | 0 | 13 | Store | ir.cpp:618:17:618:28 | +| DeclareObject() -> void | 0 | 14 | VariableAddress[s4] | ir.cpp:619:12:619:13 | +| DeclareObject() -> void | 0 | 15 | FunctionAddress[String] | ir.cpp:619:16:619:30 | +| DeclareObject() -> void | 0 | 16 | StringConstant["test"] | ir.cpp:619:24:619:29 | +| DeclareObject() -> void | 0 | 17 | Convert | ir.cpp:619:24:619:29 | +| DeclareObject() -> void | 0 | 18 | Invoke | ir.cpp:619:16:619:30 | +| DeclareObject() -> void | 0 | 19 | NoOp | ir.cpp:620:1:620:1 | +| DeclareObject() -> void | 0 | 20 | ReturnVoid | ir.cpp:615:6:615:18 | +| DeclareObject() -> void | 0 | 21 | UnmodeledUse | ir.cpp:615:6:615:18 | +| DeclareObject() -> void | 0 | 22 | ExitFunction | ir.cpp:615:6:615:18 | +| DerefReference(int &) -> int | 0 | 0 | EnterFunction | ir.cpp:675:5:675:18 | +| DerefReference(int &) -> int | 0 | 1 | UnmodeledDefinition | ir.cpp:675:5:675:18 | +| DerefReference(int &) -> int | 0 | 2 | InitializeParameter[r] | ir.cpp:675:25:675:25 | +| DerefReference(int &) -> int | 0 | 3 | VariableAddress[r] | ir.cpp:675:25:675:25 | +| DerefReference(int &) -> int | 0 | 4 | Store | ir.cpp:675:25:675:25 | +| DerefReference(int &) -> int | 0 | 5 | VariableAddress[#return] | ir.cpp:676:5:676:13 | +| DerefReference(int &) -> int | 0 | 6 | VariableAddress[r] | ir.cpp:676:12:676:12 | +| DerefReference(int &) -> int | 0 | 7 | Load | ir.cpp:676:12:676:12 | +| DerefReference(int &) -> int | 0 | 8 | Load | ir.cpp:676:12:676:12 | +| DerefReference(int &) -> int | 0 | 9 | Store | ir.cpp:676:12:676:12 | +| DerefReference(int &) -> int | 0 | 10 | VariableAddress[#return] | ir.cpp:675:5:675:18 | +| DerefReference(int &) -> int | 0 | 11 | ReturnValue | ir.cpp:675:5:675:18 | +| DerefReference(int &) -> int | 0 | 12 | UnmodeledUse | ir.cpp:675:5:675:18 | +| DerefReference(int &) -> int | 0 | 13 | ExitFunction | ir.cpp:675:5:675:18 | +| Dereference(int *) -> int | 0 | 0 | EnterFunction | ir.cpp:341:5:341:15 | +| Dereference(int *) -> int | 0 | 1 | UnmodeledDefinition | ir.cpp:341:5:341:15 | +| Dereference(int *) -> int | 0 | 2 | InitializeParameter[p] | ir.cpp:341:22:341:22 | +| Dereference(int *) -> int | 0 | 3 | VariableAddress[p] | ir.cpp:341:22:341:22 | +| Dereference(int *) -> int | 0 | 4 | Store | ir.cpp:341:22:341:22 | +| Dereference(int *) -> int | 0 | 5 | Constant[1] | ir.cpp:342:10:342:10 | +| Dereference(int *) -> int | 0 | 6 | VariableAddress[p] | ir.cpp:342:6:342:6 | +| Dereference(int *) -> int | 0 | 7 | Load | ir.cpp:342:6:342:6 | +| Dereference(int *) -> int | 0 | 8 | Store | ir.cpp:342:5:342:10 | +| Dereference(int *) -> int | 0 | 9 | VariableAddress[#return] | ir.cpp:343:5:343:14 | +| Dereference(int *) -> int | 0 | 10 | VariableAddress[p] | ir.cpp:343:13:343:13 | +| Dereference(int *) -> int | 0 | 11 | Load | ir.cpp:343:13:343:13 | +| Dereference(int *) -> int | 0 | 12 | Load | ir.cpp:343:12:343:13 | +| Dereference(int *) -> int | 0 | 13 | Store | ir.cpp:343:12:343:13 | +| Dereference(int *) -> int | 0 | 14 | VariableAddress[#return] | ir.cpp:341:5:341:15 | +| Dereference(int *) -> int | 0 | 15 | ReturnValue | ir.cpp:341:5:341:15 | +| Dereference(int *) -> int | 0 | 16 | UnmodeledUse | ir.cpp:341:5:341:15 | +| Dereference(int *) -> int | 0 | 17 | ExitFunction | ir.cpp:341:5:341:15 | +| Derived::Derived() -> void | 0 | 0 | EnterFunction | ir.cpp:766:3:766:9 | +| Derived::Derived() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:766:3:766:9 | +| Derived::Derived() -> void | 0 | 2 | InitializeThis | ir.cpp:766:3:766:9 | +| Derived::Derived() -> void | 0 | 3 | ConvertToBase[Derived : Middle] | ir.cpp:766:13:766:13 | +| Derived::Derived() -> void | 0 | 4 | FunctionAddress[Middle] | ir.cpp:766:13:766:13 | +| Derived::Derived() -> void | 0 | 5 | Invoke | ir.cpp:766:13:766:13 | +| Derived::Derived() -> void | 0 | 6 | FieldAddress[derived_s] | ir.cpp:766:13:766:13 | +| Derived::Derived() -> void | 0 | 7 | FunctionAddress[String] | ir.cpp:766:13:766:13 | +| Derived::Derived() -> void | 0 | 8 | Invoke | ir.cpp:766:13:766:13 | +| Derived::Derived() -> void | 0 | 9 | NoOp | ir.cpp:767:3:767:3 | +| Derived::Derived() -> void | 0 | 10 | ReturnVoid | ir.cpp:766:3:766:9 | +| Derived::Derived() -> void | 0 | 11 | UnmodeledUse | ir.cpp:766:3:766:9 | +| Derived::Derived() -> void | 0 | 12 | ExitFunction | ir.cpp:766:3:766:9 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 0 | EnterFunction | ir.cpp:763:8:763:8 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 1 | UnmodeledDefinition | ir.cpp:763:8:763:8 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 2 | InitializeThis | ir.cpp:763:8:763:8 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 3 | InitializeParameter[p#0] | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 4 | VariableAddress[p#0] | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 5 | Store | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 6 | CopyValue | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 7 | ConvertToBase[Derived : Middle] | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 8 | FunctionAddress[operator=] | ir.cpp:763:8:763:8 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 9 | VariableAddress[p#0] | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 10 | Load | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 11 | ConvertToBase[Derived : Middle] | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 12 | Invoke | ir.cpp:763:8:763:8 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 13 | CopyValue | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 14 | FieldAddress[derived_s] | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 15 | FunctionAddress[operator=] | ir.cpp:763:8:763:8 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 16 | VariableAddress[p#0] | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 17 | Load | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 18 | FieldAddress[derived_s] | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 19 | Invoke | ir.cpp:763:8:763:8 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 20 | VariableAddress[#return] | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 21 | CopyValue | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 22 | Store | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 23 | VariableAddress[#return] | ir.cpp:763:8:763:8 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 24 | ReturnValue | ir.cpp:763:8:763:8 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 25 | UnmodeledUse | ir.cpp:763:8:763:8 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 26 | ExitFunction | ir.cpp:763:8:763:8 | +| Derived::~Derived() -> void | 0 | 0 | EnterFunction | ir.cpp:768:3:768:10 | +| Derived::~Derived() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:768:3:768:10 | +| Derived::~Derived() -> void | 0 | 2 | InitializeThis | ir.cpp:768:3:768:10 | +| Derived::~Derived() -> void | 0 | 3 | NoOp | ir.cpp:769:3:769:3 | +| Derived::~Derived() -> void | 0 | 4 | FieldAddress[derived_s] | ir.cpp:769:3:769:3 | +| Derived::~Derived() -> void | 0 | 5 | FunctionAddress[~String] | ir.cpp:769:3:769:3 | +| Derived::~Derived() -> void | 0 | 6 | Invoke | ir.cpp:769:3:769:3 | +| Derived::~Derived() -> void | 0 | 7 | ConvertToBase[Derived : Middle] | ir.cpp:769:3:769:3 | +| Derived::~Derived() -> void | 0 | 8 | FunctionAddress[~Middle] | ir.cpp:769:3:769:3 | +| Derived::~Derived() -> void | 0 | 9 | Invoke | ir.cpp:769:3:769:3 | +| Derived::~Derived() -> void | 0 | 10 | ReturnVoid | ir.cpp:768:3:768:10 | +| Derived::~Derived() -> void | 0 | 11 | UnmodeledUse | ir.cpp:768:3:768:10 | +| Derived::~Derived() -> void | 0 | 12 | ExitFunction | ir.cpp:768:3:768:10 | +| DerivedVB::DerivedVB() -> void | 0 | 0 | EnterFunction | ir.cpp:793:3:793:11 | +| DerivedVB::DerivedVB() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:793:3:793:11 | +| DerivedVB::DerivedVB() -> void | 0 | 2 | InitializeThis | ir.cpp:793:3:793:11 | +| DerivedVB::DerivedVB() -> void | 0 | 3 | ConvertToBase[DerivedVB : Base] | ir.cpp:793:15:793:15 | +| DerivedVB::DerivedVB() -> void | 0 | 4 | FunctionAddress[Base] | ir.cpp:793:15:793:15 | +| DerivedVB::DerivedVB() -> void | 0 | 5 | Invoke | ir.cpp:793:15:793:15 | +| DerivedVB::DerivedVB() -> void | 0 | 6 | ConvertToBase[DerivedVB : MiddleVB1] | ir.cpp:793:15:793:15 | +| DerivedVB::DerivedVB() -> void | 0 | 7 | FunctionAddress[MiddleVB1] | ir.cpp:793:15:793:15 | +| DerivedVB::DerivedVB() -> void | 0 | 8 | Invoke | ir.cpp:793:15:793:15 | +| DerivedVB::DerivedVB() -> void | 0 | 9 | ConvertToBase[DerivedVB : MiddleVB2] | ir.cpp:793:15:793:15 | +| DerivedVB::DerivedVB() -> void | 0 | 10 | FunctionAddress[MiddleVB2] | ir.cpp:793:15:793:15 | +| DerivedVB::DerivedVB() -> void | 0 | 11 | Invoke | ir.cpp:793:15:793:15 | +| DerivedVB::DerivedVB() -> void | 0 | 12 | FieldAddress[derivedvb_s] | ir.cpp:793:15:793:15 | +| DerivedVB::DerivedVB() -> void | 0 | 13 | FunctionAddress[String] | ir.cpp:793:15:793:15 | +| DerivedVB::DerivedVB() -> void | 0 | 14 | Invoke | ir.cpp:793:15:793:15 | +| DerivedVB::DerivedVB() -> void | 0 | 15 | NoOp | ir.cpp:794:3:794:3 | +| DerivedVB::DerivedVB() -> void | 0 | 16 | ReturnVoid | ir.cpp:793:3:793:11 | +| DerivedVB::DerivedVB() -> void | 0 | 17 | UnmodeledUse | ir.cpp:793:3:793:11 | +| DerivedVB::DerivedVB() -> void | 0 | 18 | ExitFunction | ir.cpp:793:3:793:11 | +| DerivedVB::~DerivedVB() -> void | 0 | 0 | EnterFunction | ir.cpp:795:3:795:12 | +| DerivedVB::~DerivedVB() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:795:3:795:12 | +| DerivedVB::~DerivedVB() -> void | 0 | 2 | InitializeThis | ir.cpp:795:3:795:12 | +| DerivedVB::~DerivedVB() -> void | 0 | 3 | NoOp | ir.cpp:796:3:796:3 | +| DerivedVB::~DerivedVB() -> void | 0 | 4 | FieldAddress[derivedvb_s] | ir.cpp:796:3:796:3 | +| DerivedVB::~DerivedVB() -> void | 0 | 5 | FunctionAddress[~String] | ir.cpp:796:3:796:3 | +| DerivedVB::~DerivedVB() -> void | 0 | 6 | Invoke | ir.cpp:796:3:796:3 | +| DerivedVB::~DerivedVB() -> void | 0 | 7 | ConvertToBase[DerivedVB : MiddleVB2] | ir.cpp:796:3:796:3 | +| DerivedVB::~DerivedVB() -> void | 0 | 8 | FunctionAddress[~MiddleVB2] | ir.cpp:796:3:796:3 | +| DerivedVB::~DerivedVB() -> void | 0 | 9 | Invoke | ir.cpp:796:3:796:3 | +| DerivedVB::~DerivedVB() -> void | 0 | 10 | ConvertToBase[DerivedVB : MiddleVB1] | ir.cpp:796:3:796:3 | +| DerivedVB::~DerivedVB() -> void | 0 | 11 | FunctionAddress[~MiddleVB1] | ir.cpp:796:3:796:3 | +| DerivedVB::~DerivedVB() -> void | 0 | 12 | Invoke | ir.cpp:796:3:796:3 | +| DerivedVB::~DerivedVB() -> void | 0 | 13 | ConvertToBase[DerivedVB : Base] | ir.cpp:796:3:796:3 | +| DerivedVB::~DerivedVB() -> void | 0 | 14 | FunctionAddress[~Base] | ir.cpp:796:3:796:3 | +| DerivedVB::~DerivedVB() -> void | 0 | 15 | Invoke | ir.cpp:796:3:796:3 | +| DerivedVB::~DerivedVB() -> void | 0 | 16 | ReturnVoid | ir.cpp:795:3:795:12 | +| DerivedVB::~DerivedVB() -> void | 0 | 17 | UnmodeledUse | ir.cpp:795:3:795:12 | +| DerivedVB::~DerivedVB() -> void | 0 | 18 | ExitFunction | ir.cpp:795:3:795:12 | +| DoStatements(int) -> void | 0 | 0 | EnterFunction | ir.cpp:259:6:259:17 | +| DoStatements(int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:259:6:259:17 | +| DoStatements(int) -> void | 0 | 2 | InitializeParameter[n] | ir.cpp:259:23:259:23 | +| DoStatements(int) -> void | 0 | 3 | VariableAddress[n] | ir.cpp:259:23:259:23 | +| DoStatements(int) -> void | 0 | 4 | Store | ir.cpp:259:23:259:23 | +| DoStatements(int) -> void | 1 | 0 | Constant[1] | ir.cpp:261:14:261:14 | +| DoStatements(int) -> void | 1 | 1 | VariableAddress[n] | ir.cpp:261:9:261:9 | +| DoStatements(int) -> void | 1 | 2 | Load | ir.cpp:261:9:261:14 | +| DoStatements(int) -> void | 1 | 3 | Sub | ir.cpp:261:9:261:14 | +| DoStatements(int) -> void | 1 | 4 | Store | ir.cpp:261:9:261:14 | +| DoStatements(int) -> void | 1 | 5 | VariableAddress[n] | ir.cpp:262:14:262:14 | +| DoStatements(int) -> void | 1 | 6 | Load | ir.cpp:262:14:262:14 | +| DoStatements(int) -> void | 1 | 7 | Constant[0] | ir.cpp:262:18:262:18 | +| DoStatements(int) -> void | 1 | 8 | CompareGT | ir.cpp:262:14:262:18 | +| DoStatements(int) -> void | 1 | 9 | ConditionalBranch | ir.cpp:262:14:262:18 | +| DoStatements(int) -> void | 2 | 0 | NoOp | ir.cpp:263:1:263:1 | +| DoStatements(int) -> void | 2 | 1 | ReturnVoid | ir.cpp:259:6:259:17 | +| DoStatements(int) -> void | 2 | 2 | UnmodeledUse | ir.cpp:259:6:259:17 | +| DoStatements(int) -> void | 2 | 3 | ExitFunction | ir.cpp:259:6:259:17 | +| DynamicCast() -> void | 0 | 0 | EnterFunction | ir.cpp:849:6:849:16 | +| DynamicCast() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:849:6:849:16 | +| DynamicCast() -> void | 0 | 2 | VariableAddress[b] | ir.cpp:850:19:850:19 | +| DynamicCast() -> void | 0 | 3 | FunctionAddress[PolymorphicBase] | file://:0:0:0:0 | +| DynamicCast() -> void | 0 | 4 | Invoke | file://:0:0:0:0 | +| DynamicCast() -> void | 0 | 5 | VariableAddress[d] | ir.cpp:851:22:851:22 | +| DynamicCast() -> void | 0 | 6 | FunctionAddress[PolymorphicDerived] | ir.cpp:851:22:851:22 | +| DynamicCast() -> void | 0 | 7 | Invoke | ir.cpp:851:22:851:22 | +| DynamicCast() -> void | 0 | 8 | VariableAddress[pb] | ir.cpp:853:20:853:21 | +| DynamicCast() -> void | 0 | 9 | VariableAddress[b] | ir.cpp:853:26:853:26 | +| DynamicCast() -> void | 0 | 10 | Store | ir.cpp:853:25:853:26 | +| DynamicCast() -> void | 0 | 11 | VariableAddress[pd] | ir.cpp:854:23:854:24 | +| DynamicCast() -> void | 0 | 12 | VariableAddress[d] | ir.cpp:854:29:854:29 | +| DynamicCast() -> void | 0 | 13 | Store | ir.cpp:854:28:854:29 | +| DynamicCast() -> void | 0 | 14 | VariableAddress[pd] | ir.cpp:857:39:857:40 | +| DynamicCast() -> void | 0 | 15 | Load | ir.cpp:857:39:857:40 | +| DynamicCast() -> void | 0 | 16 | ConvertToBase[PolymorphicDerived : PolymorphicBase] | ir.cpp:857:8:857:41 | +| DynamicCast() -> void | 0 | 17 | VariableAddress[pb] | ir.cpp:857:3:857:4 | +| DynamicCast() -> void | 0 | 18 | Store | ir.cpp:857:3:857:41 | +| DynamicCast() -> void | 0 | 19 | VariableAddress[rb] | ir.cpp:858:20:858:21 | +| DynamicCast() -> void | 0 | 20 | VariableAddress[d] | ir.cpp:858:56:858:56 | +| DynamicCast() -> void | 0 | 21 | ConvertToBase[PolymorphicDerived : PolymorphicBase] | ir.cpp:858:25:858:57 | +| DynamicCast() -> void | 0 | 22 | Store | ir.cpp:858:25:858:57 | +| DynamicCast() -> void | 0 | 23 | VariableAddress[pb] | ir.cpp:860:42:860:43 | +| DynamicCast() -> void | 0 | 24 | Load | ir.cpp:860:42:860:43 | +| DynamicCast() -> void | 0 | 25 | CheckedConvertOrNull | ir.cpp:860:8:860:44 | +| DynamicCast() -> void | 0 | 26 | VariableAddress[pd] | ir.cpp:860:3:860:4 | +| DynamicCast() -> void | 0 | 27 | Store | ir.cpp:860:3:860:44 | +| DynamicCast() -> void | 0 | 28 | VariableAddress[rd] | ir.cpp:861:23:861:24 | +| DynamicCast() -> void | 0 | 29 | VariableAddress[b] | ir.cpp:861:62:861:62 | +| DynamicCast() -> void | 0 | 30 | CheckedConvertOrThrow | ir.cpp:861:28:861:63 | +| DynamicCast() -> void | 0 | 31 | Store | ir.cpp:861:28:861:63 | +| DynamicCast() -> void | 0 | 32 | VariableAddress[pv] | ir.cpp:863:9:863:10 | +| DynamicCast() -> void | 0 | 33 | VariableAddress[pb] | ir.cpp:863:34:863:35 | +| DynamicCast() -> void | 0 | 34 | Load | ir.cpp:863:34:863:35 | +| DynamicCast() -> void | 0 | 35 | DynamicCastToVoid | ir.cpp:863:14:863:36 | +| DynamicCast() -> void | 0 | 36 | Store | ir.cpp:863:14:863:36 | +| DynamicCast() -> void | 0 | 37 | VariableAddress[pcv] | ir.cpp:864:15:864:17 | +| DynamicCast() -> void | 0 | 38 | VariableAddress[pd] | ir.cpp:864:47:864:48 | +| DynamicCast() -> void | 0 | 39 | Load | ir.cpp:864:47:864:48 | +| DynamicCast() -> void | 0 | 40 | DynamicCastToVoid | ir.cpp:864:21:864:49 | +| DynamicCast() -> void | 0 | 41 | Store | ir.cpp:864:21:864:49 | +| DynamicCast() -> void | 0 | 42 | NoOp | ir.cpp:865:1:865:1 | +| DynamicCast() -> void | 0 | 43 | ReturnVoid | ir.cpp:849:6:849:16 | +| DynamicCast() -> void | 0 | 44 | UnmodeledUse | ir.cpp:849:6:849:16 | +| DynamicCast() -> void | 0 | 45 | ExitFunction | ir.cpp:849:6:849:16 | +| EarlyReturn(int, int) -> void | 0 | 0 | EnterFunction | ir.cpp:535:6:535:16 | +| EarlyReturn(int, int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:535:6:535:16 | +| EarlyReturn(int, int) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:535:22:535:22 | +| EarlyReturn(int, int) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:535:22:535:22 | +| EarlyReturn(int, int) -> void | 0 | 4 | Store | ir.cpp:535:22:535:22 | +| EarlyReturn(int, int) -> void | 0 | 5 | InitializeParameter[y] | ir.cpp:535:29:535:29 | +| EarlyReturn(int, int) -> void | 0 | 6 | VariableAddress[y] | ir.cpp:535:29:535:29 | +| EarlyReturn(int, int) -> void | 0 | 7 | Store | ir.cpp:535:29:535:29 | +| EarlyReturn(int, int) -> void | 0 | 8 | VariableAddress[x] | ir.cpp:536:9:536:9 | +| EarlyReturn(int, int) -> void | 0 | 9 | Load | ir.cpp:536:9:536:9 | +| EarlyReturn(int, int) -> void | 0 | 10 | VariableAddress[y] | ir.cpp:536:13:536:13 | +| EarlyReturn(int, int) -> void | 0 | 11 | Load | ir.cpp:536:13:536:13 | +| EarlyReturn(int, int) -> void | 0 | 12 | CompareLT | ir.cpp:536:9:536:13 | +| EarlyReturn(int, int) -> void | 0 | 13 | ConditionalBranch | ir.cpp:536:9:536:13 | +| EarlyReturn(int, int) -> void | 1 | 0 | ReturnVoid | ir.cpp:535:6:535:16 | +| EarlyReturn(int, int) -> void | 1 | 1 | UnmodeledUse | ir.cpp:535:6:535:16 | +| EarlyReturn(int, int) -> void | 1 | 2 | ExitFunction | ir.cpp:535:6:535:16 | +| EarlyReturn(int, int) -> void | 2 | 0 | NoOp | ir.cpp:537:9:537:15 | +| EarlyReturn(int, int) -> void | 3 | 0 | VariableAddress[x] | ir.cpp:540:9:540:9 | +| EarlyReturn(int, int) -> void | 3 | 1 | Load | ir.cpp:540:9:540:9 | +| EarlyReturn(int, int) -> void | 3 | 2 | VariableAddress[y] | ir.cpp:540:5:540:5 | +| EarlyReturn(int, int) -> void | 3 | 3 | Store | ir.cpp:540:5:540:9 | +| EarlyReturn(int, int) -> void | 3 | 4 | NoOp | ir.cpp:541:1:541:1 | +| EarlyReturnValue(int, int) -> int | 0 | 0 | EnterFunction | ir.cpp:543:5:543:20 | +| EarlyReturnValue(int, int) -> int | 0 | 1 | UnmodeledDefinition | ir.cpp:543:5:543:20 | +| EarlyReturnValue(int, int) -> int | 0 | 2 | InitializeParameter[x] | ir.cpp:543:26:543:26 | +| EarlyReturnValue(int, int) -> int | 0 | 3 | VariableAddress[x] | ir.cpp:543:26:543:26 | +| EarlyReturnValue(int, int) -> int | 0 | 4 | Store | ir.cpp:543:26:543:26 | +| EarlyReturnValue(int, int) -> int | 0 | 5 | InitializeParameter[y] | ir.cpp:543:33:543:33 | +| EarlyReturnValue(int, int) -> int | 0 | 6 | VariableAddress[y] | ir.cpp:543:33:543:33 | +| EarlyReturnValue(int, int) -> int | 0 | 7 | Store | ir.cpp:543:33:543:33 | +| EarlyReturnValue(int, int) -> int | 0 | 8 | VariableAddress[x] | ir.cpp:544:9:544:9 | +| EarlyReturnValue(int, int) -> int | 0 | 9 | Load | ir.cpp:544:9:544:9 | +| EarlyReturnValue(int, int) -> int | 0 | 10 | VariableAddress[y] | ir.cpp:544:13:544:13 | +| EarlyReturnValue(int, int) -> int | 0 | 11 | Load | ir.cpp:544:13:544:13 | +| EarlyReturnValue(int, int) -> int | 0 | 12 | CompareLT | ir.cpp:544:9:544:13 | +| EarlyReturnValue(int, int) -> int | 0 | 13 | ConditionalBranch | ir.cpp:544:9:544:13 | +| EarlyReturnValue(int, int) -> int | 1 | 0 | VariableAddress[#return] | ir.cpp:543:5:543:20 | +| EarlyReturnValue(int, int) -> int | 1 | 1 | ReturnValue | ir.cpp:543:5:543:20 | +| EarlyReturnValue(int, int) -> int | 1 | 2 | UnmodeledUse | ir.cpp:543:5:543:20 | +| EarlyReturnValue(int, int) -> int | 1 | 3 | ExitFunction | ir.cpp:543:5:543:20 | +| EarlyReturnValue(int, int) -> int | 2 | 0 | VariableAddress[#return] | ir.cpp:545:9:545:17 | +| EarlyReturnValue(int, int) -> int | 2 | 1 | VariableAddress[x] | ir.cpp:545:16:545:16 | +| EarlyReturnValue(int, int) -> int | 2 | 2 | Load | ir.cpp:545:16:545:16 | +| EarlyReturnValue(int, int) -> int | 2 | 3 | Store | ir.cpp:545:16:545:16 | +| EarlyReturnValue(int, int) -> int | 3 | 0 | VariableAddress[#return] | ir.cpp:548:5:548:17 | +| EarlyReturnValue(int, int) -> int | 3 | 1 | VariableAddress[x] | ir.cpp:548:12:548:12 | +| EarlyReturnValue(int, int) -> int | 3 | 2 | Load | ir.cpp:548:12:548:12 | +| EarlyReturnValue(int, int) -> int | 3 | 3 | VariableAddress[y] | ir.cpp:548:16:548:16 | +| EarlyReturnValue(int, int) -> int | 3 | 4 | Load | ir.cpp:548:16:548:16 | +| EarlyReturnValue(int, int) -> int | 3 | 5 | Add | ir.cpp:548:12:548:16 | +| EarlyReturnValue(int, int) -> int | 3 | 6 | Store | ir.cpp:548:12:548:16 | +| EnumSwitch(E) -> int | 0 | 0 | EnterFunction | ir.cpp:560:5:560:14 | +| EnumSwitch(E) -> int | 0 | 1 | UnmodeledDefinition | ir.cpp:560:5:560:14 | +| EnumSwitch(E) -> int | 0 | 2 | InitializeParameter[e] | ir.cpp:560:18:560:18 | +| EnumSwitch(E) -> int | 0 | 3 | VariableAddress[e] | ir.cpp:560:18:560:18 | +| EnumSwitch(E) -> int | 0 | 4 | Store | ir.cpp:560:18:560:18 | +| EnumSwitch(E) -> int | 0 | 5 | VariableAddress[e] | ir.cpp:561:13:561:13 | +| EnumSwitch(E) -> int | 0 | 6 | Load | ir.cpp:561:13:561:13 | +| EnumSwitch(E) -> int | 0 | 7 | Convert | ir.cpp:561:13:561:13 | +| EnumSwitch(E) -> int | 0 | 8 | Switch | ir.cpp:561:5:568:5 | +| EnumSwitch(E) -> int | 1 | 0 | VariableAddress[#return] | ir.cpp:560:5:560:14 | +| EnumSwitch(E) -> int | 1 | 1 | ReturnValue | ir.cpp:560:5:560:14 | +| EnumSwitch(E) -> int | 1 | 2 | UnmodeledUse | ir.cpp:560:5:560:14 | +| EnumSwitch(E) -> int | 1 | 3 | ExitFunction | ir.cpp:560:5:560:14 | +| EnumSwitch(E) -> int | 2 | 0 | NoOp | ir.cpp:564:9:564:17 | +| EnumSwitch(E) -> int | 2 | 1 | VariableAddress[#return] | ir.cpp:565:13:565:21 | +| EnumSwitch(E) -> int | 2 | 2 | Constant[1] | ir.cpp:565:20:565:20 | +| EnumSwitch(E) -> int | 2 | 3 | Store | ir.cpp:565:20:565:20 | +| EnumSwitch(E) -> int | 3 | 0 | NoOp | ir.cpp:566:9:566:16 | +| EnumSwitch(E) -> int | 3 | 1 | VariableAddress[#return] | ir.cpp:567:13:567:22 | +| EnumSwitch(E) -> int | 3 | 2 | Constant[-1] | ir.cpp:567:20:567:21 | +| EnumSwitch(E) -> int | 3 | 3 | Store | ir.cpp:567:20:567:21 | +| EnumSwitch(E) -> int | 4 | 0 | NoOp | ir.cpp:562:9:562:17 | +| EnumSwitch(E) -> int | 4 | 1 | VariableAddress[#return] | ir.cpp:563:13:563:21 | +| EnumSwitch(E) -> int | 4 | 2 | Constant[0] | ir.cpp:563:20:563:20 | +| EnumSwitch(E) -> int | 4 | 3 | Store | ir.cpp:563:20:563:20 | +| FieldAccess() -> void | 0 | 0 | EnterFunction | ir.cpp:426:6:426:16 | +| FieldAccess() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:426:6:426:16 | +| FieldAccess() -> void | 0 | 2 | VariableAddress[pt] | ir.cpp:427:11:427:12 | +| FieldAccess() -> void | 0 | 3 | Uninitialized | ir.cpp:427:11:427:12 | +| FieldAccess() -> void | 0 | 4 | Store | ir.cpp:427:11:427:12 | +| FieldAccess() -> void | 0 | 5 | Constant[5] | ir.cpp:428:12:428:12 | +| FieldAccess() -> void | 0 | 6 | VariableAddress[pt] | ir.cpp:428:5:428:6 | +| FieldAccess() -> void | 0 | 7 | FieldAddress[x] | ir.cpp:428:8:428:8 | +| FieldAccess() -> void | 0 | 8 | Store | ir.cpp:428:5:428:12 | +| FieldAccess() -> void | 0 | 9 | VariableAddress[pt] | ir.cpp:429:12:429:13 | +| FieldAccess() -> void | 0 | 10 | FieldAddress[x] | ir.cpp:429:15:429:15 | +| FieldAccess() -> void | 0 | 11 | Load | ir.cpp:429:15:429:15 | +| FieldAccess() -> void | 0 | 12 | VariableAddress[pt] | ir.cpp:429:5:429:6 | +| FieldAccess() -> void | 0 | 13 | FieldAddress[y] | ir.cpp:429:8:429:8 | +| FieldAccess() -> void | 0 | 14 | Store | ir.cpp:429:5:429:15 | +| FieldAccess() -> void | 0 | 15 | VariableAddress[p] | ir.cpp:430:10:430:10 | +| FieldAccess() -> void | 0 | 16 | VariableAddress[pt] | ir.cpp:430:15:430:16 | +| FieldAccess() -> void | 0 | 17 | FieldAddress[y] | ir.cpp:430:18:430:18 | +| FieldAccess() -> void | 0 | 18 | Store | ir.cpp:430:14:430:18 | +| FieldAccess() -> void | 0 | 19 | NoOp | ir.cpp:431:1:431:1 | +| FieldAccess() -> void | 0 | 20 | ReturnVoid | ir.cpp:426:6:426:16 | +| FieldAccess() -> void | 0 | 21 | UnmodeledUse | ir.cpp:426:6:426:16 | +| FieldAccess() -> void | 0 | 22 | ExitFunction | ir.cpp:426:6:426:16 | +| FloatCompare(double, double) -> void | 0 | 0 | EnterFunction | ir.cpp:133:6:133:17 | +| FloatCompare(double, double) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:133:6:133:17 | +| FloatCompare(double, double) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:133:26:133:26 | +| FloatCompare(double, double) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:133:26:133:26 | +| FloatCompare(double, double) -> void | 0 | 4 | Store | ir.cpp:133:26:133:26 | +| FloatCompare(double, double) -> void | 0 | 5 | InitializeParameter[y] | ir.cpp:133:36:133:36 | +| FloatCompare(double, double) -> void | 0 | 6 | VariableAddress[y] | ir.cpp:133:36:133:36 | +| FloatCompare(double, double) -> void | 0 | 7 | Store | ir.cpp:133:36:133:36 | +| FloatCompare(double, double) -> void | 0 | 8 | VariableAddress[b] | ir.cpp:134:10:134:10 | +| FloatCompare(double, double) -> void | 0 | 9 | Uninitialized | ir.cpp:134:10:134:10 | +| FloatCompare(double, double) -> void | 0 | 10 | Store | ir.cpp:134:10:134:10 | +| FloatCompare(double, double) -> void | 0 | 11 | VariableAddress[x] | ir.cpp:136:9:136:9 | +| FloatCompare(double, double) -> void | 0 | 12 | Load | ir.cpp:136:9:136:9 | +| FloatCompare(double, double) -> void | 0 | 13 | VariableAddress[y] | ir.cpp:136:14:136:14 | +| FloatCompare(double, double) -> void | 0 | 14 | Load | ir.cpp:136:14:136:14 | +| FloatCompare(double, double) -> void | 0 | 15 | CompareEQ | ir.cpp:136:9:136:14 | +| FloatCompare(double, double) -> void | 0 | 16 | VariableAddress[b] | ir.cpp:136:5:136:5 | +| FloatCompare(double, double) -> void | 0 | 17 | Store | ir.cpp:136:5:136:14 | +| FloatCompare(double, double) -> void | 0 | 18 | VariableAddress[x] | ir.cpp:137:9:137:9 | +| FloatCompare(double, double) -> void | 0 | 19 | Load | ir.cpp:137:9:137:9 | +| FloatCompare(double, double) -> void | 0 | 20 | VariableAddress[y] | ir.cpp:137:14:137:14 | +| FloatCompare(double, double) -> void | 0 | 21 | Load | ir.cpp:137:14:137:14 | +| FloatCompare(double, double) -> void | 0 | 22 | CompareNE | ir.cpp:137:9:137:14 | +| FloatCompare(double, double) -> void | 0 | 23 | VariableAddress[b] | ir.cpp:137:5:137:5 | +| FloatCompare(double, double) -> void | 0 | 24 | Store | ir.cpp:137:5:137:14 | +| FloatCompare(double, double) -> void | 0 | 25 | VariableAddress[x] | ir.cpp:138:9:138:9 | +| FloatCompare(double, double) -> void | 0 | 26 | Load | ir.cpp:138:9:138:9 | +| FloatCompare(double, double) -> void | 0 | 27 | VariableAddress[y] | ir.cpp:138:13:138:13 | +| FloatCompare(double, double) -> void | 0 | 28 | Load | ir.cpp:138:13:138:13 | +| FloatCompare(double, double) -> void | 0 | 29 | CompareLT | ir.cpp:138:9:138:13 | +| FloatCompare(double, double) -> void | 0 | 30 | VariableAddress[b] | ir.cpp:138:5:138:5 | +| FloatCompare(double, double) -> void | 0 | 31 | Store | ir.cpp:138:5:138:13 | +| FloatCompare(double, double) -> void | 0 | 32 | VariableAddress[x] | ir.cpp:139:9:139:9 | +| FloatCompare(double, double) -> void | 0 | 33 | Load | ir.cpp:139:9:139:9 | +| FloatCompare(double, double) -> void | 0 | 34 | VariableAddress[y] | ir.cpp:139:13:139:13 | +| FloatCompare(double, double) -> void | 0 | 35 | Load | ir.cpp:139:13:139:13 | +| FloatCompare(double, double) -> void | 0 | 36 | CompareGT | ir.cpp:139:9:139:13 | +| FloatCompare(double, double) -> void | 0 | 37 | VariableAddress[b] | ir.cpp:139:5:139:5 | +| FloatCompare(double, double) -> void | 0 | 38 | Store | ir.cpp:139:5:139:13 | +| FloatCompare(double, double) -> void | 0 | 39 | VariableAddress[x] | ir.cpp:140:9:140:9 | +| FloatCompare(double, double) -> void | 0 | 40 | Load | ir.cpp:140:9:140:9 | +| FloatCompare(double, double) -> void | 0 | 41 | VariableAddress[y] | ir.cpp:140:14:140:14 | +| FloatCompare(double, double) -> void | 0 | 42 | Load | ir.cpp:140:14:140:14 | +| FloatCompare(double, double) -> void | 0 | 43 | CompareLE | ir.cpp:140:9:140:14 | +| FloatCompare(double, double) -> void | 0 | 44 | VariableAddress[b] | ir.cpp:140:5:140:5 | +| FloatCompare(double, double) -> void | 0 | 45 | Store | ir.cpp:140:5:140:14 | +| FloatCompare(double, double) -> void | 0 | 46 | VariableAddress[x] | ir.cpp:141:9:141:9 | +| FloatCompare(double, double) -> void | 0 | 47 | Load | ir.cpp:141:9:141:9 | +| FloatCompare(double, double) -> void | 0 | 48 | VariableAddress[y] | ir.cpp:141:14:141:14 | +| FloatCompare(double, double) -> void | 0 | 49 | Load | ir.cpp:141:14:141:14 | +| FloatCompare(double, double) -> void | 0 | 50 | CompareGE | ir.cpp:141:9:141:14 | +| FloatCompare(double, double) -> void | 0 | 51 | VariableAddress[b] | ir.cpp:141:5:141:5 | +| FloatCompare(double, double) -> void | 0 | 52 | Store | ir.cpp:141:5:141:14 | +| FloatCompare(double, double) -> void | 0 | 53 | NoOp | ir.cpp:142:1:142:1 | +| FloatCompare(double, double) -> void | 0 | 54 | ReturnVoid | ir.cpp:133:6:133:17 | +| FloatCompare(double, double) -> void | 0 | 55 | UnmodeledUse | ir.cpp:133:6:133:17 | +| FloatCompare(double, double) -> void | 0 | 56 | ExitFunction | ir.cpp:133:6:133:17 | +| FloatCrement(float) -> void | 0 | 0 | EnterFunction | ir.cpp:144:6:144:17 | +| FloatCrement(float) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:144:6:144:17 | +| FloatCrement(float) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:144:25:144:25 | +| FloatCrement(float) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:144:25:144:25 | +| FloatCrement(float) -> void | 0 | 4 | Store | ir.cpp:144:25:144:25 | +| FloatCrement(float) -> void | 0 | 5 | VariableAddress[y] | ir.cpp:145:11:145:11 | +| FloatCrement(float) -> void | 0 | 6 | Uninitialized | ir.cpp:145:11:145:11 | +| FloatCrement(float) -> void | 0 | 7 | Store | ir.cpp:145:11:145:11 | +| FloatCrement(float) -> void | 0 | 8 | VariableAddress[x] | ir.cpp:147:11:147:11 | +| FloatCrement(float) -> void | 0 | 9 | Load | ir.cpp:147:9:147:11 | +| FloatCrement(float) -> void | 0 | 10 | Constant[1.0] | ir.cpp:147:9:147:11 | +| FloatCrement(float) -> void | 0 | 11 | Add | ir.cpp:147:9:147:11 | +| FloatCrement(float) -> void | 0 | 12 | Store | ir.cpp:147:9:147:11 | +| FloatCrement(float) -> void | 0 | 13 | VariableAddress[y] | ir.cpp:147:5:147:5 | +| FloatCrement(float) -> void | 0 | 14 | Store | ir.cpp:147:5:147:11 | +| FloatCrement(float) -> void | 0 | 15 | VariableAddress[x] | ir.cpp:148:11:148:11 | +| FloatCrement(float) -> void | 0 | 16 | Load | ir.cpp:148:9:148:11 | +| FloatCrement(float) -> void | 0 | 17 | Constant[1.0] | ir.cpp:148:9:148:11 | +| FloatCrement(float) -> void | 0 | 18 | Sub | ir.cpp:148:9:148:11 | +| FloatCrement(float) -> void | 0 | 19 | Store | ir.cpp:148:9:148:11 | +| FloatCrement(float) -> void | 0 | 20 | VariableAddress[y] | ir.cpp:148:5:148:5 | +| FloatCrement(float) -> void | 0 | 21 | Store | ir.cpp:148:5:148:11 | +| FloatCrement(float) -> void | 0 | 22 | VariableAddress[x] | ir.cpp:149:9:149:9 | +| FloatCrement(float) -> void | 0 | 23 | Load | ir.cpp:149:9:149:11 | +| FloatCrement(float) -> void | 0 | 24 | Constant[1.0] | ir.cpp:149:9:149:11 | +| FloatCrement(float) -> void | 0 | 25 | Add | ir.cpp:149:9:149:11 | +| FloatCrement(float) -> void | 0 | 26 | Store | ir.cpp:149:9:149:11 | +| FloatCrement(float) -> void | 0 | 27 | VariableAddress[y] | ir.cpp:149:5:149:5 | +| FloatCrement(float) -> void | 0 | 28 | Store | ir.cpp:149:5:149:11 | +| FloatCrement(float) -> void | 0 | 29 | VariableAddress[x] | ir.cpp:150:9:150:9 | +| FloatCrement(float) -> void | 0 | 30 | Load | ir.cpp:150:9:150:11 | +| FloatCrement(float) -> void | 0 | 31 | Constant[1.0] | ir.cpp:150:9:150:11 | +| FloatCrement(float) -> void | 0 | 32 | Sub | ir.cpp:150:9:150:11 | +| FloatCrement(float) -> void | 0 | 33 | Store | ir.cpp:150:9:150:11 | +| FloatCrement(float) -> void | 0 | 34 | VariableAddress[y] | ir.cpp:150:5:150:5 | +| FloatCrement(float) -> void | 0 | 35 | Store | ir.cpp:150:5:150:11 | +| FloatCrement(float) -> void | 0 | 36 | NoOp | ir.cpp:151:1:151:1 | +| FloatCrement(float) -> void | 0 | 37 | ReturnVoid | ir.cpp:144:6:144:17 | +| FloatCrement(float) -> void | 0 | 38 | UnmodeledUse | ir.cpp:144:6:144:17 | +| FloatCrement(float) -> void | 0 | 39 | ExitFunction | ir.cpp:144:6:144:17 | +| FloatOps(double, double) -> void | 0 | 0 | EnterFunction | ir.cpp:114:6:114:13 | +| FloatOps(double, double) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:114:6:114:13 | +| FloatOps(double, double) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:114:22:114:22 | +| FloatOps(double, double) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:114:22:114:22 | +| FloatOps(double, double) -> void | 0 | 4 | Store | ir.cpp:114:22:114:22 | +| FloatOps(double, double) -> void | 0 | 5 | InitializeParameter[y] | ir.cpp:114:32:114:32 | +| FloatOps(double, double) -> void | 0 | 6 | VariableAddress[y] | ir.cpp:114:32:114:32 | +| FloatOps(double, double) -> void | 0 | 7 | Store | ir.cpp:114:32:114:32 | +| FloatOps(double, double) -> void | 0 | 8 | VariableAddress[z] | ir.cpp:115:12:115:12 | +| FloatOps(double, double) -> void | 0 | 9 | Uninitialized | ir.cpp:115:12:115:12 | +| FloatOps(double, double) -> void | 0 | 10 | Store | ir.cpp:115:12:115:12 | +| FloatOps(double, double) -> void | 0 | 11 | VariableAddress[x] | ir.cpp:117:9:117:9 | +| FloatOps(double, double) -> void | 0 | 12 | Load | ir.cpp:117:9:117:9 | +| FloatOps(double, double) -> void | 0 | 13 | VariableAddress[y] | ir.cpp:117:13:117:13 | +| FloatOps(double, double) -> void | 0 | 14 | Load | ir.cpp:117:13:117:13 | +| FloatOps(double, double) -> void | 0 | 15 | Add | ir.cpp:117:9:117:13 | +| FloatOps(double, double) -> void | 0 | 16 | VariableAddress[z] | ir.cpp:117:5:117:5 | +| FloatOps(double, double) -> void | 0 | 17 | Store | ir.cpp:117:5:117:13 | +| FloatOps(double, double) -> void | 0 | 18 | VariableAddress[x] | ir.cpp:118:9:118:9 | +| FloatOps(double, double) -> void | 0 | 19 | Load | ir.cpp:118:9:118:9 | +| FloatOps(double, double) -> void | 0 | 20 | VariableAddress[y] | ir.cpp:118:13:118:13 | +| FloatOps(double, double) -> void | 0 | 21 | Load | ir.cpp:118:13:118:13 | +| FloatOps(double, double) -> void | 0 | 22 | Sub | ir.cpp:118:9:118:13 | +| FloatOps(double, double) -> void | 0 | 23 | VariableAddress[z] | ir.cpp:118:5:118:5 | +| FloatOps(double, double) -> void | 0 | 24 | Store | ir.cpp:118:5:118:13 | +| FloatOps(double, double) -> void | 0 | 25 | VariableAddress[x] | ir.cpp:119:9:119:9 | +| FloatOps(double, double) -> void | 0 | 26 | Load | ir.cpp:119:9:119:9 | +| FloatOps(double, double) -> void | 0 | 27 | VariableAddress[y] | ir.cpp:119:13:119:13 | +| FloatOps(double, double) -> void | 0 | 28 | Load | ir.cpp:119:13:119:13 | +| FloatOps(double, double) -> void | 0 | 29 | Mul | ir.cpp:119:9:119:13 | +| FloatOps(double, double) -> void | 0 | 30 | VariableAddress[z] | ir.cpp:119:5:119:5 | +| FloatOps(double, double) -> void | 0 | 31 | Store | ir.cpp:119:5:119:13 | +| FloatOps(double, double) -> void | 0 | 32 | VariableAddress[x] | ir.cpp:120:9:120:9 | +| FloatOps(double, double) -> void | 0 | 33 | Load | ir.cpp:120:9:120:9 | +| FloatOps(double, double) -> void | 0 | 34 | VariableAddress[y] | ir.cpp:120:13:120:13 | +| FloatOps(double, double) -> void | 0 | 35 | Load | ir.cpp:120:13:120:13 | +| FloatOps(double, double) -> void | 0 | 36 | Div | ir.cpp:120:9:120:13 | +| FloatOps(double, double) -> void | 0 | 37 | VariableAddress[z] | ir.cpp:120:5:120:5 | +| FloatOps(double, double) -> void | 0 | 38 | Store | ir.cpp:120:5:120:13 | +| FloatOps(double, double) -> void | 0 | 39 | VariableAddress[x] | ir.cpp:122:9:122:9 | +| FloatOps(double, double) -> void | 0 | 40 | Load | ir.cpp:122:9:122:9 | +| FloatOps(double, double) -> void | 0 | 41 | VariableAddress[z] | ir.cpp:122:5:122:5 | +| FloatOps(double, double) -> void | 0 | 42 | Store | ir.cpp:122:5:122:9 | +| FloatOps(double, double) -> void | 0 | 43 | VariableAddress[x] | ir.cpp:124:10:124:10 | +| FloatOps(double, double) -> void | 0 | 44 | Load | ir.cpp:124:10:124:10 | +| FloatOps(double, double) -> void | 0 | 45 | VariableAddress[z] | ir.cpp:124:5:124:5 | +| FloatOps(double, double) -> void | 0 | 46 | Load | ir.cpp:124:5:124:10 | +| FloatOps(double, double) -> void | 0 | 47 | Add | ir.cpp:124:5:124:10 | +| FloatOps(double, double) -> void | 0 | 48 | Store | ir.cpp:124:5:124:10 | +| FloatOps(double, double) -> void | 0 | 49 | VariableAddress[x] | ir.cpp:125:10:125:10 | +| FloatOps(double, double) -> void | 0 | 50 | Load | ir.cpp:125:10:125:10 | +| FloatOps(double, double) -> void | 0 | 51 | VariableAddress[z] | ir.cpp:125:5:125:5 | +| FloatOps(double, double) -> void | 0 | 52 | Load | ir.cpp:125:5:125:10 | +| FloatOps(double, double) -> void | 0 | 53 | Sub | ir.cpp:125:5:125:10 | +| FloatOps(double, double) -> void | 0 | 54 | Store | ir.cpp:125:5:125:10 | +| FloatOps(double, double) -> void | 0 | 55 | VariableAddress[x] | ir.cpp:126:10:126:10 | +| FloatOps(double, double) -> void | 0 | 56 | Load | ir.cpp:126:10:126:10 | +| FloatOps(double, double) -> void | 0 | 57 | VariableAddress[z] | ir.cpp:126:5:126:5 | +| FloatOps(double, double) -> void | 0 | 58 | Load | ir.cpp:126:5:126:10 | +| FloatOps(double, double) -> void | 0 | 59 | Mul | ir.cpp:126:5:126:10 | +| FloatOps(double, double) -> void | 0 | 60 | Store | ir.cpp:126:5:126:10 | +| FloatOps(double, double) -> void | 0 | 61 | VariableAddress[x] | ir.cpp:127:10:127:10 | +| FloatOps(double, double) -> void | 0 | 62 | Load | ir.cpp:127:10:127:10 | +| FloatOps(double, double) -> void | 0 | 63 | VariableAddress[z] | ir.cpp:127:5:127:5 | +| FloatOps(double, double) -> void | 0 | 64 | Load | ir.cpp:127:5:127:10 | +| FloatOps(double, double) -> void | 0 | 65 | Div | ir.cpp:127:5:127:10 | +| FloatOps(double, double) -> void | 0 | 66 | Store | ir.cpp:127:5:127:10 | +| FloatOps(double, double) -> void | 0 | 67 | VariableAddress[x] | ir.cpp:129:10:129:10 | +| FloatOps(double, double) -> void | 0 | 68 | Load | ir.cpp:129:10:129:10 | +| FloatOps(double, double) -> void | 0 | 69 | CopyValue | ir.cpp:129:9:129:10 | +| FloatOps(double, double) -> void | 0 | 70 | VariableAddress[z] | ir.cpp:129:5:129:5 | +| FloatOps(double, double) -> void | 0 | 71 | Store | ir.cpp:129:5:129:10 | +| FloatOps(double, double) -> void | 0 | 72 | VariableAddress[x] | ir.cpp:130:10:130:10 | +| FloatOps(double, double) -> void | 0 | 73 | Load | ir.cpp:130:10:130:10 | +| FloatOps(double, double) -> void | 0 | 74 | Negate | ir.cpp:130:9:130:10 | +| FloatOps(double, double) -> void | 0 | 75 | VariableAddress[z] | ir.cpp:130:5:130:5 | +| FloatOps(double, double) -> void | 0 | 76 | Store | ir.cpp:130:5:130:10 | +| FloatOps(double, double) -> void | 0 | 77 | NoOp | ir.cpp:131:1:131:1 | +| FloatOps(double, double) -> void | 0 | 78 | ReturnVoid | ir.cpp:114:6:114:13 | +| FloatOps(double, double) -> void | 0 | 79 | UnmodeledUse | ir.cpp:114:6:114:13 | +| FloatOps(double, double) -> void | 0 | 80 | ExitFunction | ir.cpp:114:6:114:13 | +| Foo() -> void | 0 | 0 | EnterFunction | ir.cpp:43:6:43:8 | +| Foo() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:43:6:43:8 | +| Foo() -> void | 0 | 2 | VariableAddress[x] | ir.cpp:44:9:44:9 | +| Foo() -> void | 0 | 3 | Constant[17] | ir.cpp:44:13:44:18 | +| Foo() -> void | 0 | 4 | Store | ir.cpp:44:13:44:18 | +| Foo() -> void | 0 | 5 | VariableAddress[y] | ir.cpp:45:11:45:11 | +| Foo() -> void | 0 | 6 | Constant[7] | ir.cpp:45:15:45:15 | +| Foo() -> void | 0 | 7 | Store | ir.cpp:45:15:45:15 | +| Foo() -> void | 0 | 8 | VariableAddress[x] | ir.cpp:46:9:46:9 | +| Foo() -> void | 0 | 9 | Load | ir.cpp:46:9:46:9 | +| Foo() -> void | 0 | 10 | VariableAddress[y] | ir.cpp:46:13:46:13 | +| Foo() -> void | 0 | 11 | Load | ir.cpp:46:13:46:13 | +| Foo() -> void | 0 | 12 | Convert | ir.cpp:46:13:46:13 | +| Foo() -> void | 0 | 13 | Add | ir.cpp:46:9:46:13 | +| Foo() -> void | 0 | 14 | Convert | ir.cpp:46:9:46:13 | +| Foo() -> void | 0 | 15 | VariableAddress[y] | ir.cpp:46:5:46:5 | +| Foo() -> void | 0 | 16 | Store | ir.cpp:46:5:46:13 | +| Foo() -> void | 0 | 17 | VariableAddress[x] | ir.cpp:47:9:47:9 | +| Foo() -> void | 0 | 18 | Load | ir.cpp:47:9:47:9 | +| Foo() -> void | 0 | 19 | VariableAddress[y] | ir.cpp:47:13:47:13 | +| Foo() -> void | 0 | 20 | Load | ir.cpp:47:13:47:13 | +| Foo() -> void | 0 | 21 | Convert | ir.cpp:47:13:47:13 | +| Foo() -> void | 0 | 22 | Mul | ir.cpp:47:9:47:13 | +| Foo() -> void | 0 | 23 | VariableAddress[x] | ir.cpp:47:5:47:5 | +| Foo() -> void | 0 | 24 | Store | ir.cpp:47:5:47:13 | +| Foo() -> void | 0 | 25 | NoOp | ir.cpp:48:1:48:1 | +| Foo() -> void | 0 | 26 | ReturnVoid | ir.cpp:43:6:43:8 | +| Foo() -> void | 0 | 27 | UnmodeledUse | ir.cpp:43:6:43:8 | +| Foo() -> void | 0 | 28 | ExitFunction | ir.cpp:43:6:43:8 | +| For_Break() -> void | 0 | 0 | EnterFunction | ir.cpp:317:6:317:14 | +| For_Break() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:317:6:317:14 | +| For_Break() -> void | 0 | 2 | VariableAddress[i] | ir.cpp:318:14:318:14 | +| For_Break() -> void | 0 | 3 | Constant[0] | ir.cpp:318:17:318:18 | +| For_Break() -> void | 0 | 4 | Store | ir.cpp:318:17:318:18 | +| For_Break() -> void | 1 | 0 | VariableAddress[i] | ir.cpp:318:21:318:21 | +| For_Break() -> void | 1 | 1 | Load | ir.cpp:318:21:318:21 | +| For_Break() -> void | 1 | 2 | Constant[10] | ir.cpp:318:25:318:26 | +| For_Break() -> void | 1 | 3 | CompareLT | ir.cpp:318:21:318:26 | +| For_Break() -> void | 1 | 4 | ConditionalBranch | ir.cpp:318:21:318:26 | +| For_Break() -> void | 2 | 0 | Constant[1] | ir.cpp:318:34:318:34 | +| For_Break() -> void | 2 | 1 | VariableAddress[i] | ir.cpp:318:29:318:29 | +| For_Break() -> void | 2 | 2 | Load | ir.cpp:318:29:318:34 | +| For_Break() -> void | 2 | 3 | Add | ir.cpp:318:29:318:34 | +| For_Break() -> void | 2 | 4 | Store | ir.cpp:318:29:318:34 | +| For_Break() -> void | 3 | 0 | VariableAddress[i] | ir.cpp:319:13:319:13 | +| For_Break() -> void | 3 | 1 | Load | ir.cpp:319:13:319:13 | +| For_Break() -> void | 3 | 2 | Constant[5] | ir.cpp:319:18:319:18 | +| For_Break() -> void | 3 | 3 | CompareEQ | ir.cpp:319:13:319:18 | +| For_Break() -> void | 3 | 4 | ConditionalBranch | ir.cpp:319:13:319:18 | +| For_Break() -> void | 4 | 0 | NoOp | ir.cpp:320:13:320:18 | +| For_Break() -> void | 5 | 0 | NoOp | ir.cpp:322:5:322:5 | +| For_Break() -> void | 5 | 1 | NoOp | ir.cpp:323:1:323:1 | +| For_Break() -> void | 5 | 2 | ReturnVoid | ir.cpp:317:6:317:14 | +| For_Break() -> void | 5 | 3 | UnmodeledUse | ir.cpp:317:6:317:14 | +| For_Break() -> void | 5 | 4 | ExitFunction | ir.cpp:317:6:317:14 | +| For_Condition() -> void | 0 | 0 | EnterFunction | ir.cpp:278:6:278:18 | +| For_Condition() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:278:6:278:18 | +| For_Condition() -> void | 0 | 2 | VariableAddress[i] | ir.cpp:279:9:279:9 | +| For_Condition() -> void | 0 | 3 | Constant[0] | ir.cpp:279:12:279:13 | +| For_Condition() -> void | 0 | 4 | Store | ir.cpp:279:12:279:13 | +| For_Condition() -> void | 1 | 0 | VariableAddress[i] | ir.cpp:280:12:280:12 | +| For_Condition() -> void | 1 | 1 | Load | ir.cpp:280:12:280:12 | +| For_Condition() -> void | 1 | 2 | Constant[10] | ir.cpp:280:16:280:17 | +| For_Condition() -> void | 1 | 3 | CompareLT | ir.cpp:280:12:280:17 | +| For_Condition() -> void | 1 | 4 | ConditionalBranch | ir.cpp:280:12:280:17 | +| For_Condition() -> void | 2 | 0 | NoOp | ir.cpp:281:9:281:9 | +| For_Condition() -> void | 3 | 0 | NoOp | ir.cpp:283:1:283:1 | +| For_Condition() -> void | 3 | 1 | ReturnVoid | ir.cpp:278:6:278:18 | +| For_Condition() -> void | 3 | 2 | UnmodeledUse | ir.cpp:278:6:278:18 | +| For_Condition() -> void | 3 | 3 | ExitFunction | ir.cpp:278:6:278:18 | +| For_ConditionUpdate() -> void | 0 | 0 | EnterFunction | ir.cpp:304:6:304:24 | +| For_ConditionUpdate() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:304:6:304:24 | +| For_ConditionUpdate() -> void | 0 | 2 | VariableAddress[i] | ir.cpp:305:9:305:9 | +| For_ConditionUpdate() -> void | 0 | 3 | Constant[0] | ir.cpp:305:12:305:13 | +| For_ConditionUpdate() -> void | 0 | 4 | Store | ir.cpp:305:12:305:13 | +| For_ConditionUpdate() -> void | 1 | 0 | VariableAddress[i] | ir.cpp:306:12:306:12 | +| For_ConditionUpdate() -> void | 1 | 1 | Load | ir.cpp:306:12:306:12 | +| For_ConditionUpdate() -> void | 1 | 2 | Constant[10] | ir.cpp:306:16:306:17 | +| For_ConditionUpdate() -> void | 1 | 3 | CompareLT | ir.cpp:306:12:306:17 | +| For_ConditionUpdate() -> void | 1 | 4 | ConditionalBranch | ir.cpp:306:12:306:17 | +| For_ConditionUpdate() -> void | 2 | 0 | NoOp | ir.cpp:307:9:307:9 | +| For_ConditionUpdate() -> void | 2 | 1 | Constant[1] | ir.cpp:306:25:306:25 | +| For_ConditionUpdate() -> void | 2 | 2 | VariableAddress[i] | ir.cpp:306:20:306:20 | +| For_ConditionUpdate() -> void | 2 | 3 | Load | ir.cpp:306:20:306:25 | +| For_ConditionUpdate() -> void | 2 | 4 | Add | ir.cpp:306:20:306:25 | +| For_ConditionUpdate() -> void | 2 | 5 | Store | ir.cpp:306:20:306:25 | +| For_ConditionUpdate() -> void | 3 | 0 | NoOp | ir.cpp:309:1:309:1 | +| For_ConditionUpdate() -> void | 3 | 1 | ReturnVoid | ir.cpp:304:6:304:24 | +| For_ConditionUpdate() -> void | 3 | 2 | UnmodeledUse | ir.cpp:304:6:304:24 | +| For_ConditionUpdate() -> void | 3 | 3 | ExitFunction | ir.cpp:304:6:304:24 | +| For_Continue_NoUpdate() -> void | 0 | 0 | EnterFunction | ir.cpp:333:6:333:26 | +| For_Continue_NoUpdate() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:333:6:333:26 | +| For_Continue_NoUpdate() -> void | 0 | 2 | VariableAddress[i] | ir.cpp:334:14:334:14 | +| For_Continue_NoUpdate() -> void | 0 | 3 | Constant[0] | ir.cpp:334:17:334:18 | +| For_Continue_NoUpdate() -> void | 0 | 4 | Store | ir.cpp:334:17:334:18 | +| For_Continue_NoUpdate() -> void | 1 | 0 | VariableAddress[i] | ir.cpp:334:21:334:21 | +| For_Continue_NoUpdate() -> void | 1 | 1 | Load | ir.cpp:334:21:334:21 | +| For_Continue_NoUpdate() -> void | 1 | 2 | Constant[10] | ir.cpp:334:25:334:26 | +| For_Continue_NoUpdate() -> void | 1 | 3 | CompareLT | ir.cpp:334:21:334:26 | +| For_Continue_NoUpdate() -> void | 1 | 4 | ConditionalBranch | ir.cpp:334:21:334:26 | +| For_Continue_NoUpdate() -> void | 2 | 0 | VariableAddress[i] | ir.cpp:335:13:335:13 | +| For_Continue_NoUpdate() -> void | 2 | 1 | Load | ir.cpp:335:13:335:13 | +| For_Continue_NoUpdate() -> void | 2 | 2 | Constant[5] | ir.cpp:335:18:335:18 | +| For_Continue_NoUpdate() -> void | 2 | 3 | CompareEQ | ir.cpp:335:13:335:18 | +| For_Continue_NoUpdate() -> void | 2 | 4 | ConditionalBranch | ir.cpp:335:13:335:18 | +| For_Continue_NoUpdate() -> void | 3 | 0 | NoOp | ir.cpp:336:13:336:21 | +| For_Continue_NoUpdate() -> void | 4 | 0 | NoOp | ir.cpp:334:5:334:5 | +| For_Continue_NoUpdate() -> void | 5 | 0 | NoOp | ir.cpp:339:1:339:1 | +| For_Continue_NoUpdate() -> void | 5 | 1 | ReturnVoid | ir.cpp:333:6:333:26 | +| For_Continue_NoUpdate() -> void | 5 | 2 | UnmodeledUse | ir.cpp:333:6:333:26 | +| For_Continue_NoUpdate() -> void | 5 | 3 | ExitFunction | ir.cpp:333:6:333:26 | +| For_Continue_Update() -> void | 0 | 0 | EnterFunction | ir.cpp:325:6:325:24 | +| For_Continue_Update() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:325:6:325:24 | +| For_Continue_Update() -> void | 0 | 2 | VariableAddress[i] | ir.cpp:326:14:326:14 | +| For_Continue_Update() -> void | 0 | 3 | Constant[0] | ir.cpp:326:17:326:18 | +| For_Continue_Update() -> void | 0 | 4 | Store | ir.cpp:326:17:326:18 | +| For_Continue_Update() -> void | 1 | 0 | VariableAddress[i] | ir.cpp:326:21:326:21 | +| For_Continue_Update() -> void | 1 | 1 | Load | ir.cpp:326:21:326:21 | +| For_Continue_Update() -> void | 1 | 2 | Constant[10] | ir.cpp:326:25:326:26 | +| For_Continue_Update() -> void | 1 | 3 | CompareLT | ir.cpp:326:21:326:26 | +| For_Continue_Update() -> void | 1 | 4 | ConditionalBranch | ir.cpp:326:21:326:26 | +| For_Continue_Update() -> void | 2 | 0 | VariableAddress[i] | ir.cpp:327:13:327:13 | +| For_Continue_Update() -> void | 2 | 1 | Load | ir.cpp:327:13:327:13 | +| For_Continue_Update() -> void | 2 | 2 | Constant[5] | ir.cpp:327:18:327:18 | +| For_Continue_Update() -> void | 2 | 3 | CompareEQ | ir.cpp:327:13:327:18 | +| For_Continue_Update() -> void | 2 | 4 | ConditionalBranch | ir.cpp:327:13:327:18 | +| For_Continue_Update() -> void | 3 | 0 | NoOp | ir.cpp:328:13:328:21 | +| For_Continue_Update() -> void | 4 | 0 | NoOp | ir.cpp:326:5:326:5 | +| For_Continue_Update() -> void | 4 | 1 | Constant[1] | ir.cpp:326:34:326:34 | +| For_Continue_Update() -> void | 4 | 2 | VariableAddress[i] | ir.cpp:326:29:326:29 | +| For_Continue_Update() -> void | 4 | 3 | Load | ir.cpp:326:29:326:34 | +| For_Continue_Update() -> void | 4 | 4 | Add | ir.cpp:326:29:326:34 | +| For_Continue_Update() -> void | 4 | 5 | Store | ir.cpp:326:29:326:34 | +| For_Continue_Update() -> void | 5 | 0 | NoOp | ir.cpp:331:1:331:1 | +| For_Continue_Update() -> void | 5 | 1 | ReturnVoid | ir.cpp:325:6:325:24 | +| For_Continue_Update() -> void | 5 | 2 | UnmodeledUse | ir.cpp:325:6:325:24 | +| For_Continue_Update() -> void | 5 | 3 | ExitFunction | ir.cpp:325:6:325:24 | +| For_Empty() -> void | 0 | 0 | EnterFunction | ir.cpp:265:6:265:14 | +| For_Empty() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:265:6:265:14 | +| For_Empty() -> void | 0 | 2 | VariableAddress[j] | ir.cpp:266:9:266:9 | +| For_Empty() -> void | 0 | 3 | Uninitialized | ir.cpp:266:9:266:9 | +| For_Empty() -> void | 0 | 4 | Store | ir.cpp:266:9:266:9 | +| For_Empty() -> void | 1 | 0 | ReturnVoid | ir.cpp:265:6:265:14 | +| For_Empty() -> void | 1 | 1 | UnmodeledUse | ir.cpp:265:6:265:14 | +| For_Empty() -> void | 1 | 2 | ExitFunction | ir.cpp:265:6:265:14 | +| For_Empty() -> void | 2 | 0 | NoOp | ir.cpp:268:9:268:9 | +| For_Init() -> void | 0 | 0 | EnterFunction | ir.cpp:272:6:272:13 | +| For_Init() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:272:6:272:13 | +| For_Init() -> void | 0 | 2 | VariableAddress[i] | ir.cpp:273:14:273:14 | +| For_Init() -> void | 0 | 3 | Constant[0] | ir.cpp:273:17:273:18 | +| For_Init() -> void | 0 | 4 | Store | ir.cpp:273:17:273:18 | +| For_Init() -> void | 1 | 0 | ReturnVoid | ir.cpp:272:6:272:13 | +| For_Init() -> void | 1 | 1 | UnmodeledUse | ir.cpp:272:6:272:13 | +| For_Init() -> void | 1 | 2 | ExitFunction | ir.cpp:272:6:272:13 | +| For_Init() -> void | 2 | 0 | NoOp | ir.cpp:274:9:274:9 | +| For_InitCondition() -> void | 0 | 0 | EnterFunction | ir.cpp:292:6:292:22 | +| For_InitCondition() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:292:6:292:22 | +| For_InitCondition() -> void | 0 | 2 | VariableAddress[i] | ir.cpp:293:14:293:14 | +| For_InitCondition() -> void | 0 | 3 | Constant[0] | ir.cpp:293:17:293:18 | +| For_InitCondition() -> void | 0 | 4 | Store | ir.cpp:293:17:293:18 | +| For_InitCondition() -> void | 1 | 0 | VariableAddress[i] | ir.cpp:293:21:293:21 | +| For_InitCondition() -> void | 1 | 1 | Load | ir.cpp:293:21:293:21 | +| For_InitCondition() -> void | 1 | 2 | Constant[10] | ir.cpp:293:25:293:26 | +| For_InitCondition() -> void | 1 | 3 | CompareLT | ir.cpp:293:21:293:26 | +| For_InitCondition() -> void | 1 | 4 | ConditionalBranch | ir.cpp:293:21:293:26 | +| For_InitCondition() -> void | 2 | 0 | NoOp | ir.cpp:294:9:294:9 | +| For_InitCondition() -> void | 3 | 0 | NoOp | ir.cpp:296:1:296:1 | +| For_InitCondition() -> void | 3 | 1 | ReturnVoid | ir.cpp:292:6:292:22 | +| For_InitCondition() -> void | 3 | 2 | UnmodeledUse | ir.cpp:292:6:292:22 | +| For_InitCondition() -> void | 3 | 3 | ExitFunction | ir.cpp:292:6:292:22 | +| For_InitConditionUpdate() -> void | 0 | 0 | EnterFunction | ir.cpp:311:6:311:28 | +| For_InitConditionUpdate() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:311:6:311:28 | +| For_InitConditionUpdate() -> void | 0 | 2 | VariableAddress[i] | ir.cpp:312:14:312:14 | +| For_InitConditionUpdate() -> void | 0 | 3 | Constant[0] | ir.cpp:312:17:312:18 | +| For_InitConditionUpdate() -> void | 0 | 4 | Store | ir.cpp:312:17:312:18 | +| For_InitConditionUpdate() -> void | 1 | 0 | VariableAddress[i] | ir.cpp:312:21:312:21 | +| For_InitConditionUpdate() -> void | 1 | 1 | Load | ir.cpp:312:21:312:21 | +| For_InitConditionUpdate() -> void | 1 | 2 | Constant[10] | ir.cpp:312:25:312:26 | +| For_InitConditionUpdate() -> void | 1 | 3 | CompareLT | ir.cpp:312:21:312:26 | +| For_InitConditionUpdate() -> void | 1 | 4 | ConditionalBranch | ir.cpp:312:21:312:26 | +| For_InitConditionUpdate() -> void | 2 | 0 | NoOp | ir.cpp:313:9:313:9 | +| For_InitConditionUpdate() -> void | 2 | 1 | Constant[1] | ir.cpp:312:34:312:34 | +| For_InitConditionUpdate() -> void | 2 | 2 | VariableAddress[i] | ir.cpp:312:29:312:29 | +| For_InitConditionUpdate() -> void | 2 | 3 | Load | ir.cpp:312:29:312:34 | +| For_InitConditionUpdate() -> void | 2 | 4 | Add | ir.cpp:312:29:312:34 | +| For_InitConditionUpdate() -> void | 2 | 5 | Store | ir.cpp:312:29:312:34 | +| For_InitConditionUpdate() -> void | 3 | 0 | NoOp | ir.cpp:315:1:315:1 | +| For_InitConditionUpdate() -> void | 3 | 1 | ReturnVoid | ir.cpp:311:6:311:28 | +| For_InitConditionUpdate() -> void | 3 | 2 | UnmodeledUse | ir.cpp:311:6:311:28 | +| For_InitConditionUpdate() -> void | 3 | 3 | ExitFunction | ir.cpp:311:6:311:28 | +| For_InitUpdate() -> void | 0 | 0 | EnterFunction | ir.cpp:298:6:298:19 | +| For_InitUpdate() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:298:6:298:19 | +| For_InitUpdate() -> void | 0 | 2 | VariableAddress[i] | ir.cpp:299:14:299:14 | +| For_InitUpdate() -> void | 0 | 3 | Constant[0] | ir.cpp:299:17:299:18 | +| For_InitUpdate() -> void | 0 | 4 | Store | ir.cpp:299:17:299:18 | +| For_InitUpdate() -> void | 1 | 0 | ReturnVoid | ir.cpp:298:6:298:19 | +| For_InitUpdate() -> void | 1 | 1 | UnmodeledUse | ir.cpp:298:6:298:19 | +| For_InitUpdate() -> void | 1 | 2 | ExitFunction | ir.cpp:298:6:298:19 | +| For_InitUpdate() -> void | 2 | 0 | NoOp | ir.cpp:300:9:300:9 | +| For_InitUpdate() -> void | 2 | 1 | Constant[1] | ir.cpp:299:27:299:27 | +| For_InitUpdate() -> void | 2 | 2 | VariableAddress[i] | ir.cpp:299:22:299:22 | +| For_InitUpdate() -> void | 2 | 3 | Load | ir.cpp:299:22:299:27 | +| For_InitUpdate() -> void | 2 | 4 | Add | ir.cpp:299:22:299:27 | +| For_InitUpdate() -> void | 2 | 5 | Store | ir.cpp:299:22:299:27 | +| For_Update() -> void | 0 | 0 | EnterFunction | ir.cpp:285:6:285:15 | +| For_Update() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:285:6:285:15 | +| For_Update() -> void | 0 | 2 | VariableAddress[i] | ir.cpp:286:9:286:9 | +| For_Update() -> void | 0 | 3 | Constant[0] | ir.cpp:286:12:286:13 | +| For_Update() -> void | 0 | 4 | Store | ir.cpp:286:12:286:13 | +| For_Update() -> void | 1 | 0 | ReturnVoid | ir.cpp:285:6:285:15 | +| For_Update() -> void | 1 | 1 | UnmodeledUse | ir.cpp:285:6:285:15 | +| For_Update() -> void | 1 | 2 | ExitFunction | ir.cpp:285:6:285:15 | +| For_Update() -> void | 2 | 0 | NoOp | ir.cpp:288:9:288:9 | +| For_Update() -> void | 2 | 1 | Constant[1] | ir.cpp:287:18:287:18 | +| For_Update() -> void | 2 | 2 | VariableAddress[i] | ir.cpp:287:13:287:13 | +| For_Update() -> void | 2 | 3 | Load | ir.cpp:287:13:287:18 | +| For_Update() -> void | 2 | 4 | Add | ir.cpp:287:13:287:18 | +| For_Update() -> void | 2 | 5 | Store | ir.cpp:287:13:287:18 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 0 | EnterFunction | ir.cpp:883:6:883:23 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:883:6:883:23 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 2 | InitializeParameter[pfn] | ir.cpp:883:30:883:32 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 3 | VariableAddress[pfn] | ir.cpp:883:30:883:32 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 4 | Store | ir.cpp:883:30:883:32 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 5 | InitializeParameter[p] | ir.cpp:883:47:883:47 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 6 | VariableAddress[p] | ir.cpp:883:47:883:47 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 7 | Store | ir.cpp:883:47:883:47 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 8 | VariableAddress[pfn] | ir.cpp:884:14:884:16 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 9 | Load | ir.cpp:884:14:884:16 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 10 | Convert | ir.cpp:884:7:884:16 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 11 | VariableAddress[p] | ir.cpp:884:3:884:3 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 12 | Store | ir.cpp:884:3:884:16 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 13 | VariableAddress[p] | ir.cpp:885:22:885:22 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 14 | Load | ir.cpp:885:22:885:22 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 15 | Convert | ir.cpp:885:9:885:22 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 16 | VariableAddress[pfn] | ir.cpp:885:3:885:5 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 17 | Store | ir.cpp:885:3:885:22 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 18 | NoOp | ir.cpp:886:1:886:1 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 19 | ReturnVoid | ir.cpp:883:6:883:23 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 20 | UnmodeledUse | ir.cpp:883:6:883:23 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 21 | ExitFunction | ir.cpp:883:6:883:23 | +| FunctionReferences() -> void | 0 | 0 | EnterFunction | ir.cpp:697:6:697:23 | +| FunctionReferences() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:697:6:697:23 | +| FunctionReferences() -> void | 0 | 2 | VariableAddress[rfn] | ir.cpp:698:8:698:10 | +| FunctionReferences() -> void | 0 | 3 | FunctionAddress[FuncPtrTarget] | ir.cpp:698:20:698:32 | +| FunctionReferences() -> void | 0 | 4 | Store | ir.cpp:698:20:698:32 | +| FunctionReferences() -> void | 0 | 5 | VariableAddress[pfn] | ir.cpp:699:8:699:10 | +| FunctionReferences() -> void | 0 | 6 | VariableAddress[rfn] | ir.cpp:699:20:699:22 | +| FunctionReferences() -> void | 0 | 7 | Load | ir.cpp:699:20:699:22 | +| FunctionReferences() -> void | 0 | 8 | Store | ir.cpp:699:20:699:22 | +| FunctionReferences() -> void | 0 | 9 | VariableAddress[rfn] | ir.cpp:700:3:700:5 | +| FunctionReferences() -> void | 0 | 10 | Load | ir.cpp:700:3:700:5 | +| FunctionReferences() -> void | 0 | 11 | Constant[5] | ir.cpp:700:7:700:7 | +| FunctionReferences() -> void | 0 | 12 | Invoke | ir.cpp:700:3:700:8 | +| FunctionReferences() -> void | 0 | 13 | NoOp | ir.cpp:701:1:701:1 | +| FunctionReferences() -> void | 0 | 14 | ReturnVoid | ir.cpp:697:6:697:23 | +| FunctionReferences() -> void | 0 | 15 | UnmodeledUse | ir.cpp:697:6:697:23 | +| FunctionReferences() -> void | 0 | 16 | ExitFunction | ir.cpp:697:6:697:23 | +| HierarchyConversions() -> void | 0 | 0 | EnterFunction | ir.cpp:799:6:799:25 | +| HierarchyConversions() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:799:6:799:25 | +| HierarchyConversions() -> void | 0 | 2 | VariableAddress[b] | ir.cpp:800:8:800:8 | +| HierarchyConversions() -> void | 0 | 3 | FunctionAddress[Base] | ir.cpp:800:8:800:8 | +| HierarchyConversions() -> void | 0 | 4 | Invoke | ir.cpp:800:8:800:8 | +| HierarchyConversions() -> void | 0 | 5 | VariableAddress[m] | ir.cpp:801:10:801:10 | +| HierarchyConversions() -> void | 0 | 6 | FunctionAddress[Middle] | ir.cpp:801:10:801:10 | +| HierarchyConversions() -> void | 0 | 7 | Invoke | ir.cpp:801:10:801:10 | +| HierarchyConversions() -> void | 0 | 8 | VariableAddress[d] | ir.cpp:802:11:802:11 | +| HierarchyConversions() -> void | 0 | 9 | FunctionAddress[Derived] | ir.cpp:802:11:802:11 | +| HierarchyConversions() -> void | 0 | 10 | Invoke | ir.cpp:802:11:802:11 | +| HierarchyConversions() -> void | 0 | 11 | VariableAddress[pb] | ir.cpp:804:9:804:10 | +| HierarchyConversions() -> void | 0 | 12 | VariableAddress[b] | ir.cpp:804:15:804:15 | +| HierarchyConversions() -> void | 0 | 13 | Store | ir.cpp:804:14:804:15 | +| HierarchyConversions() -> void | 0 | 14 | VariableAddress[pm] | ir.cpp:805:11:805:12 | +| HierarchyConversions() -> void | 0 | 15 | VariableAddress[m] | ir.cpp:805:17:805:17 | +| HierarchyConversions() -> void | 0 | 16 | Store | ir.cpp:805:16:805:17 | +| HierarchyConversions() -> void | 0 | 17 | VariableAddress[pd] | ir.cpp:806:12:806:13 | +| HierarchyConversions() -> void | 0 | 18 | VariableAddress[d] | ir.cpp:806:18:806:18 | +| HierarchyConversions() -> void | 0 | 19 | Store | ir.cpp:806:17:806:18 | +| HierarchyConversions() -> void | 0 | 20 | VariableAddress[b] | ir.cpp:808:3:808:3 | +| HierarchyConversions() -> void | 0 | 21 | FunctionAddress[operator=] | ir.cpp:808:5:808:5 | +| HierarchyConversions() -> void | 0 | 22 | VariableAddress[m] | ir.cpp:808:7:808:7 | +| HierarchyConversions() -> void | 0 | 23 | ConvertToBase[Middle : Base] | ir.cpp:808:7:808:7 | +| HierarchyConversions() -> void | 0 | 24 | Invoke | ir.cpp:808:5:808:5 | +| HierarchyConversions() -> void | 0 | 25 | VariableAddress[b] | ir.cpp:809:3:809:3 | +| HierarchyConversions() -> void | 0 | 26 | FunctionAddress[operator=] | ir.cpp:809:5:809:5 | +| HierarchyConversions() -> void | 0 | 27 | FunctionAddress[Base] | ir.cpp:809:7:809:13 | +| HierarchyConversions() -> void | 0 | 28 | VariableAddress[m] | ir.cpp:809:13:809:13 | +| HierarchyConversions() -> void | 0 | 29 | ConvertToBase[Middle : Base] | ir.cpp:809:13:809:13 | +| HierarchyConversions() -> void | 0 | 30 | Invoke | ir.cpp:809:7:809:13 | +| HierarchyConversions() -> void | 0 | 31 | Convert | ir.cpp:809:7:809:13 | +| HierarchyConversions() -> void | 0 | 32 | Invoke | ir.cpp:809:5:809:5 | +| HierarchyConversions() -> void | 0 | 33 | VariableAddress[b] | ir.cpp:810:3:810:3 | +| HierarchyConversions() -> void | 0 | 34 | FunctionAddress[operator=] | ir.cpp:810:5:810:5 | +| HierarchyConversions() -> void | 0 | 35 | FunctionAddress[Base] | ir.cpp:810:7:810:26 | +| HierarchyConversions() -> void | 0 | 36 | VariableAddress[m] | ir.cpp:810:25:810:25 | +| HierarchyConversions() -> void | 0 | 37 | ConvertToBase[Middle : Base] | ir.cpp:810:25:810:25 | +| HierarchyConversions() -> void | 0 | 38 | Invoke | ir.cpp:810:7:810:26 | +| HierarchyConversions() -> void | 0 | 39 | Convert | ir.cpp:810:7:810:26 | +| HierarchyConversions() -> void | 0 | 40 | Invoke | ir.cpp:810:5:810:5 | +| HierarchyConversions() -> void | 0 | 41 | VariableAddress[pm] | ir.cpp:811:8:811:9 | +| HierarchyConversions() -> void | 0 | 42 | Load | ir.cpp:811:8:811:9 | +| HierarchyConversions() -> void | 0 | 43 | ConvertToBase[Middle : Base] | ir.cpp:811:8:811:9 | +| HierarchyConversions() -> void | 0 | 44 | VariableAddress[pb] | ir.cpp:811:3:811:4 | +| HierarchyConversions() -> void | 0 | 45 | Store | ir.cpp:811:3:811:9 | +| HierarchyConversions() -> void | 0 | 46 | VariableAddress[pm] | ir.cpp:812:15:812:16 | +| HierarchyConversions() -> void | 0 | 47 | Load | ir.cpp:812:15:812:16 | +| HierarchyConversions() -> void | 0 | 48 | ConvertToBase[Middle : Base] | ir.cpp:812:8:812:16 | +| HierarchyConversions() -> void | 0 | 49 | VariableAddress[pb] | ir.cpp:812:3:812:4 | +| HierarchyConversions() -> void | 0 | 50 | Store | ir.cpp:812:3:812:16 | +| HierarchyConversions() -> void | 0 | 51 | VariableAddress[pm] | ir.cpp:813:27:813:28 | +| HierarchyConversions() -> void | 0 | 52 | Load | ir.cpp:813:27:813:28 | +| HierarchyConversions() -> void | 0 | 53 | ConvertToBase[Middle : Base] | ir.cpp:813:8:813:29 | +| HierarchyConversions() -> void | 0 | 54 | VariableAddress[pb] | ir.cpp:813:3:813:4 | +| HierarchyConversions() -> void | 0 | 55 | Store | ir.cpp:813:3:813:29 | +| HierarchyConversions() -> void | 0 | 56 | VariableAddress[pm] | ir.cpp:814:32:814:33 | +| HierarchyConversions() -> void | 0 | 57 | Load | ir.cpp:814:32:814:33 | +| HierarchyConversions() -> void | 0 | 58 | Convert | ir.cpp:814:8:814:34 | +| HierarchyConversions() -> void | 0 | 59 | VariableAddress[pb] | ir.cpp:814:3:814:4 | +| HierarchyConversions() -> void | 0 | 60 | Store | ir.cpp:814:3:814:34 | +| HierarchyConversions() -> void | 0 | 61 | VariableAddress[m] | ir.cpp:816:3:816:3 | +| HierarchyConversions() -> void | 0 | 62 | FunctionAddress[operator=] | ir.cpp:816:5:816:5 | +| HierarchyConversions() -> void | 0 | 63 | VariableAddress[b] | ir.cpp:816:16:816:16 | +| HierarchyConversions() -> void | 0 | 64 | ConvertToDerived[Middle : Base] | ir.cpp:816:7:816:16 | +| HierarchyConversions() -> void | 0 | 65 | Convert | ir.cpp:816:7:816:16 | +| HierarchyConversions() -> void | 0 | 66 | Invoke | ir.cpp:816:5:816:5 | +| HierarchyConversions() -> void | 0 | 67 | VariableAddress[m] | ir.cpp:817:3:817:3 | +| HierarchyConversions() -> void | 0 | 68 | FunctionAddress[operator=] | ir.cpp:817:5:817:5 | +| HierarchyConversions() -> void | 0 | 69 | VariableAddress[b] | ir.cpp:817:28:817:28 | +| HierarchyConversions() -> void | 0 | 70 | ConvertToDerived[Middle : Base] | ir.cpp:817:7:817:29 | +| HierarchyConversions() -> void | 0 | 71 | Convert | ir.cpp:817:7:817:29 | +| HierarchyConversions() -> void | 0 | 72 | Invoke | ir.cpp:817:5:817:5 | +| HierarchyConversions() -> void | 0 | 73 | VariableAddress[pb] | ir.cpp:818:17:818:18 | +| HierarchyConversions() -> void | 0 | 74 | Load | ir.cpp:818:17:818:18 | +| HierarchyConversions() -> void | 0 | 75 | ConvertToDerived[Middle : Base] | ir.cpp:818:8:818:18 | +| HierarchyConversions() -> void | 0 | 76 | VariableAddress[pm] | ir.cpp:818:3:818:4 | +| HierarchyConversions() -> void | 0 | 77 | Store | ir.cpp:818:3:818:18 | +| HierarchyConversions() -> void | 0 | 78 | VariableAddress[pb] | ir.cpp:819:29:819:30 | +| HierarchyConversions() -> void | 0 | 79 | Load | ir.cpp:819:29:819:30 | +| HierarchyConversions() -> void | 0 | 80 | ConvertToDerived[Middle : Base] | ir.cpp:819:8:819:31 | +| HierarchyConversions() -> void | 0 | 81 | VariableAddress[pm] | ir.cpp:819:3:819:4 | +| HierarchyConversions() -> void | 0 | 82 | Store | ir.cpp:819:3:819:31 | +| HierarchyConversions() -> void | 0 | 83 | VariableAddress[pb] | ir.cpp:820:34:820:35 | +| HierarchyConversions() -> void | 0 | 84 | Load | ir.cpp:820:34:820:35 | +| HierarchyConversions() -> void | 0 | 85 | Convert | ir.cpp:820:8:820:36 | +| HierarchyConversions() -> void | 0 | 86 | VariableAddress[pm] | ir.cpp:820:3:820:4 | +| HierarchyConversions() -> void | 0 | 87 | Store | ir.cpp:820:3:820:36 | +| HierarchyConversions() -> void | 0 | 88 | VariableAddress[b] | ir.cpp:822:3:822:3 | +| HierarchyConversions() -> void | 0 | 89 | FunctionAddress[operator=] | ir.cpp:822:5:822:5 | +| HierarchyConversions() -> void | 0 | 90 | VariableAddress[d] | ir.cpp:822:7:822:7 | +| HierarchyConversions() -> void | 0 | 91 | ConvertToBase[Derived : Middle] | ir.cpp:822:7:822:7 | +| HierarchyConversions() -> void | 0 | 92 | ConvertToBase[Middle : Base] | ir.cpp:822:7:822:7 | +| HierarchyConversions() -> void | 0 | 93 | Invoke | ir.cpp:822:5:822:5 | +| HierarchyConversions() -> void | 0 | 94 | VariableAddress[b] | ir.cpp:823:3:823:3 | +| HierarchyConversions() -> void | 0 | 95 | FunctionAddress[operator=] | ir.cpp:823:5:823:5 | +| HierarchyConversions() -> void | 0 | 96 | FunctionAddress[Base] | ir.cpp:823:7:823:13 | +| HierarchyConversions() -> void | 0 | 97 | VariableAddress[d] | ir.cpp:823:13:823:13 | +| HierarchyConversions() -> void | 0 | 98 | ConvertToBase[Derived : Middle] | ir.cpp:823:13:823:13 | +| HierarchyConversions() -> void | 0 | 99 | ConvertToBase[Middle : Base] | ir.cpp:823:13:823:13 | +| HierarchyConversions() -> void | 0 | 100 | Invoke | ir.cpp:823:7:823:13 | +| HierarchyConversions() -> void | 0 | 101 | Convert | ir.cpp:823:7:823:13 | +| HierarchyConversions() -> void | 0 | 102 | Invoke | ir.cpp:823:5:823:5 | +| HierarchyConversions() -> void | 0 | 103 | VariableAddress[b] | ir.cpp:824:3:824:3 | +| HierarchyConversions() -> void | 0 | 104 | FunctionAddress[operator=] | ir.cpp:824:5:824:5 | +| HierarchyConversions() -> void | 0 | 105 | FunctionAddress[Base] | ir.cpp:824:7:824:26 | +| HierarchyConversions() -> void | 0 | 106 | VariableAddress[d] | ir.cpp:824:25:824:25 | +| HierarchyConversions() -> void | 0 | 107 | ConvertToBase[Derived : Middle] | ir.cpp:824:25:824:25 | +| HierarchyConversions() -> void | 0 | 108 | ConvertToBase[Middle : Base] | ir.cpp:824:25:824:25 | +| HierarchyConversions() -> void | 0 | 109 | Invoke | ir.cpp:824:7:824:26 | +| HierarchyConversions() -> void | 0 | 110 | Convert | ir.cpp:824:7:824:26 | +| HierarchyConversions() -> void | 0 | 111 | Invoke | ir.cpp:824:5:824:5 | +| HierarchyConversions() -> void | 0 | 112 | VariableAddress[pd] | ir.cpp:825:8:825:9 | +| HierarchyConversions() -> void | 0 | 113 | Load | ir.cpp:825:8:825:9 | +| HierarchyConversions() -> void | 0 | 114 | ConvertToBase[Derived : Middle] | ir.cpp:825:8:825:9 | +| HierarchyConversions() -> void | 0 | 115 | ConvertToBase[Middle : Base] | ir.cpp:825:8:825:9 | +| HierarchyConversions() -> void | 0 | 116 | VariableAddress[pb] | ir.cpp:825:3:825:4 | +| HierarchyConversions() -> void | 0 | 117 | Store | ir.cpp:825:3:825:9 | +| HierarchyConversions() -> void | 0 | 118 | VariableAddress[pd] | ir.cpp:826:15:826:16 | +| HierarchyConversions() -> void | 0 | 119 | Load | ir.cpp:826:15:826:16 | +| HierarchyConversions() -> void | 0 | 120 | ConvertToBase[Derived : Middle] | ir.cpp:826:15:826:16 | +| HierarchyConversions() -> void | 0 | 121 | ConvertToBase[Middle : Base] | ir.cpp:826:8:826:16 | +| HierarchyConversions() -> void | 0 | 122 | VariableAddress[pb] | ir.cpp:826:3:826:4 | +| HierarchyConversions() -> void | 0 | 123 | Store | ir.cpp:826:3:826:16 | +| HierarchyConversions() -> void | 0 | 124 | VariableAddress[pd] | ir.cpp:827:27:827:28 | +| HierarchyConversions() -> void | 0 | 125 | Load | ir.cpp:827:27:827:28 | +| HierarchyConversions() -> void | 0 | 126 | ConvertToBase[Derived : Middle] | ir.cpp:827:27:827:28 | +| HierarchyConversions() -> void | 0 | 127 | ConvertToBase[Middle : Base] | ir.cpp:827:8:827:29 | +| HierarchyConversions() -> void | 0 | 128 | VariableAddress[pb] | ir.cpp:827:3:827:4 | +| HierarchyConversions() -> void | 0 | 129 | Store | ir.cpp:827:3:827:29 | +| HierarchyConversions() -> void | 0 | 130 | VariableAddress[pd] | ir.cpp:828:32:828:33 | +| HierarchyConversions() -> void | 0 | 131 | Load | ir.cpp:828:32:828:33 | +| HierarchyConversions() -> void | 0 | 132 | Convert | ir.cpp:828:8:828:34 | +| HierarchyConversions() -> void | 0 | 133 | VariableAddress[pb] | ir.cpp:828:3:828:4 | +| HierarchyConversions() -> void | 0 | 134 | Store | ir.cpp:828:3:828:34 | +| HierarchyConversions() -> void | 0 | 135 | VariableAddress[d] | ir.cpp:830:3:830:3 | +| HierarchyConversions() -> void | 0 | 136 | FunctionAddress[operator=] | ir.cpp:830:5:830:5 | +| HierarchyConversions() -> void | 0 | 137 | VariableAddress[b] | ir.cpp:830:17:830:17 | +| HierarchyConversions() -> void | 0 | 138 | ConvertToDerived[Middle : Base] | ir.cpp:830:17:830:17 | +| HierarchyConversions() -> void | 0 | 139 | ConvertToDerived[Derived : Middle] | ir.cpp:830:7:830:17 | +| HierarchyConversions() -> void | 0 | 140 | Convert | ir.cpp:830:7:830:17 | +| HierarchyConversions() -> void | 0 | 141 | Invoke | ir.cpp:830:5:830:5 | +| HierarchyConversions() -> void | 0 | 142 | VariableAddress[d] | ir.cpp:831:3:831:3 | +| HierarchyConversions() -> void | 0 | 143 | FunctionAddress[operator=] | ir.cpp:831:5:831:5 | +| HierarchyConversions() -> void | 0 | 144 | VariableAddress[b] | ir.cpp:831:29:831:29 | +| HierarchyConversions() -> void | 0 | 145 | ConvertToDerived[Middle : Base] | ir.cpp:831:29:831:29 | +| HierarchyConversions() -> void | 0 | 146 | ConvertToDerived[Derived : Middle] | ir.cpp:831:7:831:30 | +| HierarchyConversions() -> void | 0 | 147 | Convert | ir.cpp:831:7:831:30 | +| HierarchyConversions() -> void | 0 | 148 | Invoke | ir.cpp:831:5:831:5 | +| HierarchyConversions() -> void | 0 | 149 | VariableAddress[pb] | ir.cpp:832:18:832:19 | +| HierarchyConversions() -> void | 0 | 150 | Load | ir.cpp:832:18:832:19 | +| HierarchyConversions() -> void | 0 | 151 | ConvertToDerived[Middle : Base] | ir.cpp:832:18:832:19 | +| HierarchyConversions() -> void | 0 | 152 | ConvertToDerived[Derived : Middle] | ir.cpp:832:8:832:19 | +| HierarchyConversions() -> void | 0 | 153 | VariableAddress[pd] | ir.cpp:832:3:832:4 | +| HierarchyConversions() -> void | 0 | 154 | Store | ir.cpp:832:3:832:19 | +| HierarchyConversions() -> void | 0 | 155 | VariableAddress[pb] | ir.cpp:833:30:833:31 | +| HierarchyConversions() -> void | 0 | 156 | Load | ir.cpp:833:30:833:31 | +| HierarchyConversions() -> void | 0 | 157 | ConvertToDerived[Middle : Base] | ir.cpp:833:30:833:31 | +| HierarchyConversions() -> void | 0 | 158 | ConvertToDerived[Derived : Middle] | ir.cpp:833:8:833:32 | +| HierarchyConversions() -> void | 0 | 159 | VariableAddress[pd] | ir.cpp:833:3:833:4 | +| HierarchyConversions() -> void | 0 | 160 | Store | ir.cpp:833:3:833:32 | +| HierarchyConversions() -> void | 0 | 161 | VariableAddress[pb] | ir.cpp:834:35:834:36 | +| HierarchyConversions() -> void | 0 | 162 | Load | ir.cpp:834:35:834:36 | +| HierarchyConversions() -> void | 0 | 163 | Convert | ir.cpp:834:8:834:37 | +| HierarchyConversions() -> void | 0 | 164 | VariableAddress[pd] | ir.cpp:834:3:834:4 | +| HierarchyConversions() -> void | 0 | 165 | Store | ir.cpp:834:3:834:37 | +| HierarchyConversions() -> void | 0 | 166 | VariableAddress[pmv] | ir.cpp:836:14:836:16 | +| HierarchyConversions() -> void | 0 | 167 | Constant[0] | ir.cpp:836:20:836:26 | +| HierarchyConversions() -> void | 0 | 168 | Store | ir.cpp:836:20:836:26 | +| HierarchyConversions() -> void | 0 | 169 | VariableAddress[pdv] | ir.cpp:837:14:837:16 | +| HierarchyConversions() -> void | 0 | 170 | Constant[0] | ir.cpp:837:20:837:26 | +| HierarchyConversions() -> void | 0 | 171 | Store | ir.cpp:837:20:837:26 | +| HierarchyConversions() -> void | 0 | 172 | VariableAddress[pmv] | ir.cpp:838:8:838:10 | +| HierarchyConversions() -> void | 0 | 173 | Load | ir.cpp:838:8:838:10 | +| HierarchyConversions() -> void | 0 | 174 | ConvertToVirtualBase[MiddleVB1 : Base] | ir.cpp:838:8:838:10 | +| HierarchyConversions() -> void | 0 | 175 | VariableAddress[pb] | ir.cpp:838:3:838:4 | +| HierarchyConversions() -> void | 0 | 176 | Store | ir.cpp:838:3:838:10 | +| HierarchyConversions() -> void | 0 | 177 | VariableAddress[pdv] | ir.cpp:839:8:839:10 | +| HierarchyConversions() -> void | 0 | 178 | Load | ir.cpp:839:8:839:10 | +| HierarchyConversions() -> void | 0 | 179 | ConvertToVirtualBase[DerivedVB : Base] | ir.cpp:839:8:839:10 | +| HierarchyConversions() -> void | 0 | 180 | VariableAddress[pb] | ir.cpp:839:3:839:4 | +| HierarchyConversions() -> void | 0 | 181 | Store | ir.cpp:839:3:839:10 | +| HierarchyConversions() -> void | 0 | 182 | NoOp | ir.cpp:840:1:840:1 | +| HierarchyConversions() -> void | 0 | 183 | ReturnVoid | ir.cpp:799:6:799:25 | +| HierarchyConversions() -> void | 0 | 184 | UnmodeledUse | ir.cpp:799:6:799:25 | +| HierarchyConversions() -> void | 0 | 185 | ExitFunction | ir.cpp:799:6:799:25 | +| IfStatements(bool, int, int) -> void | 0 | 0 | EnterFunction | ir.cpp:239:6:239:17 | +| IfStatements(bool, int, int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:239:6:239:17 | +| IfStatements(bool, int, int) -> void | 0 | 2 | InitializeParameter[b] | ir.cpp:239:24:239:24 | +| IfStatements(bool, int, int) -> void | 0 | 3 | VariableAddress[b] | ir.cpp:239:24:239:24 | +| IfStatements(bool, int, int) -> void | 0 | 4 | Store | ir.cpp:239:24:239:24 | +| IfStatements(bool, int, int) -> void | 0 | 5 | InitializeParameter[x] | ir.cpp:239:31:239:31 | +| IfStatements(bool, int, int) -> void | 0 | 6 | VariableAddress[x] | ir.cpp:239:31:239:31 | +| IfStatements(bool, int, int) -> void | 0 | 7 | Store | ir.cpp:239:31:239:31 | +| IfStatements(bool, int, int) -> void | 0 | 8 | InitializeParameter[y] | ir.cpp:239:38:239:38 | +| IfStatements(bool, int, int) -> void | 0 | 9 | VariableAddress[y] | ir.cpp:239:38:239:38 | +| IfStatements(bool, int, int) -> void | 0 | 10 | Store | ir.cpp:239:38:239:38 | +| IfStatements(bool, int, int) -> void | 0 | 11 | VariableAddress[b] | ir.cpp:240:9:240:9 | +| IfStatements(bool, int, int) -> void | 0 | 12 | Load | ir.cpp:240:9:240:9 | +| IfStatements(bool, int, int) -> void | 0 | 13 | ConditionalBranch | ir.cpp:240:9:240:9 | +| IfStatements(bool, int, int) -> void | 1 | 0 | VariableAddress[b] | ir.cpp:243:9:243:9 | +| IfStatements(bool, int, int) -> void | 1 | 1 | Load | ir.cpp:243:9:243:9 | +| IfStatements(bool, int, int) -> void | 1 | 2 | ConditionalBranch | ir.cpp:243:9:243:9 | +| IfStatements(bool, int, int) -> void | 2 | 0 | VariableAddress[y] | ir.cpp:244:13:244:13 | +| IfStatements(bool, int, int) -> void | 2 | 1 | Load | ir.cpp:244:13:244:13 | +| IfStatements(bool, int, int) -> void | 2 | 2 | VariableAddress[x] | ir.cpp:244:9:244:9 | +| IfStatements(bool, int, int) -> void | 2 | 3 | Store | ir.cpp:244:9:244:13 | +| IfStatements(bool, int, int) -> void | 3 | 0 | VariableAddress[x] | ir.cpp:247:9:247:9 | +| IfStatements(bool, int, int) -> void | 3 | 1 | Load | ir.cpp:247:9:247:9 | +| IfStatements(bool, int, int) -> void | 3 | 2 | Constant[7] | ir.cpp:247:13:247:13 | +| IfStatements(bool, int, int) -> void | 3 | 3 | CompareLT | ir.cpp:247:9:247:13 | +| IfStatements(bool, int, int) -> void | 3 | 4 | ConditionalBranch | ir.cpp:247:9:247:13 | +| IfStatements(bool, int, int) -> void | 4 | 0 | Constant[2] | ir.cpp:248:13:248:13 | +| IfStatements(bool, int, int) -> void | 4 | 1 | VariableAddress[x] | ir.cpp:248:9:248:9 | +| IfStatements(bool, int, int) -> void | 4 | 2 | Store | ir.cpp:248:9:248:13 | +| IfStatements(bool, int, int) -> void | 5 | 0 | Constant[7] | ir.cpp:250:13:250:13 | +| IfStatements(bool, int, int) -> void | 5 | 1 | VariableAddress[x] | ir.cpp:250:9:250:9 | +| IfStatements(bool, int, int) -> void | 5 | 2 | Store | ir.cpp:250:9:250:13 | +| IfStatements(bool, int, int) -> void | 6 | 0 | NoOp | ir.cpp:251:1:251:1 | +| IfStatements(bool, int, int) -> void | 6 | 1 | ReturnVoid | ir.cpp:239:6:239:17 | +| IfStatements(bool, int, int) -> void | 6 | 2 | UnmodeledUse | ir.cpp:239:6:239:17 | +| IfStatements(bool, int, int) -> void | 6 | 3 | ExitFunction | ir.cpp:239:6:239:17 | +| IfStatements(bool, int, int) -> void | 7 | 0 | NoOp | ir.cpp:240:12:241:5 | +| InitArray() -> void | 0 | 0 | EnterFunction | ir.cpp:571:6:571:14 | +| InitArray() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:571:6:571:14 | +| InitArray() -> void | 0 | 2 | VariableAddress[a_pad] | ir.cpp:572:10:572:14 | +| InitArray() -> void | 0 | 3 | StringConstant[""] | ir.cpp:572:22:572:23 | +| InitArray() -> void | 0 | 4 | Load | ir.cpp:572:22:572:23 | +| InitArray() -> void | 0 | 5 | Store | ir.cpp:572:22:572:23 | +| InitArray() -> void | 0 | 6 | Constant[0] | ir.cpp:572:22:572:23 | +| InitArray() -> void | 0 | 7 | Constant[1] | ir.cpp:572:22:572:23 | +| InitArray() -> void | 0 | 8 | PointerAdd | ir.cpp:572:22:572:23 | +| InitArray() -> void | 0 | 9 | Store | ir.cpp:572:22:572:23 | +| InitArray() -> void | 0 | 10 | VariableAddress[a_nopad] | ir.cpp:573:10:573:16 | +| InitArray() -> void | 0 | 11 | StringConstant["foo"] | ir.cpp:573:23:573:27 | +| InitArray() -> void | 0 | 12 | Load | ir.cpp:573:23:573:27 | +| InitArray() -> void | 0 | 13 | Store | ir.cpp:573:23:573:27 | +| InitArray() -> void | 0 | 14 | VariableAddress[a_infer] | ir.cpp:574:10:574:16 | +| InitArray() -> void | 0 | 15 | StringConstant["blah"] | ir.cpp:574:22:574:27 | +| InitArray() -> void | 0 | 16 | Load | ir.cpp:574:22:574:27 | +| InitArray() -> void | 0 | 17 | Store | ir.cpp:574:22:574:27 | +| InitArray() -> void | 0 | 18 | VariableAddress[b] | ir.cpp:575:10:575:10 | +| InitArray() -> void | 0 | 19 | Uninitialized | ir.cpp:575:10:575:10 | +| InitArray() -> void | 0 | 20 | Store | ir.cpp:575:10:575:10 | +| InitArray() -> void | 0 | 21 | VariableAddress[c] | ir.cpp:576:10:576:10 | +| InitArray() -> void | 0 | 22 | Constant[0] | ir.cpp:576:16:576:18 | +| InitArray() -> void | 0 | 23 | PointerAdd | ir.cpp:576:16:576:18 | +| InitArray() -> void | 0 | 24 | Constant[0] | ir.cpp:576:16:576:18 | +| InitArray() -> void | 0 | 25 | Store | ir.cpp:576:16:576:18 | +| InitArray() -> void | 0 | 26 | VariableAddress[d] | ir.cpp:577:10:577:10 | +| InitArray() -> void | 0 | 27 | Constant[0] | ir.cpp:577:16:577:21 | +| InitArray() -> void | 0 | 28 | PointerAdd | ir.cpp:577:16:577:21 | +| InitArray() -> void | 0 | 29 | Constant[0] | ir.cpp:577:19:577:19 | +| InitArray() -> void | 0 | 30 | Store | ir.cpp:577:19:577:19 | +| InitArray() -> void | 0 | 31 | Constant[1] | ir.cpp:577:16:577:21 | +| InitArray() -> void | 0 | 32 | PointerAdd | ir.cpp:577:16:577:21 | +| InitArray() -> void | 0 | 33 | Constant[0] | ir.cpp:577:16:577:21 | +| InitArray() -> void | 0 | 34 | Store | ir.cpp:577:16:577:21 | +| InitArray() -> void | 0 | 35 | VariableAddress[e] | ir.cpp:578:10:578:10 | +| InitArray() -> void | 0 | 36 | Constant[0] | ir.cpp:578:16:578:24 | +| InitArray() -> void | 0 | 37 | PointerAdd | ir.cpp:578:16:578:24 | +| InitArray() -> void | 0 | 38 | Constant[0] | ir.cpp:578:19:578:19 | +| InitArray() -> void | 0 | 39 | Store | ir.cpp:578:19:578:19 | +| InitArray() -> void | 0 | 40 | Constant[1] | ir.cpp:578:16:578:24 | +| InitArray() -> void | 0 | 41 | PointerAdd | ir.cpp:578:16:578:24 | +| InitArray() -> void | 0 | 42 | Constant[1] | ir.cpp:578:22:578:22 | +| InitArray() -> void | 0 | 43 | Store | ir.cpp:578:22:578:22 | +| InitArray() -> void | 0 | 44 | VariableAddress[f] | ir.cpp:579:10:579:10 | +| InitArray() -> void | 0 | 45 | Constant[0] | ir.cpp:579:16:579:21 | +| InitArray() -> void | 0 | 46 | PointerAdd | ir.cpp:579:16:579:21 | +| InitArray() -> void | 0 | 47 | Constant[0] | ir.cpp:579:19:579:19 | +| InitArray() -> void | 0 | 48 | Store | ir.cpp:579:19:579:19 | +| InitArray() -> void | 0 | 49 | Constant[1] | ir.cpp:579:16:579:21 | +| InitArray() -> void | 0 | 50 | PointerAdd | ir.cpp:579:16:579:21 | +| InitArray() -> void | 0 | 51 | Constant[0] | ir.cpp:579:16:579:21 | +| InitArray() -> void | 0 | 52 | Store | ir.cpp:579:16:579:21 | +| InitArray() -> void | 0 | 53 | NoOp | ir.cpp:580:1:580:1 | +| InitArray() -> void | 0 | 54 | ReturnVoid | ir.cpp:571:6:571:14 | +| InitArray() -> void | 0 | 55 | UnmodeledUse | ir.cpp:571:6:571:14 | +| InitArray() -> void | 0 | 56 | ExitFunction | ir.cpp:571:6:571:14 | +| InitList(int, float) -> void | 0 | 0 | EnterFunction | ir.cpp:503:6:503:13 | +| InitList(int, float) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:503:6:503:13 | +| InitList(int, float) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:503:19:503:19 | +| InitList(int, float) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:503:19:503:19 | +| InitList(int, float) -> void | 0 | 4 | Store | ir.cpp:503:19:503:19 | +| InitList(int, float) -> void | 0 | 5 | InitializeParameter[f] | ir.cpp:503:28:503:28 | +| InitList(int, float) -> void | 0 | 6 | VariableAddress[f] | ir.cpp:503:28:503:28 | +| InitList(int, float) -> void | 0 | 7 | Store | ir.cpp:503:28:503:28 | +| InitList(int, float) -> void | 0 | 8 | VariableAddress[pt1] | ir.cpp:504:11:504:13 | +| InitList(int, float) -> void | 0 | 9 | FieldAddress[x] | ir.cpp:504:16:504:24 | +| InitList(int, float) -> void | 0 | 10 | VariableAddress[x] | ir.cpp:504:19:504:19 | +| InitList(int, float) -> void | 0 | 11 | Load | ir.cpp:504:19:504:19 | +| InitList(int, float) -> void | 0 | 12 | Store | ir.cpp:504:19:504:19 | +| InitList(int, float) -> void | 0 | 13 | FieldAddress[y] | ir.cpp:504:16:504:24 | +| InitList(int, float) -> void | 0 | 14 | VariableAddress[f] | ir.cpp:504:22:504:22 | +| InitList(int, float) -> void | 0 | 15 | Load | ir.cpp:504:22:504:22 | +| InitList(int, float) -> void | 0 | 16 | Convert | ir.cpp:504:22:504:22 | +| InitList(int, float) -> void | 0 | 17 | Store | ir.cpp:504:22:504:22 | +| InitList(int, float) -> void | 0 | 18 | VariableAddress[pt2] | ir.cpp:505:11:505:13 | +| InitList(int, float) -> void | 0 | 19 | FieldAddress[x] | ir.cpp:505:16:505:21 | +| InitList(int, float) -> void | 0 | 20 | VariableAddress[x] | ir.cpp:505:19:505:19 | +| InitList(int, float) -> void | 0 | 21 | Load | ir.cpp:505:19:505:19 | +| InitList(int, float) -> void | 0 | 22 | Store | ir.cpp:505:19:505:19 | +| InitList(int, float) -> void | 0 | 23 | FieldAddress[y] | ir.cpp:505:16:505:21 | +| InitList(int, float) -> void | 0 | 24 | Constant[0] | ir.cpp:505:16:505:21 | +| InitList(int, float) -> void | 0 | 25 | Store | ir.cpp:505:16:505:21 | +| InitList(int, float) -> void | 0 | 26 | VariableAddress[pt3] | ir.cpp:506:11:506:13 | +| InitList(int, float) -> void | 0 | 27 | FieldAddress[x] | ir.cpp:506:16:506:18 | +| InitList(int, float) -> void | 0 | 28 | Constant[0] | ir.cpp:506:16:506:18 | +| InitList(int, float) -> void | 0 | 29 | Store | ir.cpp:506:16:506:18 | +| InitList(int, float) -> void | 0 | 30 | FieldAddress[y] | ir.cpp:506:16:506:18 | +| InitList(int, float) -> void | 0 | 31 | Constant[0] | ir.cpp:506:16:506:18 | +| InitList(int, float) -> void | 0 | 32 | Store | ir.cpp:506:16:506:18 | +| InitList(int, float) -> void | 0 | 33 | VariableAddress[x1] | ir.cpp:508:9:508:10 | +| InitList(int, float) -> void | 0 | 34 | Constant[1] | ir.cpp:508:13:508:18 | +| InitList(int, float) -> void | 0 | 35 | Store | ir.cpp:508:13:508:18 | +| InitList(int, float) -> void | 0 | 36 | VariableAddress[x2] | ir.cpp:509:9:509:10 | +| InitList(int, float) -> void | 0 | 37 | Constant[0] | ir.cpp:509:13:509:15 | +| InitList(int, float) -> void | 0 | 38 | Store | ir.cpp:509:13:509:15 | +| InitList(int, float) -> void | 0 | 39 | NoOp | ir.cpp:510:1:510:1 | +| InitList(int, float) -> void | 0 | 40 | ReturnVoid | ir.cpp:503:6:503:13 | +| InitList(int, float) -> void | 0 | 41 | UnmodeledUse | ir.cpp:503:6:503:13 | +| InitList(int, float) -> void | 0 | 42 | ExitFunction | ir.cpp:503:6:503:13 | +| InitReference(int) -> void | 0 | 0 | EnterFunction | ir.cpp:685:6:685:18 | +| InitReference(int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:685:6:685:18 | +| InitReference(int) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:685:24:685:24 | +| InitReference(int) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:685:24:685:24 | +| InitReference(int) -> void | 0 | 4 | Store | ir.cpp:685:24:685:24 | +| InitReference(int) -> void | 0 | 5 | VariableAddress[r] | ir.cpp:686:10:686:10 | +| InitReference(int) -> void | 0 | 6 | VariableAddress[x] | ir.cpp:686:14:686:14 | +| InitReference(int) -> void | 0 | 7 | Store | ir.cpp:686:14:686:14 | +| InitReference(int) -> void | 0 | 8 | VariableAddress[r2] | ir.cpp:687:10:687:11 | +| InitReference(int) -> void | 0 | 9 | VariableAddress[r] | ir.cpp:687:15:687:15 | +| InitReference(int) -> void | 0 | 10 | Load | ir.cpp:687:15:687:15 | +| InitReference(int) -> void | 0 | 11 | Store | ir.cpp:687:15:687:15 | +| InitReference(int) -> void | 0 | 12 | VariableAddress[r3] | ir.cpp:688:19:688:20 | +| InitReference(int) -> void | 0 | 13 | FunctionAddress[ReturnReference] | ir.cpp:688:24:688:38 | +| InitReference(int) -> void | 0 | 14 | Invoke | ir.cpp:688:24:688:38 | +| InitReference(int) -> void | 0 | 15 | Convert | ir.cpp:688:24:688:41 | +| InitReference(int) -> void | 0 | 16 | Store | ir.cpp:688:24:688:41 | +| InitReference(int) -> void | 0 | 17 | NoOp | ir.cpp:689:1:689:1 | +| InitReference(int) -> void | 0 | 18 | ReturnVoid | ir.cpp:685:6:685:18 | +| InitReference(int) -> void | 0 | 19 | UnmodeledUse | ir.cpp:685:6:685:18 | +| InitReference(int) -> void | 0 | 20 | ExitFunction | ir.cpp:685:6:685:18 | +| IntegerCompare(int, int) -> void | 0 | 0 | EnterFunction | ir.cpp:87:6:87:19 | +| IntegerCompare(int, int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:87:6:87:19 | +| IntegerCompare(int, int) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:87:25:87:25 | +| IntegerCompare(int, int) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:87:25:87:25 | +| IntegerCompare(int, int) -> void | 0 | 4 | Store | ir.cpp:87:25:87:25 | +| IntegerCompare(int, int) -> void | 0 | 5 | InitializeParameter[y] | ir.cpp:87:32:87:32 | +| IntegerCompare(int, int) -> void | 0 | 6 | VariableAddress[y] | ir.cpp:87:32:87:32 | +| IntegerCompare(int, int) -> void | 0 | 7 | Store | ir.cpp:87:32:87:32 | +| IntegerCompare(int, int) -> void | 0 | 8 | VariableAddress[b] | ir.cpp:88:10:88:10 | +| IntegerCompare(int, int) -> void | 0 | 9 | Uninitialized | ir.cpp:88:10:88:10 | +| IntegerCompare(int, int) -> void | 0 | 10 | Store | ir.cpp:88:10:88:10 | +| IntegerCompare(int, int) -> void | 0 | 11 | VariableAddress[x] | ir.cpp:90:9:90:9 | +| IntegerCompare(int, int) -> void | 0 | 12 | Load | ir.cpp:90:9:90:9 | +| IntegerCompare(int, int) -> void | 0 | 13 | VariableAddress[y] | ir.cpp:90:14:90:14 | +| IntegerCompare(int, int) -> void | 0 | 14 | Load | ir.cpp:90:14:90:14 | +| IntegerCompare(int, int) -> void | 0 | 15 | CompareEQ | ir.cpp:90:9:90:14 | +| IntegerCompare(int, int) -> void | 0 | 16 | VariableAddress[b] | ir.cpp:90:5:90:5 | +| IntegerCompare(int, int) -> void | 0 | 17 | Store | ir.cpp:90:5:90:14 | +| IntegerCompare(int, int) -> void | 0 | 18 | VariableAddress[x] | ir.cpp:91:9:91:9 | +| IntegerCompare(int, int) -> void | 0 | 19 | Load | ir.cpp:91:9:91:9 | +| IntegerCompare(int, int) -> void | 0 | 20 | VariableAddress[y] | ir.cpp:91:14:91:14 | +| IntegerCompare(int, int) -> void | 0 | 21 | Load | ir.cpp:91:14:91:14 | +| IntegerCompare(int, int) -> void | 0 | 22 | CompareNE | ir.cpp:91:9:91:14 | +| IntegerCompare(int, int) -> void | 0 | 23 | VariableAddress[b] | ir.cpp:91:5:91:5 | +| IntegerCompare(int, int) -> void | 0 | 24 | Store | ir.cpp:91:5:91:14 | +| IntegerCompare(int, int) -> void | 0 | 25 | VariableAddress[x] | ir.cpp:92:9:92:9 | +| IntegerCompare(int, int) -> void | 0 | 26 | Load | ir.cpp:92:9:92:9 | +| IntegerCompare(int, int) -> void | 0 | 27 | VariableAddress[y] | ir.cpp:92:13:92:13 | +| IntegerCompare(int, int) -> void | 0 | 28 | Load | ir.cpp:92:13:92:13 | +| IntegerCompare(int, int) -> void | 0 | 29 | CompareLT | ir.cpp:92:9:92:13 | +| IntegerCompare(int, int) -> void | 0 | 30 | VariableAddress[b] | ir.cpp:92:5:92:5 | +| IntegerCompare(int, int) -> void | 0 | 31 | Store | ir.cpp:92:5:92:13 | +| IntegerCompare(int, int) -> void | 0 | 32 | VariableAddress[x] | ir.cpp:93:9:93:9 | +| IntegerCompare(int, int) -> void | 0 | 33 | Load | ir.cpp:93:9:93:9 | +| IntegerCompare(int, int) -> void | 0 | 34 | VariableAddress[y] | ir.cpp:93:13:93:13 | +| IntegerCompare(int, int) -> void | 0 | 35 | Load | ir.cpp:93:13:93:13 | +| IntegerCompare(int, int) -> void | 0 | 36 | CompareGT | ir.cpp:93:9:93:13 | +| IntegerCompare(int, int) -> void | 0 | 37 | VariableAddress[b] | ir.cpp:93:5:93:5 | +| IntegerCompare(int, int) -> void | 0 | 38 | Store | ir.cpp:93:5:93:13 | +| IntegerCompare(int, int) -> void | 0 | 39 | VariableAddress[x] | ir.cpp:94:9:94:9 | +| IntegerCompare(int, int) -> void | 0 | 40 | Load | ir.cpp:94:9:94:9 | +| IntegerCompare(int, int) -> void | 0 | 41 | VariableAddress[y] | ir.cpp:94:14:94:14 | +| IntegerCompare(int, int) -> void | 0 | 42 | Load | ir.cpp:94:14:94:14 | +| IntegerCompare(int, int) -> void | 0 | 43 | CompareLE | ir.cpp:94:9:94:14 | +| IntegerCompare(int, int) -> void | 0 | 44 | VariableAddress[b] | ir.cpp:94:5:94:5 | +| IntegerCompare(int, int) -> void | 0 | 45 | Store | ir.cpp:94:5:94:14 | +| IntegerCompare(int, int) -> void | 0 | 46 | VariableAddress[x] | ir.cpp:95:9:95:9 | +| IntegerCompare(int, int) -> void | 0 | 47 | Load | ir.cpp:95:9:95:9 | +| IntegerCompare(int, int) -> void | 0 | 48 | VariableAddress[y] | ir.cpp:95:14:95:14 | +| IntegerCompare(int, int) -> void | 0 | 49 | Load | ir.cpp:95:14:95:14 | +| IntegerCompare(int, int) -> void | 0 | 50 | CompareGE | ir.cpp:95:9:95:14 | +| IntegerCompare(int, int) -> void | 0 | 51 | VariableAddress[b] | ir.cpp:95:5:95:5 | +| IntegerCompare(int, int) -> void | 0 | 52 | Store | ir.cpp:95:5:95:14 | +| IntegerCompare(int, int) -> void | 0 | 53 | NoOp | ir.cpp:96:1:96:1 | +| IntegerCompare(int, int) -> void | 0 | 54 | ReturnVoid | ir.cpp:87:6:87:19 | +| IntegerCompare(int, int) -> void | 0 | 55 | UnmodeledUse | ir.cpp:87:6:87:19 | +| IntegerCompare(int, int) -> void | 0 | 56 | ExitFunction | ir.cpp:87:6:87:19 | +| IntegerCrement(int) -> void | 0 | 0 | EnterFunction | ir.cpp:98:6:98:19 | +| IntegerCrement(int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:98:6:98:19 | +| IntegerCrement(int) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:98:25:98:25 | +| IntegerCrement(int) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:98:25:98:25 | +| IntegerCrement(int) -> void | 0 | 4 | Store | ir.cpp:98:25:98:25 | +| IntegerCrement(int) -> void | 0 | 5 | VariableAddress[y] | ir.cpp:99:9:99:9 | +| IntegerCrement(int) -> void | 0 | 6 | Uninitialized | ir.cpp:99:9:99:9 | +| IntegerCrement(int) -> void | 0 | 7 | Store | ir.cpp:99:9:99:9 | +| IntegerCrement(int) -> void | 0 | 8 | VariableAddress[x] | ir.cpp:101:11:101:11 | +| IntegerCrement(int) -> void | 0 | 9 | Load | ir.cpp:101:9:101:11 | +| IntegerCrement(int) -> void | 0 | 10 | Constant[1] | ir.cpp:101:9:101:11 | +| IntegerCrement(int) -> void | 0 | 11 | Add | ir.cpp:101:9:101:11 | +| IntegerCrement(int) -> void | 0 | 12 | Store | ir.cpp:101:9:101:11 | +| IntegerCrement(int) -> void | 0 | 13 | VariableAddress[y] | ir.cpp:101:5:101:5 | +| IntegerCrement(int) -> void | 0 | 14 | Store | ir.cpp:101:5:101:11 | +| IntegerCrement(int) -> void | 0 | 15 | VariableAddress[x] | ir.cpp:102:11:102:11 | +| IntegerCrement(int) -> void | 0 | 16 | Load | ir.cpp:102:9:102:11 | +| IntegerCrement(int) -> void | 0 | 17 | Constant[1] | ir.cpp:102:9:102:11 | +| IntegerCrement(int) -> void | 0 | 18 | Sub | ir.cpp:102:9:102:11 | +| IntegerCrement(int) -> void | 0 | 19 | Store | ir.cpp:102:9:102:11 | +| IntegerCrement(int) -> void | 0 | 20 | VariableAddress[y] | ir.cpp:102:5:102:5 | +| IntegerCrement(int) -> void | 0 | 21 | Store | ir.cpp:102:5:102:11 | +| IntegerCrement(int) -> void | 0 | 22 | VariableAddress[x] | ir.cpp:103:9:103:9 | +| IntegerCrement(int) -> void | 0 | 23 | Load | ir.cpp:103:9:103:11 | +| IntegerCrement(int) -> void | 0 | 24 | Constant[1] | ir.cpp:103:9:103:11 | +| IntegerCrement(int) -> void | 0 | 25 | Add | ir.cpp:103:9:103:11 | +| IntegerCrement(int) -> void | 0 | 26 | Store | ir.cpp:103:9:103:11 | +| IntegerCrement(int) -> void | 0 | 27 | VariableAddress[y] | ir.cpp:103:5:103:5 | +| IntegerCrement(int) -> void | 0 | 28 | Store | ir.cpp:103:5:103:11 | +| IntegerCrement(int) -> void | 0 | 29 | VariableAddress[x] | ir.cpp:104:9:104:9 | +| IntegerCrement(int) -> void | 0 | 30 | Load | ir.cpp:104:9:104:11 | +| IntegerCrement(int) -> void | 0 | 31 | Constant[1] | ir.cpp:104:9:104:11 | +| IntegerCrement(int) -> void | 0 | 32 | Sub | ir.cpp:104:9:104:11 | +| IntegerCrement(int) -> void | 0 | 33 | Store | ir.cpp:104:9:104:11 | +| IntegerCrement(int) -> void | 0 | 34 | VariableAddress[y] | ir.cpp:104:5:104:5 | +| IntegerCrement(int) -> void | 0 | 35 | Store | ir.cpp:104:5:104:11 | +| IntegerCrement(int) -> void | 0 | 36 | NoOp | ir.cpp:105:1:105:1 | +| IntegerCrement(int) -> void | 0 | 37 | ReturnVoid | ir.cpp:98:6:98:19 | +| IntegerCrement(int) -> void | 0 | 38 | UnmodeledUse | ir.cpp:98:6:98:19 | +| IntegerCrement(int) -> void | 0 | 39 | ExitFunction | ir.cpp:98:6:98:19 | +| IntegerCrement_LValue(int) -> void | 0 | 0 | EnterFunction | ir.cpp:107:6:107:26 | +| IntegerCrement_LValue(int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:107:6:107:26 | +| IntegerCrement_LValue(int) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:107:32:107:32 | +| IntegerCrement_LValue(int) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:107:32:107:32 | +| IntegerCrement_LValue(int) -> void | 0 | 4 | Store | ir.cpp:107:32:107:32 | +| IntegerCrement_LValue(int) -> void | 0 | 5 | VariableAddress[p] | ir.cpp:108:10:108:10 | +| IntegerCrement_LValue(int) -> void | 0 | 6 | Uninitialized | ir.cpp:108:10:108:10 | +| IntegerCrement_LValue(int) -> void | 0 | 7 | Store | ir.cpp:108:10:108:10 | +| IntegerCrement_LValue(int) -> void | 0 | 8 | VariableAddress[x] | ir.cpp:110:13:110:13 | +| IntegerCrement_LValue(int) -> void | 0 | 9 | Load | ir.cpp:110:11:110:13 | +| IntegerCrement_LValue(int) -> void | 0 | 10 | Constant[1] | ir.cpp:110:11:110:13 | +| IntegerCrement_LValue(int) -> void | 0 | 11 | Add | ir.cpp:110:11:110:13 | +| IntegerCrement_LValue(int) -> void | 0 | 12 | Store | ir.cpp:110:11:110:13 | +| IntegerCrement_LValue(int) -> void | 0 | 13 | VariableAddress[p] | ir.cpp:110:5:110:5 | +| IntegerCrement_LValue(int) -> void | 0 | 14 | Store | ir.cpp:110:5:110:14 | +| IntegerCrement_LValue(int) -> void | 0 | 15 | VariableAddress[x] | ir.cpp:111:13:111:13 | +| IntegerCrement_LValue(int) -> void | 0 | 16 | Load | ir.cpp:111:11:111:13 | +| IntegerCrement_LValue(int) -> void | 0 | 17 | Constant[1] | ir.cpp:111:11:111:13 | +| IntegerCrement_LValue(int) -> void | 0 | 18 | Sub | ir.cpp:111:11:111:13 | +| IntegerCrement_LValue(int) -> void | 0 | 19 | Store | ir.cpp:111:11:111:13 | +| IntegerCrement_LValue(int) -> void | 0 | 20 | VariableAddress[p] | ir.cpp:111:5:111:5 | +| IntegerCrement_LValue(int) -> void | 0 | 21 | Store | ir.cpp:111:5:111:14 | +| IntegerCrement_LValue(int) -> void | 0 | 22 | NoOp | ir.cpp:112:1:112:1 | +| IntegerCrement_LValue(int) -> void | 0 | 23 | ReturnVoid | ir.cpp:107:6:107:26 | +| IntegerCrement_LValue(int) -> void | 0 | 24 | UnmodeledUse | ir.cpp:107:6:107:26 | +| IntegerCrement_LValue(int) -> void | 0 | 25 | ExitFunction | ir.cpp:107:6:107:26 | +| IntegerOps(int, int) -> void | 0 | 0 | EnterFunction | ir.cpp:50:6:50:15 | +| IntegerOps(int, int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:50:6:50:15 | +| IntegerOps(int, int) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:50:21:50:21 | +| IntegerOps(int, int) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:50:21:50:21 | +| IntegerOps(int, int) -> void | 0 | 4 | Store | ir.cpp:50:21:50:21 | +| IntegerOps(int, int) -> void | 0 | 5 | InitializeParameter[y] | ir.cpp:50:28:50:28 | +| IntegerOps(int, int) -> void | 0 | 6 | VariableAddress[y] | ir.cpp:50:28:50:28 | +| IntegerOps(int, int) -> void | 0 | 7 | Store | ir.cpp:50:28:50:28 | +| IntegerOps(int, int) -> void | 0 | 8 | VariableAddress[z] | ir.cpp:51:9:51:9 | +| IntegerOps(int, int) -> void | 0 | 9 | Uninitialized | ir.cpp:51:9:51:9 | +| IntegerOps(int, int) -> void | 0 | 10 | Store | ir.cpp:51:9:51:9 | +| IntegerOps(int, int) -> void | 0 | 11 | VariableAddress[x] | ir.cpp:53:9:53:9 | +| IntegerOps(int, int) -> void | 0 | 12 | Load | ir.cpp:53:9:53:9 | +| IntegerOps(int, int) -> void | 0 | 13 | VariableAddress[y] | ir.cpp:53:13:53:13 | +| IntegerOps(int, int) -> void | 0 | 14 | Load | ir.cpp:53:13:53:13 | +| IntegerOps(int, int) -> void | 0 | 15 | Add | ir.cpp:53:9:53:13 | +| IntegerOps(int, int) -> void | 0 | 16 | VariableAddress[z] | ir.cpp:53:5:53:5 | +| IntegerOps(int, int) -> void | 0 | 17 | Store | ir.cpp:53:5:53:13 | +| IntegerOps(int, int) -> void | 0 | 18 | VariableAddress[x] | ir.cpp:54:9:54:9 | +| IntegerOps(int, int) -> void | 0 | 19 | Load | ir.cpp:54:9:54:9 | +| IntegerOps(int, int) -> void | 0 | 20 | VariableAddress[y] | ir.cpp:54:13:54:13 | +| IntegerOps(int, int) -> void | 0 | 21 | Load | ir.cpp:54:13:54:13 | +| IntegerOps(int, int) -> void | 0 | 22 | Sub | ir.cpp:54:9:54:13 | +| IntegerOps(int, int) -> void | 0 | 23 | VariableAddress[z] | ir.cpp:54:5:54:5 | +| IntegerOps(int, int) -> void | 0 | 24 | Store | ir.cpp:54:5:54:13 | +| IntegerOps(int, int) -> void | 0 | 25 | VariableAddress[x] | ir.cpp:55:9:55:9 | +| IntegerOps(int, int) -> void | 0 | 26 | Load | ir.cpp:55:9:55:9 | +| IntegerOps(int, int) -> void | 0 | 27 | VariableAddress[y] | ir.cpp:55:13:55:13 | +| IntegerOps(int, int) -> void | 0 | 28 | Load | ir.cpp:55:13:55:13 | +| IntegerOps(int, int) -> void | 0 | 29 | Mul | ir.cpp:55:9:55:13 | +| IntegerOps(int, int) -> void | 0 | 30 | VariableAddress[z] | ir.cpp:55:5:55:5 | +| IntegerOps(int, int) -> void | 0 | 31 | Store | ir.cpp:55:5:55:13 | +| IntegerOps(int, int) -> void | 0 | 32 | VariableAddress[x] | ir.cpp:56:9:56:9 | +| IntegerOps(int, int) -> void | 0 | 33 | Load | ir.cpp:56:9:56:9 | +| IntegerOps(int, int) -> void | 0 | 34 | VariableAddress[y] | ir.cpp:56:13:56:13 | +| IntegerOps(int, int) -> void | 0 | 35 | Load | ir.cpp:56:13:56:13 | +| IntegerOps(int, int) -> void | 0 | 36 | Div | ir.cpp:56:9:56:13 | +| IntegerOps(int, int) -> void | 0 | 37 | VariableAddress[z] | ir.cpp:56:5:56:5 | +| IntegerOps(int, int) -> void | 0 | 38 | Store | ir.cpp:56:5:56:13 | +| IntegerOps(int, int) -> void | 0 | 39 | VariableAddress[x] | ir.cpp:57:9:57:9 | +| IntegerOps(int, int) -> void | 0 | 40 | Load | ir.cpp:57:9:57:9 | +| IntegerOps(int, int) -> void | 0 | 41 | VariableAddress[y] | ir.cpp:57:13:57:13 | +| IntegerOps(int, int) -> void | 0 | 42 | Load | ir.cpp:57:13:57:13 | +| IntegerOps(int, int) -> void | 0 | 43 | Rem | ir.cpp:57:9:57:13 | +| IntegerOps(int, int) -> void | 0 | 44 | VariableAddress[z] | ir.cpp:57:5:57:5 | +| IntegerOps(int, int) -> void | 0 | 45 | Store | ir.cpp:57:5:57:13 | +| IntegerOps(int, int) -> void | 0 | 46 | VariableAddress[x] | ir.cpp:59:9:59:9 | +| IntegerOps(int, int) -> void | 0 | 47 | Load | ir.cpp:59:9:59:9 | +| IntegerOps(int, int) -> void | 0 | 48 | VariableAddress[y] | ir.cpp:59:13:59:13 | +| IntegerOps(int, int) -> void | 0 | 49 | Load | ir.cpp:59:13:59:13 | +| IntegerOps(int, int) -> void | 0 | 50 | BitAnd | ir.cpp:59:9:59:13 | +| IntegerOps(int, int) -> void | 0 | 51 | VariableAddress[z] | ir.cpp:59:5:59:5 | +| IntegerOps(int, int) -> void | 0 | 52 | Store | ir.cpp:59:5:59:13 | +| IntegerOps(int, int) -> void | 0 | 53 | VariableAddress[x] | ir.cpp:60:9:60:9 | +| IntegerOps(int, int) -> void | 0 | 54 | Load | ir.cpp:60:9:60:9 | +| IntegerOps(int, int) -> void | 0 | 55 | VariableAddress[y] | ir.cpp:60:13:60:13 | +| IntegerOps(int, int) -> void | 0 | 56 | Load | ir.cpp:60:13:60:13 | +| IntegerOps(int, int) -> void | 0 | 57 | BitOr | ir.cpp:60:9:60:13 | +| IntegerOps(int, int) -> void | 0 | 58 | VariableAddress[z] | ir.cpp:60:5:60:5 | +| IntegerOps(int, int) -> void | 0 | 59 | Store | ir.cpp:60:5:60:13 | +| IntegerOps(int, int) -> void | 0 | 60 | VariableAddress[x] | ir.cpp:61:9:61:9 | +| IntegerOps(int, int) -> void | 0 | 61 | Load | ir.cpp:61:9:61:9 | +| IntegerOps(int, int) -> void | 0 | 62 | VariableAddress[y] | ir.cpp:61:13:61:13 | +| IntegerOps(int, int) -> void | 0 | 63 | Load | ir.cpp:61:13:61:13 | +| IntegerOps(int, int) -> void | 0 | 64 | BitXor | ir.cpp:61:9:61:13 | +| IntegerOps(int, int) -> void | 0 | 65 | VariableAddress[z] | ir.cpp:61:5:61:5 | +| IntegerOps(int, int) -> void | 0 | 66 | Store | ir.cpp:61:5:61:13 | +| IntegerOps(int, int) -> void | 0 | 67 | VariableAddress[x] | ir.cpp:63:9:63:9 | +| IntegerOps(int, int) -> void | 0 | 68 | Load | ir.cpp:63:9:63:9 | +| IntegerOps(int, int) -> void | 0 | 69 | VariableAddress[y] | ir.cpp:63:14:63:14 | +| IntegerOps(int, int) -> void | 0 | 70 | Load | ir.cpp:63:14:63:14 | +| IntegerOps(int, int) -> void | 0 | 71 | ShiftLeft | ir.cpp:63:9:63:14 | +| IntegerOps(int, int) -> void | 0 | 72 | VariableAddress[z] | ir.cpp:63:5:63:5 | +| IntegerOps(int, int) -> void | 0 | 73 | Store | ir.cpp:63:5:63:14 | +| IntegerOps(int, int) -> void | 0 | 74 | VariableAddress[x] | ir.cpp:64:9:64:9 | +| IntegerOps(int, int) -> void | 0 | 75 | Load | ir.cpp:64:9:64:9 | +| IntegerOps(int, int) -> void | 0 | 76 | VariableAddress[y] | ir.cpp:64:14:64:14 | +| IntegerOps(int, int) -> void | 0 | 77 | Load | ir.cpp:64:14:64:14 | +| IntegerOps(int, int) -> void | 0 | 78 | ShiftRight | ir.cpp:64:9:64:14 | +| IntegerOps(int, int) -> void | 0 | 79 | VariableAddress[z] | ir.cpp:64:5:64:5 | +| IntegerOps(int, int) -> void | 0 | 80 | Store | ir.cpp:64:5:64:14 | +| IntegerOps(int, int) -> void | 0 | 81 | VariableAddress[x] | ir.cpp:66:9:66:9 | +| IntegerOps(int, int) -> void | 0 | 82 | Load | ir.cpp:66:9:66:9 | +| IntegerOps(int, int) -> void | 0 | 83 | VariableAddress[z] | ir.cpp:66:5:66:5 | +| IntegerOps(int, int) -> void | 0 | 84 | Store | ir.cpp:66:5:66:9 | +| IntegerOps(int, int) -> void | 0 | 85 | VariableAddress[x] | ir.cpp:68:10:68:10 | +| IntegerOps(int, int) -> void | 0 | 86 | Load | ir.cpp:68:10:68:10 | +| IntegerOps(int, int) -> void | 0 | 87 | VariableAddress[z] | ir.cpp:68:5:68:5 | +| IntegerOps(int, int) -> void | 0 | 88 | Load | ir.cpp:68:5:68:10 | +| IntegerOps(int, int) -> void | 0 | 89 | Add | ir.cpp:68:5:68:10 | +| IntegerOps(int, int) -> void | 0 | 90 | Store | ir.cpp:68:5:68:10 | +| IntegerOps(int, int) -> void | 0 | 91 | VariableAddress[x] | ir.cpp:69:10:69:10 | +| IntegerOps(int, int) -> void | 0 | 92 | Load | ir.cpp:69:10:69:10 | +| IntegerOps(int, int) -> void | 0 | 93 | VariableAddress[z] | ir.cpp:69:5:69:5 | +| IntegerOps(int, int) -> void | 0 | 94 | Load | ir.cpp:69:5:69:10 | +| IntegerOps(int, int) -> void | 0 | 95 | Sub | ir.cpp:69:5:69:10 | +| IntegerOps(int, int) -> void | 0 | 96 | Store | ir.cpp:69:5:69:10 | +| IntegerOps(int, int) -> void | 0 | 97 | VariableAddress[x] | ir.cpp:70:10:70:10 | +| IntegerOps(int, int) -> void | 0 | 98 | Load | ir.cpp:70:10:70:10 | +| IntegerOps(int, int) -> void | 0 | 99 | VariableAddress[z] | ir.cpp:70:5:70:5 | +| IntegerOps(int, int) -> void | 0 | 100 | Load | ir.cpp:70:5:70:10 | +| IntegerOps(int, int) -> void | 0 | 101 | Mul | ir.cpp:70:5:70:10 | +| IntegerOps(int, int) -> void | 0 | 102 | Store | ir.cpp:70:5:70:10 | +| IntegerOps(int, int) -> void | 0 | 103 | VariableAddress[x] | ir.cpp:71:10:71:10 | +| IntegerOps(int, int) -> void | 0 | 104 | Load | ir.cpp:71:10:71:10 | +| IntegerOps(int, int) -> void | 0 | 105 | VariableAddress[z] | ir.cpp:71:5:71:5 | +| IntegerOps(int, int) -> void | 0 | 106 | Load | ir.cpp:71:5:71:10 | +| IntegerOps(int, int) -> void | 0 | 107 | Div | ir.cpp:71:5:71:10 | +| IntegerOps(int, int) -> void | 0 | 108 | Store | ir.cpp:71:5:71:10 | +| IntegerOps(int, int) -> void | 0 | 109 | VariableAddress[x] | ir.cpp:72:10:72:10 | +| IntegerOps(int, int) -> void | 0 | 110 | Load | ir.cpp:72:10:72:10 | +| IntegerOps(int, int) -> void | 0 | 111 | VariableAddress[z] | ir.cpp:72:5:72:5 | +| IntegerOps(int, int) -> void | 0 | 112 | Load | ir.cpp:72:5:72:10 | +| IntegerOps(int, int) -> void | 0 | 113 | Rem | ir.cpp:72:5:72:10 | +| IntegerOps(int, int) -> void | 0 | 114 | Store | ir.cpp:72:5:72:10 | +| IntegerOps(int, int) -> void | 0 | 115 | VariableAddress[x] | ir.cpp:74:10:74:10 | +| IntegerOps(int, int) -> void | 0 | 116 | Load | ir.cpp:74:10:74:10 | +| IntegerOps(int, int) -> void | 0 | 117 | VariableAddress[z] | ir.cpp:74:5:74:5 | +| IntegerOps(int, int) -> void | 0 | 118 | Load | ir.cpp:74:5:74:10 | +| IntegerOps(int, int) -> void | 0 | 119 | BitAnd | ir.cpp:74:5:74:10 | +| IntegerOps(int, int) -> void | 0 | 120 | Store | ir.cpp:74:5:74:10 | +| IntegerOps(int, int) -> void | 0 | 121 | VariableAddress[x] | ir.cpp:75:10:75:10 | +| IntegerOps(int, int) -> void | 0 | 122 | Load | ir.cpp:75:10:75:10 | +| IntegerOps(int, int) -> void | 0 | 123 | VariableAddress[z] | ir.cpp:75:5:75:5 | +| IntegerOps(int, int) -> void | 0 | 124 | Load | ir.cpp:75:5:75:10 | +| IntegerOps(int, int) -> void | 0 | 125 | BitOr | ir.cpp:75:5:75:10 | +| IntegerOps(int, int) -> void | 0 | 126 | Store | ir.cpp:75:5:75:10 | +| IntegerOps(int, int) -> void | 0 | 127 | VariableAddress[x] | ir.cpp:76:10:76:10 | +| IntegerOps(int, int) -> void | 0 | 128 | Load | ir.cpp:76:10:76:10 | +| IntegerOps(int, int) -> void | 0 | 129 | VariableAddress[z] | ir.cpp:76:5:76:5 | +| IntegerOps(int, int) -> void | 0 | 130 | Load | ir.cpp:76:5:76:10 | +| IntegerOps(int, int) -> void | 0 | 131 | BitXor | ir.cpp:76:5:76:10 | +| IntegerOps(int, int) -> void | 0 | 132 | Store | ir.cpp:76:5:76:10 | +| IntegerOps(int, int) -> void | 0 | 133 | VariableAddress[x] | ir.cpp:78:11:78:11 | +| IntegerOps(int, int) -> void | 0 | 134 | Load | ir.cpp:78:11:78:11 | +| IntegerOps(int, int) -> void | 0 | 135 | VariableAddress[z] | ir.cpp:78:5:78:5 | +| IntegerOps(int, int) -> void | 0 | 136 | Load | ir.cpp:78:5:78:11 | +| IntegerOps(int, int) -> void | 0 | 137 | ShiftLeft | ir.cpp:78:5:78:11 | +| IntegerOps(int, int) -> void | 0 | 138 | Store | ir.cpp:78:5:78:11 | +| IntegerOps(int, int) -> void | 0 | 139 | VariableAddress[x] | ir.cpp:79:11:79:11 | +| IntegerOps(int, int) -> void | 0 | 140 | Load | ir.cpp:79:11:79:11 | +| IntegerOps(int, int) -> void | 0 | 141 | VariableAddress[z] | ir.cpp:79:5:79:5 | +| IntegerOps(int, int) -> void | 0 | 142 | Load | ir.cpp:79:5:79:11 | +| IntegerOps(int, int) -> void | 0 | 143 | ShiftRight | ir.cpp:79:5:79:11 | +| IntegerOps(int, int) -> void | 0 | 144 | Store | ir.cpp:79:5:79:11 | +| IntegerOps(int, int) -> void | 0 | 145 | VariableAddress[x] | ir.cpp:81:10:81:10 | +| IntegerOps(int, int) -> void | 0 | 146 | Load | ir.cpp:81:10:81:10 | +| IntegerOps(int, int) -> void | 0 | 147 | CopyValue | ir.cpp:81:9:81:10 | +| IntegerOps(int, int) -> void | 0 | 148 | VariableAddress[z] | ir.cpp:81:5:81:5 | +| IntegerOps(int, int) -> void | 0 | 149 | Store | ir.cpp:81:5:81:10 | +| IntegerOps(int, int) -> void | 0 | 150 | VariableAddress[x] | ir.cpp:82:10:82:10 | +| IntegerOps(int, int) -> void | 0 | 151 | Load | ir.cpp:82:10:82:10 | +| IntegerOps(int, int) -> void | 0 | 152 | Negate | ir.cpp:82:9:82:10 | +| IntegerOps(int, int) -> void | 0 | 153 | VariableAddress[z] | ir.cpp:82:5:82:5 | +| IntegerOps(int, int) -> void | 0 | 154 | Store | ir.cpp:82:5:82:10 | +| IntegerOps(int, int) -> void | 0 | 155 | VariableAddress[x] | ir.cpp:83:10:83:10 | +| IntegerOps(int, int) -> void | 0 | 156 | Load | ir.cpp:83:10:83:10 | +| IntegerOps(int, int) -> void | 0 | 157 | BitComplement | ir.cpp:83:9:83:10 | +| IntegerOps(int, int) -> void | 0 | 158 | VariableAddress[z] | ir.cpp:83:5:83:5 | +| IntegerOps(int, int) -> void | 0 | 159 | Store | ir.cpp:83:5:83:10 | +| IntegerOps(int, int) -> void | 0 | 160 | VariableAddress[x] | ir.cpp:84:10:84:10 | +| IntegerOps(int, int) -> void | 0 | 161 | Load | ir.cpp:84:10:84:10 | +| IntegerOps(int, int) -> void | 0 | 162 | Constant[0] | ir.cpp:84:10:84:10 | +| IntegerOps(int, int) -> void | 0 | 163 | CompareNE | ir.cpp:84:10:84:10 | +| IntegerOps(int, int) -> void | 0 | 164 | LogicalNot | ir.cpp:84:9:84:10 | +| IntegerOps(int, int) -> void | 0 | 165 | Convert | ir.cpp:84:9:84:10 | +| IntegerOps(int, int) -> void | 0 | 166 | VariableAddress[z] | ir.cpp:84:5:84:5 | +| IntegerOps(int, int) -> void | 0 | 167 | Store | ir.cpp:84:5:84:10 | +| IntegerOps(int, int) -> void | 0 | 168 | NoOp | ir.cpp:85:1:85:1 | +| IntegerOps(int, int) -> void | 0 | 169 | ReturnVoid | ir.cpp:50:6:50:15 | +| IntegerOps(int, int) -> void | 0 | 170 | UnmodeledUse | ir.cpp:50:6:50:15 | +| IntegerOps(int, int) -> void | 0 | 171 | ExitFunction | ir.cpp:50:6:50:15 | +| LogicalAnd(bool, bool) -> void | 0 | 0 | EnterFunction | ir.cpp:447:6:447:15 | +| LogicalAnd(bool, bool) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:447:6:447:15 | +| LogicalAnd(bool, bool) -> void | 0 | 2 | InitializeParameter[a] | ir.cpp:447:22:447:22 | +| LogicalAnd(bool, bool) -> void | 0 | 3 | VariableAddress[a] | ir.cpp:447:22:447:22 | +| LogicalAnd(bool, bool) -> void | 0 | 4 | Store | ir.cpp:447:22:447:22 | +| LogicalAnd(bool, bool) -> void | 0 | 5 | InitializeParameter[b] | ir.cpp:447:30:447:30 | +| LogicalAnd(bool, bool) -> void | 0 | 6 | VariableAddress[b] | ir.cpp:447:30:447:30 | +| LogicalAnd(bool, bool) -> void | 0 | 7 | Store | ir.cpp:447:30:447:30 | +| LogicalAnd(bool, bool) -> void | 0 | 8 | VariableAddress[x] | ir.cpp:448:9:448:9 | +| LogicalAnd(bool, bool) -> void | 0 | 9 | Uninitialized | ir.cpp:448:9:448:9 | +| LogicalAnd(bool, bool) -> void | 0 | 10 | Store | ir.cpp:448:9:448:9 | +| LogicalAnd(bool, bool) -> void | 0 | 11 | VariableAddress[a] | ir.cpp:449:9:449:9 | +| LogicalAnd(bool, bool) -> void | 0 | 12 | Load | ir.cpp:449:9:449:9 | +| LogicalAnd(bool, bool) -> void | 0 | 13 | ConditionalBranch | ir.cpp:449:9:449:9 | +| LogicalAnd(bool, bool) -> void | 1 | 0 | VariableAddress[b] | ir.cpp:449:14:449:14 | +| LogicalAnd(bool, bool) -> void | 1 | 1 | Load | ir.cpp:449:14:449:14 | +| LogicalAnd(bool, bool) -> void | 1 | 2 | ConditionalBranch | ir.cpp:449:14:449:14 | +| LogicalAnd(bool, bool) -> void | 2 | 0 | Constant[7] | ir.cpp:450:13:450:13 | +| LogicalAnd(bool, bool) -> void | 2 | 1 | VariableAddress[x] | ir.cpp:450:9:450:9 | +| LogicalAnd(bool, bool) -> void | 2 | 2 | Store | ir.cpp:450:9:450:13 | +| LogicalAnd(bool, bool) -> void | 3 | 0 | VariableAddress[a] | ir.cpp:453:9:453:9 | +| LogicalAnd(bool, bool) -> void | 3 | 1 | Load | ir.cpp:453:9:453:9 | +| LogicalAnd(bool, bool) -> void | 3 | 2 | ConditionalBranch | ir.cpp:453:9:453:9 | +| LogicalAnd(bool, bool) -> void | 4 | 0 | VariableAddress[b] | ir.cpp:453:14:453:14 | +| LogicalAnd(bool, bool) -> void | 4 | 1 | Load | ir.cpp:453:14:453:14 | +| LogicalAnd(bool, bool) -> void | 4 | 2 | ConditionalBranch | ir.cpp:453:14:453:14 | +| LogicalAnd(bool, bool) -> void | 5 | 0 | Constant[1] | ir.cpp:454:13:454:13 | +| LogicalAnd(bool, bool) -> void | 5 | 1 | VariableAddress[x] | ir.cpp:454:9:454:9 | +| LogicalAnd(bool, bool) -> void | 5 | 2 | Store | ir.cpp:454:9:454:13 | +| LogicalAnd(bool, bool) -> void | 6 | 0 | Constant[5] | ir.cpp:457:13:457:13 | +| LogicalAnd(bool, bool) -> void | 6 | 1 | VariableAddress[x] | ir.cpp:457:9:457:9 | +| LogicalAnd(bool, bool) -> void | 6 | 2 | Store | ir.cpp:457:9:457:13 | +| LogicalAnd(bool, bool) -> void | 7 | 0 | NoOp | ir.cpp:459:1:459:1 | +| LogicalAnd(bool, bool) -> void | 7 | 1 | ReturnVoid | ir.cpp:447:6:447:15 | +| LogicalAnd(bool, bool) -> void | 7 | 2 | UnmodeledUse | ir.cpp:447:6:447:15 | +| LogicalAnd(bool, bool) -> void | 7 | 3 | ExitFunction | ir.cpp:447:6:447:15 | +| LogicalNot(bool, bool) -> void | 0 | 0 | EnterFunction | ir.cpp:461:6:461:15 | +| LogicalNot(bool, bool) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:461:6:461:15 | +| LogicalNot(bool, bool) -> void | 0 | 2 | InitializeParameter[a] | ir.cpp:461:22:461:22 | +| LogicalNot(bool, bool) -> void | 0 | 3 | VariableAddress[a] | ir.cpp:461:22:461:22 | +| LogicalNot(bool, bool) -> void | 0 | 4 | Store | ir.cpp:461:22:461:22 | +| LogicalNot(bool, bool) -> void | 0 | 5 | InitializeParameter[b] | ir.cpp:461:30:461:30 | +| LogicalNot(bool, bool) -> void | 0 | 6 | VariableAddress[b] | ir.cpp:461:30:461:30 | +| LogicalNot(bool, bool) -> void | 0 | 7 | Store | ir.cpp:461:30:461:30 | +| LogicalNot(bool, bool) -> void | 0 | 8 | VariableAddress[x] | ir.cpp:462:9:462:9 | +| LogicalNot(bool, bool) -> void | 0 | 9 | Uninitialized | ir.cpp:462:9:462:9 | +| LogicalNot(bool, bool) -> void | 0 | 10 | Store | ir.cpp:462:9:462:9 | +| LogicalNot(bool, bool) -> void | 0 | 11 | VariableAddress[a] | ir.cpp:463:10:463:10 | +| LogicalNot(bool, bool) -> void | 0 | 12 | Load | ir.cpp:463:10:463:10 | +| LogicalNot(bool, bool) -> void | 0 | 13 | ConditionalBranch | ir.cpp:463:10:463:10 | +| LogicalNot(bool, bool) -> void | 1 | 0 | Constant[1] | ir.cpp:464:13:464:13 | +| LogicalNot(bool, bool) -> void | 1 | 1 | VariableAddress[x] | ir.cpp:464:9:464:9 | +| LogicalNot(bool, bool) -> void | 1 | 2 | Store | ir.cpp:464:9:464:13 | +| LogicalNot(bool, bool) -> void | 2 | 0 | VariableAddress[a] | ir.cpp:467:11:467:11 | +| LogicalNot(bool, bool) -> void | 2 | 1 | Load | ir.cpp:467:11:467:11 | +| LogicalNot(bool, bool) -> void | 2 | 2 | ConditionalBranch | ir.cpp:467:11:467:11 | +| LogicalNot(bool, bool) -> void | 3 | 0 | VariableAddress[b] | ir.cpp:467:16:467:16 | +| LogicalNot(bool, bool) -> void | 3 | 1 | Load | ir.cpp:467:16:467:16 | +| LogicalNot(bool, bool) -> void | 3 | 2 | ConditionalBranch | ir.cpp:467:16:467:16 | +| LogicalNot(bool, bool) -> void | 4 | 0 | Constant[2] | ir.cpp:468:13:468:13 | +| LogicalNot(bool, bool) -> void | 4 | 1 | VariableAddress[x] | ir.cpp:468:9:468:9 | +| LogicalNot(bool, bool) -> void | 4 | 2 | Store | ir.cpp:468:9:468:13 | +| LogicalNot(bool, bool) -> void | 5 | 0 | Constant[3] | ir.cpp:471:13:471:13 | +| LogicalNot(bool, bool) -> void | 5 | 1 | VariableAddress[x] | ir.cpp:471:9:471:9 | +| LogicalNot(bool, bool) -> void | 5 | 2 | Store | ir.cpp:471:9:471:13 | +| LogicalNot(bool, bool) -> void | 6 | 0 | NoOp | ir.cpp:473:1:473:1 | +| LogicalNot(bool, bool) -> void | 6 | 1 | ReturnVoid | ir.cpp:461:6:461:15 | +| LogicalNot(bool, bool) -> void | 6 | 2 | UnmodeledUse | ir.cpp:461:6:461:15 | +| LogicalNot(bool, bool) -> void | 6 | 3 | ExitFunction | ir.cpp:461:6:461:15 | +| LogicalOr(bool, bool) -> void | 0 | 0 | EnterFunction | ir.cpp:433:6:433:14 | +| LogicalOr(bool, bool) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:433:6:433:14 | +| LogicalOr(bool, bool) -> void | 0 | 2 | InitializeParameter[a] | ir.cpp:433:21:433:21 | +| LogicalOr(bool, bool) -> void | 0 | 3 | VariableAddress[a] | ir.cpp:433:21:433:21 | +| LogicalOr(bool, bool) -> void | 0 | 4 | Store | ir.cpp:433:21:433:21 | +| LogicalOr(bool, bool) -> void | 0 | 5 | InitializeParameter[b] | ir.cpp:433:29:433:29 | +| LogicalOr(bool, bool) -> void | 0 | 6 | VariableAddress[b] | ir.cpp:433:29:433:29 | +| LogicalOr(bool, bool) -> void | 0 | 7 | Store | ir.cpp:433:29:433:29 | +| LogicalOr(bool, bool) -> void | 0 | 8 | VariableAddress[x] | ir.cpp:434:9:434:9 | +| LogicalOr(bool, bool) -> void | 0 | 9 | Uninitialized | ir.cpp:434:9:434:9 | +| LogicalOr(bool, bool) -> void | 0 | 10 | Store | ir.cpp:434:9:434:9 | +| LogicalOr(bool, bool) -> void | 0 | 11 | VariableAddress[a] | ir.cpp:435:9:435:9 | +| LogicalOr(bool, bool) -> void | 0 | 12 | Load | ir.cpp:435:9:435:9 | +| LogicalOr(bool, bool) -> void | 0 | 13 | ConditionalBranch | ir.cpp:435:9:435:9 | +| LogicalOr(bool, bool) -> void | 1 | 0 | VariableAddress[b] | ir.cpp:435:14:435:14 | +| LogicalOr(bool, bool) -> void | 1 | 1 | Load | ir.cpp:435:14:435:14 | +| LogicalOr(bool, bool) -> void | 1 | 2 | ConditionalBranch | ir.cpp:435:14:435:14 | +| LogicalOr(bool, bool) -> void | 2 | 0 | Constant[7] | ir.cpp:436:13:436:13 | +| LogicalOr(bool, bool) -> void | 2 | 1 | VariableAddress[x] | ir.cpp:436:9:436:9 | +| LogicalOr(bool, bool) -> void | 2 | 2 | Store | ir.cpp:436:9:436:13 | +| LogicalOr(bool, bool) -> void | 3 | 0 | VariableAddress[a] | ir.cpp:439:9:439:9 | +| LogicalOr(bool, bool) -> void | 3 | 1 | Load | ir.cpp:439:9:439:9 | +| LogicalOr(bool, bool) -> void | 3 | 2 | ConditionalBranch | ir.cpp:439:9:439:9 | +| LogicalOr(bool, bool) -> void | 4 | 0 | VariableAddress[b] | ir.cpp:439:14:439:14 | +| LogicalOr(bool, bool) -> void | 4 | 1 | Load | ir.cpp:439:14:439:14 | +| LogicalOr(bool, bool) -> void | 4 | 2 | ConditionalBranch | ir.cpp:439:14:439:14 | +| LogicalOr(bool, bool) -> void | 5 | 0 | Constant[1] | ir.cpp:440:13:440:13 | +| LogicalOr(bool, bool) -> void | 5 | 1 | VariableAddress[x] | ir.cpp:440:9:440:9 | +| LogicalOr(bool, bool) -> void | 5 | 2 | Store | ir.cpp:440:9:440:13 | +| LogicalOr(bool, bool) -> void | 6 | 0 | Constant[5] | ir.cpp:443:13:443:13 | +| LogicalOr(bool, bool) -> void | 6 | 1 | VariableAddress[x] | ir.cpp:443:9:443:9 | +| LogicalOr(bool, bool) -> void | 6 | 2 | Store | ir.cpp:443:9:443:13 | +| LogicalOr(bool, bool) -> void | 7 | 0 | NoOp | ir.cpp:445:1:445:1 | +| LogicalOr(bool, bool) -> void | 7 | 1 | ReturnVoid | ir.cpp:433:6:433:14 | +| LogicalOr(bool, bool) -> void | 7 | 2 | UnmodeledUse | ir.cpp:433:6:433:14 | +| LogicalOr(bool, bool) -> void | 7 | 3 | ExitFunction | ir.cpp:433:6:433:14 | +| Middle::Middle() -> void | 0 | 0 | EnterFunction | ir.cpp:757:3:757:8 | +| Middle::Middle() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:757:3:757:8 | +| Middle::Middle() -> void | 0 | 2 | InitializeThis | ir.cpp:757:3:757:8 | +| Middle::Middle() -> void | 0 | 3 | ConvertToBase[Middle : Base] | ir.cpp:757:12:757:12 | +| Middle::Middle() -> void | 0 | 4 | FunctionAddress[Base] | ir.cpp:757:12:757:12 | +| Middle::Middle() -> void | 0 | 5 | Invoke | ir.cpp:757:12:757:12 | +| Middle::Middle() -> void | 0 | 6 | FieldAddress[middle_s] | ir.cpp:757:12:757:12 | +| Middle::Middle() -> void | 0 | 7 | FunctionAddress[String] | ir.cpp:757:12:757:12 | +| Middle::Middle() -> void | 0 | 8 | Invoke | ir.cpp:757:12:757:12 | +| Middle::Middle() -> void | 0 | 9 | NoOp | ir.cpp:758:3:758:3 | +| Middle::Middle() -> void | 0 | 10 | ReturnVoid | ir.cpp:757:3:757:8 | +| Middle::Middle() -> void | 0 | 11 | UnmodeledUse | ir.cpp:757:3:757:8 | +| Middle::Middle() -> void | 0 | 12 | ExitFunction | ir.cpp:757:3:757:8 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 0 | EnterFunction | ir.cpp:754:8:754:8 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 1 | UnmodeledDefinition | ir.cpp:754:8:754:8 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 2 | InitializeThis | ir.cpp:754:8:754:8 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 3 | InitializeParameter[p#0] | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 4 | VariableAddress[p#0] | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 5 | Store | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 6 | CopyValue | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 7 | ConvertToBase[Middle : Base] | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 8 | FunctionAddress[operator=] | ir.cpp:754:8:754:8 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 9 | VariableAddress[p#0] | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 10 | Load | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 11 | ConvertToBase[Middle : Base] | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 12 | Invoke | ir.cpp:754:8:754:8 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 13 | CopyValue | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 14 | FieldAddress[middle_s] | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 15 | FunctionAddress[operator=] | ir.cpp:754:8:754:8 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 16 | VariableAddress[p#0] | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 17 | Load | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 18 | FieldAddress[middle_s] | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 19 | Invoke | ir.cpp:754:8:754:8 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 20 | VariableAddress[#return] | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 21 | CopyValue | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 22 | Store | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 23 | VariableAddress[#return] | ir.cpp:754:8:754:8 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 24 | ReturnValue | ir.cpp:754:8:754:8 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 25 | UnmodeledUse | ir.cpp:754:8:754:8 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 26 | ExitFunction | ir.cpp:754:8:754:8 | +| Middle::~Middle() -> void | 0 | 0 | EnterFunction | ir.cpp:759:3:759:9 | +| Middle::~Middle() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:759:3:759:9 | +| Middle::~Middle() -> void | 0 | 2 | InitializeThis | ir.cpp:759:3:759:9 | +| Middle::~Middle() -> void | 0 | 3 | NoOp | ir.cpp:760:3:760:3 | +| Middle::~Middle() -> void | 0 | 4 | FieldAddress[middle_s] | ir.cpp:760:3:760:3 | +| Middle::~Middle() -> void | 0 | 5 | FunctionAddress[~String] | ir.cpp:760:3:760:3 | +| Middle::~Middle() -> void | 0 | 6 | Invoke | ir.cpp:760:3:760:3 | +| Middle::~Middle() -> void | 0 | 7 | ConvertToBase[Middle : Base] | ir.cpp:760:3:760:3 | +| Middle::~Middle() -> void | 0 | 8 | FunctionAddress[~Base] | ir.cpp:760:3:760:3 | +| Middle::~Middle() -> void | 0 | 9 | Invoke | ir.cpp:760:3:760:3 | +| Middle::~Middle() -> void | 0 | 10 | ReturnVoid | ir.cpp:759:3:759:9 | +| Middle::~Middle() -> void | 0 | 11 | UnmodeledUse | ir.cpp:759:3:759:9 | +| Middle::~Middle() -> void | 0 | 12 | ExitFunction | ir.cpp:759:3:759:9 | +| MiddleVB1::MiddleVB1() -> void | 0 | 0 | EnterFunction | ir.cpp:775:3:775:11 | +| MiddleVB1::MiddleVB1() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:775:3:775:11 | +| MiddleVB1::MiddleVB1() -> void | 0 | 2 | InitializeThis | ir.cpp:775:3:775:11 | +| MiddleVB1::MiddleVB1() -> void | 0 | 3 | ConvertToBase[MiddleVB1 : Base] | ir.cpp:775:15:775:15 | +| MiddleVB1::MiddleVB1() -> void | 0 | 4 | FunctionAddress[Base] | ir.cpp:775:15:775:15 | +| MiddleVB1::MiddleVB1() -> void | 0 | 5 | Invoke | ir.cpp:775:15:775:15 | +| MiddleVB1::MiddleVB1() -> void | 0 | 6 | FieldAddress[middlevb1_s] | ir.cpp:775:15:775:15 | +| MiddleVB1::MiddleVB1() -> void | 0 | 7 | FunctionAddress[String] | ir.cpp:775:15:775:15 | +| MiddleVB1::MiddleVB1() -> void | 0 | 8 | Invoke | ir.cpp:775:15:775:15 | +| MiddleVB1::MiddleVB1() -> void | 0 | 9 | NoOp | ir.cpp:776:3:776:3 | +| MiddleVB1::MiddleVB1() -> void | 0 | 10 | ReturnVoid | ir.cpp:775:3:775:11 | +| MiddleVB1::MiddleVB1() -> void | 0 | 11 | UnmodeledUse | ir.cpp:775:3:775:11 | +| MiddleVB1::MiddleVB1() -> void | 0 | 12 | ExitFunction | ir.cpp:775:3:775:11 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 0 | EnterFunction | ir.cpp:777:3:777:12 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:777:3:777:12 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 2 | InitializeThis | ir.cpp:777:3:777:12 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 3 | NoOp | ir.cpp:778:3:778:3 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 4 | FieldAddress[middlevb1_s] | ir.cpp:778:3:778:3 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 5 | FunctionAddress[~String] | ir.cpp:778:3:778:3 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 6 | Invoke | ir.cpp:778:3:778:3 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 7 | ConvertToBase[MiddleVB1 : Base] | ir.cpp:778:3:778:3 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 8 | FunctionAddress[~Base] | ir.cpp:778:3:778:3 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 9 | Invoke | ir.cpp:778:3:778:3 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 10 | ReturnVoid | ir.cpp:777:3:777:12 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 11 | UnmodeledUse | ir.cpp:777:3:777:12 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 12 | ExitFunction | ir.cpp:777:3:777:12 | +| MiddleVB2::MiddleVB2() -> void | 0 | 0 | EnterFunction | ir.cpp:784:3:784:11 | +| MiddleVB2::MiddleVB2() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:784:3:784:11 | +| MiddleVB2::MiddleVB2() -> void | 0 | 2 | InitializeThis | ir.cpp:784:3:784:11 | +| MiddleVB2::MiddleVB2() -> void | 0 | 3 | ConvertToBase[MiddleVB2 : Base] | ir.cpp:784:15:784:15 | +| MiddleVB2::MiddleVB2() -> void | 0 | 4 | FunctionAddress[Base] | ir.cpp:784:15:784:15 | +| MiddleVB2::MiddleVB2() -> void | 0 | 5 | Invoke | ir.cpp:784:15:784:15 | +| MiddleVB2::MiddleVB2() -> void | 0 | 6 | FieldAddress[middlevb2_s] | ir.cpp:784:15:784:15 | +| MiddleVB2::MiddleVB2() -> void | 0 | 7 | FunctionAddress[String] | ir.cpp:784:15:784:15 | +| MiddleVB2::MiddleVB2() -> void | 0 | 8 | Invoke | ir.cpp:784:15:784:15 | +| MiddleVB2::MiddleVB2() -> void | 0 | 9 | NoOp | ir.cpp:785:3:785:3 | +| MiddleVB2::MiddleVB2() -> void | 0 | 10 | ReturnVoid | ir.cpp:784:3:784:11 | +| MiddleVB2::MiddleVB2() -> void | 0 | 11 | UnmodeledUse | ir.cpp:784:3:784:11 | +| MiddleVB2::MiddleVB2() -> void | 0 | 12 | ExitFunction | ir.cpp:784:3:784:11 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 0 | EnterFunction | ir.cpp:786:3:786:12 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:786:3:786:12 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 2 | InitializeThis | ir.cpp:786:3:786:12 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 3 | NoOp | ir.cpp:787:3:787:3 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 4 | FieldAddress[middlevb2_s] | ir.cpp:787:3:787:3 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 5 | FunctionAddress[~String] | ir.cpp:787:3:787:3 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 6 | Invoke | ir.cpp:787:3:787:3 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 7 | ConvertToBase[MiddleVB2 : Base] | ir.cpp:787:3:787:3 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 8 | FunctionAddress[~Base] | ir.cpp:787:3:787:3 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 9 | Invoke | ir.cpp:787:3:787:3 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 10 | ReturnVoid | ir.cpp:786:3:786:12 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 11 | UnmodeledUse | ir.cpp:786:3:786:12 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 12 | ExitFunction | ir.cpp:786:3:786:12 | +| NestedInitList(int, float) -> void | 0 | 0 | EnterFunction | ir.cpp:512:6:512:19 | +| NestedInitList(int, float) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:512:6:512:19 | +| NestedInitList(int, float) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:512:25:512:25 | +| NestedInitList(int, float) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:512:25:512:25 | +| NestedInitList(int, float) -> void | 0 | 4 | Store | ir.cpp:512:25:512:25 | +| NestedInitList(int, float) -> void | 0 | 5 | InitializeParameter[f] | ir.cpp:512:34:512:34 | +| NestedInitList(int, float) -> void | 0 | 6 | VariableAddress[f] | ir.cpp:512:34:512:34 | +| NestedInitList(int, float) -> void | 0 | 7 | Store | ir.cpp:512:34:512:34 | +| NestedInitList(int, float) -> void | 0 | 8 | VariableAddress[r1] | ir.cpp:513:10:513:11 | +| NestedInitList(int, float) -> void | 0 | 9 | FieldAddress[topLeft] | ir.cpp:513:14:513:16 | +| NestedInitList(int, float) -> void | 0 | 10 | Constant[0] | ir.cpp:513:14:513:16 | +| NestedInitList(int, float) -> void | 0 | 11 | Store | ir.cpp:513:14:513:16 | +| NestedInitList(int, float) -> void | 0 | 12 | FieldAddress[bottomRight] | ir.cpp:513:14:513:16 | +| NestedInitList(int, float) -> void | 0 | 13 | Constant[0] | ir.cpp:513:14:513:16 | +| NestedInitList(int, float) -> void | 0 | 14 | Store | ir.cpp:513:14:513:16 | +| NestedInitList(int, float) -> void | 0 | 15 | VariableAddress[r2] | ir.cpp:514:10:514:11 | +| NestedInitList(int, float) -> void | 0 | 16 | FieldAddress[topLeft] | ir.cpp:514:14:514:26 | +| NestedInitList(int, float) -> void | 0 | 17 | FieldAddress[x] | ir.cpp:514:17:514:24 | +| NestedInitList(int, float) -> void | 0 | 18 | VariableAddress[x] | ir.cpp:514:19:514:19 | +| NestedInitList(int, float) -> void | 0 | 19 | Load | ir.cpp:514:19:514:19 | +| NestedInitList(int, float) -> void | 0 | 20 | Store | ir.cpp:514:19:514:19 | +| NestedInitList(int, float) -> void | 0 | 21 | FieldAddress[y] | ir.cpp:514:17:514:24 | +| NestedInitList(int, float) -> void | 0 | 22 | VariableAddress[f] | ir.cpp:514:22:514:22 | +| NestedInitList(int, float) -> void | 0 | 23 | Load | ir.cpp:514:22:514:22 | +| NestedInitList(int, float) -> void | 0 | 24 | Convert | ir.cpp:514:22:514:22 | +| NestedInitList(int, float) -> void | 0 | 25 | Store | ir.cpp:514:22:514:22 | +| NestedInitList(int, float) -> void | 0 | 26 | FieldAddress[bottomRight] | ir.cpp:514:14:514:26 | +| NestedInitList(int, float) -> void | 0 | 27 | Constant[0] | ir.cpp:514:14:514:26 | +| NestedInitList(int, float) -> void | 0 | 28 | Store | ir.cpp:514:14:514:26 | +| NestedInitList(int, float) -> void | 0 | 29 | VariableAddress[r3] | ir.cpp:515:10:515:11 | +| NestedInitList(int, float) -> void | 0 | 30 | FieldAddress[topLeft] | ir.cpp:515:14:515:36 | +| NestedInitList(int, float) -> void | 0 | 31 | FieldAddress[x] | ir.cpp:515:17:515:24 | +| NestedInitList(int, float) -> void | 0 | 32 | VariableAddress[x] | ir.cpp:515:19:515:19 | +| NestedInitList(int, float) -> void | 0 | 33 | Load | ir.cpp:515:19:515:19 | +| NestedInitList(int, float) -> void | 0 | 34 | Store | ir.cpp:515:19:515:19 | +| NestedInitList(int, float) -> void | 0 | 35 | FieldAddress[y] | ir.cpp:515:17:515:24 | +| NestedInitList(int, float) -> void | 0 | 36 | VariableAddress[f] | ir.cpp:515:22:515:22 | +| NestedInitList(int, float) -> void | 0 | 37 | Load | ir.cpp:515:22:515:22 | +| NestedInitList(int, float) -> void | 0 | 38 | Convert | ir.cpp:515:22:515:22 | +| NestedInitList(int, float) -> void | 0 | 39 | Store | ir.cpp:515:22:515:22 | +| NestedInitList(int, float) -> void | 0 | 40 | FieldAddress[bottomRight] | ir.cpp:515:14:515:36 | +| NestedInitList(int, float) -> void | 0 | 41 | FieldAddress[x] | ir.cpp:515:27:515:34 | +| NestedInitList(int, float) -> void | 0 | 42 | VariableAddress[x] | ir.cpp:515:29:515:29 | +| NestedInitList(int, float) -> void | 0 | 43 | Load | ir.cpp:515:29:515:29 | +| NestedInitList(int, float) -> void | 0 | 44 | Store | ir.cpp:515:29:515:29 | +| NestedInitList(int, float) -> void | 0 | 45 | FieldAddress[y] | ir.cpp:515:27:515:34 | +| NestedInitList(int, float) -> void | 0 | 46 | VariableAddress[f] | ir.cpp:515:32:515:32 | +| NestedInitList(int, float) -> void | 0 | 47 | Load | ir.cpp:515:32:515:32 | +| NestedInitList(int, float) -> void | 0 | 48 | Convert | ir.cpp:515:32:515:32 | +| NestedInitList(int, float) -> void | 0 | 49 | Store | ir.cpp:515:32:515:32 | +| NestedInitList(int, float) -> void | 0 | 50 | VariableAddress[r4] | ir.cpp:516:10:516:11 | +| NestedInitList(int, float) -> void | 0 | 51 | FieldAddress[topLeft] | ir.cpp:516:14:516:30 | +| NestedInitList(int, float) -> void | 0 | 52 | FieldAddress[x] | ir.cpp:516:17:516:21 | +| NestedInitList(int, float) -> void | 0 | 53 | VariableAddress[x] | ir.cpp:516:19:516:19 | +| NestedInitList(int, float) -> void | 0 | 54 | Load | ir.cpp:516:19:516:19 | +| NestedInitList(int, float) -> void | 0 | 55 | Store | ir.cpp:516:19:516:19 | +| NestedInitList(int, float) -> void | 0 | 56 | FieldAddress[y] | ir.cpp:516:17:516:21 | +| NestedInitList(int, float) -> void | 0 | 57 | Constant[0] | ir.cpp:516:17:516:21 | +| NestedInitList(int, float) -> void | 0 | 58 | Store | ir.cpp:516:17:516:21 | +| NestedInitList(int, float) -> void | 0 | 59 | FieldAddress[bottomRight] | ir.cpp:516:14:516:30 | +| NestedInitList(int, float) -> void | 0 | 60 | FieldAddress[x] | ir.cpp:516:24:516:28 | +| NestedInitList(int, float) -> void | 0 | 61 | VariableAddress[x] | ir.cpp:516:26:516:26 | +| NestedInitList(int, float) -> void | 0 | 62 | Load | ir.cpp:516:26:516:26 | +| NestedInitList(int, float) -> void | 0 | 63 | Store | ir.cpp:516:26:516:26 | +| NestedInitList(int, float) -> void | 0 | 64 | FieldAddress[y] | ir.cpp:516:24:516:28 | +| NestedInitList(int, float) -> void | 0 | 65 | Constant[0] | ir.cpp:516:24:516:28 | +| NestedInitList(int, float) -> void | 0 | 66 | Store | ir.cpp:516:24:516:28 | +| NestedInitList(int, float) -> void | 0 | 67 | NoOp | ir.cpp:517:1:517:1 | +| NestedInitList(int, float) -> void | 0 | 68 | ReturnVoid | ir.cpp:512:6:512:19 | +| NestedInitList(int, float) -> void | 0 | 69 | UnmodeledUse | ir.cpp:512:6:512:19 | +| NestedInitList(int, float) -> void | 0 | 70 | ExitFunction | ir.cpp:512:6:512:19 | +| Nullptr() -> void | 0 | 0 | EnterFunction | ir.cpp:496:6:496:12 | +| Nullptr() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:496:6:496:12 | +| Nullptr() -> void | 0 | 2 | VariableAddress[p] | ir.cpp:497:10:497:10 | +| Nullptr() -> void | 0 | 3 | Constant[0] | ir.cpp:497:14:497:20 | +| Nullptr() -> void | 0 | 4 | Store | ir.cpp:497:14:497:20 | +| Nullptr() -> void | 0 | 5 | VariableAddress[q] | ir.cpp:498:10:498:10 | +| Nullptr() -> void | 0 | 6 | Constant[0] | ir.cpp:498:14:498:14 | +| Nullptr() -> void | 0 | 7 | Store | ir.cpp:498:14:498:14 | +| Nullptr() -> void | 0 | 8 | Constant[0] | ir.cpp:499:9:499:15 | +| Nullptr() -> void | 0 | 9 | VariableAddress[p] | ir.cpp:499:5:499:5 | +| Nullptr() -> void | 0 | 10 | Store | ir.cpp:499:5:499:15 | +| Nullptr() -> void | 0 | 11 | Constant[0] | ir.cpp:500:9:500:9 | +| Nullptr() -> void | 0 | 12 | VariableAddress[q] | ir.cpp:500:5:500:5 | +| Nullptr() -> void | 0 | 13 | Store | ir.cpp:500:5:500:9 | +| Nullptr() -> void | 0 | 14 | NoOp | ir.cpp:501:1:501:1 | +| Nullptr() -> void | 0 | 15 | ReturnVoid | ir.cpp:496:6:496:12 | +| Nullptr() -> void | 0 | 16 | UnmodeledUse | ir.cpp:496:6:496:12 | +| Nullptr() -> void | 0 | 17 | ExitFunction | ir.cpp:496:6:496:12 | +| Outer::Func(void *, char) -> long | 0 | 0 | EnterFunction | ir.cpp:715:12:715:15 | +| Outer::Func(void *, char) -> long | 0 | 1 | UnmodeledDefinition | ir.cpp:715:12:715:15 | +| Outer::Func(void *, char) -> long | 0 | 2 | InitializeParameter[x] | ir.cpp:715:19:715:19 | +| Outer::Func(void *, char) -> long | 0 | 3 | VariableAddress[x] | ir.cpp:715:19:715:19 | +| Outer::Func(void *, char) -> long | 0 | 4 | Store | ir.cpp:715:19:715:19 | +| Outer::Func(void *, char) -> long | 0 | 5 | InitializeParameter[y] | ir.cpp:715:24:715:24 | +| Outer::Func(void *, char) -> long | 0 | 6 | VariableAddress[y] | ir.cpp:715:24:715:24 | +| Outer::Func(void *, char) -> long | 0 | 7 | Store | ir.cpp:715:24:715:24 | +| Outer::Func(void *, char) -> long | 0 | 8 | VariableAddress[#return] | ir.cpp:716:5:716:15 | +| Outer::Func(void *, char) -> long | 0 | 9 | Constant[0] | ir.cpp:716:12:716:14 | +| Outer::Func(void *, char) -> long | 0 | 10 | Store | ir.cpp:716:12:716:14 | +| Outer::Func(void *, char) -> long | 0 | 11 | VariableAddress[#return] | ir.cpp:715:12:715:15 | +| Outer::Func(void *, char) -> long | 0 | 12 | ReturnValue | ir.cpp:715:12:715:15 | +| Outer::Func(void *, char) -> long | 0 | 13 | UnmodeledUse | ir.cpp:715:12:715:15 | +| Outer::Func(void *, char) -> long | 0 | 14 | ExitFunction | ir.cpp:715:12:715:15 | +| Parameters(int, int) -> int | 0 | 0 | EnterFunction | ir.cpp:235:5:235:14 | +| Parameters(int, int) -> int | 0 | 1 | UnmodeledDefinition | ir.cpp:235:5:235:14 | +| Parameters(int, int) -> int | 0 | 2 | InitializeParameter[x] | ir.cpp:235:20:235:20 | +| Parameters(int, int) -> int | 0 | 3 | VariableAddress[x] | ir.cpp:235:20:235:20 | +| Parameters(int, int) -> int | 0 | 4 | Store | ir.cpp:235:20:235:20 | +| Parameters(int, int) -> int | 0 | 5 | InitializeParameter[y] | ir.cpp:235:27:235:27 | +| Parameters(int, int) -> int | 0 | 6 | VariableAddress[y] | ir.cpp:235:27:235:27 | +| Parameters(int, int) -> int | 0 | 7 | Store | ir.cpp:235:27:235:27 | +| Parameters(int, int) -> int | 0 | 8 | VariableAddress[#return] | ir.cpp:236:5:236:17 | +| Parameters(int, int) -> int | 0 | 9 | VariableAddress[x] | ir.cpp:236:12:236:12 | +| Parameters(int, int) -> int | 0 | 10 | Load | ir.cpp:236:12:236:12 | +| Parameters(int, int) -> int | 0 | 11 | VariableAddress[y] | ir.cpp:236:16:236:16 | +| Parameters(int, int) -> int | 0 | 12 | Load | ir.cpp:236:16:236:16 | +| Parameters(int, int) -> int | 0 | 13 | Rem | ir.cpp:236:12:236:16 | +| Parameters(int, int) -> int | 0 | 14 | Store | ir.cpp:236:12:236:16 | +| Parameters(int, int) -> int | 0 | 15 | VariableAddress[#return] | ir.cpp:235:5:235:14 | +| Parameters(int, int) -> int | 0 | 16 | ReturnValue | ir.cpp:235:5:235:14 | +| Parameters(int, int) -> int | 0 | 17 | UnmodeledUse | ir.cpp:235:5:235:14 | +| Parameters(int, int) -> int | 0 | 18 | ExitFunction | ir.cpp:235:5:235:14 | +| PointerCompare(int *, int *) -> void | 0 | 0 | EnterFunction | ir.cpp:193:6:193:19 | +| PointerCompare(int *, int *) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:193:6:193:19 | +| PointerCompare(int *, int *) -> void | 0 | 2 | InitializeParameter[p] | ir.cpp:193:26:193:26 | +| PointerCompare(int *, int *) -> void | 0 | 3 | VariableAddress[p] | ir.cpp:193:26:193:26 | +| PointerCompare(int *, int *) -> void | 0 | 4 | Store | ir.cpp:193:26:193:26 | +| PointerCompare(int *, int *) -> void | 0 | 5 | InitializeParameter[q] | ir.cpp:193:34:193:34 | +| PointerCompare(int *, int *) -> void | 0 | 6 | VariableAddress[q] | ir.cpp:193:34:193:34 | +| PointerCompare(int *, int *) -> void | 0 | 7 | Store | ir.cpp:193:34:193:34 | +| PointerCompare(int *, int *) -> void | 0 | 8 | VariableAddress[b] | ir.cpp:194:10:194:10 | +| PointerCompare(int *, int *) -> void | 0 | 9 | Uninitialized | ir.cpp:194:10:194:10 | +| PointerCompare(int *, int *) -> void | 0 | 10 | Store | ir.cpp:194:10:194:10 | +| PointerCompare(int *, int *) -> void | 0 | 11 | VariableAddress[p] | ir.cpp:196:9:196:9 | +| PointerCompare(int *, int *) -> void | 0 | 12 | Load | ir.cpp:196:9:196:9 | +| PointerCompare(int *, int *) -> void | 0 | 13 | VariableAddress[q] | ir.cpp:196:14:196:14 | +| PointerCompare(int *, int *) -> void | 0 | 14 | Load | ir.cpp:196:14:196:14 | +| PointerCompare(int *, int *) -> void | 0 | 15 | CompareEQ | ir.cpp:196:9:196:14 | +| PointerCompare(int *, int *) -> void | 0 | 16 | VariableAddress[b] | ir.cpp:196:5:196:5 | +| PointerCompare(int *, int *) -> void | 0 | 17 | Store | ir.cpp:196:5:196:14 | +| PointerCompare(int *, int *) -> void | 0 | 18 | VariableAddress[p] | ir.cpp:197:9:197:9 | +| PointerCompare(int *, int *) -> void | 0 | 19 | Load | ir.cpp:197:9:197:9 | +| PointerCompare(int *, int *) -> void | 0 | 20 | VariableAddress[q] | ir.cpp:197:14:197:14 | +| PointerCompare(int *, int *) -> void | 0 | 21 | Load | ir.cpp:197:14:197:14 | +| PointerCompare(int *, int *) -> void | 0 | 22 | CompareNE | ir.cpp:197:9:197:14 | +| PointerCompare(int *, int *) -> void | 0 | 23 | VariableAddress[b] | ir.cpp:197:5:197:5 | +| PointerCompare(int *, int *) -> void | 0 | 24 | Store | ir.cpp:197:5:197:14 | +| PointerCompare(int *, int *) -> void | 0 | 25 | VariableAddress[p] | ir.cpp:198:9:198:9 | +| PointerCompare(int *, int *) -> void | 0 | 26 | Load | ir.cpp:198:9:198:9 | +| PointerCompare(int *, int *) -> void | 0 | 27 | VariableAddress[q] | ir.cpp:198:13:198:13 | +| PointerCompare(int *, int *) -> void | 0 | 28 | Load | ir.cpp:198:13:198:13 | +| PointerCompare(int *, int *) -> void | 0 | 29 | CompareLT | ir.cpp:198:9:198:13 | +| PointerCompare(int *, int *) -> void | 0 | 30 | VariableAddress[b] | ir.cpp:198:5:198:5 | +| PointerCompare(int *, int *) -> void | 0 | 31 | Store | ir.cpp:198:5:198:13 | +| PointerCompare(int *, int *) -> void | 0 | 32 | VariableAddress[p] | ir.cpp:199:9:199:9 | +| PointerCompare(int *, int *) -> void | 0 | 33 | Load | ir.cpp:199:9:199:9 | +| PointerCompare(int *, int *) -> void | 0 | 34 | VariableAddress[q] | ir.cpp:199:13:199:13 | +| PointerCompare(int *, int *) -> void | 0 | 35 | Load | ir.cpp:199:13:199:13 | +| PointerCompare(int *, int *) -> void | 0 | 36 | CompareGT | ir.cpp:199:9:199:13 | +| PointerCompare(int *, int *) -> void | 0 | 37 | VariableAddress[b] | ir.cpp:199:5:199:5 | +| PointerCompare(int *, int *) -> void | 0 | 38 | Store | ir.cpp:199:5:199:13 | +| PointerCompare(int *, int *) -> void | 0 | 39 | VariableAddress[p] | ir.cpp:200:9:200:9 | +| PointerCompare(int *, int *) -> void | 0 | 40 | Load | ir.cpp:200:9:200:9 | +| PointerCompare(int *, int *) -> void | 0 | 41 | VariableAddress[q] | ir.cpp:200:14:200:14 | +| PointerCompare(int *, int *) -> void | 0 | 42 | Load | ir.cpp:200:14:200:14 | +| PointerCompare(int *, int *) -> void | 0 | 43 | CompareLE | ir.cpp:200:9:200:14 | +| PointerCompare(int *, int *) -> void | 0 | 44 | VariableAddress[b] | ir.cpp:200:5:200:5 | +| PointerCompare(int *, int *) -> void | 0 | 45 | Store | ir.cpp:200:5:200:14 | +| PointerCompare(int *, int *) -> void | 0 | 46 | VariableAddress[p] | ir.cpp:201:9:201:9 | +| PointerCompare(int *, int *) -> void | 0 | 47 | Load | ir.cpp:201:9:201:9 | +| PointerCompare(int *, int *) -> void | 0 | 48 | VariableAddress[q] | ir.cpp:201:14:201:14 | +| PointerCompare(int *, int *) -> void | 0 | 49 | Load | ir.cpp:201:14:201:14 | +| PointerCompare(int *, int *) -> void | 0 | 50 | CompareGE | ir.cpp:201:9:201:14 | +| PointerCompare(int *, int *) -> void | 0 | 51 | VariableAddress[b] | ir.cpp:201:5:201:5 | +| PointerCompare(int *, int *) -> void | 0 | 52 | Store | ir.cpp:201:5:201:14 | +| PointerCompare(int *, int *) -> void | 0 | 53 | NoOp | ir.cpp:202:1:202:1 | +| PointerCompare(int *, int *) -> void | 0 | 54 | ReturnVoid | ir.cpp:193:6:193:19 | +| PointerCompare(int *, int *) -> void | 0 | 55 | UnmodeledUse | ir.cpp:193:6:193:19 | +| PointerCompare(int *, int *) -> void | 0 | 56 | ExitFunction | ir.cpp:193:6:193:19 | +| PointerCrement(int *) -> void | 0 | 0 | EnterFunction | ir.cpp:204:6:204:19 | +| PointerCrement(int *) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:204:6:204:19 | +| PointerCrement(int *) -> void | 0 | 2 | InitializeParameter[p] | ir.cpp:204:26:204:26 | +| PointerCrement(int *) -> void | 0 | 3 | VariableAddress[p] | ir.cpp:204:26:204:26 | +| PointerCrement(int *) -> void | 0 | 4 | Store | ir.cpp:204:26:204:26 | +| PointerCrement(int *) -> void | 0 | 5 | VariableAddress[q] | ir.cpp:205:10:205:10 | +| PointerCrement(int *) -> void | 0 | 6 | Uninitialized | ir.cpp:205:10:205:10 | +| PointerCrement(int *) -> void | 0 | 7 | Store | ir.cpp:205:10:205:10 | +| PointerCrement(int *) -> void | 0 | 8 | VariableAddress[p] | ir.cpp:207:11:207:11 | +| PointerCrement(int *) -> void | 0 | 9 | Load | ir.cpp:207:9:207:11 | +| PointerCrement(int *) -> void | 0 | 10 | Constant[1] | ir.cpp:207:9:207:11 | +| PointerCrement(int *) -> void | 0 | 11 | PointerAdd[4] | ir.cpp:207:9:207:11 | +| PointerCrement(int *) -> void | 0 | 12 | Store | ir.cpp:207:9:207:11 | +| PointerCrement(int *) -> void | 0 | 13 | VariableAddress[q] | ir.cpp:207:5:207:5 | +| PointerCrement(int *) -> void | 0 | 14 | Store | ir.cpp:207:5:207:11 | +| PointerCrement(int *) -> void | 0 | 15 | VariableAddress[p] | ir.cpp:208:11:208:11 | +| PointerCrement(int *) -> void | 0 | 16 | Load | ir.cpp:208:9:208:11 | +| PointerCrement(int *) -> void | 0 | 17 | Constant[1] | ir.cpp:208:9:208:11 | +| PointerCrement(int *) -> void | 0 | 18 | PointerSub[4] | ir.cpp:208:9:208:11 | +| PointerCrement(int *) -> void | 0 | 19 | Store | ir.cpp:208:9:208:11 | +| PointerCrement(int *) -> void | 0 | 20 | VariableAddress[q] | ir.cpp:208:5:208:5 | +| PointerCrement(int *) -> void | 0 | 21 | Store | ir.cpp:208:5:208:11 | +| PointerCrement(int *) -> void | 0 | 22 | VariableAddress[p] | ir.cpp:209:9:209:9 | +| PointerCrement(int *) -> void | 0 | 23 | Load | ir.cpp:209:9:209:11 | +| PointerCrement(int *) -> void | 0 | 24 | Constant[1] | ir.cpp:209:9:209:11 | +| PointerCrement(int *) -> void | 0 | 25 | PointerAdd[4] | ir.cpp:209:9:209:11 | +| PointerCrement(int *) -> void | 0 | 26 | Store | ir.cpp:209:9:209:11 | +| PointerCrement(int *) -> void | 0 | 27 | VariableAddress[q] | ir.cpp:209:5:209:5 | +| PointerCrement(int *) -> void | 0 | 28 | Store | ir.cpp:209:5:209:11 | +| PointerCrement(int *) -> void | 0 | 29 | VariableAddress[p] | ir.cpp:210:9:210:9 | +| PointerCrement(int *) -> void | 0 | 30 | Load | ir.cpp:210:9:210:11 | +| PointerCrement(int *) -> void | 0 | 31 | Constant[1] | ir.cpp:210:9:210:11 | +| PointerCrement(int *) -> void | 0 | 32 | PointerSub[4] | ir.cpp:210:9:210:11 | +| PointerCrement(int *) -> void | 0 | 33 | Store | ir.cpp:210:9:210:11 | +| PointerCrement(int *) -> void | 0 | 34 | VariableAddress[q] | ir.cpp:210:5:210:5 | +| PointerCrement(int *) -> void | 0 | 35 | Store | ir.cpp:210:5:210:11 | +| PointerCrement(int *) -> void | 0 | 36 | NoOp | ir.cpp:211:1:211:1 | +| PointerCrement(int *) -> void | 0 | 37 | ReturnVoid | ir.cpp:204:6:204:19 | +| PointerCrement(int *) -> void | 0 | 38 | UnmodeledUse | ir.cpp:204:6:204:19 | +| PointerCrement(int *) -> void | 0 | 39 | ExitFunction | ir.cpp:204:6:204:19 | +| PointerOps(int *, int) -> void | 0 | 0 | EnterFunction | ir.cpp:153:6:153:15 | +| PointerOps(int *, int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:153:6:153:15 | +| PointerOps(int *, int) -> void | 0 | 2 | InitializeParameter[p] | ir.cpp:153:22:153:22 | +| PointerOps(int *, int) -> void | 0 | 3 | VariableAddress[p] | ir.cpp:153:22:153:22 | +| PointerOps(int *, int) -> void | 0 | 4 | Store | ir.cpp:153:22:153:22 | +| PointerOps(int *, int) -> void | 0 | 5 | InitializeParameter[i] | ir.cpp:153:29:153:29 | +| PointerOps(int *, int) -> void | 0 | 6 | VariableAddress[i] | ir.cpp:153:29:153:29 | +| PointerOps(int *, int) -> void | 0 | 7 | Store | ir.cpp:153:29:153:29 | +| PointerOps(int *, int) -> void | 0 | 8 | VariableAddress[q] | ir.cpp:154:10:154:10 | +| PointerOps(int *, int) -> void | 0 | 9 | Uninitialized | ir.cpp:154:10:154:10 | +| PointerOps(int *, int) -> void | 0 | 10 | Store | ir.cpp:154:10:154:10 | +| PointerOps(int *, int) -> void | 0 | 11 | VariableAddress[b] | ir.cpp:155:10:155:10 | +| PointerOps(int *, int) -> void | 0 | 12 | Uninitialized | ir.cpp:155:10:155:10 | +| PointerOps(int *, int) -> void | 0 | 13 | Store | ir.cpp:155:10:155:10 | +| PointerOps(int *, int) -> void | 0 | 14 | VariableAddress[p] | ir.cpp:157:9:157:9 | +| PointerOps(int *, int) -> void | 0 | 15 | Load | ir.cpp:157:9:157:9 | +| PointerOps(int *, int) -> void | 0 | 16 | VariableAddress[i] | ir.cpp:157:13:157:13 | +| PointerOps(int *, int) -> void | 0 | 17 | Load | ir.cpp:157:13:157:13 | +| PointerOps(int *, int) -> void | 0 | 18 | PointerAdd[4] | ir.cpp:157:9:157:13 | +| PointerOps(int *, int) -> void | 0 | 19 | VariableAddress[q] | ir.cpp:157:5:157:5 | +| PointerOps(int *, int) -> void | 0 | 20 | Store | ir.cpp:157:5:157:13 | +| PointerOps(int *, int) -> void | 0 | 21 | VariableAddress[i] | ir.cpp:158:9:158:9 | +| PointerOps(int *, int) -> void | 0 | 22 | Load | ir.cpp:158:9:158:9 | +| PointerOps(int *, int) -> void | 0 | 23 | VariableAddress[p] | ir.cpp:158:13:158:13 | +| PointerOps(int *, int) -> void | 0 | 24 | Load | ir.cpp:158:13:158:13 | +| PointerOps(int *, int) -> void | 0 | 25 | PointerAdd[4] | ir.cpp:158:9:158:13 | +| PointerOps(int *, int) -> void | 0 | 26 | VariableAddress[q] | ir.cpp:158:5:158:5 | +| PointerOps(int *, int) -> void | 0 | 27 | Store | ir.cpp:158:5:158:13 | +| PointerOps(int *, int) -> void | 0 | 28 | VariableAddress[p] | ir.cpp:159:9:159:9 | +| PointerOps(int *, int) -> void | 0 | 29 | Load | ir.cpp:159:9:159:9 | +| PointerOps(int *, int) -> void | 0 | 30 | VariableAddress[i] | ir.cpp:159:13:159:13 | +| PointerOps(int *, int) -> void | 0 | 31 | Load | ir.cpp:159:13:159:13 | +| PointerOps(int *, int) -> void | 0 | 32 | PointerSub[4] | ir.cpp:159:9:159:13 | +| PointerOps(int *, int) -> void | 0 | 33 | VariableAddress[q] | ir.cpp:159:5:159:5 | +| PointerOps(int *, int) -> void | 0 | 34 | Store | ir.cpp:159:5:159:13 | +| PointerOps(int *, int) -> void | 0 | 35 | VariableAddress[p] | ir.cpp:160:9:160:9 | +| PointerOps(int *, int) -> void | 0 | 36 | Load | ir.cpp:160:9:160:9 | +| PointerOps(int *, int) -> void | 0 | 37 | VariableAddress[q] | ir.cpp:160:13:160:13 | +| PointerOps(int *, int) -> void | 0 | 38 | Load | ir.cpp:160:13:160:13 | +| PointerOps(int *, int) -> void | 0 | 39 | PointerDiff[4] | ir.cpp:160:9:160:13 | +| PointerOps(int *, int) -> void | 0 | 40 | Convert | ir.cpp:160:9:160:13 | +| PointerOps(int *, int) -> void | 0 | 41 | VariableAddress[i] | ir.cpp:160:5:160:5 | +| PointerOps(int *, int) -> void | 0 | 42 | Store | ir.cpp:160:5:160:13 | +| PointerOps(int *, int) -> void | 0 | 43 | VariableAddress[p] | ir.cpp:162:9:162:9 | +| PointerOps(int *, int) -> void | 0 | 44 | Load | ir.cpp:162:9:162:9 | +| PointerOps(int *, int) -> void | 0 | 45 | VariableAddress[q] | ir.cpp:162:5:162:5 | +| PointerOps(int *, int) -> void | 0 | 46 | Store | ir.cpp:162:5:162:9 | +| PointerOps(int *, int) -> void | 0 | 47 | VariableAddress[i] | ir.cpp:164:10:164:10 | +| PointerOps(int *, int) -> void | 0 | 48 | Load | ir.cpp:164:10:164:10 | +| PointerOps(int *, int) -> void | 0 | 49 | VariableAddress[q] | ir.cpp:164:5:164:5 | +| PointerOps(int *, int) -> void | 0 | 50 | Load | ir.cpp:164:5:164:10 | +| PointerOps(int *, int) -> void | 0 | 51 | PointerAdd[4] | ir.cpp:164:5:164:10 | +| PointerOps(int *, int) -> void | 0 | 52 | Store | ir.cpp:164:5:164:10 | +| PointerOps(int *, int) -> void | 0 | 53 | VariableAddress[i] | ir.cpp:165:10:165:10 | +| PointerOps(int *, int) -> void | 0 | 54 | Load | ir.cpp:165:10:165:10 | +| PointerOps(int *, int) -> void | 0 | 55 | VariableAddress[q] | ir.cpp:165:5:165:5 | +| PointerOps(int *, int) -> void | 0 | 56 | Load | ir.cpp:165:5:165:10 | +| PointerOps(int *, int) -> void | 0 | 57 | PointerSub[4] | ir.cpp:165:5:165:10 | +| PointerOps(int *, int) -> void | 0 | 58 | Store | ir.cpp:165:5:165:10 | +| PointerOps(int *, int) -> void | 0 | 59 | VariableAddress[p] | ir.cpp:167:9:167:9 | +| PointerOps(int *, int) -> void | 0 | 60 | Load | ir.cpp:167:9:167:9 | +| PointerOps(int *, int) -> void | 0 | 61 | Constant[0] | ir.cpp:167:9:167:9 | +| PointerOps(int *, int) -> void | 0 | 62 | CompareNE | ir.cpp:167:9:167:9 | +| PointerOps(int *, int) -> void | 0 | 63 | VariableAddress[b] | ir.cpp:167:5:167:5 | +| PointerOps(int *, int) -> void | 0 | 64 | Store | ir.cpp:167:5:167:9 | +| PointerOps(int *, int) -> void | 0 | 65 | VariableAddress[p] | ir.cpp:168:10:168:10 | +| PointerOps(int *, int) -> void | 0 | 66 | Load | ir.cpp:168:10:168:10 | +| PointerOps(int *, int) -> void | 0 | 67 | Constant[0] | ir.cpp:168:10:168:10 | +| PointerOps(int *, int) -> void | 0 | 68 | CompareNE | ir.cpp:168:10:168:10 | +| PointerOps(int *, int) -> void | 0 | 69 | LogicalNot | ir.cpp:168:9:168:10 | +| PointerOps(int *, int) -> void | 0 | 70 | VariableAddress[b] | ir.cpp:168:5:168:5 | +| PointerOps(int *, int) -> void | 0 | 71 | Store | ir.cpp:168:5:168:10 | +| PointerOps(int *, int) -> void | 0 | 72 | NoOp | ir.cpp:169:1:169:1 | +| PointerOps(int *, int) -> void | 0 | 73 | ReturnVoid | ir.cpp:153:6:153:15 | +| PointerOps(int *, int) -> void | 0 | 74 | UnmodeledUse | ir.cpp:153:6:153:15 | +| PointerOps(int *, int) -> void | 0 | 75 | ExitFunction | ir.cpp:153:6:153:15 | +| PolymorphicBase::PolymorphicBase() -> void | 0 | 0 | EnterFunction | ir.cpp:842:8:842:8 | +| PolymorphicBase::PolymorphicBase() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:842:8:842:8 | +| PolymorphicBase::PolymorphicBase() -> void | 0 | 2 | InitializeThis | ir.cpp:842:8:842:8 | +| PolymorphicBase::PolymorphicBase() -> void | 0 | 3 | NoOp | ir.cpp:842:8:842:8 | +| PolymorphicBase::PolymorphicBase() -> void | 0 | 4 | ReturnVoid | ir.cpp:842:8:842:8 | +| PolymorphicBase::PolymorphicBase() -> void | 0 | 5 | UnmodeledUse | ir.cpp:842:8:842:8 | +| PolymorphicBase::PolymorphicBase() -> void | 0 | 6 | ExitFunction | ir.cpp:842:8:842:8 | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 0 | EnterFunction | ir.cpp:846:8:846:8 | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:846:8:846:8 | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 2 | InitializeThis | ir.cpp:846:8:846:8 | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 3 | ConvertToBase[PolymorphicDerived : PolymorphicBase] | ir.cpp:846:8:846:8 | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 4 | FunctionAddress[PolymorphicBase] | ir.cpp:846:8:846:8 | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 5 | Invoke | ir.cpp:846:8:846:8 | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 6 | NoOp | ir.cpp:846:8:846:8 | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 7 | ReturnVoid | ir.cpp:846:8:846:8 | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 8 | UnmodeledUse | ir.cpp:846:8:846:8 | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 9 | ExitFunction | ir.cpp:846:8:846:8 | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 0 | EnterFunction | ir.cpp:846:8:846:8 | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:846:8:846:8 | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 2 | InitializeThis | ir.cpp:846:8:846:8 | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 3 | NoOp | file://:0:0:0:0 | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 4 | ConvertToBase[PolymorphicDerived : PolymorphicBase] | ir.cpp:846:8:846:8 | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 5 | FunctionAddress[~PolymorphicBase] | ir.cpp:846:8:846:8 | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 6 | Invoke | ir.cpp:846:8:846:8 | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 7 | ReturnVoid | ir.cpp:846:8:846:8 | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 8 | UnmodeledUse | ir.cpp:846:8:846:8 | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 9 | ExitFunction | ir.cpp:846:8:846:8 | +| ReturnStruct(Point) -> Point | 0 | 0 | EnterFunction | ir.cpp:422:7:422:18 | +| ReturnStruct(Point) -> Point | 0 | 1 | UnmodeledDefinition | ir.cpp:422:7:422:18 | +| ReturnStruct(Point) -> Point | 0 | 2 | InitializeParameter[pt] | ir.cpp:422:26:422:27 | +| ReturnStruct(Point) -> Point | 0 | 3 | VariableAddress[pt] | ir.cpp:422:26:422:27 | +| ReturnStruct(Point) -> Point | 0 | 4 | Store | ir.cpp:422:26:422:27 | +| ReturnStruct(Point) -> Point | 0 | 5 | VariableAddress[#return] | ir.cpp:423:5:423:14 | +| ReturnStruct(Point) -> Point | 0 | 6 | VariableAddress[pt] | ir.cpp:423:12:423:13 | +| ReturnStruct(Point) -> Point | 0 | 7 | Load | ir.cpp:423:12:423:13 | +| ReturnStruct(Point) -> Point | 0 | 8 | Store | ir.cpp:423:12:423:13 | +| ReturnStruct(Point) -> Point | 0 | 9 | VariableAddress[#return] | ir.cpp:422:7:422:18 | +| ReturnStruct(Point) -> Point | 0 | 10 | ReturnValue | ir.cpp:422:7:422:18 | +| ReturnStruct(Point) -> Point | 0 | 11 | UnmodeledUse | ir.cpp:422:7:422:18 | +| ReturnStruct(Point) -> Point | 0 | 12 | ExitFunction | ir.cpp:422:7:422:18 | +| SetFuncPtr() -> void | 0 | 0 | EnterFunction | ir.cpp:590:6:590:15 | +| SetFuncPtr() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:590:6:590:15 | +| SetFuncPtr() -> void | 0 | 2 | VariableAddress[pfn] | ir.cpp:591:11:591:13 | +| SetFuncPtr() -> void | 0 | 3 | FunctionAddress[FuncPtrTarget] | ir.cpp:591:23:591:35 | +| SetFuncPtr() -> void | 0 | 4 | Store | ir.cpp:591:23:591:35 | +| SetFuncPtr() -> void | 0 | 5 | FunctionAddress[FuncPtrTarget] | ir.cpp:592:12:592:24 | +| SetFuncPtr() -> void | 0 | 6 | VariableAddress[pfn] | ir.cpp:592:5:592:7 | +| SetFuncPtr() -> void | 0 | 7 | Store | ir.cpp:592:5:592:24 | +| SetFuncPtr() -> void | 0 | 8 | FunctionAddress[FuncPtrTarget] | ir.cpp:593:12:593:24 | +| SetFuncPtr() -> void | 0 | 9 | VariableAddress[pfn] | ir.cpp:593:5:593:7 | +| SetFuncPtr() -> void | 0 | 10 | Store | ir.cpp:593:5:593:24 | +| SetFuncPtr() -> void | 0 | 11 | FunctionAddress[FuncPtrTarget] | ir.cpp:594:15:594:27 | +| SetFuncPtr() -> void | 0 | 12 | VariableAddress[pfn] | ir.cpp:594:5:594:7 | +| SetFuncPtr() -> void | 0 | 13 | Store | ir.cpp:594:5:594:27 | +| SetFuncPtr() -> void | 0 | 14 | NoOp | ir.cpp:595:1:595:1 | +| SetFuncPtr() -> void | 0 | 15 | ReturnVoid | ir.cpp:590:6:590:15 | +| SetFuncPtr() -> void | 0 | 16 | UnmodeledUse | ir.cpp:590:6:590:15 | +| SetFuncPtr() -> void | 0 | 17 | ExitFunction | ir.cpp:590:6:590:15 | +| String::String() -> void | 0 | 0 | EnterFunction | ir.cpp:867:1:867:14 | +| String::String() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:867:1:867:14 | +| String::String() -> void | 0 | 2 | InitializeThis | ir.cpp:867:1:867:14 | +| String::String() -> void | 0 | 3 | FunctionAddress[String] | ir.cpp:868:3:868:12 | +| String::String() -> void | 0 | 4 | StringConstant[""] | ir.cpp:868:10:868:11 | +| String::String() -> void | 0 | 5 | Convert | ir.cpp:868:10:868:11 | +| String::String() -> void | 0 | 6 | Invoke | ir.cpp:868:3:868:12 | +| String::String() -> void | 0 | 7 | NoOp | ir.cpp:869:1:869:1 | +| String::String() -> void | 0 | 8 | ReturnVoid | ir.cpp:867:1:867:14 | +| String::String() -> void | 0 | 9 | UnmodeledUse | ir.cpp:867:1:867:14 | +| String::String() -> void | 0 | 10 | ExitFunction | ir.cpp:867:1:867:14 | +| StringLiteral(int) -> void | 0 | 0 | EnterFunction | ir.cpp:187:6:187:18 | +| StringLiteral(int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:187:6:187:18 | +| StringLiteral(int) -> void | 0 | 2 | InitializeParameter[i] | ir.cpp:187:24:187:24 | +| StringLiteral(int) -> void | 0 | 3 | VariableAddress[i] | ir.cpp:187:24:187:24 | +| StringLiteral(int) -> void | 0 | 4 | Store | ir.cpp:187:24:187:24 | +| StringLiteral(int) -> void | 0 | 5 | VariableAddress[c] | ir.cpp:188:10:188:10 | +| StringLiteral(int) -> void | 0 | 6 | StringConstant["Foo"] | ir.cpp:188:14:188:18 | +| StringLiteral(int) -> void | 0 | 7 | Convert | ir.cpp:188:14:188:18 | +| StringLiteral(int) -> void | 0 | 8 | VariableAddress[i] | ir.cpp:188:20:188:20 | +| StringLiteral(int) -> void | 0 | 9 | Load | ir.cpp:188:20:188:20 | +| StringLiteral(int) -> void | 0 | 10 | PointerAdd[1] | ir.cpp:188:14:188:21 | +| StringLiteral(int) -> void | 0 | 11 | Load | ir.cpp:188:14:188:21 | +| StringLiteral(int) -> void | 0 | 12 | Store | ir.cpp:188:14:188:21 | +| StringLiteral(int) -> void | 0 | 13 | VariableAddress[pwc] | ir.cpp:189:14:189:16 | +| StringLiteral(int) -> void | 0 | 14 | StringConstant[L"Bar"] | ir.cpp:189:20:189:25 | +| StringLiteral(int) -> void | 0 | 15 | Convert | ir.cpp:189:20:189:25 | +| StringLiteral(int) -> void | 0 | 16 | Convert | ir.cpp:189:20:189:25 | +| StringLiteral(int) -> void | 0 | 17 | Store | ir.cpp:189:20:189:25 | +| StringLiteral(int) -> void | 0 | 18 | VariableAddress[wc] | ir.cpp:190:13:190:14 | +| StringLiteral(int) -> void | 0 | 19 | VariableAddress[pwc] | ir.cpp:190:18:190:20 | +| StringLiteral(int) -> void | 0 | 20 | Load | ir.cpp:190:18:190:20 | +| StringLiteral(int) -> void | 0 | 21 | VariableAddress[i] | ir.cpp:190:22:190:22 | +| StringLiteral(int) -> void | 0 | 22 | Load | ir.cpp:190:22:190:22 | +| StringLiteral(int) -> void | 0 | 23 | PointerAdd[4] | ir.cpp:190:18:190:23 | +| StringLiteral(int) -> void | 0 | 24 | Load | ir.cpp:190:18:190:23 | +| StringLiteral(int) -> void | 0 | 25 | Store | ir.cpp:190:18:190:23 | +| StringLiteral(int) -> void | 0 | 26 | NoOp | ir.cpp:191:1:191:1 | +| StringLiteral(int) -> void | 0 | 27 | ReturnVoid | ir.cpp:187:6:187:18 | +| StringLiteral(int) -> void | 0 | 28 | UnmodeledUse | ir.cpp:187:6:187:18 | +| StringLiteral(int) -> void | 0 | 29 | ExitFunction | ir.cpp:187:6:187:18 | +| Switch(int) -> void | 0 | 0 | EnterFunction | ir.cpp:384:6:384:11 | +| Switch(int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:384:6:384:11 | +| Switch(int) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:384:17:384:17 | +| Switch(int) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:384:17:384:17 | +| Switch(int) -> void | 0 | 4 | Store | ir.cpp:384:17:384:17 | +| Switch(int) -> void | 0 | 5 | VariableAddress[y] | ir.cpp:385:9:385:9 | +| Switch(int) -> void | 0 | 6 | Uninitialized | ir.cpp:385:9:385:9 | +| Switch(int) -> void | 0 | 7 | Store | ir.cpp:385:9:385:9 | +| Switch(int) -> void | 0 | 8 | VariableAddress[x] | ir.cpp:386:13:386:13 | +| Switch(int) -> void | 0 | 9 | Load | ir.cpp:386:13:386:13 | +| Switch(int) -> void | 0 | 10 | Switch | ir.cpp:386:5:409:5 | +| Switch(int) -> void | 1 | 0 | Constant[1234] | ir.cpp:387:13:387:16 | +| Switch(int) -> void | 1 | 1 | VariableAddress[y] | ir.cpp:387:9:387:9 | +| Switch(int) -> void | 1 | 2 | Store | ir.cpp:387:9:387:16 | +| Switch(int) -> void | 2 | 0 | NoOp | ir.cpp:389:9:389:16 | +| Switch(int) -> void | 2 | 1 | Constant[-1] | ir.cpp:390:17:390:18 | +| Switch(int) -> void | 2 | 2 | VariableAddress[y] | ir.cpp:390:13:390:13 | +| Switch(int) -> void | 2 | 3 | Store | ir.cpp:390:13:390:18 | +| Switch(int) -> void | 2 | 4 | NoOp | ir.cpp:391:13:391:18 | +| Switch(int) -> void | 3 | 0 | NoOp | ir.cpp:393:9:393:15 | +| Switch(int) -> void | 4 | 0 | NoOp | ir.cpp:394:9:394:15 | +| Switch(int) -> void | 4 | 1 | Constant[1] | ir.cpp:395:17:395:17 | +| Switch(int) -> void | 4 | 2 | VariableAddress[y] | ir.cpp:395:13:395:13 | +| Switch(int) -> void | 4 | 3 | Store | ir.cpp:395:13:395:17 | +| Switch(int) -> void | 4 | 4 | NoOp | ir.cpp:396:13:396:18 | +| Switch(int) -> void | 5 | 0 | NoOp | ir.cpp:398:9:398:15 | +| Switch(int) -> void | 5 | 1 | Constant[3] | ir.cpp:399:17:399:17 | +| Switch(int) -> void | 5 | 2 | VariableAddress[y] | ir.cpp:399:13:399:13 | +| Switch(int) -> void | 5 | 3 | Store | ir.cpp:399:13:399:17 | +| Switch(int) -> void | 6 | 0 | NoOp | ir.cpp:400:9:400:15 | +| Switch(int) -> void | 6 | 1 | Constant[4] | ir.cpp:401:17:401:17 | +| Switch(int) -> void | 6 | 2 | VariableAddress[y] | ir.cpp:401:13:401:13 | +| Switch(int) -> void | 6 | 3 | Store | ir.cpp:401:13:401:17 | +| Switch(int) -> void | 6 | 4 | NoOp | ir.cpp:402:13:402:18 | +| Switch(int) -> void | 7 | 0 | NoOp | ir.cpp:404:9:404:16 | +| Switch(int) -> void | 7 | 1 | Constant[0] | ir.cpp:405:17:405:17 | +| Switch(int) -> void | 7 | 2 | VariableAddress[y] | ir.cpp:405:13:405:13 | +| Switch(int) -> void | 7 | 3 | Store | ir.cpp:405:13:405:17 | +| Switch(int) -> void | 7 | 4 | NoOp | ir.cpp:406:13:406:18 | +| Switch(int) -> void | 8 | 0 | Constant[5678] | ir.cpp:408:13:408:16 | +| Switch(int) -> void | 8 | 1 | VariableAddress[y] | ir.cpp:408:9:408:9 | +| Switch(int) -> void | 8 | 2 | Store | ir.cpp:408:9:408:16 | +| Switch(int) -> void | 9 | 0 | NoOp | ir.cpp:409:5:409:5 | +| Switch(int) -> void | 9 | 1 | NoOp | ir.cpp:410:1:410:1 | +| Switch(int) -> void | 9 | 2 | ReturnVoid | ir.cpp:384:6:384:11 | +| Switch(int) -> void | 9 | 3 | UnmodeledUse | ir.cpp:384:6:384:11 | +| Switch(int) -> void | 9 | 4 | ExitFunction | ir.cpp:384:6:384:11 | +| TakeReference() -> int & | 0 | 0 | EnterFunction | ir.cpp:679:6:679:18 | +| TakeReference() -> int & | 0 | 1 | UnmodeledDefinition | ir.cpp:679:6:679:18 | +| TakeReference() -> int & | 0 | 2 | VariableAddress[#return] | ir.cpp:680:5:680:13 | +| TakeReference() -> int & | 0 | 3 | VariableAddress[g] | ir.cpp:680:12:680:12 | +| TakeReference() -> int & | 0 | 4 | Store | ir.cpp:680:12:680:12 | +| TakeReference() -> int & | 0 | 5 | VariableAddress[#return] | ir.cpp:679:6:679:18 | +| TakeReference() -> int & | 0 | 6 | ReturnValue | ir.cpp:679:6:679:18 | +| TakeReference() -> int & | 0 | 7 | UnmodeledUse | ir.cpp:679:6:679:18 | +| TakeReference() -> int & | 0 | 8 | ExitFunction | ir.cpp:679:6:679:18 | +| TryCatch(bool) -> void | 0 | 0 | EnterFunction | ir.cpp:724:6:724:13 | +| TryCatch(bool) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:724:6:724:13 | +| TryCatch(bool) -> void | 0 | 2 | InitializeParameter[b] | ir.cpp:724:20:724:20 | +| TryCatch(bool) -> void | 0 | 3 | VariableAddress[b] | ir.cpp:724:20:724:20 | +| TryCatch(bool) -> void | 0 | 4 | Store | ir.cpp:724:20:724:20 | +| TryCatch(bool) -> void | 0 | 5 | VariableAddress[x] | ir.cpp:726:9:726:9 | +| TryCatch(bool) -> void | 0 | 6 | Constant[5] | ir.cpp:726:12:726:13 | +| TryCatch(bool) -> void | 0 | 7 | Store | ir.cpp:726:12:726:13 | +| TryCatch(bool) -> void | 0 | 8 | VariableAddress[b] | ir.cpp:727:9:727:9 | +| TryCatch(bool) -> void | 0 | 9 | Load | ir.cpp:727:9:727:9 | +| TryCatch(bool) -> void | 0 | 10 | ConditionalBranch | ir.cpp:727:9:727:9 | +| TryCatch(bool) -> void | 1 | 0 | UnmodeledUse | ir.cpp:724:6:724:13 | +| TryCatch(bool) -> void | 1 | 1 | ExitFunction | ir.cpp:724:6:724:13 | +| TryCatch(bool) -> void | 2 | 0 | Unwind | ir.cpp:724:6:724:13 | +| TryCatch(bool) -> void | 3 | 0 | VariableAddress[#throw728:7] | ir.cpp:728:7:728:28 | +| TryCatch(bool) -> void | 3 | 1 | StringConstant["string literal"] | ir.cpp:728:13:728:28 | +| TryCatch(bool) -> void | 3 | 2 | Convert | ir.cpp:728:13:728:28 | +| TryCatch(bool) -> void | 3 | 3 | Store | ir.cpp:728:13:728:28 | +| TryCatch(bool) -> void | 3 | 4 | ThrowValue | ir.cpp:728:7:728:28 | +| TryCatch(bool) -> void | 4 | 0 | VariableAddress[x] | ir.cpp:730:14:730:14 | +| TryCatch(bool) -> void | 4 | 1 | Load | ir.cpp:730:14:730:14 | +| TryCatch(bool) -> void | 4 | 2 | Constant[2] | ir.cpp:730:18:730:18 | +| TryCatch(bool) -> void | 4 | 3 | CompareLT | ir.cpp:730:14:730:18 | +| TryCatch(bool) -> void | 4 | 4 | ConditionalBranch | ir.cpp:730:14:730:18 | +| TryCatch(bool) -> void | 5 | 0 | VariableAddress[b] | ir.cpp:731:11:731:11 | +| TryCatch(bool) -> void | 5 | 1 | Load | ir.cpp:731:11:731:11 | +| TryCatch(bool) -> void | 5 | 2 | ConditionalBranch | ir.cpp:731:11:731:11 | +| TryCatch(bool) -> void | 6 | 0 | Constant[7] | ir.cpp:731:15:731:15 | +| TryCatch(bool) -> void | 6 | 1 | VariableAddress[#temp731:11] | ir.cpp:731:11:731:47 | +| TryCatch(bool) -> void | 6 | 2 | Store | ir.cpp:731:11:731:47 | +| TryCatch(bool) -> void | 6 | 3 | VariableAddress[#temp731:11] | ir.cpp:731:11:731:47 | +| TryCatch(bool) -> void | 6 | 4 | Load | ir.cpp:731:11:731:47 | +| TryCatch(bool) -> void | 6 | 5 | VariableAddress[x] | ir.cpp:731:7:731:7 | +| TryCatch(bool) -> void | 6 | 6 | Store | ir.cpp:731:7:731:47 | +| TryCatch(bool) -> void | 7 | 0 | VariableAddress[#throw731:19] | ir.cpp:731:19:731:47 | +| TryCatch(bool) -> void | 7 | 1 | FunctionAddress[String] | ir.cpp:731:19:731:47 | +| TryCatch(bool) -> void | 7 | 2 | StringConstant["String object"] | ir.cpp:731:32:731:46 | +| TryCatch(bool) -> void | 7 | 3 | Convert | ir.cpp:731:32:731:46 | +| TryCatch(bool) -> void | 7 | 4 | Invoke | ir.cpp:731:19:731:47 | +| TryCatch(bool) -> void | 7 | 5 | ThrowValue | ir.cpp:731:19:731:47 | +| TryCatch(bool) -> void | 8 | 0 | Constant[7] | ir.cpp:733:9:733:9 | +| TryCatch(bool) -> void | 8 | 1 | VariableAddress[x] | ir.cpp:733:5:733:5 | +| TryCatch(bool) -> void | 8 | 2 | Store | ir.cpp:733:5:733:9 | +| TryCatch(bool) -> void | 9 | 0 | CatchByType[const char *] | ir.cpp:735:25:737:3 | +| TryCatch(bool) -> void | 10 | 0 | InitializeParameter[s] | ir.cpp:735:22:735:22 | +| TryCatch(bool) -> void | 10 | 1 | VariableAddress[s] | ir.cpp:735:22:735:22 | +| TryCatch(bool) -> void | 10 | 2 | Store | ir.cpp:735:22:735:22 | +| TryCatch(bool) -> void | 10 | 3 | VariableAddress[#throw736:5] | ir.cpp:736:5:736:19 | +| TryCatch(bool) -> void | 10 | 4 | FunctionAddress[String] | ir.cpp:736:5:736:19 | +| TryCatch(bool) -> void | 10 | 5 | VariableAddress[s] | ir.cpp:736:18:736:18 | +| TryCatch(bool) -> void | 10 | 6 | Load | ir.cpp:736:18:736:18 | +| TryCatch(bool) -> void | 10 | 7 | Invoke | ir.cpp:736:5:736:19 | +| TryCatch(bool) -> void | 10 | 8 | ThrowValue | ir.cpp:736:5:736:19 | +| TryCatch(bool) -> void | 11 | 0 | CatchByType[const String &] | ir.cpp:738:27:739:3 | +| TryCatch(bool) -> void | 12 | 0 | InitializeParameter[e] | ir.cpp:738:24:738:24 | +| TryCatch(bool) -> void | 12 | 1 | VariableAddress[e] | ir.cpp:738:24:738:24 | +| TryCatch(bool) -> void | 12 | 2 | Store | ir.cpp:738:24:738:24 | +| TryCatch(bool) -> void | 12 | 3 | NoOp | ir.cpp:738:27:739:3 | +| TryCatch(bool) -> void | 13 | 0 | CatchAny | ir.cpp:740:15:742:3 | +| TryCatch(bool) -> void | 13 | 1 | ReThrow | ir.cpp:741:5:741:9 | +| TryCatch(bool) -> void | 14 | 0 | NoOp | ir.cpp:743:1:743:1 | +| TryCatch(bool) -> void | 14 | 1 | ReturnVoid | ir.cpp:724:6:724:13 | +| UninitializedVariables() -> void | 0 | 0 | EnterFunction | ir.cpp:230:6:230:27 | +| UninitializedVariables() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:230:6:230:27 | +| UninitializedVariables() -> void | 0 | 2 | VariableAddress[x] | ir.cpp:231:9:231:9 | +| UninitializedVariables() -> void | 0 | 3 | Uninitialized | ir.cpp:231:9:231:9 | +| UninitializedVariables() -> void | 0 | 4 | Store | ir.cpp:231:9:231:9 | +| UninitializedVariables() -> void | 0 | 5 | VariableAddress[y] | ir.cpp:232:9:232:9 | +| UninitializedVariables() -> void | 0 | 6 | VariableAddress[x] | ir.cpp:232:13:232:13 | +| UninitializedVariables() -> void | 0 | 7 | Load | ir.cpp:232:13:232:13 | +| UninitializedVariables() -> void | 0 | 8 | Store | ir.cpp:232:13:232:13 | +| UninitializedVariables() -> void | 0 | 9 | NoOp | ir.cpp:233:1:233:1 | +| UninitializedVariables() -> void | 0 | 10 | ReturnVoid | ir.cpp:230:6:230:27 | +| UninitializedVariables() -> void | 0 | 11 | UnmodeledUse | ir.cpp:230:6:230:27 | +| UninitializedVariables() -> void | 0 | 12 | ExitFunction | ir.cpp:230:6:230:27 | +| UnionInit(int, float) -> void | 0 | 0 | EnterFunction | ir.cpp:530:6:530:14 | +| UnionInit(int, float) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:530:6:530:14 | +| UnionInit(int, float) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:530:20:530:20 | +| UnionInit(int, float) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:530:20:530:20 | +| UnionInit(int, float) -> void | 0 | 4 | Store | ir.cpp:530:20:530:20 | +| UnionInit(int, float) -> void | 0 | 5 | InitializeParameter[f] | ir.cpp:530:29:530:29 | +| UnionInit(int, float) -> void | 0 | 6 | VariableAddress[f] | ir.cpp:530:29:530:29 | +| UnionInit(int, float) -> void | 0 | 7 | Store | ir.cpp:530:29:530:29 | +| UnionInit(int, float) -> void | 0 | 8 | VariableAddress[u1] | ir.cpp:531:7:531:8 | +| UnionInit(int, float) -> void | 0 | 9 | FieldAddress[d] | ir.cpp:531:11:531:16 | +| UnionInit(int, float) -> void | 0 | 10 | VariableAddress[f] | ir.cpp:531:14:531:14 | +| UnionInit(int, float) -> void | 0 | 11 | Load | ir.cpp:531:14:531:14 | +| UnionInit(int, float) -> void | 0 | 12 | Convert | ir.cpp:531:14:531:14 | +| UnionInit(int, float) -> void | 0 | 13 | Store | ir.cpp:531:14:531:14 | +| UnionInit(int, float) -> void | 0 | 14 | NoOp | ir.cpp:533:1:533:1 | +| UnionInit(int, float) -> void | 0 | 15 | ReturnVoid | ir.cpp:530:6:530:14 | +| UnionInit(int, float) -> void | 0 | 16 | UnmodeledUse | ir.cpp:530:6:530:14 | +| UnionInit(int, float) -> void | 0 | 17 | ExitFunction | ir.cpp:530:6:530:14 | +| VarArgUsage(int) -> void | 0 | 0 | EnterFunction | ir.cpp:888:6:888:16 | +| VarArgUsage(int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:888:6:888:16 | +| VarArgUsage(int) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:888:22:888:22 | +| VarArgUsage(int) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:888:22:888:22 | +| VarArgUsage(int) -> void | 0 | 4 | Store | ir.cpp:888:22:888:22 | +| VarArgUsage(int) -> void | 0 | 5 | VariableAddress[args] | ir.cpp:889:21:889:24 | +| VarArgUsage(int) -> void | 0 | 6 | Uninitialized | ir.cpp:889:21:889:24 | +| VarArgUsage(int) -> void | 0 | 7 | Store | ir.cpp:889:21:889:24 | +| VarArgUsage(int) -> void | 0 | 8 | VariableAddress[args] | ir.cpp:891:22:891:25 | +| VarArgUsage(int) -> void | 0 | 9 | Convert | ir.cpp:891:22:891:25 | +| VarArgUsage(int) -> void | 0 | 10 | VariableAddress[x] | ir.cpp:891:28:891:28 | +| VarArgUsage(int) -> void | 0 | 11 | VarArgsStart | ir.cpp:891:3:891:29 | +| VarArgUsage(int) -> void | 0 | 12 | VariableAddress[args2] | ir.cpp:892:21:892:25 | +| VarArgUsage(int) -> void | 0 | 13 | Uninitialized | ir.cpp:892:21:892:25 | +| VarArgUsage(int) -> void | 0 | 14 | Store | ir.cpp:892:21:892:25 | +| VarArgUsage(int) -> void | 0 | 15 | VariableAddress[args2] | ir.cpp:893:22:893:26 | +| VarArgUsage(int) -> void | 0 | 16 | Convert | ir.cpp:893:22:893:26 | +| VarArgUsage(int) -> void | 0 | 17 | VariableAddress[args] | ir.cpp:893:29:893:32 | +| VarArgUsage(int) -> void | 0 | 18 | Convert | ir.cpp:893:29:893:32 | +| VarArgUsage(int) -> void | 0 | 19 | VarArgsStart | ir.cpp:893:3:893:33 | +| VarArgUsage(int) -> void | 0 | 20 | VariableAddress[d] | ir.cpp:894:10:894:10 | +| VarArgUsage(int) -> void | 0 | 21 | VariableAddress[args] | ir.cpp:894:31:894:34 | +| VarArgUsage(int) -> void | 0 | 22 | Convert | ir.cpp:894:31:894:34 | +| VarArgUsage(int) -> void | 0 | 23 | VarArg | ir.cpp:894:14:894:43 | +| VarArgUsage(int) -> void | 0 | 24 | Load | ir.cpp:894:14:894:43 | +| VarArgUsage(int) -> void | 0 | 25 | Store | ir.cpp:894:14:894:43 | +| VarArgUsage(int) -> void | 0 | 26 | VariableAddress[f] | ir.cpp:895:9:895:9 | +| VarArgUsage(int) -> void | 0 | 27 | VariableAddress[args] | ir.cpp:895:30:895:33 | +| VarArgUsage(int) -> void | 0 | 28 | Convert | ir.cpp:895:30:895:33 | +| VarArgUsage(int) -> void | 0 | 29 | VarArg | ir.cpp:895:30:895:33 | +| VarArgUsage(int) -> void | 0 | 30 | Load | ir.cpp:895:30:895:33 | +| VarArgUsage(int) -> void | 0 | 31 | Convert | ir.cpp:895:13:895:41 | +| VarArgUsage(int) -> void | 0 | 32 | Store | ir.cpp:895:13:895:41 | +| VarArgUsage(int) -> void | 0 | 33 | VariableAddress[args] | ir.cpp:896:20:896:23 | +| VarArgUsage(int) -> void | 0 | 34 | Convert | ir.cpp:896:20:896:23 | +| VarArgUsage(int) -> void | 0 | 35 | VarArgsEnd | ir.cpp:896:3:896:24 | +| VarArgUsage(int) -> void | 0 | 36 | VariableAddress[args2] | ir.cpp:897:20:897:24 | +| VarArgUsage(int) -> void | 0 | 37 | Convert | ir.cpp:897:20:897:24 | +| VarArgUsage(int) -> void | 0 | 38 | VarArgsEnd | ir.cpp:897:3:897:25 | +| VarArgUsage(int) -> void | 0 | 39 | NoOp | ir.cpp:898:1:898:1 | +| VarArgUsage(int) -> void | 0 | 40 | ReturnVoid | ir.cpp:888:6:888:16 | +| VarArgUsage(int) -> void | 0 | 41 | UnmodeledUse | ir.cpp:888:6:888:16 | +| VarArgUsage(int) -> void | 0 | 42 | ExitFunction | ir.cpp:888:6:888:16 | +| VarArgs() -> void | 0 | 0 | EnterFunction | ir.cpp:584:6:584:12 | +| VarArgs() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:584:6:584:12 | +| VarArgs() -> void | 0 | 2 | FunctionAddress[VarArgFunction] | ir.cpp:585:5:585:18 | +| VarArgs() -> void | 0 | 3 | StringConstant["%d %s"] | ir.cpp:585:20:585:26 | +| VarArgs() -> void | 0 | 4 | Convert | ir.cpp:585:20:585:26 | +| VarArgs() -> void | 0 | 5 | Constant[1] | ir.cpp:585:29:585:29 | +| VarArgs() -> void | 0 | 6 | StringConstant["string"] | ir.cpp:585:32:585:39 | +| VarArgs() -> void | 0 | 7 | Convert | ir.cpp:585:32:585:39 | +| VarArgs() -> void | 0 | 8 | Invoke | ir.cpp:585:5:585:18 | +| VarArgs() -> void | 0 | 9 | NoOp | ir.cpp:586:1:586:1 | +| VarArgs() -> void | 0 | 10 | ReturnVoid | ir.cpp:584:6:584:12 | +| VarArgs() -> void | 0 | 11 | UnmodeledUse | ir.cpp:584:6:584:12 | +| VarArgs() -> void | 0 | 12 | ExitFunction | ir.cpp:584:6:584:12 | +| WhileStatements(int) -> void | 0 | 0 | EnterFunction | ir.cpp:253:6:253:20 | +| WhileStatements(int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:253:6:253:20 | +| WhileStatements(int) -> void | 0 | 2 | InitializeParameter[n] | ir.cpp:253:26:253:26 | +| WhileStatements(int) -> void | 0 | 3 | VariableAddress[n] | ir.cpp:253:26:253:26 | +| WhileStatements(int) -> void | 0 | 4 | Store | ir.cpp:253:26:253:26 | +| WhileStatements(int) -> void | 1 | 0 | Constant[1] | ir.cpp:255:14:255:14 | +| WhileStatements(int) -> void | 1 | 1 | VariableAddress[n] | ir.cpp:255:9:255:9 | +| WhileStatements(int) -> void | 1 | 2 | Load | ir.cpp:255:9:255:14 | +| WhileStatements(int) -> void | 1 | 3 | Sub | ir.cpp:255:9:255:14 | +| WhileStatements(int) -> void | 1 | 4 | Store | ir.cpp:255:9:255:14 | +| WhileStatements(int) -> void | 2 | 0 | NoOp | ir.cpp:257:1:257:1 | +| WhileStatements(int) -> void | 2 | 1 | ReturnVoid | ir.cpp:253:6:253:20 | +| WhileStatements(int) -> void | 2 | 2 | UnmodeledUse | ir.cpp:253:6:253:20 | +| WhileStatements(int) -> void | 2 | 3 | ExitFunction | ir.cpp:253:6:253:20 | +| WhileStatements(int) -> void | 3 | 0 | VariableAddress[n] | ir.cpp:254:12:254:12 | +| WhileStatements(int) -> void | 3 | 1 | Load | ir.cpp:254:12:254:12 | +| WhileStatements(int) -> void | 3 | 2 | Constant[0] | ir.cpp:254:16:254:16 | +| WhileStatements(int) -> void | 3 | 3 | CompareGT | ir.cpp:254:12:254:16 | +| WhileStatements(int) -> void | 3 | 4 | ConditionalBranch | ir.cpp:254:12:254:16 | +| min(int, int) -> int | 0 | 0 | EnterFunction | ir.cpp:704:3:704:5 | +| min(int, int) -> int | 0 | 1 | UnmodeledDefinition | ir.cpp:704:3:704:5 | +| min(int, int) -> int | 0 | 2 | InitializeParameter[x] | ir.cpp:704:9:704:9 | +| min(int, int) -> int | 0 | 3 | VariableAddress[x] | ir.cpp:704:9:704:9 | +| min(int, int) -> int | 0 | 4 | Store | ir.cpp:704:9:704:9 | +| min(int, int) -> int | 0 | 5 | InitializeParameter[y] | ir.cpp:704:14:704:14 | +| min(int, int) -> int | 0 | 6 | VariableAddress[y] | ir.cpp:704:14:704:14 | +| min(int, int) -> int | 0 | 7 | Store | ir.cpp:704:14:704:14 | +| min(int, int) -> int | 0 | 8 | VariableAddress[#return] | ir.cpp:705:3:705:25 | +| min(int, int) -> int | 0 | 9 | VariableAddress[x] | ir.cpp:705:11:705:11 | +| min(int, int) -> int | 0 | 10 | Load | ir.cpp:705:11:705:11 | +| min(int, int) -> int | 0 | 11 | VariableAddress[y] | ir.cpp:705:15:705:15 | +| min(int, int) -> int | 0 | 12 | Load | ir.cpp:705:15:705:15 | +| min(int, int) -> int | 0 | 13 | CompareLT | ir.cpp:705:11:705:15 | +| min(int, int) -> int | 0 | 14 | ConditionalBranch | ir.cpp:705:11:705:15 | +| min(int, int) -> int | 1 | 0 | VariableAddress[x] | ir.cpp:705:20:705:20 | +| min(int, int) -> int | 1 | 1 | Load | ir.cpp:705:20:705:20 | +| min(int, int) -> int | 1 | 2 | VariableAddress[#temp705:10] | ir.cpp:705:10:705:24 | +| min(int, int) -> int | 1 | 3 | Store | ir.cpp:705:10:705:24 | +| min(int, int) -> int | 2 | 0 | VariableAddress[y] | ir.cpp:705:24:705:24 | +| min(int, int) -> int | 2 | 1 | Load | ir.cpp:705:24:705:24 | +| min(int, int) -> int | 2 | 2 | VariableAddress[#temp705:10] | ir.cpp:705:10:705:24 | +| min(int, int) -> int | 2 | 3 | Store | ir.cpp:705:10:705:24 | +| min(int, int) -> int | 3 | 0 | VariableAddress[#temp705:10] | ir.cpp:705:10:705:24 | +| min(int, int) -> int | 3 | 1 | Load | ir.cpp:705:10:705:24 | +| min(int, int) -> int | 3 | 2 | Store | ir.cpp:705:10:705:24 | +| min(int, int) -> int | 3 | 3 | VariableAddress[#return] | ir.cpp:704:3:704:5 | +| min(int, int) -> int | 3 | 4 | ReturnValue | ir.cpp:704:3:704:5 | +| min(int, int) -> int | 3 | 5 | UnmodeledUse | ir.cpp:704:3:704:5 | +| min(int, int) -> int | 3 | 6 | ExitFunction | ir.cpp:704:3:704:5 | +printIRGraphEdges +| Break(int) -> void | 0 | 5 | Goto | +| Break(int) -> void | 1 | 2 | True | +| Break(int) -> void | 1 | 3 | False | +| Break(int) -> void | 2 | 4 | Goto | +| Break(int) -> void | 3 | 5 | Goto | +| Break(int) -> void | 5 | 1 | True | +| Break(int) -> void | 5 | 4 | False | +| ConditionValues(bool, bool) -> void | 0 | 1 | True | +| ConditionValues(bool, bool) -> void | 0 | 10 | False | +| ConditionValues(bool, bool) -> void | 1 | 10 | False | +| ConditionValues(bool, bool) -> void | 1 | 12 | True | +| ConditionValues(bool, bool) -> void | 2 | 3 | Goto | +| ConditionValues(bool, bool) -> void | 3 | 8 | True | +| ConditionValues(bool, bool) -> void | 3 | 9 | False | +| ConditionValues(bool, bool) -> void | 4 | 3 | Goto | +| ConditionValues(bool, bool) -> void | 5 | 2 | False | +| ConditionValues(bool, bool) -> void | 5 | 4 | True | +| ConditionValues(bool, bool) -> void | 6 | 7 | Goto | +| ConditionValues(bool, bool) -> void | 8 | 7 | Goto | +| ConditionValues(bool, bool) -> void | 9 | 6 | False | +| ConditionValues(bool, bool) -> void | 9 | 8 | True | +| ConditionValues(bool, bool) -> void | 10 | 11 | Goto | +| ConditionValues(bool, bool) -> void | 11 | 4 | True | +| ConditionValues(bool, bool) -> void | 11 | 5 | False | +| ConditionValues(bool, bool) -> void | 12 | 11 | Goto | +| Conditional(bool, int, int) -> void | 0 | 1 | True | +| Conditional(bool, int, int) -> void | 0 | 2 | False | +| Conditional(bool, int, int) -> void | 1 | 3 | Goto | +| Conditional(bool, int, int) -> void | 2 | 3 | Goto | +| Conditional_LValue(bool) -> void | 0 | 2 | True | +| Conditional_LValue(bool) -> void | 0 | 3 | False | +| Conditional_LValue(bool) -> void | 2 | 1 | Goto | +| Conditional_LValue(bool) -> void | 3 | 1 | Goto | +| Conditional_Void(bool) -> void | 0 | 2 | True | +| Conditional_Void(bool) -> void | 0 | 3 | False | +| Conditional_Void(bool) -> void | 2 | 1 | Goto | +| Conditional_Void(bool) -> void | 3 | 1 | Goto | +| Continue(int) -> void | 0 | 1 | Goto | +| Continue(int) -> void | 1 | 2 | True | +| Continue(int) -> void | 1 | 3 | False | +| Continue(int) -> void | 2 | 4 | Goto | +| Continue(int) -> void | 3 | 4 | Goto | +| Continue(int) -> void | 4 | 1 | True | +| Continue(int) -> void | 4 | 5 | False | +| DoStatements(int) -> void | 0 | 1 | Goto | +| DoStatements(int) -> void | 1 | 1 | True | +| DoStatements(int) -> void | 1 | 2 | False | +| EarlyReturn(int, int) -> void | 0 | 2 | True | +| EarlyReturn(int, int) -> void | 0 | 3 | False | +| EarlyReturn(int, int) -> void | 2 | 1 | Goto | +| EarlyReturn(int, int) -> void | 3 | 1 | Goto | +| EarlyReturnValue(int, int) -> int | 0 | 2 | True | +| EarlyReturnValue(int, int) -> int | 0 | 3 | False | +| EarlyReturnValue(int, int) -> int | 2 | 1 | Goto | +| EarlyReturnValue(int, int) -> int | 3 | 1 | Goto | +| EnumSwitch(E) -> int | 0 | 2 | Case[1] | +| EnumSwitch(E) -> int | 0 | 3 | Default | +| EnumSwitch(E) -> int | 0 | 4 | Case[0] | +| EnumSwitch(E) -> int | 2 | 1 | Goto | +| EnumSwitch(E) -> int | 3 | 1 | Goto | +| EnumSwitch(E) -> int | 4 | 1 | Goto | +| For_Break() -> void | 0 | 1 | Goto | +| For_Break() -> void | 1 | 3 | True | +| For_Break() -> void | 1 | 5 | False | +| For_Break() -> void | 2 | 1 | Goto | +| For_Break() -> void | 3 | 2 | False | +| For_Break() -> void | 3 | 4 | True | +| For_Break() -> void | 4 | 5 | Goto | +| For_Condition() -> void | 0 | 1 | Goto | +| For_Condition() -> void | 1 | 2 | True | +| For_Condition() -> void | 1 | 3 | False | +| For_Condition() -> void | 2 | 1 | Goto | +| For_ConditionUpdate() -> void | 0 | 1 | Goto | +| For_ConditionUpdate() -> void | 1 | 2 | True | +| For_ConditionUpdate() -> void | 1 | 3 | False | +| For_ConditionUpdate() -> void | 2 | 1 | Goto | +| For_Continue_NoUpdate() -> void | 0 | 1 | Goto | +| For_Continue_NoUpdate() -> void | 1 | 2 | True | +| For_Continue_NoUpdate() -> void | 1 | 5 | False | +| For_Continue_NoUpdate() -> void | 2 | 3 | True | +| For_Continue_NoUpdate() -> void | 2 | 4 | False | +| For_Continue_NoUpdate() -> void | 3 | 4 | Goto | +| For_Continue_NoUpdate() -> void | 4 | 1 | Goto | +| For_Continue_Update() -> void | 0 | 1 | Goto | +| For_Continue_Update() -> void | 1 | 2 | True | +| For_Continue_Update() -> void | 1 | 5 | False | +| For_Continue_Update() -> void | 2 | 3 | True | +| For_Continue_Update() -> void | 2 | 4 | False | +| For_Continue_Update() -> void | 3 | 4 | Goto | +| For_Continue_Update() -> void | 4 | 1 | Goto | +| For_Empty() -> void | 0 | 2 | Goto | +| For_Empty() -> void | 2 | 2 | Goto | +| For_Init() -> void | 0 | 2 | Goto | +| For_Init() -> void | 2 | 2 | Goto | +| For_InitCondition() -> void | 0 | 1 | Goto | +| For_InitCondition() -> void | 1 | 2 | True | +| For_InitCondition() -> void | 1 | 3 | False | +| For_InitCondition() -> void | 2 | 1 | Goto | +| For_InitConditionUpdate() -> void | 0 | 1 | Goto | +| For_InitConditionUpdate() -> void | 1 | 2 | True | +| For_InitConditionUpdate() -> void | 1 | 3 | False | +| For_InitConditionUpdate() -> void | 2 | 1 | Goto | +| For_InitUpdate() -> void | 0 | 2 | Goto | +| For_InitUpdate() -> void | 2 | 2 | Goto | +| For_Update() -> void | 0 | 2 | Goto | +| For_Update() -> void | 2 | 2 | Goto | +| IfStatements(bool, int, int) -> void | 0 | 1 | False | +| IfStatements(bool, int, int) -> void | 0 | 7 | True | +| IfStatements(bool, int, int) -> void | 1 | 2 | True | +| IfStatements(bool, int, int) -> void | 1 | 3 | False | +| IfStatements(bool, int, int) -> void | 2 | 3 | Goto | +| IfStatements(bool, int, int) -> void | 3 | 4 | True | +| IfStatements(bool, int, int) -> void | 3 | 5 | False | +| IfStatements(bool, int, int) -> void | 4 | 6 | Goto | +| IfStatements(bool, int, int) -> void | 5 | 6 | Goto | +| IfStatements(bool, int, int) -> void | 7 | 1 | Goto | +| LogicalAnd(bool, bool) -> void | 0 | 1 | True | +| LogicalAnd(bool, bool) -> void | 0 | 3 | False | +| LogicalAnd(bool, bool) -> void | 1 | 2 | True | +| LogicalAnd(bool, bool) -> void | 1 | 3 | False | +| LogicalAnd(bool, bool) -> void | 2 | 3 | Goto | +| LogicalAnd(bool, bool) -> void | 3 | 4 | True | +| LogicalAnd(bool, bool) -> void | 3 | 6 | False | +| LogicalAnd(bool, bool) -> void | 4 | 5 | True | +| LogicalAnd(bool, bool) -> void | 4 | 6 | False | +| LogicalAnd(bool, bool) -> void | 5 | 7 | Goto | +| LogicalAnd(bool, bool) -> void | 6 | 7 | Goto | +| LogicalNot(bool, bool) -> void | 0 | 1 | False | +| LogicalNot(bool, bool) -> void | 0 | 2 | True | +| LogicalNot(bool, bool) -> void | 1 | 2 | Goto | +| LogicalNot(bool, bool) -> void | 2 | 3 | True | +| LogicalNot(bool, bool) -> void | 2 | 4 | False | +| LogicalNot(bool, bool) -> void | 3 | 4 | False | +| LogicalNot(bool, bool) -> void | 3 | 5 | True | +| LogicalNot(bool, bool) -> void | 4 | 6 | Goto | +| LogicalNot(bool, bool) -> void | 5 | 6 | Goto | +| LogicalOr(bool, bool) -> void | 0 | 1 | False | +| LogicalOr(bool, bool) -> void | 0 | 2 | True | +| LogicalOr(bool, bool) -> void | 1 | 2 | True | +| LogicalOr(bool, bool) -> void | 1 | 3 | False | +| LogicalOr(bool, bool) -> void | 2 | 3 | Goto | +| LogicalOr(bool, bool) -> void | 3 | 4 | False | +| LogicalOr(bool, bool) -> void | 3 | 5 | True | +| LogicalOr(bool, bool) -> void | 4 | 5 | True | +| LogicalOr(bool, bool) -> void | 4 | 6 | False | +| LogicalOr(bool, bool) -> void | 5 | 7 | Goto | +| LogicalOr(bool, bool) -> void | 6 | 7 | Goto | +| Switch(int) -> void | 0 | 2 | Case[-1] | +| Switch(int) -> void | 0 | 3 | Case[1] | +| Switch(int) -> void | 0 | 4 | Case[2] | +| Switch(int) -> void | 0 | 5 | Case[3] | +| Switch(int) -> void | 0 | 6 | Case[4] | +| Switch(int) -> void | 0 | 7 | Default | +| Switch(int) -> void | 1 | 2 | Goto | +| Switch(int) -> void | 2 | 9 | Goto | +| Switch(int) -> void | 3 | 4 | Goto | +| Switch(int) -> void | 4 | 9 | Goto | +| Switch(int) -> void | 5 | 6 | Goto | +| Switch(int) -> void | 6 | 9 | Goto | +| Switch(int) -> void | 7 | 9 | Goto | +| Switch(int) -> void | 8 | 9 | Goto | +| TryCatch(bool) -> void | 0 | 3 | True | +| TryCatch(bool) -> void | 0 | 4 | False | +| TryCatch(bool) -> void | 2 | 1 | Goto | +| TryCatch(bool) -> void | 3 | 9 | Exception | +| TryCatch(bool) -> void | 4 | 5 | True | +| TryCatch(bool) -> void | 4 | 8 | False | +| TryCatch(bool) -> void | 5 | 6 | True | +| TryCatch(bool) -> void | 5 | 7 | False | +| TryCatch(bool) -> void | 6 | 8 | Goto | +| TryCatch(bool) -> void | 7 | 9 | Exception | +| TryCatch(bool) -> void | 8 | 14 | Goto | +| TryCatch(bool) -> void | 9 | 10 | Goto | +| TryCatch(bool) -> void | 9 | 11 | Exception | +| TryCatch(bool) -> void | 10 | 2 | Exception | +| TryCatch(bool) -> void | 11 | 12 | Goto | +| TryCatch(bool) -> void | 11 | 13 | Exception | +| TryCatch(bool) -> void | 12 | 14 | Goto | +| TryCatch(bool) -> void | 13 | 2 | Exception | +| TryCatch(bool) -> void | 14 | 1 | Goto | +| WhileStatements(int) -> void | 0 | 3 | Goto | +| WhileStatements(int) -> void | 1 | 3 | Goto | +| WhileStatements(int) -> void | 3 | 1 | True | +| WhileStatements(int) -> void | 3 | 2 | False | +| min(int, int) -> int | 0 | 1 | True | +| min(int, int) -> int | 0 | 2 | False | +| min(int, int) -> int | 1 | 3 | Goto | +| min(int, int) -> int | 2 | 3 | Goto | +printIRGraphDestinationOperands +| AddressOf() -> int * | 0 | 1 | 0 | @mu0_1(unknown) | +| AddressOf() -> int * | 0 | 2 | 0 | @r0_2(glval:int *) | +| AddressOf() -> int * | 0 | 3 | 0 | @r0_3(glval:int) | +| AddressOf() -> int * | 0 | 4 | 0 | @mu0_4(int *) | +| AddressOf() -> int * | 0 | 5 | 0 | @r0_5(glval:int *) | +| ArrayAccess(int *, int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| ArrayAccess(int *, int) -> void | 0 | 2 | 0 | @r0_2(int *) | +| ArrayAccess(int *, int) -> void | 0 | 3 | 0 | @r0_3(glval:int *) | +| ArrayAccess(int *, int) -> void | 0 | 4 | 0 | @mu0_4(int *) | +| ArrayAccess(int *, int) -> void | 0 | 5 | 0 | @r0_5(int) | +| ArrayAccess(int *, int) -> void | 0 | 6 | 0 | @r0_6(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 7 | 0 | @mu0_7(int) | +| ArrayAccess(int *, int) -> void | 0 | 8 | 0 | @r0_8(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 9 | 0 | @r0_9(int) | +| ArrayAccess(int *, int) -> void | 0 | 10 | 0 | @mu0_10(int) | +| ArrayAccess(int *, int) -> void | 0 | 11 | 0 | @r0_11(glval:int *) | +| ArrayAccess(int *, int) -> void | 0 | 12 | 0 | @r0_12(int *) | +| ArrayAccess(int *, int) -> void | 0 | 13 | 0 | @r0_13(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 14 | 0 | @r0_14(int) | +| ArrayAccess(int *, int) -> void | 0 | 15 | 0 | @r0_15(int *) | +| ArrayAccess(int *, int) -> void | 0 | 16 | 0 | @r0_16(int) | +| ArrayAccess(int *, int) -> void | 0 | 17 | 0 | @r0_17(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 18 | 0 | @mu0_18(int) | +| ArrayAccess(int *, int) -> void | 0 | 19 | 0 | @r0_19(glval:int *) | +| ArrayAccess(int *, int) -> void | 0 | 20 | 0 | @r0_20(int *) | +| ArrayAccess(int *, int) -> void | 0 | 21 | 0 | @r0_21(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 22 | 0 | @r0_22(int) | +| ArrayAccess(int *, int) -> void | 0 | 23 | 0 | @r0_23(int *) | +| ArrayAccess(int *, int) -> void | 0 | 24 | 0 | @r0_24(int) | +| ArrayAccess(int *, int) -> void | 0 | 25 | 0 | @r0_25(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 26 | 0 | @mu0_26(int) | +| ArrayAccess(int *, int) -> void | 0 | 27 | 0 | @r0_27(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 28 | 0 | @r0_28(int) | +| ArrayAccess(int *, int) -> void | 0 | 29 | 0 | @r0_29(glval:int *) | +| ArrayAccess(int *, int) -> void | 0 | 30 | 0 | @r0_30(int *) | +| ArrayAccess(int *, int) -> void | 0 | 31 | 0 | @r0_31(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 32 | 0 | @r0_32(int) | +| ArrayAccess(int *, int) -> void | 0 | 33 | 0 | @r0_33(int *) | +| ArrayAccess(int *, int) -> void | 0 | 34 | 0 | @mu0_34(int) | +| ArrayAccess(int *, int) -> void | 0 | 35 | 0 | @r0_35(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 36 | 0 | @r0_36(int) | +| ArrayAccess(int *, int) -> void | 0 | 37 | 0 | @r0_37(glval:int *) | +| ArrayAccess(int *, int) -> void | 0 | 38 | 0 | @r0_38(int *) | +| ArrayAccess(int *, int) -> void | 0 | 39 | 0 | @r0_39(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 40 | 0 | @r0_40(int) | +| ArrayAccess(int *, int) -> void | 0 | 41 | 0 | @r0_41(int *) | +| ArrayAccess(int *, int) -> void | 0 | 42 | 0 | @mu0_42(int) | +| ArrayAccess(int *, int) -> void | 0 | 43 | 0 | @r0_43(glval:int[10]) | +| ArrayAccess(int *, int) -> void | 0 | 44 | 0 | @r0_44(int[10]) | +| ArrayAccess(int *, int) -> void | 0 | 45 | 0 | @mu0_45(int[10]) | +| ArrayAccess(int *, int) -> void | 0 | 46 | 0 | @r0_46(glval:int[10]) | +| ArrayAccess(int *, int) -> void | 0 | 47 | 0 | @r0_47(int *) | +| ArrayAccess(int *, int) -> void | 0 | 48 | 0 | @r0_48(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 49 | 0 | @r0_49(int) | +| ArrayAccess(int *, int) -> void | 0 | 50 | 0 | @r0_50(int *) | +| ArrayAccess(int *, int) -> void | 0 | 51 | 0 | @r0_51(int) | +| ArrayAccess(int *, int) -> void | 0 | 52 | 0 | @r0_52(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 53 | 0 | @mu0_53(int) | +| ArrayAccess(int *, int) -> void | 0 | 54 | 0 | @r0_54(glval:int[10]) | +| ArrayAccess(int *, int) -> void | 0 | 55 | 0 | @r0_55(int *) | +| ArrayAccess(int *, int) -> void | 0 | 56 | 0 | @r0_56(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 57 | 0 | @r0_57(int) | +| ArrayAccess(int *, int) -> void | 0 | 58 | 0 | @r0_58(int *) | +| ArrayAccess(int *, int) -> void | 0 | 59 | 0 | @r0_59(int) | +| ArrayAccess(int *, int) -> void | 0 | 60 | 0 | @r0_60(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 61 | 0 | @mu0_61(int) | +| ArrayAccess(int *, int) -> void | 0 | 62 | 0 | @r0_62(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 63 | 0 | @r0_63(int) | +| ArrayAccess(int *, int) -> void | 0 | 64 | 0 | @r0_64(glval:int[10]) | +| ArrayAccess(int *, int) -> void | 0 | 65 | 0 | @r0_65(int *) | +| ArrayAccess(int *, int) -> void | 0 | 66 | 0 | @r0_66(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 67 | 0 | @r0_67(int) | +| ArrayAccess(int *, int) -> void | 0 | 68 | 0 | @r0_68(int *) | +| ArrayAccess(int *, int) -> void | 0 | 69 | 0 | @mu0_69(int) | +| ArrayAccess(int *, int) -> void | 0 | 70 | 0 | @r0_70(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 71 | 0 | @r0_71(int) | +| ArrayAccess(int *, int) -> void | 0 | 72 | 0 | @r0_72(glval:int[10]) | +| ArrayAccess(int *, int) -> void | 0 | 73 | 0 | @r0_73(int *) | +| ArrayAccess(int *, int) -> void | 0 | 74 | 0 | @r0_74(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 75 | 0 | @r0_75(int) | +| ArrayAccess(int *, int) -> void | 0 | 76 | 0 | @r0_76(int *) | +| ArrayAccess(int *, int) -> void | 0 | 77 | 0 | @mu0_77(int) | +| ArrayConversions() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| ArrayConversions() -> void | 0 | 2 | 0 | @r0_2(glval:char[5]) | +| ArrayConversions() -> void | 0 | 3 | 0 | @r0_3(char[5]) | +| ArrayConversions() -> void | 0 | 4 | 0 | @mu0_4(char[5]) | +| ArrayConversions() -> void | 0 | 5 | 0 | @r0_5(glval:char *) | +| ArrayConversions() -> void | 0 | 6 | 0 | @r0_6(glval:char[5]) | +| ArrayConversions() -> void | 0 | 7 | 0 | @r0_7(char *) | +| ArrayConversions() -> void | 0 | 8 | 0 | @r0_8(char *) | +| ArrayConversions() -> void | 0 | 9 | 0 | @mu0_9(char *) | +| ArrayConversions() -> void | 0 | 10 | 0 | @r0_10(glval:char[5]) | +| ArrayConversions() -> void | 0 | 11 | 0 | @r0_11(char *) | +| ArrayConversions() -> void | 0 | 12 | 0 | @r0_12(glval:char *) | +| ArrayConversions() -> void | 0 | 13 | 0 | @mu0_13(char *) | +| ArrayConversions() -> void | 0 | 14 | 0 | @r0_14(glval:char[5]) | +| ArrayConversions() -> void | 0 | 15 | 0 | @r0_15(char *) | +| ArrayConversions() -> void | 0 | 16 | 0 | @r0_16(int) | +| ArrayConversions() -> void | 0 | 17 | 0 | @r0_17(char *) | +| ArrayConversions() -> void | 0 | 18 | 0 | @r0_18(char *) | +| ArrayConversions() -> void | 0 | 19 | 0 | @r0_19(glval:char *) | +| ArrayConversions() -> void | 0 | 20 | 0 | @mu0_20(char *) | +| ArrayConversions() -> void | 0 | 21 | 0 | @r0_21(glval:char[5]) | +| ArrayConversions() -> void | 0 | 22 | 0 | @r0_22(char *) | +| ArrayConversions() -> void | 0 | 23 | 0 | @r0_23(int) | +| ArrayConversions() -> void | 0 | 24 | 0 | @r0_24(char *) | +| ArrayConversions() -> void | 0 | 25 | 0 | @r0_25(glval:char *) | +| ArrayConversions() -> void | 0 | 26 | 0 | @mu0_26(char *) | +| ArrayConversions() -> void | 0 | 27 | 0 | @r0_27(glval:char(&)[5]) | +| ArrayConversions() -> void | 0 | 28 | 0 | @r0_28(glval:char[5]) | +| ArrayConversions() -> void | 0 | 29 | 0 | @mu0_29(char(&)[5]) | +| ArrayConversions() -> void | 0 | 30 | 0 | @r0_30(glval:char(&)[5]) | +| ArrayConversions() -> void | 0 | 31 | 0 | @r0_31(glval:char[5]) | +| ArrayConversions() -> void | 0 | 32 | 0 | @mu0_32(char(&)[5]) | +| ArrayConversions() -> void | 0 | 33 | 0 | @r0_33(glval:char(*)[5]) | +| ArrayConversions() -> void | 0 | 34 | 0 | @r0_34(glval:char[5]) | +| ArrayConversions() -> void | 0 | 35 | 0 | @r0_35(char(*)[5]) | +| ArrayConversions() -> void | 0 | 36 | 0 | @mu0_36(char(*)[5]) | +| ArrayConversions() -> void | 0 | 37 | 0 | @r0_37(glval:char[5]) | +| ArrayConversions() -> void | 0 | 38 | 0 | @r0_38(glval:char(*)[5]) | +| ArrayConversions() -> void | 0 | 39 | 0 | @mu0_39(char(*)[5]) | +| ArrayInit(int, float) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| ArrayInit(int, float) -> void | 0 | 2 | 0 | @r0_2(int) | +| ArrayInit(int, float) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| ArrayInit(int, float) -> void | 0 | 4 | 0 | @mu0_4(int) | +| ArrayInit(int, float) -> void | 0 | 5 | 0 | @r0_5(float) | +| ArrayInit(int, float) -> void | 0 | 6 | 0 | @r0_6(glval:float) | +| ArrayInit(int, float) -> void | 0 | 7 | 0 | @mu0_7(float) | +| ArrayInit(int, float) -> void | 0 | 8 | 0 | @r0_8(glval:int[3]) | +| ArrayInit(int, float) -> void | 0 | 9 | 0 | @r0_9(int) | +| ArrayInit(int, float) -> void | 0 | 10 | 0 | @r0_10(glval:int) | +| ArrayInit(int, float) -> void | 0 | 11 | 0 | @r0_11(unknown[12]) | +| ArrayInit(int, float) -> void | 0 | 12 | 0 | @mu0_12(unknown[12]) | +| ArrayInit(int, float) -> void | 0 | 13 | 0 | @r0_13(glval:int[3]) | +| ArrayInit(int, float) -> void | 0 | 14 | 0 | @r0_14(int) | +| ArrayInit(int, float) -> void | 0 | 15 | 0 | @r0_15(glval:int) | +| ArrayInit(int, float) -> void | 0 | 16 | 0 | @r0_16(glval:int) | +| ArrayInit(int, float) -> void | 0 | 17 | 0 | @r0_17(int) | +| ArrayInit(int, float) -> void | 0 | 18 | 0 | @mu0_18(int) | +| ArrayInit(int, float) -> void | 0 | 19 | 0 | @r0_19(int) | +| ArrayInit(int, float) -> void | 0 | 20 | 0 | @r0_20(glval:int) | +| ArrayInit(int, float) -> void | 0 | 21 | 0 | @r0_21(glval:float) | +| ArrayInit(int, float) -> void | 0 | 22 | 0 | @r0_22(float) | +| ArrayInit(int, float) -> void | 0 | 23 | 0 | @r0_23(int) | +| ArrayInit(int, float) -> void | 0 | 24 | 0 | @mu0_24(int) | +| ArrayInit(int, float) -> void | 0 | 25 | 0 | @r0_25(int) | +| ArrayInit(int, float) -> void | 0 | 26 | 0 | @r0_26(glval:int) | +| ArrayInit(int, float) -> void | 0 | 27 | 0 | @r0_27(int) | +| ArrayInit(int, float) -> void | 0 | 28 | 0 | @mu0_28(int) | +| ArrayInit(int, float) -> void | 0 | 29 | 0 | @r0_29(glval:int[3]) | +| ArrayInit(int, float) -> void | 0 | 30 | 0 | @r0_30(int) | +| ArrayInit(int, float) -> void | 0 | 31 | 0 | @r0_31(glval:int) | +| ArrayInit(int, float) -> void | 0 | 32 | 0 | @r0_32(glval:int) | +| ArrayInit(int, float) -> void | 0 | 33 | 0 | @r0_33(int) | +| ArrayInit(int, float) -> void | 0 | 34 | 0 | @mu0_34(int) | +| ArrayInit(int, float) -> void | 0 | 35 | 0 | @r0_35(int) | +| ArrayInit(int, float) -> void | 0 | 36 | 0 | @r0_36(glval:int) | +| ArrayInit(int, float) -> void | 0 | 37 | 0 | @r0_37(unknown[8]) | +| ArrayInit(int, float) -> void | 0 | 38 | 0 | @mu0_38(unknown[8]) | +| ArrayReferences() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| ArrayReferences() -> void | 0 | 2 | 0 | @r0_2(glval:int[10]) | +| ArrayReferences() -> void | 0 | 3 | 0 | @r0_3(int[10]) | +| ArrayReferences() -> void | 0 | 4 | 0 | @mu0_4(int[10]) | +| ArrayReferences() -> void | 0 | 5 | 0 | @r0_5(glval:int(&)[10]) | +| ArrayReferences() -> void | 0 | 6 | 0 | @r0_6(glval:int[10]) | +| ArrayReferences() -> void | 0 | 7 | 0 | @mu0_7(int(&)[10]) | +| ArrayReferences() -> void | 0 | 8 | 0 | @r0_8(glval:int) | +| ArrayReferences() -> void | 0 | 9 | 0 | @r0_9(glval:int(&)[10]) | +| ArrayReferences() -> void | 0 | 10 | 0 | @r0_10(int(&)[10]) | +| ArrayReferences() -> void | 0 | 11 | 0 | @r0_11(int *) | +| ArrayReferences() -> void | 0 | 12 | 0 | @r0_12(int) | +| ArrayReferences() -> void | 0 | 13 | 0 | @r0_13(int *) | +| ArrayReferences() -> void | 0 | 14 | 0 | @r0_14(int) | +| ArrayReferences() -> void | 0 | 15 | 0 | @mu0_15(int) | +| Base::Base() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Base::Base() -> void | 0 | 2 | 0 | @r0_2(glval:Base) | +| Base::Base() -> void | 0 | 3 | 0 | @r0_3(glval:String) | +| Base::Base() -> void | 0 | 4 | 0 | @r0_4(bool) | +| Base::Base(const Base &) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Base::Base(const Base &) -> void | 0 | 2 | 0 | @r0_2(glval:Base) | +| Base::Base(const Base &) -> void | 0 | 3 | 0 | @r0_3(Base &) | +| Base::Base(const Base &) -> void | 0 | 4 | 0 | @r0_4(glval:Base &) | +| Base::Base(const Base &) -> void | 0 | 5 | 0 | @mu0_5(Base &) | +| Base::Base(const Base &) -> void | 0 | 6 | 0 | @r0_6(glval:String) | +| Base::Base(const Base &) -> void | 0 | 7 | 0 | @r0_7(bool) | +| Base::operator=(const Base &) -> Base & | 0 | 1 | 0 | @mu0_1(unknown) | +| Base::operator=(const Base &) -> Base & | 0 | 2 | 0 | @r0_2(glval:Base) | +| Base::operator=(const Base &) -> Base & | 0 | 3 | 0 | @r0_3(Base &) | +| Base::operator=(const Base &) -> Base & | 0 | 4 | 0 | @r0_4(glval:Base &) | +| Base::operator=(const Base &) -> Base & | 0 | 5 | 0 | @mu0_5(Base &) | +| Base::operator=(const Base &) -> Base & | 0 | 6 | 0 | @r0_6(Base *) | +| Base::operator=(const Base &) -> Base & | 0 | 7 | 0 | @r0_7(glval:String) | +| Base::operator=(const Base &) -> Base & | 0 | 8 | 0 | @r0_8(bool) | +| Base::operator=(const Base &) -> Base & | 0 | 9 | 0 | @r0_9(glval:Base &) | +| Base::operator=(const Base &) -> Base & | 0 | 10 | 0 | @r0_10(Base &) | +| Base::operator=(const Base &) -> Base & | 0 | 11 | 0 | @r0_11(glval:String) | +| Base::operator=(const Base &) -> Base & | 0 | 12 | 0 | @r0_12(String &) | +| Base::operator=(const Base &) -> Base & | 0 | 13 | 0 | @r0_13(glval:Base &) | +| Base::operator=(const Base &) -> Base & | 0 | 14 | 0 | @r0_14(Base *) | +| Base::operator=(const Base &) -> Base & | 0 | 15 | 0 | @mu0_15(Base &) | +| Base::operator=(const Base &) -> Base & | 0 | 16 | 0 | @r0_16(glval:Base &) | +| Base::~Base() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Base::~Base() -> void | 0 | 2 | 0 | @r0_2(glval:Base) | +| Base::~Base() -> void | 0 | 4 | 0 | @r0_4(glval:String) | +| Base::~Base() -> void | 0 | 5 | 0 | @r0_5(bool) | +| Break(int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Break(int) -> void | 0 | 2 | 0 | @r0_2(int) | +| Break(int) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| Break(int) -> void | 0 | 4 | 0 | @mu0_4(int) | +| Break(int) -> void | 1 | 0 | 0 | @r1_0(glval:int) | +| Break(int) -> void | 1 | 1 | 0 | @r1_1(int) | +| Break(int) -> void | 1 | 2 | 0 | @r1_2(int) | +| Break(int) -> void | 1 | 3 | 0 | @r1_3(bool) | +| Break(int) -> void | 3 | 0 | 0 | @r3_0(int) | +| Break(int) -> void | 3 | 1 | 0 | @r3_1(glval:int) | +| Break(int) -> void | 3 | 2 | 0 | @r3_2(int) | +| Break(int) -> void | 3 | 3 | 0 | @r3_3(int) | +| Break(int) -> void | 3 | 4 | 0 | @mu3_4(int) | +| Break(int) -> void | 5 | 0 | 0 | @r5_0(glval:int) | +| Break(int) -> void | 5 | 1 | 0 | @r5_1(int) | +| Break(int) -> void | 5 | 2 | 0 | @r5_2(int) | +| Break(int) -> void | 5 | 3 | 0 | @r5_3(bool) | +| C::C() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| C::C() -> void | 0 | 2 | 0 | @r0_2(glval:C) | +| C::C() -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| C::C() -> void | 0 | 4 | 0 | @r0_4(int) | +| C::C() -> void | 0 | 5 | 0 | @mu0_5(int) | +| C::C() -> void | 0 | 6 | 0 | @r0_6(glval:String) | +| C::C() -> void | 0 | 7 | 0 | @r0_7(bool) | +| C::C() -> void | 0 | 9 | 0 | @r0_9(glval:char) | +| C::C() -> void | 0 | 10 | 0 | @r0_10(char) | +| C::C() -> void | 0 | 11 | 0 | @mu0_11(char) | +| C::C() -> void | 0 | 12 | 0 | @r0_12(glval:void *) | +| C::C() -> void | 0 | 13 | 0 | @r0_13(void *) | +| C::C() -> void | 0 | 14 | 0 | @mu0_14(void *) | +| C::C() -> void | 0 | 15 | 0 | @r0_15(glval:String) | +| C::C() -> void | 0 | 16 | 0 | @r0_16(bool) | +| C::C() -> void | 0 | 17 | 0 | @r0_17(glval:char[5]) | +| C::C() -> void | 0 | 18 | 0 | @r0_18(char *) | +| C::FieldAccess() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| C::FieldAccess() -> void | 0 | 2 | 0 | @r0_2(glval:C) | +| C::FieldAccess() -> void | 0 | 3 | 0 | @r0_3(int) | +| C::FieldAccess() -> void | 0 | 4 | 0 | @r0_4(C *) | +| C::FieldAccess() -> void | 0 | 5 | 0 | @r0_5(glval:int) | +| C::FieldAccess() -> void | 0 | 6 | 0 | @mu0_6(int) | +| C::FieldAccess() -> void | 0 | 7 | 0 | @r0_7(int) | +| C::FieldAccess() -> void | 0 | 8 | 0 | @r0_8(C *) | +| C::FieldAccess() -> void | 0 | 9 | 0 | @r0_9(glval:int) | +| C::FieldAccess() -> void | 0 | 10 | 0 | @mu0_10(int) | +| C::FieldAccess() -> void | 0 | 11 | 0 | @r0_11(int) | +| C::FieldAccess() -> void | 0 | 12 | 0 | @r0_12(C *) | +| C::FieldAccess() -> void | 0 | 13 | 0 | @r0_13(glval:int) | +| C::FieldAccess() -> void | 0 | 14 | 0 | @mu0_14(int) | +| C::FieldAccess() -> void | 0 | 15 | 0 | @r0_15(glval:int) | +| C::FieldAccess() -> void | 0 | 16 | 0 | @r0_16(int) | +| C::FieldAccess() -> void | 0 | 17 | 0 | @mu0_17(int) | +| C::FieldAccess() -> void | 0 | 18 | 0 | @r0_18(C *) | +| C::FieldAccess() -> void | 0 | 19 | 0 | @r0_19(glval:int) | +| C::FieldAccess() -> void | 0 | 20 | 0 | @r0_20(int) | +| C::FieldAccess() -> void | 0 | 21 | 0 | @r0_21(glval:int) | +| C::FieldAccess() -> void | 0 | 22 | 0 | @mu0_22(int) | +| C::FieldAccess() -> void | 0 | 23 | 0 | @r0_23(C *) | +| C::FieldAccess() -> void | 0 | 24 | 0 | @r0_24(glval:int) | +| C::FieldAccess() -> void | 0 | 25 | 0 | @r0_25(int) | +| C::FieldAccess() -> void | 0 | 26 | 0 | @r0_26(glval:int) | +| C::FieldAccess() -> void | 0 | 27 | 0 | @mu0_27(int) | +| C::FieldAccess() -> void | 0 | 28 | 0 | @r0_28(C *) | +| C::FieldAccess() -> void | 0 | 29 | 0 | @r0_29(glval:int) | +| C::FieldAccess() -> void | 0 | 30 | 0 | @r0_30(int) | +| C::FieldAccess() -> void | 0 | 31 | 0 | @r0_31(glval:int) | +| C::FieldAccess() -> void | 0 | 32 | 0 | @mu0_32(int) | +| C::InstanceMemberFunction(int) -> int | 0 | 1 | 0 | @mu0_1(unknown) | +| C::InstanceMemberFunction(int) -> int | 0 | 2 | 0 | @r0_2(glval:C) | +| C::InstanceMemberFunction(int) -> int | 0 | 3 | 0 | @r0_3(int) | +| C::InstanceMemberFunction(int) -> int | 0 | 4 | 0 | @r0_4(glval:int) | +| C::InstanceMemberFunction(int) -> int | 0 | 5 | 0 | @mu0_5(int) | +| C::InstanceMemberFunction(int) -> int | 0 | 6 | 0 | @r0_6(glval:int) | +| C::InstanceMemberFunction(int) -> int | 0 | 7 | 0 | @r0_7(glval:int) | +| C::InstanceMemberFunction(int) -> int | 0 | 8 | 0 | @r0_8(int) | +| C::InstanceMemberFunction(int) -> int | 0 | 9 | 0 | @mu0_9(int) | +| C::InstanceMemberFunction(int) -> int | 0 | 10 | 0 | @r0_10(glval:int) | +| C::MethodCalls() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| C::MethodCalls() -> void | 0 | 2 | 0 | @r0_2(glval:C) | +| C::MethodCalls() -> void | 0 | 3 | 0 | @r0_3(C *) | +| C::MethodCalls() -> void | 0 | 4 | 0 | @r0_4(bool) | +| C::MethodCalls() -> void | 0 | 5 | 0 | @r0_5(int) | +| C::MethodCalls() -> void | 0 | 6 | 0 | @r0_6(int) | +| C::MethodCalls() -> void | 0 | 7 | 0 | @r0_7(C *) | +| C::MethodCalls() -> void | 0 | 8 | 0 | @r0_8(bool) | +| C::MethodCalls() -> void | 0 | 9 | 0 | @r0_9(int) | +| C::MethodCalls() -> void | 0 | 10 | 0 | @r0_10(int) | +| C::MethodCalls() -> void | 0 | 11 | 0 | @r0_11(C *) | +| C::MethodCalls() -> void | 0 | 12 | 0 | @r0_12(bool) | +| C::MethodCalls() -> void | 0 | 13 | 0 | @r0_13(int) | +| C::MethodCalls() -> void | 0 | 14 | 0 | @r0_14(int) | +| C::StaticMemberFunction(int) -> int | 0 | 1 | 0 | @mu0_1(unknown) | +| C::StaticMemberFunction(int) -> int | 0 | 2 | 0 | @r0_2(int) | +| C::StaticMemberFunction(int) -> int | 0 | 3 | 0 | @r0_3(glval:int) | +| C::StaticMemberFunction(int) -> int | 0 | 4 | 0 | @mu0_4(int) | +| C::StaticMemberFunction(int) -> int | 0 | 5 | 0 | @r0_5(glval:int) | +| C::StaticMemberFunction(int) -> int | 0 | 6 | 0 | @r0_6(glval:int) | +| C::StaticMemberFunction(int) -> int | 0 | 7 | 0 | @r0_7(int) | +| C::StaticMemberFunction(int) -> int | 0 | 8 | 0 | @mu0_8(int) | +| C::StaticMemberFunction(int) -> int | 0 | 9 | 0 | @r0_9(glval:int) | +| C::VirtualMemberFunction(int) -> int | 0 | 1 | 0 | @mu0_1(unknown) | +| C::VirtualMemberFunction(int) -> int | 0 | 2 | 0 | @r0_2(glval:C) | +| C::VirtualMemberFunction(int) -> int | 0 | 3 | 0 | @r0_3(int) | +| C::VirtualMemberFunction(int) -> int | 0 | 4 | 0 | @r0_4(glval:int) | +| C::VirtualMemberFunction(int) -> int | 0 | 5 | 0 | @mu0_5(int) | +| C::VirtualMemberFunction(int) -> int | 0 | 6 | 0 | @r0_6(glval:int) | +| C::VirtualMemberFunction(int) -> int | 0 | 7 | 0 | @r0_7(glval:int) | +| C::VirtualMemberFunction(int) -> int | 0 | 8 | 0 | @r0_8(int) | +| C::VirtualMemberFunction(int) -> int | 0 | 9 | 0 | @mu0_9(int) | +| C::VirtualMemberFunction(int) -> int | 0 | 10 | 0 | @r0_10(glval:int) | +| Call() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Call() -> void | 0 | 2 | 0 | @r0_2(bool) | +| CallAdd(int, int) -> int | 0 | 1 | 0 | @mu0_1(unknown) | +| CallAdd(int, int) -> int | 0 | 2 | 0 | @r0_2(int) | +| CallAdd(int, int) -> int | 0 | 3 | 0 | @r0_3(glval:int) | +| CallAdd(int, int) -> int | 0 | 4 | 0 | @mu0_4(int) | +| CallAdd(int, int) -> int | 0 | 5 | 0 | @r0_5(int) | +| CallAdd(int, int) -> int | 0 | 6 | 0 | @r0_6(glval:int) | +| CallAdd(int, int) -> int | 0 | 7 | 0 | @mu0_7(int) | +| CallAdd(int, int) -> int | 0 | 8 | 0 | @r0_8(glval:int) | +| CallAdd(int, int) -> int | 0 | 9 | 0 | @r0_9(bool) | +| CallAdd(int, int) -> int | 0 | 10 | 0 | @r0_10(glval:int) | +| CallAdd(int, int) -> int | 0 | 11 | 0 | @r0_11(int) | +| CallAdd(int, int) -> int | 0 | 12 | 0 | @r0_12(glval:int) | +| CallAdd(int, int) -> int | 0 | 13 | 0 | @r0_13(int) | +| CallAdd(int, int) -> int | 0 | 14 | 0 | @r0_14(int) | +| CallAdd(int, int) -> int | 0 | 15 | 0 | @mu0_15(int) | +| CallAdd(int, int) -> int | 0 | 16 | 0 | @r0_16(glval:int) | +| CallMethods(String &, String *, String) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| CallMethods(String &, String *, String) -> void | 0 | 2 | 0 | @r0_2(String &) | +| CallMethods(String &, String *, String) -> void | 0 | 3 | 0 | @r0_3(glval:String &) | +| CallMethods(String &, String *, String) -> void | 0 | 4 | 0 | @mu0_4(String &) | +| CallMethods(String &, String *, String) -> void | 0 | 5 | 0 | @r0_5(String *) | +| CallMethods(String &, String *, String) -> void | 0 | 6 | 0 | @r0_6(glval:String *) | +| CallMethods(String &, String *, String) -> void | 0 | 7 | 0 | @mu0_7(String *) | +| CallMethods(String &, String *, String) -> void | 0 | 8 | 0 | @r0_8(String) | +| CallMethods(String &, String *, String) -> void | 0 | 9 | 0 | @r0_9(glval:String) | +| CallMethods(String &, String *, String) -> void | 0 | 10 | 0 | @mu0_10(String) | +| CallMethods(String &, String *, String) -> void | 0 | 11 | 0 | @r0_11(glval:String &) | +| CallMethods(String &, String *, String) -> void | 0 | 12 | 0 | @r0_12(String &) | +| CallMethods(String &, String *, String) -> void | 0 | 13 | 0 | @r0_13(glval:String) | +| CallMethods(String &, String *, String) -> void | 0 | 14 | 0 | @r0_14(bool) | +| CallMethods(String &, String *, String) -> void | 0 | 15 | 0 | @r0_15(char *) | +| CallMethods(String &, String *, String) -> void | 0 | 16 | 0 | @r0_16(glval:String *) | +| CallMethods(String &, String *, String) -> void | 0 | 17 | 0 | @r0_17(String *) | +| CallMethods(String &, String *, String) -> void | 0 | 18 | 0 | @r0_18(String *) | +| CallMethods(String &, String *, String) -> void | 0 | 19 | 0 | @r0_19(bool) | +| CallMethods(String &, String *, String) -> void | 0 | 20 | 0 | @r0_20(char *) | +| CallMethods(String &, String *, String) -> void | 0 | 21 | 0 | @r0_21(glval:String) | +| CallMethods(String &, String *, String) -> void | 0 | 22 | 0 | @r0_22(glval:String) | +| CallMethods(String &, String *, String) -> void | 0 | 23 | 0 | @r0_23(bool) | +| CallMethods(String &, String *, String) -> void | 0 | 24 | 0 | @r0_24(char *) | +| CallMin(int, int) -> int | 0 | 1 | 0 | @mu0_1(unknown) | +| CallMin(int, int) -> int | 0 | 2 | 0 | @r0_2(int) | +| CallMin(int, int) -> int | 0 | 3 | 0 | @r0_3(glval:int) | +| CallMin(int, int) -> int | 0 | 4 | 0 | @mu0_4(int) | +| CallMin(int, int) -> int | 0 | 5 | 0 | @r0_5(int) | +| CallMin(int, int) -> int | 0 | 6 | 0 | @r0_6(glval:int) | +| CallMin(int, int) -> int | 0 | 7 | 0 | @mu0_7(int) | +| CallMin(int, int) -> int | 0 | 8 | 0 | @r0_8(glval:int) | +| CallMin(int, int) -> int | 0 | 9 | 0 | @r0_9(bool) | +| CallMin(int, int) -> int | 0 | 10 | 0 | @r0_10(glval:int) | +| CallMin(int, int) -> int | 0 | 11 | 0 | @r0_11(int) | +| CallMin(int, int) -> int | 0 | 12 | 0 | @r0_12(glval:int) | +| CallMin(int, int) -> int | 0 | 13 | 0 | @r0_13(int) | +| CallMin(int, int) -> int | 0 | 14 | 0 | @r0_14(int) | +| CallMin(int, int) -> int | 0 | 15 | 0 | @mu0_15(int) | +| CallMin(int, int) -> int | 0 | 16 | 0 | @r0_16(glval:int) | +| CallNestedTemplateFunc() -> double | 0 | 1 | 0 | @mu0_1(unknown) | +| CallNestedTemplateFunc() -> double | 0 | 2 | 0 | @r0_2(glval:double) | +| CallNestedTemplateFunc() -> double | 0 | 3 | 0 | @r0_3(bool) | +| CallNestedTemplateFunc() -> double | 0 | 4 | 0 | @r0_4(void *) | +| CallNestedTemplateFunc() -> double | 0 | 5 | 0 | @r0_5(char) | +| CallNestedTemplateFunc() -> double | 0 | 6 | 0 | @r0_6(long) | +| CallNestedTemplateFunc() -> double | 0 | 7 | 0 | @r0_7(double) | +| CallNestedTemplateFunc() -> double | 0 | 8 | 0 | @mu0_8(double) | +| CallNestedTemplateFunc() -> double | 0 | 9 | 0 | @r0_9(glval:double) | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 1 | 0 | @mu0_1(unknown) | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 2 | 0 | @r0_2(..(*)(..)) | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 3 | 0 | @r0_3(glval:..(*)(..)) | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 4 | 0 | @mu0_4(..(*)(..)) | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 5 | 0 | @r0_5(glval:int) | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 6 | 0 | @r0_6(glval:..(*)(..)) | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 7 | 0 | @r0_7(..(*)(..)) | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 8 | 0 | @r0_8(int) | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 9 | 0 | @r0_9(int) | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 10 | 0 | @mu0_10(int) | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 11 | 0 | @r0_11(glval:int) | +| Comma(int, int) -> int | 0 | 1 | 0 | @mu0_1(unknown) | +| Comma(int, int) -> int | 0 | 2 | 0 | @r0_2(int) | +| Comma(int, int) -> int | 0 | 3 | 0 | @r0_3(glval:int) | +| Comma(int, int) -> int | 0 | 4 | 0 | @mu0_4(int) | +| Comma(int, int) -> int | 0 | 5 | 0 | @r0_5(int) | +| Comma(int, int) -> int | 0 | 6 | 0 | @r0_6(glval:int) | +| Comma(int, int) -> int | 0 | 7 | 0 | @mu0_7(int) | +| Comma(int, int) -> int | 0 | 8 | 0 | @r0_8(glval:int) | +| Comma(int, int) -> int | 0 | 9 | 0 | @r0_9(bool) | +| Comma(int, int) -> int | 0 | 11 | 0 | @r0_11(bool) | +| Comma(int, int) -> int | 0 | 12 | 0 | @r0_12(glval:int) | +| Comma(int, int) -> int | 0 | 13 | 0 | @r0_13(int) | +| Comma(int, int) -> int | 0 | 14 | 0 | @r0_14(glval:int) | +| Comma(int, int) -> int | 0 | 15 | 0 | @r0_15(int) | +| Comma(int, int) -> int | 0 | 16 | 0 | @r0_16(int) | +| Comma(int, int) -> int | 0 | 17 | 0 | @mu0_17(int) | +| Comma(int, int) -> int | 0 | 18 | 0 | @r0_18(glval:int) | +| CompoundAssignment() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| CompoundAssignment() -> void | 0 | 2 | 0 | @r0_2(glval:int) | +| CompoundAssignment() -> void | 0 | 3 | 0 | @r0_3(int) | +| CompoundAssignment() -> void | 0 | 4 | 0 | @mu0_4(int) | +| CompoundAssignment() -> void | 0 | 5 | 0 | @r0_5(int) | +| CompoundAssignment() -> void | 0 | 6 | 0 | @r0_6(glval:int) | +| CompoundAssignment() -> void | 0 | 7 | 0 | @r0_7(int) | +| CompoundAssignment() -> void | 0 | 8 | 0 | @r0_8(int) | +| CompoundAssignment() -> void | 0 | 9 | 0 | @mu0_9(int) | +| CompoundAssignment() -> void | 0 | 10 | 0 | @r0_10(glval:short) | +| CompoundAssignment() -> void | 0 | 11 | 0 | @r0_11(short) | +| CompoundAssignment() -> void | 0 | 12 | 0 | @mu0_12(short) | +| CompoundAssignment() -> void | 0 | 13 | 0 | @r0_13(glval:int) | +| CompoundAssignment() -> void | 0 | 14 | 0 | @r0_14(int) | +| CompoundAssignment() -> void | 0 | 15 | 0 | @r0_15(glval:short) | +| CompoundAssignment() -> void | 0 | 16 | 0 | @r0_16(short) | +| CompoundAssignment() -> void | 0 | 17 | 0 | @r0_17(int) | +| CompoundAssignment() -> void | 0 | 18 | 0 | @r0_18(int) | +| CompoundAssignment() -> void | 0 | 19 | 0 | @r0_19(short) | +| CompoundAssignment() -> void | 0 | 20 | 0 | @mu0_20(short) | +| CompoundAssignment() -> void | 0 | 21 | 0 | @r0_21(int) | +| CompoundAssignment() -> void | 0 | 22 | 0 | @r0_22(glval:short) | +| CompoundAssignment() -> void | 0 | 23 | 0 | @r0_23(short) | +| CompoundAssignment() -> void | 0 | 24 | 0 | @r0_24(short) | +| CompoundAssignment() -> void | 0 | 25 | 0 | @mu0_25(short) | +| CompoundAssignment() -> void | 0 | 26 | 0 | @r0_26(glval:long) | +| CompoundAssignment() -> void | 0 | 27 | 0 | @r0_27(long) | +| CompoundAssignment() -> void | 0 | 28 | 0 | @mu0_28(long) | +| CompoundAssignment() -> void | 0 | 29 | 0 | @r0_29(float) | +| CompoundAssignment() -> void | 0 | 30 | 0 | @r0_30(glval:long) | +| CompoundAssignment() -> void | 0 | 31 | 0 | @r0_31(long) | +| CompoundAssignment() -> void | 0 | 32 | 0 | @r0_32(float) | +| CompoundAssignment() -> void | 0 | 33 | 0 | @r0_33(float) | +| CompoundAssignment() -> void | 0 | 34 | 0 | @r0_34(long) | +| CompoundAssignment() -> void | 0 | 35 | 0 | @mu0_35(long) | +| ConditionValues(bool, bool) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| ConditionValues(bool, bool) -> void | 0 | 2 | 0 | @r0_2(bool) | +| ConditionValues(bool, bool) -> void | 0 | 3 | 0 | @r0_3(glval:bool) | +| ConditionValues(bool, bool) -> void | 0 | 4 | 0 | @mu0_4(bool) | +| ConditionValues(bool, bool) -> void | 0 | 5 | 0 | @r0_5(bool) | +| ConditionValues(bool, bool) -> void | 0 | 6 | 0 | @r0_6(glval:bool) | +| ConditionValues(bool, bool) -> void | 0 | 7 | 0 | @mu0_7(bool) | +| ConditionValues(bool, bool) -> void | 0 | 8 | 0 | @r0_8(glval:bool) | +| ConditionValues(bool, bool) -> void | 0 | 9 | 0 | @r0_9(bool) | +| ConditionValues(bool, bool) -> void | 0 | 10 | 0 | @mu0_10(bool) | +| ConditionValues(bool, bool) -> void | 0 | 11 | 0 | @r0_11(glval:bool) | +| ConditionValues(bool, bool) -> void | 0 | 12 | 0 | @r0_12(bool) | +| ConditionValues(bool, bool) -> void | 1 | 0 | 0 | @r1_0(glval:bool) | +| ConditionValues(bool, bool) -> void | 1 | 1 | 0 | @r1_1(bool) | +| ConditionValues(bool, bool) -> void | 2 | 0 | 0 | @r2_0(glval:bool) | +| ConditionValues(bool, bool) -> void | 2 | 1 | 0 | @r2_1(bool) | +| ConditionValues(bool, bool) -> void | 2 | 2 | 0 | @mu2_2(bool) | +| ConditionValues(bool, bool) -> void | 3 | 0 | 0 | @r3_0(glval:bool) | +| ConditionValues(bool, bool) -> void | 3 | 1 | 0 | @r3_1(bool) | +| ConditionValues(bool, bool) -> void | 3 | 2 | 0 | @r3_2(glval:bool) | +| ConditionValues(bool, bool) -> void | 3 | 3 | 0 | @mu3_3(bool) | +| ConditionValues(bool, bool) -> void | 3 | 4 | 0 | @r3_4(glval:bool) | +| ConditionValues(bool, bool) -> void | 3 | 5 | 0 | @r3_5(bool) | +| ConditionValues(bool, bool) -> void | 4 | 0 | 0 | @r4_0(glval:bool) | +| ConditionValues(bool, bool) -> void | 4 | 1 | 0 | @r4_1(bool) | +| ConditionValues(bool, bool) -> void | 4 | 2 | 0 | @mu4_2(bool) | +| ConditionValues(bool, bool) -> void | 5 | 0 | 0 | @r5_0(glval:bool) | +| ConditionValues(bool, bool) -> void | 5 | 1 | 0 | @r5_1(bool) | +| ConditionValues(bool, bool) -> void | 6 | 0 | 0 | @r6_0(glval:bool) | +| ConditionValues(bool, bool) -> void | 6 | 1 | 0 | @r6_1(bool) | +| ConditionValues(bool, bool) -> void | 6 | 2 | 0 | @mu6_2(bool) | +| ConditionValues(bool, bool) -> void | 7 | 0 | 0 | @r7_0(glval:bool) | +| ConditionValues(bool, bool) -> void | 7 | 1 | 0 | @r7_1(bool) | +| ConditionValues(bool, bool) -> void | 7 | 2 | 0 | @r7_2(bool) | +| ConditionValues(bool, bool) -> void | 7 | 3 | 0 | @r7_3(glval:bool) | +| ConditionValues(bool, bool) -> void | 7 | 4 | 0 | @mu7_4(bool) | +| ConditionValues(bool, bool) -> void | 8 | 0 | 0 | @r8_0(glval:bool) | +| ConditionValues(bool, bool) -> void | 8 | 1 | 0 | @r8_1(bool) | +| ConditionValues(bool, bool) -> void | 8 | 2 | 0 | @mu8_2(bool) | +| ConditionValues(bool, bool) -> void | 9 | 0 | 0 | @r9_0(glval:bool) | +| ConditionValues(bool, bool) -> void | 9 | 1 | 0 | @r9_1(bool) | +| ConditionValues(bool, bool) -> void | 10 | 0 | 0 | @r10_0(glval:bool) | +| ConditionValues(bool, bool) -> void | 10 | 1 | 0 | @r10_1(bool) | +| ConditionValues(bool, bool) -> void | 10 | 2 | 0 | @mu10_2(bool) | +| ConditionValues(bool, bool) -> void | 11 | 0 | 0 | @r11_0(glval:bool) | +| ConditionValues(bool, bool) -> void | 11 | 1 | 0 | @r11_1(bool) | +| ConditionValues(bool, bool) -> void | 11 | 2 | 0 | @r11_2(glval:bool) | +| ConditionValues(bool, bool) -> void | 11 | 3 | 0 | @mu11_3(bool) | +| ConditionValues(bool, bool) -> void | 11 | 4 | 0 | @r11_4(glval:bool) | +| ConditionValues(bool, bool) -> void | 11 | 5 | 0 | @r11_5(bool) | +| ConditionValues(bool, bool) -> void | 12 | 0 | 0 | @r12_0(glval:bool) | +| ConditionValues(bool, bool) -> void | 12 | 1 | 0 | @r12_1(bool) | +| ConditionValues(bool, bool) -> void | 12 | 2 | 0 | @mu12_2(bool) | +| Conditional(bool, int, int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Conditional(bool, int, int) -> void | 0 | 2 | 0 | @r0_2(bool) | +| Conditional(bool, int, int) -> void | 0 | 3 | 0 | @r0_3(glval:bool) | +| Conditional(bool, int, int) -> void | 0 | 4 | 0 | @mu0_4(bool) | +| Conditional(bool, int, int) -> void | 0 | 5 | 0 | @r0_5(int) | +| Conditional(bool, int, int) -> void | 0 | 6 | 0 | @r0_6(glval:int) | +| Conditional(bool, int, int) -> void | 0 | 7 | 0 | @mu0_7(int) | +| Conditional(bool, int, int) -> void | 0 | 8 | 0 | @r0_8(int) | +| Conditional(bool, int, int) -> void | 0 | 9 | 0 | @r0_9(glval:int) | +| Conditional(bool, int, int) -> void | 0 | 10 | 0 | @mu0_10(int) | +| Conditional(bool, int, int) -> void | 0 | 11 | 0 | @r0_11(glval:int) | +| Conditional(bool, int, int) -> void | 0 | 12 | 0 | @r0_12(glval:bool) | +| Conditional(bool, int, int) -> void | 0 | 13 | 0 | @r0_13(bool) | +| Conditional(bool, int, int) -> void | 1 | 0 | 0 | @r1_0(glval:int) | +| Conditional(bool, int, int) -> void | 1 | 1 | 0 | @r1_1(int) | +| Conditional(bool, int, int) -> void | 1 | 2 | 0 | @r1_2(glval:int) | +| Conditional(bool, int, int) -> void | 1 | 3 | 0 | @mu1_3(int) | +| Conditional(bool, int, int) -> void | 2 | 0 | 0 | @r2_0(glval:int) | +| Conditional(bool, int, int) -> void | 2 | 1 | 0 | @r2_1(int) | +| Conditional(bool, int, int) -> void | 2 | 2 | 0 | @r2_2(glval:int) | +| Conditional(bool, int, int) -> void | 2 | 3 | 0 | @mu2_3(int) | +| Conditional(bool, int, int) -> void | 3 | 0 | 0 | @r3_0(glval:int) | +| Conditional(bool, int, int) -> void | 3 | 1 | 0 | @r3_1(int) | +| Conditional(bool, int, int) -> void | 3 | 2 | 0 | @mu3_2(int) | +| Conditional_LValue(bool) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Conditional_LValue(bool) -> void | 0 | 2 | 0 | @r0_2(bool) | +| Conditional_LValue(bool) -> void | 0 | 3 | 0 | @r0_3(glval:bool) | +| Conditional_LValue(bool) -> void | 0 | 4 | 0 | @mu0_4(bool) | +| Conditional_LValue(bool) -> void | 0 | 5 | 0 | @r0_5(glval:int) | +| Conditional_LValue(bool) -> void | 0 | 6 | 0 | @r0_6(int) | +| Conditional_LValue(bool) -> void | 0 | 7 | 0 | @mu0_7(int) | +| Conditional_LValue(bool) -> void | 0 | 8 | 0 | @r0_8(glval:int) | +| Conditional_LValue(bool) -> void | 0 | 9 | 0 | @r0_9(int) | +| Conditional_LValue(bool) -> void | 0 | 10 | 0 | @mu0_10(int) | +| Conditional_LValue(bool) -> void | 0 | 11 | 0 | @r0_11(int) | +| Conditional_LValue(bool) -> void | 0 | 12 | 0 | @r0_12(glval:bool) | +| Conditional_LValue(bool) -> void | 0 | 13 | 0 | @r0_13(bool) | +| Conditional_LValue(bool) -> void | 1 | 0 | 0 | @r1_0(glval:int) | +| Conditional_LValue(bool) -> void | 1 | 1 | 0 | @r1_1(glval:int) | +| Conditional_LValue(bool) -> void | 1 | 2 | 0 | @mu1_2(int) | +| Conditional_LValue(bool) -> void | 2 | 0 | 0 | @r2_0(glval:int) | +| Conditional_LValue(bool) -> void | 2 | 1 | 0 | @r2_1(glval:int) | +| Conditional_LValue(bool) -> void | 2 | 2 | 0 | @mu2_2(int) | +| Conditional_LValue(bool) -> void | 3 | 0 | 0 | @r3_0(glval:int) | +| Conditional_LValue(bool) -> void | 3 | 1 | 0 | @r3_1(glval:int) | +| Conditional_LValue(bool) -> void | 3 | 2 | 0 | @mu3_2(int) | +| Conditional_Void(bool) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Conditional_Void(bool) -> void | 0 | 2 | 0 | @r0_2(bool) | +| Conditional_Void(bool) -> void | 0 | 3 | 0 | @r0_3(glval:bool) | +| Conditional_Void(bool) -> void | 0 | 4 | 0 | @mu0_4(bool) | +| Conditional_Void(bool) -> void | 0 | 5 | 0 | @r0_5(glval:bool) | +| Conditional_Void(bool) -> void | 0 | 6 | 0 | @r0_6(bool) | +| Conditional_Void(bool) -> void | 2 | 0 | 0 | @r2_0(bool) | +| Conditional_Void(bool) -> void | 3 | 0 | 0 | @r3_0(bool) | +| Constants() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Constants() -> void | 0 | 2 | 0 | @r0_2(glval:char) | +| Constants() -> void | 0 | 3 | 0 | @r0_3(char) | +| Constants() -> void | 0 | 4 | 0 | @mu0_4(char) | +| Constants() -> void | 0 | 5 | 0 | @r0_5(glval:char) | +| Constants() -> void | 0 | 6 | 0 | @r0_6(char) | +| Constants() -> void | 0 | 7 | 0 | @mu0_7(char) | +| Constants() -> void | 0 | 8 | 0 | @r0_8(glval:signed char) | +| Constants() -> void | 0 | 9 | 0 | @r0_9(signed char) | +| Constants() -> void | 0 | 10 | 0 | @mu0_10(signed char) | +| Constants() -> void | 0 | 11 | 0 | @r0_11(glval:signed char) | +| Constants() -> void | 0 | 12 | 0 | @r0_12(signed char) | +| Constants() -> void | 0 | 13 | 0 | @mu0_13(signed char) | +| Constants() -> void | 0 | 14 | 0 | @r0_14(glval:unsigned char) | +| Constants() -> void | 0 | 15 | 0 | @r0_15(unsigned char) | +| Constants() -> void | 0 | 16 | 0 | @mu0_16(unsigned char) | +| Constants() -> void | 0 | 17 | 0 | @r0_17(glval:unsigned char) | +| Constants() -> void | 0 | 18 | 0 | @r0_18(unsigned char) | +| Constants() -> void | 0 | 19 | 0 | @mu0_19(unsigned char) | +| Constants() -> void | 0 | 20 | 0 | @r0_20(glval:short) | +| Constants() -> void | 0 | 21 | 0 | @r0_21(short) | +| Constants() -> void | 0 | 22 | 0 | @mu0_22(short) | +| Constants() -> void | 0 | 23 | 0 | @r0_23(glval:unsigned short) | +| Constants() -> void | 0 | 24 | 0 | @r0_24(unsigned short) | +| Constants() -> void | 0 | 25 | 0 | @mu0_25(unsigned short) | +| Constants() -> void | 0 | 26 | 0 | @r0_26(glval:int) | +| Constants() -> void | 0 | 27 | 0 | @r0_27(int) | +| Constants() -> void | 0 | 28 | 0 | @mu0_28(int) | +| Constants() -> void | 0 | 29 | 0 | @r0_29(glval:unsigned int) | +| Constants() -> void | 0 | 30 | 0 | @r0_30(unsigned int) | +| Constants() -> void | 0 | 31 | 0 | @mu0_31(unsigned int) | +| Constants() -> void | 0 | 32 | 0 | @r0_32(glval:long) | +| Constants() -> void | 0 | 33 | 0 | @r0_33(long) | +| Constants() -> void | 0 | 34 | 0 | @mu0_34(long) | +| Constants() -> void | 0 | 35 | 0 | @r0_35(glval:unsigned long) | +| Constants() -> void | 0 | 36 | 0 | @r0_36(unsigned long) | +| Constants() -> void | 0 | 37 | 0 | @mu0_37(unsigned long) | +| Constants() -> void | 0 | 38 | 0 | @r0_38(glval:long long) | +| Constants() -> void | 0 | 39 | 0 | @r0_39(long long) | +| Constants() -> void | 0 | 40 | 0 | @mu0_40(long long) | +| Constants() -> void | 0 | 41 | 0 | @r0_41(glval:long long) | +| Constants() -> void | 0 | 42 | 0 | @r0_42(long long) | +| Constants() -> void | 0 | 43 | 0 | @mu0_43(long long) | +| Constants() -> void | 0 | 44 | 0 | @r0_44(glval:unsigned long long) | +| Constants() -> void | 0 | 45 | 0 | @r0_45(unsigned long long) | +| Constants() -> void | 0 | 46 | 0 | @mu0_46(unsigned long long) | +| Constants() -> void | 0 | 47 | 0 | @r0_47(glval:unsigned long long) | +| Constants() -> void | 0 | 48 | 0 | @r0_48(unsigned long long) | +| Constants() -> void | 0 | 49 | 0 | @mu0_49(unsigned long long) | +| Constants() -> void | 0 | 50 | 0 | @r0_50(glval:bool) | +| Constants() -> void | 0 | 51 | 0 | @r0_51(bool) | +| Constants() -> void | 0 | 52 | 0 | @mu0_52(bool) | +| Constants() -> void | 0 | 53 | 0 | @r0_53(glval:bool) | +| Constants() -> void | 0 | 54 | 0 | @r0_54(bool) | +| Constants() -> void | 0 | 55 | 0 | @mu0_55(bool) | +| Constants() -> void | 0 | 56 | 0 | @r0_56(glval:wchar_t) | +| Constants() -> void | 0 | 57 | 0 | @r0_57(wchar_t) | +| Constants() -> void | 0 | 58 | 0 | @mu0_58(wchar_t) | +| Constants() -> void | 0 | 59 | 0 | @r0_59(glval:wchar_t) | +| Constants() -> void | 0 | 60 | 0 | @r0_60(wchar_t) | +| Constants() -> void | 0 | 61 | 0 | @mu0_61(wchar_t) | +| Constants() -> void | 0 | 62 | 0 | @r0_62(glval:char16_t) | +| Constants() -> void | 0 | 63 | 0 | @r0_63(char16_t) | +| Constants() -> void | 0 | 64 | 0 | @mu0_64(char16_t) | +| Constants() -> void | 0 | 65 | 0 | @r0_65(glval:char32_t) | +| Constants() -> void | 0 | 66 | 0 | @r0_66(char32_t) | +| Constants() -> void | 0 | 67 | 0 | @mu0_67(char32_t) | +| Constants() -> void | 0 | 68 | 0 | @r0_68(glval:float) | +| Constants() -> void | 0 | 69 | 0 | @r0_69(float) | +| Constants() -> void | 0 | 70 | 0 | @mu0_70(float) | +| Constants() -> void | 0 | 71 | 0 | @r0_71(glval:float) | +| Constants() -> void | 0 | 72 | 0 | @r0_72(float) | +| Constants() -> void | 0 | 73 | 0 | @mu0_73(float) | +| Constants() -> void | 0 | 74 | 0 | @r0_74(glval:float) | +| Constants() -> void | 0 | 75 | 0 | @r0_75(float) | +| Constants() -> void | 0 | 76 | 0 | @mu0_76(float) | +| Constants() -> void | 0 | 77 | 0 | @r0_77(glval:double) | +| Constants() -> void | 0 | 78 | 0 | @r0_78(double) | +| Constants() -> void | 0 | 79 | 0 | @mu0_79(double) | +| Constants() -> void | 0 | 80 | 0 | @r0_80(glval:double) | +| Constants() -> void | 0 | 81 | 0 | @r0_81(double) | +| Constants() -> void | 0 | 82 | 0 | @mu0_82(double) | +| Constants() -> void | 0 | 83 | 0 | @r0_83(glval:double) | +| Constants() -> void | 0 | 84 | 0 | @r0_84(double) | +| Constants() -> void | 0 | 85 | 0 | @mu0_85(double) | +| Continue(int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Continue(int) -> void | 0 | 2 | 0 | @r0_2(int) | +| Continue(int) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| Continue(int) -> void | 0 | 4 | 0 | @mu0_4(int) | +| Continue(int) -> void | 1 | 0 | 0 | @r1_0(glval:int) | +| Continue(int) -> void | 1 | 1 | 0 | @r1_1(int) | +| Continue(int) -> void | 1 | 2 | 0 | @r1_2(int) | +| Continue(int) -> void | 1 | 3 | 0 | @r1_3(bool) | +| Continue(int) -> void | 3 | 0 | 0 | @r3_0(int) | +| Continue(int) -> void | 3 | 1 | 0 | @r3_1(glval:int) | +| Continue(int) -> void | 3 | 2 | 0 | @r3_2(int) | +| Continue(int) -> void | 3 | 3 | 0 | @r3_3(int) | +| Continue(int) -> void | 3 | 4 | 0 | @mu3_4(int) | +| Continue(int) -> void | 4 | 1 | 0 | @r4_1(glval:int) | +| Continue(int) -> void | 4 | 2 | 0 | @r4_2(int) | +| Continue(int) -> void | 4 | 3 | 0 | @r4_3(int) | +| Continue(int) -> void | 4 | 4 | 0 | @r4_4(bool) | +| DeclareObject() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| DeclareObject() -> void | 0 | 2 | 0 | @r0_2(glval:String) | +| DeclareObject() -> void | 0 | 3 | 0 | @r0_3(bool) | +| DeclareObject() -> void | 0 | 5 | 0 | @r0_5(glval:String) | +| DeclareObject() -> void | 0 | 6 | 0 | @r0_6(bool) | +| DeclareObject() -> void | 0 | 7 | 0 | @r0_7(glval:char[6]) | +| DeclareObject() -> void | 0 | 8 | 0 | @r0_8(char *) | +| DeclareObject() -> void | 0 | 10 | 0 | @r0_10(glval:String) | +| DeclareObject() -> void | 0 | 11 | 0 | @r0_11(bool) | +| DeclareObject() -> void | 0 | 12 | 0 | @r0_12(String) | +| DeclareObject() -> void | 0 | 13 | 0 | @mu0_13(String) | +| DeclareObject() -> void | 0 | 14 | 0 | @r0_14(glval:String) | +| DeclareObject() -> void | 0 | 15 | 0 | @r0_15(bool) | +| DeclareObject() -> void | 0 | 16 | 0 | @r0_16(glval:char[5]) | +| DeclareObject() -> void | 0 | 17 | 0 | @r0_17(char *) | +| DerefReference(int &) -> int | 0 | 1 | 0 | @mu0_1(unknown) | +| DerefReference(int &) -> int | 0 | 2 | 0 | @r0_2(int &) | +| DerefReference(int &) -> int | 0 | 3 | 0 | @r0_3(glval:int &) | +| DerefReference(int &) -> int | 0 | 4 | 0 | @mu0_4(int &) | +| DerefReference(int &) -> int | 0 | 5 | 0 | @r0_5(glval:int) | +| DerefReference(int &) -> int | 0 | 6 | 0 | @r0_6(glval:int &) | +| DerefReference(int &) -> int | 0 | 7 | 0 | @r0_7(int &) | +| DerefReference(int &) -> int | 0 | 8 | 0 | @r0_8(int) | +| DerefReference(int &) -> int | 0 | 9 | 0 | @mu0_9(int) | +| DerefReference(int &) -> int | 0 | 10 | 0 | @r0_10(glval:int) | +| Dereference(int *) -> int | 0 | 1 | 0 | @mu0_1(unknown) | +| Dereference(int *) -> int | 0 | 2 | 0 | @r0_2(int *) | +| Dereference(int *) -> int | 0 | 3 | 0 | @r0_3(glval:int *) | +| Dereference(int *) -> int | 0 | 4 | 0 | @mu0_4(int *) | +| Dereference(int *) -> int | 0 | 5 | 0 | @r0_5(int) | +| Dereference(int *) -> int | 0 | 6 | 0 | @r0_6(glval:int *) | +| Dereference(int *) -> int | 0 | 7 | 0 | @r0_7(int *) | +| Dereference(int *) -> int | 0 | 8 | 0 | @mu0_8(int) | +| Dereference(int *) -> int | 0 | 9 | 0 | @r0_9(glval:int) | +| Dereference(int *) -> int | 0 | 10 | 0 | @r0_10(glval:int *) | +| Dereference(int *) -> int | 0 | 11 | 0 | @r0_11(int *) | +| Dereference(int *) -> int | 0 | 12 | 0 | @r0_12(int) | +| Dereference(int *) -> int | 0 | 13 | 0 | @mu0_13(int) | +| Dereference(int *) -> int | 0 | 14 | 0 | @r0_14(glval:int) | +| Derived::Derived() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Derived::Derived() -> void | 0 | 2 | 0 | @r0_2(glval:Derived) | +| Derived::Derived() -> void | 0 | 3 | 0 | @r0_3(glval:Middle) | +| Derived::Derived() -> void | 0 | 4 | 0 | @r0_4(bool) | +| Derived::Derived() -> void | 0 | 6 | 0 | @r0_6(glval:String) | +| Derived::Derived() -> void | 0 | 7 | 0 | @r0_7(bool) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 1 | 0 | @mu0_1(unknown) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 2 | 0 | @r0_2(glval:Derived) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 3 | 0 | @r0_3(Derived &) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 4 | 0 | @r0_4(glval:Derived &) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 5 | 0 | @mu0_5(Derived &) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 6 | 0 | @r0_6(Derived *) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 7 | 0 | @r0_7(Middle *) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 8 | 0 | @r0_8(bool) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 9 | 0 | @r0_9(glval:Derived &) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 10 | 0 | @r0_10(Derived &) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 11 | 0 | @r0_11(Middle *) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 12 | 0 | @r0_12(Middle &) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 13 | 0 | @r0_13(Derived *) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 14 | 0 | @r0_14(glval:String) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 15 | 0 | @r0_15(bool) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 16 | 0 | @r0_16(glval:Derived &) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 17 | 0 | @r0_17(Derived &) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 18 | 0 | @r0_18(glval:String) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 19 | 0 | @r0_19(String &) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 20 | 0 | @r0_20(glval:Derived &) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 21 | 0 | @r0_21(Derived *) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 22 | 0 | @mu0_22(Derived &) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 23 | 0 | @r0_23(glval:Derived &) | +| Derived::~Derived() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Derived::~Derived() -> void | 0 | 2 | 0 | @r0_2(glval:Derived) | +| Derived::~Derived() -> void | 0 | 4 | 0 | @r0_4(glval:String) | +| Derived::~Derived() -> void | 0 | 5 | 0 | @r0_5(bool) | +| Derived::~Derived() -> void | 0 | 7 | 0 | @r0_7(glval:Middle) | +| Derived::~Derived() -> void | 0 | 8 | 0 | @r0_8(bool) | +| DerivedVB::DerivedVB() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| DerivedVB::DerivedVB() -> void | 0 | 2 | 0 | @r0_2(glval:DerivedVB) | +| DerivedVB::DerivedVB() -> void | 0 | 3 | 0 | @r0_3(glval:Base) | +| DerivedVB::DerivedVB() -> void | 0 | 4 | 0 | @r0_4(bool) | +| DerivedVB::DerivedVB() -> void | 0 | 6 | 0 | @r0_6(glval:MiddleVB1) | +| DerivedVB::DerivedVB() -> void | 0 | 7 | 0 | @r0_7(bool) | +| DerivedVB::DerivedVB() -> void | 0 | 9 | 0 | @r0_9(glval:MiddleVB2) | +| DerivedVB::DerivedVB() -> void | 0 | 10 | 0 | @r0_10(bool) | +| DerivedVB::DerivedVB() -> void | 0 | 12 | 0 | @r0_12(glval:String) | +| DerivedVB::DerivedVB() -> void | 0 | 13 | 0 | @r0_13(bool) | +| DerivedVB::~DerivedVB() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| DerivedVB::~DerivedVB() -> void | 0 | 2 | 0 | @r0_2(glval:DerivedVB) | +| DerivedVB::~DerivedVB() -> void | 0 | 4 | 0 | @r0_4(glval:String) | +| DerivedVB::~DerivedVB() -> void | 0 | 5 | 0 | @r0_5(bool) | +| DerivedVB::~DerivedVB() -> void | 0 | 7 | 0 | @r0_7(glval:MiddleVB2) | +| DerivedVB::~DerivedVB() -> void | 0 | 8 | 0 | @r0_8(bool) | +| DerivedVB::~DerivedVB() -> void | 0 | 10 | 0 | @r0_10(glval:MiddleVB1) | +| DerivedVB::~DerivedVB() -> void | 0 | 11 | 0 | @r0_11(bool) | +| DerivedVB::~DerivedVB() -> void | 0 | 13 | 0 | @r0_13(glval:Base) | +| DerivedVB::~DerivedVB() -> void | 0 | 14 | 0 | @r0_14(bool) | +| DoStatements(int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| DoStatements(int) -> void | 0 | 2 | 0 | @r0_2(int) | +| DoStatements(int) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| DoStatements(int) -> void | 0 | 4 | 0 | @mu0_4(int) | +| DoStatements(int) -> void | 1 | 0 | 0 | @r1_0(int) | +| DoStatements(int) -> void | 1 | 1 | 0 | @r1_1(glval:int) | +| DoStatements(int) -> void | 1 | 2 | 0 | @r1_2(int) | +| DoStatements(int) -> void | 1 | 3 | 0 | @r1_3(int) | +| DoStatements(int) -> void | 1 | 4 | 0 | @mu1_4(int) | +| DoStatements(int) -> void | 1 | 5 | 0 | @r1_5(glval:int) | +| DoStatements(int) -> void | 1 | 6 | 0 | @r1_6(int) | +| DoStatements(int) -> void | 1 | 7 | 0 | @r1_7(int) | +| DoStatements(int) -> void | 1 | 8 | 0 | @r1_8(bool) | +| DynamicCast() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| DynamicCast() -> void | 0 | 2 | 0 | @r0_2(glval:PolymorphicBase) | +| DynamicCast() -> void | 0 | 3 | 0 | @r0_3(bool) | +| DynamicCast() -> void | 0 | 5 | 0 | @r0_5(glval:PolymorphicDerived) | +| DynamicCast() -> void | 0 | 6 | 0 | @r0_6(bool) | +| DynamicCast() -> void | 0 | 8 | 0 | @r0_8(glval:PolymorphicBase *) | +| DynamicCast() -> void | 0 | 9 | 0 | @r0_9(glval:PolymorphicBase) | +| DynamicCast() -> void | 0 | 10 | 0 | @mu0_10(PolymorphicBase *) | +| DynamicCast() -> void | 0 | 11 | 0 | @r0_11(glval:PolymorphicDerived *) | +| DynamicCast() -> void | 0 | 12 | 0 | @r0_12(glval:PolymorphicDerived) | +| DynamicCast() -> void | 0 | 13 | 0 | @mu0_13(PolymorphicDerived *) | +| DynamicCast() -> void | 0 | 14 | 0 | @r0_14(glval:PolymorphicDerived *) | +| DynamicCast() -> void | 0 | 15 | 0 | @r0_15(PolymorphicDerived *) | +| DynamicCast() -> void | 0 | 16 | 0 | @r0_16(PolymorphicBase *) | +| DynamicCast() -> void | 0 | 17 | 0 | @r0_17(glval:PolymorphicBase *) | +| DynamicCast() -> void | 0 | 18 | 0 | @mu0_18(PolymorphicBase *) | +| DynamicCast() -> void | 0 | 19 | 0 | @r0_19(glval:PolymorphicBase &) | +| DynamicCast() -> void | 0 | 20 | 0 | @r0_20(glval:PolymorphicDerived) | +| DynamicCast() -> void | 0 | 21 | 0 | @r0_21(glval:PolymorphicBase) | +| DynamicCast() -> void | 0 | 22 | 0 | @mu0_22(PolymorphicBase &) | +| DynamicCast() -> void | 0 | 23 | 0 | @r0_23(glval:PolymorphicBase *) | +| DynamicCast() -> void | 0 | 24 | 0 | @r0_24(PolymorphicBase *) | +| DynamicCast() -> void | 0 | 25 | 0 | @r0_25(PolymorphicDerived *) | +| DynamicCast() -> void | 0 | 26 | 0 | @r0_26(glval:PolymorphicDerived *) | +| DynamicCast() -> void | 0 | 27 | 0 | @mu0_27(PolymorphicDerived *) | +| DynamicCast() -> void | 0 | 28 | 0 | @r0_28(glval:PolymorphicDerived &) | +| DynamicCast() -> void | 0 | 29 | 0 | @r0_29(glval:PolymorphicBase) | +| DynamicCast() -> void | 0 | 30 | 0 | @r0_30(glval:PolymorphicDerived) | +| DynamicCast() -> void | 0 | 31 | 0 | @mu0_31(PolymorphicDerived &) | +| DynamicCast() -> void | 0 | 32 | 0 | @r0_32(glval:void *) | +| DynamicCast() -> void | 0 | 33 | 0 | @r0_33(glval:PolymorphicBase *) | +| DynamicCast() -> void | 0 | 34 | 0 | @r0_34(PolymorphicBase *) | +| DynamicCast() -> void | 0 | 35 | 0 | @r0_35(void *) | +| DynamicCast() -> void | 0 | 36 | 0 | @mu0_36(void *) | +| DynamicCast() -> void | 0 | 37 | 0 | @r0_37(glval:void *) | +| DynamicCast() -> void | 0 | 38 | 0 | @r0_38(glval:PolymorphicDerived *) | +| DynamicCast() -> void | 0 | 39 | 0 | @r0_39(PolymorphicDerived *) | +| DynamicCast() -> void | 0 | 40 | 0 | @r0_40(void *) | +| DynamicCast() -> void | 0 | 41 | 0 | @mu0_41(void *) | +| EarlyReturn(int, int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| EarlyReturn(int, int) -> void | 0 | 2 | 0 | @r0_2(int) | +| EarlyReturn(int, int) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| EarlyReturn(int, int) -> void | 0 | 4 | 0 | @mu0_4(int) | +| EarlyReturn(int, int) -> void | 0 | 5 | 0 | @r0_5(int) | +| EarlyReturn(int, int) -> void | 0 | 6 | 0 | @r0_6(glval:int) | +| EarlyReturn(int, int) -> void | 0 | 7 | 0 | @mu0_7(int) | +| EarlyReturn(int, int) -> void | 0 | 8 | 0 | @r0_8(glval:int) | +| EarlyReturn(int, int) -> void | 0 | 9 | 0 | @r0_9(int) | +| EarlyReturn(int, int) -> void | 0 | 10 | 0 | @r0_10(glval:int) | +| EarlyReturn(int, int) -> void | 0 | 11 | 0 | @r0_11(int) | +| EarlyReturn(int, int) -> void | 0 | 12 | 0 | @r0_12(bool) | +| EarlyReturn(int, int) -> void | 3 | 0 | 0 | @r3_0(glval:int) | +| EarlyReturn(int, int) -> void | 3 | 1 | 0 | @r3_1(int) | +| EarlyReturn(int, int) -> void | 3 | 2 | 0 | @r3_2(glval:int) | +| EarlyReturn(int, int) -> void | 3 | 3 | 0 | @mu3_3(int) | +| EarlyReturnValue(int, int) -> int | 0 | 1 | 0 | @mu0_1(unknown) | +| EarlyReturnValue(int, int) -> int | 0 | 2 | 0 | @r0_2(int) | +| EarlyReturnValue(int, int) -> int | 0 | 3 | 0 | @r0_3(glval:int) | +| EarlyReturnValue(int, int) -> int | 0 | 4 | 0 | @mu0_4(int) | +| EarlyReturnValue(int, int) -> int | 0 | 5 | 0 | @r0_5(int) | +| EarlyReturnValue(int, int) -> int | 0 | 6 | 0 | @r0_6(glval:int) | +| EarlyReturnValue(int, int) -> int | 0 | 7 | 0 | @mu0_7(int) | +| EarlyReturnValue(int, int) -> int | 0 | 8 | 0 | @r0_8(glval:int) | +| EarlyReturnValue(int, int) -> int | 0 | 9 | 0 | @r0_9(int) | +| EarlyReturnValue(int, int) -> int | 0 | 10 | 0 | @r0_10(glval:int) | +| EarlyReturnValue(int, int) -> int | 0 | 11 | 0 | @r0_11(int) | +| EarlyReturnValue(int, int) -> int | 0 | 12 | 0 | @r0_12(bool) | +| EarlyReturnValue(int, int) -> int | 1 | 0 | 0 | @r1_0(glval:int) | +| EarlyReturnValue(int, int) -> int | 2 | 0 | 0 | @r2_0(glval:int) | +| EarlyReturnValue(int, int) -> int | 2 | 1 | 0 | @r2_1(glval:int) | +| EarlyReturnValue(int, int) -> int | 2 | 2 | 0 | @r2_2(int) | +| EarlyReturnValue(int, int) -> int | 2 | 3 | 0 | @mu2_3(int) | +| EarlyReturnValue(int, int) -> int | 3 | 0 | 0 | @r3_0(glval:int) | +| EarlyReturnValue(int, int) -> int | 3 | 1 | 0 | @r3_1(glval:int) | +| EarlyReturnValue(int, int) -> int | 3 | 2 | 0 | @r3_2(int) | +| EarlyReturnValue(int, int) -> int | 3 | 3 | 0 | @r3_3(glval:int) | +| EarlyReturnValue(int, int) -> int | 3 | 4 | 0 | @r3_4(int) | +| EarlyReturnValue(int, int) -> int | 3 | 5 | 0 | @r3_5(int) | +| EarlyReturnValue(int, int) -> int | 3 | 6 | 0 | @mu3_6(int) | +| EnumSwitch(E) -> int | 0 | 1 | 0 | @mu0_1(unknown) | +| EnumSwitch(E) -> int | 0 | 2 | 0 | @r0_2(E) | +| EnumSwitch(E) -> int | 0 | 3 | 0 | @r0_3(glval:E) | +| EnumSwitch(E) -> int | 0 | 4 | 0 | @mu0_4(E) | +| EnumSwitch(E) -> int | 0 | 5 | 0 | @r0_5(glval:E) | +| EnumSwitch(E) -> int | 0 | 6 | 0 | @r0_6(E) | +| EnumSwitch(E) -> int | 0 | 7 | 0 | @r0_7(int) | +| EnumSwitch(E) -> int | 1 | 0 | 0 | @r1_0(glval:int) | +| EnumSwitch(E) -> int | 2 | 1 | 0 | @r2_1(glval:int) | +| EnumSwitch(E) -> int | 2 | 2 | 0 | @r2_2(int) | +| EnumSwitch(E) -> int | 2 | 3 | 0 | @mu2_3(int) | +| EnumSwitch(E) -> int | 3 | 1 | 0 | @r3_1(glval:int) | +| EnumSwitch(E) -> int | 3 | 2 | 0 | @r3_2(int) | +| EnumSwitch(E) -> int | 3 | 3 | 0 | @mu3_3(int) | +| EnumSwitch(E) -> int | 4 | 1 | 0 | @r4_1(glval:int) | +| EnumSwitch(E) -> int | 4 | 2 | 0 | @r4_2(int) | +| EnumSwitch(E) -> int | 4 | 3 | 0 | @mu4_3(int) | +| FieldAccess() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| FieldAccess() -> void | 0 | 2 | 0 | @r0_2(glval:Point) | +| FieldAccess() -> void | 0 | 3 | 0 | @r0_3(Point) | +| FieldAccess() -> void | 0 | 4 | 0 | @mu0_4(Point) | +| FieldAccess() -> void | 0 | 5 | 0 | @r0_5(int) | +| FieldAccess() -> void | 0 | 6 | 0 | @r0_6(glval:Point) | +| FieldAccess() -> void | 0 | 7 | 0 | @r0_7(glval:int) | +| FieldAccess() -> void | 0 | 8 | 0 | @mu0_8(int) | +| FieldAccess() -> void | 0 | 9 | 0 | @r0_9(glval:Point) | +| FieldAccess() -> void | 0 | 10 | 0 | @r0_10(glval:int) | +| FieldAccess() -> void | 0 | 11 | 0 | @r0_11(int) | +| FieldAccess() -> void | 0 | 12 | 0 | @r0_12(glval:Point) | +| FieldAccess() -> void | 0 | 13 | 0 | @r0_13(glval:int) | +| FieldAccess() -> void | 0 | 14 | 0 | @mu0_14(int) | +| FieldAccess() -> void | 0 | 15 | 0 | @r0_15(glval:int *) | +| FieldAccess() -> void | 0 | 16 | 0 | @r0_16(glval:Point) | +| FieldAccess() -> void | 0 | 17 | 0 | @r0_17(glval:int) | +| FieldAccess() -> void | 0 | 18 | 0 | @mu0_18(int *) | +| FloatCompare(double, double) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| FloatCompare(double, double) -> void | 0 | 2 | 0 | @r0_2(double) | +| FloatCompare(double, double) -> void | 0 | 3 | 0 | @r0_3(glval:double) | +| FloatCompare(double, double) -> void | 0 | 4 | 0 | @mu0_4(double) | +| FloatCompare(double, double) -> void | 0 | 5 | 0 | @r0_5(double) | +| FloatCompare(double, double) -> void | 0 | 6 | 0 | @r0_6(glval:double) | +| FloatCompare(double, double) -> void | 0 | 7 | 0 | @mu0_7(double) | +| FloatCompare(double, double) -> void | 0 | 8 | 0 | @r0_8(glval:bool) | +| FloatCompare(double, double) -> void | 0 | 9 | 0 | @r0_9(bool) | +| FloatCompare(double, double) -> void | 0 | 10 | 0 | @mu0_10(bool) | +| FloatCompare(double, double) -> void | 0 | 11 | 0 | @r0_11(glval:double) | +| FloatCompare(double, double) -> void | 0 | 12 | 0 | @r0_12(double) | +| FloatCompare(double, double) -> void | 0 | 13 | 0 | @r0_13(glval:double) | +| FloatCompare(double, double) -> void | 0 | 14 | 0 | @r0_14(double) | +| FloatCompare(double, double) -> void | 0 | 15 | 0 | @r0_15(bool) | +| FloatCompare(double, double) -> void | 0 | 16 | 0 | @r0_16(glval:bool) | +| FloatCompare(double, double) -> void | 0 | 17 | 0 | @mu0_17(bool) | +| FloatCompare(double, double) -> void | 0 | 18 | 0 | @r0_18(glval:double) | +| FloatCompare(double, double) -> void | 0 | 19 | 0 | @r0_19(double) | +| FloatCompare(double, double) -> void | 0 | 20 | 0 | @r0_20(glval:double) | +| FloatCompare(double, double) -> void | 0 | 21 | 0 | @r0_21(double) | +| FloatCompare(double, double) -> void | 0 | 22 | 0 | @r0_22(bool) | +| FloatCompare(double, double) -> void | 0 | 23 | 0 | @r0_23(glval:bool) | +| FloatCompare(double, double) -> void | 0 | 24 | 0 | @mu0_24(bool) | +| FloatCompare(double, double) -> void | 0 | 25 | 0 | @r0_25(glval:double) | +| FloatCompare(double, double) -> void | 0 | 26 | 0 | @r0_26(double) | +| FloatCompare(double, double) -> void | 0 | 27 | 0 | @r0_27(glval:double) | +| FloatCompare(double, double) -> void | 0 | 28 | 0 | @r0_28(double) | +| FloatCompare(double, double) -> void | 0 | 29 | 0 | @r0_29(bool) | +| FloatCompare(double, double) -> void | 0 | 30 | 0 | @r0_30(glval:bool) | +| FloatCompare(double, double) -> void | 0 | 31 | 0 | @mu0_31(bool) | +| FloatCompare(double, double) -> void | 0 | 32 | 0 | @r0_32(glval:double) | +| FloatCompare(double, double) -> void | 0 | 33 | 0 | @r0_33(double) | +| FloatCompare(double, double) -> void | 0 | 34 | 0 | @r0_34(glval:double) | +| FloatCompare(double, double) -> void | 0 | 35 | 0 | @r0_35(double) | +| FloatCompare(double, double) -> void | 0 | 36 | 0 | @r0_36(bool) | +| FloatCompare(double, double) -> void | 0 | 37 | 0 | @r0_37(glval:bool) | +| FloatCompare(double, double) -> void | 0 | 38 | 0 | @mu0_38(bool) | +| FloatCompare(double, double) -> void | 0 | 39 | 0 | @r0_39(glval:double) | +| FloatCompare(double, double) -> void | 0 | 40 | 0 | @r0_40(double) | +| FloatCompare(double, double) -> void | 0 | 41 | 0 | @r0_41(glval:double) | +| FloatCompare(double, double) -> void | 0 | 42 | 0 | @r0_42(double) | +| FloatCompare(double, double) -> void | 0 | 43 | 0 | @r0_43(bool) | +| FloatCompare(double, double) -> void | 0 | 44 | 0 | @r0_44(glval:bool) | +| FloatCompare(double, double) -> void | 0 | 45 | 0 | @mu0_45(bool) | +| FloatCompare(double, double) -> void | 0 | 46 | 0 | @r0_46(glval:double) | +| FloatCompare(double, double) -> void | 0 | 47 | 0 | @r0_47(double) | +| FloatCompare(double, double) -> void | 0 | 48 | 0 | @r0_48(glval:double) | +| FloatCompare(double, double) -> void | 0 | 49 | 0 | @r0_49(double) | +| FloatCompare(double, double) -> void | 0 | 50 | 0 | @r0_50(bool) | +| FloatCompare(double, double) -> void | 0 | 51 | 0 | @r0_51(glval:bool) | +| FloatCompare(double, double) -> void | 0 | 52 | 0 | @mu0_52(bool) | +| FloatCrement(float) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| FloatCrement(float) -> void | 0 | 2 | 0 | @r0_2(float) | +| FloatCrement(float) -> void | 0 | 3 | 0 | @r0_3(glval:float) | +| FloatCrement(float) -> void | 0 | 4 | 0 | @mu0_4(float) | +| FloatCrement(float) -> void | 0 | 5 | 0 | @r0_5(glval:float) | +| FloatCrement(float) -> void | 0 | 6 | 0 | @r0_6(float) | +| FloatCrement(float) -> void | 0 | 7 | 0 | @mu0_7(float) | +| FloatCrement(float) -> void | 0 | 8 | 0 | @r0_8(glval:float) | +| FloatCrement(float) -> void | 0 | 9 | 0 | @r0_9(float) | +| FloatCrement(float) -> void | 0 | 10 | 0 | @r0_10(float) | +| FloatCrement(float) -> void | 0 | 11 | 0 | @r0_11(float) | +| FloatCrement(float) -> void | 0 | 12 | 0 | @mu0_12(float) | +| FloatCrement(float) -> void | 0 | 13 | 0 | @r0_13(glval:float) | +| FloatCrement(float) -> void | 0 | 14 | 0 | @mu0_14(float) | +| FloatCrement(float) -> void | 0 | 15 | 0 | @r0_15(glval:float) | +| FloatCrement(float) -> void | 0 | 16 | 0 | @r0_16(float) | +| FloatCrement(float) -> void | 0 | 17 | 0 | @r0_17(float) | +| FloatCrement(float) -> void | 0 | 18 | 0 | @r0_18(float) | +| FloatCrement(float) -> void | 0 | 19 | 0 | @mu0_19(float) | +| FloatCrement(float) -> void | 0 | 20 | 0 | @r0_20(glval:float) | +| FloatCrement(float) -> void | 0 | 21 | 0 | @mu0_21(float) | +| FloatCrement(float) -> void | 0 | 22 | 0 | @r0_22(glval:float) | +| FloatCrement(float) -> void | 0 | 23 | 0 | @r0_23(float) | +| FloatCrement(float) -> void | 0 | 24 | 0 | @r0_24(float) | +| FloatCrement(float) -> void | 0 | 25 | 0 | @r0_25(float) | +| FloatCrement(float) -> void | 0 | 26 | 0 | @mu0_26(float) | +| FloatCrement(float) -> void | 0 | 27 | 0 | @r0_27(glval:float) | +| FloatCrement(float) -> void | 0 | 28 | 0 | @mu0_28(float) | +| FloatCrement(float) -> void | 0 | 29 | 0 | @r0_29(glval:float) | +| FloatCrement(float) -> void | 0 | 30 | 0 | @r0_30(float) | +| FloatCrement(float) -> void | 0 | 31 | 0 | @r0_31(float) | +| FloatCrement(float) -> void | 0 | 32 | 0 | @r0_32(float) | +| FloatCrement(float) -> void | 0 | 33 | 0 | @mu0_33(float) | +| FloatCrement(float) -> void | 0 | 34 | 0 | @r0_34(glval:float) | +| FloatCrement(float) -> void | 0 | 35 | 0 | @mu0_35(float) | +| FloatOps(double, double) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| FloatOps(double, double) -> void | 0 | 2 | 0 | @r0_2(double) | +| FloatOps(double, double) -> void | 0 | 3 | 0 | @r0_3(glval:double) | +| FloatOps(double, double) -> void | 0 | 4 | 0 | @mu0_4(double) | +| FloatOps(double, double) -> void | 0 | 5 | 0 | @r0_5(double) | +| FloatOps(double, double) -> void | 0 | 6 | 0 | @r0_6(glval:double) | +| FloatOps(double, double) -> void | 0 | 7 | 0 | @mu0_7(double) | +| FloatOps(double, double) -> void | 0 | 8 | 0 | @r0_8(glval:double) | +| FloatOps(double, double) -> void | 0 | 9 | 0 | @r0_9(double) | +| FloatOps(double, double) -> void | 0 | 10 | 0 | @mu0_10(double) | +| FloatOps(double, double) -> void | 0 | 11 | 0 | @r0_11(glval:double) | +| FloatOps(double, double) -> void | 0 | 12 | 0 | @r0_12(double) | +| FloatOps(double, double) -> void | 0 | 13 | 0 | @r0_13(glval:double) | +| FloatOps(double, double) -> void | 0 | 14 | 0 | @r0_14(double) | +| FloatOps(double, double) -> void | 0 | 15 | 0 | @r0_15(double) | +| FloatOps(double, double) -> void | 0 | 16 | 0 | @r0_16(glval:double) | +| FloatOps(double, double) -> void | 0 | 17 | 0 | @mu0_17(double) | +| FloatOps(double, double) -> void | 0 | 18 | 0 | @r0_18(glval:double) | +| FloatOps(double, double) -> void | 0 | 19 | 0 | @r0_19(double) | +| FloatOps(double, double) -> void | 0 | 20 | 0 | @r0_20(glval:double) | +| FloatOps(double, double) -> void | 0 | 21 | 0 | @r0_21(double) | +| FloatOps(double, double) -> void | 0 | 22 | 0 | @r0_22(double) | +| FloatOps(double, double) -> void | 0 | 23 | 0 | @r0_23(glval:double) | +| FloatOps(double, double) -> void | 0 | 24 | 0 | @mu0_24(double) | +| FloatOps(double, double) -> void | 0 | 25 | 0 | @r0_25(glval:double) | +| FloatOps(double, double) -> void | 0 | 26 | 0 | @r0_26(double) | +| FloatOps(double, double) -> void | 0 | 27 | 0 | @r0_27(glval:double) | +| FloatOps(double, double) -> void | 0 | 28 | 0 | @r0_28(double) | +| FloatOps(double, double) -> void | 0 | 29 | 0 | @r0_29(double) | +| FloatOps(double, double) -> void | 0 | 30 | 0 | @r0_30(glval:double) | +| FloatOps(double, double) -> void | 0 | 31 | 0 | @mu0_31(double) | +| FloatOps(double, double) -> void | 0 | 32 | 0 | @r0_32(glval:double) | +| FloatOps(double, double) -> void | 0 | 33 | 0 | @r0_33(double) | +| FloatOps(double, double) -> void | 0 | 34 | 0 | @r0_34(glval:double) | +| FloatOps(double, double) -> void | 0 | 35 | 0 | @r0_35(double) | +| FloatOps(double, double) -> void | 0 | 36 | 0 | @r0_36(double) | +| FloatOps(double, double) -> void | 0 | 37 | 0 | @r0_37(glval:double) | +| FloatOps(double, double) -> void | 0 | 38 | 0 | @mu0_38(double) | +| FloatOps(double, double) -> void | 0 | 39 | 0 | @r0_39(glval:double) | +| FloatOps(double, double) -> void | 0 | 40 | 0 | @r0_40(double) | +| FloatOps(double, double) -> void | 0 | 41 | 0 | @r0_41(glval:double) | +| FloatOps(double, double) -> void | 0 | 42 | 0 | @mu0_42(double) | +| FloatOps(double, double) -> void | 0 | 43 | 0 | @r0_43(glval:double) | +| FloatOps(double, double) -> void | 0 | 44 | 0 | @r0_44(double) | +| FloatOps(double, double) -> void | 0 | 45 | 0 | @r0_45(glval:double) | +| FloatOps(double, double) -> void | 0 | 46 | 0 | @r0_46(double) | +| FloatOps(double, double) -> void | 0 | 47 | 0 | @r0_47(double) | +| FloatOps(double, double) -> void | 0 | 48 | 0 | @mu0_48(double) | +| FloatOps(double, double) -> void | 0 | 49 | 0 | @r0_49(glval:double) | +| FloatOps(double, double) -> void | 0 | 50 | 0 | @r0_50(double) | +| FloatOps(double, double) -> void | 0 | 51 | 0 | @r0_51(glval:double) | +| FloatOps(double, double) -> void | 0 | 52 | 0 | @r0_52(double) | +| FloatOps(double, double) -> void | 0 | 53 | 0 | @r0_53(double) | +| FloatOps(double, double) -> void | 0 | 54 | 0 | @mu0_54(double) | +| FloatOps(double, double) -> void | 0 | 55 | 0 | @r0_55(glval:double) | +| FloatOps(double, double) -> void | 0 | 56 | 0 | @r0_56(double) | +| FloatOps(double, double) -> void | 0 | 57 | 0 | @r0_57(glval:double) | +| FloatOps(double, double) -> void | 0 | 58 | 0 | @r0_58(double) | +| FloatOps(double, double) -> void | 0 | 59 | 0 | @r0_59(double) | +| FloatOps(double, double) -> void | 0 | 60 | 0 | @mu0_60(double) | +| FloatOps(double, double) -> void | 0 | 61 | 0 | @r0_61(glval:double) | +| FloatOps(double, double) -> void | 0 | 62 | 0 | @r0_62(double) | +| FloatOps(double, double) -> void | 0 | 63 | 0 | @r0_63(glval:double) | +| FloatOps(double, double) -> void | 0 | 64 | 0 | @r0_64(double) | +| FloatOps(double, double) -> void | 0 | 65 | 0 | @r0_65(double) | +| FloatOps(double, double) -> void | 0 | 66 | 0 | @mu0_66(double) | +| FloatOps(double, double) -> void | 0 | 67 | 0 | @r0_67(glval:double) | +| FloatOps(double, double) -> void | 0 | 68 | 0 | @r0_68(double) | +| FloatOps(double, double) -> void | 0 | 69 | 0 | @r0_69(double) | +| FloatOps(double, double) -> void | 0 | 70 | 0 | @r0_70(glval:double) | +| FloatOps(double, double) -> void | 0 | 71 | 0 | @mu0_71(double) | +| FloatOps(double, double) -> void | 0 | 72 | 0 | @r0_72(glval:double) | +| FloatOps(double, double) -> void | 0 | 73 | 0 | @r0_73(double) | +| FloatOps(double, double) -> void | 0 | 74 | 0 | @r0_74(double) | +| FloatOps(double, double) -> void | 0 | 75 | 0 | @r0_75(glval:double) | +| FloatOps(double, double) -> void | 0 | 76 | 0 | @mu0_76(double) | +| Foo() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Foo() -> void | 0 | 2 | 0 | @r0_2(glval:int) | +| Foo() -> void | 0 | 3 | 0 | @r0_3(int) | +| Foo() -> void | 0 | 4 | 0 | @mu0_4(int) | +| Foo() -> void | 0 | 5 | 0 | @r0_5(glval:short) | +| Foo() -> void | 0 | 6 | 0 | @r0_6(short) | +| Foo() -> void | 0 | 7 | 0 | @mu0_7(short) | +| Foo() -> void | 0 | 8 | 0 | @r0_8(glval:int) | +| Foo() -> void | 0 | 9 | 0 | @r0_9(int) | +| Foo() -> void | 0 | 10 | 0 | @r0_10(glval:short) | +| Foo() -> void | 0 | 11 | 0 | @r0_11(short) | +| Foo() -> void | 0 | 12 | 0 | @r0_12(int) | +| Foo() -> void | 0 | 13 | 0 | @r0_13(int) | +| Foo() -> void | 0 | 14 | 0 | @r0_14(short) | +| Foo() -> void | 0 | 15 | 0 | @r0_15(glval:short) | +| Foo() -> void | 0 | 16 | 0 | @mu0_16(short) | +| Foo() -> void | 0 | 17 | 0 | @r0_17(glval:int) | +| Foo() -> void | 0 | 18 | 0 | @r0_18(int) | +| Foo() -> void | 0 | 19 | 0 | @r0_19(glval:short) | +| Foo() -> void | 0 | 20 | 0 | @r0_20(short) | +| Foo() -> void | 0 | 21 | 0 | @r0_21(int) | +| Foo() -> void | 0 | 22 | 0 | @r0_22(int) | +| Foo() -> void | 0 | 23 | 0 | @r0_23(glval:int) | +| Foo() -> void | 0 | 24 | 0 | @mu0_24(int) | +| For_Break() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| For_Break() -> void | 0 | 2 | 0 | @r0_2(glval:int) | +| For_Break() -> void | 0 | 3 | 0 | @r0_3(int) | +| For_Break() -> void | 0 | 4 | 0 | @mu0_4(int) | +| For_Break() -> void | 1 | 0 | 0 | @r1_0(glval:int) | +| For_Break() -> void | 1 | 1 | 0 | @r1_1(int) | +| For_Break() -> void | 1 | 2 | 0 | @r1_2(int) | +| For_Break() -> void | 1 | 3 | 0 | @r1_3(bool) | +| For_Break() -> void | 2 | 0 | 0 | @r2_0(int) | +| For_Break() -> void | 2 | 1 | 0 | @r2_1(glval:int) | +| For_Break() -> void | 2 | 2 | 0 | @r2_2(int) | +| For_Break() -> void | 2 | 3 | 0 | @r2_3(int) | +| For_Break() -> void | 2 | 4 | 0 | @mu2_4(int) | +| For_Break() -> void | 3 | 0 | 0 | @r3_0(glval:int) | +| For_Break() -> void | 3 | 1 | 0 | @r3_1(int) | +| For_Break() -> void | 3 | 2 | 0 | @r3_2(int) | +| For_Break() -> void | 3 | 3 | 0 | @r3_3(bool) | +| For_Condition() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| For_Condition() -> void | 0 | 2 | 0 | @r0_2(glval:int) | +| For_Condition() -> void | 0 | 3 | 0 | @r0_3(int) | +| For_Condition() -> void | 0 | 4 | 0 | @mu0_4(int) | +| For_Condition() -> void | 1 | 0 | 0 | @r1_0(glval:int) | +| For_Condition() -> void | 1 | 1 | 0 | @r1_1(int) | +| For_Condition() -> void | 1 | 2 | 0 | @r1_2(int) | +| For_Condition() -> void | 1 | 3 | 0 | @r1_3(bool) | +| For_ConditionUpdate() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| For_ConditionUpdate() -> void | 0 | 2 | 0 | @r0_2(glval:int) | +| For_ConditionUpdate() -> void | 0 | 3 | 0 | @r0_3(int) | +| For_ConditionUpdate() -> void | 0 | 4 | 0 | @mu0_4(int) | +| For_ConditionUpdate() -> void | 1 | 0 | 0 | @r1_0(glval:int) | +| For_ConditionUpdate() -> void | 1 | 1 | 0 | @r1_1(int) | +| For_ConditionUpdate() -> void | 1 | 2 | 0 | @r1_2(int) | +| For_ConditionUpdate() -> void | 1 | 3 | 0 | @r1_3(bool) | +| For_ConditionUpdate() -> void | 2 | 1 | 0 | @r2_1(int) | +| For_ConditionUpdate() -> void | 2 | 2 | 0 | @r2_2(glval:int) | +| For_ConditionUpdate() -> void | 2 | 3 | 0 | @r2_3(int) | +| For_ConditionUpdate() -> void | 2 | 4 | 0 | @r2_4(int) | +| For_ConditionUpdate() -> void | 2 | 5 | 0 | @mu2_5(int) | +| For_Continue_NoUpdate() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| For_Continue_NoUpdate() -> void | 0 | 2 | 0 | @r0_2(glval:int) | +| For_Continue_NoUpdate() -> void | 0 | 3 | 0 | @r0_3(int) | +| For_Continue_NoUpdate() -> void | 0 | 4 | 0 | @mu0_4(int) | +| For_Continue_NoUpdate() -> void | 1 | 0 | 0 | @r1_0(glval:int) | +| For_Continue_NoUpdate() -> void | 1 | 1 | 0 | @r1_1(int) | +| For_Continue_NoUpdate() -> void | 1 | 2 | 0 | @r1_2(int) | +| For_Continue_NoUpdate() -> void | 1 | 3 | 0 | @r1_3(bool) | +| For_Continue_NoUpdate() -> void | 2 | 0 | 0 | @r2_0(glval:int) | +| For_Continue_NoUpdate() -> void | 2 | 1 | 0 | @r2_1(int) | +| For_Continue_NoUpdate() -> void | 2 | 2 | 0 | @r2_2(int) | +| For_Continue_NoUpdate() -> void | 2 | 3 | 0 | @r2_3(bool) | +| For_Continue_Update() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| For_Continue_Update() -> void | 0 | 2 | 0 | @r0_2(glval:int) | +| For_Continue_Update() -> void | 0 | 3 | 0 | @r0_3(int) | +| For_Continue_Update() -> void | 0 | 4 | 0 | @mu0_4(int) | +| For_Continue_Update() -> void | 1 | 0 | 0 | @r1_0(glval:int) | +| For_Continue_Update() -> void | 1 | 1 | 0 | @r1_1(int) | +| For_Continue_Update() -> void | 1 | 2 | 0 | @r1_2(int) | +| For_Continue_Update() -> void | 1 | 3 | 0 | @r1_3(bool) | +| For_Continue_Update() -> void | 2 | 0 | 0 | @r2_0(glval:int) | +| For_Continue_Update() -> void | 2 | 1 | 0 | @r2_1(int) | +| For_Continue_Update() -> void | 2 | 2 | 0 | @r2_2(int) | +| For_Continue_Update() -> void | 2 | 3 | 0 | @r2_3(bool) | +| For_Continue_Update() -> void | 4 | 1 | 0 | @r4_1(int) | +| For_Continue_Update() -> void | 4 | 2 | 0 | @r4_2(glval:int) | +| For_Continue_Update() -> void | 4 | 3 | 0 | @r4_3(int) | +| For_Continue_Update() -> void | 4 | 4 | 0 | @r4_4(int) | +| For_Continue_Update() -> void | 4 | 5 | 0 | @mu4_5(int) | +| For_Empty() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| For_Empty() -> void | 0 | 2 | 0 | @r0_2(glval:int) | +| For_Empty() -> void | 0 | 3 | 0 | @r0_3(int) | +| For_Empty() -> void | 0 | 4 | 0 | @mu0_4(int) | +| For_Init() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| For_Init() -> void | 0 | 2 | 0 | @r0_2(glval:int) | +| For_Init() -> void | 0 | 3 | 0 | @r0_3(int) | +| For_Init() -> void | 0 | 4 | 0 | @mu0_4(int) | +| For_InitCondition() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| For_InitCondition() -> void | 0 | 2 | 0 | @r0_2(glval:int) | +| For_InitCondition() -> void | 0 | 3 | 0 | @r0_3(int) | +| For_InitCondition() -> void | 0 | 4 | 0 | @mu0_4(int) | +| For_InitCondition() -> void | 1 | 0 | 0 | @r1_0(glval:int) | +| For_InitCondition() -> void | 1 | 1 | 0 | @r1_1(int) | +| For_InitCondition() -> void | 1 | 2 | 0 | @r1_2(int) | +| For_InitCondition() -> void | 1 | 3 | 0 | @r1_3(bool) | +| For_InitConditionUpdate() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| For_InitConditionUpdate() -> void | 0 | 2 | 0 | @r0_2(glval:int) | +| For_InitConditionUpdate() -> void | 0 | 3 | 0 | @r0_3(int) | +| For_InitConditionUpdate() -> void | 0 | 4 | 0 | @mu0_4(int) | +| For_InitConditionUpdate() -> void | 1 | 0 | 0 | @r1_0(glval:int) | +| For_InitConditionUpdate() -> void | 1 | 1 | 0 | @r1_1(int) | +| For_InitConditionUpdate() -> void | 1 | 2 | 0 | @r1_2(int) | +| For_InitConditionUpdate() -> void | 1 | 3 | 0 | @r1_3(bool) | +| For_InitConditionUpdate() -> void | 2 | 1 | 0 | @r2_1(int) | +| For_InitConditionUpdate() -> void | 2 | 2 | 0 | @r2_2(glval:int) | +| For_InitConditionUpdate() -> void | 2 | 3 | 0 | @r2_3(int) | +| For_InitConditionUpdate() -> void | 2 | 4 | 0 | @r2_4(int) | +| For_InitConditionUpdate() -> void | 2 | 5 | 0 | @mu2_5(int) | +| For_InitUpdate() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| For_InitUpdate() -> void | 0 | 2 | 0 | @r0_2(glval:int) | +| For_InitUpdate() -> void | 0 | 3 | 0 | @r0_3(int) | +| For_InitUpdate() -> void | 0 | 4 | 0 | @mu0_4(int) | +| For_InitUpdate() -> void | 2 | 1 | 0 | @r2_1(int) | +| For_InitUpdate() -> void | 2 | 2 | 0 | @r2_2(glval:int) | +| For_InitUpdate() -> void | 2 | 3 | 0 | @r2_3(int) | +| For_InitUpdate() -> void | 2 | 4 | 0 | @r2_4(int) | +| For_InitUpdate() -> void | 2 | 5 | 0 | @mu2_5(int) | +| For_Update() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| For_Update() -> void | 0 | 2 | 0 | @r0_2(glval:int) | +| For_Update() -> void | 0 | 3 | 0 | @r0_3(int) | +| For_Update() -> void | 0 | 4 | 0 | @mu0_4(int) | +| For_Update() -> void | 2 | 1 | 0 | @r2_1(int) | +| For_Update() -> void | 2 | 2 | 0 | @r2_2(glval:int) | +| For_Update() -> void | 2 | 3 | 0 | @r2_3(int) | +| For_Update() -> void | 2 | 4 | 0 | @r2_4(int) | +| For_Update() -> void | 2 | 5 | 0 | @mu2_5(int) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 2 | 0 | @r0_2(..(*)(..)) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 3 | 0 | @r0_3(glval:..(*)(..)) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 4 | 0 | @mu0_4(..(*)(..)) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 5 | 0 | @r0_5(void *) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 6 | 0 | @r0_6(glval:void *) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 7 | 0 | @mu0_7(void *) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 8 | 0 | @r0_8(glval:..(*)(..)) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 9 | 0 | @r0_9(..(*)(..)) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 10 | 0 | @r0_10(void *) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 11 | 0 | @r0_11(glval:void *) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 12 | 0 | @mu0_12(void *) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 13 | 0 | @r0_13(glval:void *) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 14 | 0 | @r0_14(void *) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 15 | 0 | @r0_15(..(*)(..)) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 16 | 0 | @r0_16(glval:..(*)(..)) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 17 | 0 | @mu0_17(..(*)(..)) | +| FunctionReferences() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| FunctionReferences() -> void | 0 | 2 | 0 | @r0_2(glval:..(&)(..)) | +| FunctionReferences() -> void | 0 | 3 | 0 | @r0_3(glval:..()(..)) | +| FunctionReferences() -> void | 0 | 4 | 0 | @mu0_4(..(&)(..)) | +| FunctionReferences() -> void | 0 | 5 | 0 | @r0_5(glval:..(*)(..)) | +| FunctionReferences() -> void | 0 | 6 | 0 | @r0_6(glval:..(&)(..)) | +| FunctionReferences() -> void | 0 | 7 | 0 | @r0_7(..(&)(..)) | +| FunctionReferences() -> void | 0 | 8 | 0 | @mu0_8(..(*)(..)) | +| FunctionReferences() -> void | 0 | 9 | 0 | @r0_9(glval:..(&)(..)) | +| FunctionReferences() -> void | 0 | 10 | 0 | @r0_10(..(&)(..)) | +| FunctionReferences() -> void | 0 | 11 | 0 | @r0_11(int) | +| FunctionReferences() -> void | 0 | 12 | 0 | @r0_12(int) | +| HierarchyConversions() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| HierarchyConversions() -> void | 0 | 2 | 0 | @r0_2(glval:Base) | +| HierarchyConversions() -> void | 0 | 3 | 0 | @r0_3(bool) | +| HierarchyConversions() -> void | 0 | 5 | 0 | @r0_5(glval:Middle) | +| HierarchyConversions() -> void | 0 | 6 | 0 | @r0_6(bool) | +| HierarchyConversions() -> void | 0 | 8 | 0 | @r0_8(glval:Derived) | +| HierarchyConversions() -> void | 0 | 9 | 0 | @r0_9(bool) | +| HierarchyConversions() -> void | 0 | 11 | 0 | @r0_11(glval:Base *) | +| HierarchyConversions() -> void | 0 | 12 | 0 | @r0_12(glval:Base) | +| HierarchyConversions() -> void | 0 | 13 | 0 | @mu0_13(Base *) | +| HierarchyConversions() -> void | 0 | 14 | 0 | @r0_14(glval:Middle *) | +| HierarchyConversions() -> void | 0 | 15 | 0 | @r0_15(glval:Middle) | +| HierarchyConversions() -> void | 0 | 16 | 0 | @mu0_16(Middle *) | +| HierarchyConversions() -> void | 0 | 17 | 0 | @r0_17(glval:Derived *) | +| HierarchyConversions() -> void | 0 | 18 | 0 | @r0_18(glval:Derived) | +| HierarchyConversions() -> void | 0 | 19 | 0 | @mu0_19(Derived *) | +| HierarchyConversions() -> void | 0 | 20 | 0 | @r0_20(glval:Base) | +| HierarchyConversions() -> void | 0 | 21 | 0 | @r0_21(bool) | +| HierarchyConversions() -> void | 0 | 22 | 0 | @r0_22(glval:Middle) | +| HierarchyConversions() -> void | 0 | 23 | 0 | @r0_23(glval:Base) | +| HierarchyConversions() -> void | 0 | 24 | 0 | @r0_24(Base &) | +| HierarchyConversions() -> void | 0 | 25 | 0 | @r0_25(glval:Base) | +| HierarchyConversions() -> void | 0 | 26 | 0 | @r0_26(bool) | +| HierarchyConversions() -> void | 0 | 27 | 0 | @r0_27(bool) | +| HierarchyConversions() -> void | 0 | 28 | 0 | @r0_28(glval:Middle) | +| HierarchyConversions() -> void | 0 | 29 | 0 | @r0_29(glval:Base) | +| HierarchyConversions() -> void | 0 | 31 | 0 | @r0_31(Base) | +| HierarchyConversions() -> void | 0 | 32 | 0 | @r0_32(Base &) | +| HierarchyConversions() -> void | 0 | 33 | 0 | @r0_33(glval:Base) | +| HierarchyConversions() -> void | 0 | 34 | 0 | @r0_34(bool) | +| HierarchyConversions() -> void | 0 | 35 | 0 | @r0_35(bool) | +| HierarchyConversions() -> void | 0 | 36 | 0 | @r0_36(glval:Middle) | +| HierarchyConversions() -> void | 0 | 37 | 0 | @r0_37(glval:Base) | +| HierarchyConversions() -> void | 0 | 39 | 0 | @r0_39(Base) | +| HierarchyConversions() -> void | 0 | 40 | 0 | @r0_40(Base &) | +| HierarchyConversions() -> void | 0 | 41 | 0 | @r0_41(glval:Middle *) | +| HierarchyConversions() -> void | 0 | 42 | 0 | @r0_42(Middle *) | +| HierarchyConversions() -> void | 0 | 43 | 0 | @r0_43(Base *) | +| HierarchyConversions() -> void | 0 | 44 | 0 | @r0_44(glval:Base *) | +| HierarchyConversions() -> void | 0 | 45 | 0 | @mu0_45(Base *) | +| HierarchyConversions() -> void | 0 | 46 | 0 | @r0_46(glval:Middle *) | +| HierarchyConversions() -> void | 0 | 47 | 0 | @r0_47(Middle *) | +| HierarchyConversions() -> void | 0 | 48 | 0 | @r0_48(Base *) | +| HierarchyConversions() -> void | 0 | 49 | 0 | @r0_49(glval:Base *) | +| HierarchyConversions() -> void | 0 | 50 | 0 | @mu0_50(Base *) | +| HierarchyConversions() -> void | 0 | 51 | 0 | @r0_51(glval:Middle *) | +| HierarchyConversions() -> void | 0 | 52 | 0 | @r0_52(Middle *) | +| HierarchyConversions() -> void | 0 | 53 | 0 | @r0_53(Base *) | +| HierarchyConversions() -> void | 0 | 54 | 0 | @r0_54(glval:Base *) | +| HierarchyConversions() -> void | 0 | 55 | 0 | @mu0_55(Base *) | +| HierarchyConversions() -> void | 0 | 56 | 0 | @r0_56(glval:Middle *) | +| HierarchyConversions() -> void | 0 | 57 | 0 | @r0_57(Middle *) | +| HierarchyConversions() -> void | 0 | 58 | 0 | @r0_58(Base *) | +| HierarchyConversions() -> void | 0 | 59 | 0 | @r0_59(glval:Base *) | +| HierarchyConversions() -> void | 0 | 60 | 0 | @mu0_60(Base *) | +| HierarchyConversions() -> void | 0 | 61 | 0 | @r0_61(glval:Middle) | +| HierarchyConversions() -> void | 0 | 62 | 0 | @r0_62(bool) | +| HierarchyConversions() -> void | 0 | 63 | 0 | @r0_63(glval:Base) | +| HierarchyConversions() -> void | 0 | 64 | 0 | @r0_64(glval:Middle) | +| HierarchyConversions() -> void | 0 | 65 | 0 | @r0_65(glval:Middle) | +| HierarchyConversions() -> void | 0 | 66 | 0 | @r0_66(Middle &) | +| HierarchyConversions() -> void | 0 | 67 | 0 | @r0_67(glval:Middle) | +| HierarchyConversions() -> void | 0 | 68 | 0 | @r0_68(bool) | +| HierarchyConversions() -> void | 0 | 69 | 0 | @r0_69(glval:Base) | +| HierarchyConversions() -> void | 0 | 70 | 0 | @r0_70(glval:Middle) | +| HierarchyConversions() -> void | 0 | 71 | 0 | @r0_71(glval:Middle) | +| HierarchyConversions() -> void | 0 | 72 | 0 | @r0_72(Middle &) | +| HierarchyConversions() -> void | 0 | 73 | 0 | @r0_73(glval:Base *) | +| HierarchyConversions() -> void | 0 | 74 | 0 | @r0_74(Base *) | +| HierarchyConversions() -> void | 0 | 75 | 0 | @r0_75(Middle *) | +| HierarchyConversions() -> void | 0 | 76 | 0 | @r0_76(glval:Middle *) | +| HierarchyConversions() -> void | 0 | 77 | 0 | @mu0_77(Middle *) | +| HierarchyConversions() -> void | 0 | 78 | 0 | @r0_78(glval:Base *) | +| HierarchyConversions() -> void | 0 | 79 | 0 | @r0_79(Base *) | +| HierarchyConversions() -> void | 0 | 80 | 0 | @r0_80(Middle *) | +| HierarchyConversions() -> void | 0 | 81 | 0 | @r0_81(glval:Middle *) | +| HierarchyConversions() -> void | 0 | 82 | 0 | @mu0_82(Middle *) | +| HierarchyConversions() -> void | 0 | 83 | 0 | @r0_83(glval:Base *) | +| HierarchyConversions() -> void | 0 | 84 | 0 | @r0_84(Base *) | +| HierarchyConversions() -> void | 0 | 85 | 0 | @r0_85(Middle *) | +| HierarchyConversions() -> void | 0 | 86 | 0 | @r0_86(glval:Middle *) | +| HierarchyConversions() -> void | 0 | 87 | 0 | @mu0_87(Middle *) | +| HierarchyConversions() -> void | 0 | 88 | 0 | @r0_88(glval:Base) | +| HierarchyConversions() -> void | 0 | 89 | 0 | @r0_89(bool) | +| HierarchyConversions() -> void | 0 | 90 | 0 | @r0_90(glval:Derived) | +| HierarchyConversions() -> void | 0 | 91 | 0 | @r0_91(glval:Middle) | +| HierarchyConversions() -> void | 0 | 92 | 0 | @r0_92(glval:Base) | +| HierarchyConversions() -> void | 0 | 93 | 0 | @r0_93(Base &) | +| HierarchyConversions() -> void | 0 | 94 | 0 | @r0_94(glval:Base) | +| HierarchyConversions() -> void | 0 | 95 | 0 | @r0_95(bool) | +| HierarchyConversions() -> void | 0 | 96 | 0 | @r0_96(bool) | +| HierarchyConversions() -> void | 0 | 97 | 0 | @r0_97(glval:Derived) | +| HierarchyConversions() -> void | 0 | 98 | 0 | @r0_98(glval:Middle) | +| HierarchyConversions() -> void | 0 | 99 | 0 | @r0_99(glval:Base) | +| HierarchyConversions() -> void | 0 | 101 | 0 | @r0_101(Base) | +| HierarchyConversions() -> void | 0 | 102 | 0 | @r0_102(Base &) | +| HierarchyConversions() -> void | 0 | 103 | 0 | @r0_103(glval:Base) | +| HierarchyConversions() -> void | 0 | 104 | 0 | @r0_104(bool) | +| HierarchyConversions() -> void | 0 | 105 | 0 | @r0_105(bool) | +| HierarchyConversions() -> void | 0 | 106 | 0 | @r0_106(glval:Derived) | +| HierarchyConversions() -> void | 0 | 107 | 0 | @r0_107(glval:Middle) | +| HierarchyConversions() -> void | 0 | 108 | 0 | @r0_108(glval:Base) | +| HierarchyConversions() -> void | 0 | 110 | 0 | @r0_110(Base) | +| HierarchyConversions() -> void | 0 | 111 | 0 | @r0_111(Base &) | +| HierarchyConversions() -> void | 0 | 112 | 0 | @r0_112(glval:Derived *) | +| HierarchyConversions() -> void | 0 | 113 | 0 | @r0_113(Derived *) | +| HierarchyConversions() -> void | 0 | 114 | 0 | @r0_114(Middle *) | +| HierarchyConversions() -> void | 0 | 115 | 0 | @r0_115(Base *) | +| HierarchyConversions() -> void | 0 | 116 | 0 | @r0_116(glval:Base *) | +| HierarchyConversions() -> void | 0 | 117 | 0 | @mu0_117(Base *) | +| HierarchyConversions() -> void | 0 | 118 | 0 | @r0_118(glval:Derived *) | +| HierarchyConversions() -> void | 0 | 119 | 0 | @r0_119(Derived *) | +| HierarchyConversions() -> void | 0 | 120 | 0 | @r0_120(Middle *) | +| HierarchyConversions() -> void | 0 | 121 | 0 | @r0_121(Base *) | +| HierarchyConversions() -> void | 0 | 122 | 0 | @r0_122(glval:Base *) | +| HierarchyConversions() -> void | 0 | 123 | 0 | @mu0_123(Base *) | +| HierarchyConversions() -> void | 0 | 124 | 0 | @r0_124(glval:Derived *) | +| HierarchyConversions() -> void | 0 | 125 | 0 | @r0_125(Derived *) | +| HierarchyConversions() -> void | 0 | 126 | 0 | @r0_126(Middle *) | +| HierarchyConversions() -> void | 0 | 127 | 0 | @r0_127(Base *) | +| HierarchyConversions() -> void | 0 | 128 | 0 | @r0_128(glval:Base *) | +| HierarchyConversions() -> void | 0 | 129 | 0 | @mu0_129(Base *) | +| HierarchyConversions() -> void | 0 | 130 | 0 | @r0_130(glval:Derived *) | +| HierarchyConversions() -> void | 0 | 131 | 0 | @r0_131(Derived *) | +| HierarchyConversions() -> void | 0 | 132 | 0 | @r0_132(Base *) | +| HierarchyConversions() -> void | 0 | 133 | 0 | @r0_133(glval:Base *) | +| HierarchyConversions() -> void | 0 | 134 | 0 | @mu0_134(Base *) | +| HierarchyConversions() -> void | 0 | 135 | 0 | @r0_135(glval:Derived) | +| HierarchyConversions() -> void | 0 | 136 | 0 | @r0_136(bool) | +| HierarchyConversions() -> void | 0 | 137 | 0 | @r0_137(glval:Base) | +| HierarchyConversions() -> void | 0 | 138 | 0 | @r0_138(glval:Middle) | +| HierarchyConversions() -> void | 0 | 139 | 0 | @r0_139(glval:Derived) | +| HierarchyConversions() -> void | 0 | 140 | 0 | @r0_140(glval:Derived) | +| HierarchyConversions() -> void | 0 | 141 | 0 | @r0_141(Derived &) | +| HierarchyConversions() -> void | 0 | 142 | 0 | @r0_142(glval:Derived) | +| HierarchyConversions() -> void | 0 | 143 | 0 | @r0_143(bool) | +| HierarchyConversions() -> void | 0 | 144 | 0 | @r0_144(glval:Base) | +| HierarchyConversions() -> void | 0 | 145 | 0 | @r0_145(glval:Middle) | +| HierarchyConversions() -> void | 0 | 146 | 0 | @r0_146(glval:Derived) | +| HierarchyConversions() -> void | 0 | 147 | 0 | @r0_147(glval:Derived) | +| HierarchyConversions() -> void | 0 | 148 | 0 | @r0_148(Derived &) | +| HierarchyConversions() -> void | 0 | 149 | 0 | @r0_149(glval:Base *) | +| HierarchyConversions() -> void | 0 | 150 | 0 | @r0_150(Base *) | +| HierarchyConversions() -> void | 0 | 151 | 0 | @r0_151(Middle *) | +| HierarchyConversions() -> void | 0 | 152 | 0 | @r0_152(Derived *) | +| HierarchyConversions() -> void | 0 | 153 | 0 | @r0_153(glval:Derived *) | +| HierarchyConversions() -> void | 0 | 154 | 0 | @mu0_154(Derived *) | +| HierarchyConversions() -> void | 0 | 155 | 0 | @r0_155(glval:Base *) | +| HierarchyConversions() -> void | 0 | 156 | 0 | @r0_156(Base *) | +| HierarchyConversions() -> void | 0 | 157 | 0 | @r0_157(Middle *) | +| HierarchyConversions() -> void | 0 | 158 | 0 | @r0_158(Derived *) | +| HierarchyConversions() -> void | 0 | 159 | 0 | @r0_159(glval:Derived *) | +| HierarchyConversions() -> void | 0 | 160 | 0 | @mu0_160(Derived *) | +| HierarchyConversions() -> void | 0 | 161 | 0 | @r0_161(glval:Base *) | +| HierarchyConversions() -> void | 0 | 162 | 0 | @r0_162(Base *) | +| HierarchyConversions() -> void | 0 | 163 | 0 | @r0_163(Derived *) | +| HierarchyConversions() -> void | 0 | 164 | 0 | @r0_164(glval:Derived *) | +| HierarchyConversions() -> void | 0 | 165 | 0 | @mu0_165(Derived *) | +| HierarchyConversions() -> void | 0 | 166 | 0 | @r0_166(glval:MiddleVB1 *) | +| HierarchyConversions() -> void | 0 | 167 | 0 | @r0_167(MiddleVB1 *) | +| HierarchyConversions() -> void | 0 | 168 | 0 | @mu0_168(MiddleVB1 *) | +| HierarchyConversions() -> void | 0 | 169 | 0 | @r0_169(glval:DerivedVB *) | +| HierarchyConversions() -> void | 0 | 170 | 0 | @r0_170(DerivedVB *) | +| HierarchyConversions() -> void | 0 | 171 | 0 | @mu0_171(DerivedVB *) | +| HierarchyConversions() -> void | 0 | 172 | 0 | @r0_172(glval:MiddleVB1 *) | +| HierarchyConversions() -> void | 0 | 173 | 0 | @r0_173(MiddleVB1 *) | +| HierarchyConversions() -> void | 0 | 174 | 0 | @r0_174(Base *) | +| HierarchyConversions() -> void | 0 | 175 | 0 | @r0_175(glval:Base *) | +| HierarchyConversions() -> void | 0 | 176 | 0 | @mu0_176(Base *) | +| HierarchyConversions() -> void | 0 | 177 | 0 | @r0_177(glval:DerivedVB *) | +| HierarchyConversions() -> void | 0 | 178 | 0 | @r0_178(DerivedVB *) | +| HierarchyConversions() -> void | 0 | 179 | 0 | @r0_179(Base *) | +| HierarchyConversions() -> void | 0 | 180 | 0 | @r0_180(glval:Base *) | +| HierarchyConversions() -> void | 0 | 181 | 0 | @mu0_181(Base *) | +| IfStatements(bool, int, int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| IfStatements(bool, int, int) -> void | 0 | 2 | 0 | @r0_2(bool) | +| IfStatements(bool, int, int) -> void | 0 | 3 | 0 | @r0_3(glval:bool) | +| IfStatements(bool, int, int) -> void | 0 | 4 | 0 | @mu0_4(bool) | +| IfStatements(bool, int, int) -> void | 0 | 5 | 0 | @r0_5(int) | +| IfStatements(bool, int, int) -> void | 0 | 6 | 0 | @r0_6(glval:int) | +| IfStatements(bool, int, int) -> void | 0 | 7 | 0 | @mu0_7(int) | +| IfStatements(bool, int, int) -> void | 0 | 8 | 0 | @r0_8(int) | +| IfStatements(bool, int, int) -> void | 0 | 9 | 0 | @r0_9(glval:int) | +| IfStatements(bool, int, int) -> void | 0 | 10 | 0 | @mu0_10(int) | +| IfStatements(bool, int, int) -> void | 0 | 11 | 0 | @r0_11(glval:bool) | +| IfStatements(bool, int, int) -> void | 0 | 12 | 0 | @r0_12(bool) | +| IfStatements(bool, int, int) -> void | 1 | 0 | 0 | @r1_0(glval:bool) | +| IfStatements(bool, int, int) -> void | 1 | 1 | 0 | @r1_1(bool) | +| IfStatements(bool, int, int) -> void | 2 | 0 | 0 | @r2_0(glval:int) | +| IfStatements(bool, int, int) -> void | 2 | 1 | 0 | @r2_1(int) | +| IfStatements(bool, int, int) -> void | 2 | 2 | 0 | @r2_2(glval:int) | +| IfStatements(bool, int, int) -> void | 2 | 3 | 0 | @mu2_3(int) | +| IfStatements(bool, int, int) -> void | 3 | 0 | 0 | @r3_0(glval:int) | +| IfStatements(bool, int, int) -> void | 3 | 1 | 0 | @r3_1(int) | +| IfStatements(bool, int, int) -> void | 3 | 2 | 0 | @r3_2(int) | +| IfStatements(bool, int, int) -> void | 3 | 3 | 0 | @r3_3(bool) | +| IfStatements(bool, int, int) -> void | 4 | 0 | 0 | @r4_0(int) | +| IfStatements(bool, int, int) -> void | 4 | 1 | 0 | @r4_1(glval:int) | +| IfStatements(bool, int, int) -> void | 4 | 2 | 0 | @mu4_2(int) | +| IfStatements(bool, int, int) -> void | 5 | 0 | 0 | @r5_0(int) | +| IfStatements(bool, int, int) -> void | 5 | 1 | 0 | @r5_1(glval:int) | +| IfStatements(bool, int, int) -> void | 5 | 2 | 0 | @mu5_2(int) | +| InitArray() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| InitArray() -> void | 0 | 2 | 0 | @r0_2(glval:char[32]) | +| InitArray() -> void | 0 | 3 | 0 | @r0_3(glval:char[1]) | +| InitArray() -> void | 0 | 4 | 0 | @r0_4(char[1]) | +| InitArray() -> void | 0 | 5 | 0 | @mu0_5(char[1]) | +| InitArray() -> void | 0 | 6 | 0 | @r0_6(unknown[31]) | +| InitArray() -> void | 0 | 7 | 0 | @r0_7(int) | +| InitArray() -> void | 0 | 8 | 0 | @r0_8(glval:char) | +| InitArray() -> void | 0 | 9 | 0 | @mu0_9(unknown[31]) | +| InitArray() -> void | 0 | 10 | 0 | @r0_10(glval:char[4]) | +| InitArray() -> void | 0 | 11 | 0 | @r0_11(glval:char[4]) | +| InitArray() -> void | 0 | 12 | 0 | @r0_12(char[4]) | +| InitArray() -> void | 0 | 13 | 0 | @mu0_13(char[4]) | +| InitArray() -> void | 0 | 14 | 0 | @r0_14(glval:char[]) | +| InitArray() -> void | 0 | 15 | 0 | @r0_15(glval:char[5]) | +| InitArray() -> void | 0 | 16 | 0 | @r0_16(char[5]) | +| InitArray() -> void | 0 | 17 | 0 | @mu0_17(char[5]) | +| InitArray() -> void | 0 | 18 | 0 | @r0_18(glval:char[2]) | +| InitArray() -> void | 0 | 19 | 0 | @r0_19(char[2]) | +| InitArray() -> void | 0 | 20 | 0 | @mu0_20(char[2]) | +| InitArray() -> void | 0 | 21 | 0 | @r0_21(glval:char[2]) | +| InitArray() -> void | 0 | 22 | 0 | @r0_22(int) | +| InitArray() -> void | 0 | 23 | 0 | @r0_23(glval:char) | +| InitArray() -> void | 0 | 24 | 0 | @r0_24(unknown[2]) | +| InitArray() -> void | 0 | 25 | 0 | @mu0_25(unknown[2]) | +| InitArray() -> void | 0 | 26 | 0 | @r0_26(glval:char[2]) | +| InitArray() -> void | 0 | 27 | 0 | @r0_27(int) | +| InitArray() -> void | 0 | 28 | 0 | @r0_28(glval:char) | +| InitArray() -> void | 0 | 29 | 0 | @r0_29(char) | +| InitArray() -> void | 0 | 30 | 0 | @mu0_30(char) | +| InitArray() -> void | 0 | 31 | 0 | @r0_31(int) | +| InitArray() -> void | 0 | 32 | 0 | @r0_32(glval:char) | +| InitArray() -> void | 0 | 33 | 0 | @r0_33(char) | +| InitArray() -> void | 0 | 34 | 0 | @mu0_34(char) | +| InitArray() -> void | 0 | 35 | 0 | @r0_35(glval:char[2]) | +| InitArray() -> void | 0 | 36 | 0 | @r0_36(int) | +| InitArray() -> void | 0 | 37 | 0 | @r0_37(glval:char) | +| InitArray() -> void | 0 | 38 | 0 | @r0_38(char) | +| InitArray() -> void | 0 | 39 | 0 | @mu0_39(char) | +| InitArray() -> void | 0 | 40 | 0 | @r0_40(int) | +| InitArray() -> void | 0 | 41 | 0 | @r0_41(glval:char) | +| InitArray() -> void | 0 | 42 | 0 | @r0_42(char) | +| InitArray() -> void | 0 | 43 | 0 | @mu0_43(char) | +| InitArray() -> void | 0 | 44 | 0 | @r0_44(glval:char[3]) | +| InitArray() -> void | 0 | 45 | 0 | @r0_45(int) | +| InitArray() -> void | 0 | 46 | 0 | @r0_46(glval:char) | +| InitArray() -> void | 0 | 47 | 0 | @r0_47(char) | +| InitArray() -> void | 0 | 48 | 0 | @mu0_48(char) | +| InitArray() -> void | 0 | 49 | 0 | @r0_49(int) | +| InitArray() -> void | 0 | 50 | 0 | @r0_50(glval:char) | +| InitArray() -> void | 0 | 51 | 0 | @r0_51(unknown[2]) | +| InitArray() -> void | 0 | 52 | 0 | @mu0_52(unknown[2]) | +| InitList(int, float) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| InitList(int, float) -> void | 0 | 2 | 0 | @r0_2(int) | +| InitList(int, float) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| InitList(int, float) -> void | 0 | 4 | 0 | @mu0_4(int) | +| InitList(int, float) -> void | 0 | 5 | 0 | @r0_5(float) | +| InitList(int, float) -> void | 0 | 6 | 0 | @r0_6(glval:float) | +| InitList(int, float) -> void | 0 | 7 | 0 | @mu0_7(float) | +| InitList(int, float) -> void | 0 | 8 | 0 | @r0_8(glval:Point) | +| InitList(int, float) -> void | 0 | 9 | 0 | @r0_9(glval:int) | +| InitList(int, float) -> void | 0 | 10 | 0 | @r0_10(glval:int) | +| InitList(int, float) -> void | 0 | 11 | 0 | @r0_11(int) | +| InitList(int, float) -> void | 0 | 12 | 0 | @mu0_12(int) | +| InitList(int, float) -> void | 0 | 13 | 0 | @r0_13(glval:int) | +| InitList(int, float) -> void | 0 | 14 | 0 | @r0_14(glval:float) | +| InitList(int, float) -> void | 0 | 15 | 0 | @r0_15(float) | +| InitList(int, float) -> void | 0 | 16 | 0 | @r0_16(int) | +| InitList(int, float) -> void | 0 | 17 | 0 | @mu0_17(int) | +| InitList(int, float) -> void | 0 | 18 | 0 | @r0_18(glval:Point) | +| InitList(int, float) -> void | 0 | 19 | 0 | @r0_19(glval:int) | +| InitList(int, float) -> void | 0 | 20 | 0 | @r0_20(glval:int) | +| InitList(int, float) -> void | 0 | 21 | 0 | @r0_21(int) | +| InitList(int, float) -> void | 0 | 22 | 0 | @mu0_22(int) | +| InitList(int, float) -> void | 0 | 23 | 0 | @r0_23(glval:int) | +| InitList(int, float) -> void | 0 | 24 | 0 | @r0_24(int) | +| InitList(int, float) -> void | 0 | 25 | 0 | @mu0_25(int) | +| InitList(int, float) -> void | 0 | 26 | 0 | @r0_26(glval:Point) | +| InitList(int, float) -> void | 0 | 27 | 0 | @r0_27(glval:int) | +| InitList(int, float) -> void | 0 | 28 | 0 | @r0_28(int) | +| InitList(int, float) -> void | 0 | 29 | 0 | @mu0_29(int) | +| InitList(int, float) -> void | 0 | 30 | 0 | @r0_30(glval:int) | +| InitList(int, float) -> void | 0 | 31 | 0 | @r0_31(int) | +| InitList(int, float) -> void | 0 | 32 | 0 | @mu0_32(int) | +| InitList(int, float) -> void | 0 | 33 | 0 | @r0_33(glval:int) | +| InitList(int, float) -> void | 0 | 34 | 0 | @r0_34(int) | +| InitList(int, float) -> void | 0 | 35 | 0 | @mu0_35(int) | +| InitList(int, float) -> void | 0 | 36 | 0 | @r0_36(glval:int) | +| InitList(int, float) -> void | 0 | 37 | 0 | @r0_37(int) | +| InitList(int, float) -> void | 0 | 38 | 0 | @mu0_38(int) | +| InitReference(int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| InitReference(int) -> void | 0 | 2 | 0 | @r0_2(int) | +| InitReference(int) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| InitReference(int) -> void | 0 | 4 | 0 | @mu0_4(int) | +| InitReference(int) -> void | 0 | 5 | 0 | @r0_5(glval:int &) | +| InitReference(int) -> void | 0 | 6 | 0 | @r0_6(glval:int) | +| InitReference(int) -> void | 0 | 7 | 0 | @mu0_7(int &) | +| InitReference(int) -> void | 0 | 8 | 0 | @r0_8(glval:int &) | +| InitReference(int) -> void | 0 | 9 | 0 | @r0_9(glval:int &) | +| InitReference(int) -> void | 0 | 10 | 0 | @r0_10(int &) | +| InitReference(int) -> void | 0 | 11 | 0 | @mu0_11(int &) | +| InitReference(int) -> void | 0 | 12 | 0 | @r0_12(glval:String &) | +| InitReference(int) -> void | 0 | 13 | 0 | @r0_13(bool) | +| InitReference(int) -> void | 0 | 14 | 0 | @r0_14(String &) | +| InitReference(int) -> void | 0 | 15 | 0 | @r0_15(glval:String) | +| InitReference(int) -> void | 0 | 16 | 0 | @mu0_16(String &) | +| IntegerCompare(int, int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| IntegerCompare(int, int) -> void | 0 | 2 | 0 | @r0_2(int) | +| IntegerCompare(int, int) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| IntegerCompare(int, int) -> void | 0 | 4 | 0 | @mu0_4(int) | +| IntegerCompare(int, int) -> void | 0 | 5 | 0 | @r0_5(int) | +| IntegerCompare(int, int) -> void | 0 | 6 | 0 | @r0_6(glval:int) | +| IntegerCompare(int, int) -> void | 0 | 7 | 0 | @mu0_7(int) | +| IntegerCompare(int, int) -> void | 0 | 8 | 0 | @r0_8(glval:bool) | +| IntegerCompare(int, int) -> void | 0 | 9 | 0 | @r0_9(bool) | +| IntegerCompare(int, int) -> void | 0 | 10 | 0 | @mu0_10(bool) | +| IntegerCompare(int, int) -> void | 0 | 11 | 0 | @r0_11(glval:int) | +| IntegerCompare(int, int) -> void | 0 | 12 | 0 | @r0_12(int) | +| IntegerCompare(int, int) -> void | 0 | 13 | 0 | @r0_13(glval:int) | +| IntegerCompare(int, int) -> void | 0 | 14 | 0 | @r0_14(int) | +| IntegerCompare(int, int) -> void | 0 | 15 | 0 | @r0_15(bool) | +| IntegerCompare(int, int) -> void | 0 | 16 | 0 | @r0_16(glval:bool) | +| IntegerCompare(int, int) -> void | 0 | 17 | 0 | @mu0_17(bool) | +| IntegerCompare(int, int) -> void | 0 | 18 | 0 | @r0_18(glval:int) | +| IntegerCompare(int, int) -> void | 0 | 19 | 0 | @r0_19(int) | +| IntegerCompare(int, int) -> void | 0 | 20 | 0 | @r0_20(glval:int) | +| IntegerCompare(int, int) -> void | 0 | 21 | 0 | @r0_21(int) | +| IntegerCompare(int, int) -> void | 0 | 22 | 0 | @r0_22(bool) | +| IntegerCompare(int, int) -> void | 0 | 23 | 0 | @r0_23(glval:bool) | +| IntegerCompare(int, int) -> void | 0 | 24 | 0 | @mu0_24(bool) | +| IntegerCompare(int, int) -> void | 0 | 25 | 0 | @r0_25(glval:int) | +| IntegerCompare(int, int) -> void | 0 | 26 | 0 | @r0_26(int) | +| IntegerCompare(int, int) -> void | 0 | 27 | 0 | @r0_27(glval:int) | +| IntegerCompare(int, int) -> void | 0 | 28 | 0 | @r0_28(int) | +| IntegerCompare(int, int) -> void | 0 | 29 | 0 | @r0_29(bool) | +| IntegerCompare(int, int) -> void | 0 | 30 | 0 | @r0_30(glval:bool) | +| IntegerCompare(int, int) -> void | 0 | 31 | 0 | @mu0_31(bool) | +| IntegerCompare(int, int) -> void | 0 | 32 | 0 | @r0_32(glval:int) | +| IntegerCompare(int, int) -> void | 0 | 33 | 0 | @r0_33(int) | +| IntegerCompare(int, int) -> void | 0 | 34 | 0 | @r0_34(glval:int) | +| IntegerCompare(int, int) -> void | 0 | 35 | 0 | @r0_35(int) | +| IntegerCompare(int, int) -> void | 0 | 36 | 0 | @r0_36(bool) | +| IntegerCompare(int, int) -> void | 0 | 37 | 0 | @r0_37(glval:bool) | +| IntegerCompare(int, int) -> void | 0 | 38 | 0 | @mu0_38(bool) | +| IntegerCompare(int, int) -> void | 0 | 39 | 0 | @r0_39(glval:int) | +| IntegerCompare(int, int) -> void | 0 | 40 | 0 | @r0_40(int) | +| IntegerCompare(int, int) -> void | 0 | 41 | 0 | @r0_41(glval:int) | +| IntegerCompare(int, int) -> void | 0 | 42 | 0 | @r0_42(int) | +| IntegerCompare(int, int) -> void | 0 | 43 | 0 | @r0_43(bool) | +| IntegerCompare(int, int) -> void | 0 | 44 | 0 | @r0_44(glval:bool) | +| IntegerCompare(int, int) -> void | 0 | 45 | 0 | @mu0_45(bool) | +| IntegerCompare(int, int) -> void | 0 | 46 | 0 | @r0_46(glval:int) | +| IntegerCompare(int, int) -> void | 0 | 47 | 0 | @r0_47(int) | +| IntegerCompare(int, int) -> void | 0 | 48 | 0 | @r0_48(glval:int) | +| IntegerCompare(int, int) -> void | 0 | 49 | 0 | @r0_49(int) | +| IntegerCompare(int, int) -> void | 0 | 50 | 0 | @r0_50(bool) | +| IntegerCompare(int, int) -> void | 0 | 51 | 0 | @r0_51(glval:bool) | +| IntegerCompare(int, int) -> void | 0 | 52 | 0 | @mu0_52(bool) | +| IntegerCrement(int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| IntegerCrement(int) -> void | 0 | 2 | 0 | @r0_2(int) | +| IntegerCrement(int) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| IntegerCrement(int) -> void | 0 | 4 | 0 | @mu0_4(int) | +| IntegerCrement(int) -> void | 0 | 5 | 0 | @r0_5(glval:int) | +| IntegerCrement(int) -> void | 0 | 6 | 0 | @r0_6(int) | +| IntegerCrement(int) -> void | 0 | 7 | 0 | @mu0_7(int) | +| IntegerCrement(int) -> void | 0 | 8 | 0 | @r0_8(glval:int) | +| IntegerCrement(int) -> void | 0 | 9 | 0 | @r0_9(int) | +| IntegerCrement(int) -> void | 0 | 10 | 0 | @r0_10(int) | +| IntegerCrement(int) -> void | 0 | 11 | 0 | @r0_11(int) | +| IntegerCrement(int) -> void | 0 | 12 | 0 | @mu0_12(int) | +| IntegerCrement(int) -> void | 0 | 13 | 0 | @r0_13(glval:int) | +| IntegerCrement(int) -> void | 0 | 14 | 0 | @mu0_14(int) | +| IntegerCrement(int) -> void | 0 | 15 | 0 | @r0_15(glval:int) | +| IntegerCrement(int) -> void | 0 | 16 | 0 | @r0_16(int) | +| IntegerCrement(int) -> void | 0 | 17 | 0 | @r0_17(int) | +| IntegerCrement(int) -> void | 0 | 18 | 0 | @r0_18(int) | +| IntegerCrement(int) -> void | 0 | 19 | 0 | @mu0_19(int) | +| IntegerCrement(int) -> void | 0 | 20 | 0 | @r0_20(glval:int) | +| IntegerCrement(int) -> void | 0 | 21 | 0 | @mu0_21(int) | +| IntegerCrement(int) -> void | 0 | 22 | 0 | @r0_22(glval:int) | +| IntegerCrement(int) -> void | 0 | 23 | 0 | @r0_23(int) | +| IntegerCrement(int) -> void | 0 | 24 | 0 | @r0_24(int) | +| IntegerCrement(int) -> void | 0 | 25 | 0 | @r0_25(int) | +| IntegerCrement(int) -> void | 0 | 26 | 0 | @mu0_26(int) | +| IntegerCrement(int) -> void | 0 | 27 | 0 | @r0_27(glval:int) | +| IntegerCrement(int) -> void | 0 | 28 | 0 | @mu0_28(int) | +| IntegerCrement(int) -> void | 0 | 29 | 0 | @r0_29(glval:int) | +| IntegerCrement(int) -> void | 0 | 30 | 0 | @r0_30(int) | +| IntegerCrement(int) -> void | 0 | 31 | 0 | @r0_31(int) | +| IntegerCrement(int) -> void | 0 | 32 | 0 | @r0_32(int) | +| IntegerCrement(int) -> void | 0 | 33 | 0 | @mu0_33(int) | +| IntegerCrement(int) -> void | 0 | 34 | 0 | @r0_34(glval:int) | +| IntegerCrement(int) -> void | 0 | 35 | 0 | @mu0_35(int) | +| IntegerCrement_LValue(int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| IntegerCrement_LValue(int) -> void | 0 | 2 | 0 | @r0_2(int) | +| IntegerCrement_LValue(int) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| IntegerCrement_LValue(int) -> void | 0 | 4 | 0 | @mu0_4(int) | +| IntegerCrement_LValue(int) -> void | 0 | 5 | 0 | @r0_5(glval:int *) | +| IntegerCrement_LValue(int) -> void | 0 | 6 | 0 | @r0_6(int *) | +| IntegerCrement_LValue(int) -> void | 0 | 7 | 0 | @mu0_7(int *) | +| IntegerCrement_LValue(int) -> void | 0 | 8 | 0 | @r0_8(glval:int) | +| IntegerCrement_LValue(int) -> void | 0 | 9 | 0 | @r0_9(int) | +| IntegerCrement_LValue(int) -> void | 0 | 10 | 0 | @r0_10(int) | +| IntegerCrement_LValue(int) -> void | 0 | 11 | 0 | @r0_11(int) | +| IntegerCrement_LValue(int) -> void | 0 | 12 | 0 | @mu0_12(int) | +| IntegerCrement_LValue(int) -> void | 0 | 13 | 0 | @r0_13(glval:int *) | +| IntegerCrement_LValue(int) -> void | 0 | 14 | 0 | @mu0_14(int *) | +| IntegerCrement_LValue(int) -> void | 0 | 15 | 0 | @r0_15(glval:int) | +| IntegerCrement_LValue(int) -> void | 0 | 16 | 0 | @r0_16(int) | +| IntegerCrement_LValue(int) -> void | 0 | 17 | 0 | @r0_17(int) | +| IntegerCrement_LValue(int) -> void | 0 | 18 | 0 | @r0_18(int) | +| IntegerCrement_LValue(int) -> void | 0 | 19 | 0 | @mu0_19(int) | +| IntegerCrement_LValue(int) -> void | 0 | 20 | 0 | @r0_20(glval:int *) | +| IntegerCrement_LValue(int) -> void | 0 | 21 | 0 | @mu0_21(int *) | +| IntegerOps(int, int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| IntegerOps(int, int) -> void | 0 | 2 | 0 | @r0_2(int) | +| IntegerOps(int, int) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| IntegerOps(int, int) -> void | 0 | 4 | 0 | @mu0_4(int) | +| IntegerOps(int, int) -> void | 0 | 5 | 0 | @r0_5(int) | +| IntegerOps(int, int) -> void | 0 | 6 | 0 | @r0_6(glval:int) | +| IntegerOps(int, int) -> void | 0 | 7 | 0 | @mu0_7(int) | +| IntegerOps(int, int) -> void | 0 | 8 | 0 | @r0_8(glval:int) | +| IntegerOps(int, int) -> void | 0 | 9 | 0 | @r0_9(int) | +| IntegerOps(int, int) -> void | 0 | 10 | 0 | @mu0_10(int) | +| IntegerOps(int, int) -> void | 0 | 11 | 0 | @r0_11(glval:int) | +| IntegerOps(int, int) -> void | 0 | 12 | 0 | @r0_12(int) | +| IntegerOps(int, int) -> void | 0 | 13 | 0 | @r0_13(glval:int) | +| IntegerOps(int, int) -> void | 0 | 14 | 0 | @r0_14(int) | +| IntegerOps(int, int) -> void | 0 | 15 | 0 | @r0_15(int) | +| IntegerOps(int, int) -> void | 0 | 16 | 0 | @r0_16(glval:int) | +| IntegerOps(int, int) -> void | 0 | 17 | 0 | @mu0_17(int) | +| IntegerOps(int, int) -> void | 0 | 18 | 0 | @r0_18(glval:int) | +| IntegerOps(int, int) -> void | 0 | 19 | 0 | @r0_19(int) | +| IntegerOps(int, int) -> void | 0 | 20 | 0 | @r0_20(glval:int) | +| IntegerOps(int, int) -> void | 0 | 21 | 0 | @r0_21(int) | +| IntegerOps(int, int) -> void | 0 | 22 | 0 | @r0_22(int) | +| IntegerOps(int, int) -> void | 0 | 23 | 0 | @r0_23(glval:int) | +| IntegerOps(int, int) -> void | 0 | 24 | 0 | @mu0_24(int) | +| IntegerOps(int, int) -> void | 0 | 25 | 0 | @r0_25(glval:int) | +| IntegerOps(int, int) -> void | 0 | 26 | 0 | @r0_26(int) | +| IntegerOps(int, int) -> void | 0 | 27 | 0 | @r0_27(glval:int) | +| IntegerOps(int, int) -> void | 0 | 28 | 0 | @r0_28(int) | +| IntegerOps(int, int) -> void | 0 | 29 | 0 | @r0_29(int) | +| IntegerOps(int, int) -> void | 0 | 30 | 0 | @r0_30(glval:int) | +| IntegerOps(int, int) -> void | 0 | 31 | 0 | @mu0_31(int) | +| IntegerOps(int, int) -> void | 0 | 32 | 0 | @r0_32(glval:int) | +| IntegerOps(int, int) -> void | 0 | 33 | 0 | @r0_33(int) | +| IntegerOps(int, int) -> void | 0 | 34 | 0 | @r0_34(glval:int) | +| IntegerOps(int, int) -> void | 0 | 35 | 0 | @r0_35(int) | +| IntegerOps(int, int) -> void | 0 | 36 | 0 | @r0_36(int) | +| IntegerOps(int, int) -> void | 0 | 37 | 0 | @r0_37(glval:int) | +| IntegerOps(int, int) -> void | 0 | 38 | 0 | @mu0_38(int) | +| IntegerOps(int, int) -> void | 0 | 39 | 0 | @r0_39(glval:int) | +| IntegerOps(int, int) -> void | 0 | 40 | 0 | @r0_40(int) | +| IntegerOps(int, int) -> void | 0 | 41 | 0 | @r0_41(glval:int) | +| IntegerOps(int, int) -> void | 0 | 42 | 0 | @r0_42(int) | +| IntegerOps(int, int) -> void | 0 | 43 | 0 | @r0_43(int) | +| IntegerOps(int, int) -> void | 0 | 44 | 0 | @r0_44(glval:int) | +| IntegerOps(int, int) -> void | 0 | 45 | 0 | @mu0_45(int) | +| IntegerOps(int, int) -> void | 0 | 46 | 0 | @r0_46(glval:int) | +| IntegerOps(int, int) -> void | 0 | 47 | 0 | @r0_47(int) | +| IntegerOps(int, int) -> void | 0 | 48 | 0 | @r0_48(glval:int) | +| IntegerOps(int, int) -> void | 0 | 49 | 0 | @r0_49(int) | +| IntegerOps(int, int) -> void | 0 | 50 | 0 | @r0_50(int) | +| IntegerOps(int, int) -> void | 0 | 51 | 0 | @r0_51(glval:int) | +| IntegerOps(int, int) -> void | 0 | 52 | 0 | @mu0_52(int) | +| IntegerOps(int, int) -> void | 0 | 53 | 0 | @r0_53(glval:int) | +| IntegerOps(int, int) -> void | 0 | 54 | 0 | @r0_54(int) | +| IntegerOps(int, int) -> void | 0 | 55 | 0 | @r0_55(glval:int) | +| IntegerOps(int, int) -> void | 0 | 56 | 0 | @r0_56(int) | +| IntegerOps(int, int) -> void | 0 | 57 | 0 | @r0_57(int) | +| IntegerOps(int, int) -> void | 0 | 58 | 0 | @r0_58(glval:int) | +| IntegerOps(int, int) -> void | 0 | 59 | 0 | @mu0_59(int) | +| IntegerOps(int, int) -> void | 0 | 60 | 0 | @r0_60(glval:int) | +| IntegerOps(int, int) -> void | 0 | 61 | 0 | @r0_61(int) | +| IntegerOps(int, int) -> void | 0 | 62 | 0 | @r0_62(glval:int) | +| IntegerOps(int, int) -> void | 0 | 63 | 0 | @r0_63(int) | +| IntegerOps(int, int) -> void | 0 | 64 | 0 | @r0_64(int) | +| IntegerOps(int, int) -> void | 0 | 65 | 0 | @r0_65(glval:int) | +| IntegerOps(int, int) -> void | 0 | 66 | 0 | @mu0_66(int) | +| IntegerOps(int, int) -> void | 0 | 67 | 0 | @r0_67(glval:int) | +| IntegerOps(int, int) -> void | 0 | 68 | 0 | @r0_68(int) | +| IntegerOps(int, int) -> void | 0 | 69 | 0 | @r0_69(glval:int) | +| IntegerOps(int, int) -> void | 0 | 70 | 0 | @r0_70(int) | +| IntegerOps(int, int) -> void | 0 | 71 | 0 | @r0_71(int) | +| IntegerOps(int, int) -> void | 0 | 72 | 0 | @r0_72(glval:int) | +| IntegerOps(int, int) -> void | 0 | 73 | 0 | @mu0_73(int) | +| IntegerOps(int, int) -> void | 0 | 74 | 0 | @r0_74(glval:int) | +| IntegerOps(int, int) -> void | 0 | 75 | 0 | @r0_75(int) | +| IntegerOps(int, int) -> void | 0 | 76 | 0 | @r0_76(glval:int) | +| IntegerOps(int, int) -> void | 0 | 77 | 0 | @r0_77(int) | +| IntegerOps(int, int) -> void | 0 | 78 | 0 | @r0_78(int) | +| IntegerOps(int, int) -> void | 0 | 79 | 0 | @r0_79(glval:int) | +| IntegerOps(int, int) -> void | 0 | 80 | 0 | @mu0_80(int) | +| IntegerOps(int, int) -> void | 0 | 81 | 0 | @r0_81(glval:int) | +| IntegerOps(int, int) -> void | 0 | 82 | 0 | @r0_82(int) | +| IntegerOps(int, int) -> void | 0 | 83 | 0 | @r0_83(glval:int) | +| IntegerOps(int, int) -> void | 0 | 84 | 0 | @mu0_84(int) | +| IntegerOps(int, int) -> void | 0 | 85 | 0 | @r0_85(glval:int) | +| IntegerOps(int, int) -> void | 0 | 86 | 0 | @r0_86(int) | +| IntegerOps(int, int) -> void | 0 | 87 | 0 | @r0_87(glval:int) | +| IntegerOps(int, int) -> void | 0 | 88 | 0 | @r0_88(int) | +| IntegerOps(int, int) -> void | 0 | 89 | 0 | @r0_89(int) | +| IntegerOps(int, int) -> void | 0 | 90 | 0 | @mu0_90(int) | +| IntegerOps(int, int) -> void | 0 | 91 | 0 | @r0_91(glval:int) | +| IntegerOps(int, int) -> void | 0 | 92 | 0 | @r0_92(int) | +| IntegerOps(int, int) -> void | 0 | 93 | 0 | @r0_93(glval:int) | +| IntegerOps(int, int) -> void | 0 | 94 | 0 | @r0_94(int) | +| IntegerOps(int, int) -> void | 0 | 95 | 0 | @r0_95(int) | +| IntegerOps(int, int) -> void | 0 | 96 | 0 | @mu0_96(int) | +| IntegerOps(int, int) -> void | 0 | 97 | 0 | @r0_97(glval:int) | +| IntegerOps(int, int) -> void | 0 | 98 | 0 | @r0_98(int) | +| IntegerOps(int, int) -> void | 0 | 99 | 0 | @r0_99(glval:int) | +| IntegerOps(int, int) -> void | 0 | 100 | 0 | @r0_100(int) | +| IntegerOps(int, int) -> void | 0 | 101 | 0 | @r0_101(int) | +| IntegerOps(int, int) -> void | 0 | 102 | 0 | @mu0_102(int) | +| IntegerOps(int, int) -> void | 0 | 103 | 0 | @r0_103(glval:int) | +| IntegerOps(int, int) -> void | 0 | 104 | 0 | @r0_104(int) | +| IntegerOps(int, int) -> void | 0 | 105 | 0 | @r0_105(glval:int) | +| IntegerOps(int, int) -> void | 0 | 106 | 0 | @r0_106(int) | +| IntegerOps(int, int) -> void | 0 | 107 | 0 | @r0_107(int) | +| IntegerOps(int, int) -> void | 0 | 108 | 0 | @mu0_108(int) | +| IntegerOps(int, int) -> void | 0 | 109 | 0 | @r0_109(glval:int) | +| IntegerOps(int, int) -> void | 0 | 110 | 0 | @r0_110(int) | +| IntegerOps(int, int) -> void | 0 | 111 | 0 | @r0_111(glval:int) | +| IntegerOps(int, int) -> void | 0 | 112 | 0 | @r0_112(int) | +| IntegerOps(int, int) -> void | 0 | 113 | 0 | @r0_113(int) | +| IntegerOps(int, int) -> void | 0 | 114 | 0 | @mu0_114(int) | +| IntegerOps(int, int) -> void | 0 | 115 | 0 | @r0_115(glval:int) | +| IntegerOps(int, int) -> void | 0 | 116 | 0 | @r0_116(int) | +| IntegerOps(int, int) -> void | 0 | 117 | 0 | @r0_117(glval:int) | +| IntegerOps(int, int) -> void | 0 | 118 | 0 | @r0_118(int) | +| IntegerOps(int, int) -> void | 0 | 119 | 0 | @r0_119(int) | +| IntegerOps(int, int) -> void | 0 | 120 | 0 | @mu0_120(int) | +| IntegerOps(int, int) -> void | 0 | 121 | 0 | @r0_121(glval:int) | +| IntegerOps(int, int) -> void | 0 | 122 | 0 | @r0_122(int) | +| IntegerOps(int, int) -> void | 0 | 123 | 0 | @r0_123(glval:int) | +| IntegerOps(int, int) -> void | 0 | 124 | 0 | @r0_124(int) | +| IntegerOps(int, int) -> void | 0 | 125 | 0 | @r0_125(int) | +| IntegerOps(int, int) -> void | 0 | 126 | 0 | @mu0_126(int) | +| IntegerOps(int, int) -> void | 0 | 127 | 0 | @r0_127(glval:int) | +| IntegerOps(int, int) -> void | 0 | 128 | 0 | @r0_128(int) | +| IntegerOps(int, int) -> void | 0 | 129 | 0 | @r0_129(glval:int) | +| IntegerOps(int, int) -> void | 0 | 130 | 0 | @r0_130(int) | +| IntegerOps(int, int) -> void | 0 | 131 | 0 | @r0_131(int) | +| IntegerOps(int, int) -> void | 0 | 132 | 0 | @mu0_132(int) | +| IntegerOps(int, int) -> void | 0 | 133 | 0 | @r0_133(glval:int) | +| IntegerOps(int, int) -> void | 0 | 134 | 0 | @r0_134(int) | +| IntegerOps(int, int) -> void | 0 | 135 | 0 | @r0_135(glval:int) | +| IntegerOps(int, int) -> void | 0 | 136 | 0 | @r0_136(int) | +| IntegerOps(int, int) -> void | 0 | 137 | 0 | @r0_137(int) | +| IntegerOps(int, int) -> void | 0 | 138 | 0 | @mu0_138(int) | +| IntegerOps(int, int) -> void | 0 | 139 | 0 | @r0_139(glval:int) | +| IntegerOps(int, int) -> void | 0 | 140 | 0 | @r0_140(int) | +| IntegerOps(int, int) -> void | 0 | 141 | 0 | @r0_141(glval:int) | +| IntegerOps(int, int) -> void | 0 | 142 | 0 | @r0_142(int) | +| IntegerOps(int, int) -> void | 0 | 143 | 0 | @r0_143(int) | +| IntegerOps(int, int) -> void | 0 | 144 | 0 | @mu0_144(int) | +| IntegerOps(int, int) -> void | 0 | 145 | 0 | @r0_145(glval:int) | +| IntegerOps(int, int) -> void | 0 | 146 | 0 | @r0_146(int) | +| IntegerOps(int, int) -> void | 0 | 147 | 0 | @r0_147(int) | +| IntegerOps(int, int) -> void | 0 | 148 | 0 | @r0_148(glval:int) | +| IntegerOps(int, int) -> void | 0 | 149 | 0 | @mu0_149(int) | +| IntegerOps(int, int) -> void | 0 | 150 | 0 | @r0_150(glval:int) | +| IntegerOps(int, int) -> void | 0 | 151 | 0 | @r0_151(int) | +| IntegerOps(int, int) -> void | 0 | 152 | 0 | @r0_152(int) | +| IntegerOps(int, int) -> void | 0 | 153 | 0 | @r0_153(glval:int) | +| IntegerOps(int, int) -> void | 0 | 154 | 0 | @mu0_154(int) | +| IntegerOps(int, int) -> void | 0 | 155 | 0 | @r0_155(glval:int) | +| IntegerOps(int, int) -> void | 0 | 156 | 0 | @r0_156(int) | +| IntegerOps(int, int) -> void | 0 | 157 | 0 | @r0_157(int) | +| IntegerOps(int, int) -> void | 0 | 158 | 0 | @r0_158(glval:int) | +| IntegerOps(int, int) -> void | 0 | 159 | 0 | @mu0_159(int) | +| IntegerOps(int, int) -> void | 0 | 160 | 0 | @r0_160(glval:int) | +| IntegerOps(int, int) -> void | 0 | 161 | 0 | @r0_161(int) | +| IntegerOps(int, int) -> void | 0 | 162 | 0 | @r0_162(int) | +| IntegerOps(int, int) -> void | 0 | 163 | 0 | @r0_163(bool) | +| IntegerOps(int, int) -> void | 0 | 164 | 0 | @r0_164(bool) | +| IntegerOps(int, int) -> void | 0 | 165 | 0 | @r0_165(int) | +| IntegerOps(int, int) -> void | 0 | 166 | 0 | @r0_166(glval:int) | +| IntegerOps(int, int) -> void | 0 | 167 | 0 | @mu0_167(int) | +| LogicalAnd(bool, bool) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| LogicalAnd(bool, bool) -> void | 0 | 2 | 0 | @r0_2(bool) | +| LogicalAnd(bool, bool) -> void | 0 | 3 | 0 | @r0_3(glval:bool) | +| LogicalAnd(bool, bool) -> void | 0 | 4 | 0 | @mu0_4(bool) | +| LogicalAnd(bool, bool) -> void | 0 | 5 | 0 | @r0_5(bool) | +| LogicalAnd(bool, bool) -> void | 0 | 6 | 0 | @r0_6(glval:bool) | +| LogicalAnd(bool, bool) -> void | 0 | 7 | 0 | @mu0_7(bool) | +| LogicalAnd(bool, bool) -> void | 0 | 8 | 0 | @r0_8(glval:int) | +| LogicalAnd(bool, bool) -> void | 0 | 9 | 0 | @r0_9(int) | +| LogicalAnd(bool, bool) -> void | 0 | 10 | 0 | @mu0_10(int) | +| LogicalAnd(bool, bool) -> void | 0 | 11 | 0 | @r0_11(glval:bool) | +| LogicalAnd(bool, bool) -> void | 0 | 12 | 0 | @r0_12(bool) | +| LogicalAnd(bool, bool) -> void | 1 | 0 | 0 | @r1_0(glval:bool) | +| LogicalAnd(bool, bool) -> void | 1 | 1 | 0 | @r1_1(bool) | +| LogicalAnd(bool, bool) -> void | 2 | 0 | 0 | @r2_0(int) | +| LogicalAnd(bool, bool) -> void | 2 | 1 | 0 | @r2_1(glval:int) | +| LogicalAnd(bool, bool) -> void | 2 | 2 | 0 | @mu2_2(int) | +| LogicalAnd(bool, bool) -> void | 3 | 0 | 0 | @r3_0(glval:bool) | +| LogicalAnd(bool, bool) -> void | 3 | 1 | 0 | @r3_1(bool) | +| LogicalAnd(bool, bool) -> void | 4 | 0 | 0 | @r4_0(glval:bool) | +| LogicalAnd(bool, bool) -> void | 4 | 1 | 0 | @r4_1(bool) | +| LogicalAnd(bool, bool) -> void | 5 | 0 | 0 | @r5_0(int) | +| LogicalAnd(bool, bool) -> void | 5 | 1 | 0 | @r5_1(glval:int) | +| LogicalAnd(bool, bool) -> void | 5 | 2 | 0 | @mu5_2(int) | +| LogicalAnd(bool, bool) -> void | 6 | 0 | 0 | @r6_0(int) | +| LogicalAnd(bool, bool) -> void | 6 | 1 | 0 | @r6_1(glval:int) | +| LogicalAnd(bool, bool) -> void | 6 | 2 | 0 | @mu6_2(int) | +| LogicalNot(bool, bool) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| LogicalNot(bool, bool) -> void | 0 | 2 | 0 | @r0_2(bool) | +| LogicalNot(bool, bool) -> void | 0 | 3 | 0 | @r0_3(glval:bool) | +| LogicalNot(bool, bool) -> void | 0 | 4 | 0 | @mu0_4(bool) | +| LogicalNot(bool, bool) -> void | 0 | 5 | 0 | @r0_5(bool) | +| LogicalNot(bool, bool) -> void | 0 | 6 | 0 | @r0_6(glval:bool) | +| LogicalNot(bool, bool) -> void | 0 | 7 | 0 | @mu0_7(bool) | +| LogicalNot(bool, bool) -> void | 0 | 8 | 0 | @r0_8(glval:int) | +| LogicalNot(bool, bool) -> void | 0 | 9 | 0 | @r0_9(int) | +| LogicalNot(bool, bool) -> void | 0 | 10 | 0 | @mu0_10(int) | +| LogicalNot(bool, bool) -> void | 0 | 11 | 0 | @r0_11(glval:bool) | +| LogicalNot(bool, bool) -> void | 0 | 12 | 0 | @r0_12(bool) | +| LogicalNot(bool, bool) -> void | 1 | 0 | 0 | @r1_0(int) | +| LogicalNot(bool, bool) -> void | 1 | 1 | 0 | @r1_1(glval:int) | +| LogicalNot(bool, bool) -> void | 1 | 2 | 0 | @mu1_2(int) | +| LogicalNot(bool, bool) -> void | 2 | 0 | 0 | @r2_0(glval:bool) | +| LogicalNot(bool, bool) -> void | 2 | 1 | 0 | @r2_1(bool) | +| LogicalNot(bool, bool) -> void | 3 | 0 | 0 | @r3_0(glval:bool) | +| LogicalNot(bool, bool) -> void | 3 | 1 | 0 | @r3_1(bool) | +| LogicalNot(bool, bool) -> void | 4 | 0 | 0 | @r4_0(int) | +| LogicalNot(bool, bool) -> void | 4 | 1 | 0 | @r4_1(glval:int) | +| LogicalNot(bool, bool) -> void | 4 | 2 | 0 | @mu4_2(int) | +| LogicalNot(bool, bool) -> void | 5 | 0 | 0 | @r5_0(int) | +| LogicalNot(bool, bool) -> void | 5 | 1 | 0 | @r5_1(glval:int) | +| LogicalNot(bool, bool) -> void | 5 | 2 | 0 | @mu5_2(int) | +| LogicalOr(bool, bool) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| LogicalOr(bool, bool) -> void | 0 | 2 | 0 | @r0_2(bool) | +| LogicalOr(bool, bool) -> void | 0 | 3 | 0 | @r0_3(glval:bool) | +| LogicalOr(bool, bool) -> void | 0 | 4 | 0 | @mu0_4(bool) | +| LogicalOr(bool, bool) -> void | 0 | 5 | 0 | @r0_5(bool) | +| LogicalOr(bool, bool) -> void | 0 | 6 | 0 | @r0_6(glval:bool) | +| LogicalOr(bool, bool) -> void | 0 | 7 | 0 | @mu0_7(bool) | +| LogicalOr(bool, bool) -> void | 0 | 8 | 0 | @r0_8(glval:int) | +| LogicalOr(bool, bool) -> void | 0 | 9 | 0 | @r0_9(int) | +| LogicalOr(bool, bool) -> void | 0 | 10 | 0 | @mu0_10(int) | +| LogicalOr(bool, bool) -> void | 0 | 11 | 0 | @r0_11(glval:bool) | +| LogicalOr(bool, bool) -> void | 0 | 12 | 0 | @r0_12(bool) | +| LogicalOr(bool, bool) -> void | 1 | 0 | 0 | @r1_0(glval:bool) | +| LogicalOr(bool, bool) -> void | 1 | 1 | 0 | @r1_1(bool) | +| LogicalOr(bool, bool) -> void | 2 | 0 | 0 | @r2_0(int) | +| LogicalOr(bool, bool) -> void | 2 | 1 | 0 | @r2_1(glval:int) | +| LogicalOr(bool, bool) -> void | 2 | 2 | 0 | @mu2_2(int) | +| LogicalOr(bool, bool) -> void | 3 | 0 | 0 | @r3_0(glval:bool) | +| LogicalOr(bool, bool) -> void | 3 | 1 | 0 | @r3_1(bool) | +| LogicalOr(bool, bool) -> void | 4 | 0 | 0 | @r4_0(glval:bool) | +| LogicalOr(bool, bool) -> void | 4 | 1 | 0 | @r4_1(bool) | +| LogicalOr(bool, bool) -> void | 5 | 0 | 0 | @r5_0(int) | +| LogicalOr(bool, bool) -> void | 5 | 1 | 0 | @r5_1(glval:int) | +| LogicalOr(bool, bool) -> void | 5 | 2 | 0 | @mu5_2(int) | +| LogicalOr(bool, bool) -> void | 6 | 0 | 0 | @r6_0(int) | +| LogicalOr(bool, bool) -> void | 6 | 1 | 0 | @r6_1(glval:int) | +| LogicalOr(bool, bool) -> void | 6 | 2 | 0 | @mu6_2(int) | +| Middle::Middle() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Middle::Middle() -> void | 0 | 2 | 0 | @r0_2(glval:Middle) | +| Middle::Middle() -> void | 0 | 3 | 0 | @r0_3(glval:Base) | +| Middle::Middle() -> void | 0 | 4 | 0 | @r0_4(bool) | +| Middle::Middle() -> void | 0 | 6 | 0 | @r0_6(glval:String) | +| Middle::Middle() -> void | 0 | 7 | 0 | @r0_7(bool) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 1 | 0 | @mu0_1(unknown) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 2 | 0 | @r0_2(glval:Middle) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 3 | 0 | @r0_3(Middle &) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 4 | 0 | @r0_4(glval:Middle &) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 5 | 0 | @mu0_5(Middle &) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 6 | 0 | @r0_6(Middle *) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 7 | 0 | @r0_7(Base *) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 8 | 0 | @r0_8(bool) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 9 | 0 | @r0_9(glval:Middle &) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 10 | 0 | @r0_10(Middle &) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 11 | 0 | @r0_11(Base *) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 12 | 0 | @r0_12(Base &) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 13 | 0 | @r0_13(Middle *) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 14 | 0 | @r0_14(glval:String) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 15 | 0 | @r0_15(bool) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 16 | 0 | @r0_16(glval:Middle &) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 17 | 0 | @r0_17(Middle &) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 18 | 0 | @r0_18(glval:String) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 19 | 0 | @r0_19(String &) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 20 | 0 | @r0_20(glval:Middle &) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 21 | 0 | @r0_21(Middle *) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 22 | 0 | @mu0_22(Middle &) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 23 | 0 | @r0_23(glval:Middle &) | +| Middle::~Middle() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Middle::~Middle() -> void | 0 | 2 | 0 | @r0_2(glval:Middle) | +| Middle::~Middle() -> void | 0 | 4 | 0 | @r0_4(glval:String) | +| Middle::~Middle() -> void | 0 | 5 | 0 | @r0_5(bool) | +| Middle::~Middle() -> void | 0 | 7 | 0 | @r0_7(glval:Base) | +| Middle::~Middle() -> void | 0 | 8 | 0 | @r0_8(bool) | +| MiddleVB1::MiddleVB1() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| MiddleVB1::MiddleVB1() -> void | 0 | 2 | 0 | @r0_2(glval:MiddleVB1) | +| MiddleVB1::MiddleVB1() -> void | 0 | 3 | 0 | @r0_3(glval:Base) | +| MiddleVB1::MiddleVB1() -> void | 0 | 4 | 0 | @r0_4(bool) | +| MiddleVB1::MiddleVB1() -> void | 0 | 6 | 0 | @r0_6(glval:String) | +| MiddleVB1::MiddleVB1() -> void | 0 | 7 | 0 | @r0_7(bool) | +| MiddleVB1::~MiddleVB1() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| MiddleVB1::~MiddleVB1() -> void | 0 | 2 | 0 | @r0_2(glval:MiddleVB1) | +| MiddleVB1::~MiddleVB1() -> void | 0 | 4 | 0 | @r0_4(glval:String) | +| MiddleVB1::~MiddleVB1() -> void | 0 | 5 | 0 | @r0_5(bool) | +| MiddleVB1::~MiddleVB1() -> void | 0 | 7 | 0 | @r0_7(glval:Base) | +| MiddleVB1::~MiddleVB1() -> void | 0 | 8 | 0 | @r0_8(bool) | +| MiddleVB2::MiddleVB2() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| MiddleVB2::MiddleVB2() -> void | 0 | 2 | 0 | @r0_2(glval:MiddleVB2) | +| MiddleVB2::MiddleVB2() -> void | 0 | 3 | 0 | @r0_3(glval:Base) | +| MiddleVB2::MiddleVB2() -> void | 0 | 4 | 0 | @r0_4(bool) | +| MiddleVB2::MiddleVB2() -> void | 0 | 6 | 0 | @r0_6(glval:String) | +| MiddleVB2::MiddleVB2() -> void | 0 | 7 | 0 | @r0_7(bool) | +| MiddleVB2::~MiddleVB2() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| MiddleVB2::~MiddleVB2() -> void | 0 | 2 | 0 | @r0_2(glval:MiddleVB2) | +| MiddleVB2::~MiddleVB2() -> void | 0 | 4 | 0 | @r0_4(glval:String) | +| MiddleVB2::~MiddleVB2() -> void | 0 | 5 | 0 | @r0_5(bool) | +| MiddleVB2::~MiddleVB2() -> void | 0 | 7 | 0 | @r0_7(glval:Base) | +| MiddleVB2::~MiddleVB2() -> void | 0 | 8 | 0 | @r0_8(bool) | +| NestedInitList(int, float) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| NestedInitList(int, float) -> void | 0 | 2 | 0 | @r0_2(int) | +| NestedInitList(int, float) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| NestedInitList(int, float) -> void | 0 | 4 | 0 | @mu0_4(int) | +| NestedInitList(int, float) -> void | 0 | 5 | 0 | @r0_5(float) | +| NestedInitList(int, float) -> void | 0 | 6 | 0 | @r0_6(glval:float) | +| NestedInitList(int, float) -> void | 0 | 7 | 0 | @mu0_7(float) | +| NestedInitList(int, float) -> void | 0 | 8 | 0 | @r0_8(glval:Rect) | +| NestedInitList(int, float) -> void | 0 | 9 | 0 | @r0_9(glval:Point) | +| NestedInitList(int, float) -> void | 0 | 10 | 0 | @r0_10(Point) | +| NestedInitList(int, float) -> void | 0 | 11 | 0 | @mu0_11(Point) | +| NestedInitList(int, float) -> void | 0 | 12 | 0 | @r0_12(glval:Point) | +| NestedInitList(int, float) -> void | 0 | 13 | 0 | @r0_13(Point) | +| NestedInitList(int, float) -> void | 0 | 14 | 0 | @mu0_14(Point) | +| NestedInitList(int, float) -> void | 0 | 15 | 0 | @r0_15(glval:Rect) | +| NestedInitList(int, float) -> void | 0 | 16 | 0 | @r0_16(glval:Point) | +| NestedInitList(int, float) -> void | 0 | 17 | 0 | @r0_17(glval:int) | +| NestedInitList(int, float) -> void | 0 | 18 | 0 | @r0_18(glval:int) | +| NestedInitList(int, float) -> void | 0 | 19 | 0 | @r0_19(int) | +| NestedInitList(int, float) -> void | 0 | 20 | 0 | @mu0_20(int) | +| NestedInitList(int, float) -> void | 0 | 21 | 0 | @r0_21(glval:int) | +| NestedInitList(int, float) -> void | 0 | 22 | 0 | @r0_22(glval:float) | +| NestedInitList(int, float) -> void | 0 | 23 | 0 | @r0_23(float) | +| NestedInitList(int, float) -> void | 0 | 24 | 0 | @r0_24(int) | +| NestedInitList(int, float) -> void | 0 | 25 | 0 | @mu0_25(int) | +| NestedInitList(int, float) -> void | 0 | 26 | 0 | @r0_26(glval:Point) | +| NestedInitList(int, float) -> void | 0 | 27 | 0 | @r0_27(Point) | +| NestedInitList(int, float) -> void | 0 | 28 | 0 | @mu0_28(Point) | +| NestedInitList(int, float) -> void | 0 | 29 | 0 | @r0_29(glval:Rect) | +| NestedInitList(int, float) -> void | 0 | 30 | 0 | @r0_30(glval:Point) | +| NestedInitList(int, float) -> void | 0 | 31 | 0 | @r0_31(glval:int) | +| NestedInitList(int, float) -> void | 0 | 32 | 0 | @r0_32(glval:int) | +| NestedInitList(int, float) -> void | 0 | 33 | 0 | @r0_33(int) | +| NestedInitList(int, float) -> void | 0 | 34 | 0 | @mu0_34(int) | +| NestedInitList(int, float) -> void | 0 | 35 | 0 | @r0_35(glval:int) | +| NestedInitList(int, float) -> void | 0 | 36 | 0 | @r0_36(glval:float) | +| NestedInitList(int, float) -> void | 0 | 37 | 0 | @r0_37(float) | +| NestedInitList(int, float) -> void | 0 | 38 | 0 | @r0_38(int) | +| NestedInitList(int, float) -> void | 0 | 39 | 0 | @mu0_39(int) | +| NestedInitList(int, float) -> void | 0 | 40 | 0 | @r0_40(glval:Point) | +| NestedInitList(int, float) -> void | 0 | 41 | 0 | @r0_41(glval:int) | +| NestedInitList(int, float) -> void | 0 | 42 | 0 | @r0_42(glval:int) | +| NestedInitList(int, float) -> void | 0 | 43 | 0 | @r0_43(int) | +| NestedInitList(int, float) -> void | 0 | 44 | 0 | @mu0_44(int) | +| NestedInitList(int, float) -> void | 0 | 45 | 0 | @r0_45(glval:int) | +| NestedInitList(int, float) -> void | 0 | 46 | 0 | @r0_46(glval:float) | +| NestedInitList(int, float) -> void | 0 | 47 | 0 | @r0_47(float) | +| NestedInitList(int, float) -> void | 0 | 48 | 0 | @r0_48(int) | +| NestedInitList(int, float) -> void | 0 | 49 | 0 | @mu0_49(int) | +| NestedInitList(int, float) -> void | 0 | 50 | 0 | @r0_50(glval:Rect) | +| NestedInitList(int, float) -> void | 0 | 51 | 0 | @r0_51(glval:Point) | +| NestedInitList(int, float) -> void | 0 | 52 | 0 | @r0_52(glval:int) | +| NestedInitList(int, float) -> void | 0 | 53 | 0 | @r0_53(glval:int) | +| NestedInitList(int, float) -> void | 0 | 54 | 0 | @r0_54(int) | +| NestedInitList(int, float) -> void | 0 | 55 | 0 | @mu0_55(int) | +| NestedInitList(int, float) -> void | 0 | 56 | 0 | @r0_56(glval:int) | +| NestedInitList(int, float) -> void | 0 | 57 | 0 | @r0_57(int) | +| NestedInitList(int, float) -> void | 0 | 58 | 0 | @mu0_58(int) | +| NestedInitList(int, float) -> void | 0 | 59 | 0 | @r0_59(glval:Point) | +| NestedInitList(int, float) -> void | 0 | 60 | 0 | @r0_60(glval:int) | +| NestedInitList(int, float) -> void | 0 | 61 | 0 | @r0_61(glval:int) | +| NestedInitList(int, float) -> void | 0 | 62 | 0 | @r0_62(int) | +| NestedInitList(int, float) -> void | 0 | 63 | 0 | @mu0_63(int) | +| NestedInitList(int, float) -> void | 0 | 64 | 0 | @r0_64(glval:int) | +| NestedInitList(int, float) -> void | 0 | 65 | 0 | @r0_65(int) | +| NestedInitList(int, float) -> void | 0 | 66 | 0 | @mu0_66(int) | +| Nullptr() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Nullptr() -> void | 0 | 2 | 0 | @r0_2(glval:int *) | +| Nullptr() -> void | 0 | 3 | 0 | @r0_3(int *) | +| Nullptr() -> void | 0 | 4 | 0 | @mu0_4(int *) | +| Nullptr() -> void | 0 | 5 | 0 | @r0_5(glval:int *) | +| Nullptr() -> void | 0 | 6 | 0 | @r0_6(int *) | +| Nullptr() -> void | 0 | 7 | 0 | @mu0_7(int *) | +| Nullptr() -> void | 0 | 8 | 0 | @r0_8(int *) | +| Nullptr() -> void | 0 | 9 | 0 | @r0_9(glval:int *) | +| Nullptr() -> void | 0 | 10 | 0 | @mu0_10(int *) | +| Nullptr() -> void | 0 | 11 | 0 | @r0_11(int *) | +| Nullptr() -> void | 0 | 12 | 0 | @r0_12(glval:int *) | +| Nullptr() -> void | 0 | 13 | 0 | @mu0_13(int *) | +| Outer::Func(void *, char) -> long | 0 | 1 | 0 | @mu0_1(unknown) | +| Outer::Func(void *, char) -> long | 0 | 2 | 0 | @r0_2(void *) | +| Outer::Func(void *, char) -> long | 0 | 3 | 0 | @r0_3(glval:void *) | +| Outer::Func(void *, char) -> long | 0 | 4 | 0 | @mu0_4(void *) | +| Outer::Func(void *, char) -> long | 0 | 5 | 0 | @r0_5(char) | +| Outer::Func(void *, char) -> long | 0 | 6 | 0 | @r0_6(glval:char) | +| Outer::Func(void *, char) -> long | 0 | 7 | 0 | @mu0_7(char) | +| Outer::Func(void *, char) -> long | 0 | 8 | 0 | @r0_8(glval:long) | +| Outer::Func(void *, char) -> long | 0 | 9 | 0 | @r0_9(long) | +| Outer::Func(void *, char) -> long | 0 | 10 | 0 | @mu0_10(long) | +| Outer::Func(void *, char) -> long | 0 | 11 | 0 | @r0_11(glval:long) | +| Parameters(int, int) -> int | 0 | 1 | 0 | @mu0_1(unknown) | +| Parameters(int, int) -> int | 0 | 2 | 0 | @r0_2(int) | +| Parameters(int, int) -> int | 0 | 3 | 0 | @r0_3(glval:int) | +| Parameters(int, int) -> int | 0 | 4 | 0 | @mu0_4(int) | +| Parameters(int, int) -> int | 0 | 5 | 0 | @r0_5(int) | +| Parameters(int, int) -> int | 0 | 6 | 0 | @r0_6(glval:int) | +| Parameters(int, int) -> int | 0 | 7 | 0 | @mu0_7(int) | +| Parameters(int, int) -> int | 0 | 8 | 0 | @r0_8(glval:int) | +| Parameters(int, int) -> int | 0 | 9 | 0 | @r0_9(glval:int) | +| Parameters(int, int) -> int | 0 | 10 | 0 | @r0_10(int) | +| Parameters(int, int) -> int | 0 | 11 | 0 | @r0_11(glval:int) | +| Parameters(int, int) -> int | 0 | 12 | 0 | @r0_12(int) | +| Parameters(int, int) -> int | 0 | 13 | 0 | @r0_13(int) | +| Parameters(int, int) -> int | 0 | 14 | 0 | @mu0_14(int) | +| Parameters(int, int) -> int | 0 | 15 | 0 | @r0_15(glval:int) | +| PointerCompare(int *, int *) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| PointerCompare(int *, int *) -> void | 0 | 2 | 0 | @r0_2(int *) | +| PointerCompare(int *, int *) -> void | 0 | 3 | 0 | @r0_3(glval:int *) | +| PointerCompare(int *, int *) -> void | 0 | 4 | 0 | @mu0_4(int *) | +| PointerCompare(int *, int *) -> void | 0 | 5 | 0 | @r0_5(int *) | +| PointerCompare(int *, int *) -> void | 0 | 6 | 0 | @r0_6(glval:int *) | +| PointerCompare(int *, int *) -> void | 0 | 7 | 0 | @mu0_7(int *) | +| PointerCompare(int *, int *) -> void | 0 | 8 | 0 | @r0_8(glval:bool) | +| PointerCompare(int *, int *) -> void | 0 | 9 | 0 | @r0_9(bool) | +| PointerCompare(int *, int *) -> void | 0 | 10 | 0 | @mu0_10(bool) | +| PointerCompare(int *, int *) -> void | 0 | 11 | 0 | @r0_11(glval:int *) | +| PointerCompare(int *, int *) -> void | 0 | 12 | 0 | @r0_12(int *) | +| PointerCompare(int *, int *) -> void | 0 | 13 | 0 | @r0_13(glval:int *) | +| PointerCompare(int *, int *) -> void | 0 | 14 | 0 | @r0_14(int *) | +| PointerCompare(int *, int *) -> void | 0 | 15 | 0 | @r0_15(bool) | +| PointerCompare(int *, int *) -> void | 0 | 16 | 0 | @r0_16(glval:bool) | +| PointerCompare(int *, int *) -> void | 0 | 17 | 0 | @mu0_17(bool) | +| PointerCompare(int *, int *) -> void | 0 | 18 | 0 | @r0_18(glval:int *) | +| PointerCompare(int *, int *) -> void | 0 | 19 | 0 | @r0_19(int *) | +| PointerCompare(int *, int *) -> void | 0 | 20 | 0 | @r0_20(glval:int *) | +| PointerCompare(int *, int *) -> void | 0 | 21 | 0 | @r0_21(int *) | +| PointerCompare(int *, int *) -> void | 0 | 22 | 0 | @r0_22(bool) | +| PointerCompare(int *, int *) -> void | 0 | 23 | 0 | @r0_23(glval:bool) | +| PointerCompare(int *, int *) -> void | 0 | 24 | 0 | @mu0_24(bool) | +| PointerCompare(int *, int *) -> void | 0 | 25 | 0 | @r0_25(glval:int *) | +| PointerCompare(int *, int *) -> void | 0 | 26 | 0 | @r0_26(int *) | +| PointerCompare(int *, int *) -> void | 0 | 27 | 0 | @r0_27(glval:int *) | +| PointerCompare(int *, int *) -> void | 0 | 28 | 0 | @r0_28(int *) | +| PointerCompare(int *, int *) -> void | 0 | 29 | 0 | @r0_29(bool) | +| PointerCompare(int *, int *) -> void | 0 | 30 | 0 | @r0_30(glval:bool) | +| PointerCompare(int *, int *) -> void | 0 | 31 | 0 | @mu0_31(bool) | +| PointerCompare(int *, int *) -> void | 0 | 32 | 0 | @r0_32(glval:int *) | +| PointerCompare(int *, int *) -> void | 0 | 33 | 0 | @r0_33(int *) | +| PointerCompare(int *, int *) -> void | 0 | 34 | 0 | @r0_34(glval:int *) | +| PointerCompare(int *, int *) -> void | 0 | 35 | 0 | @r0_35(int *) | +| PointerCompare(int *, int *) -> void | 0 | 36 | 0 | @r0_36(bool) | +| PointerCompare(int *, int *) -> void | 0 | 37 | 0 | @r0_37(glval:bool) | +| PointerCompare(int *, int *) -> void | 0 | 38 | 0 | @mu0_38(bool) | +| PointerCompare(int *, int *) -> void | 0 | 39 | 0 | @r0_39(glval:int *) | +| PointerCompare(int *, int *) -> void | 0 | 40 | 0 | @r0_40(int *) | +| PointerCompare(int *, int *) -> void | 0 | 41 | 0 | @r0_41(glval:int *) | +| PointerCompare(int *, int *) -> void | 0 | 42 | 0 | @r0_42(int *) | +| PointerCompare(int *, int *) -> void | 0 | 43 | 0 | @r0_43(bool) | +| PointerCompare(int *, int *) -> void | 0 | 44 | 0 | @r0_44(glval:bool) | +| PointerCompare(int *, int *) -> void | 0 | 45 | 0 | @mu0_45(bool) | +| PointerCompare(int *, int *) -> void | 0 | 46 | 0 | @r0_46(glval:int *) | +| PointerCompare(int *, int *) -> void | 0 | 47 | 0 | @r0_47(int *) | +| PointerCompare(int *, int *) -> void | 0 | 48 | 0 | @r0_48(glval:int *) | +| PointerCompare(int *, int *) -> void | 0 | 49 | 0 | @r0_49(int *) | +| PointerCompare(int *, int *) -> void | 0 | 50 | 0 | @r0_50(bool) | +| PointerCompare(int *, int *) -> void | 0 | 51 | 0 | @r0_51(glval:bool) | +| PointerCompare(int *, int *) -> void | 0 | 52 | 0 | @mu0_52(bool) | +| PointerCrement(int *) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| PointerCrement(int *) -> void | 0 | 2 | 0 | @r0_2(int *) | +| PointerCrement(int *) -> void | 0 | 3 | 0 | @r0_3(glval:int *) | +| PointerCrement(int *) -> void | 0 | 4 | 0 | @mu0_4(int *) | +| PointerCrement(int *) -> void | 0 | 5 | 0 | @r0_5(glval:int *) | +| PointerCrement(int *) -> void | 0 | 6 | 0 | @r0_6(int *) | +| PointerCrement(int *) -> void | 0 | 7 | 0 | @mu0_7(int *) | +| PointerCrement(int *) -> void | 0 | 8 | 0 | @r0_8(glval:int *) | +| PointerCrement(int *) -> void | 0 | 9 | 0 | @r0_9(int *) | +| PointerCrement(int *) -> void | 0 | 10 | 0 | @r0_10(int) | +| PointerCrement(int *) -> void | 0 | 11 | 0 | @r0_11(int *) | +| PointerCrement(int *) -> void | 0 | 12 | 0 | @mu0_12(int *) | +| PointerCrement(int *) -> void | 0 | 13 | 0 | @r0_13(glval:int *) | +| PointerCrement(int *) -> void | 0 | 14 | 0 | @mu0_14(int *) | +| PointerCrement(int *) -> void | 0 | 15 | 0 | @r0_15(glval:int *) | +| PointerCrement(int *) -> void | 0 | 16 | 0 | @r0_16(int *) | +| PointerCrement(int *) -> void | 0 | 17 | 0 | @r0_17(int) | +| PointerCrement(int *) -> void | 0 | 18 | 0 | @r0_18(int *) | +| PointerCrement(int *) -> void | 0 | 19 | 0 | @mu0_19(int *) | +| PointerCrement(int *) -> void | 0 | 20 | 0 | @r0_20(glval:int *) | +| PointerCrement(int *) -> void | 0 | 21 | 0 | @mu0_21(int *) | +| PointerCrement(int *) -> void | 0 | 22 | 0 | @r0_22(glval:int *) | +| PointerCrement(int *) -> void | 0 | 23 | 0 | @r0_23(int *) | +| PointerCrement(int *) -> void | 0 | 24 | 0 | @r0_24(int) | +| PointerCrement(int *) -> void | 0 | 25 | 0 | @r0_25(int *) | +| PointerCrement(int *) -> void | 0 | 26 | 0 | @mu0_26(int *) | +| PointerCrement(int *) -> void | 0 | 27 | 0 | @r0_27(glval:int *) | +| PointerCrement(int *) -> void | 0 | 28 | 0 | @mu0_28(int *) | +| PointerCrement(int *) -> void | 0 | 29 | 0 | @r0_29(glval:int *) | +| PointerCrement(int *) -> void | 0 | 30 | 0 | @r0_30(int *) | +| PointerCrement(int *) -> void | 0 | 31 | 0 | @r0_31(int) | +| PointerCrement(int *) -> void | 0 | 32 | 0 | @r0_32(int *) | +| PointerCrement(int *) -> void | 0 | 33 | 0 | @mu0_33(int *) | +| PointerCrement(int *) -> void | 0 | 34 | 0 | @r0_34(glval:int *) | +| PointerCrement(int *) -> void | 0 | 35 | 0 | @mu0_35(int *) | +| PointerOps(int *, int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| PointerOps(int *, int) -> void | 0 | 2 | 0 | @r0_2(int *) | +| PointerOps(int *, int) -> void | 0 | 3 | 0 | @r0_3(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 4 | 0 | @mu0_4(int *) | +| PointerOps(int *, int) -> void | 0 | 5 | 0 | @r0_5(int) | +| PointerOps(int *, int) -> void | 0 | 6 | 0 | @r0_6(glval:int) | +| PointerOps(int *, int) -> void | 0 | 7 | 0 | @mu0_7(int) | +| PointerOps(int *, int) -> void | 0 | 8 | 0 | @r0_8(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 9 | 0 | @r0_9(int *) | +| PointerOps(int *, int) -> void | 0 | 10 | 0 | @mu0_10(int *) | +| PointerOps(int *, int) -> void | 0 | 11 | 0 | @r0_11(glval:bool) | +| PointerOps(int *, int) -> void | 0 | 12 | 0 | @r0_12(bool) | +| PointerOps(int *, int) -> void | 0 | 13 | 0 | @mu0_13(bool) | +| PointerOps(int *, int) -> void | 0 | 14 | 0 | @r0_14(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 15 | 0 | @r0_15(int *) | +| PointerOps(int *, int) -> void | 0 | 16 | 0 | @r0_16(glval:int) | +| PointerOps(int *, int) -> void | 0 | 17 | 0 | @r0_17(int) | +| PointerOps(int *, int) -> void | 0 | 18 | 0 | @r0_18(int *) | +| PointerOps(int *, int) -> void | 0 | 19 | 0 | @r0_19(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 20 | 0 | @mu0_20(int *) | +| PointerOps(int *, int) -> void | 0 | 21 | 0 | @r0_21(glval:int) | +| PointerOps(int *, int) -> void | 0 | 22 | 0 | @r0_22(int) | +| PointerOps(int *, int) -> void | 0 | 23 | 0 | @r0_23(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 24 | 0 | @r0_24(int *) | +| PointerOps(int *, int) -> void | 0 | 25 | 0 | @r0_25(int *) | +| PointerOps(int *, int) -> void | 0 | 26 | 0 | @r0_26(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 27 | 0 | @mu0_27(int *) | +| PointerOps(int *, int) -> void | 0 | 28 | 0 | @r0_28(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 29 | 0 | @r0_29(int *) | +| PointerOps(int *, int) -> void | 0 | 30 | 0 | @r0_30(glval:int) | +| PointerOps(int *, int) -> void | 0 | 31 | 0 | @r0_31(int) | +| PointerOps(int *, int) -> void | 0 | 32 | 0 | @r0_32(int *) | +| PointerOps(int *, int) -> void | 0 | 33 | 0 | @r0_33(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 34 | 0 | @mu0_34(int *) | +| PointerOps(int *, int) -> void | 0 | 35 | 0 | @r0_35(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 36 | 0 | @r0_36(int *) | +| PointerOps(int *, int) -> void | 0 | 37 | 0 | @r0_37(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 38 | 0 | @r0_38(int *) | +| PointerOps(int *, int) -> void | 0 | 39 | 0 | @r0_39(long) | +| PointerOps(int *, int) -> void | 0 | 40 | 0 | @r0_40(int) | +| PointerOps(int *, int) -> void | 0 | 41 | 0 | @r0_41(glval:int) | +| PointerOps(int *, int) -> void | 0 | 42 | 0 | @mu0_42(int) | +| PointerOps(int *, int) -> void | 0 | 43 | 0 | @r0_43(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 44 | 0 | @r0_44(int *) | +| PointerOps(int *, int) -> void | 0 | 45 | 0 | @r0_45(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 46 | 0 | @mu0_46(int *) | +| PointerOps(int *, int) -> void | 0 | 47 | 0 | @r0_47(glval:int) | +| PointerOps(int *, int) -> void | 0 | 48 | 0 | @r0_48(int) | +| PointerOps(int *, int) -> void | 0 | 49 | 0 | @r0_49(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 50 | 0 | @r0_50(int *) | +| PointerOps(int *, int) -> void | 0 | 51 | 0 | @r0_51(int *) | +| PointerOps(int *, int) -> void | 0 | 52 | 0 | @mu0_52(int *) | +| PointerOps(int *, int) -> void | 0 | 53 | 0 | @r0_53(glval:int) | +| PointerOps(int *, int) -> void | 0 | 54 | 0 | @r0_54(int) | +| PointerOps(int *, int) -> void | 0 | 55 | 0 | @r0_55(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 56 | 0 | @r0_56(int *) | +| PointerOps(int *, int) -> void | 0 | 57 | 0 | @r0_57(int *) | +| PointerOps(int *, int) -> void | 0 | 58 | 0 | @mu0_58(int *) | +| PointerOps(int *, int) -> void | 0 | 59 | 0 | @r0_59(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 60 | 0 | @r0_60(int *) | +| PointerOps(int *, int) -> void | 0 | 61 | 0 | @r0_61(int *) | +| PointerOps(int *, int) -> void | 0 | 62 | 0 | @r0_62(bool) | +| PointerOps(int *, int) -> void | 0 | 63 | 0 | @r0_63(glval:bool) | +| PointerOps(int *, int) -> void | 0 | 64 | 0 | @mu0_64(bool) | +| PointerOps(int *, int) -> void | 0 | 65 | 0 | @r0_65(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 66 | 0 | @r0_66(int *) | +| PointerOps(int *, int) -> void | 0 | 67 | 0 | @r0_67(int *) | +| PointerOps(int *, int) -> void | 0 | 68 | 0 | @r0_68(bool) | +| PointerOps(int *, int) -> void | 0 | 69 | 0 | @r0_69(bool) | +| PointerOps(int *, int) -> void | 0 | 70 | 0 | @r0_70(glval:bool) | +| PointerOps(int *, int) -> void | 0 | 71 | 0 | @mu0_71(bool) | +| PolymorphicBase::PolymorphicBase() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| PolymorphicBase::PolymorphicBase() -> void | 0 | 2 | 0 | @r0_2(glval:PolymorphicBase) | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 2 | 0 | @r0_2(glval:PolymorphicDerived) | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 3 | 0 | @r0_3(glval:PolymorphicBase) | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 4 | 0 | @r0_4(bool) | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 2 | 0 | @r0_2(glval:PolymorphicDerived) | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 4 | 0 | @r0_4(glval:PolymorphicBase) | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 5 | 0 | @r0_5(bool) | +| ReturnStruct(Point) -> Point | 0 | 1 | 0 | @mu0_1(unknown) | +| ReturnStruct(Point) -> Point | 0 | 2 | 0 | @r0_2(Point) | +| ReturnStruct(Point) -> Point | 0 | 3 | 0 | @r0_3(glval:Point) | +| ReturnStruct(Point) -> Point | 0 | 4 | 0 | @mu0_4(Point) | +| ReturnStruct(Point) -> Point | 0 | 5 | 0 | @r0_5(glval:Point) | +| ReturnStruct(Point) -> Point | 0 | 6 | 0 | @r0_6(glval:Point) | +| ReturnStruct(Point) -> Point | 0 | 7 | 0 | @r0_7(Point) | +| ReturnStruct(Point) -> Point | 0 | 8 | 0 | @mu0_8(Point) | +| ReturnStruct(Point) -> Point | 0 | 9 | 0 | @r0_9(glval:Point) | +| SetFuncPtr() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| SetFuncPtr() -> void | 0 | 2 | 0 | @r0_2(glval:..(*)(..)) | +| SetFuncPtr() -> void | 0 | 3 | 0 | @r0_3(glval:..(*)(..)) | +| SetFuncPtr() -> void | 0 | 4 | 0 | @mu0_4(..(*)(..)) | +| SetFuncPtr() -> void | 0 | 5 | 0 | @r0_5(glval:..()(..)) | +| SetFuncPtr() -> void | 0 | 6 | 0 | @r0_6(glval:..(*)(..)) | +| SetFuncPtr() -> void | 0 | 7 | 0 | @mu0_7(..(*)(..)) | +| SetFuncPtr() -> void | 0 | 8 | 0 | @r0_8(glval:..(*)(..)) | +| SetFuncPtr() -> void | 0 | 9 | 0 | @r0_9(glval:..(*)(..)) | +| SetFuncPtr() -> void | 0 | 10 | 0 | @mu0_10(..(*)(..)) | +| SetFuncPtr() -> void | 0 | 11 | 0 | @r0_11(glval:..()(..)) | +| SetFuncPtr() -> void | 0 | 12 | 0 | @r0_12(glval:..(*)(..)) | +| SetFuncPtr() -> void | 0 | 13 | 0 | @mu0_13(..(*)(..)) | +| String::String() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| String::String() -> void | 0 | 2 | 0 | @r0_2(glval:String) | +| String::String() -> void | 0 | 3 | 0 | @r0_3(bool) | +| String::String() -> void | 0 | 4 | 0 | @r0_4(glval:char[1]) | +| String::String() -> void | 0 | 5 | 0 | @r0_5(char *) | +| StringLiteral(int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| StringLiteral(int) -> void | 0 | 2 | 0 | @r0_2(int) | +| StringLiteral(int) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| StringLiteral(int) -> void | 0 | 4 | 0 | @mu0_4(int) | +| StringLiteral(int) -> void | 0 | 5 | 0 | @r0_5(glval:char) | +| StringLiteral(int) -> void | 0 | 6 | 0 | @r0_6(glval:char[4]) | +| StringLiteral(int) -> void | 0 | 7 | 0 | @r0_7(char *) | +| StringLiteral(int) -> void | 0 | 8 | 0 | @r0_8(glval:int) | +| StringLiteral(int) -> void | 0 | 9 | 0 | @r0_9(int) | +| StringLiteral(int) -> void | 0 | 10 | 0 | @r0_10(char *) | +| StringLiteral(int) -> void | 0 | 11 | 0 | @r0_11(char) | +| StringLiteral(int) -> void | 0 | 12 | 0 | @mu0_12(char) | +| StringLiteral(int) -> void | 0 | 13 | 0 | @r0_13(glval:wchar_t *) | +| StringLiteral(int) -> void | 0 | 14 | 0 | @r0_14(glval:wchar_t[4]) | +| StringLiteral(int) -> void | 0 | 15 | 0 | @r0_15(wchar_t *) | +| StringLiteral(int) -> void | 0 | 16 | 0 | @r0_16(wchar_t *) | +| StringLiteral(int) -> void | 0 | 17 | 0 | @mu0_17(wchar_t *) | +| StringLiteral(int) -> void | 0 | 18 | 0 | @r0_18(glval:wchar_t) | +| StringLiteral(int) -> void | 0 | 19 | 0 | @r0_19(glval:wchar_t *) | +| StringLiteral(int) -> void | 0 | 20 | 0 | @r0_20(wchar_t *) | +| StringLiteral(int) -> void | 0 | 21 | 0 | @r0_21(glval:int) | +| StringLiteral(int) -> void | 0 | 22 | 0 | @r0_22(int) | +| StringLiteral(int) -> void | 0 | 23 | 0 | @r0_23(wchar_t *) | +| StringLiteral(int) -> void | 0 | 24 | 0 | @r0_24(wchar_t) | +| StringLiteral(int) -> void | 0 | 25 | 0 | @mu0_25(wchar_t) | +| Switch(int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Switch(int) -> void | 0 | 2 | 0 | @r0_2(int) | +| Switch(int) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| Switch(int) -> void | 0 | 4 | 0 | @mu0_4(int) | +| Switch(int) -> void | 0 | 5 | 0 | @r0_5(glval:int) | +| Switch(int) -> void | 0 | 6 | 0 | @r0_6(int) | +| Switch(int) -> void | 0 | 7 | 0 | @mu0_7(int) | +| Switch(int) -> void | 0 | 8 | 0 | @r0_8(glval:int) | +| Switch(int) -> void | 0 | 9 | 0 | @r0_9(int) | +| Switch(int) -> void | 1 | 0 | 0 | @r1_0(int) | +| Switch(int) -> void | 1 | 1 | 0 | @r1_1(glval:int) | +| Switch(int) -> void | 1 | 2 | 0 | @mu1_2(int) | +| Switch(int) -> void | 2 | 1 | 0 | @r2_1(int) | +| Switch(int) -> void | 2 | 2 | 0 | @r2_2(glval:int) | +| Switch(int) -> void | 2 | 3 | 0 | @mu2_3(int) | +| Switch(int) -> void | 4 | 1 | 0 | @r4_1(int) | +| Switch(int) -> void | 4 | 2 | 0 | @r4_2(glval:int) | +| Switch(int) -> void | 4 | 3 | 0 | @mu4_3(int) | +| Switch(int) -> void | 5 | 1 | 0 | @r5_1(int) | +| Switch(int) -> void | 5 | 2 | 0 | @r5_2(glval:int) | +| Switch(int) -> void | 5 | 3 | 0 | @mu5_3(int) | +| Switch(int) -> void | 6 | 1 | 0 | @r6_1(int) | +| Switch(int) -> void | 6 | 2 | 0 | @r6_2(glval:int) | +| Switch(int) -> void | 6 | 3 | 0 | @mu6_3(int) | +| Switch(int) -> void | 7 | 1 | 0 | @r7_1(int) | +| Switch(int) -> void | 7 | 2 | 0 | @r7_2(glval:int) | +| Switch(int) -> void | 7 | 3 | 0 | @mu7_3(int) | +| Switch(int) -> void | 8 | 0 | 0 | @r8_0(int) | +| Switch(int) -> void | 8 | 1 | 0 | @r8_1(glval:int) | +| Switch(int) -> void | 8 | 2 | 0 | @mu8_2(int) | +| TakeReference() -> int & | 0 | 1 | 0 | @mu0_1(unknown) | +| TakeReference() -> int & | 0 | 2 | 0 | @r0_2(glval:int &) | +| TakeReference() -> int & | 0 | 3 | 0 | @r0_3(glval:int) | +| TakeReference() -> int & | 0 | 4 | 0 | @mu0_4(int &) | +| TakeReference() -> int & | 0 | 5 | 0 | @r0_5(glval:int &) | +| TryCatch(bool) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| TryCatch(bool) -> void | 0 | 2 | 0 | @r0_2(bool) | +| TryCatch(bool) -> void | 0 | 3 | 0 | @r0_3(glval:bool) | +| TryCatch(bool) -> void | 0 | 4 | 0 | @mu0_4(bool) | +| TryCatch(bool) -> void | 0 | 5 | 0 | @r0_5(glval:int) | +| TryCatch(bool) -> void | 0 | 6 | 0 | @r0_6(int) | +| TryCatch(bool) -> void | 0 | 7 | 0 | @mu0_7(int) | +| TryCatch(bool) -> void | 0 | 8 | 0 | @r0_8(glval:bool) | +| TryCatch(bool) -> void | 0 | 9 | 0 | @r0_9(bool) | +| TryCatch(bool) -> void | 3 | 0 | 0 | @r3_0(glval:char *) | +| TryCatch(bool) -> void | 3 | 1 | 0 | @r3_1(glval:char[15]) | +| TryCatch(bool) -> void | 3 | 2 | 0 | @r3_2(char *) | +| TryCatch(bool) -> void | 3 | 3 | 0 | @mu3_3(char *) | +| TryCatch(bool) -> void | 4 | 0 | 0 | @r4_0(glval:int) | +| TryCatch(bool) -> void | 4 | 1 | 0 | @r4_1(int) | +| TryCatch(bool) -> void | 4 | 2 | 0 | @r4_2(int) | +| TryCatch(bool) -> void | 4 | 3 | 0 | @r4_3(bool) | +| TryCatch(bool) -> void | 5 | 0 | 0 | @r5_0(glval:bool) | +| TryCatch(bool) -> void | 5 | 1 | 0 | @r5_1(bool) | +| TryCatch(bool) -> void | 6 | 0 | 0 | @r6_0(int) | +| TryCatch(bool) -> void | 6 | 1 | 0 | @r6_1(glval:int) | +| TryCatch(bool) -> void | 6 | 2 | 0 | @mu6_2(int) | +| TryCatch(bool) -> void | 6 | 3 | 0 | @r6_3(glval:int) | +| TryCatch(bool) -> void | 6 | 4 | 0 | @r6_4(int) | +| TryCatch(bool) -> void | 6 | 5 | 0 | @r6_5(glval:int) | +| TryCatch(bool) -> void | 6 | 6 | 0 | @mu6_6(int) | +| TryCatch(bool) -> void | 7 | 0 | 0 | @r7_0(glval:String) | +| TryCatch(bool) -> void | 7 | 1 | 0 | @r7_1(bool) | +| TryCatch(bool) -> void | 7 | 2 | 0 | @r7_2(glval:char[14]) | +| TryCatch(bool) -> void | 7 | 3 | 0 | @r7_3(char *) | +| TryCatch(bool) -> void | 8 | 0 | 0 | @r8_0(int) | +| TryCatch(bool) -> void | 8 | 1 | 0 | @r8_1(glval:int) | +| TryCatch(bool) -> void | 8 | 2 | 0 | @mu8_2(int) | +| TryCatch(bool) -> void | 10 | 0 | 0 | @r10_0(char *) | +| TryCatch(bool) -> void | 10 | 1 | 0 | @r10_1(glval:char *) | +| TryCatch(bool) -> void | 10 | 2 | 0 | @mu10_2(char *) | +| TryCatch(bool) -> void | 10 | 3 | 0 | @r10_3(glval:String) | +| TryCatch(bool) -> void | 10 | 4 | 0 | @r10_4(bool) | +| TryCatch(bool) -> void | 10 | 5 | 0 | @r10_5(glval:char *) | +| TryCatch(bool) -> void | 10 | 6 | 0 | @r10_6(char *) | +| TryCatch(bool) -> void | 12 | 0 | 0 | @r12_0(String &) | +| TryCatch(bool) -> void | 12 | 1 | 0 | @r12_1(glval:String &) | +| TryCatch(bool) -> void | 12 | 2 | 0 | @mu12_2(String &) | +| UninitializedVariables() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| UninitializedVariables() -> void | 0 | 2 | 0 | @r0_2(glval:int) | +| UninitializedVariables() -> void | 0 | 3 | 0 | @r0_3(int) | +| UninitializedVariables() -> void | 0 | 4 | 0 | @mu0_4(int) | +| UninitializedVariables() -> void | 0 | 5 | 0 | @r0_5(glval:int) | +| UninitializedVariables() -> void | 0 | 6 | 0 | @r0_6(glval:int) | +| UninitializedVariables() -> void | 0 | 7 | 0 | @r0_7(int) | +| UninitializedVariables() -> void | 0 | 8 | 0 | @mu0_8(int) | +| UnionInit(int, float) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| UnionInit(int, float) -> void | 0 | 2 | 0 | @r0_2(int) | +| UnionInit(int, float) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| UnionInit(int, float) -> void | 0 | 4 | 0 | @mu0_4(int) | +| UnionInit(int, float) -> void | 0 | 5 | 0 | @r0_5(float) | +| UnionInit(int, float) -> void | 0 | 6 | 0 | @r0_6(glval:float) | +| UnionInit(int, float) -> void | 0 | 7 | 0 | @mu0_7(float) | +| UnionInit(int, float) -> void | 0 | 8 | 0 | @r0_8(glval:U) | +| UnionInit(int, float) -> void | 0 | 9 | 0 | @r0_9(glval:double) | +| UnionInit(int, float) -> void | 0 | 10 | 0 | @r0_10(glval:float) | +| UnionInit(int, float) -> void | 0 | 11 | 0 | @r0_11(float) | +| UnionInit(int, float) -> void | 0 | 12 | 0 | @r0_12(double) | +| UnionInit(int, float) -> void | 0 | 13 | 0 | @mu0_13(double) | +| VarArgUsage(int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| VarArgUsage(int) -> void | 0 | 2 | 0 | @r0_2(int) | +| VarArgUsage(int) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| VarArgUsage(int) -> void | 0 | 4 | 0 | @mu0_4(int) | +| VarArgUsage(int) -> void | 0 | 5 | 0 | @r0_5(glval:__va_list_tag[1]) | +| VarArgUsage(int) -> void | 0 | 6 | 0 | @r0_6(__va_list_tag[1]) | +| VarArgUsage(int) -> void | 0 | 7 | 0 | @mu0_7(__va_list_tag[1]) | +| VarArgUsage(int) -> void | 0 | 8 | 0 | @r0_8(glval:__va_list_tag[1]) | +| VarArgUsage(int) -> void | 0 | 9 | 0 | @r0_9(__va_list_tag *) | +| VarArgUsage(int) -> void | 0 | 10 | 0 | @r0_10(glval:int) | +| VarArgUsage(int) -> void | 0 | 12 | 0 | @r0_12(glval:__va_list_tag[1]) | +| VarArgUsage(int) -> void | 0 | 13 | 0 | @r0_13(__va_list_tag[1]) | +| VarArgUsage(int) -> void | 0 | 14 | 0 | @mu0_14(__va_list_tag[1]) | +| VarArgUsage(int) -> void | 0 | 15 | 0 | @r0_15(glval:__va_list_tag[1]) | +| VarArgUsage(int) -> void | 0 | 16 | 0 | @r0_16(__va_list_tag *) | +| VarArgUsage(int) -> void | 0 | 17 | 0 | @r0_17(glval:__va_list_tag[1]) | +| VarArgUsage(int) -> void | 0 | 18 | 0 | @r0_18(__va_list_tag *) | +| VarArgUsage(int) -> void | 0 | 20 | 0 | @r0_20(glval:double) | +| VarArgUsage(int) -> void | 0 | 21 | 0 | @r0_21(glval:__va_list_tag[1]) | +| VarArgUsage(int) -> void | 0 | 22 | 0 | @r0_22(__va_list_tag *) | +| VarArgUsage(int) -> void | 0 | 23 | 0 | @r0_23(glval:double) | +| VarArgUsage(int) -> void | 0 | 24 | 0 | @r0_24(double) | +| VarArgUsage(int) -> void | 0 | 25 | 0 | @mu0_25(double) | +| VarArgUsage(int) -> void | 0 | 26 | 0 | @r0_26(glval:float) | +| VarArgUsage(int) -> void | 0 | 27 | 0 | @r0_27(glval:__va_list_tag[1]) | +| VarArgUsage(int) -> void | 0 | 28 | 0 | @r0_28(__va_list_tag *) | +| VarArgUsage(int) -> void | 0 | 29 | 0 | @r0_29(glval:double) | +| VarArgUsage(int) -> void | 0 | 30 | 0 | @r0_30(double) | +| VarArgUsage(int) -> void | 0 | 31 | 0 | @r0_31(float) | +| VarArgUsage(int) -> void | 0 | 32 | 0 | @mu0_32(float) | +| VarArgUsage(int) -> void | 0 | 33 | 0 | @r0_33(glval:__va_list_tag[1]) | +| VarArgUsage(int) -> void | 0 | 34 | 0 | @r0_34(__va_list_tag *) | +| VarArgUsage(int) -> void | 0 | 36 | 0 | @r0_36(glval:__va_list_tag[1]) | +| VarArgUsage(int) -> void | 0 | 37 | 0 | @r0_37(__va_list_tag *) | +| VarArgs() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| VarArgs() -> void | 0 | 2 | 0 | @r0_2(bool) | +| VarArgs() -> void | 0 | 3 | 0 | @r0_3(glval:char[6]) | +| VarArgs() -> void | 0 | 4 | 0 | @r0_4(char *) | +| VarArgs() -> void | 0 | 5 | 0 | @r0_5(int) | +| VarArgs() -> void | 0 | 6 | 0 | @r0_6(glval:char[7]) | +| VarArgs() -> void | 0 | 7 | 0 | @r0_7(char *) | +| WhileStatements(int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| WhileStatements(int) -> void | 0 | 2 | 0 | @r0_2(int) | +| WhileStatements(int) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| WhileStatements(int) -> void | 0 | 4 | 0 | @mu0_4(int) | +| WhileStatements(int) -> void | 1 | 0 | 0 | @r1_0(int) | +| WhileStatements(int) -> void | 1 | 1 | 0 | @r1_1(glval:int) | +| WhileStatements(int) -> void | 1 | 2 | 0 | @r1_2(int) | +| WhileStatements(int) -> void | 1 | 3 | 0 | @r1_3(int) | +| WhileStatements(int) -> void | 1 | 4 | 0 | @mu1_4(int) | +| WhileStatements(int) -> void | 3 | 0 | 0 | @r3_0(glval:int) | +| WhileStatements(int) -> void | 3 | 1 | 0 | @r3_1(int) | +| WhileStatements(int) -> void | 3 | 2 | 0 | @r3_2(int) | +| WhileStatements(int) -> void | 3 | 3 | 0 | @r3_3(bool) | +| min(int, int) -> int | 0 | 1 | 0 | @mu0_1(unknown) | +| min(int, int) -> int | 0 | 2 | 0 | @r0_2(int) | +| min(int, int) -> int | 0 | 3 | 0 | @r0_3(glval:int) | +| min(int, int) -> int | 0 | 4 | 0 | @mu0_4(int) | +| min(int, int) -> int | 0 | 5 | 0 | @r0_5(int) | +| min(int, int) -> int | 0 | 6 | 0 | @r0_6(glval:int) | +| min(int, int) -> int | 0 | 7 | 0 | @mu0_7(int) | +| min(int, int) -> int | 0 | 8 | 0 | @r0_8(glval:int) | +| min(int, int) -> int | 0 | 9 | 0 | @r0_9(glval:int) | +| min(int, int) -> int | 0 | 10 | 0 | @r0_10(int) | +| min(int, int) -> int | 0 | 11 | 0 | @r0_11(glval:int) | +| min(int, int) -> int | 0 | 12 | 0 | @r0_12(int) | +| min(int, int) -> int | 0 | 13 | 0 | @r0_13(bool) | +| min(int, int) -> int | 1 | 0 | 0 | @r1_0(glval:int) | +| min(int, int) -> int | 1 | 1 | 0 | @r1_1(int) | +| min(int, int) -> int | 1 | 2 | 0 | @r1_2(glval:int) | +| min(int, int) -> int | 1 | 3 | 0 | @mu1_3(int) | +| min(int, int) -> int | 2 | 0 | 0 | @r2_0(glval:int) | +| min(int, int) -> int | 2 | 1 | 0 | @r2_1(int) | +| min(int, int) -> int | 2 | 2 | 0 | @r2_2(glval:int) | +| min(int, int) -> int | 2 | 3 | 0 | @mu2_3(int) | +| min(int, int) -> int | 3 | 0 | 0 | @r3_0(glval:int) | +| min(int, int) -> int | 3 | 1 | 0 | @r3_1(int) | +| min(int, int) -> int | 3 | 2 | 0 | @mu3_2(int) | +| min(int, int) -> int | 3 | 3 | 0 | @r3_3(glval:int) | +printIRGraphSourceOperands +| AddressOf() -> int * | 0 | 4 | 0 | @r0_2 | +| AddressOf() -> int * | 0 | 4 | 1 | @r0_3 | +| AddressOf() -> int * | 0 | 6 | 0 | @r0_5 | +| AddressOf() -> int * | 0 | 6 | 5 | @mu0_1 | +| AddressOf() -> int * | 0 | 7 | 0 | @mu* | +| ArrayAccess(int *, int) -> void | 0 | 4 | 0 | @r0_3 | +| ArrayAccess(int *, int) -> void | 0 | 4 | 1 | @r0_2 | +| ArrayAccess(int *, int) -> void | 0 | 7 | 0 | @r0_6 | +| ArrayAccess(int *, int) -> void | 0 | 7 | 1 | @r0_5 | +| ArrayAccess(int *, int) -> void | 0 | 10 | 0 | @r0_8 | +| ArrayAccess(int *, int) -> void | 0 | 10 | 1 | @r0_9 | +| ArrayAccess(int *, int) -> void | 0 | 12 | 0 | @r0_11 | +| ArrayAccess(int *, int) -> void | 0 | 12 | 1 | @mu0_1 | +| ArrayAccess(int *, int) -> void | 0 | 14 | 0 | @r0_13 | +| ArrayAccess(int *, int) -> void | 0 | 14 | 1 | @mu0_1 | +| ArrayAccess(int *, int) -> void | 0 | 15 | 3 | @r0_12 | +| ArrayAccess(int *, int) -> void | 0 | 15 | 4 | @r0_14 | +| ArrayAccess(int *, int) -> void | 0 | 16 | 0 | @r0_15 | +| ArrayAccess(int *, int) -> void | 0 | 16 | 1 | @mu0_1 | +| ArrayAccess(int *, int) -> void | 0 | 18 | 0 | @r0_17 | +| ArrayAccess(int *, int) -> void | 0 | 18 | 1 | @r0_16 | +| ArrayAccess(int *, int) -> void | 0 | 20 | 0 | @r0_19 | +| ArrayAccess(int *, int) -> void | 0 | 20 | 1 | @mu0_1 | +| ArrayAccess(int *, int) -> void | 0 | 22 | 0 | @r0_21 | +| ArrayAccess(int *, int) -> void | 0 | 22 | 1 | @mu0_1 | +| ArrayAccess(int *, int) -> void | 0 | 23 | 3 | @r0_20 | +| ArrayAccess(int *, int) -> void | 0 | 23 | 4 | @r0_22 | +| ArrayAccess(int *, int) -> void | 0 | 24 | 0 | @r0_23 | +| ArrayAccess(int *, int) -> void | 0 | 24 | 1 | @mu0_1 | +| ArrayAccess(int *, int) -> void | 0 | 26 | 0 | @r0_25 | +| ArrayAccess(int *, int) -> void | 0 | 26 | 1 | @r0_24 | +| ArrayAccess(int *, int) -> void | 0 | 28 | 0 | @r0_27 | +| ArrayAccess(int *, int) -> void | 0 | 28 | 1 | @mu0_1 | +| ArrayAccess(int *, int) -> void | 0 | 30 | 0 | @r0_29 | +| ArrayAccess(int *, int) -> void | 0 | 30 | 1 | @mu0_1 | +| ArrayAccess(int *, int) -> void | 0 | 32 | 0 | @r0_31 | +| ArrayAccess(int *, int) -> void | 0 | 32 | 1 | @mu0_1 | +| ArrayAccess(int *, int) -> void | 0 | 33 | 3 | @r0_30 | +| ArrayAccess(int *, int) -> void | 0 | 33 | 4 | @r0_32 | +| ArrayAccess(int *, int) -> void | 0 | 34 | 0 | @r0_33 | +| ArrayAccess(int *, int) -> void | 0 | 34 | 1 | @r0_28 | +| ArrayAccess(int *, int) -> void | 0 | 36 | 0 | @r0_35 | +| ArrayAccess(int *, int) -> void | 0 | 36 | 1 | @mu0_1 | +| ArrayAccess(int *, int) -> void | 0 | 38 | 0 | @r0_37 | +| ArrayAccess(int *, int) -> void | 0 | 38 | 1 | @mu0_1 | +| ArrayAccess(int *, int) -> void | 0 | 40 | 0 | @r0_39 | +| ArrayAccess(int *, int) -> void | 0 | 40 | 1 | @mu0_1 | +| ArrayAccess(int *, int) -> void | 0 | 41 | 3 | @r0_38 | +| ArrayAccess(int *, int) -> void | 0 | 41 | 4 | @r0_40 | +| ArrayAccess(int *, int) -> void | 0 | 42 | 0 | @r0_41 | +| ArrayAccess(int *, int) -> void | 0 | 42 | 1 | @r0_36 | +| ArrayAccess(int *, int) -> void | 0 | 45 | 0 | @r0_43 | +| ArrayAccess(int *, int) -> void | 0 | 45 | 1 | @r0_44 | +| ArrayAccess(int *, int) -> void | 0 | 47 | 2 | @r0_46 | +| ArrayAccess(int *, int) -> void | 0 | 49 | 0 | @r0_48 | +| ArrayAccess(int *, int) -> void | 0 | 49 | 1 | @mu0_1 | +| ArrayAccess(int *, int) -> void | 0 | 50 | 3 | @r0_47 | +| ArrayAccess(int *, int) -> void | 0 | 50 | 4 | @r0_49 | +| ArrayAccess(int *, int) -> void | 0 | 51 | 0 | @r0_50 | +| ArrayAccess(int *, int) -> void | 0 | 51 | 1 | @mu0_1 | +| ArrayAccess(int *, int) -> void | 0 | 53 | 0 | @r0_52 | +| ArrayAccess(int *, int) -> void | 0 | 53 | 1 | @r0_51 | +| ArrayAccess(int *, int) -> void | 0 | 55 | 2 | @r0_54 | +| ArrayAccess(int *, int) -> void | 0 | 57 | 0 | @r0_56 | +| ArrayAccess(int *, int) -> void | 0 | 57 | 1 | @mu0_1 | +| ArrayAccess(int *, int) -> void | 0 | 58 | 3 | @r0_55 | +| ArrayAccess(int *, int) -> void | 0 | 58 | 4 | @r0_57 | +| ArrayAccess(int *, int) -> void | 0 | 59 | 0 | @r0_58 | +| ArrayAccess(int *, int) -> void | 0 | 59 | 1 | @mu0_1 | +| ArrayAccess(int *, int) -> void | 0 | 61 | 0 | @r0_60 | +| ArrayAccess(int *, int) -> void | 0 | 61 | 1 | @r0_59 | +| ArrayAccess(int *, int) -> void | 0 | 63 | 0 | @r0_62 | +| ArrayAccess(int *, int) -> void | 0 | 63 | 1 | @mu0_1 | +| ArrayAccess(int *, int) -> void | 0 | 65 | 2 | @r0_64 | +| ArrayAccess(int *, int) -> void | 0 | 67 | 0 | @r0_66 | +| ArrayAccess(int *, int) -> void | 0 | 67 | 1 | @mu0_1 | +| ArrayAccess(int *, int) -> void | 0 | 68 | 3 | @r0_65 | +| ArrayAccess(int *, int) -> void | 0 | 68 | 4 | @r0_67 | +| ArrayAccess(int *, int) -> void | 0 | 69 | 0 | @r0_68 | +| ArrayAccess(int *, int) -> void | 0 | 69 | 1 | @r0_63 | +| ArrayAccess(int *, int) -> void | 0 | 71 | 0 | @r0_70 | +| ArrayAccess(int *, int) -> void | 0 | 71 | 1 | @mu0_1 | +| ArrayAccess(int *, int) -> void | 0 | 73 | 2 | @r0_72 | +| ArrayAccess(int *, int) -> void | 0 | 75 | 0 | @r0_74 | +| ArrayAccess(int *, int) -> void | 0 | 75 | 1 | @mu0_1 | +| ArrayAccess(int *, int) -> void | 0 | 76 | 3 | @r0_73 | +| ArrayAccess(int *, int) -> void | 0 | 76 | 4 | @r0_75 | +| ArrayAccess(int *, int) -> void | 0 | 77 | 0 | @r0_76 | +| ArrayAccess(int *, int) -> void | 0 | 77 | 1 | @r0_71 | +| ArrayAccess(int *, int) -> void | 0 | 80 | 0 | @mu* | +| ArrayConversions() -> void | 0 | 4 | 0 | @r0_2 | +| ArrayConversions() -> void | 0 | 4 | 1 | @r0_3 | +| ArrayConversions() -> void | 0 | 7 | 2 | @r0_6 | +| ArrayConversions() -> void | 0 | 8 | 2 | @r0_7 | +| ArrayConversions() -> void | 0 | 9 | 0 | @r0_5 | +| ArrayConversions() -> void | 0 | 9 | 1 | @r0_8 | +| ArrayConversions() -> void | 0 | 11 | 2 | @r0_10 | +| ArrayConversions() -> void | 0 | 13 | 0 | @r0_12 | +| ArrayConversions() -> void | 0 | 13 | 1 | @r0_11 | +| ArrayConversions() -> void | 0 | 15 | 2 | @r0_14 | +| ArrayConversions() -> void | 0 | 17 | 3 | @r0_15 | +| ArrayConversions() -> void | 0 | 17 | 4 | @r0_16 | +| ArrayConversions() -> void | 0 | 18 | 2 | @r0_17 | +| ArrayConversions() -> void | 0 | 20 | 0 | @r0_19 | +| ArrayConversions() -> void | 0 | 20 | 1 | @r0_18 | +| ArrayConversions() -> void | 0 | 22 | 2 | @r0_21 | +| ArrayConversions() -> void | 0 | 24 | 3 | @r0_22 | +| ArrayConversions() -> void | 0 | 24 | 4 | @r0_23 | +| ArrayConversions() -> void | 0 | 26 | 0 | @r0_25 | +| ArrayConversions() -> void | 0 | 26 | 1 | @r0_24 | +| ArrayConversions() -> void | 0 | 29 | 0 | @r0_27 | +| ArrayConversions() -> void | 0 | 29 | 1 | @r0_28 | +| ArrayConversions() -> void | 0 | 32 | 0 | @r0_30 | +| ArrayConversions() -> void | 0 | 32 | 1 | @r0_31 | +| ArrayConversions() -> void | 0 | 35 | 2 | @r0_34 | +| ArrayConversions() -> void | 0 | 36 | 0 | @r0_33 | +| ArrayConversions() -> void | 0 | 36 | 1 | @r0_35 | +| ArrayConversions() -> void | 0 | 39 | 0 | @r0_38 | +| ArrayConversions() -> void | 0 | 39 | 1 | @r0_37 | +| ArrayConversions() -> void | 0 | 42 | 0 | @mu* | +| ArrayInit(int, float) -> void | 0 | 4 | 0 | @r0_3 | +| ArrayInit(int, float) -> void | 0 | 4 | 1 | @r0_2 | +| ArrayInit(int, float) -> void | 0 | 7 | 0 | @r0_6 | +| ArrayInit(int, float) -> void | 0 | 7 | 1 | @r0_5 | +| ArrayInit(int, float) -> void | 0 | 10 | 3 | @r0_8 | +| ArrayInit(int, float) -> void | 0 | 10 | 4 | @r0_9 | +| ArrayInit(int, float) -> void | 0 | 12 | 0 | @r0_10 | +| ArrayInit(int, float) -> void | 0 | 12 | 1 | @r0_11 | +| ArrayInit(int, float) -> void | 0 | 15 | 3 | @r0_13 | +| ArrayInit(int, float) -> void | 0 | 15 | 4 | @r0_14 | +| ArrayInit(int, float) -> void | 0 | 17 | 0 | @r0_16 | +| ArrayInit(int, float) -> void | 0 | 17 | 1 | @mu0_1 | +| ArrayInit(int, float) -> void | 0 | 18 | 0 | @r0_15 | +| ArrayInit(int, float) -> void | 0 | 18 | 1 | @r0_17 | +| ArrayInit(int, float) -> void | 0 | 20 | 3 | @r0_13 | +| ArrayInit(int, float) -> void | 0 | 20 | 4 | @r0_19 | +| ArrayInit(int, float) -> void | 0 | 22 | 0 | @r0_21 | +| ArrayInit(int, float) -> void | 0 | 22 | 1 | @mu0_1 | +| ArrayInit(int, float) -> void | 0 | 23 | 2 | @r0_22 | +| ArrayInit(int, float) -> void | 0 | 24 | 0 | @r0_20 | +| ArrayInit(int, float) -> void | 0 | 24 | 1 | @r0_23 | +| ArrayInit(int, float) -> void | 0 | 26 | 3 | @r0_13 | +| ArrayInit(int, float) -> void | 0 | 26 | 4 | @r0_25 | +| ArrayInit(int, float) -> void | 0 | 28 | 0 | @r0_26 | +| ArrayInit(int, float) -> void | 0 | 28 | 1 | @r0_27 | +| ArrayInit(int, float) -> void | 0 | 31 | 3 | @r0_29 | +| ArrayInit(int, float) -> void | 0 | 31 | 4 | @r0_30 | +| ArrayInit(int, float) -> void | 0 | 33 | 0 | @r0_32 | +| ArrayInit(int, float) -> void | 0 | 33 | 1 | @mu0_1 | +| ArrayInit(int, float) -> void | 0 | 34 | 0 | @r0_31 | +| ArrayInit(int, float) -> void | 0 | 34 | 1 | @r0_33 | +| ArrayInit(int, float) -> void | 0 | 36 | 3 | @r0_29 | +| ArrayInit(int, float) -> void | 0 | 36 | 4 | @r0_35 | +| ArrayInit(int, float) -> void | 0 | 38 | 0 | @r0_36 | +| ArrayInit(int, float) -> void | 0 | 38 | 1 | @r0_37 | +| ArrayInit(int, float) -> void | 0 | 41 | 0 | @mu* | +| ArrayReferences() -> void | 0 | 4 | 0 | @r0_2 | +| ArrayReferences() -> void | 0 | 4 | 1 | @r0_3 | +| ArrayReferences() -> void | 0 | 7 | 0 | @r0_5 | +| ArrayReferences() -> void | 0 | 7 | 1 | @r0_6 | +| ArrayReferences() -> void | 0 | 10 | 0 | @r0_9 | +| ArrayReferences() -> void | 0 | 10 | 1 | @mu0_1 | +| ArrayReferences() -> void | 0 | 11 | 2 | @r0_10 | +| ArrayReferences() -> void | 0 | 13 | 3 | @r0_11 | +| ArrayReferences() -> void | 0 | 13 | 4 | @r0_12 | +| ArrayReferences() -> void | 0 | 14 | 0 | @r0_13 | +| ArrayReferences() -> void | 0 | 14 | 1 | @mu0_1 | +| ArrayReferences() -> void | 0 | 15 | 0 | @r0_8 | +| ArrayReferences() -> void | 0 | 15 | 1 | @r0_14 | +| ArrayReferences() -> void | 0 | 18 | 0 | @mu* | +| Base::Base() -> void | 0 | 3 | 2 | @r0_2 | +| Base::Base() -> void | 0 | 5 | 9 | @r0_4 | +| Base::Base() -> void | 0 | 5 | 10 | this:@r0_3 | +| Base::Base() -> void | 0 | 8 | 0 | @mu* | +| Base::Base(const Base &) -> void | 0 | 5 | 0 | @r0_4 | +| Base::Base(const Base &) -> void | 0 | 5 | 1 | @r0_3 | +| Base::Base(const Base &) -> void | 0 | 6 | 2 | @r0_2 | +| Base::Base(const Base &) -> void | 0 | 8 | 9 | @r0_7 | +| Base::Base(const Base &) -> void | 0 | 8 | 10 | this:@r0_6 | +| Base::Base(const Base &) -> void | 0 | 11 | 0 | @mu* | +| Base::operator=(const Base &) -> Base & | 0 | 5 | 0 | @r0_4 | +| Base::operator=(const Base &) -> Base & | 0 | 5 | 1 | @r0_3 | +| Base::operator=(const Base &) -> Base & | 0 | 6 | 1 | @r0_2 | +| Base::operator=(const Base &) -> Base & | 0 | 7 | 2 | @r0_6 | +| Base::operator=(const Base &) -> Base & | 0 | 10 | 0 | @r0_9 | +| Base::operator=(const Base &) -> Base & | 0 | 10 | 1 | @mu0_1 | +| Base::operator=(const Base &) -> Base & | 0 | 11 | 2 | @r0_10 | +| Base::operator=(const Base &) -> Base & | 0 | 12 | 9 | @r0_8 | +| Base::operator=(const Base &) -> Base & | 0 | 12 | 10 | this:@r0_7 | +| Base::operator=(const Base &) -> Base & | 0 | 12 | 11 | @r0_11 | +| Base::operator=(const Base &) -> Base & | 0 | 14 | 1 | @r0_2 | +| Base::operator=(const Base &) -> Base & | 0 | 15 | 0 | @r0_13 | +| Base::operator=(const Base &) -> Base & | 0 | 15 | 1 | @r0_14 | +| Base::operator=(const Base &) -> Base & | 0 | 17 | 0 | @r0_16 | +| Base::operator=(const Base &) -> Base & | 0 | 17 | 5 | @mu0_1 | +| Base::operator=(const Base &) -> Base & | 0 | 18 | 0 | @mu* | +| Base::~Base() -> void | 0 | 4 | 2 | @r0_2 | +| Base::~Base() -> void | 0 | 6 | 9 | @r0_5 | +| Base::~Base() -> void | 0 | 6 | 10 | this:@r0_4 | +| Base::~Base() -> void | 0 | 8 | 0 | @mu* | +| Break(int) -> void | 0 | 4 | 0 | @r0_3 | +| Break(int) -> void | 0 | 4 | 1 | @r0_2 | +| Break(int) -> void | 1 | 1 | 0 | @r1_0 | +| Break(int) -> void | 1 | 1 | 1 | @mu0_1 | +| Break(int) -> void | 1 | 3 | 3 | @r1_1 | +| Break(int) -> void | 1 | 3 | 4 | @r1_2 | +| Break(int) -> void | 1 | 4 | 7 | @r1_3 | +| Break(int) -> void | 3 | 2 | 0 | @r3_1 | +| Break(int) -> void | 3 | 2 | 1 | @mu0_1 | +| Break(int) -> void | 3 | 3 | 3 | @r3_2 | +| Break(int) -> void | 3 | 3 | 4 | @r3_0 | +| Break(int) -> void | 3 | 4 | 0 | @r3_1 | +| Break(int) -> void | 3 | 4 | 1 | @r3_3 | +| Break(int) -> void | 4 | 3 | 0 | @mu* | +| Break(int) -> void | 5 | 1 | 0 | @r5_0 | +| Break(int) -> void | 5 | 1 | 1 | @mu0_1 | +| Break(int) -> void | 5 | 3 | 3 | @r5_1 | +| Break(int) -> void | 5 | 3 | 4 | @r5_2 | +| Break(int) -> void | 5 | 4 | 7 | @r5_3 | +| C::C() -> void | 0 | 3 | 2 | @r0_2 | +| C::C() -> void | 0 | 5 | 0 | @r0_3 | +| C::C() -> void | 0 | 5 | 1 | @r0_4 | +| C::C() -> void | 0 | 6 | 2 | @r0_2 | +| C::C() -> void | 0 | 8 | 9 | @r0_7 | +| C::C() -> void | 0 | 8 | 10 | this:@r0_6 | +| C::C() -> void | 0 | 9 | 2 | @r0_2 | +| C::C() -> void | 0 | 11 | 0 | @r0_9 | +| C::C() -> void | 0 | 11 | 1 | @r0_10 | +| C::C() -> void | 0 | 12 | 2 | @r0_2 | +| C::C() -> void | 0 | 14 | 0 | @r0_12 | +| C::C() -> void | 0 | 14 | 1 | @r0_13 | +| C::C() -> void | 0 | 15 | 2 | @r0_2 | +| C::C() -> void | 0 | 18 | 2 | @r0_17 | +| C::C() -> void | 0 | 19 | 9 | @r0_16 | +| C::C() -> void | 0 | 19 | 10 | this:@r0_15 | +| C::C() -> void | 0 | 19 | 11 | @r0_18 | +| C::C() -> void | 0 | 22 | 0 | @mu* | +| C::FieldAccess() -> void | 0 | 4 | 1 | @r0_2 | +| C::FieldAccess() -> void | 0 | 5 | 2 | @r0_4 | +| C::FieldAccess() -> void | 0 | 6 | 0 | @r0_5 | +| C::FieldAccess() -> void | 0 | 6 | 1 | @r0_3 | +| C::FieldAccess() -> void | 0 | 8 | 1 | @r0_2 | +| C::FieldAccess() -> void | 0 | 9 | 2 | @r0_8 | +| C::FieldAccess() -> void | 0 | 10 | 0 | @r0_9 | +| C::FieldAccess() -> void | 0 | 10 | 1 | @r0_7 | +| C::FieldAccess() -> void | 0 | 12 | 1 | @r0_2 | +| C::FieldAccess() -> void | 0 | 13 | 2 | @r0_12 | +| C::FieldAccess() -> void | 0 | 14 | 0 | @r0_13 | +| C::FieldAccess() -> void | 0 | 14 | 1 | @r0_11 | +| C::FieldAccess() -> void | 0 | 17 | 0 | @r0_15 | +| C::FieldAccess() -> void | 0 | 17 | 1 | @r0_16 | +| C::FieldAccess() -> void | 0 | 18 | 1 | @r0_2 | +| C::FieldAccess() -> void | 0 | 19 | 2 | @r0_18 | +| C::FieldAccess() -> void | 0 | 20 | 0 | @r0_19 | +| C::FieldAccess() -> void | 0 | 20 | 1 | @mu0_1 | +| C::FieldAccess() -> void | 0 | 22 | 0 | @r0_21 | +| C::FieldAccess() -> void | 0 | 22 | 1 | @r0_20 | +| C::FieldAccess() -> void | 0 | 23 | 1 | @r0_2 | +| C::FieldAccess() -> void | 0 | 24 | 2 | @r0_23 | +| C::FieldAccess() -> void | 0 | 25 | 0 | @r0_24 | +| C::FieldAccess() -> void | 0 | 25 | 1 | @mu0_1 | +| C::FieldAccess() -> void | 0 | 27 | 0 | @r0_26 | +| C::FieldAccess() -> void | 0 | 27 | 1 | @r0_25 | +| C::FieldAccess() -> void | 0 | 28 | 1 | @r0_2 | +| C::FieldAccess() -> void | 0 | 29 | 2 | @r0_28 | +| C::FieldAccess() -> void | 0 | 30 | 0 | @r0_29 | +| C::FieldAccess() -> void | 0 | 30 | 1 | @mu0_1 | +| C::FieldAccess() -> void | 0 | 32 | 0 | @r0_31 | +| C::FieldAccess() -> void | 0 | 32 | 1 | @r0_30 | +| C::FieldAccess() -> void | 0 | 35 | 0 | @mu* | +| C::InstanceMemberFunction(int) -> int | 0 | 5 | 0 | @r0_4 | +| C::InstanceMemberFunction(int) -> int | 0 | 5 | 1 | @r0_3 | +| C::InstanceMemberFunction(int) -> int | 0 | 8 | 0 | @r0_7 | +| C::InstanceMemberFunction(int) -> int | 0 | 8 | 1 | @mu0_1 | +| C::InstanceMemberFunction(int) -> int | 0 | 9 | 0 | @r0_6 | +| C::InstanceMemberFunction(int) -> int | 0 | 9 | 1 | @r0_8 | +| C::InstanceMemberFunction(int) -> int | 0 | 11 | 0 | @r0_10 | +| C::InstanceMemberFunction(int) -> int | 0 | 11 | 5 | @mu0_1 | +| C::InstanceMemberFunction(int) -> int | 0 | 12 | 0 | @mu* | +| C::MethodCalls() -> void | 0 | 3 | 1 | @r0_2 | +| C::MethodCalls() -> void | 0 | 6 | 9 | @r0_4 | +| C::MethodCalls() -> void | 0 | 6 | 10 | this:@r0_3 | +| C::MethodCalls() -> void | 0 | 6 | 11 | @r0_5 | +| C::MethodCalls() -> void | 0 | 7 | 1 | @r0_2 | +| C::MethodCalls() -> void | 0 | 10 | 9 | @r0_8 | +| C::MethodCalls() -> void | 0 | 10 | 10 | this:@r0_7 | +| C::MethodCalls() -> void | 0 | 10 | 11 | @r0_9 | +| C::MethodCalls() -> void | 0 | 11 | 1 | @r0_2 | +| C::MethodCalls() -> void | 0 | 14 | 9 | @r0_12 | +| C::MethodCalls() -> void | 0 | 14 | 10 | this:@r0_11 | +| C::MethodCalls() -> void | 0 | 14 | 11 | @r0_13 | +| C::MethodCalls() -> void | 0 | 17 | 0 | @mu* | +| C::StaticMemberFunction(int) -> int | 0 | 4 | 0 | @r0_3 | +| C::StaticMemberFunction(int) -> int | 0 | 4 | 1 | @r0_2 | +| C::StaticMemberFunction(int) -> int | 0 | 7 | 0 | @r0_6 | +| C::StaticMemberFunction(int) -> int | 0 | 7 | 1 | @mu0_1 | +| C::StaticMemberFunction(int) -> int | 0 | 8 | 0 | @r0_5 | +| C::StaticMemberFunction(int) -> int | 0 | 8 | 1 | @r0_7 | +| C::StaticMemberFunction(int) -> int | 0 | 10 | 0 | @r0_9 | +| C::StaticMemberFunction(int) -> int | 0 | 10 | 5 | @mu0_1 | +| C::StaticMemberFunction(int) -> int | 0 | 11 | 0 | @mu* | +| C::VirtualMemberFunction(int) -> int | 0 | 5 | 0 | @r0_4 | +| C::VirtualMemberFunction(int) -> int | 0 | 5 | 1 | @r0_3 | +| C::VirtualMemberFunction(int) -> int | 0 | 8 | 0 | @r0_7 | +| C::VirtualMemberFunction(int) -> int | 0 | 8 | 1 | @mu0_1 | +| C::VirtualMemberFunction(int) -> int | 0 | 9 | 0 | @r0_6 | +| C::VirtualMemberFunction(int) -> int | 0 | 9 | 1 | @r0_8 | +| C::VirtualMemberFunction(int) -> int | 0 | 11 | 0 | @r0_10 | +| C::VirtualMemberFunction(int) -> int | 0 | 11 | 5 | @mu0_1 | +| C::VirtualMemberFunction(int) -> int | 0 | 12 | 0 | @mu* | +| Call() -> void | 0 | 3 | 9 | @r0_2 | +| Call() -> void | 0 | 6 | 0 | @mu* | +| CallAdd(int, int) -> int | 0 | 4 | 0 | @r0_3 | +| CallAdd(int, int) -> int | 0 | 4 | 1 | @r0_2 | +| CallAdd(int, int) -> int | 0 | 7 | 0 | @r0_6 | +| CallAdd(int, int) -> int | 0 | 7 | 1 | @r0_5 | +| CallAdd(int, int) -> int | 0 | 11 | 0 | @r0_10 | +| CallAdd(int, int) -> int | 0 | 11 | 1 | @mu0_1 | +| CallAdd(int, int) -> int | 0 | 13 | 0 | @r0_12 | +| CallAdd(int, int) -> int | 0 | 13 | 1 | @mu0_1 | +| CallAdd(int, int) -> int | 0 | 14 | 9 | @r0_9 | +| CallAdd(int, int) -> int | 0 | 14 | 11 | @r0_11 | +| CallAdd(int, int) -> int | 0 | 14 | 12 | @r0_13 | +| CallAdd(int, int) -> int | 0 | 15 | 0 | @r0_8 | +| CallAdd(int, int) -> int | 0 | 15 | 1 | @r0_14 | +| CallAdd(int, int) -> int | 0 | 17 | 0 | @r0_16 | +| CallAdd(int, int) -> int | 0 | 17 | 5 | @mu0_1 | +| CallAdd(int, int) -> int | 0 | 18 | 0 | @mu* | +| CallMethods(String &, String *, String) -> void | 0 | 4 | 0 | @r0_3 | +| CallMethods(String &, String *, String) -> void | 0 | 4 | 1 | @r0_2 | +| CallMethods(String &, String *, String) -> void | 0 | 7 | 0 | @r0_6 | +| CallMethods(String &, String *, String) -> void | 0 | 7 | 1 | @r0_5 | +| CallMethods(String &, String *, String) -> void | 0 | 10 | 0 | @r0_9 | +| CallMethods(String &, String *, String) -> void | 0 | 10 | 1 | @r0_8 | +| CallMethods(String &, String *, String) -> void | 0 | 12 | 0 | @r0_11 | +| CallMethods(String &, String *, String) -> void | 0 | 12 | 1 | @mu0_1 | +| CallMethods(String &, String *, String) -> void | 0 | 13 | 2 | @r0_12 | +| CallMethods(String &, String *, String) -> void | 0 | 15 | 9 | @r0_14 | +| CallMethods(String &, String *, String) -> void | 0 | 15 | 10 | this:@r0_13 | +| CallMethods(String &, String *, String) -> void | 0 | 17 | 0 | @r0_16 | +| CallMethods(String &, String *, String) -> void | 0 | 17 | 1 | @mu0_1 | +| CallMethods(String &, String *, String) -> void | 0 | 18 | 2 | @r0_17 | +| CallMethods(String &, String *, String) -> void | 0 | 20 | 9 | @r0_19 | +| CallMethods(String &, String *, String) -> void | 0 | 20 | 10 | this:@r0_18 | +| CallMethods(String &, String *, String) -> void | 0 | 22 | 2 | @r0_21 | +| CallMethods(String &, String *, String) -> void | 0 | 24 | 9 | @r0_23 | +| CallMethods(String &, String *, String) -> void | 0 | 24 | 10 | this:@r0_22 | +| CallMethods(String &, String *, String) -> void | 0 | 27 | 0 | @mu* | +| CallMin(int, int) -> int | 0 | 4 | 0 | @r0_3 | +| CallMin(int, int) -> int | 0 | 4 | 1 | @r0_2 | +| CallMin(int, int) -> int | 0 | 7 | 0 | @r0_6 | +| CallMin(int, int) -> int | 0 | 7 | 1 | @r0_5 | +| CallMin(int, int) -> int | 0 | 11 | 0 | @r0_10 | +| CallMin(int, int) -> int | 0 | 11 | 1 | @mu0_1 | +| CallMin(int, int) -> int | 0 | 13 | 0 | @r0_12 | +| CallMin(int, int) -> int | 0 | 13 | 1 | @mu0_1 | +| CallMin(int, int) -> int | 0 | 14 | 9 | @r0_9 | +| CallMin(int, int) -> int | 0 | 14 | 11 | @r0_11 | +| CallMin(int, int) -> int | 0 | 14 | 12 | @r0_13 | +| CallMin(int, int) -> int | 0 | 15 | 0 | @r0_8 | +| CallMin(int, int) -> int | 0 | 15 | 1 | @r0_14 | +| CallMin(int, int) -> int | 0 | 17 | 0 | @r0_16 | +| CallMin(int, int) -> int | 0 | 17 | 5 | @mu0_1 | +| CallMin(int, int) -> int | 0 | 18 | 0 | @mu* | +| CallNestedTemplateFunc() -> double | 0 | 6 | 9 | @r0_3 | +| CallNestedTemplateFunc() -> double | 0 | 6 | 11 | @r0_4 | +| CallNestedTemplateFunc() -> double | 0 | 6 | 12 | @r0_5 | +| CallNestedTemplateFunc() -> double | 0 | 7 | 2 | @r0_6 | +| CallNestedTemplateFunc() -> double | 0 | 8 | 0 | @r0_2 | +| CallNestedTemplateFunc() -> double | 0 | 8 | 1 | @r0_7 | +| CallNestedTemplateFunc() -> double | 0 | 10 | 0 | @r0_9 | +| CallNestedTemplateFunc() -> double | 0 | 10 | 5 | @mu0_1 | +| CallNestedTemplateFunc() -> double | 0 | 11 | 0 | @mu* | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 4 | 0 | @r0_3 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 4 | 1 | @r0_2 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 7 | 0 | @r0_6 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 7 | 1 | @mu0_1 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 9 | 9 | @r0_7 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 9 | 11 | @r0_8 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 10 | 0 | @r0_5 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 10 | 1 | @r0_9 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 12 | 0 | @r0_11 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 12 | 5 | @mu0_1 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 13 | 0 | @mu* | +| Comma(int, int) -> int | 0 | 4 | 0 | @r0_3 | +| Comma(int, int) -> int | 0 | 4 | 1 | @r0_2 | +| Comma(int, int) -> int | 0 | 7 | 0 | @r0_6 | +| Comma(int, int) -> int | 0 | 7 | 1 | @r0_5 | +| Comma(int, int) -> int | 0 | 10 | 9 | @r0_9 | +| Comma(int, int) -> int | 0 | 13 | 0 | @r0_12 | +| Comma(int, int) -> int | 0 | 13 | 1 | @mu0_1 | +| Comma(int, int) -> int | 0 | 15 | 0 | @r0_14 | +| Comma(int, int) -> int | 0 | 15 | 1 | @mu0_1 | +| Comma(int, int) -> int | 0 | 16 | 9 | @r0_11 | +| Comma(int, int) -> int | 0 | 16 | 11 | @r0_13 | +| Comma(int, int) -> int | 0 | 16 | 12 | @r0_15 | +| Comma(int, int) -> int | 0 | 17 | 0 | @r0_8 | +| Comma(int, int) -> int | 0 | 17 | 1 | @r0_16 | +| Comma(int, int) -> int | 0 | 19 | 0 | @r0_18 | +| Comma(int, int) -> int | 0 | 19 | 5 | @mu0_1 | +| Comma(int, int) -> int | 0 | 20 | 0 | @mu* | +| CompoundAssignment() -> void | 0 | 4 | 0 | @r0_2 | +| CompoundAssignment() -> void | 0 | 4 | 1 | @r0_3 | +| CompoundAssignment() -> void | 0 | 7 | 0 | @r0_6 | +| CompoundAssignment() -> void | 0 | 7 | 1 | @mu0_1 | +| CompoundAssignment() -> void | 0 | 8 | 3 | @r0_7 | +| CompoundAssignment() -> void | 0 | 8 | 4 | @r0_5 | +| CompoundAssignment() -> void | 0 | 9 | 0 | @r0_6 | +| CompoundAssignment() -> void | 0 | 9 | 1 | @r0_8 | +| CompoundAssignment() -> void | 0 | 12 | 0 | @r0_10 | +| CompoundAssignment() -> void | 0 | 12 | 1 | @r0_11 | +| CompoundAssignment() -> void | 0 | 14 | 0 | @r0_13 | +| CompoundAssignment() -> void | 0 | 14 | 1 | @mu0_1 | +| CompoundAssignment() -> void | 0 | 16 | 0 | @r0_15 | +| CompoundAssignment() -> void | 0 | 16 | 1 | @mu0_1 | +| CompoundAssignment() -> void | 0 | 17 | 2 | @r0_16 | +| CompoundAssignment() -> void | 0 | 18 | 3 | @r0_17 | +| CompoundAssignment() -> void | 0 | 18 | 4 | @r0_14 | +| CompoundAssignment() -> void | 0 | 19 | 2 | @r0_18 | +| CompoundAssignment() -> void | 0 | 20 | 0 | @r0_15 | +| CompoundAssignment() -> void | 0 | 20 | 1 | @r0_19 | +| CompoundAssignment() -> void | 0 | 23 | 0 | @r0_22 | +| CompoundAssignment() -> void | 0 | 23 | 1 | @mu0_1 | +| CompoundAssignment() -> void | 0 | 24 | 3 | @r0_23 | +| CompoundAssignment() -> void | 0 | 24 | 4 | @r0_21 | +| CompoundAssignment() -> void | 0 | 25 | 0 | @r0_22 | +| CompoundAssignment() -> void | 0 | 25 | 1 | @r0_24 | +| CompoundAssignment() -> void | 0 | 28 | 0 | @r0_26 | +| CompoundAssignment() -> void | 0 | 28 | 1 | @r0_27 | +| CompoundAssignment() -> void | 0 | 31 | 0 | @r0_30 | +| CompoundAssignment() -> void | 0 | 31 | 1 | @mu0_1 | +| CompoundAssignment() -> void | 0 | 32 | 2 | @r0_31 | +| CompoundAssignment() -> void | 0 | 33 | 3 | @r0_32 | +| CompoundAssignment() -> void | 0 | 33 | 4 | @r0_29 | +| CompoundAssignment() -> void | 0 | 34 | 2 | @r0_33 | +| CompoundAssignment() -> void | 0 | 35 | 0 | @r0_30 | +| CompoundAssignment() -> void | 0 | 35 | 1 | @r0_34 | +| CompoundAssignment() -> void | 0 | 38 | 0 | @mu* | +| ConditionValues(bool, bool) -> void | 0 | 4 | 0 | @r0_3 | +| ConditionValues(bool, bool) -> void | 0 | 4 | 1 | @r0_2 | +| ConditionValues(bool, bool) -> void | 0 | 7 | 0 | @r0_6 | +| ConditionValues(bool, bool) -> void | 0 | 7 | 1 | @r0_5 | +| ConditionValues(bool, bool) -> void | 0 | 10 | 0 | @r0_8 | +| ConditionValues(bool, bool) -> void | 0 | 10 | 1 | @r0_9 | +| ConditionValues(bool, bool) -> void | 0 | 12 | 0 | @r0_11 | +| ConditionValues(bool, bool) -> void | 0 | 12 | 1 | @mu0_1 | +| ConditionValues(bool, bool) -> void | 0 | 13 | 7 | @r0_12 | +| ConditionValues(bool, bool) -> void | 1 | 1 | 0 | @r1_0 | +| ConditionValues(bool, bool) -> void | 1 | 1 | 1 | @mu0_1 | +| ConditionValues(bool, bool) -> void | 1 | 2 | 7 | @r1_1 | +| ConditionValues(bool, bool) -> void | 2 | 2 | 0 | @r2_0 | +| ConditionValues(bool, bool) -> void | 2 | 2 | 1 | @r2_1 | +| ConditionValues(bool, bool) -> void | 3 | 1 | 0 | @r3_0 | +| ConditionValues(bool, bool) -> void | 3 | 1 | 1 | @mu0_1 | +| ConditionValues(bool, bool) -> void | 3 | 3 | 0 | @r3_2 | +| ConditionValues(bool, bool) -> void | 3 | 3 | 1 | @r3_1 | +| ConditionValues(bool, bool) -> void | 3 | 5 | 0 | @r3_4 | +| ConditionValues(bool, bool) -> void | 3 | 5 | 1 | @mu0_1 | +| ConditionValues(bool, bool) -> void | 3 | 6 | 7 | @r3_5 | +| ConditionValues(bool, bool) -> void | 4 | 2 | 0 | @r4_0 | +| ConditionValues(bool, bool) -> void | 4 | 2 | 1 | @r4_1 | +| ConditionValues(bool, bool) -> void | 5 | 1 | 0 | @r5_0 | +| ConditionValues(bool, bool) -> void | 5 | 1 | 1 | @mu0_1 | +| ConditionValues(bool, bool) -> void | 5 | 2 | 7 | @r5_1 | +| ConditionValues(bool, bool) -> void | 6 | 2 | 0 | @r6_0 | +| ConditionValues(bool, bool) -> void | 6 | 2 | 1 | @r6_1 | +| ConditionValues(bool, bool) -> void | 7 | 1 | 0 | @r7_0 | +| ConditionValues(bool, bool) -> void | 7 | 1 | 1 | @mu0_1 | +| ConditionValues(bool, bool) -> void | 7 | 2 | 2 | @r7_1 | +| ConditionValues(bool, bool) -> void | 7 | 4 | 0 | @r7_3 | +| ConditionValues(bool, bool) -> void | 7 | 4 | 1 | @r7_2 | +| ConditionValues(bool, bool) -> void | 7 | 7 | 0 | @mu* | +| ConditionValues(bool, bool) -> void | 8 | 2 | 0 | @r8_0 | +| ConditionValues(bool, bool) -> void | 8 | 2 | 1 | @r8_1 | +| ConditionValues(bool, bool) -> void | 9 | 1 | 0 | @r9_0 | +| ConditionValues(bool, bool) -> void | 9 | 1 | 1 | @mu0_1 | +| ConditionValues(bool, bool) -> void | 9 | 2 | 7 | @r9_1 | +| ConditionValues(bool, bool) -> void | 10 | 2 | 0 | @r10_0 | +| ConditionValues(bool, bool) -> void | 10 | 2 | 1 | @r10_1 | +| ConditionValues(bool, bool) -> void | 11 | 1 | 0 | @r11_0 | +| ConditionValues(bool, bool) -> void | 11 | 1 | 1 | @mu0_1 | +| ConditionValues(bool, bool) -> void | 11 | 3 | 0 | @r11_2 | +| ConditionValues(bool, bool) -> void | 11 | 3 | 1 | @r11_1 | +| ConditionValues(bool, bool) -> void | 11 | 5 | 0 | @r11_4 | +| ConditionValues(bool, bool) -> void | 11 | 5 | 1 | @mu0_1 | +| ConditionValues(bool, bool) -> void | 11 | 6 | 7 | @r11_5 | +| ConditionValues(bool, bool) -> void | 12 | 2 | 0 | @r12_0 | +| ConditionValues(bool, bool) -> void | 12 | 2 | 1 | @r12_1 | +| Conditional(bool, int, int) -> void | 0 | 4 | 0 | @r0_3 | +| Conditional(bool, int, int) -> void | 0 | 4 | 1 | @r0_2 | +| Conditional(bool, int, int) -> void | 0 | 7 | 0 | @r0_6 | +| Conditional(bool, int, int) -> void | 0 | 7 | 1 | @r0_5 | +| Conditional(bool, int, int) -> void | 0 | 10 | 0 | @r0_9 | +| Conditional(bool, int, int) -> void | 0 | 10 | 1 | @r0_8 | +| Conditional(bool, int, int) -> void | 0 | 13 | 0 | @r0_12 | +| Conditional(bool, int, int) -> void | 0 | 13 | 1 | @mu0_1 | +| Conditional(bool, int, int) -> void | 0 | 14 | 7 | @r0_13 | +| Conditional(bool, int, int) -> void | 1 | 1 | 0 | @r1_0 | +| Conditional(bool, int, int) -> void | 1 | 1 | 1 | @mu0_1 | +| Conditional(bool, int, int) -> void | 1 | 3 | 0 | @r1_2 | +| Conditional(bool, int, int) -> void | 1 | 3 | 1 | @r1_1 | +| Conditional(bool, int, int) -> void | 2 | 1 | 0 | @r2_0 | +| Conditional(bool, int, int) -> void | 2 | 1 | 1 | @mu0_1 | +| Conditional(bool, int, int) -> void | 2 | 3 | 0 | @r2_2 | +| Conditional(bool, int, int) -> void | 2 | 3 | 1 | @r2_1 | +| Conditional(bool, int, int) -> void | 3 | 1 | 0 | @r3_0 | +| Conditional(bool, int, int) -> void | 3 | 1 | 1 | @mu0_1 | +| Conditional(bool, int, int) -> void | 3 | 2 | 0 | @r0_11 | +| Conditional(bool, int, int) -> void | 3 | 2 | 1 | @r3_1 | +| Conditional(bool, int, int) -> void | 3 | 5 | 0 | @mu* | +| Conditional_LValue(bool) -> void | 0 | 4 | 0 | @r0_3 | +| Conditional_LValue(bool) -> void | 0 | 4 | 1 | @r0_2 | +| Conditional_LValue(bool) -> void | 0 | 7 | 0 | @r0_5 | +| Conditional_LValue(bool) -> void | 0 | 7 | 1 | @r0_6 | +| Conditional_LValue(bool) -> void | 0 | 10 | 0 | @r0_8 | +| Conditional_LValue(bool) -> void | 0 | 10 | 1 | @r0_9 | +| Conditional_LValue(bool) -> void | 0 | 13 | 0 | @r0_12 | +| Conditional_LValue(bool) -> void | 0 | 13 | 1 | @mu0_1 | +| Conditional_LValue(bool) -> void | 0 | 14 | 7 | @r0_13 | +| Conditional_LValue(bool) -> void | 1 | 1 | 0 | @r1_0 | +| Conditional_LValue(bool) -> void | 1 | 1 | 1 | @mu0_1 | +| Conditional_LValue(bool) -> void | 1 | 2 | 0 | @r1_1 | +| Conditional_LValue(bool) -> void | 1 | 2 | 1 | @r0_11 | +| Conditional_LValue(bool) -> void | 1 | 5 | 0 | @mu* | +| Conditional_LValue(bool) -> void | 2 | 2 | 0 | @r2_1 | +| Conditional_LValue(bool) -> void | 2 | 2 | 1 | @r2_0 | +| Conditional_LValue(bool) -> void | 3 | 2 | 0 | @r3_1 | +| Conditional_LValue(bool) -> void | 3 | 2 | 1 | @r3_0 | +| Conditional_Void(bool) -> void | 0 | 4 | 0 | @r0_3 | +| Conditional_Void(bool) -> void | 0 | 4 | 1 | @r0_2 | +| Conditional_Void(bool) -> void | 0 | 6 | 0 | @r0_5 | +| Conditional_Void(bool) -> void | 0 | 6 | 1 | @mu0_1 | +| Conditional_Void(bool) -> void | 0 | 7 | 7 | @r0_6 | +| Conditional_Void(bool) -> void | 1 | 2 | 0 | @mu* | +| Conditional_Void(bool) -> void | 2 | 1 | 9 | @r2_0 | +| Conditional_Void(bool) -> void | 3 | 1 | 9 | @r3_0 | +| Constants() -> void | 0 | 4 | 0 | @r0_2 | +| Constants() -> void | 0 | 4 | 1 | @r0_3 | +| Constants() -> void | 0 | 7 | 0 | @r0_5 | +| Constants() -> void | 0 | 7 | 1 | @r0_6 | +| Constants() -> void | 0 | 10 | 0 | @r0_8 | +| Constants() -> void | 0 | 10 | 1 | @r0_9 | +| Constants() -> void | 0 | 13 | 0 | @r0_11 | +| Constants() -> void | 0 | 13 | 1 | @r0_12 | +| Constants() -> void | 0 | 16 | 0 | @r0_14 | +| Constants() -> void | 0 | 16 | 1 | @r0_15 | +| Constants() -> void | 0 | 19 | 0 | @r0_17 | +| Constants() -> void | 0 | 19 | 1 | @r0_18 | +| Constants() -> void | 0 | 22 | 0 | @r0_20 | +| Constants() -> void | 0 | 22 | 1 | @r0_21 | +| Constants() -> void | 0 | 25 | 0 | @r0_23 | +| Constants() -> void | 0 | 25 | 1 | @r0_24 | +| Constants() -> void | 0 | 28 | 0 | @r0_26 | +| Constants() -> void | 0 | 28 | 1 | @r0_27 | +| Constants() -> void | 0 | 31 | 0 | @r0_29 | +| Constants() -> void | 0 | 31 | 1 | @r0_30 | +| Constants() -> void | 0 | 34 | 0 | @r0_32 | +| Constants() -> void | 0 | 34 | 1 | @r0_33 | +| Constants() -> void | 0 | 37 | 0 | @r0_35 | +| Constants() -> void | 0 | 37 | 1 | @r0_36 | +| Constants() -> void | 0 | 40 | 0 | @r0_38 | +| Constants() -> void | 0 | 40 | 1 | @r0_39 | +| Constants() -> void | 0 | 43 | 0 | @r0_41 | +| Constants() -> void | 0 | 43 | 1 | @r0_42 | +| Constants() -> void | 0 | 46 | 0 | @r0_44 | +| Constants() -> void | 0 | 46 | 1 | @r0_45 | +| Constants() -> void | 0 | 49 | 0 | @r0_47 | +| Constants() -> void | 0 | 49 | 1 | @r0_48 | +| Constants() -> void | 0 | 52 | 0 | @r0_50 | +| Constants() -> void | 0 | 52 | 1 | @r0_51 | +| Constants() -> void | 0 | 55 | 0 | @r0_53 | +| Constants() -> void | 0 | 55 | 1 | @r0_54 | +| Constants() -> void | 0 | 58 | 0 | @r0_56 | +| Constants() -> void | 0 | 58 | 1 | @r0_57 | +| Constants() -> void | 0 | 61 | 0 | @r0_59 | +| Constants() -> void | 0 | 61 | 1 | @r0_60 | +| Constants() -> void | 0 | 64 | 0 | @r0_62 | +| Constants() -> void | 0 | 64 | 1 | @r0_63 | +| Constants() -> void | 0 | 67 | 0 | @r0_65 | +| Constants() -> void | 0 | 67 | 1 | @r0_66 | +| Constants() -> void | 0 | 70 | 0 | @r0_68 | +| Constants() -> void | 0 | 70 | 1 | @r0_69 | +| Constants() -> void | 0 | 73 | 0 | @r0_71 | +| Constants() -> void | 0 | 73 | 1 | @r0_72 | +| Constants() -> void | 0 | 76 | 0 | @r0_74 | +| Constants() -> void | 0 | 76 | 1 | @r0_75 | +| Constants() -> void | 0 | 79 | 0 | @r0_77 | +| Constants() -> void | 0 | 79 | 1 | @r0_78 | +| Constants() -> void | 0 | 82 | 0 | @r0_80 | +| Constants() -> void | 0 | 82 | 1 | @r0_81 | +| Constants() -> void | 0 | 85 | 0 | @r0_83 | +| Constants() -> void | 0 | 85 | 1 | @r0_84 | +| Constants() -> void | 0 | 88 | 0 | @mu* | +| Continue(int) -> void | 0 | 4 | 0 | @r0_3 | +| Continue(int) -> void | 0 | 4 | 1 | @r0_2 | +| Continue(int) -> void | 1 | 1 | 0 | @r1_0 | +| Continue(int) -> void | 1 | 1 | 1 | @mu0_1 | +| Continue(int) -> void | 1 | 3 | 3 | @r1_1 | +| Continue(int) -> void | 1 | 3 | 4 | @r1_2 | +| Continue(int) -> void | 1 | 4 | 7 | @r1_3 | +| Continue(int) -> void | 3 | 2 | 0 | @r3_1 | +| Continue(int) -> void | 3 | 2 | 1 | @mu0_1 | +| Continue(int) -> void | 3 | 3 | 3 | @r3_2 | +| Continue(int) -> void | 3 | 3 | 4 | @r3_0 | +| Continue(int) -> void | 3 | 4 | 0 | @r3_1 | +| Continue(int) -> void | 3 | 4 | 1 | @r3_3 | +| Continue(int) -> void | 4 | 2 | 0 | @r4_1 | +| Continue(int) -> void | 4 | 2 | 1 | @mu0_1 | +| Continue(int) -> void | 4 | 4 | 3 | @r4_2 | +| Continue(int) -> void | 4 | 4 | 4 | @r4_3 | +| Continue(int) -> void | 4 | 5 | 7 | @r4_4 | +| Continue(int) -> void | 5 | 2 | 0 | @mu* | +| DeclareObject() -> void | 0 | 4 | 9 | @r0_3 | +| DeclareObject() -> void | 0 | 4 | 10 | this:@r0_2 | +| DeclareObject() -> void | 0 | 8 | 2 | @r0_7 | +| DeclareObject() -> void | 0 | 9 | 9 | @r0_6 | +| DeclareObject() -> void | 0 | 9 | 10 | this:@r0_5 | +| DeclareObject() -> void | 0 | 9 | 11 | @r0_8 | +| DeclareObject() -> void | 0 | 12 | 9 | @r0_11 | +| DeclareObject() -> void | 0 | 13 | 0 | @r0_10 | +| DeclareObject() -> void | 0 | 13 | 1 | @r0_12 | +| DeclareObject() -> void | 0 | 17 | 2 | @r0_16 | +| DeclareObject() -> void | 0 | 18 | 9 | @r0_15 | +| DeclareObject() -> void | 0 | 18 | 10 | this:@r0_14 | +| DeclareObject() -> void | 0 | 18 | 11 | @r0_17 | +| DeclareObject() -> void | 0 | 21 | 0 | @mu* | +| DerefReference(int &) -> int | 0 | 4 | 0 | @r0_3 | +| DerefReference(int &) -> int | 0 | 4 | 1 | @r0_2 | +| DerefReference(int &) -> int | 0 | 7 | 0 | @r0_6 | +| DerefReference(int &) -> int | 0 | 7 | 1 | @mu0_1 | +| DerefReference(int &) -> int | 0 | 8 | 0 | @r0_7 | +| DerefReference(int &) -> int | 0 | 8 | 1 | @mu0_1 | +| DerefReference(int &) -> int | 0 | 9 | 0 | @r0_5 | +| DerefReference(int &) -> int | 0 | 9 | 1 | @r0_8 | +| DerefReference(int &) -> int | 0 | 11 | 0 | @r0_10 | +| DerefReference(int &) -> int | 0 | 11 | 5 | @mu0_1 | +| DerefReference(int &) -> int | 0 | 12 | 0 | @mu* | +| Dereference(int *) -> int | 0 | 4 | 0 | @r0_3 | +| Dereference(int *) -> int | 0 | 4 | 1 | @r0_2 | +| Dereference(int *) -> int | 0 | 7 | 0 | @r0_6 | +| Dereference(int *) -> int | 0 | 7 | 1 | @mu0_1 | +| Dereference(int *) -> int | 0 | 8 | 0 | @r0_7 | +| Dereference(int *) -> int | 0 | 8 | 1 | @r0_5 | +| Dereference(int *) -> int | 0 | 11 | 0 | @r0_10 | +| Dereference(int *) -> int | 0 | 11 | 1 | @mu0_1 | +| Dereference(int *) -> int | 0 | 12 | 0 | @r0_11 | +| Dereference(int *) -> int | 0 | 12 | 1 | @mu0_1 | +| Dereference(int *) -> int | 0 | 13 | 0 | @r0_9 | +| Dereference(int *) -> int | 0 | 13 | 1 | @r0_12 | +| Dereference(int *) -> int | 0 | 15 | 0 | @r0_14 | +| Dereference(int *) -> int | 0 | 15 | 5 | @mu0_1 | +| Dereference(int *) -> int | 0 | 16 | 0 | @mu* | +| Derived::Derived() -> void | 0 | 3 | 2 | @r0_2 | +| Derived::Derived() -> void | 0 | 5 | 9 | @r0_4 | +| Derived::Derived() -> void | 0 | 5 | 10 | this:@r0_3 | +| Derived::Derived() -> void | 0 | 6 | 2 | @r0_2 | +| Derived::Derived() -> void | 0 | 8 | 9 | @r0_7 | +| Derived::Derived() -> void | 0 | 8 | 10 | this:@r0_6 | +| Derived::Derived() -> void | 0 | 11 | 0 | @mu* | +| Derived::operator=(const Derived &) -> Derived & | 0 | 5 | 0 | @r0_4 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 5 | 1 | @r0_3 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 6 | 1 | @r0_2 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 7 | 2 | @r0_6 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 10 | 0 | @r0_9 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 10 | 1 | @mu0_1 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 11 | 2 | @r0_10 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 12 | 9 | @r0_8 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 12 | 10 | this:@r0_7 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 12 | 11 | @r0_11 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 13 | 1 | @r0_2 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 14 | 2 | @r0_13 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 17 | 0 | @r0_16 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 17 | 1 | @mu0_1 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 18 | 2 | @r0_17 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 19 | 9 | @r0_15 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 19 | 10 | this:@r0_14 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 19 | 11 | @r0_18 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 21 | 1 | @r0_2 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 22 | 0 | @r0_20 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 22 | 1 | @r0_21 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 24 | 0 | @r0_23 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 24 | 5 | @mu0_1 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 25 | 0 | @mu* | +| Derived::~Derived() -> void | 0 | 4 | 2 | @r0_2 | +| Derived::~Derived() -> void | 0 | 6 | 9 | @r0_5 | +| Derived::~Derived() -> void | 0 | 6 | 10 | this:@r0_4 | +| Derived::~Derived() -> void | 0 | 7 | 2 | @r0_2 | +| Derived::~Derived() -> void | 0 | 9 | 9 | @r0_8 | +| Derived::~Derived() -> void | 0 | 9 | 10 | this:@r0_7 | +| Derived::~Derived() -> void | 0 | 11 | 0 | @mu* | +| DerivedVB::DerivedVB() -> void | 0 | 3 | 2 | @r0_2 | +| DerivedVB::DerivedVB() -> void | 0 | 5 | 9 | @r0_4 | +| DerivedVB::DerivedVB() -> void | 0 | 5 | 10 | this:@r0_3 | +| DerivedVB::DerivedVB() -> void | 0 | 6 | 2 | @r0_2 | +| DerivedVB::DerivedVB() -> void | 0 | 8 | 9 | @r0_7 | +| DerivedVB::DerivedVB() -> void | 0 | 8 | 10 | this:@r0_6 | +| DerivedVB::DerivedVB() -> void | 0 | 9 | 2 | @r0_2 | +| DerivedVB::DerivedVB() -> void | 0 | 11 | 9 | @r0_10 | +| DerivedVB::DerivedVB() -> void | 0 | 11 | 10 | this:@r0_9 | +| DerivedVB::DerivedVB() -> void | 0 | 12 | 2 | @r0_2 | +| DerivedVB::DerivedVB() -> void | 0 | 14 | 9 | @r0_13 | +| DerivedVB::DerivedVB() -> void | 0 | 14 | 10 | this:@r0_12 | +| DerivedVB::DerivedVB() -> void | 0 | 17 | 0 | @mu* | +| DerivedVB::~DerivedVB() -> void | 0 | 4 | 2 | @r0_2 | +| DerivedVB::~DerivedVB() -> void | 0 | 6 | 9 | @r0_5 | +| DerivedVB::~DerivedVB() -> void | 0 | 6 | 10 | this:@r0_4 | +| DerivedVB::~DerivedVB() -> void | 0 | 7 | 2 | @r0_2 | +| DerivedVB::~DerivedVB() -> void | 0 | 9 | 9 | @r0_8 | +| DerivedVB::~DerivedVB() -> void | 0 | 9 | 10 | this:@r0_7 | +| DerivedVB::~DerivedVB() -> void | 0 | 10 | 2 | @r0_2 | +| DerivedVB::~DerivedVB() -> void | 0 | 12 | 9 | @r0_11 | +| DerivedVB::~DerivedVB() -> void | 0 | 12 | 10 | this:@r0_10 | +| DerivedVB::~DerivedVB() -> void | 0 | 13 | 2 | @r0_2 | +| DerivedVB::~DerivedVB() -> void | 0 | 15 | 9 | @r0_14 | +| DerivedVB::~DerivedVB() -> void | 0 | 15 | 10 | this:@r0_13 | +| DerivedVB::~DerivedVB() -> void | 0 | 17 | 0 | @mu* | +| DoStatements(int) -> void | 0 | 4 | 0 | @r0_3 | +| DoStatements(int) -> void | 0 | 4 | 1 | @r0_2 | +| DoStatements(int) -> void | 1 | 2 | 0 | @r1_1 | +| DoStatements(int) -> void | 1 | 2 | 1 | @mu0_1 | +| DoStatements(int) -> void | 1 | 3 | 3 | @r1_2 | +| DoStatements(int) -> void | 1 | 3 | 4 | @r1_0 | +| DoStatements(int) -> void | 1 | 4 | 0 | @r1_1 | +| DoStatements(int) -> void | 1 | 4 | 1 | @r1_3 | +| DoStatements(int) -> void | 1 | 6 | 0 | @r1_5 | +| DoStatements(int) -> void | 1 | 6 | 1 | @mu0_1 | +| DoStatements(int) -> void | 1 | 8 | 3 | @r1_6 | +| DoStatements(int) -> void | 1 | 8 | 4 | @r1_7 | +| DoStatements(int) -> void | 1 | 9 | 7 | @r1_8 | +| DoStatements(int) -> void | 2 | 2 | 0 | @mu* | +| DynamicCast() -> void | 0 | 4 | 9 | @r0_3 | +| DynamicCast() -> void | 0 | 4 | 10 | this:@r0_2 | +| DynamicCast() -> void | 0 | 7 | 9 | @r0_6 | +| DynamicCast() -> void | 0 | 7 | 10 | this:@r0_5 | +| DynamicCast() -> void | 0 | 10 | 0 | @r0_8 | +| DynamicCast() -> void | 0 | 10 | 1 | @r0_9 | +| DynamicCast() -> void | 0 | 13 | 0 | @r0_11 | +| DynamicCast() -> void | 0 | 13 | 1 | @r0_12 | +| DynamicCast() -> void | 0 | 15 | 0 | @r0_14 | +| DynamicCast() -> void | 0 | 15 | 1 | @mu0_1 | +| DynamicCast() -> void | 0 | 16 | 2 | @r0_15 | +| DynamicCast() -> void | 0 | 18 | 0 | @r0_17 | +| DynamicCast() -> void | 0 | 18 | 1 | @r0_16 | +| DynamicCast() -> void | 0 | 21 | 2 | @r0_20 | +| DynamicCast() -> void | 0 | 22 | 0 | @r0_19 | +| DynamicCast() -> void | 0 | 22 | 1 | @r0_21 | +| DynamicCast() -> void | 0 | 24 | 0 | @r0_23 | +| DynamicCast() -> void | 0 | 24 | 1 | @mu0_1 | +| DynamicCast() -> void | 0 | 25 | 2 | @r0_24 | +| DynamicCast() -> void | 0 | 27 | 0 | @r0_26 | +| DynamicCast() -> void | 0 | 27 | 1 | @r0_25 | +| DynamicCast() -> void | 0 | 30 | 2 | @r0_29 | +| DynamicCast() -> void | 0 | 31 | 0 | @r0_28 | +| DynamicCast() -> void | 0 | 31 | 1 | @r0_30 | +| DynamicCast() -> void | 0 | 34 | 0 | @r0_33 | +| DynamicCast() -> void | 0 | 34 | 1 | @mu0_1 | +| DynamicCast() -> void | 0 | 35 | 2 | @r0_34 | +| DynamicCast() -> void | 0 | 36 | 0 | @r0_32 | +| DynamicCast() -> void | 0 | 36 | 1 | @r0_35 | +| DynamicCast() -> void | 0 | 39 | 0 | @r0_38 | +| DynamicCast() -> void | 0 | 39 | 1 | @mu0_1 | +| DynamicCast() -> void | 0 | 40 | 2 | @r0_39 | +| DynamicCast() -> void | 0 | 41 | 0 | @r0_37 | +| DynamicCast() -> void | 0 | 41 | 1 | @r0_40 | +| DynamicCast() -> void | 0 | 44 | 0 | @mu* | +| EarlyReturn(int, int) -> void | 0 | 4 | 0 | @r0_3 | +| EarlyReturn(int, int) -> void | 0 | 4 | 1 | @r0_2 | +| EarlyReturn(int, int) -> void | 0 | 7 | 0 | @r0_6 | +| EarlyReturn(int, int) -> void | 0 | 7 | 1 | @r0_5 | +| EarlyReturn(int, int) -> void | 0 | 9 | 0 | @r0_8 | +| EarlyReturn(int, int) -> void | 0 | 9 | 1 | @mu0_1 | +| EarlyReturn(int, int) -> void | 0 | 11 | 0 | @r0_10 | +| EarlyReturn(int, int) -> void | 0 | 11 | 1 | @mu0_1 | +| EarlyReturn(int, int) -> void | 0 | 12 | 3 | @r0_9 | +| EarlyReturn(int, int) -> void | 0 | 12 | 4 | @r0_11 | +| EarlyReturn(int, int) -> void | 0 | 13 | 7 | @r0_12 | +| EarlyReturn(int, int) -> void | 1 | 1 | 0 | @mu* | +| EarlyReturn(int, int) -> void | 3 | 1 | 0 | @r3_0 | +| EarlyReturn(int, int) -> void | 3 | 1 | 1 | @mu0_1 | +| EarlyReturn(int, int) -> void | 3 | 3 | 0 | @r3_2 | +| EarlyReturn(int, int) -> void | 3 | 3 | 1 | @r3_1 | +| EarlyReturnValue(int, int) -> int | 0 | 4 | 0 | @r0_3 | +| EarlyReturnValue(int, int) -> int | 0 | 4 | 1 | @r0_2 | +| EarlyReturnValue(int, int) -> int | 0 | 7 | 0 | @r0_6 | +| EarlyReturnValue(int, int) -> int | 0 | 7 | 1 | @r0_5 | +| EarlyReturnValue(int, int) -> int | 0 | 9 | 0 | @r0_8 | +| EarlyReturnValue(int, int) -> int | 0 | 9 | 1 | @mu0_1 | +| EarlyReturnValue(int, int) -> int | 0 | 11 | 0 | @r0_10 | +| EarlyReturnValue(int, int) -> int | 0 | 11 | 1 | @mu0_1 | +| EarlyReturnValue(int, int) -> int | 0 | 12 | 3 | @r0_9 | +| EarlyReturnValue(int, int) -> int | 0 | 12 | 4 | @r0_11 | +| EarlyReturnValue(int, int) -> int | 0 | 13 | 7 | @r0_12 | +| EarlyReturnValue(int, int) -> int | 1 | 1 | 0 | @r1_0 | +| EarlyReturnValue(int, int) -> int | 1 | 1 | 5 | @mu0_1 | +| EarlyReturnValue(int, int) -> int | 1 | 2 | 0 | @mu* | +| EarlyReturnValue(int, int) -> int | 2 | 2 | 0 | @r2_1 | +| EarlyReturnValue(int, int) -> int | 2 | 2 | 1 | @mu0_1 | +| EarlyReturnValue(int, int) -> int | 2 | 3 | 0 | @r2_0 | +| EarlyReturnValue(int, int) -> int | 2 | 3 | 1 | @r2_2 | +| EarlyReturnValue(int, int) -> int | 3 | 2 | 0 | @r3_1 | +| EarlyReturnValue(int, int) -> int | 3 | 2 | 1 | @mu0_1 | +| EarlyReturnValue(int, int) -> int | 3 | 4 | 0 | @r3_3 | +| EarlyReturnValue(int, int) -> int | 3 | 4 | 1 | @mu0_1 | +| EarlyReturnValue(int, int) -> int | 3 | 5 | 3 | @r3_2 | +| EarlyReturnValue(int, int) -> int | 3 | 5 | 4 | @r3_4 | +| EarlyReturnValue(int, int) -> int | 3 | 6 | 0 | @r3_0 | +| EarlyReturnValue(int, int) -> int | 3 | 6 | 1 | @r3_5 | +| EnumSwitch(E) -> int | 0 | 4 | 0 | @r0_3 | +| EnumSwitch(E) -> int | 0 | 4 | 1 | @r0_2 | +| EnumSwitch(E) -> int | 0 | 6 | 0 | @r0_5 | +| EnumSwitch(E) -> int | 0 | 6 | 1 | @mu0_1 | +| EnumSwitch(E) -> int | 0 | 7 | 2 | @r0_6 | +| EnumSwitch(E) -> int | 0 | 8 | 7 | @r0_7 | +| EnumSwitch(E) -> int | 1 | 1 | 0 | @r1_0 | +| EnumSwitch(E) -> int | 1 | 1 | 5 | @mu0_1 | +| EnumSwitch(E) -> int | 1 | 2 | 0 | @mu* | +| EnumSwitch(E) -> int | 2 | 3 | 0 | @r2_1 | +| EnumSwitch(E) -> int | 2 | 3 | 1 | @r2_2 | +| EnumSwitch(E) -> int | 3 | 3 | 0 | @r3_1 | +| EnumSwitch(E) -> int | 3 | 3 | 1 | @r3_2 | +| EnumSwitch(E) -> int | 4 | 3 | 0 | @r4_1 | +| EnumSwitch(E) -> int | 4 | 3 | 1 | @r4_2 | +| FieldAccess() -> void | 0 | 4 | 0 | @r0_2 | +| FieldAccess() -> void | 0 | 4 | 1 | @r0_3 | +| FieldAccess() -> void | 0 | 7 | 2 | @r0_6 | +| FieldAccess() -> void | 0 | 8 | 0 | @r0_7 | +| FieldAccess() -> void | 0 | 8 | 1 | @r0_5 | +| FieldAccess() -> void | 0 | 10 | 2 | @r0_9 | +| FieldAccess() -> void | 0 | 11 | 0 | @r0_10 | +| FieldAccess() -> void | 0 | 11 | 1 | @mu0_1 | +| FieldAccess() -> void | 0 | 13 | 2 | @r0_12 | +| FieldAccess() -> void | 0 | 14 | 0 | @r0_13 | +| FieldAccess() -> void | 0 | 14 | 1 | @r0_11 | +| FieldAccess() -> void | 0 | 17 | 2 | @r0_16 | +| FieldAccess() -> void | 0 | 18 | 0 | @r0_15 | +| FieldAccess() -> void | 0 | 18 | 1 | @r0_17 | +| FieldAccess() -> void | 0 | 21 | 0 | @mu* | +| FloatCompare(double, double) -> void | 0 | 4 | 0 | @r0_3 | +| FloatCompare(double, double) -> void | 0 | 4 | 1 | @r0_2 | +| FloatCompare(double, double) -> void | 0 | 7 | 0 | @r0_6 | +| FloatCompare(double, double) -> void | 0 | 7 | 1 | @r0_5 | +| FloatCompare(double, double) -> void | 0 | 10 | 0 | @r0_8 | +| FloatCompare(double, double) -> void | 0 | 10 | 1 | @r0_9 | +| FloatCompare(double, double) -> void | 0 | 12 | 0 | @r0_11 | +| FloatCompare(double, double) -> void | 0 | 12 | 1 | @mu0_1 | +| FloatCompare(double, double) -> void | 0 | 14 | 0 | @r0_13 | +| FloatCompare(double, double) -> void | 0 | 14 | 1 | @mu0_1 | +| FloatCompare(double, double) -> void | 0 | 15 | 3 | @r0_12 | +| FloatCompare(double, double) -> void | 0 | 15 | 4 | @r0_14 | +| FloatCompare(double, double) -> void | 0 | 17 | 0 | @r0_16 | +| FloatCompare(double, double) -> void | 0 | 17 | 1 | @r0_15 | +| FloatCompare(double, double) -> void | 0 | 19 | 0 | @r0_18 | +| FloatCompare(double, double) -> void | 0 | 19 | 1 | @mu0_1 | +| FloatCompare(double, double) -> void | 0 | 21 | 0 | @r0_20 | +| FloatCompare(double, double) -> void | 0 | 21 | 1 | @mu0_1 | +| FloatCompare(double, double) -> void | 0 | 22 | 3 | @r0_19 | +| FloatCompare(double, double) -> void | 0 | 22 | 4 | @r0_21 | +| FloatCompare(double, double) -> void | 0 | 24 | 0 | @r0_23 | +| FloatCompare(double, double) -> void | 0 | 24 | 1 | @r0_22 | +| FloatCompare(double, double) -> void | 0 | 26 | 0 | @r0_25 | +| FloatCompare(double, double) -> void | 0 | 26 | 1 | @mu0_1 | +| FloatCompare(double, double) -> void | 0 | 28 | 0 | @r0_27 | +| FloatCompare(double, double) -> void | 0 | 28 | 1 | @mu0_1 | +| FloatCompare(double, double) -> void | 0 | 29 | 3 | @r0_26 | +| FloatCompare(double, double) -> void | 0 | 29 | 4 | @r0_28 | +| FloatCompare(double, double) -> void | 0 | 31 | 0 | @r0_30 | +| FloatCompare(double, double) -> void | 0 | 31 | 1 | @r0_29 | +| FloatCompare(double, double) -> void | 0 | 33 | 0 | @r0_32 | +| FloatCompare(double, double) -> void | 0 | 33 | 1 | @mu0_1 | +| FloatCompare(double, double) -> void | 0 | 35 | 0 | @r0_34 | +| FloatCompare(double, double) -> void | 0 | 35 | 1 | @mu0_1 | +| FloatCompare(double, double) -> void | 0 | 36 | 3 | @r0_33 | +| FloatCompare(double, double) -> void | 0 | 36 | 4 | @r0_35 | +| FloatCompare(double, double) -> void | 0 | 38 | 0 | @r0_37 | +| FloatCompare(double, double) -> void | 0 | 38 | 1 | @r0_36 | +| FloatCompare(double, double) -> void | 0 | 40 | 0 | @r0_39 | +| FloatCompare(double, double) -> void | 0 | 40 | 1 | @mu0_1 | +| FloatCompare(double, double) -> void | 0 | 42 | 0 | @r0_41 | +| FloatCompare(double, double) -> void | 0 | 42 | 1 | @mu0_1 | +| FloatCompare(double, double) -> void | 0 | 43 | 3 | @r0_40 | +| FloatCompare(double, double) -> void | 0 | 43 | 4 | @r0_42 | +| FloatCompare(double, double) -> void | 0 | 45 | 0 | @r0_44 | +| FloatCompare(double, double) -> void | 0 | 45 | 1 | @r0_43 | +| FloatCompare(double, double) -> void | 0 | 47 | 0 | @r0_46 | +| FloatCompare(double, double) -> void | 0 | 47 | 1 | @mu0_1 | +| FloatCompare(double, double) -> void | 0 | 49 | 0 | @r0_48 | +| FloatCompare(double, double) -> void | 0 | 49 | 1 | @mu0_1 | +| FloatCompare(double, double) -> void | 0 | 50 | 3 | @r0_47 | +| FloatCompare(double, double) -> void | 0 | 50 | 4 | @r0_49 | +| FloatCompare(double, double) -> void | 0 | 52 | 0 | @r0_51 | +| FloatCompare(double, double) -> void | 0 | 52 | 1 | @r0_50 | +| FloatCompare(double, double) -> void | 0 | 55 | 0 | @mu* | +| FloatCrement(float) -> void | 0 | 4 | 0 | @r0_3 | +| FloatCrement(float) -> void | 0 | 4 | 1 | @r0_2 | +| FloatCrement(float) -> void | 0 | 7 | 0 | @r0_5 | +| FloatCrement(float) -> void | 0 | 7 | 1 | @r0_6 | +| FloatCrement(float) -> void | 0 | 9 | 0 | @r0_8 | +| FloatCrement(float) -> void | 0 | 9 | 1 | @mu0_1 | +| FloatCrement(float) -> void | 0 | 11 | 3 | @r0_9 | +| FloatCrement(float) -> void | 0 | 11 | 4 | @r0_10 | +| FloatCrement(float) -> void | 0 | 12 | 0 | @r0_8 | +| FloatCrement(float) -> void | 0 | 12 | 1 | @r0_11 | +| FloatCrement(float) -> void | 0 | 14 | 0 | @r0_13 | +| FloatCrement(float) -> void | 0 | 14 | 1 | @r0_11 | +| FloatCrement(float) -> void | 0 | 16 | 0 | @r0_15 | +| FloatCrement(float) -> void | 0 | 16 | 1 | @mu0_1 | +| FloatCrement(float) -> void | 0 | 18 | 3 | @r0_16 | +| FloatCrement(float) -> void | 0 | 18 | 4 | @r0_17 | +| FloatCrement(float) -> void | 0 | 19 | 0 | @r0_15 | +| FloatCrement(float) -> void | 0 | 19 | 1 | @r0_18 | +| FloatCrement(float) -> void | 0 | 21 | 0 | @r0_20 | +| FloatCrement(float) -> void | 0 | 21 | 1 | @r0_18 | +| FloatCrement(float) -> void | 0 | 23 | 0 | @r0_22 | +| FloatCrement(float) -> void | 0 | 23 | 1 | @mu0_1 | +| FloatCrement(float) -> void | 0 | 25 | 3 | @r0_23 | +| FloatCrement(float) -> void | 0 | 25 | 4 | @r0_24 | +| FloatCrement(float) -> void | 0 | 26 | 0 | @r0_22 | +| FloatCrement(float) -> void | 0 | 26 | 1 | @r0_25 | +| FloatCrement(float) -> void | 0 | 28 | 0 | @r0_27 | +| FloatCrement(float) -> void | 0 | 28 | 1 | @r0_23 | +| FloatCrement(float) -> void | 0 | 30 | 0 | @r0_29 | +| FloatCrement(float) -> void | 0 | 30 | 1 | @mu0_1 | +| FloatCrement(float) -> void | 0 | 32 | 3 | @r0_30 | +| FloatCrement(float) -> void | 0 | 32 | 4 | @r0_31 | +| FloatCrement(float) -> void | 0 | 33 | 0 | @r0_29 | +| FloatCrement(float) -> void | 0 | 33 | 1 | @r0_32 | +| FloatCrement(float) -> void | 0 | 35 | 0 | @r0_34 | +| FloatCrement(float) -> void | 0 | 35 | 1 | @r0_30 | +| FloatCrement(float) -> void | 0 | 38 | 0 | @mu* | +| FloatOps(double, double) -> void | 0 | 4 | 0 | @r0_3 | +| FloatOps(double, double) -> void | 0 | 4 | 1 | @r0_2 | +| FloatOps(double, double) -> void | 0 | 7 | 0 | @r0_6 | +| FloatOps(double, double) -> void | 0 | 7 | 1 | @r0_5 | +| FloatOps(double, double) -> void | 0 | 10 | 0 | @r0_8 | +| FloatOps(double, double) -> void | 0 | 10 | 1 | @r0_9 | +| FloatOps(double, double) -> void | 0 | 12 | 0 | @r0_11 | +| FloatOps(double, double) -> void | 0 | 12 | 1 | @mu0_1 | +| FloatOps(double, double) -> void | 0 | 14 | 0 | @r0_13 | +| FloatOps(double, double) -> void | 0 | 14 | 1 | @mu0_1 | +| FloatOps(double, double) -> void | 0 | 15 | 3 | @r0_12 | +| FloatOps(double, double) -> void | 0 | 15 | 4 | @r0_14 | +| FloatOps(double, double) -> void | 0 | 17 | 0 | @r0_16 | +| FloatOps(double, double) -> void | 0 | 17 | 1 | @r0_15 | +| FloatOps(double, double) -> void | 0 | 19 | 0 | @r0_18 | +| FloatOps(double, double) -> void | 0 | 19 | 1 | @mu0_1 | +| FloatOps(double, double) -> void | 0 | 21 | 0 | @r0_20 | +| FloatOps(double, double) -> void | 0 | 21 | 1 | @mu0_1 | +| FloatOps(double, double) -> void | 0 | 22 | 3 | @r0_19 | +| FloatOps(double, double) -> void | 0 | 22 | 4 | @r0_21 | +| FloatOps(double, double) -> void | 0 | 24 | 0 | @r0_23 | +| FloatOps(double, double) -> void | 0 | 24 | 1 | @r0_22 | +| FloatOps(double, double) -> void | 0 | 26 | 0 | @r0_25 | +| FloatOps(double, double) -> void | 0 | 26 | 1 | @mu0_1 | +| FloatOps(double, double) -> void | 0 | 28 | 0 | @r0_27 | +| FloatOps(double, double) -> void | 0 | 28 | 1 | @mu0_1 | +| FloatOps(double, double) -> void | 0 | 29 | 3 | @r0_26 | +| FloatOps(double, double) -> void | 0 | 29 | 4 | @r0_28 | +| FloatOps(double, double) -> void | 0 | 31 | 0 | @r0_30 | +| FloatOps(double, double) -> void | 0 | 31 | 1 | @r0_29 | +| FloatOps(double, double) -> void | 0 | 33 | 0 | @r0_32 | +| FloatOps(double, double) -> void | 0 | 33 | 1 | @mu0_1 | +| FloatOps(double, double) -> void | 0 | 35 | 0 | @r0_34 | +| FloatOps(double, double) -> void | 0 | 35 | 1 | @mu0_1 | +| FloatOps(double, double) -> void | 0 | 36 | 3 | @r0_33 | +| FloatOps(double, double) -> void | 0 | 36 | 4 | @r0_35 | +| FloatOps(double, double) -> void | 0 | 38 | 0 | @r0_37 | +| FloatOps(double, double) -> void | 0 | 38 | 1 | @r0_36 | +| FloatOps(double, double) -> void | 0 | 40 | 0 | @r0_39 | +| FloatOps(double, double) -> void | 0 | 40 | 1 | @mu0_1 | +| FloatOps(double, double) -> void | 0 | 42 | 0 | @r0_41 | +| FloatOps(double, double) -> void | 0 | 42 | 1 | @r0_40 | +| FloatOps(double, double) -> void | 0 | 44 | 0 | @r0_43 | +| FloatOps(double, double) -> void | 0 | 44 | 1 | @mu0_1 | +| FloatOps(double, double) -> void | 0 | 46 | 0 | @r0_45 | +| FloatOps(double, double) -> void | 0 | 46 | 1 | @mu0_1 | +| FloatOps(double, double) -> void | 0 | 47 | 3 | @r0_46 | +| FloatOps(double, double) -> void | 0 | 47 | 4 | @r0_44 | +| FloatOps(double, double) -> void | 0 | 48 | 0 | @r0_45 | +| FloatOps(double, double) -> void | 0 | 48 | 1 | @r0_47 | +| FloatOps(double, double) -> void | 0 | 50 | 0 | @r0_49 | +| FloatOps(double, double) -> void | 0 | 50 | 1 | @mu0_1 | +| FloatOps(double, double) -> void | 0 | 52 | 0 | @r0_51 | +| FloatOps(double, double) -> void | 0 | 52 | 1 | @mu0_1 | +| FloatOps(double, double) -> void | 0 | 53 | 3 | @r0_52 | +| FloatOps(double, double) -> void | 0 | 53 | 4 | @r0_50 | +| FloatOps(double, double) -> void | 0 | 54 | 0 | @r0_51 | +| FloatOps(double, double) -> void | 0 | 54 | 1 | @r0_53 | +| FloatOps(double, double) -> void | 0 | 56 | 0 | @r0_55 | +| FloatOps(double, double) -> void | 0 | 56 | 1 | @mu0_1 | +| FloatOps(double, double) -> void | 0 | 58 | 0 | @r0_57 | +| FloatOps(double, double) -> void | 0 | 58 | 1 | @mu0_1 | +| FloatOps(double, double) -> void | 0 | 59 | 3 | @r0_58 | +| FloatOps(double, double) -> void | 0 | 59 | 4 | @r0_56 | +| FloatOps(double, double) -> void | 0 | 60 | 0 | @r0_57 | +| FloatOps(double, double) -> void | 0 | 60 | 1 | @r0_59 | +| FloatOps(double, double) -> void | 0 | 62 | 0 | @r0_61 | +| FloatOps(double, double) -> void | 0 | 62 | 1 | @mu0_1 | +| FloatOps(double, double) -> void | 0 | 64 | 0 | @r0_63 | +| FloatOps(double, double) -> void | 0 | 64 | 1 | @mu0_1 | +| FloatOps(double, double) -> void | 0 | 65 | 3 | @r0_64 | +| FloatOps(double, double) -> void | 0 | 65 | 4 | @r0_62 | +| FloatOps(double, double) -> void | 0 | 66 | 0 | @r0_63 | +| FloatOps(double, double) -> void | 0 | 66 | 1 | @r0_65 | +| FloatOps(double, double) -> void | 0 | 68 | 0 | @r0_67 | +| FloatOps(double, double) -> void | 0 | 68 | 1 | @mu0_1 | +| FloatOps(double, double) -> void | 0 | 69 | 1 | @r0_68 | +| FloatOps(double, double) -> void | 0 | 71 | 0 | @r0_70 | +| FloatOps(double, double) -> void | 0 | 71 | 1 | @r0_69 | +| FloatOps(double, double) -> void | 0 | 73 | 0 | @r0_72 | +| FloatOps(double, double) -> void | 0 | 73 | 1 | @mu0_1 | +| FloatOps(double, double) -> void | 0 | 74 | 2 | @r0_73 | +| FloatOps(double, double) -> void | 0 | 76 | 0 | @r0_75 | +| FloatOps(double, double) -> void | 0 | 76 | 1 | @r0_74 | +| FloatOps(double, double) -> void | 0 | 79 | 0 | @mu* | +| Foo() -> void | 0 | 4 | 0 | @r0_2 | +| Foo() -> void | 0 | 4 | 1 | @r0_3 | +| Foo() -> void | 0 | 7 | 0 | @r0_5 | +| Foo() -> void | 0 | 7 | 1 | @r0_6 | +| Foo() -> void | 0 | 9 | 0 | @r0_8 | +| Foo() -> void | 0 | 9 | 1 | @mu0_1 | +| Foo() -> void | 0 | 11 | 0 | @r0_10 | +| Foo() -> void | 0 | 11 | 1 | @mu0_1 | +| Foo() -> void | 0 | 12 | 2 | @r0_11 | +| Foo() -> void | 0 | 13 | 3 | @r0_9 | +| Foo() -> void | 0 | 13 | 4 | @r0_12 | +| Foo() -> void | 0 | 14 | 2 | @r0_13 | +| Foo() -> void | 0 | 16 | 0 | @r0_15 | +| Foo() -> void | 0 | 16 | 1 | @r0_14 | +| Foo() -> void | 0 | 18 | 0 | @r0_17 | +| Foo() -> void | 0 | 18 | 1 | @mu0_1 | +| Foo() -> void | 0 | 20 | 0 | @r0_19 | +| Foo() -> void | 0 | 20 | 1 | @mu0_1 | +| Foo() -> void | 0 | 21 | 2 | @r0_20 | +| Foo() -> void | 0 | 22 | 3 | @r0_18 | +| Foo() -> void | 0 | 22 | 4 | @r0_21 | +| Foo() -> void | 0 | 24 | 0 | @r0_23 | +| Foo() -> void | 0 | 24 | 1 | @r0_22 | +| Foo() -> void | 0 | 27 | 0 | @mu* | +| For_Break() -> void | 0 | 4 | 0 | @r0_2 | +| For_Break() -> void | 0 | 4 | 1 | @r0_3 | +| For_Break() -> void | 1 | 1 | 0 | @r1_0 | +| For_Break() -> void | 1 | 1 | 1 | @mu0_1 | +| For_Break() -> void | 1 | 3 | 3 | @r1_1 | +| For_Break() -> void | 1 | 3 | 4 | @r1_2 | +| For_Break() -> void | 1 | 4 | 7 | @r1_3 | +| For_Break() -> void | 2 | 2 | 0 | @r2_1 | +| For_Break() -> void | 2 | 2 | 1 | @mu0_1 | +| For_Break() -> void | 2 | 3 | 3 | @r2_2 | +| For_Break() -> void | 2 | 3 | 4 | @r2_0 | +| For_Break() -> void | 2 | 4 | 0 | @r2_1 | +| For_Break() -> void | 2 | 4 | 1 | @r2_3 | +| For_Break() -> void | 3 | 1 | 0 | @r3_0 | +| For_Break() -> void | 3 | 1 | 1 | @mu0_1 | +| For_Break() -> void | 3 | 3 | 3 | @r3_1 | +| For_Break() -> void | 3 | 3 | 4 | @r3_2 | +| For_Break() -> void | 3 | 4 | 7 | @r3_3 | +| For_Break() -> void | 5 | 3 | 0 | @mu* | +| For_Condition() -> void | 0 | 4 | 0 | @r0_2 | +| For_Condition() -> void | 0 | 4 | 1 | @r0_3 | +| For_Condition() -> void | 1 | 1 | 0 | @r1_0 | +| For_Condition() -> void | 1 | 1 | 1 | @mu0_1 | +| For_Condition() -> void | 1 | 3 | 3 | @r1_1 | +| For_Condition() -> void | 1 | 3 | 4 | @r1_2 | +| For_Condition() -> void | 1 | 4 | 7 | @r1_3 | +| For_Condition() -> void | 3 | 2 | 0 | @mu* | +| For_ConditionUpdate() -> void | 0 | 4 | 0 | @r0_2 | +| For_ConditionUpdate() -> void | 0 | 4 | 1 | @r0_3 | +| For_ConditionUpdate() -> void | 1 | 1 | 0 | @r1_0 | +| For_ConditionUpdate() -> void | 1 | 1 | 1 | @mu0_1 | +| For_ConditionUpdate() -> void | 1 | 3 | 3 | @r1_1 | +| For_ConditionUpdate() -> void | 1 | 3 | 4 | @r1_2 | +| For_ConditionUpdate() -> void | 1 | 4 | 7 | @r1_3 | +| For_ConditionUpdate() -> void | 2 | 3 | 0 | @r2_2 | +| For_ConditionUpdate() -> void | 2 | 3 | 1 | @mu0_1 | +| For_ConditionUpdate() -> void | 2 | 4 | 3 | @r2_3 | +| For_ConditionUpdate() -> void | 2 | 4 | 4 | @r2_1 | +| For_ConditionUpdate() -> void | 2 | 5 | 0 | @r2_2 | +| For_ConditionUpdate() -> void | 2 | 5 | 1 | @r2_4 | +| For_ConditionUpdate() -> void | 3 | 2 | 0 | @mu* | +| For_Continue_NoUpdate() -> void | 0 | 4 | 0 | @r0_2 | +| For_Continue_NoUpdate() -> void | 0 | 4 | 1 | @r0_3 | +| For_Continue_NoUpdate() -> void | 1 | 1 | 0 | @r1_0 | +| For_Continue_NoUpdate() -> void | 1 | 1 | 1 | @mu0_1 | +| For_Continue_NoUpdate() -> void | 1 | 3 | 3 | @r1_1 | +| For_Continue_NoUpdate() -> void | 1 | 3 | 4 | @r1_2 | +| For_Continue_NoUpdate() -> void | 1 | 4 | 7 | @r1_3 | +| For_Continue_NoUpdate() -> void | 2 | 1 | 0 | @r2_0 | +| For_Continue_NoUpdate() -> void | 2 | 1 | 1 | @mu0_1 | +| For_Continue_NoUpdate() -> void | 2 | 3 | 3 | @r2_1 | +| For_Continue_NoUpdate() -> void | 2 | 3 | 4 | @r2_2 | +| For_Continue_NoUpdate() -> void | 2 | 4 | 7 | @r2_3 | +| For_Continue_NoUpdate() -> void | 5 | 2 | 0 | @mu* | +| For_Continue_Update() -> void | 0 | 4 | 0 | @r0_2 | +| For_Continue_Update() -> void | 0 | 4 | 1 | @r0_3 | +| For_Continue_Update() -> void | 1 | 1 | 0 | @r1_0 | +| For_Continue_Update() -> void | 1 | 1 | 1 | @mu0_1 | +| For_Continue_Update() -> void | 1 | 3 | 3 | @r1_1 | +| For_Continue_Update() -> void | 1 | 3 | 4 | @r1_2 | +| For_Continue_Update() -> void | 1 | 4 | 7 | @r1_3 | +| For_Continue_Update() -> void | 2 | 1 | 0 | @r2_0 | +| For_Continue_Update() -> void | 2 | 1 | 1 | @mu0_1 | +| For_Continue_Update() -> void | 2 | 3 | 3 | @r2_1 | +| For_Continue_Update() -> void | 2 | 3 | 4 | @r2_2 | +| For_Continue_Update() -> void | 2 | 4 | 7 | @r2_3 | +| For_Continue_Update() -> void | 4 | 3 | 0 | @r4_2 | +| For_Continue_Update() -> void | 4 | 3 | 1 | @mu0_1 | +| For_Continue_Update() -> void | 4 | 4 | 3 | @r4_3 | +| For_Continue_Update() -> void | 4 | 4 | 4 | @r4_1 | +| For_Continue_Update() -> void | 4 | 5 | 0 | @r4_2 | +| For_Continue_Update() -> void | 4 | 5 | 1 | @r4_4 | +| For_Continue_Update() -> void | 5 | 2 | 0 | @mu* | +| For_Empty() -> void | 0 | 4 | 0 | @r0_2 | +| For_Empty() -> void | 0 | 4 | 1 | @r0_3 | +| For_Empty() -> void | 1 | 1 | 0 | @mu* | +| For_Init() -> void | 0 | 4 | 0 | @r0_2 | +| For_Init() -> void | 0 | 4 | 1 | @r0_3 | +| For_Init() -> void | 1 | 1 | 0 | @mu* | +| For_InitCondition() -> void | 0 | 4 | 0 | @r0_2 | +| For_InitCondition() -> void | 0 | 4 | 1 | @r0_3 | +| For_InitCondition() -> void | 1 | 1 | 0 | @r1_0 | +| For_InitCondition() -> void | 1 | 1 | 1 | @mu0_1 | +| For_InitCondition() -> void | 1 | 3 | 3 | @r1_1 | +| For_InitCondition() -> void | 1 | 3 | 4 | @r1_2 | +| For_InitCondition() -> void | 1 | 4 | 7 | @r1_3 | +| For_InitCondition() -> void | 3 | 2 | 0 | @mu* | +| For_InitConditionUpdate() -> void | 0 | 4 | 0 | @r0_2 | +| For_InitConditionUpdate() -> void | 0 | 4 | 1 | @r0_3 | +| For_InitConditionUpdate() -> void | 1 | 1 | 0 | @r1_0 | +| For_InitConditionUpdate() -> void | 1 | 1 | 1 | @mu0_1 | +| For_InitConditionUpdate() -> void | 1 | 3 | 3 | @r1_1 | +| For_InitConditionUpdate() -> void | 1 | 3 | 4 | @r1_2 | +| For_InitConditionUpdate() -> void | 1 | 4 | 7 | @r1_3 | +| For_InitConditionUpdate() -> void | 2 | 3 | 0 | @r2_2 | +| For_InitConditionUpdate() -> void | 2 | 3 | 1 | @mu0_1 | +| For_InitConditionUpdate() -> void | 2 | 4 | 3 | @r2_3 | +| For_InitConditionUpdate() -> void | 2 | 4 | 4 | @r2_1 | +| For_InitConditionUpdate() -> void | 2 | 5 | 0 | @r2_2 | +| For_InitConditionUpdate() -> void | 2 | 5 | 1 | @r2_4 | +| For_InitConditionUpdate() -> void | 3 | 2 | 0 | @mu* | +| For_InitUpdate() -> void | 0 | 4 | 0 | @r0_2 | +| For_InitUpdate() -> void | 0 | 4 | 1 | @r0_3 | +| For_InitUpdate() -> void | 1 | 1 | 0 | @mu* | +| For_InitUpdate() -> void | 2 | 3 | 0 | @r2_2 | +| For_InitUpdate() -> void | 2 | 3 | 1 | @mu0_1 | +| For_InitUpdate() -> void | 2 | 4 | 3 | @r2_3 | +| For_InitUpdate() -> void | 2 | 4 | 4 | @r2_1 | +| For_InitUpdate() -> void | 2 | 5 | 0 | @r2_2 | +| For_InitUpdate() -> void | 2 | 5 | 1 | @r2_4 | +| For_Update() -> void | 0 | 4 | 0 | @r0_2 | +| For_Update() -> void | 0 | 4 | 1 | @r0_3 | +| For_Update() -> void | 1 | 1 | 0 | @mu* | +| For_Update() -> void | 2 | 3 | 0 | @r2_2 | +| For_Update() -> void | 2 | 3 | 1 | @mu0_1 | +| For_Update() -> void | 2 | 4 | 3 | @r2_3 | +| For_Update() -> void | 2 | 4 | 4 | @r2_1 | +| For_Update() -> void | 2 | 5 | 0 | @r2_2 | +| For_Update() -> void | 2 | 5 | 1 | @r2_4 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 4 | 0 | @r0_3 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 4 | 1 | @r0_2 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 7 | 0 | @r0_6 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 7 | 1 | @r0_5 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 9 | 0 | @r0_8 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 9 | 1 | @mu0_1 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 10 | 2 | @r0_9 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 12 | 0 | @r0_11 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 12 | 1 | @r0_10 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 14 | 0 | @r0_13 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 14 | 1 | @mu0_1 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 15 | 2 | @r0_14 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 17 | 0 | @r0_16 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 17 | 1 | @r0_15 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 20 | 0 | @mu* | +| FunctionReferences() -> void | 0 | 4 | 0 | @r0_2 | +| FunctionReferences() -> void | 0 | 4 | 1 | @r0_3 | +| FunctionReferences() -> void | 0 | 7 | 0 | @r0_6 | +| FunctionReferences() -> void | 0 | 7 | 1 | @mu0_1 | +| FunctionReferences() -> void | 0 | 8 | 0 | @r0_5 | +| FunctionReferences() -> void | 0 | 8 | 1 | @r0_7 | +| FunctionReferences() -> void | 0 | 10 | 0 | @r0_9 | +| FunctionReferences() -> void | 0 | 10 | 1 | @mu0_1 | +| FunctionReferences() -> void | 0 | 12 | 9 | @r0_10 | +| FunctionReferences() -> void | 0 | 12 | 11 | @r0_11 | +| FunctionReferences() -> void | 0 | 15 | 0 | @mu* | +| HierarchyConversions() -> void | 0 | 4 | 9 | @r0_3 | +| HierarchyConversions() -> void | 0 | 4 | 10 | this:@r0_2 | +| HierarchyConversions() -> void | 0 | 7 | 9 | @r0_6 | +| HierarchyConversions() -> void | 0 | 7 | 10 | this:@r0_5 | +| HierarchyConversions() -> void | 0 | 10 | 9 | @r0_9 | +| HierarchyConversions() -> void | 0 | 10 | 10 | this:@r0_8 | +| HierarchyConversions() -> void | 0 | 13 | 0 | @r0_11 | +| HierarchyConversions() -> void | 0 | 13 | 1 | @r0_12 | +| HierarchyConversions() -> void | 0 | 16 | 0 | @r0_14 | +| HierarchyConversions() -> void | 0 | 16 | 1 | @r0_15 | +| HierarchyConversions() -> void | 0 | 19 | 0 | @r0_17 | +| HierarchyConversions() -> void | 0 | 19 | 1 | @r0_18 | +| HierarchyConversions() -> void | 0 | 23 | 2 | @r0_22 | +| HierarchyConversions() -> void | 0 | 24 | 9 | @r0_21 | +| HierarchyConversions() -> void | 0 | 24 | 10 | this:@r0_20 | +| HierarchyConversions() -> void | 0 | 24 | 11 | @r0_23 | +| HierarchyConversions() -> void | 0 | 29 | 2 | @r0_28 | +| HierarchyConversions() -> void | 0 | 30 | 9 | @r0_27 | +| HierarchyConversions() -> void | 0 | 30 | 11 | @r0_29 | +| HierarchyConversions() -> void | 0 | 31 | 2 | @r0_30 | +| HierarchyConversions() -> void | 0 | 32 | 9 | @r0_26 | +| HierarchyConversions() -> void | 0 | 32 | 10 | this:@r0_25 | +| HierarchyConversions() -> void | 0 | 32 | 11 | @r0_31 | +| HierarchyConversions() -> void | 0 | 37 | 2 | @r0_36 | +| HierarchyConversions() -> void | 0 | 38 | 9 | @r0_35 | +| HierarchyConversions() -> void | 0 | 38 | 11 | @r0_37 | +| HierarchyConversions() -> void | 0 | 39 | 2 | @r0_38 | +| HierarchyConversions() -> void | 0 | 40 | 9 | @r0_34 | +| HierarchyConversions() -> void | 0 | 40 | 10 | this:@r0_33 | +| HierarchyConversions() -> void | 0 | 40 | 11 | @r0_39 | +| HierarchyConversions() -> void | 0 | 42 | 0 | @r0_41 | +| HierarchyConversions() -> void | 0 | 42 | 1 | @mu0_1 | +| HierarchyConversions() -> void | 0 | 43 | 2 | @r0_42 | +| HierarchyConversions() -> void | 0 | 45 | 0 | @r0_44 | +| HierarchyConversions() -> void | 0 | 45 | 1 | @r0_43 | +| HierarchyConversions() -> void | 0 | 47 | 0 | @r0_46 | +| HierarchyConversions() -> void | 0 | 47 | 1 | @mu0_1 | +| HierarchyConversions() -> void | 0 | 48 | 2 | @r0_47 | +| HierarchyConversions() -> void | 0 | 50 | 0 | @r0_49 | +| HierarchyConversions() -> void | 0 | 50 | 1 | @r0_48 | +| HierarchyConversions() -> void | 0 | 52 | 0 | @r0_51 | +| HierarchyConversions() -> void | 0 | 52 | 1 | @mu0_1 | +| HierarchyConversions() -> void | 0 | 53 | 2 | @r0_52 | +| HierarchyConversions() -> void | 0 | 55 | 0 | @r0_54 | +| HierarchyConversions() -> void | 0 | 55 | 1 | @r0_53 | +| HierarchyConversions() -> void | 0 | 57 | 0 | @r0_56 | +| HierarchyConversions() -> void | 0 | 57 | 1 | @mu0_1 | +| HierarchyConversions() -> void | 0 | 58 | 2 | @r0_57 | +| HierarchyConversions() -> void | 0 | 60 | 0 | @r0_59 | +| HierarchyConversions() -> void | 0 | 60 | 1 | @r0_58 | +| HierarchyConversions() -> void | 0 | 64 | 2 | @r0_63 | +| HierarchyConversions() -> void | 0 | 65 | 2 | @r0_64 | +| HierarchyConversions() -> void | 0 | 66 | 9 | @r0_62 | +| HierarchyConversions() -> void | 0 | 66 | 10 | this:@r0_61 | +| HierarchyConversions() -> void | 0 | 66 | 11 | @r0_65 | +| HierarchyConversions() -> void | 0 | 70 | 2 | @r0_69 | +| HierarchyConversions() -> void | 0 | 71 | 2 | @r0_70 | +| HierarchyConversions() -> void | 0 | 72 | 9 | @r0_68 | +| HierarchyConversions() -> void | 0 | 72 | 10 | this:@r0_67 | +| HierarchyConversions() -> void | 0 | 72 | 11 | @r0_71 | +| HierarchyConversions() -> void | 0 | 74 | 0 | @r0_73 | +| HierarchyConversions() -> void | 0 | 74 | 1 | @mu0_1 | +| HierarchyConversions() -> void | 0 | 75 | 2 | @r0_74 | +| HierarchyConversions() -> void | 0 | 77 | 0 | @r0_76 | +| HierarchyConversions() -> void | 0 | 77 | 1 | @r0_75 | +| HierarchyConversions() -> void | 0 | 79 | 0 | @r0_78 | +| HierarchyConversions() -> void | 0 | 79 | 1 | @mu0_1 | +| HierarchyConversions() -> void | 0 | 80 | 2 | @r0_79 | +| HierarchyConversions() -> void | 0 | 82 | 0 | @r0_81 | +| HierarchyConversions() -> void | 0 | 82 | 1 | @r0_80 | +| HierarchyConversions() -> void | 0 | 84 | 0 | @r0_83 | +| HierarchyConversions() -> void | 0 | 84 | 1 | @mu0_1 | +| HierarchyConversions() -> void | 0 | 85 | 2 | @r0_84 | +| HierarchyConversions() -> void | 0 | 87 | 0 | @r0_86 | +| HierarchyConversions() -> void | 0 | 87 | 1 | @r0_85 | +| HierarchyConversions() -> void | 0 | 91 | 2 | @r0_90 | +| HierarchyConversions() -> void | 0 | 92 | 2 | @r0_91 | +| HierarchyConversions() -> void | 0 | 93 | 9 | @r0_89 | +| HierarchyConversions() -> void | 0 | 93 | 10 | this:@r0_88 | +| HierarchyConversions() -> void | 0 | 93 | 11 | @r0_92 | +| HierarchyConversions() -> void | 0 | 98 | 2 | @r0_97 | +| HierarchyConversions() -> void | 0 | 99 | 2 | @r0_98 | +| HierarchyConversions() -> void | 0 | 100 | 9 | @r0_96 | +| HierarchyConversions() -> void | 0 | 100 | 11 | @r0_99 | +| HierarchyConversions() -> void | 0 | 101 | 2 | @r0_100 | +| HierarchyConversions() -> void | 0 | 102 | 9 | @r0_95 | +| HierarchyConversions() -> void | 0 | 102 | 10 | this:@r0_94 | +| HierarchyConversions() -> void | 0 | 102 | 11 | @r0_101 | +| HierarchyConversions() -> void | 0 | 107 | 2 | @r0_106 | +| HierarchyConversions() -> void | 0 | 108 | 2 | @r0_107 | +| HierarchyConversions() -> void | 0 | 109 | 9 | @r0_105 | +| HierarchyConversions() -> void | 0 | 109 | 11 | @r0_108 | +| HierarchyConversions() -> void | 0 | 110 | 2 | @r0_109 | +| HierarchyConversions() -> void | 0 | 111 | 9 | @r0_104 | +| HierarchyConversions() -> void | 0 | 111 | 10 | this:@r0_103 | +| HierarchyConversions() -> void | 0 | 111 | 11 | @r0_110 | +| HierarchyConversions() -> void | 0 | 113 | 0 | @r0_112 | +| HierarchyConversions() -> void | 0 | 113 | 1 | @mu0_1 | +| HierarchyConversions() -> void | 0 | 114 | 2 | @r0_113 | +| HierarchyConversions() -> void | 0 | 115 | 2 | @r0_114 | +| HierarchyConversions() -> void | 0 | 117 | 0 | @r0_116 | +| HierarchyConversions() -> void | 0 | 117 | 1 | @r0_115 | +| HierarchyConversions() -> void | 0 | 119 | 0 | @r0_118 | +| HierarchyConversions() -> void | 0 | 119 | 1 | @mu0_1 | +| HierarchyConversions() -> void | 0 | 120 | 2 | @r0_119 | +| HierarchyConversions() -> void | 0 | 121 | 2 | @r0_120 | +| HierarchyConversions() -> void | 0 | 123 | 0 | @r0_122 | +| HierarchyConversions() -> void | 0 | 123 | 1 | @r0_121 | +| HierarchyConversions() -> void | 0 | 125 | 0 | @r0_124 | +| HierarchyConversions() -> void | 0 | 125 | 1 | @mu0_1 | +| HierarchyConversions() -> void | 0 | 126 | 2 | @r0_125 | +| HierarchyConversions() -> void | 0 | 127 | 2 | @r0_126 | +| HierarchyConversions() -> void | 0 | 129 | 0 | @r0_128 | +| HierarchyConversions() -> void | 0 | 129 | 1 | @r0_127 | +| HierarchyConversions() -> void | 0 | 131 | 0 | @r0_130 | +| HierarchyConversions() -> void | 0 | 131 | 1 | @mu0_1 | +| HierarchyConversions() -> void | 0 | 132 | 2 | @r0_131 | +| HierarchyConversions() -> void | 0 | 134 | 0 | @r0_133 | +| HierarchyConversions() -> void | 0 | 134 | 1 | @r0_132 | +| HierarchyConversions() -> void | 0 | 138 | 2 | @r0_137 | +| HierarchyConversions() -> void | 0 | 139 | 2 | @r0_138 | +| HierarchyConversions() -> void | 0 | 140 | 2 | @r0_139 | +| HierarchyConversions() -> void | 0 | 141 | 9 | @r0_136 | +| HierarchyConversions() -> void | 0 | 141 | 10 | this:@r0_135 | +| HierarchyConversions() -> void | 0 | 141 | 11 | @r0_140 | +| HierarchyConversions() -> void | 0 | 145 | 2 | @r0_144 | +| HierarchyConversions() -> void | 0 | 146 | 2 | @r0_145 | +| HierarchyConversions() -> void | 0 | 147 | 2 | @r0_146 | +| HierarchyConversions() -> void | 0 | 148 | 9 | @r0_143 | +| HierarchyConversions() -> void | 0 | 148 | 10 | this:@r0_142 | +| HierarchyConversions() -> void | 0 | 148 | 11 | @r0_147 | +| HierarchyConversions() -> void | 0 | 150 | 0 | @r0_149 | +| HierarchyConversions() -> void | 0 | 150 | 1 | @mu0_1 | +| HierarchyConversions() -> void | 0 | 151 | 2 | @r0_150 | +| HierarchyConversions() -> void | 0 | 152 | 2 | @r0_151 | +| HierarchyConversions() -> void | 0 | 154 | 0 | @r0_153 | +| HierarchyConversions() -> void | 0 | 154 | 1 | @r0_152 | +| HierarchyConversions() -> void | 0 | 156 | 0 | @r0_155 | +| HierarchyConversions() -> void | 0 | 156 | 1 | @mu0_1 | +| HierarchyConversions() -> void | 0 | 157 | 2 | @r0_156 | +| HierarchyConversions() -> void | 0 | 158 | 2 | @r0_157 | +| HierarchyConversions() -> void | 0 | 160 | 0 | @r0_159 | +| HierarchyConversions() -> void | 0 | 160 | 1 | @r0_158 | +| HierarchyConversions() -> void | 0 | 162 | 0 | @r0_161 | +| HierarchyConversions() -> void | 0 | 162 | 1 | @mu0_1 | +| HierarchyConversions() -> void | 0 | 163 | 2 | @r0_162 | +| HierarchyConversions() -> void | 0 | 165 | 0 | @r0_164 | +| HierarchyConversions() -> void | 0 | 165 | 1 | @r0_163 | +| HierarchyConversions() -> void | 0 | 168 | 0 | @r0_166 | +| HierarchyConversions() -> void | 0 | 168 | 1 | @r0_167 | +| HierarchyConversions() -> void | 0 | 171 | 0 | @r0_169 | +| HierarchyConversions() -> void | 0 | 171 | 1 | @r0_170 | +| HierarchyConversions() -> void | 0 | 173 | 0 | @r0_172 | +| HierarchyConversions() -> void | 0 | 173 | 1 | @mu0_1 | +| HierarchyConversions() -> void | 0 | 174 | 2 | @r0_173 | +| HierarchyConversions() -> void | 0 | 176 | 0 | @r0_175 | +| HierarchyConversions() -> void | 0 | 176 | 1 | @r0_174 | +| HierarchyConversions() -> void | 0 | 178 | 0 | @r0_177 | +| HierarchyConversions() -> void | 0 | 178 | 1 | @mu0_1 | +| HierarchyConversions() -> void | 0 | 179 | 2 | @r0_178 | +| HierarchyConversions() -> void | 0 | 181 | 0 | @r0_180 | +| HierarchyConversions() -> void | 0 | 181 | 1 | @r0_179 | +| HierarchyConversions() -> void | 0 | 184 | 0 | @mu* | +| IfStatements(bool, int, int) -> void | 0 | 4 | 0 | @r0_3 | +| IfStatements(bool, int, int) -> void | 0 | 4 | 1 | @r0_2 | +| IfStatements(bool, int, int) -> void | 0 | 7 | 0 | @r0_6 | +| IfStatements(bool, int, int) -> void | 0 | 7 | 1 | @r0_5 | +| IfStatements(bool, int, int) -> void | 0 | 10 | 0 | @r0_9 | +| IfStatements(bool, int, int) -> void | 0 | 10 | 1 | @r0_8 | +| IfStatements(bool, int, int) -> void | 0 | 12 | 0 | @r0_11 | +| IfStatements(bool, int, int) -> void | 0 | 12 | 1 | @mu0_1 | +| IfStatements(bool, int, int) -> void | 0 | 13 | 7 | @r0_12 | +| IfStatements(bool, int, int) -> void | 1 | 1 | 0 | @r1_0 | +| IfStatements(bool, int, int) -> void | 1 | 1 | 1 | @mu0_1 | +| IfStatements(bool, int, int) -> void | 1 | 2 | 7 | @r1_1 | +| IfStatements(bool, int, int) -> void | 2 | 1 | 0 | @r2_0 | +| IfStatements(bool, int, int) -> void | 2 | 1 | 1 | @mu0_1 | +| IfStatements(bool, int, int) -> void | 2 | 3 | 0 | @r2_2 | +| IfStatements(bool, int, int) -> void | 2 | 3 | 1 | @r2_1 | +| IfStatements(bool, int, int) -> void | 3 | 1 | 0 | @r3_0 | +| IfStatements(bool, int, int) -> void | 3 | 1 | 1 | @mu0_1 | +| IfStatements(bool, int, int) -> void | 3 | 3 | 3 | @r3_1 | +| IfStatements(bool, int, int) -> void | 3 | 3 | 4 | @r3_2 | +| IfStatements(bool, int, int) -> void | 3 | 4 | 7 | @r3_3 | +| IfStatements(bool, int, int) -> void | 4 | 2 | 0 | @r4_1 | +| IfStatements(bool, int, int) -> void | 4 | 2 | 1 | @r4_0 | +| IfStatements(bool, int, int) -> void | 5 | 2 | 0 | @r5_1 | +| IfStatements(bool, int, int) -> void | 5 | 2 | 1 | @r5_0 | +| IfStatements(bool, int, int) -> void | 6 | 2 | 0 | @mu* | +| InitArray() -> void | 0 | 4 | 0 | @r0_3 | +| InitArray() -> void | 0 | 4 | 1 | @mu0_1 | +| InitArray() -> void | 0 | 5 | 0 | @r0_2 | +| InitArray() -> void | 0 | 5 | 1 | @r0_4 | +| InitArray() -> void | 0 | 8 | 3 | @r0_2 | +| InitArray() -> void | 0 | 8 | 4 | @r0_7 | +| InitArray() -> void | 0 | 9 | 0 | @r0_8 | +| InitArray() -> void | 0 | 9 | 1 | @r0_6 | +| InitArray() -> void | 0 | 12 | 0 | @r0_11 | +| InitArray() -> void | 0 | 12 | 1 | @mu0_1 | +| InitArray() -> void | 0 | 13 | 0 | @r0_10 | +| InitArray() -> void | 0 | 13 | 1 | @r0_12 | +| InitArray() -> void | 0 | 16 | 0 | @r0_15 | +| InitArray() -> void | 0 | 16 | 1 | @mu0_1 | +| InitArray() -> void | 0 | 17 | 0 | @r0_14 | +| InitArray() -> void | 0 | 17 | 1 | @r0_16 | +| InitArray() -> void | 0 | 20 | 0 | @r0_18 | +| InitArray() -> void | 0 | 20 | 1 | @r0_19 | +| InitArray() -> void | 0 | 23 | 3 | @r0_21 | +| InitArray() -> void | 0 | 23 | 4 | @r0_22 | +| InitArray() -> void | 0 | 25 | 0 | @r0_23 | +| InitArray() -> void | 0 | 25 | 1 | @r0_24 | +| InitArray() -> void | 0 | 28 | 3 | @r0_26 | +| InitArray() -> void | 0 | 28 | 4 | @r0_27 | +| InitArray() -> void | 0 | 30 | 0 | @r0_28 | +| InitArray() -> void | 0 | 30 | 1 | @r0_29 | +| InitArray() -> void | 0 | 32 | 3 | @r0_26 | +| InitArray() -> void | 0 | 32 | 4 | @r0_31 | +| InitArray() -> void | 0 | 34 | 0 | @r0_32 | +| InitArray() -> void | 0 | 34 | 1 | @r0_33 | +| InitArray() -> void | 0 | 37 | 3 | @r0_35 | +| InitArray() -> void | 0 | 37 | 4 | @r0_36 | +| InitArray() -> void | 0 | 39 | 0 | @r0_37 | +| InitArray() -> void | 0 | 39 | 1 | @r0_38 | +| InitArray() -> void | 0 | 41 | 3 | @r0_35 | +| InitArray() -> void | 0 | 41 | 4 | @r0_40 | +| InitArray() -> void | 0 | 43 | 0 | @r0_41 | +| InitArray() -> void | 0 | 43 | 1 | @r0_42 | +| InitArray() -> void | 0 | 46 | 3 | @r0_44 | +| InitArray() -> void | 0 | 46 | 4 | @r0_45 | +| InitArray() -> void | 0 | 48 | 0 | @r0_46 | +| InitArray() -> void | 0 | 48 | 1 | @r0_47 | +| InitArray() -> void | 0 | 50 | 3 | @r0_44 | +| InitArray() -> void | 0 | 50 | 4 | @r0_49 | +| InitArray() -> void | 0 | 52 | 0 | @r0_50 | +| InitArray() -> void | 0 | 52 | 1 | @r0_51 | +| InitArray() -> void | 0 | 55 | 0 | @mu* | +| InitList(int, float) -> void | 0 | 4 | 0 | @r0_3 | +| InitList(int, float) -> void | 0 | 4 | 1 | @r0_2 | +| InitList(int, float) -> void | 0 | 7 | 0 | @r0_6 | +| InitList(int, float) -> void | 0 | 7 | 1 | @r0_5 | +| InitList(int, float) -> void | 0 | 9 | 2 | @r0_8 | +| InitList(int, float) -> void | 0 | 11 | 0 | @r0_10 | +| InitList(int, float) -> void | 0 | 11 | 1 | @mu0_1 | +| InitList(int, float) -> void | 0 | 12 | 0 | @r0_9 | +| InitList(int, float) -> void | 0 | 12 | 1 | @r0_11 | +| InitList(int, float) -> void | 0 | 13 | 2 | @r0_8 | +| InitList(int, float) -> void | 0 | 15 | 0 | @r0_14 | +| InitList(int, float) -> void | 0 | 15 | 1 | @mu0_1 | +| InitList(int, float) -> void | 0 | 16 | 2 | @r0_15 | +| InitList(int, float) -> void | 0 | 17 | 0 | @r0_13 | +| InitList(int, float) -> void | 0 | 17 | 1 | @r0_16 | +| InitList(int, float) -> void | 0 | 19 | 2 | @r0_18 | +| InitList(int, float) -> void | 0 | 21 | 0 | @r0_20 | +| InitList(int, float) -> void | 0 | 21 | 1 | @mu0_1 | +| InitList(int, float) -> void | 0 | 22 | 0 | @r0_19 | +| InitList(int, float) -> void | 0 | 22 | 1 | @r0_21 | +| InitList(int, float) -> void | 0 | 23 | 2 | @r0_18 | +| InitList(int, float) -> void | 0 | 25 | 0 | @r0_23 | +| InitList(int, float) -> void | 0 | 25 | 1 | @r0_24 | +| InitList(int, float) -> void | 0 | 27 | 2 | @r0_26 | +| InitList(int, float) -> void | 0 | 29 | 0 | @r0_27 | +| InitList(int, float) -> void | 0 | 29 | 1 | @r0_28 | +| InitList(int, float) -> void | 0 | 30 | 2 | @r0_26 | +| InitList(int, float) -> void | 0 | 32 | 0 | @r0_30 | +| InitList(int, float) -> void | 0 | 32 | 1 | @r0_31 | +| InitList(int, float) -> void | 0 | 35 | 0 | @r0_33 | +| InitList(int, float) -> void | 0 | 35 | 1 | @r0_34 | +| InitList(int, float) -> void | 0 | 38 | 0 | @r0_36 | +| InitList(int, float) -> void | 0 | 38 | 1 | @r0_37 | +| InitList(int, float) -> void | 0 | 41 | 0 | @mu* | +| InitReference(int) -> void | 0 | 4 | 0 | @r0_3 | +| InitReference(int) -> void | 0 | 4 | 1 | @r0_2 | +| InitReference(int) -> void | 0 | 7 | 0 | @r0_5 | +| InitReference(int) -> void | 0 | 7 | 1 | @r0_6 | +| InitReference(int) -> void | 0 | 10 | 0 | @r0_9 | +| InitReference(int) -> void | 0 | 10 | 1 | @mu0_1 | +| InitReference(int) -> void | 0 | 11 | 0 | @r0_8 | +| InitReference(int) -> void | 0 | 11 | 1 | @r0_10 | +| InitReference(int) -> void | 0 | 14 | 9 | @r0_13 | +| InitReference(int) -> void | 0 | 15 | 2 | @r0_14 | +| InitReference(int) -> void | 0 | 16 | 0 | @r0_12 | +| InitReference(int) -> void | 0 | 16 | 1 | @r0_15 | +| InitReference(int) -> void | 0 | 19 | 0 | @mu* | +| IntegerCompare(int, int) -> void | 0 | 4 | 0 | @r0_3 | +| IntegerCompare(int, int) -> void | 0 | 4 | 1 | @r0_2 | +| IntegerCompare(int, int) -> void | 0 | 7 | 0 | @r0_6 | +| IntegerCompare(int, int) -> void | 0 | 7 | 1 | @r0_5 | +| IntegerCompare(int, int) -> void | 0 | 10 | 0 | @r0_8 | +| IntegerCompare(int, int) -> void | 0 | 10 | 1 | @r0_9 | +| IntegerCompare(int, int) -> void | 0 | 12 | 0 | @r0_11 | +| IntegerCompare(int, int) -> void | 0 | 12 | 1 | @mu0_1 | +| IntegerCompare(int, int) -> void | 0 | 14 | 0 | @r0_13 | +| IntegerCompare(int, int) -> void | 0 | 14 | 1 | @mu0_1 | +| IntegerCompare(int, int) -> void | 0 | 15 | 3 | @r0_12 | +| IntegerCompare(int, int) -> void | 0 | 15 | 4 | @r0_14 | +| IntegerCompare(int, int) -> void | 0 | 17 | 0 | @r0_16 | +| IntegerCompare(int, int) -> void | 0 | 17 | 1 | @r0_15 | +| IntegerCompare(int, int) -> void | 0 | 19 | 0 | @r0_18 | +| IntegerCompare(int, int) -> void | 0 | 19 | 1 | @mu0_1 | +| IntegerCompare(int, int) -> void | 0 | 21 | 0 | @r0_20 | +| IntegerCompare(int, int) -> void | 0 | 21 | 1 | @mu0_1 | +| IntegerCompare(int, int) -> void | 0 | 22 | 3 | @r0_19 | +| IntegerCompare(int, int) -> void | 0 | 22 | 4 | @r0_21 | +| IntegerCompare(int, int) -> void | 0 | 24 | 0 | @r0_23 | +| IntegerCompare(int, int) -> void | 0 | 24 | 1 | @r0_22 | +| IntegerCompare(int, int) -> void | 0 | 26 | 0 | @r0_25 | +| IntegerCompare(int, int) -> void | 0 | 26 | 1 | @mu0_1 | +| IntegerCompare(int, int) -> void | 0 | 28 | 0 | @r0_27 | +| IntegerCompare(int, int) -> void | 0 | 28 | 1 | @mu0_1 | +| IntegerCompare(int, int) -> void | 0 | 29 | 3 | @r0_26 | +| IntegerCompare(int, int) -> void | 0 | 29 | 4 | @r0_28 | +| IntegerCompare(int, int) -> void | 0 | 31 | 0 | @r0_30 | +| IntegerCompare(int, int) -> void | 0 | 31 | 1 | @r0_29 | +| IntegerCompare(int, int) -> void | 0 | 33 | 0 | @r0_32 | +| IntegerCompare(int, int) -> void | 0 | 33 | 1 | @mu0_1 | +| IntegerCompare(int, int) -> void | 0 | 35 | 0 | @r0_34 | +| IntegerCompare(int, int) -> void | 0 | 35 | 1 | @mu0_1 | +| IntegerCompare(int, int) -> void | 0 | 36 | 3 | @r0_33 | +| IntegerCompare(int, int) -> void | 0 | 36 | 4 | @r0_35 | +| IntegerCompare(int, int) -> void | 0 | 38 | 0 | @r0_37 | +| IntegerCompare(int, int) -> void | 0 | 38 | 1 | @r0_36 | +| IntegerCompare(int, int) -> void | 0 | 40 | 0 | @r0_39 | +| IntegerCompare(int, int) -> void | 0 | 40 | 1 | @mu0_1 | +| IntegerCompare(int, int) -> void | 0 | 42 | 0 | @r0_41 | +| IntegerCompare(int, int) -> void | 0 | 42 | 1 | @mu0_1 | +| IntegerCompare(int, int) -> void | 0 | 43 | 3 | @r0_40 | +| IntegerCompare(int, int) -> void | 0 | 43 | 4 | @r0_42 | +| IntegerCompare(int, int) -> void | 0 | 45 | 0 | @r0_44 | +| IntegerCompare(int, int) -> void | 0 | 45 | 1 | @r0_43 | +| IntegerCompare(int, int) -> void | 0 | 47 | 0 | @r0_46 | +| IntegerCompare(int, int) -> void | 0 | 47 | 1 | @mu0_1 | +| IntegerCompare(int, int) -> void | 0 | 49 | 0 | @r0_48 | +| IntegerCompare(int, int) -> void | 0 | 49 | 1 | @mu0_1 | +| IntegerCompare(int, int) -> void | 0 | 50 | 3 | @r0_47 | +| IntegerCompare(int, int) -> void | 0 | 50 | 4 | @r0_49 | +| IntegerCompare(int, int) -> void | 0 | 52 | 0 | @r0_51 | +| IntegerCompare(int, int) -> void | 0 | 52 | 1 | @r0_50 | +| IntegerCompare(int, int) -> void | 0 | 55 | 0 | @mu* | +| IntegerCrement(int) -> void | 0 | 4 | 0 | @r0_3 | +| IntegerCrement(int) -> void | 0 | 4 | 1 | @r0_2 | +| IntegerCrement(int) -> void | 0 | 7 | 0 | @r0_5 | +| IntegerCrement(int) -> void | 0 | 7 | 1 | @r0_6 | +| IntegerCrement(int) -> void | 0 | 9 | 0 | @r0_8 | +| IntegerCrement(int) -> void | 0 | 9 | 1 | @mu0_1 | +| IntegerCrement(int) -> void | 0 | 11 | 3 | @r0_9 | +| IntegerCrement(int) -> void | 0 | 11 | 4 | @r0_10 | +| IntegerCrement(int) -> void | 0 | 12 | 0 | @r0_8 | +| IntegerCrement(int) -> void | 0 | 12 | 1 | @r0_11 | +| IntegerCrement(int) -> void | 0 | 14 | 0 | @r0_13 | +| IntegerCrement(int) -> void | 0 | 14 | 1 | @r0_11 | +| IntegerCrement(int) -> void | 0 | 16 | 0 | @r0_15 | +| IntegerCrement(int) -> void | 0 | 16 | 1 | @mu0_1 | +| IntegerCrement(int) -> void | 0 | 18 | 3 | @r0_16 | +| IntegerCrement(int) -> void | 0 | 18 | 4 | @r0_17 | +| IntegerCrement(int) -> void | 0 | 19 | 0 | @r0_15 | +| IntegerCrement(int) -> void | 0 | 19 | 1 | @r0_18 | +| IntegerCrement(int) -> void | 0 | 21 | 0 | @r0_20 | +| IntegerCrement(int) -> void | 0 | 21 | 1 | @r0_18 | +| IntegerCrement(int) -> void | 0 | 23 | 0 | @r0_22 | +| IntegerCrement(int) -> void | 0 | 23 | 1 | @mu0_1 | +| IntegerCrement(int) -> void | 0 | 25 | 3 | @r0_23 | +| IntegerCrement(int) -> void | 0 | 25 | 4 | @r0_24 | +| IntegerCrement(int) -> void | 0 | 26 | 0 | @r0_22 | +| IntegerCrement(int) -> void | 0 | 26 | 1 | @r0_25 | +| IntegerCrement(int) -> void | 0 | 28 | 0 | @r0_27 | +| IntegerCrement(int) -> void | 0 | 28 | 1 | @r0_23 | +| IntegerCrement(int) -> void | 0 | 30 | 0 | @r0_29 | +| IntegerCrement(int) -> void | 0 | 30 | 1 | @mu0_1 | +| IntegerCrement(int) -> void | 0 | 32 | 3 | @r0_30 | +| IntegerCrement(int) -> void | 0 | 32 | 4 | @r0_31 | +| IntegerCrement(int) -> void | 0 | 33 | 0 | @r0_29 | +| IntegerCrement(int) -> void | 0 | 33 | 1 | @r0_32 | +| IntegerCrement(int) -> void | 0 | 35 | 0 | @r0_34 | +| IntegerCrement(int) -> void | 0 | 35 | 1 | @r0_30 | +| IntegerCrement(int) -> void | 0 | 38 | 0 | @mu* | +| IntegerCrement_LValue(int) -> void | 0 | 4 | 0 | @r0_3 | +| IntegerCrement_LValue(int) -> void | 0 | 4 | 1 | @r0_2 | +| IntegerCrement_LValue(int) -> void | 0 | 7 | 0 | @r0_5 | +| IntegerCrement_LValue(int) -> void | 0 | 7 | 1 | @r0_6 | +| IntegerCrement_LValue(int) -> void | 0 | 9 | 0 | @r0_8 | +| IntegerCrement_LValue(int) -> void | 0 | 9 | 1 | @mu0_1 | +| IntegerCrement_LValue(int) -> void | 0 | 11 | 3 | @r0_9 | +| IntegerCrement_LValue(int) -> void | 0 | 11 | 4 | @r0_10 | +| IntegerCrement_LValue(int) -> void | 0 | 12 | 0 | @r0_8 | +| IntegerCrement_LValue(int) -> void | 0 | 12 | 1 | @r0_11 | +| IntegerCrement_LValue(int) -> void | 0 | 14 | 0 | @r0_13 | +| IntegerCrement_LValue(int) -> void | 0 | 14 | 1 | @r0_8 | +| IntegerCrement_LValue(int) -> void | 0 | 16 | 0 | @r0_15 | +| IntegerCrement_LValue(int) -> void | 0 | 16 | 1 | @mu0_1 | +| IntegerCrement_LValue(int) -> void | 0 | 18 | 3 | @r0_16 | +| IntegerCrement_LValue(int) -> void | 0 | 18 | 4 | @r0_17 | +| IntegerCrement_LValue(int) -> void | 0 | 19 | 0 | @r0_15 | +| IntegerCrement_LValue(int) -> void | 0 | 19 | 1 | @r0_18 | +| IntegerCrement_LValue(int) -> void | 0 | 21 | 0 | @r0_20 | +| IntegerCrement_LValue(int) -> void | 0 | 21 | 1 | @r0_15 | +| IntegerCrement_LValue(int) -> void | 0 | 24 | 0 | @mu* | +| IntegerOps(int, int) -> void | 0 | 4 | 0 | @r0_3 | +| IntegerOps(int, int) -> void | 0 | 4 | 1 | @r0_2 | +| IntegerOps(int, int) -> void | 0 | 7 | 0 | @r0_6 | +| IntegerOps(int, int) -> void | 0 | 7 | 1 | @r0_5 | +| IntegerOps(int, int) -> void | 0 | 10 | 0 | @r0_8 | +| IntegerOps(int, int) -> void | 0 | 10 | 1 | @r0_9 | +| IntegerOps(int, int) -> void | 0 | 12 | 0 | @r0_11 | +| IntegerOps(int, int) -> void | 0 | 12 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 14 | 0 | @r0_13 | +| IntegerOps(int, int) -> void | 0 | 14 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 15 | 3 | @r0_12 | +| IntegerOps(int, int) -> void | 0 | 15 | 4 | @r0_14 | +| IntegerOps(int, int) -> void | 0 | 17 | 0 | @r0_16 | +| IntegerOps(int, int) -> void | 0 | 17 | 1 | @r0_15 | +| IntegerOps(int, int) -> void | 0 | 19 | 0 | @r0_18 | +| IntegerOps(int, int) -> void | 0 | 19 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 21 | 0 | @r0_20 | +| IntegerOps(int, int) -> void | 0 | 21 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 22 | 3 | @r0_19 | +| IntegerOps(int, int) -> void | 0 | 22 | 4 | @r0_21 | +| IntegerOps(int, int) -> void | 0 | 24 | 0 | @r0_23 | +| IntegerOps(int, int) -> void | 0 | 24 | 1 | @r0_22 | +| IntegerOps(int, int) -> void | 0 | 26 | 0 | @r0_25 | +| IntegerOps(int, int) -> void | 0 | 26 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 28 | 0 | @r0_27 | +| IntegerOps(int, int) -> void | 0 | 28 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 29 | 3 | @r0_26 | +| IntegerOps(int, int) -> void | 0 | 29 | 4 | @r0_28 | +| IntegerOps(int, int) -> void | 0 | 31 | 0 | @r0_30 | +| IntegerOps(int, int) -> void | 0 | 31 | 1 | @r0_29 | +| IntegerOps(int, int) -> void | 0 | 33 | 0 | @r0_32 | +| IntegerOps(int, int) -> void | 0 | 33 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 35 | 0 | @r0_34 | +| IntegerOps(int, int) -> void | 0 | 35 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 36 | 3 | @r0_33 | +| IntegerOps(int, int) -> void | 0 | 36 | 4 | @r0_35 | +| IntegerOps(int, int) -> void | 0 | 38 | 0 | @r0_37 | +| IntegerOps(int, int) -> void | 0 | 38 | 1 | @r0_36 | +| IntegerOps(int, int) -> void | 0 | 40 | 0 | @r0_39 | +| IntegerOps(int, int) -> void | 0 | 40 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 42 | 0 | @r0_41 | +| IntegerOps(int, int) -> void | 0 | 42 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 43 | 3 | @r0_40 | +| IntegerOps(int, int) -> void | 0 | 43 | 4 | @r0_42 | +| IntegerOps(int, int) -> void | 0 | 45 | 0 | @r0_44 | +| IntegerOps(int, int) -> void | 0 | 45 | 1 | @r0_43 | +| IntegerOps(int, int) -> void | 0 | 47 | 0 | @r0_46 | +| IntegerOps(int, int) -> void | 0 | 47 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 49 | 0 | @r0_48 | +| IntegerOps(int, int) -> void | 0 | 49 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 50 | 3 | @r0_47 | +| IntegerOps(int, int) -> void | 0 | 50 | 4 | @r0_49 | +| IntegerOps(int, int) -> void | 0 | 52 | 0 | @r0_51 | +| IntegerOps(int, int) -> void | 0 | 52 | 1 | @r0_50 | +| IntegerOps(int, int) -> void | 0 | 54 | 0 | @r0_53 | +| IntegerOps(int, int) -> void | 0 | 54 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 56 | 0 | @r0_55 | +| IntegerOps(int, int) -> void | 0 | 56 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 57 | 3 | @r0_54 | +| IntegerOps(int, int) -> void | 0 | 57 | 4 | @r0_56 | +| IntegerOps(int, int) -> void | 0 | 59 | 0 | @r0_58 | +| IntegerOps(int, int) -> void | 0 | 59 | 1 | @r0_57 | +| IntegerOps(int, int) -> void | 0 | 61 | 0 | @r0_60 | +| IntegerOps(int, int) -> void | 0 | 61 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 63 | 0 | @r0_62 | +| IntegerOps(int, int) -> void | 0 | 63 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 64 | 3 | @r0_61 | +| IntegerOps(int, int) -> void | 0 | 64 | 4 | @r0_63 | +| IntegerOps(int, int) -> void | 0 | 66 | 0 | @r0_65 | +| IntegerOps(int, int) -> void | 0 | 66 | 1 | @r0_64 | +| IntegerOps(int, int) -> void | 0 | 68 | 0 | @r0_67 | +| IntegerOps(int, int) -> void | 0 | 68 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 70 | 0 | @r0_69 | +| IntegerOps(int, int) -> void | 0 | 70 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 71 | 3 | @r0_68 | +| IntegerOps(int, int) -> void | 0 | 71 | 4 | @r0_70 | +| IntegerOps(int, int) -> void | 0 | 73 | 0 | @r0_72 | +| IntegerOps(int, int) -> void | 0 | 73 | 1 | @r0_71 | +| IntegerOps(int, int) -> void | 0 | 75 | 0 | @r0_74 | +| IntegerOps(int, int) -> void | 0 | 75 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 77 | 0 | @r0_76 | +| IntegerOps(int, int) -> void | 0 | 77 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 78 | 3 | @r0_75 | +| IntegerOps(int, int) -> void | 0 | 78 | 4 | @r0_77 | +| IntegerOps(int, int) -> void | 0 | 80 | 0 | @r0_79 | +| IntegerOps(int, int) -> void | 0 | 80 | 1 | @r0_78 | +| IntegerOps(int, int) -> void | 0 | 82 | 0 | @r0_81 | +| IntegerOps(int, int) -> void | 0 | 82 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 84 | 0 | @r0_83 | +| IntegerOps(int, int) -> void | 0 | 84 | 1 | @r0_82 | +| IntegerOps(int, int) -> void | 0 | 86 | 0 | @r0_85 | +| IntegerOps(int, int) -> void | 0 | 86 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 88 | 0 | @r0_87 | +| IntegerOps(int, int) -> void | 0 | 88 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 89 | 3 | @r0_88 | +| IntegerOps(int, int) -> void | 0 | 89 | 4 | @r0_86 | +| IntegerOps(int, int) -> void | 0 | 90 | 0 | @r0_87 | +| IntegerOps(int, int) -> void | 0 | 90 | 1 | @r0_89 | +| IntegerOps(int, int) -> void | 0 | 92 | 0 | @r0_91 | +| IntegerOps(int, int) -> void | 0 | 92 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 94 | 0 | @r0_93 | +| IntegerOps(int, int) -> void | 0 | 94 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 95 | 3 | @r0_94 | +| IntegerOps(int, int) -> void | 0 | 95 | 4 | @r0_92 | +| IntegerOps(int, int) -> void | 0 | 96 | 0 | @r0_93 | +| IntegerOps(int, int) -> void | 0 | 96 | 1 | @r0_95 | +| IntegerOps(int, int) -> void | 0 | 98 | 0 | @r0_97 | +| IntegerOps(int, int) -> void | 0 | 98 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 100 | 0 | @r0_99 | +| IntegerOps(int, int) -> void | 0 | 100 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 101 | 3 | @r0_100 | +| IntegerOps(int, int) -> void | 0 | 101 | 4 | @r0_98 | +| IntegerOps(int, int) -> void | 0 | 102 | 0 | @r0_99 | +| IntegerOps(int, int) -> void | 0 | 102 | 1 | @r0_101 | +| IntegerOps(int, int) -> void | 0 | 104 | 0 | @r0_103 | +| IntegerOps(int, int) -> void | 0 | 104 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 106 | 0 | @r0_105 | +| IntegerOps(int, int) -> void | 0 | 106 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 107 | 3 | @r0_106 | +| IntegerOps(int, int) -> void | 0 | 107 | 4 | @r0_104 | +| IntegerOps(int, int) -> void | 0 | 108 | 0 | @r0_105 | +| IntegerOps(int, int) -> void | 0 | 108 | 1 | @r0_107 | +| IntegerOps(int, int) -> void | 0 | 110 | 0 | @r0_109 | +| IntegerOps(int, int) -> void | 0 | 110 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 112 | 0 | @r0_111 | +| IntegerOps(int, int) -> void | 0 | 112 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 113 | 3 | @r0_112 | +| IntegerOps(int, int) -> void | 0 | 113 | 4 | @r0_110 | +| IntegerOps(int, int) -> void | 0 | 114 | 0 | @r0_111 | +| IntegerOps(int, int) -> void | 0 | 114 | 1 | @r0_113 | +| IntegerOps(int, int) -> void | 0 | 116 | 0 | @r0_115 | +| IntegerOps(int, int) -> void | 0 | 116 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 118 | 0 | @r0_117 | +| IntegerOps(int, int) -> void | 0 | 118 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 119 | 3 | @r0_118 | +| IntegerOps(int, int) -> void | 0 | 119 | 4 | @r0_116 | +| IntegerOps(int, int) -> void | 0 | 120 | 0 | @r0_117 | +| IntegerOps(int, int) -> void | 0 | 120 | 1 | @r0_119 | +| IntegerOps(int, int) -> void | 0 | 122 | 0 | @r0_121 | +| IntegerOps(int, int) -> void | 0 | 122 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 124 | 0 | @r0_123 | +| IntegerOps(int, int) -> void | 0 | 124 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 125 | 3 | @r0_124 | +| IntegerOps(int, int) -> void | 0 | 125 | 4 | @r0_122 | +| IntegerOps(int, int) -> void | 0 | 126 | 0 | @r0_123 | +| IntegerOps(int, int) -> void | 0 | 126 | 1 | @r0_125 | +| IntegerOps(int, int) -> void | 0 | 128 | 0 | @r0_127 | +| IntegerOps(int, int) -> void | 0 | 128 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 130 | 0 | @r0_129 | +| IntegerOps(int, int) -> void | 0 | 130 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 131 | 3 | @r0_130 | +| IntegerOps(int, int) -> void | 0 | 131 | 4 | @r0_128 | +| IntegerOps(int, int) -> void | 0 | 132 | 0 | @r0_129 | +| IntegerOps(int, int) -> void | 0 | 132 | 1 | @r0_131 | +| IntegerOps(int, int) -> void | 0 | 134 | 0 | @r0_133 | +| IntegerOps(int, int) -> void | 0 | 134 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 136 | 0 | @r0_135 | +| IntegerOps(int, int) -> void | 0 | 136 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 137 | 3 | @r0_136 | +| IntegerOps(int, int) -> void | 0 | 137 | 4 | @r0_134 | +| IntegerOps(int, int) -> void | 0 | 138 | 0 | @r0_135 | +| IntegerOps(int, int) -> void | 0 | 138 | 1 | @r0_137 | +| IntegerOps(int, int) -> void | 0 | 140 | 0 | @r0_139 | +| IntegerOps(int, int) -> void | 0 | 140 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 142 | 0 | @r0_141 | +| IntegerOps(int, int) -> void | 0 | 142 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 143 | 3 | @r0_142 | +| IntegerOps(int, int) -> void | 0 | 143 | 4 | @r0_140 | +| IntegerOps(int, int) -> void | 0 | 144 | 0 | @r0_141 | +| IntegerOps(int, int) -> void | 0 | 144 | 1 | @r0_143 | +| IntegerOps(int, int) -> void | 0 | 146 | 0 | @r0_145 | +| IntegerOps(int, int) -> void | 0 | 146 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 147 | 1 | @r0_146 | +| IntegerOps(int, int) -> void | 0 | 149 | 0 | @r0_148 | +| IntegerOps(int, int) -> void | 0 | 149 | 1 | @r0_147 | +| IntegerOps(int, int) -> void | 0 | 151 | 0 | @r0_150 | +| IntegerOps(int, int) -> void | 0 | 151 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 152 | 2 | @r0_151 | +| IntegerOps(int, int) -> void | 0 | 154 | 0 | @r0_153 | +| IntegerOps(int, int) -> void | 0 | 154 | 1 | @r0_152 | +| IntegerOps(int, int) -> void | 0 | 156 | 0 | @r0_155 | +| IntegerOps(int, int) -> void | 0 | 156 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 157 | 2 | @r0_156 | +| IntegerOps(int, int) -> void | 0 | 159 | 0 | @r0_158 | +| IntegerOps(int, int) -> void | 0 | 159 | 1 | @r0_157 | +| IntegerOps(int, int) -> void | 0 | 161 | 0 | @r0_160 | +| IntegerOps(int, int) -> void | 0 | 161 | 1 | @mu0_1 | +| IntegerOps(int, int) -> void | 0 | 163 | 3 | @r0_161 | +| IntegerOps(int, int) -> void | 0 | 163 | 4 | @r0_162 | +| IntegerOps(int, int) -> void | 0 | 164 | 2 | @r0_163 | +| IntegerOps(int, int) -> void | 0 | 165 | 2 | @r0_164 | +| IntegerOps(int, int) -> void | 0 | 167 | 0 | @r0_166 | +| IntegerOps(int, int) -> void | 0 | 167 | 1 | @r0_165 | +| IntegerOps(int, int) -> void | 0 | 170 | 0 | @mu* | +| LogicalAnd(bool, bool) -> void | 0 | 4 | 0 | @r0_3 | +| LogicalAnd(bool, bool) -> void | 0 | 4 | 1 | @r0_2 | +| LogicalAnd(bool, bool) -> void | 0 | 7 | 0 | @r0_6 | +| LogicalAnd(bool, bool) -> void | 0 | 7 | 1 | @r0_5 | +| LogicalAnd(bool, bool) -> void | 0 | 10 | 0 | @r0_8 | +| LogicalAnd(bool, bool) -> void | 0 | 10 | 1 | @r0_9 | +| LogicalAnd(bool, bool) -> void | 0 | 12 | 0 | @r0_11 | +| LogicalAnd(bool, bool) -> void | 0 | 12 | 1 | @mu0_1 | +| LogicalAnd(bool, bool) -> void | 0 | 13 | 7 | @r0_12 | +| LogicalAnd(bool, bool) -> void | 1 | 1 | 0 | @r1_0 | +| LogicalAnd(bool, bool) -> void | 1 | 1 | 1 | @mu0_1 | +| LogicalAnd(bool, bool) -> void | 1 | 2 | 7 | @r1_1 | +| LogicalAnd(bool, bool) -> void | 2 | 2 | 0 | @r2_1 | +| LogicalAnd(bool, bool) -> void | 2 | 2 | 1 | @r2_0 | +| LogicalAnd(bool, bool) -> void | 3 | 1 | 0 | @r3_0 | +| LogicalAnd(bool, bool) -> void | 3 | 1 | 1 | @mu0_1 | +| LogicalAnd(bool, bool) -> void | 3 | 2 | 7 | @r3_1 | +| LogicalAnd(bool, bool) -> void | 4 | 1 | 0 | @r4_0 | +| LogicalAnd(bool, bool) -> void | 4 | 1 | 1 | @mu0_1 | +| LogicalAnd(bool, bool) -> void | 4 | 2 | 7 | @r4_1 | +| LogicalAnd(bool, bool) -> void | 5 | 2 | 0 | @r5_1 | +| LogicalAnd(bool, bool) -> void | 5 | 2 | 1 | @r5_0 | +| LogicalAnd(bool, bool) -> void | 6 | 2 | 0 | @r6_1 | +| LogicalAnd(bool, bool) -> void | 6 | 2 | 1 | @r6_0 | +| LogicalAnd(bool, bool) -> void | 7 | 2 | 0 | @mu* | +| LogicalNot(bool, bool) -> void | 0 | 4 | 0 | @r0_3 | +| LogicalNot(bool, bool) -> void | 0 | 4 | 1 | @r0_2 | +| LogicalNot(bool, bool) -> void | 0 | 7 | 0 | @r0_6 | +| LogicalNot(bool, bool) -> void | 0 | 7 | 1 | @r0_5 | +| LogicalNot(bool, bool) -> void | 0 | 10 | 0 | @r0_8 | +| LogicalNot(bool, bool) -> void | 0 | 10 | 1 | @r0_9 | +| LogicalNot(bool, bool) -> void | 0 | 12 | 0 | @r0_11 | +| LogicalNot(bool, bool) -> void | 0 | 12 | 1 | @mu0_1 | +| LogicalNot(bool, bool) -> void | 0 | 13 | 7 | @r0_12 | +| LogicalNot(bool, bool) -> void | 1 | 2 | 0 | @r1_1 | +| LogicalNot(bool, bool) -> void | 1 | 2 | 1 | @r1_0 | +| LogicalNot(bool, bool) -> void | 2 | 1 | 0 | @r2_0 | +| LogicalNot(bool, bool) -> void | 2 | 1 | 1 | @mu0_1 | +| LogicalNot(bool, bool) -> void | 2 | 2 | 7 | @r2_1 | +| LogicalNot(bool, bool) -> void | 3 | 1 | 0 | @r3_0 | +| LogicalNot(bool, bool) -> void | 3 | 1 | 1 | @mu0_1 | +| LogicalNot(bool, bool) -> void | 3 | 2 | 7 | @r3_1 | +| LogicalNot(bool, bool) -> void | 4 | 2 | 0 | @r4_1 | +| LogicalNot(bool, bool) -> void | 4 | 2 | 1 | @r4_0 | +| LogicalNot(bool, bool) -> void | 5 | 2 | 0 | @r5_1 | +| LogicalNot(bool, bool) -> void | 5 | 2 | 1 | @r5_0 | +| LogicalNot(bool, bool) -> void | 6 | 2 | 0 | @mu* | +| LogicalOr(bool, bool) -> void | 0 | 4 | 0 | @r0_3 | +| LogicalOr(bool, bool) -> void | 0 | 4 | 1 | @r0_2 | +| LogicalOr(bool, bool) -> void | 0 | 7 | 0 | @r0_6 | +| LogicalOr(bool, bool) -> void | 0 | 7 | 1 | @r0_5 | +| LogicalOr(bool, bool) -> void | 0 | 10 | 0 | @r0_8 | +| LogicalOr(bool, bool) -> void | 0 | 10 | 1 | @r0_9 | +| LogicalOr(bool, bool) -> void | 0 | 12 | 0 | @r0_11 | +| LogicalOr(bool, bool) -> void | 0 | 12 | 1 | @mu0_1 | +| LogicalOr(bool, bool) -> void | 0 | 13 | 7 | @r0_12 | +| LogicalOr(bool, bool) -> void | 1 | 1 | 0 | @r1_0 | +| LogicalOr(bool, bool) -> void | 1 | 1 | 1 | @mu0_1 | +| LogicalOr(bool, bool) -> void | 1 | 2 | 7 | @r1_1 | +| LogicalOr(bool, bool) -> void | 2 | 2 | 0 | @r2_1 | +| LogicalOr(bool, bool) -> void | 2 | 2 | 1 | @r2_0 | +| LogicalOr(bool, bool) -> void | 3 | 1 | 0 | @r3_0 | +| LogicalOr(bool, bool) -> void | 3 | 1 | 1 | @mu0_1 | +| LogicalOr(bool, bool) -> void | 3 | 2 | 7 | @r3_1 | +| LogicalOr(bool, bool) -> void | 4 | 1 | 0 | @r4_0 | +| LogicalOr(bool, bool) -> void | 4 | 1 | 1 | @mu0_1 | +| LogicalOr(bool, bool) -> void | 4 | 2 | 7 | @r4_1 | +| LogicalOr(bool, bool) -> void | 5 | 2 | 0 | @r5_1 | +| LogicalOr(bool, bool) -> void | 5 | 2 | 1 | @r5_0 | +| LogicalOr(bool, bool) -> void | 6 | 2 | 0 | @r6_1 | +| LogicalOr(bool, bool) -> void | 6 | 2 | 1 | @r6_0 | +| LogicalOr(bool, bool) -> void | 7 | 2 | 0 | @mu* | +| Middle::Middle() -> void | 0 | 3 | 2 | @r0_2 | +| Middle::Middle() -> void | 0 | 5 | 9 | @r0_4 | +| Middle::Middle() -> void | 0 | 5 | 10 | this:@r0_3 | +| Middle::Middle() -> void | 0 | 6 | 2 | @r0_2 | +| Middle::Middle() -> void | 0 | 8 | 9 | @r0_7 | +| Middle::Middle() -> void | 0 | 8 | 10 | this:@r0_6 | +| Middle::Middle() -> void | 0 | 11 | 0 | @mu* | +| Middle::operator=(const Middle &) -> Middle & | 0 | 5 | 0 | @r0_4 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 5 | 1 | @r0_3 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 6 | 1 | @r0_2 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 7 | 2 | @r0_6 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 10 | 0 | @r0_9 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 10 | 1 | @mu0_1 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 11 | 2 | @r0_10 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 12 | 9 | @r0_8 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 12 | 10 | this:@r0_7 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 12 | 11 | @r0_11 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 13 | 1 | @r0_2 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 14 | 2 | @r0_13 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 17 | 0 | @r0_16 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 17 | 1 | @mu0_1 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 18 | 2 | @r0_17 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 19 | 9 | @r0_15 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 19 | 10 | this:@r0_14 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 19 | 11 | @r0_18 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 21 | 1 | @r0_2 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 22 | 0 | @r0_20 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 22 | 1 | @r0_21 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 24 | 0 | @r0_23 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 24 | 5 | @mu0_1 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 25 | 0 | @mu* | +| Middle::~Middle() -> void | 0 | 4 | 2 | @r0_2 | +| Middle::~Middle() -> void | 0 | 6 | 9 | @r0_5 | +| Middle::~Middle() -> void | 0 | 6 | 10 | this:@r0_4 | +| Middle::~Middle() -> void | 0 | 7 | 2 | @r0_2 | +| Middle::~Middle() -> void | 0 | 9 | 9 | @r0_8 | +| Middle::~Middle() -> void | 0 | 9 | 10 | this:@r0_7 | +| Middle::~Middle() -> void | 0 | 11 | 0 | @mu* | +| MiddleVB1::MiddleVB1() -> void | 0 | 3 | 2 | @r0_2 | +| MiddleVB1::MiddleVB1() -> void | 0 | 5 | 9 | @r0_4 | +| MiddleVB1::MiddleVB1() -> void | 0 | 5 | 10 | this:@r0_3 | +| MiddleVB1::MiddleVB1() -> void | 0 | 6 | 2 | @r0_2 | +| MiddleVB1::MiddleVB1() -> void | 0 | 8 | 9 | @r0_7 | +| MiddleVB1::MiddleVB1() -> void | 0 | 8 | 10 | this:@r0_6 | +| MiddleVB1::MiddleVB1() -> void | 0 | 11 | 0 | @mu* | +| MiddleVB1::~MiddleVB1() -> void | 0 | 4 | 2 | @r0_2 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 6 | 9 | @r0_5 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 6 | 10 | this:@r0_4 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 7 | 2 | @r0_2 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 9 | 9 | @r0_8 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 9 | 10 | this:@r0_7 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 11 | 0 | @mu* | +| MiddleVB2::MiddleVB2() -> void | 0 | 3 | 2 | @r0_2 | +| MiddleVB2::MiddleVB2() -> void | 0 | 5 | 9 | @r0_4 | +| MiddleVB2::MiddleVB2() -> void | 0 | 5 | 10 | this:@r0_3 | +| MiddleVB2::MiddleVB2() -> void | 0 | 6 | 2 | @r0_2 | +| MiddleVB2::MiddleVB2() -> void | 0 | 8 | 9 | @r0_7 | +| MiddleVB2::MiddleVB2() -> void | 0 | 8 | 10 | this:@r0_6 | +| MiddleVB2::MiddleVB2() -> void | 0 | 11 | 0 | @mu* | +| MiddleVB2::~MiddleVB2() -> void | 0 | 4 | 2 | @r0_2 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 6 | 9 | @r0_5 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 6 | 10 | this:@r0_4 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 7 | 2 | @r0_2 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 9 | 9 | @r0_8 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 9 | 10 | this:@r0_7 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 11 | 0 | @mu* | +| NestedInitList(int, float) -> void | 0 | 4 | 0 | @r0_3 | +| NestedInitList(int, float) -> void | 0 | 4 | 1 | @r0_2 | +| NestedInitList(int, float) -> void | 0 | 7 | 0 | @r0_6 | +| NestedInitList(int, float) -> void | 0 | 7 | 1 | @r0_5 | +| NestedInitList(int, float) -> void | 0 | 9 | 2 | @r0_8 | +| NestedInitList(int, float) -> void | 0 | 11 | 0 | @r0_9 | +| NestedInitList(int, float) -> void | 0 | 11 | 1 | @r0_10 | +| NestedInitList(int, float) -> void | 0 | 12 | 2 | @r0_8 | +| NestedInitList(int, float) -> void | 0 | 14 | 0 | @r0_12 | +| NestedInitList(int, float) -> void | 0 | 14 | 1 | @r0_13 | +| NestedInitList(int, float) -> void | 0 | 16 | 2 | @r0_15 | +| NestedInitList(int, float) -> void | 0 | 17 | 2 | @r0_16 | +| NestedInitList(int, float) -> void | 0 | 19 | 0 | @r0_18 | +| NestedInitList(int, float) -> void | 0 | 19 | 1 | @mu0_1 | +| NestedInitList(int, float) -> void | 0 | 20 | 0 | @r0_17 | +| NestedInitList(int, float) -> void | 0 | 20 | 1 | @r0_19 | +| NestedInitList(int, float) -> void | 0 | 21 | 2 | @r0_16 | +| NestedInitList(int, float) -> void | 0 | 23 | 0 | @r0_22 | +| NestedInitList(int, float) -> void | 0 | 23 | 1 | @mu0_1 | +| NestedInitList(int, float) -> void | 0 | 24 | 2 | @r0_23 | +| NestedInitList(int, float) -> void | 0 | 25 | 0 | @r0_21 | +| NestedInitList(int, float) -> void | 0 | 25 | 1 | @r0_24 | +| NestedInitList(int, float) -> void | 0 | 26 | 2 | @r0_15 | +| NestedInitList(int, float) -> void | 0 | 28 | 0 | @r0_26 | +| NestedInitList(int, float) -> void | 0 | 28 | 1 | @r0_27 | +| NestedInitList(int, float) -> void | 0 | 30 | 2 | @r0_29 | +| NestedInitList(int, float) -> void | 0 | 31 | 2 | @r0_30 | +| NestedInitList(int, float) -> void | 0 | 33 | 0 | @r0_32 | +| NestedInitList(int, float) -> void | 0 | 33 | 1 | @mu0_1 | +| NestedInitList(int, float) -> void | 0 | 34 | 0 | @r0_31 | +| NestedInitList(int, float) -> void | 0 | 34 | 1 | @r0_33 | +| NestedInitList(int, float) -> void | 0 | 35 | 2 | @r0_30 | +| NestedInitList(int, float) -> void | 0 | 37 | 0 | @r0_36 | +| NestedInitList(int, float) -> void | 0 | 37 | 1 | @mu0_1 | +| NestedInitList(int, float) -> void | 0 | 38 | 2 | @r0_37 | +| NestedInitList(int, float) -> void | 0 | 39 | 0 | @r0_35 | +| NestedInitList(int, float) -> void | 0 | 39 | 1 | @r0_38 | +| NestedInitList(int, float) -> void | 0 | 40 | 2 | @r0_29 | +| NestedInitList(int, float) -> void | 0 | 41 | 2 | @r0_40 | +| NestedInitList(int, float) -> void | 0 | 43 | 0 | @r0_42 | +| NestedInitList(int, float) -> void | 0 | 43 | 1 | @mu0_1 | +| NestedInitList(int, float) -> void | 0 | 44 | 0 | @r0_41 | +| NestedInitList(int, float) -> void | 0 | 44 | 1 | @r0_43 | +| NestedInitList(int, float) -> void | 0 | 45 | 2 | @r0_40 | +| NestedInitList(int, float) -> void | 0 | 47 | 0 | @r0_46 | +| NestedInitList(int, float) -> void | 0 | 47 | 1 | @mu0_1 | +| NestedInitList(int, float) -> void | 0 | 48 | 2 | @r0_47 | +| NestedInitList(int, float) -> void | 0 | 49 | 0 | @r0_45 | +| NestedInitList(int, float) -> void | 0 | 49 | 1 | @r0_48 | +| NestedInitList(int, float) -> void | 0 | 51 | 2 | @r0_50 | +| NestedInitList(int, float) -> void | 0 | 52 | 2 | @r0_51 | +| NestedInitList(int, float) -> void | 0 | 54 | 0 | @r0_53 | +| NestedInitList(int, float) -> void | 0 | 54 | 1 | @mu0_1 | +| NestedInitList(int, float) -> void | 0 | 55 | 0 | @r0_52 | +| NestedInitList(int, float) -> void | 0 | 55 | 1 | @r0_54 | +| NestedInitList(int, float) -> void | 0 | 56 | 2 | @r0_51 | +| NestedInitList(int, float) -> void | 0 | 58 | 0 | @r0_56 | +| NestedInitList(int, float) -> void | 0 | 58 | 1 | @r0_57 | +| NestedInitList(int, float) -> void | 0 | 59 | 2 | @r0_50 | +| NestedInitList(int, float) -> void | 0 | 60 | 2 | @r0_59 | +| NestedInitList(int, float) -> void | 0 | 62 | 0 | @r0_61 | +| NestedInitList(int, float) -> void | 0 | 62 | 1 | @mu0_1 | +| NestedInitList(int, float) -> void | 0 | 63 | 0 | @r0_60 | +| NestedInitList(int, float) -> void | 0 | 63 | 1 | @r0_62 | +| NestedInitList(int, float) -> void | 0 | 64 | 2 | @r0_59 | +| NestedInitList(int, float) -> void | 0 | 66 | 0 | @r0_64 | +| NestedInitList(int, float) -> void | 0 | 66 | 1 | @r0_65 | +| NestedInitList(int, float) -> void | 0 | 69 | 0 | @mu* | +| Nullptr() -> void | 0 | 4 | 0 | @r0_2 | +| Nullptr() -> void | 0 | 4 | 1 | @r0_3 | +| Nullptr() -> void | 0 | 7 | 0 | @r0_5 | +| Nullptr() -> void | 0 | 7 | 1 | @r0_6 | +| Nullptr() -> void | 0 | 10 | 0 | @r0_9 | +| Nullptr() -> void | 0 | 10 | 1 | @r0_8 | +| Nullptr() -> void | 0 | 13 | 0 | @r0_12 | +| Nullptr() -> void | 0 | 13 | 1 | @r0_11 | +| Nullptr() -> void | 0 | 16 | 0 | @mu* | +| Outer::Func(void *, char) -> long | 0 | 4 | 0 | @r0_3 | +| Outer::Func(void *, char) -> long | 0 | 4 | 1 | @r0_2 | +| Outer::Func(void *, char) -> long | 0 | 7 | 0 | @r0_6 | +| Outer::Func(void *, char) -> long | 0 | 7 | 1 | @r0_5 | +| Outer::Func(void *, char) -> long | 0 | 10 | 0 | @r0_8 | +| Outer::Func(void *, char) -> long | 0 | 10 | 1 | @r0_9 | +| Outer::Func(void *, char) -> long | 0 | 12 | 0 | @r0_11 | +| Outer::Func(void *, char) -> long | 0 | 12 | 5 | @mu0_1 | +| Outer::Func(void *, char) -> long | 0 | 13 | 0 | @mu* | +| Parameters(int, int) -> int | 0 | 4 | 0 | @r0_3 | +| Parameters(int, int) -> int | 0 | 4 | 1 | @r0_2 | +| Parameters(int, int) -> int | 0 | 7 | 0 | @r0_6 | +| Parameters(int, int) -> int | 0 | 7 | 1 | @r0_5 | +| Parameters(int, int) -> int | 0 | 10 | 0 | @r0_9 | +| Parameters(int, int) -> int | 0 | 10 | 1 | @mu0_1 | +| Parameters(int, int) -> int | 0 | 12 | 0 | @r0_11 | +| Parameters(int, int) -> int | 0 | 12 | 1 | @mu0_1 | +| Parameters(int, int) -> int | 0 | 13 | 3 | @r0_10 | +| Parameters(int, int) -> int | 0 | 13 | 4 | @r0_12 | +| Parameters(int, int) -> int | 0 | 14 | 0 | @r0_8 | +| Parameters(int, int) -> int | 0 | 14 | 1 | @r0_13 | +| Parameters(int, int) -> int | 0 | 16 | 0 | @r0_15 | +| Parameters(int, int) -> int | 0 | 16 | 5 | @mu0_1 | +| Parameters(int, int) -> int | 0 | 17 | 0 | @mu* | +| PointerCompare(int *, int *) -> void | 0 | 4 | 0 | @r0_3 | +| PointerCompare(int *, int *) -> void | 0 | 4 | 1 | @r0_2 | +| PointerCompare(int *, int *) -> void | 0 | 7 | 0 | @r0_6 | +| PointerCompare(int *, int *) -> void | 0 | 7 | 1 | @r0_5 | +| PointerCompare(int *, int *) -> void | 0 | 10 | 0 | @r0_8 | +| PointerCompare(int *, int *) -> void | 0 | 10 | 1 | @r0_9 | +| PointerCompare(int *, int *) -> void | 0 | 12 | 0 | @r0_11 | +| PointerCompare(int *, int *) -> void | 0 | 12 | 1 | @mu0_1 | +| PointerCompare(int *, int *) -> void | 0 | 14 | 0 | @r0_13 | +| PointerCompare(int *, int *) -> void | 0 | 14 | 1 | @mu0_1 | +| PointerCompare(int *, int *) -> void | 0 | 15 | 3 | @r0_12 | +| PointerCompare(int *, int *) -> void | 0 | 15 | 4 | @r0_14 | +| PointerCompare(int *, int *) -> void | 0 | 17 | 0 | @r0_16 | +| PointerCompare(int *, int *) -> void | 0 | 17 | 1 | @r0_15 | +| PointerCompare(int *, int *) -> void | 0 | 19 | 0 | @r0_18 | +| PointerCompare(int *, int *) -> void | 0 | 19 | 1 | @mu0_1 | +| PointerCompare(int *, int *) -> void | 0 | 21 | 0 | @r0_20 | +| PointerCompare(int *, int *) -> void | 0 | 21 | 1 | @mu0_1 | +| PointerCompare(int *, int *) -> void | 0 | 22 | 3 | @r0_19 | +| PointerCompare(int *, int *) -> void | 0 | 22 | 4 | @r0_21 | +| PointerCompare(int *, int *) -> void | 0 | 24 | 0 | @r0_23 | +| PointerCompare(int *, int *) -> void | 0 | 24 | 1 | @r0_22 | +| PointerCompare(int *, int *) -> void | 0 | 26 | 0 | @r0_25 | +| PointerCompare(int *, int *) -> void | 0 | 26 | 1 | @mu0_1 | +| PointerCompare(int *, int *) -> void | 0 | 28 | 0 | @r0_27 | +| PointerCompare(int *, int *) -> void | 0 | 28 | 1 | @mu0_1 | +| PointerCompare(int *, int *) -> void | 0 | 29 | 3 | @r0_26 | +| PointerCompare(int *, int *) -> void | 0 | 29 | 4 | @r0_28 | +| PointerCompare(int *, int *) -> void | 0 | 31 | 0 | @r0_30 | +| PointerCompare(int *, int *) -> void | 0 | 31 | 1 | @r0_29 | +| PointerCompare(int *, int *) -> void | 0 | 33 | 0 | @r0_32 | +| PointerCompare(int *, int *) -> void | 0 | 33 | 1 | @mu0_1 | +| PointerCompare(int *, int *) -> void | 0 | 35 | 0 | @r0_34 | +| PointerCompare(int *, int *) -> void | 0 | 35 | 1 | @mu0_1 | +| PointerCompare(int *, int *) -> void | 0 | 36 | 3 | @r0_33 | +| PointerCompare(int *, int *) -> void | 0 | 36 | 4 | @r0_35 | +| PointerCompare(int *, int *) -> void | 0 | 38 | 0 | @r0_37 | +| PointerCompare(int *, int *) -> void | 0 | 38 | 1 | @r0_36 | +| PointerCompare(int *, int *) -> void | 0 | 40 | 0 | @r0_39 | +| PointerCompare(int *, int *) -> void | 0 | 40 | 1 | @mu0_1 | +| PointerCompare(int *, int *) -> void | 0 | 42 | 0 | @r0_41 | +| PointerCompare(int *, int *) -> void | 0 | 42 | 1 | @mu0_1 | +| PointerCompare(int *, int *) -> void | 0 | 43 | 3 | @r0_40 | +| PointerCompare(int *, int *) -> void | 0 | 43 | 4 | @r0_42 | +| PointerCompare(int *, int *) -> void | 0 | 45 | 0 | @r0_44 | +| PointerCompare(int *, int *) -> void | 0 | 45 | 1 | @r0_43 | +| PointerCompare(int *, int *) -> void | 0 | 47 | 0 | @r0_46 | +| PointerCompare(int *, int *) -> void | 0 | 47 | 1 | @mu0_1 | +| PointerCompare(int *, int *) -> void | 0 | 49 | 0 | @r0_48 | +| PointerCompare(int *, int *) -> void | 0 | 49 | 1 | @mu0_1 | +| PointerCompare(int *, int *) -> void | 0 | 50 | 3 | @r0_47 | +| PointerCompare(int *, int *) -> void | 0 | 50 | 4 | @r0_49 | +| PointerCompare(int *, int *) -> void | 0 | 52 | 0 | @r0_51 | +| PointerCompare(int *, int *) -> void | 0 | 52 | 1 | @r0_50 | +| PointerCompare(int *, int *) -> void | 0 | 55 | 0 | @mu* | +| PointerCrement(int *) -> void | 0 | 4 | 0 | @r0_3 | +| PointerCrement(int *) -> void | 0 | 4 | 1 | @r0_2 | +| PointerCrement(int *) -> void | 0 | 7 | 0 | @r0_5 | +| PointerCrement(int *) -> void | 0 | 7 | 1 | @r0_6 | +| PointerCrement(int *) -> void | 0 | 9 | 0 | @r0_8 | +| PointerCrement(int *) -> void | 0 | 9 | 1 | @mu0_1 | +| PointerCrement(int *) -> void | 0 | 11 | 3 | @r0_9 | +| PointerCrement(int *) -> void | 0 | 11 | 4 | @r0_10 | +| PointerCrement(int *) -> void | 0 | 12 | 0 | @r0_8 | +| PointerCrement(int *) -> void | 0 | 12 | 1 | @r0_11 | +| PointerCrement(int *) -> void | 0 | 14 | 0 | @r0_13 | +| PointerCrement(int *) -> void | 0 | 14 | 1 | @r0_11 | +| PointerCrement(int *) -> void | 0 | 16 | 0 | @r0_15 | +| PointerCrement(int *) -> void | 0 | 16 | 1 | @mu0_1 | +| PointerCrement(int *) -> void | 0 | 18 | 3 | @r0_16 | +| PointerCrement(int *) -> void | 0 | 18 | 4 | @r0_17 | +| PointerCrement(int *) -> void | 0 | 19 | 0 | @r0_15 | +| PointerCrement(int *) -> void | 0 | 19 | 1 | @r0_18 | +| PointerCrement(int *) -> void | 0 | 21 | 0 | @r0_20 | +| PointerCrement(int *) -> void | 0 | 21 | 1 | @r0_18 | +| PointerCrement(int *) -> void | 0 | 23 | 0 | @r0_22 | +| PointerCrement(int *) -> void | 0 | 23 | 1 | @mu0_1 | +| PointerCrement(int *) -> void | 0 | 25 | 3 | @r0_23 | +| PointerCrement(int *) -> void | 0 | 25 | 4 | @r0_24 | +| PointerCrement(int *) -> void | 0 | 26 | 0 | @r0_22 | +| PointerCrement(int *) -> void | 0 | 26 | 1 | @r0_25 | +| PointerCrement(int *) -> void | 0 | 28 | 0 | @r0_27 | +| PointerCrement(int *) -> void | 0 | 28 | 1 | @r0_23 | +| PointerCrement(int *) -> void | 0 | 30 | 0 | @r0_29 | +| PointerCrement(int *) -> void | 0 | 30 | 1 | @mu0_1 | +| PointerCrement(int *) -> void | 0 | 32 | 3 | @r0_30 | +| PointerCrement(int *) -> void | 0 | 32 | 4 | @r0_31 | +| PointerCrement(int *) -> void | 0 | 33 | 0 | @r0_29 | +| PointerCrement(int *) -> void | 0 | 33 | 1 | @r0_32 | +| PointerCrement(int *) -> void | 0 | 35 | 0 | @r0_34 | +| PointerCrement(int *) -> void | 0 | 35 | 1 | @r0_30 | +| PointerCrement(int *) -> void | 0 | 38 | 0 | @mu* | +| PointerOps(int *, int) -> void | 0 | 4 | 0 | @r0_3 | +| PointerOps(int *, int) -> void | 0 | 4 | 1 | @r0_2 | +| PointerOps(int *, int) -> void | 0 | 7 | 0 | @r0_6 | +| PointerOps(int *, int) -> void | 0 | 7 | 1 | @r0_5 | +| PointerOps(int *, int) -> void | 0 | 10 | 0 | @r0_8 | +| PointerOps(int *, int) -> void | 0 | 10 | 1 | @r0_9 | +| PointerOps(int *, int) -> void | 0 | 13 | 0 | @r0_11 | +| PointerOps(int *, int) -> void | 0 | 13 | 1 | @r0_12 | +| PointerOps(int *, int) -> void | 0 | 15 | 0 | @r0_14 | +| PointerOps(int *, int) -> void | 0 | 15 | 1 | @mu0_1 | +| PointerOps(int *, int) -> void | 0 | 17 | 0 | @r0_16 | +| PointerOps(int *, int) -> void | 0 | 17 | 1 | @mu0_1 | +| PointerOps(int *, int) -> void | 0 | 18 | 3 | @r0_15 | +| PointerOps(int *, int) -> void | 0 | 18 | 4 | @r0_17 | +| PointerOps(int *, int) -> void | 0 | 20 | 0 | @r0_19 | +| PointerOps(int *, int) -> void | 0 | 20 | 1 | @r0_18 | +| PointerOps(int *, int) -> void | 0 | 22 | 0 | @r0_21 | +| PointerOps(int *, int) -> void | 0 | 22 | 1 | @mu0_1 | +| PointerOps(int *, int) -> void | 0 | 24 | 0 | @r0_23 | +| PointerOps(int *, int) -> void | 0 | 24 | 1 | @mu0_1 | +| PointerOps(int *, int) -> void | 0 | 25 | 3 | @r0_24 | +| PointerOps(int *, int) -> void | 0 | 25 | 4 | @r0_22 | +| PointerOps(int *, int) -> void | 0 | 27 | 0 | @r0_26 | +| PointerOps(int *, int) -> void | 0 | 27 | 1 | @r0_25 | +| PointerOps(int *, int) -> void | 0 | 29 | 0 | @r0_28 | +| PointerOps(int *, int) -> void | 0 | 29 | 1 | @mu0_1 | +| PointerOps(int *, int) -> void | 0 | 31 | 0 | @r0_30 | +| PointerOps(int *, int) -> void | 0 | 31 | 1 | @mu0_1 | +| PointerOps(int *, int) -> void | 0 | 32 | 3 | @r0_29 | +| PointerOps(int *, int) -> void | 0 | 32 | 4 | @r0_31 | +| PointerOps(int *, int) -> void | 0 | 34 | 0 | @r0_33 | +| PointerOps(int *, int) -> void | 0 | 34 | 1 | @r0_32 | +| PointerOps(int *, int) -> void | 0 | 36 | 0 | @r0_35 | +| PointerOps(int *, int) -> void | 0 | 36 | 1 | @mu0_1 | +| PointerOps(int *, int) -> void | 0 | 38 | 0 | @r0_37 | +| PointerOps(int *, int) -> void | 0 | 38 | 1 | @mu0_1 | +| PointerOps(int *, int) -> void | 0 | 39 | 3 | @r0_36 | +| PointerOps(int *, int) -> void | 0 | 39 | 4 | @r0_38 | +| PointerOps(int *, int) -> void | 0 | 40 | 2 | @r0_39 | +| PointerOps(int *, int) -> void | 0 | 42 | 0 | @r0_41 | +| PointerOps(int *, int) -> void | 0 | 42 | 1 | @r0_40 | +| PointerOps(int *, int) -> void | 0 | 44 | 0 | @r0_43 | +| PointerOps(int *, int) -> void | 0 | 44 | 1 | @mu0_1 | +| PointerOps(int *, int) -> void | 0 | 46 | 0 | @r0_45 | +| PointerOps(int *, int) -> void | 0 | 46 | 1 | @r0_44 | +| PointerOps(int *, int) -> void | 0 | 48 | 0 | @r0_47 | +| PointerOps(int *, int) -> void | 0 | 48 | 1 | @mu0_1 | +| PointerOps(int *, int) -> void | 0 | 50 | 0 | @r0_49 | +| PointerOps(int *, int) -> void | 0 | 50 | 1 | @mu0_1 | +| PointerOps(int *, int) -> void | 0 | 51 | 3 | @r0_50 | +| PointerOps(int *, int) -> void | 0 | 51 | 4 | @r0_48 | +| PointerOps(int *, int) -> void | 0 | 52 | 0 | @r0_49 | +| PointerOps(int *, int) -> void | 0 | 52 | 1 | @r0_51 | +| PointerOps(int *, int) -> void | 0 | 54 | 0 | @r0_53 | +| PointerOps(int *, int) -> void | 0 | 54 | 1 | @mu0_1 | +| PointerOps(int *, int) -> void | 0 | 56 | 0 | @r0_55 | +| PointerOps(int *, int) -> void | 0 | 56 | 1 | @mu0_1 | +| PointerOps(int *, int) -> void | 0 | 57 | 3 | @r0_56 | +| PointerOps(int *, int) -> void | 0 | 57 | 4 | @r0_54 | +| PointerOps(int *, int) -> void | 0 | 58 | 0 | @r0_55 | +| PointerOps(int *, int) -> void | 0 | 58 | 1 | @r0_57 | +| PointerOps(int *, int) -> void | 0 | 60 | 0 | @r0_59 | +| PointerOps(int *, int) -> void | 0 | 60 | 1 | @mu0_1 | +| PointerOps(int *, int) -> void | 0 | 62 | 3 | @r0_60 | +| PointerOps(int *, int) -> void | 0 | 62 | 4 | @r0_61 | +| PointerOps(int *, int) -> void | 0 | 64 | 0 | @r0_63 | +| PointerOps(int *, int) -> void | 0 | 64 | 1 | @r0_62 | +| PointerOps(int *, int) -> void | 0 | 66 | 0 | @r0_65 | +| PointerOps(int *, int) -> void | 0 | 66 | 1 | @mu0_1 | +| PointerOps(int *, int) -> void | 0 | 68 | 3 | @r0_66 | +| PointerOps(int *, int) -> void | 0 | 68 | 4 | @r0_67 | +| PointerOps(int *, int) -> void | 0 | 69 | 2 | @r0_68 | +| PointerOps(int *, int) -> void | 0 | 71 | 0 | @r0_70 | +| PointerOps(int *, int) -> void | 0 | 71 | 1 | @r0_69 | +| PointerOps(int *, int) -> void | 0 | 74 | 0 | @mu* | +| PolymorphicBase::PolymorphicBase() -> void | 0 | 5 | 0 | @mu* | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 3 | 2 | @r0_2 | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 5 | 9 | @r0_4 | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 5 | 10 | this:@r0_3 | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 8 | 0 | @mu* | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 4 | 2 | @r0_2 | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 6 | 9 | @r0_5 | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 6 | 10 | this:@r0_4 | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 8 | 0 | @mu* | +| ReturnStruct(Point) -> Point | 0 | 4 | 0 | @r0_3 | +| ReturnStruct(Point) -> Point | 0 | 4 | 1 | @r0_2 | +| ReturnStruct(Point) -> Point | 0 | 7 | 0 | @r0_6 | +| ReturnStruct(Point) -> Point | 0 | 7 | 1 | @mu0_1 | +| ReturnStruct(Point) -> Point | 0 | 8 | 0 | @r0_5 | +| ReturnStruct(Point) -> Point | 0 | 8 | 1 | @r0_7 | +| ReturnStruct(Point) -> Point | 0 | 10 | 0 | @r0_9 | +| ReturnStruct(Point) -> Point | 0 | 10 | 5 | @mu0_1 | +| ReturnStruct(Point) -> Point | 0 | 11 | 0 | @mu* | +| SetFuncPtr() -> void | 0 | 4 | 0 | @r0_2 | +| SetFuncPtr() -> void | 0 | 4 | 1 | @r0_3 | +| SetFuncPtr() -> void | 0 | 7 | 0 | @r0_6 | +| SetFuncPtr() -> void | 0 | 7 | 1 | @r0_5 | +| SetFuncPtr() -> void | 0 | 10 | 0 | @r0_9 | +| SetFuncPtr() -> void | 0 | 10 | 1 | @r0_8 | +| SetFuncPtr() -> void | 0 | 13 | 0 | @r0_12 | +| SetFuncPtr() -> void | 0 | 13 | 1 | @r0_11 | +| SetFuncPtr() -> void | 0 | 16 | 0 | @mu* | +| String::String() -> void | 0 | 5 | 2 | @r0_4 | +| String::String() -> void | 0 | 6 | 9 | @r0_3 | +| String::String() -> void | 0 | 6 | 10 | this:@r0_2 | +| String::String() -> void | 0 | 6 | 11 | @r0_5 | +| String::String() -> void | 0 | 9 | 0 | @mu* | +| StringLiteral(int) -> void | 0 | 4 | 0 | @r0_3 | +| StringLiteral(int) -> void | 0 | 4 | 1 | @r0_2 | +| StringLiteral(int) -> void | 0 | 7 | 2 | @r0_6 | +| StringLiteral(int) -> void | 0 | 9 | 0 | @r0_8 | +| StringLiteral(int) -> void | 0 | 9 | 1 | @mu0_1 | +| StringLiteral(int) -> void | 0 | 10 | 3 | @r0_7 | +| StringLiteral(int) -> void | 0 | 10 | 4 | @r0_9 | +| StringLiteral(int) -> void | 0 | 11 | 0 | @r0_10 | +| StringLiteral(int) -> void | 0 | 11 | 1 | @mu0_1 | +| StringLiteral(int) -> void | 0 | 12 | 0 | @r0_5 | +| StringLiteral(int) -> void | 0 | 12 | 1 | @r0_11 | +| StringLiteral(int) -> void | 0 | 15 | 2 | @r0_14 | +| StringLiteral(int) -> void | 0 | 16 | 2 | @r0_15 | +| StringLiteral(int) -> void | 0 | 17 | 0 | @r0_13 | +| StringLiteral(int) -> void | 0 | 17 | 1 | @r0_16 | +| StringLiteral(int) -> void | 0 | 20 | 0 | @r0_19 | +| StringLiteral(int) -> void | 0 | 20 | 1 | @mu0_1 | +| StringLiteral(int) -> void | 0 | 22 | 0 | @r0_21 | +| StringLiteral(int) -> void | 0 | 22 | 1 | @mu0_1 | +| StringLiteral(int) -> void | 0 | 23 | 3 | @r0_20 | +| StringLiteral(int) -> void | 0 | 23 | 4 | @r0_22 | +| StringLiteral(int) -> void | 0 | 24 | 0 | @r0_23 | +| StringLiteral(int) -> void | 0 | 24 | 1 | @mu0_1 | +| StringLiteral(int) -> void | 0 | 25 | 0 | @r0_18 | +| StringLiteral(int) -> void | 0 | 25 | 1 | @r0_24 | +| StringLiteral(int) -> void | 0 | 28 | 0 | @mu* | +| Switch(int) -> void | 0 | 4 | 0 | @r0_3 | +| Switch(int) -> void | 0 | 4 | 1 | @r0_2 | +| Switch(int) -> void | 0 | 7 | 0 | @r0_5 | +| Switch(int) -> void | 0 | 7 | 1 | @r0_6 | +| Switch(int) -> void | 0 | 9 | 0 | @r0_8 | +| Switch(int) -> void | 0 | 9 | 1 | @mu0_1 | +| Switch(int) -> void | 0 | 10 | 7 | @r0_9 | +| Switch(int) -> void | 1 | 2 | 0 | @r1_1 | +| Switch(int) -> void | 1 | 2 | 1 | @r1_0 | +| Switch(int) -> void | 2 | 3 | 0 | @r2_2 | +| Switch(int) -> void | 2 | 3 | 1 | @r2_1 | +| Switch(int) -> void | 4 | 3 | 0 | @r4_2 | +| Switch(int) -> void | 4 | 3 | 1 | @r4_1 | +| Switch(int) -> void | 5 | 3 | 0 | @r5_2 | +| Switch(int) -> void | 5 | 3 | 1 | @r5_1 | +| Switch(int) -> void | 6 | 3 | 0 | @r6_2 | +| Switch(int) -> void | 6 | 3 | 1 | @r6_1 | +| Switch(int) -> void | 7 | 3 | 0 | @r7_2 | +| Switch(int) -> void | 7 | 3 | 1 | @r7_1 | +| Switch(int) -> void | 8 | 2 | 0 | @r8_1 | +| Switch(int) -> void | 8 | 2 | 1 | @r8_0 | +| Switch(int) -> void | 9 | 3 | 0 | @mu* | +| TakeReference() -> int & | 0 | 4 | 0 | @r0_2 | +| TakeReference() -> int & | 0 | 4 | 1 | @r0_3 | +| TakeReference() -> int & | 0 | 6 | 0 | @r0_5 | +| TakeReference() -> int & | 0 | 6 | 5 | @mu0_1 | +| TakeReference() -> int & | 0 | 7 | 0 | @mu* | +| TryCatch(bool) -> void | 0 | 4 | 0 | @r0_3 | +| TryCatch(bool) -> void | 0 | 4 | 1 | @r0_2 | +| TryCatch(bool) -> void | 0 | 7 | 0 | @r0_5 | +| TryCatch(bool) -> void | 0 | 7 | 1 | @r0_6 | +| TryCatch(bool) -> void | 0 | 9 | 0 | @r0_8 | +| TryCatch(bool) -> void | 0 | 9 | 1 | @mu0_1 | +| TryCatch(bool) -> void | 0 | 10 | 7 | @r0_9 | +| TryCatch(bool) -> void | 1 | 0 | 0 | @mu* | +| TryCatch(bool) -> void | 3 | 2 | 2 | @r3_1 | +| TryCatch(bool) -> void | 3 | 3 | 0 | @r3_0 | +| TryCatch(bool) -> void | 3 | 3 | 1 | @r3_2 | +| TryCatch(bool) -> void | 3 | 4 | 0 | @r3_0 | +| TryCatch(bool) -> void | 3 | 4 | 6 | @mu0_1 | +| TryCatch(bool) -> void | 4 | 1 | 0 | @r4_0 | +| TryCatch(bool) -> void | 4 | 1 | 1 | @mu0_1 | +| TryCatch(bool) -> void | 4 | 3 | 3 | @r4_1 | +| TryCatch(bool) -> void | 4 | 3 | 4 | @r4_2 | +| TryCatch(bool) -> void | 4 | 4 | 7 | @r4_3 | +| TryCatch(bool) -> void | 5 | 1 | 0 | @r5_0 | +| TryCatch(bool) -> void | 5 | 1 | 1 | @mu0_1 | +| TryCatch(bool) -> void | 5 | 2 | 7 | @r5_1 | +| TryCatch(bool) -> void | 6 | 2 | 0 | @r6_1 | +| TryCatch(bool) -> void | 6 | 2 | 1 | @r6_0 | +| TryCatch(bool) -> void | 6 | 4 | 0 | @r6_3 | +| TryCatch(bool) -> void | 6 | 4 | 1 | @mu0_1 | +| TryCatch(bool) -> void | 6 | 6 | 0 | @r6_5 | +| TryCatch(bool) -> void | 6 | 6 | 1 | @r6_4 | +| TryCatch(bool) -> void | 7 | 3 | 2 | @r7_2 | +| TryCatch(bool) -> void | 7 | 4 | 9 | @r7_1 | +| TryCatch(bool) -> void | 7 | 4 | 10 | this:@r7_0 | +| TryCatch(bool) -> void | 7 | 4 | 11 | @r7_3 | +| TryCatch(bool) -> void | 7 | 5 | 0 | @r7_0 | +| TryCatch(bool) -> void | 7 | 5 | 6 | @mu0_1 | +| TryCatch(bool) -> void | 8 | 2 | 0 | @r8_1 | +| TryCatch(bool) -> void | 8 | 2 | 1 | @r8_0 | +| TryCatch(bool) -> void | 10 | 2 | 0 | @r10_1 | +| TryCatch(bool) -> void | 10 | 2 | 1 | @r10_0 | +| TryCatch(bool) -> void | 10 | 6 | 0 | @r10_5 | +| TryCatch(bool) -> void | 10 | 6 | 1 | @mu0_1 | +| TryCatch(bool) -> void | 10 | 7 | 9 | @r10_4 | +| TryCatch(bool) -> void | 10 | 7 | 10 | this:@r10_3 | +| TryCatch(bool) -> void | 10 | 7 | 11 | @r10_6 | +| TryCatch(bool) -> void | 10 | 8 | 0 | @r10_3 | +| TryCatch(bool) -> void | 10 | 8 | 6 | @mu0_1 | +| TryCatch(bool) -> void | 12 | 2 | 0 | @r12_1 | +| TryCatch(bool) -> void | 12 | 2 | 1 | @r12_0 | +| UninitializedVariables() -> void | 0 | 4 | 0 | @r0_2 | +| UninitializedVariables() -> void | 0 | 4 | 1 | @r0_3 | +| UninitializedVariables() -> void | 0 | 7 | 0 | @r0_6 | +| UninitializedVariables() -> void | 0 | 7 | 1 | @mu0_1 | +| UninitializedVariables() -> void | 0 | 8 | 0 | @r0_5 | +| UninitializedVariables() -> void | 0 | 8 | 1 | @r0_7 | +| UninitializedVariables() -> void | 0 | 11 | 0 | @mu* | +| UnionInit(int, float) -> void | 0 | 4 | 0 | @r0_3 | +| UnionInit(int, float) -> void | 0 | 4 | 1 | @r0_2 | +| UnionInit(int, float) -> void | 0 | 7 | 0 | @r0_6 | +| UnionInit(int, float) -> void | 0 | 7 | 1 | @r0_5 | +| UnionInit(int, float) -> void | 0 | 9 | 2 | @r0_8 | +| UnionInit(int, float) -> void | 0 | 11 | 0 | @r0_10 | +| UnionInit(int, float) -> void | 0 | 11 | 1 | @mu0_1 | +| UnionInit(int, float) -> void | 0 | 12 | 2 | @r0_11 | +| UnionInit(int, float) -> void | 0 | 13 | 0 | @r0_9 | +| UnionInit(int, float) -> void | 0 | 13 | 1 | @r0_12 | +| UnionInit(int, float) -> void | 0 | 16 | 0 | @mu* | +| VarArgUsage(int) -> void | 0 | 4 | 0 | @r0_3 | +| VarArgUsage(int) -> void | 0 | 4 | 1 | @r0_2 | +| VarArgUsage(int) -> void | 0 | 7 | 0 | @r0_5 | +| VarArgUsage(int) -> void | 0 | 7 | 1 | @r0_6 | +| VarArgUsage(int) -> void | 0 | 9 | 2 | @r0_8 | +| VarArgUsage(int) -> void | 0 | 11 | 11 | @r0_9 | +| VarArgUsage(int) -> void | 0 | 11 | 12 | @r0_10 | +| VarArgUsage(int) -> void | 0 | 14 | 0 | @r0_12 | +| VarArgUsage(int) -> void | 0 | 14 | 1 | @r0_13 | +| VarArgUsage(int) -> void | 0 | 16 | 2 | @r0_15 | +| VarArgUsage(int) -> void | 0 | 18 | 2 | @r0_17 | +| VarArgUsage(int) -> void | 0 | 19 | 11 | @r0_16 | +| VarArgUsage(int) -> void | 0 | 19 | 12 | @r0_18 | +| VarArgUsage(int) -> void | 0 | 22 | 2 | @r0_21 | +| VarArgUsage(int) -> void | 0 | 23 | 11 | @r0_22 | +| VarArgUsage(int) -> void | 0 | 24 | 0 | @r0_23 | +| VarArgUsage(int) -> void | 0 | 24 | 1 | @mu0_1 | +| VarArgUsage(int) -> void | 0 | 25 | 0 | @r0_20 | +| VarArgUsage(int) -> void | 0 | 25 | 1 | @r0_24 | +| VarArgUsage(int) -> void | 0 | 28 | 2 | @r0_27 | +| VarArgUsage(int) -> void | 0 | 29 | 11 | @r0_28 | +| VarArgUsage(int) -> void | 0 | 30 | 0 | @r0_29 | +| VarArgUsage(int) -> void | 0 | 30 | 1 | @mu0_1 | +| VarArgUsage(int) -> void | 0 | 31 | 2 | @r0_30 | +| VarArgUsage(int) -> void | 0 | 32 | 0 | @r0_26 | +| VarArgUsage(int) -> void | 0 | 32 | 1 | @r0_31 | +| VarArgUsage(int) -> void | 0 | 34 | 2 | @r0_33 | +| VarArgUsage(int) -> void | 0 | 35 | 11 | @r0_34 | +| VarArgUsage(int) -> void | 0 | 37 | 2 | @r0_36 | +| VarArgUsage(int) -> void | 0 | 38 | 11 | @r0_37 | +| VarArgUsage(int) -> void | 0 | 41 | 0 | @mu* | +| VarArgs() -> void | 0 | 4 | 2 | @r0_3 | +| VarArgs() -> void | 0 | 7 | 2 | @r0_6 | +| VarArgs() -> void | 0 | 8 | 9 | @r0_2 | +| VarArgs() -> void | 0 | 8 | 11 | @r0_4 | +| VarArgs() -> void | 0 | 8 | 12 | @r0_5 | +| VarArgs() -> void | 0 | 8 | 13 | @r0_7 | +| VarArgs() -> void | 0 | 11 | 0 | @mu* | +| WhileStatements(int) -> void | 0 | 4 | 0 | @r0_3 | +| WhileStatements(int) -> void | 0 | 4 | 1 | @r0_2 | +| WhileStatements(int) -> void | 1 | 2 | 0 | @r1_1 | +| WhileStatements(int) -> void | 1 | 2 | 1 | @mu0_1 | +| WhileStatements(int) -> void | 1 | 3 | 3 | @r1_2 | +| WhileStatements(int) -> void | 1 | 3 | 4 | @r1_0 | +| WhileStatements(int) -> void | 1 | 4 | 0 | @r1_1 | +| WhileStatements(int) -> void | 1 | 4 | 1 | @r1_3 | +| WhileStatements(int) -> void | 2 | 2 | 0 | @mu* | +| WhileStatements(int) -> void | 3 | 1 | 0 | @r3_0 | +| WhileStatements(int) -> void | 3 | 1 | 1 | @mu0_1 | +| WhileStatements(int) -> void | 3 | 3 | 3 | @r3_1 | +| WhileStatements(int) -> void | 3 | 3 | 4 | @r3_2 | +| WhileStatements(int) -> void | 3 | 4 | 7 | @r3_3 | +| min(int, int) -> int | 0 | 4 | 0 | @r0_3 | +| min(int, int) -> int | 0 | 4 | 1 | @r0_2 | +| min(int, int) -> int | 0 | 7 | 0 | @r0_6 | +| min(int, int) -> int | 0 | 7 | 1 | @r0_5 | +| min(int, int) -> int | 0 | 10 | 0 | @r0_9 | +| min(int, int) -> int | 0 | 10 | 1 | @mu0_1 | +| min(int, int) -> int | 0 | 12 | 0 | @r0_11 | +| min(int, int) -> int | 0 | 12 | 1 | @mu0_1 | +| min(int, int) -> int | 0 | 13 | 3 | @r0_10 | +| min(int, int) -> int | 0 | 13 | 4 | @r0_12 | +| min(int, int) -> int | 0 | 14 | 7 | @r0_13 | +| min(int, int) -> int | 1 | 1 | 0 | @r1_0 | +| min(int, int) -> int | 1 | 1 | 1 | @mu0_1 | +| min(int, int) -> int | 1 | 3 | 0 | @r1_2 | +| min(int, int) -> int | 1 | 3 | 1 | @r1_1 | +| min(int, int) -> int | 2 | 1 | 0 | @r2_0 | +| min(int, int) -> int | 2 | 1 | 1 | @mu0_1 | +| min(int, int) -> int | 2 | 3 | 0 | @r2_2 | +| min(int, int) -> int | 2 | 3 | 1 | @r2_1 | +| min(int, int) -> int | 3 | 1 | 0 | @r3_0 | +| min(int, int) -> int | 3 | 1 | 1 | @mu0_1 | +| min(int, int) -> int | 3 | 2 | 0 | @r0_8 | +| min(int, int) -> int | 3 | 2 | 1 | @r3_1 | +| min(int, int) -> int | 3 | 4 | 0 | @r3_3 | +| min(int, int) -> int | 3 | 4 | 5 | @mu0_1 | +| min(int, int) -> int | 3 | 5 | 0 | @mu* | +#select diff --git a/cpp/ql/test/library-tests/ir/ir/ir.ql b/cpp/ql/test/library-tests/ir/ir/ir.ql new file mode 100644 index 000000000000..43fbc043dd42 --- /dev/null +++ b/cpp/ql/test/library-tests/ir/ir/ir.ql @@ -0,0 +1,7 @@ +import default +import semmle.code.cpp.ir.IR +import semmle.code.cpp.ir.PrintIR + +from Instruction instr +where none() +select instr diff --git a/cpp/ql/test/library-tests/ir/ir/ssa_block_count.expected b/cpp/ql/test/library-tests/ir/ir/ssa_block_count.expected new file mode 100644 index 000000000000..2fdcb215153a --- /dev/null +++ b/cpp/ql/test/library-tests/ir/ir/ssa_block_count.expected @@ -0,0 +1,99 @@ +| IR: AddressOf | 1 | +| IR: ArrayAccess | 1 | +| IR: ArrayConversions | 1 | +| IR: ArrayInit | 1 | +| IR: ArrayReferences | 1 | +| IR: Base | 1 | +| IR: Break | 6 | +| IR: C | 1 | +| IR: Call | 1 | +| IR: CallAdd | 1 | +| IR: CallMethods | 1 | +| IR: CallMin | 1 | +| IR: CallNestedTemplateFunc | 1 | +| IR: CallViaFuncPtr | 1 | +| IR: Comma | 1 | +| IR: CompoundAssignment | 1 | +| IR: ConditionValues | 13 | +| IR: Conditional | 4 | +| IR: Conditional_LValue | 4 | +| IR: Conditional_Void | 4 | +| IR: Constants | 1 | +| IR: Continue | 6 | +| IR: DeclareObject | 1 | +| IR: DerefReference | 1 | +| IR: Dereference | 1 | +| IR: Derived | 1 | +| IR: DerivedVB | 1 | +| IR: DoStatements | 3 | +| IR: DynamicCast | 1 | +| IR: EarlyReturn | 4 | +| IR: EarlyReturnValue | 4 | +| IR: EnumSwitch | 5 | +| IR: FieldAccess | 1 | +| IR: FloatCompare | 1 | +| IR: FloatCrement | 1 | +| IR: FloatOps | 1 | +| IR: Foo | 1 | +| IR: For_Break | 6 | +| IR: For_Condition | 4 | +| IR: For_ConditionUpdate | 4 | +| IR: For_Continue_NoUpdate | 6 | +| IR: For_Continue_Update | 6 | +| IR: For_Empty | 3 | +| IR: For_Init | 3 | +| IR: For_InitCondition | 4 | +| IR: For_InitConditionUpdate | 4 | +| IR: For_InitUpdate | 3 | +| IR: For_Update | 3 | +| IR: Func | 1 | +| IR: FuncPtrConversions | 1 | +| IR: FunctionReferences | 1 | +| IR: HierarchyConversions | 1 | +| IR: IfStatements | 8 | +| IR: InitArray | 1 | +| IR: InitList | 1 | +| IR: InitReference | 1 | +| IR: InstanceMemberFunction | 1 | +| IR: IntegerCompare | 1 | +| IR: IntegerCrement | 1 | +| IR: IntegerCrement_LValue | 1 | +| IR: IntegerOps | 1 | +| IR: LogicalAnd | 8 | +| IR: LogicalNot | 7 | +| IR: LogicalOr | 8 | +| IR: MethodCalls | 1 | +| IR: Middle | 1 | +| IR: MiddleVB1 | 1 | +| IR: MiddleVB2 | 1 | +| IR: NestedInitList | 1 | +| IR: Nullptr | 1 | +| IR: Parameters | 1 | +| IR: PointerCompare | 1 | +| IR: PointerCrement | 1 | +| IR: PointerOps | 1 | +| IR: PolymorphicBase | 1 | +| IR: PolymorphicDerived | 1 | +| IR: ReturnStruct | 1 | +| IR: SetFuncPtr | 1 | +| IR: StaticMemberFunction | 1 | +| IR: String | 1 | +| IR: StringLiteral | 1 | +| IR: Switch | 10 | +| IR: TakeReference | 1 | +| IR: TryCatch | 15 | +| IR: UninitializedVariables | 1 | +| IR: UnionInit | 1 | +| IR: VarArgUsage | 1 | +| IR: VarArgs | 1 | +| IR: VirtualMemberFunction | 1 | +| IR: WhileStatements | 4 | +| IR: min | 4 | +| IR: operator= | 1 | +| IR: ~Base | 1 | +| IR: ~Derived | 1 | +| IR: ~DerivedVB | 1 | +| IR: ~Middle | 1 | +| IR: ~MiddleVB1 | 1 | +| IR: ~MiddleVB2 | 1 | +| IR: ~PolymorphicDerived | 1 | diff --git a/cpp/ql/test/library-tests/ir/ir/ssa_block_count.ql b/cpp/ql/test/library-tests/ir/ir/ssa_block_count.ql new file mode 100644 index 000000000000..859041f74e07 --- /dev/null +++ b/cpp/ql/test/library-tests/ir/ir/ssa_block_count.ql @@ -0,0 +1,5 @@ +import default +import semmle.code.cpp.ssa.SSAIR + +from FunctionIR funcIR +select funcIR.toString(), count(funcIR.getABlock()) diff --git a/cpp/ql/test/library-tests/ir/ir/ssa_ir.expected b/cpp/ql/test/library-tests/ir/ir/ssa_ir.expected new file mode 100644 index 000000000000..c27d5ff10153 --- /dev/null +++ b/cpp/ql/test/library-tests/ir/ir/ssa_ir.expected @@ -0,0 +1,8513 @@ +printIRGraphScopes +| AddressOf() -> int * | AddressOf | +| ArrayAccess(int *, int) -> void | ArrayAccess | +| ArrayConversions() -> void | ArrayConversions | +| ArrayInit(int, float) -> void | ArrayInit | +| ArrayReferences() -> void | ArrayReferences | +| Base::Base() -> void | Base::Base | +| Base::Base(const Base &) -> void | Base::Base | +| Base::operator=(const Base &) -> Base & | Base::operator= | +| Base::~Base() -> void | Base::~Base | +| Break(int) -> void | Break | +| C::C() -> void | C::C | +| C::FieldAccess() -> void | C::FieldAccess | +| C::InstanceMemberFunction(int) -> int | C::InstanceMemberFunction | +| C::MethodCalls() -> void | C::MethodCalls | +| C::StaticMemberFunction(int) -> int | C::StaticMemberFunction | +| C::VirtualMemberFunction(int) -> int | C::VirtualMemberFunction | +| Call() -> void | Call | +| CallAdd(int, int) -> int | CallAdd | +| CallMethods(String &, String *, String) -> void | CallMethods | +| CallMin(int, int) -> int | CallMin | +| CallNestedTemplateFunc() -> double | CallNestedTemplateFunc | +| CallViaFuncPtr(..(*)(..)) -> int | CallViaFuncPtr | +| Comma(int, int) -> int | Comma | +| CompoundAssignment() -> void | CompoundAssignment | +| ConditionValues(bool, bool) -> void | ConditionValues | +| Conditional(bool, int, int) -> void | Conditional | +| Conditional_LValue(bool) -> void | Conditional_LValue | +| Conditional_Void(bool) -> void | Conditional_Void | +| Constants() -> void | Constants | +| Continue(int) -> void | Continue | +| DeclareObject() -> void | DeclareObject | +| DerefReference(int &) -> int | DerefReference | +| Dereference(int *) -> int | Dereference | +| Derived::Derived() -> void | Derived::Derived | +| Derived::operator=(const Derived &) -> Derived & | Derived::operator= | +| Derived::~Derived() -> void | Derived::~Derived | +| DerivedVB::DerivedVB() -> void | DerivedVB::DerivedVB | +| DerivedVB::~DerivedVB() -> void | DerivedVB::~DerivedVB | +| DoStatements(int) -> void | DoStatements | +| DynamicCast() -> void | DynamicCast | +| EarlyReturn(int, int) -> void | EarlyReturn | +| EarlyReturnValue(int, int) -> int | EarlyReturnValue | +| EnumSwitch(E) -> int | EnumSwitch | +| FieldAccess() -> void | FieldAccess | +| FloatCompare(double, double) -> void | FloatCompare | +| FloatCrement(float) -> void | FloatCrement | +| FloatOps(double, double) -> void | FloatOps | +| Foo() -> void | Foo | +| For_Break() -> void | For_Break | +| For_Condition() -> void | For_Condition | +| For_ConditionUpdate() -> void | For_ConditionUpdate | +| For_Continue_NoUpdate() -> void | For_Continue_NoUpdate | +| For_Continue_Update() -> void | For_Continue_Update | +| For_Empty() -> void | For_Empty | +| For_Init() -> void | For_Init | +| For_InitCondition() -> void | For_InitCondition | +| For_InitConditionUpdate() -> void | For_InitConditionUpdate | +| For_InitUpdate() -> void | For_InitUpdate | +| For_Update() -> void | For_Update | +| FuncPtrConversions(..(*)(..), void *) -> void | FuncPtrConversions | +| FunctionReferences() -> void | FunctionReferences | +| HierarchyConversions() -> void | HierarchyConversions | +| IfStatements(bool, int, int) -> void | IfStatements | +| InitArray() -> void | InitArray | +| InitList(int, float) -> void | InitList | +| InitReference(int) -> void | InitReference | +| IntegerCompare(int, int) -> void | IntegerCompare | +| IntegerCrement(int) -> void | IntegerCrement | +| IntegerCrement_LValue(int) -> void | IntegerCrement_LValue | +| IntegerOps(int, int) -> void | IntegerOps | +| LogicalAnd(bool, bool) -> void | LogicalAnd | +| LogicalNot(bool, bool) -> void | LogicalNot | +| LogicalOr(bool, bool) -> void | LogicalOr | +| Middle::Middle() -> void | Middle::Middle | +| Middle::operator=(const Middle &) -> Middle & | Middle::operator= | +| Middle::~Middle() -> void | Middle::~Middle | +| MiddleVB1::MiddleVB1() -> void | MiddleVB1::MiddleVB1 | +| MiddleVB1::~MiddleVB1() -> void | MiddleVB1::~MiddleVB1 | +| MiddleVB2::MiddleVB2() -> void | MiddleVB2::MiddleVB2 | +| MiddleVB2::~MiddleVB2() -> void | MiddleVB2::~MiddleVB2 | +| NestedInitList(int, float) -> void | NestedInitList | +| Nullptr() -> void | Nullptr | +| Outer::Func(void *, char) -> long | Outer::Func | +| Parameters(int, int) -> int | Parameters | +| PointerCompare(int *, int *) -> void | PointerCompare | +| PointerCrement(int *) -> void | PointerCrement | +| PointerOps(int *, int) -> void | PointerOps | +| PolymorphicBase::PolymorphicBase() -> void | PolymorphicBase::PolymorphicBase | +| PolymorphicDerived::PolymorphicDerived() -> void | PolymorphicDerived::PolymorphicDerived | +| PolymorphicDerived::~PolymorphicDerived() -> void | PolymorphicDerived::~PolymorphicDerived | +| ReturnStruct(Point) -> Point | ReturnStruct | +| SetFuncPtr() -> void | SetFuncPtr | +| String::String() -> void | String::String | +| StringLiteral(int) -> void | StringLiteral | +| Switch(int) -> void | Switch | +| TakeReference() -> int & | TakeReference | +| TryCatch(bool) -> void | TryCatch | +| UninitializedVariables() -> void | UninitializedVariables | +| UnionInit(int, float) -> void | UnionInit | +| VarArgUsage(int) -> void | VarArgUsage | +| VarArgs() -> void | VarArgs | +| WhileStatements(int) -> void | WhileStatements | +| min(int, int) -> int | min | +printIRGraphNodes +| AddressOf() -> int * | 0 | | | +| ArrayAccess(int *, int) -> void | 0 | | | +| ArrayConversions() -> void | 0 | | | +| ArrayInit(int, float) -> void | 0 | | | +| ArrayReferences() -> void | 0 | | | +| Base::Base() -> void | 0 | | | +| Base::Base(const Base &) -> void | 0 | | | +| Base::operator=(const Base &) -> Base & | 0 | | | +| Base::~Base() -> void | 0 | | | +| Break(int) -> void | 0 | | | +| Break(int) -> void | 1 | | | +| Break(int) -> void | 2 | | | +| Break(int) -> void | 3 | | | +| Break(int) -> void | 4 | | | +| Break(int) -> void | 5 | | | +| C::C() -> void | 0 | | | +| C::FieldAccess() -> void | 0 | | | +| C::InstanceMemberFunction(int) -> int | 0 | | | +| C::MethodCalls() -> void | 0 | | | +| C::StaticMemberFunction(int) -> int | 0 | | | +| C::VirtualMemberFunction(int) -> int | 0 | | | +| Call() -> void | 0 | | | +| CallAdd(int, int) -> int | 0 | | | +| CallMethods(String &, String *, String) -> void | 0 | | | +| CallMin(int, int) -> int | 0 | | | +| CallNestedTemplateFunc() -> double | 0 | | | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | | | +| Comma(int, int) -> int | 0 | | | +| CompoundAssignment() -> void | 0 | | | +| ConditionValues(bool, bool) -> void | 0 | | | +| ConditionValues(bool, bool) -> void | 1 | | | +| ConditionValues(bool, bool) -> void | 2 | | | +| ConditionValues(bool, bool) -> void | 3 | | | +| ConditionValues(bool, bool) -> void | 4 | | | +| ConditionValues(bool, bool) -> void | 5 | | | +| ConditionValues(bool, bool) -> void | 6 | | | +| ConditionValues(bool, bool) -> void | 7 | | | +| ConditionValues(bool, bool) -> void | 8 | | | +| ConditionValues(bool, bool) -> void | 9 | | | +| ConditionValues(bool, bool) -> void | 10 | | | +| ConditionValues(bool, bool) -> void | 11 | | | +| ConditionValues(bool, bool) -> void | 12 | | | +| Conditional(bool, int, int) -> void | 0 | | | +| Conditional(bool, int, int) -> void | 1 | | | +| Conditional(bool, int, int) -> void | 2 | | | +| Conditional(bool, int, int) -> void | 3 | | | +| Conditional_LValue(bool) -> void | 0 | | | +| Conditional_LValue(bool) -> void | 1 | | | +| Conditional_LValue(bool) -> void | 2 | | | +| Conditional_LValue(bool) -> void | 3 | | | +| Conditional_Void(bool) -> void | 0 | | | +| Conditional_Void(bool) -> void | 1 | | | +| Conditional_Void(bool) -> void | 2 | | | +| Conditional_Void(bool) -> void | 3 | | | +| Constants() -> void | 0 | | | +| Continue(int) -> void | 0 | | | +| Continue(int) -> void | 1 | | | +| Continue(int) -> void | 2 | | | +| Continue(int) -> void | 3 | | | +| Continue(int) -> void | 4 | | | +| Continue(int) -> void | 5 | | | +| DeclareObject() -> void | 0 | | | +| DerefReference(int &) -> int | 0 | | | +| Dereference(int *) -> int | 0 | | | +| Derived::Derived() -> void | 0 | | | +| Derived::operator=(const Derived &) -> Derived & | 0 | | | +| Derived::~Derived() -> void | 0 | | | +| DerivedVB::DerivedVB() -> void | 0 | | | +| DerivedVB::~DerivedVB() -> void | 0 | | | +| DoStatements(int) -> void | 0 | | | +| DoStatements(int) -> void | 1 | | | +| DoStatements(int) -> void | 2 | | | +| DynamicCast() -> void | 0 | | | +| EarlyReturn(int, int) -> void | 0 | | | +| EarlyReturn(int, int) -> void | 1 | | | +| EarlyReturn(int, int) -> void | 2 | | | +| EarlyReturn(int, int) -> void | 3 | | | +| EarlyReturnValue(int, int) -> int | 0 | | | +| EarlyReturnValue(int, int) -> int | 1 | | | +| EarlyReturnValue(int, int) -> int | 2 | | | +| EarlyReturnValue(int, int) -> int | 3 | | | +| EnumSwitch(E) -> int | 0 | | | +| EnumSwitch(E) -> int | 1 | | | +| EnumSwitch(E) -> int | 2 | | | +| EnumSwitch(E) -> int | 3 | | | +| EnumSwitch(E) -> int | 4 | | | +| FieldAccess() -> void | 0 | | | +| FloatCompare(double, double) -> void | 0 | | | +| FloatCrement(float) -> void | 0 | | | +| FloatOps(double, double) -> void | 0 | | | +| Foo() -> void | 0 | | | +| For_Break() -> void | 0 | | | +| For_Break() -> void | 1 | | | +| For_Break() -> void | 2 | | | +| For_Break() -> void | 3 | | | +| For_Break() -> void | 4 | | | +| For_Break() -> void | 5 | | | +| For_Condition() -> void | 0 | | | +| For_Condition() -> void | 1 | | | +| For_Condition() -> void | 2 | | | +| For_Condition() -> void | 3 | | | +| For_ConditionUpdate() -> void | 0 | | | +| For_ConditionUpdate() -> void | 1 | | | +| For_ConditionUpdate() -> void | 2 | | | +| For_ConditionUpdate() -> void | 3 | | | +| For_Continue_NoUpdate() -> void | 0 | | | +| For_Continue_NoUpdate() -> void | 1 | | | +| For_Continue_NoUpdate() -> void | 2 | | | +| For_Continue_NoUpdate() -> void | 3 | | | +| For_Continue_NoUpdate() -> void | 4 | | | +| For_Continue_NoUpdate() -> void | 5 | | | +| For_Continue_Update() -> void | 0 | | | +| For_Continue_Update() -> void | 1 | | | +| For_Continue_Update() -> void | 2 | | | +| For_Continue_Update() -> void | 3 | | | +| For_Continue_Update() -> void | 4 | | | +| For_Continue_Update() -> void | 5 | | | +| For_Empty() -> void | 0 | | | +| For_Empty() -> void | 1 | | | +| For_Empty() -> void | 2 | | | +| For_Init() -> void | 0 | | | +| For_Init() -> void | 1 | | | +| For_Init() -> void | 2 | | | +| For_InitCondition() -> void | 0 | | | +| For_InitCondition() -> void | 1 | | | +| For_InitCondition() -> void | 2 | | | +| For_InitCondition() -> void | 3 | | | +| For_InitConditionUpdate() -> void | 0 | | | +| For_InitConditionUpdate() -> void | 1 | | | +| For_InitConditionUpdate() -> void | 2 | | | +| For_InitConditionUpdate() -> void | 3 | | | +| For_InitUpdate() -> void | 0 | | | +| For_InitUpdate() -> void | 1 | | | +| For_InitUpdate() -> void | 2 | | | +| For_Update() -> void | 0 | | | +| For_Update() -> void | 1 | | | +| For_Update() -> void | 2 | | | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | | | +| FunctionReferences() -> void | 0 | | | +| HierarchyConversions() -> void | 0 | | | +| IfStatements(bool, int, int) -> void | 0 | | | +| IfStatements(bool, int, int) -> void | 1 | | | +| IfStatements(bool, int, int) -> void | 2 | | | +| IfStatements(bool, int, int) -> void | 3 | | | +| IfStatements(bool, int, int) -> void | 4 | | | +| IfStatements(bool, int, int) -> void | 5 | | | +| IfStatements(bool, int, int) -> void | 6 | | | +| IfStatements(bool, int, int) -> void | 7 | | | +| InitArray() -> void | 0 | | | +| InitList(int, float) -> void | 0 | | | +| InitReference(int) -> void | 0 | | | +| IntegerCompare(int, int) -> void | 0 | | | +| IntegerCrement(int) -> void | 0 | | | +| IntegerCrement_LValue(int) -> void | 0 | | | +| IntegerOps(int, int) -> void | 0 | | | +| LogicalAnd(bool, bool) -> void | 0 | | | +| LogicalAnd(bool, bool) -> void | 1 | | | +| LogicalAnd(bool, bool) -> void | 2 | | | +| LogicalAnd(bool, bool) -> void | 3 | | | +| LogicalAnd(bool, bool) -> void | 4 | | | +| LogicalAnd(bool, bool) -> void | 5 | | | +| LogicalAnd(bool, bool) -> void | 6 | | | +| LogicalAnd(bool, bool) -> void | 7 | | | +| LogicalNot(bool, bool) -> void | 0 | | | +| LogicalNot(bool, bool) -> void | 1 | | | +| LogicalNot(bool, bool) -> void | 2 | | | +| LogicalNot(bool, bool) -> void | 3 | | | +| LogicalNot(bool, bool) -> void | 4 | | | +| LogicalNot(bool, bool) -> void | 5 | | | +| LogicalNot(bool, bool) -> void | 6 | | | +| LogicalOr(bool, bool) -> void | 0 | | | +| LogicalOr(bool, bool) -> void | 1 | | | +| LogicalOr(bool, bool) -> void | 2 | | | +| LogicalOr(bool, bool) -> void | 3 | | | +| LogicalOr(bool, bool) -> void | 4 | | | +| LogicalOr(bool, bool) -> void | 5 | | | +| LogicalOr(bool, bool) -> void | 6 | | | +| LogicalOr(bool, bool) -> void | 7 | | | +| Middle::Middle() -> void | 0 | | | +| Middle::operator=(const Middle &) -> Middle & | 0 | | | +| Middle::~Middle() -> void | 0 | | | +| MiddleVB1::MiddleVB1() -> void | 0 | | | +| MiddleVB1::~MiddleVB1() -> void | 0 | | | +| MiddleVB2::MiddleVB2() -> void | 0 | | | +| MiddleVB2::~MiddleVB2() -> void | 0 | | | +| NestedInitList(int, float) -> void | 0 | | | +| Nullptr() -> void | 0 | | | +| Outer::Func(void *, char) -> long | 0 | | | +| Parameters(int, int) -> int | 0 | | | +| PointerCompare(int *, int *) -> void | 0 | | | +| PointerCrement(int *) -> void | 0 | | | +| PointerOps(int *, int) -> void | 0 | | | +| PolymorphicBase::PolymorphicBase() -> void | 0 | | | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | | | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | | | +| ReturnStruct(Point) -> Point | 0 | | | +| SetFuncPtr() -> void | 0 | | | +| String::String() -> void | 0 | | | +| StringLiteral(int) -> void | 0 | | | +| Switch(int) -> void | 0 | | | +| Switch(int) -> void | 1 | | | +| Switch(int) -> void | 2 | | | +| Switch(int) -> void | 3 | | | +| Switch(int) -> void | 4 | | | +| Switch(int) -> void | 5 | | | +| Switch(int) -> void | 6 | | | +| Switch(int) -> void | 7 | | | +| Switch(int) -> void | 8 | | | +| Switch(int) -> void | 9 | | | +| TakeReference() -> int & | 0 | | | +| TryCatch(bool) -> void | 0 | | | +| TryCatch(bool) -> void | 1 | | | +| TryCatch(bool) -> void | 2 | | | +| TryCatch(bool) -> void | 3 | | | +| TryCatch(bool) -> void | 4 | | | +| TryCatch(bool) -> void | 5 | | | +| TryCatch(bool) -> void | 6 | | | +| TryCatch(bool) -> void | 7 | | | +| TryCatch(bool) -> void | 8 | | | +| TryCatch(bool) -> void | 9 | | | +| TryCatch(bool) -> void | 10 | | | +| TryCatch(bool) -> void | 11 | | | +| TryCatch(bool) -> void | 12 | | | +| TryCatch(bool) -> void | 13 | | | +| TryCatch(bool) -> void | 14 | | | +| UninitializedVariables() -> void | 0 | | | +| UnionInit(int, float) -> void | 0 | | | +| VarArgUsage(int) -> void | 0 | | | +| VarArgs() -> void | 0 | | | +| WhileStatements(int) -> void | 0 | | | +| WhileStatements(int) -> void | 1 | | | +| WhileStatements(int) -> void | 2 | | | +| WhileStatements(int) -> void | 3 | | | +| min(int, int) -> int | 0 | | | +| min(int, int) -> int | 1 | | | +| min(int, int) -> int | 2 | | | +| min(int, int) -> int | 3 | | | +printIRGraphInstructions +| AddressOf() -> int * | 0 | 0 | EnterFunction | ir.cpp:348:6:348:14 | +| AddressOf() -> int * | 0 | 1 | UnmodeledDefinition | ir.cpp:348:6:348:14 | +| AddressOf() -> int * | 0 | 2 | VariableAddress[#return] | ir.cpp:349:5:349:14 | +| AddressOf() -> int * | 0 | 3 | VariableAddress[g] | ir.cpp:349:13:349:13 | +| AddressOf() -> int * | 0 | 4 | Store | ir.cpp:349:12:349:13 | +| AddressOf() -> int * | 0 | 5 | VariableAddress[#return] | ir.cpp:348:6:348:14 | +| AddressOf() -> int * | 0 | 6 | ReturnValue | ir.cpp:348:6:348:14 | +| AddressOf() -> int * | 0 | 7 | UnmodeledUse | ir.cpp:348:6:348:14 | +| AddressOf() -> int * | 0 | 8 | ExitFunction | ir.cpp:348:6:348:14 | +| ArrayAccess(int *, int) -> void | 0 | 0 | EnterFunction | ir.cpp:171:6:171:16 | +| ArrayAccess(int *, int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:171:6:171:16 | +| ArrayAccess(int *, int) -> void | 0 | 2 | InitializeParameter[p] | ir.cpp:171:23:171:23 | +| ArrayAccess(int *, int) -> void | 0 | 3 | VariableAddress[p] | ir.cpp:171:23:171:23 | +| ArrayAccess(int *, int) -> void | 0 | 4 | Store | ir.cpp:171:23:171:23 | +| ArrayAccess(int *, int) -> void | 0 | 5 | InitializeParameter[i] | ir.cpp:171:30:171:30 | +| ArrayAccess(int *, int) -> void | 0 | 6 | VariableAddress[i] | ir.cpp:171:30:171:30 | +| ArrayAccess(int *, int) -> void | 0 | 7 | Store | ir.cpp:171:30:171:30 | +| ArrayAccess(int *, int) -> void | 0 | 8 | VariableAddress[x] | ir.cpp:172:9:172:9 | +| ArrayAccess(int *, int) -> void | 0 | 9 | Uninitialized | ir.cpp:172:9:172:9 | +| ArrayAccess(int *, int) -> void | 0 | 10 | Store | ir.cpp:172:9:172:9 | +| ArrayAccess(int *, int) -> void | 0 | 11 | VariableAddress[p] | ir.cpp:174:9:174:9 | +| ArrayAccess(int *, int) -> void | 0 | 12 | Load | ir.cpp:174:9:174:9 | +| ArrayAccess(int *, int) -> void | 0 | 13 | VariableAddress[i] | ir.cpp:174:11:174:11 | +| ArrayAccess(int *, int) -> void | 0 | 14 | Load | ir.cpp:174:11:174:11 | +| ArrayAccess(int *, int) -> void | 0 | 15 | PointerAdd[4] | ir.cpp:174:9:174:12 | +| ArrayAccess(int *, int) -> void | 0 | 16 | Load | ir.cpp:174:9:174:12 | +| ArrayAccess(int *, int) -> void | 0 | 17 | VariableAddress[x] | ir.cpp:174:5:174:5 | +| ArrayAccess(int *, int) -> void | 0 | 18 | Store | ir.cpp:174:5:174:12 | +| ArrayAccess(int *, int) -> void | 0 | 19 | VariableAddress[p] | ir.cpp:175:11:175:11 | +| ArrayAccess(int *, int) -> void | 0 | 20 | Load | ir.cpp:175:11:175:11 | +| ArrayAccess(int *, int) -> void | 0 | 21 | VariableAddress[i] | ir.cpp:175:9:175:9 | +| ArrayAccess(int *, int) -> void | 0 | 22 | Load | ir.cpp:175:9:175:9 | +| ArrayAccess(int *, int) -> void | 0 | 23 | PointerAdd[4] | ir.cpp:175:9:175:12 | +| ArrayAccess(int *, int) -> void | 0 | 24 | Load | ir.cpp:175:9:175:12 | +| ArrayAccess(int *, int) -> void | 0 | 25 | VariableAddress[x] | ir.cpp:175:5:175:5 | +| ArrayAccess(int *, int) -> void | 0 | 26 | Store | ir.cpp:175:5:175:12 | +| ArrayAccess(int *, int) -> void | 0 | 27 | VariableAddress[x] | ir.cpp:177:12:177:12 | +| ArrayAccess(int *, int) -> void | 0 | 28 | Load | ir.cpp:177:12:177:12 | +| ArrayAccess(int *, int) -> void | 0 | 29 | VariableAddress[p] | ir.cpp:177:5:177:5 | +| ArrayAccess(int *, int) -> void | 0 | 30 | Load | ir.cpp:177:5:177:5 | +| ArrayAccess(int *, int) -> void | 0 | 31 | VariableAddress[i] | ir.cpp:177:7:177:7 | +| ArrayAccess(int *, int) -> void | 0 | 32 | Load | ir.cpp:177:7:177:7 | +| ArrayAccess(int *, int) -> void | 0 | 33 | PointerAdd[4] | ir.cpp:177:5:177:8 | +| ArrayAccess(int *, int) -> void | 0 | 34 | Store | ir.cpp:177:5:177:12 | +| ArrayAccess(int *, int) -> void | 0 | 35 | VariableAddress[x] | ir.cpp:178:12:178:12 | +| ArrayAccess(int *, int) -> void | 0 | 36 | Load | ir.cpp:178:12:178:12 | +| ArrayAccess(int *, int) -> void | 0 | 37 | VariableAddress[p] | ir.cpp:178:7:178:7 | +| ArrayAccess(int *, int) -> void | 0 | 38 | Load | ir.cpp:178:7:178:7 | +| ArrayAccess(int *, int) -> void | 0 | 39 | VariableAddress[i] | ir.cpp:178:5:178:5 | +| ArrayAccess(int *, int) -> void | 0 | 40 | Load | ir.cpp:178:5:178:5 | +| ArrayAccess(int *, int) -> void | 0 | 41 | PointerAdd[4] | ir.cpp:178:5:178:8 | +| ArrayAccess(int *, int) -> void | 0 | 42 | Store | ir.cpp:178:5:178:12 | +| ArrayAccess(int *, int) -> void | 0 | 43 | VariableAddress[a] | ir.cpp:180:9:180:9 | +| ArrayAccess(int *, int) -> void | 0 | 44 | Uninitialized | ir.cpp:180:9:180:9 | +| ArrayAccess(int *, int) -> void | 0 | 45 | Store | ir.cpp:180:9:180:9 | +| ArrayAccess(int *, int) -> void | 0 | 46 | VariableAddress[a] | ir.cpp:181:9:181:9 | +| ArrayAccess(int *, int) -> void | 0 | 47 | Convert | ir.cpp:181:9:181:9 | +| ArrayAccess(int *, int) -> void | 0 | 48 | VariableAddress[i] | ir.cpp:181:11:181:11 | +| ArrayAccess(int *, int) -> void | 0 | 49 | Load | ir.cpp:181:11:181:11 | +| ArrayAccess(int *, int) -> void | 0 | 50 | PointerAdd[4] | ir.cpp:181:9:181:12 | +| ArrayAccess(int *, int) -> void | 0 | 51 | Load | ir.cpp:181:9:181:12 | +| ArrayAccess(int *, int) -> void | 0 | 52 | VariableAddress[x] | ir.cpp:181:5:181:5 | +| ArrayAccess(int *, int) -> void | 0 | 53 | Store | ir.cpp:181:5:181:12 | +| ArrayAccess(int *, int) -> void | 0 | 54 | VariableAddress[a] | ir.cpp:182:11:182:11 | +| ArrayAccess(int *, int) -> void | 0 | 55 | Convert | ir.cpp:182:11:182:11 | +| ArrayAccess(int *, int) -> void | 0 | 56 | VariableAddress[i] | ir.cpp:182:9:182:9 | +| ArrayAccess(int *, int) -> void | 0 | 57 | Load | ir.cpp:182:9:182:9 | +| ArrayAccess(int *, int) -> void | 0 | 58 | PointerAdd[4] | ir.cpp:182:9:182:12 | +| ArrayAccess(int *, int) -> void | 0 | 59 | Load | ir.cpp:182:9:182:12 | +| ArrayAccess(int *, int) -> void | 0 | 60 | VariableAddress[x] | ir.cpp:182:5:182:5 | +| ArrayAccess(int *, int) -> void | 0 | 61 | Store | ir.cpp:182:5:182:12 | +| ArrayAccess(int *, int) -> void | 0 | 62 | VariableAddress[x] | ir.cpp:183:12:183:12 | +| ArrayAccess(int *, int) -> void | 0 | 63 | Load | ir.cpp:183:12:183:12 | +| ArrayAccess(int *, int) -> void | 0 | 64 | VariableAddress[a] | ir.cpp:183:5:183:5 | +| ArrayAccess(int *, int) -> void | 0 | 65 | Convert | ir.cpp:183:5:183:5 | +| ArrayAccess(int *, int) -> void | 0 | 66 | VariableAddress[i] | ir.cpp:183:7:183:7 | +| ArrayAccess(int *, int) -> void | 0 | 67 | Load | ir.cpp:183:7:183:7 | +| ArrayAccess(int *, int) -> void | 0 | 68 | PointerAdd[4] | ir.cpp:183:5:183:8 | +| ArrayAccess(int *, int) -> void | 0 | 69 | Store | ir.cpp:183:5:183:12 | +| ArrayAccess(int *, int) -> void | 0 | 70 | VariableAddress[x] | ir.cpp:184:12:184:12 | +| ArrayAccess(int *, int) -> void | 0 | 71 | Load | ir.cpp:184:12:184:12 | +| ArrayAccess(int *, int) -> void | 0 | 72 | VariableAddress[a] | ir.cpp:184:7:184:7 | +| ArrayAccess(int *, int) -> void | 0 | 73 | Convert | ir.cpp:184:7:184:7 | +| ArrayAccess(int *, int) -> void | 0 | 74 | VariableAddress[i] | ir.cpp:184:5:184:5 | +| ArrayAccess(int *, int) -> void | 0 | 75 | Load | ir.cpp:184:5:184:5 | +| ArrayAccess(int *, int) -> void | 0 | 76 | PointerAdd[4] | ir.cpp:184:5:184:8 | +| ArrayAccess(int *, int) -> void | 0 | 77 | Store | ir.cpp:184:5:184:12 | +| ArrayAccess(int *, int) -> void | 0 | 78 | NoOp | ir.cpp:185:1:185:1 | +| ArrayAccess(int *, int) -> void | 0 | 79 | ReturnVoid | ir.cpp:171:6:171:16 | +| ArrayAccess(int *, int) -> void | 0 | 80 | UnmodeledUse | ir.cpp:171:6:171:16 | +| ArrayAccess(int *, int) -> void | 0 | 81 | ExitFunction | ir.cpp:171:6:171:16 | +| ArrayConversions() -> void | 0 | 0 | EnterFunction | ir.cpp:871:6:871:21 | +| ArrayConversions() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:871:6:871:21 | +| ArrayConversions() -> void | 0 | 2 | VariableAddress[a] | ir.cpp:872:8:872:8 | +| ArrayConversions() -> void | 0 | 3 | Uninitialized | ir.cpp:872:8:872:8 | +| ArrayConversions() -> void | 0 | 4 | Store | ir.cpp:872:8:872:8 | +| ArrayConversions() -> void | 0 | 5 | VariableAddress[p] | ir.cpp:873:15:873:15 | +| ArrayConversions() -> void | 0 | 6 | VariableAddress[a] | ir.cpp:873:19:873:19 | +| ArrayConversions() -> void | 0 | 7 | Convert | ir.cpp:873:19:873:19 | +| ArrayConversions() -> void | 0 | 8 | Convert | ir.cpp:873:19:873:19 | +| ArrayConversions() -> void | 0 | 9 | Store | ir.cpp:873:19:873:19 | +| ArrayConversions() -> void | 0 | 10 | StringConstant["test"] | ir.cpp:874:7:874:12 | +| ArrayConversions() -> void | 0 | 11 | Convert | ir.cpp:874:7:874:12 | +| ArrayConversions() -> void | 0 | 12 | VariableAddress[p] | ir.cpp:874:3:874:3 | +| ArrayConversions() -> void | 0 | 13 | Store | ir.cpp:874:3:874:12 | +| ArrayConversions() -> void | 0 | 14 | VariableAddress[a] | ir.cpp:875:8:875:8 | +| ArrayConversions() -> void | 0 | 15 | Convert | ir.cpp:875:8:875:8 | +| ArrayConversions() -> void | 0 | 16 | Constant[0] | ir.cpp:875:10:875:10 | +| ArrayConversions() -> void | 0 | 17 | PointerAdd[1] | ir.cpp:875:8:875:11 | +| ArrayConversions() -> void | 0 | 18 | Convert | ir.cpp:875:7:875:11 | +| ArrayConversions() -> void | 0 | 19 | VariableAddress[p] | ir.cpp:875:3:875:3 | +| ArrayConversions() -> void | 0 | 20 | Store | ir.cpp:875:3:875:11 | +| ArrayConversions() -> void | 0 | 21 | StringConstant["test"] | ir.cpp:876:8:876:13 | +| ArrayConversions() -> void | 0 | 22 | Convert | ir.cpp:876:8:876:13 | +| ArrayConversions() -> void | 0 | 23 | Constant[0] | ir.cpp:876:15:876:15 | +| ArrayConversions() -> void | 0 | 24 | PointerAdd[1] | ir.cpp:876:8:876:16 | +| ArrayConversions() -> void | 0 | 25 | VariableAddress[p] | ir.cpp:876:3:876:3 | +| ArrayConversions() -> void | 0 | 26 | Store | ir.cpp:876:3:876:16 | +| ArrayConversions() -> void | 0 | 27 | VariableAddress[ra] | ir.cpp:877:10:877:11 | +| ArrayConversions() -> void | 0 | 28 | VariableAddress[a] | ir.cpp:877:19:877:19 | +| ArrayConversions() -> void | 0 | 29 | Store | ir.cpp:877:19:877:19 | +| ArrayConversions() -> void | 0 | 30 | VariableAddress[rs] | ir.cpp:878:16:878:17 | +| ArrayConversions() -> void | 0 | 31 | StringConstant["test"] | ir.cpp:878:25:878:30 | +| ArrayConversions() -> void | 0 | 32 | Store | ir.cpp:878:25:878:30 | +| ArrayConversions() -> void | 0 | 33 | VariableAddress[pa] | ir.cpp:879:16:879:17 | +| ArrayConversions() -> void | 0 | 34 | VariableAddress[a] | ir.cpp:879:26:879:26 | +| ArrayConversions() -> void | 0 | 35 | Convert | ir.cpp:879:25:879:26 | +| ArrayConversions() -> void | 0 | 36 | Store | ir.cpp:879:25:879:26 | +| ArrayConversions() -> void | 0 | 37 | StringConstant["test"] | ir.cpp:880:9:880:14 | +| ArrayConversions() -> void | 0 | 38 | VariableAddress[pa] | ir.cpp:880:3:880:4 | +| ArrayConversions() -> void | 0 | 39 | Store | ir.cpp:880:3:880:14 | +| ArrayConversions() -> void | 0 | 40 | NoOp | ir.cpp:881:1:881:1 | +| ArrayConversions() -> void | 0 | 41 | ReturnVoid | ir.cpp:871:6:871:21 | +| ArrayConversions() -> void | 0 | 42 | UnmodeledUse | ir.cpp:871:6:871:21 | +| ArrayConversions() -> void | 0 | 43 | ExitFunction | ir.cpp:871:6:871:21 | +| ArrayInit(int, float) -> void | 0 | 0 | EnterFunction | ir.cpp:519:6:519:14 | +| ArrayInit(int, float) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:519:6:519:14 | +| ArrayInit(int, float) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:519:20:519:20 | +| ArrayInit(int, float) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:519:20:519:20 | +| ArrayInit(int, float) -> void | 0 | 4 | Store | ir.cpp:519:20:519:20 | +| ArrayInit(int, float) -> void | 0 | 5 | InitializeParameter[f] | ir.cpp:519:29:519:29 | +| ArrayInit(int, float) -> void | 0 | 6 | VariableAddress[f] | ir.cpp:519:29:519:29 | +| ArrayInit(int, float) -> void | 0 | 7 | Store | ir.cpp:519:29:519:29 | +| ArrayInit(int, float) -> void | 0 | 8 | VariableAddress[a1] | ir.cpp:520:9:520:10 | +| ArrayInit(int, float) -> void | 0 | 9 | Constant[0] | ir.cpp:520:16:520:18 | +| ArrayInit(int, float) -> void | 0 | 10 | PointerAdd | ir.cpp:520:16:520:18 | +| ArrayInit(int, float) -> void | 0 | 11 | Constant[0] | ir.cpp:520:16:520:18 | +| ArrayInit(int, float) -> void | 0 | 12 | Store | ir.cpp:520:16:520:18 | +| ArrayInit(int, float) -> void | 0 | 13 | VariableAddress[a2] | ir.cpp:521:9:521:10 | +| ArrayInit(int, float) -> void | 0 | 14 | Constant[0] | ir.cpp:521:16:521:27 | +| ArrayInit(int, float) -> void | 0 | 15 | PointerAdd | ir.cpp:521:16:521:27 | +| ArrayInit(int, float) -> void | 0 | 16 | VariableAddress[x] | ir.cpp:521:19:521:19 | +| ArrayInit(int, float) -> void | 0 | 17 | Load | ir.cpp:521:19:521:19 | +| ArrayInit(int, float) -> void | 0 | 18 | Store | ir.cpp:521:19:521:19 | +| ArrayInit(int, float) -> void | 0 | 19 | Constant[1] | ir.cpp:521:16:521:27 | +| ArrayInit(int, float) -> void | 0 | 20 | PointerAdd | ir.cpp:521:16:521:27 | +| ArrayInit(int, float) -> void | 0 | 21 | VariableAddress[f] | ir.cpp:521:22:521:22 | +| ArrayInit(int, float) -> void | 0 | 22 | Load | ir.cpp:521:22:521:22 | +| ArrayInit(int, float) -> void | 0 | 23 | Convert | ir.cpp:521:22:521:22 | +| ArrayInit(int, float) -> void | 0 | 24 | Store | ir.cpp:521:22:521:22 | +| ArrayInit(int, float) -> void | 0 | 25 | Constant[2] | ir.cpp:521:16:521:27 | +| ArrayInit(int, float) -> void | 0 | 26 | PointerAdd | ir.cpp:521:16:521:27 | +| ArrayInit(int, float) -> void | 0 | 27 | Constant[0] | ir.cpp:521:25:521:25 | +| ArrayInit(int, float) -> void | 0 | 28 | Store | ir.cpp:521:25:521:25 | +| ArrayInit(int, float) -> void | 0 | 29 | VariableAddress[a3] | ir.cpp:522:9:522:10 | +| ArrayInit(int, float) -> void | 0 | 30 | Constant[0] | ir.cpp:522:16:522:21 | +| ArrayInit(int, float) -> void | 0 | 31 | PointerAdd | ir.cpp:522:16:522:21 | +| ArrayInit(int, float) -> void | 0 | 32 | VariableAddress[x] | ir.cpp:522:19:522:19 | +| ArrayInit(int, float) -> void | 0 | 33 | Load | ir.cpp:522:19:522:19 | +| ArrayInit(int, float) -> void | 0 | 34 | Store | ir.cpp:522:19:522:19 | +| ArrayInit(int, float) -> void | 0 | 35 | Constant[1] | ir.cpp:522:16:522:21 | +| ArrayInit(int, float) -> void | 0 | 36 | PointerAdd | ir.cpp:522:16:522:21 | +| ArrayInit(int, float) -> void | 0 | 37 | Constant[0] | ir.cpp:522:16:522:21 | +| ArrayInit(int, float) -> void | 0 | 38 | Store | ir.cpp:522:16:522:21 | +| ArrayInit(int, float) -> void | 0 | 39 | NoOp | ir.cpp:523:1:523:1 | +| ArrayInit(int, float) -> void | 0 | 40 | ReturnVoid | ir.cpp:519:6:519:14 | +| ArrayInit(int, float) -> void | 0 | 41 | UnmodeledUse | ir.cpp:519:6:519:14 | +| ArrayInit(int, float) -> void | 0 | 42 | ExitFunction | ir.cpp:519:6:519:14 | +| ArrayReferences() -> void | 0 | 0 | EnterFunction | ir.cpp:691:6:691:20 | +| ArrayReferences() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:691:6:691:20 | +| ArrayReferences() -> void | 0 | 2 | VariableAddress[a] | ir.cpp:692:7:692:7 | +| ArrayReferences() -> void | 0 | 3 | Uninitialized | ir.cpp:692:7:692:7 | +| ArrayReferences() -> void | 0 | 4 | Store | ir.cpp:692:7:692:7 | +| ArrayReferences() -> void | 0 | 5 | VariableAddress[ra] | ir.cpp:693:9:693:10 | +| ArrayReferences() -> void | 0 | 6 | VariableAddress[a] | ir.cpp:693:19:693:19 | +| ArrayReferences() -> void | 0 | 7 | Store | ir.cpp:693:19:693:19 | +| ArrayReferences() -> void | 0 | 8 | VariableAddress[x] | ir.cpp:694:7:694:7 | +| ArrayReferences() -> void | 0 | 9 | VariableAddress[ra] | ir.cpp:694:11:694:12 | +| ArrayReferences() -> void | 0 | 10 | Load | ir.cpp:694:11:694:12 | +| ArrayReferences() -> void | 0 | 11 | Convert | ir.cpp:694:11:694:12 | +| ArrayReferences() -> void | 0 | 12 | Constant[5] | ir.cpp:694:14:694:14 | +| ArrayReferences() -> void | 0 | 13 | PointerAdd[4] | ir.cpp:694:11:694:15 | +| ArrayReferences() -> void | 0 | 14 | Load | ir.cpp:694:11:694:15 | +| ArrayReferences() -> void | 0 | 15 | Store | ir.cpp:694:11:694:15 | +| ArrayReferences() -> void | 0 | 16 | NoOp | ir.cpp:695:1:695:1 | +| ArrayReferences() -> void | 0 | 17 | ReturnVoid | ir.cpp:691:6:691:20 | +| ArrayReferences() -> void | 0 | 18 | UnmodeledUse | ir.cpp:691:6:691:20 | +| ArrayReferences() -> void | 0 | 19 | ExitFunction | ir.cpp:691:6:691:20 | +| Base::Base() -> void | 0 | 0 | EnterFunction | ir.cpp:748:3:748:6 | +| Base::Base() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:748:3:748:6 | +| Base::Base() -> void | 0 | 2 | InitializeThis | ir.cpp:748:3:748:6 | +| Base::Base() -> void | 0 | 3 | FieldAddress[base_s] | ir.cpp:748:10:748:10 | +| Base::Base() -> void | 0 | 4 | FunctionAddress[String] | ir.cpp:748:10:748:10 | +| Base::Base() -> void | 0 | 5 | Invoke | ir.cpp:748:10:748:10 | +| Base::Base() -> void | 0 | 6 | NoOp | ir.cpp:749:3:749:3 | +| Base::Base() -> void | 0 | 7 | ReturnVoid | ir.cpp:748:3:748:6 | +| Base::Base() -> void | 0 | 8 | UnmodeledUse | ir.cpp:748:3:748:6 | +| Base::Base() -> void | 0 | 9 | ExitFunction | ir.cpp:748:3:748:6 | +| Base::Base(const Base &) -> void | 0 | 0 | EnterFunction | ir.cpp:745:8:745:8 | +| Base::Base(const Base &) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:745:8:745:8 | +| Base::Base(const Base &) -> void | 0 | 2 | InitializeThis | ir.cpp:745:8:745:8 | +| Base::Base(const Base &) -> void | 0 | 3 | InitializeParameter[p#0] | file://:0:0:0:0 | +| Base::Base(const Base &) -> void | 0 | 4 | VariableAddress[p#0] | file://:0:0:0:0 | +| Base::Base(const Base &) -> void | 0 | 5 | Store | file://:0:0:0:0 | +| Base::Base(const Base &) -> void | 0 | 6 | FieldAddress[base_s] | ir.cpp:745:8:745:8 | +| Base::Base(const Base &) -> void | 0 | 7 | FunctionAddress[String] | ir.cpp:745:8:745:8 | +| Base::Base(const Base &) -> void | 0 | 8 | Invoke | ir.cpp:745:8:745:8 | +| Base::Base(const Base &) -> void | 0 | 9 | NoOp | ir.cpp:745:8:745:8 | +| Base::Base(const Base &) -> void | 0 | 10 | ReturnVoid | ir.cpp:745:8:745:8 | +| Base::Base(const Base &) -> void | 0 | 11 | UnmodeledUse | ir.cpp:745:8:745:8 | +| Base::Base(const Base &) -> void | 0 | 12 | ExitFunction | ir.cpp:745:8:745:8 | +| Base::operator=(const Base &) -> Base & | 0 | 0 | EnterFunction | ir.cpp:745:8:745:8 | +| Base::operator=(const Base &) -> Base & | 0 | 1 | UnmodeledDefinition | ir.cpp:745:8:745:8 | +| Base::operator=(const Base &) -> Base & | 0 | 2 | InitializeThis | ir.cpp:745:8:745:8 | +| Base::operator=(const Base &) -> Base & | 0 | 3 | InitializeParameter[p#0] | file://:0:0:0:0 | +| Base::operator=(const Base &) -> Base & | 0 | 4 | VariableAddress[p#0] | file://:0:0:0:0 | +| Base::operator=(const Base &) -> Base & | 0 | 5 | Store | file://:0:0:0:0 | +| Base::operator=(const Base &) -> Base & | 0 | 6 | CopyValue | file://:0:0:0:0 | +| Base::operator=(const Base &) -> Base & | 0 | 7 | FieldAddress[base_s] | file://:0:0:0:0 | +| Base::operator=(const Base &) -> Base & | 0 | 8 | FunctionAddress[operator=] | ir.cpp:745:8:745:8 | +| Base::operator=(const Base &) -> Base & | 0 | 9 | VariableAddress[p#0] | file://:0:0:0:0 | +| Base::operator=(const Base &) -> Base & | 0 | 10 | Load | file://:0:0:0:0 | +| Base::operator=(const Base &) -> Base & | 0 | 11 | FieldAddress[base_s] | file://:0:0:0:0 | +| Base::operator=(const Base &) -> Base & | 0 | 12 | Invoke | ir.cpp:745:8:745:8 | +| Base::operator=(const Base &) -> Base & | 0 | 13 | VariableAddress[#return] | file://:0:0:0:0 | +| Base::operator=(const Base &) -> Base & | 0 | 14 | CopyValue | file://:0:0:0:0 | +| Base::operator=(const Base &) -> Base & | 0 | 15 | Store | file://:0:0:0:0 | +| Base::operator=(const Base &) -> Base & | 0 | 16 | VariableAddress[#return] | ir.cpp:745:8:745:8 | +| Base::operator=(const Base &) -> Base & | 0 | 17 | ReturnValue | ir.cpp:745:8:745:8 | +| Base::operator=(const Base &) -> Base & | 0 | 18 | UnmodeledUse | ir.cpp:745:8:745:8 | +| Base::operator=(const Base &) -> Base & | 0 | 19 | ExitFunction | ir.cpp:745:8:745:8 | +| Base::~Base() -> void | 0 | 0 | EnterFunction | ir.cpp:750:3:750:7 | +| Base::~Base() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:750:3:750:7 | +| Base::~Base() -> void | 0 | 2 | InitializeThis | ir.cpp:750:3:750:7 | +| Base::~Base() -> void | 0 | 3 | NoOp | ir.cpp:751:3:751:3 | +| Base::~Base() -> void | 0 | 4 | FieldAddress[base_s] | ir.cpp:751:3:751:3 | +| Base::~Base() -> void | 0 | 5 | FunctionAddress[~String] | ir.cpp:751:3:751:3 | +| Base::~Base() -> void | 0 | 6 | Invoke | ir.cpp:751:3:751:3 | +| Base::~Base() -> void | 0 | 7 | ReturnVoid | ir.cpp:750:3:750:7 | +| Base::~Base() -> void | 0 | 8 | UnmodeledUse | ir.cpp:750:3:750:7 | +| Base::~Base() -> void | 0 | 9 | ExitFunction | ir.cpp:750:3:750:7 | +| Break(int) -> void | 0 | 0 | EnterFunction | ir.cpp:352:6:352:10 | +| Break(int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:352:6:352:10 | +| Break(int) -> void | 0 | 2 | InitializeParameter[n] | ir.cpp:352:16:352:16 | +| Break(int) -> void | 0 | 3 | VariableAddress[n] | ir.cpp:352:16:352:16 | +| Break(int) -> void | 0 | 4 | Store | ir.cpp:352:16:352:16 | +| Break(int) -> void | 1 | 0 | VariableAddress[n] | ir.cpp:354:13:354:13 | +| Break(int) -> void | 1 | 1 | Load | ir.cpp:354:13:354:13 | +| Break(int) -> void | 1 | 2 | Constant[1] | ir.cpp:354:18:354:18 | +| Break(int) -> void | 1 | 3 | CompareEQ | ir.cpp:354:13:354:18 | +| Break(int) -> void | 1 | 4 | ConditionalBranch | ir.cpp:354:13:354:18 | +| Break(int) -> void | 2 | 0 | NoOp | ir.cpp:355:13:355:18 | +| Break(int) -> void | 3 | 0 | Constant[1] | ir.cpp:356:14:356:14 | +| Break(int) -> void | 3 | 1 | VariableAddress[n] | ir.cpp:356:9:356:9 | +| Break(int) -> void | 3 | 2 | Load | ir.cpp:356:9:356:14 | +| Break(int) -> void | 3 | 3 | Sub | ir.cpp:356:9:356:14 | +| Break(int) -> void | 3 | 4 | Store | ir.cpp:356:9:356:14 | +| Break(int) -> void | 4 | 0 | NoOp | ir.cpp:357:5:357:5 | +| Break(int) -> void | 4 | 1 | NoOp | ir.cpp:358:1:358:1 | +| Break(int) -> void | 4 | 2 | ReturnVoid | ir.cpp:352:6:352:10 | +| Break(int) -> void | 4 | 3 | UnmodeledUse | ir.cpp:352:6:352:10 | +| Break(int) -> void | 4 | 4 | ExitFunction | ir.cpp:352:6:352:10 | +| Break(int) -> void | 5 | 0 | Phi | ir.cpp:353:12:353:12 | +| Break(int) -> void | 5 | 1 | VariableAddress[n] | ir.cpp:353:12:353:12 | +| Break(int) -> void | 5 | 2 | Load | ir.cpp:353:12:353:12 | +| Break(int) -> void | 5 | 3 | Constant[0] | ir.cpp:353:16:353:16 | +| Break(int) -> void | 5 | 4 | CompareGT | ir.cpp:353:12:353:16 | +| Break(int) -> void | 5 | 5 | ConditionalBranch | ir.cpp:353:12:353:16 | +| C::C() -> void | 0 | 0 | EnterFunction | ir.cpp:658:5:658:5 | +| C::C() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:658:5:658:5 | +| C::C() -> void | 0 | 2 | InitializeThis | ir.cpp:658:5:658:5 | +| C::C() -> void | 0 | 3 | FieldAddress[m_a] | ir.cpp:659:9:659:14 | +| C::C() -> void | 0 | 4 | Constant[1] | ir.cpp:659:9:659:14 | +| C::C() -> void | 0 | 5 | Store | ir.cpp:659:9:659:14 | +| C::C() -> void | 0 | 6 | FieldAddress[m_b] | ir.cpp:663:5:663:5 | +| C::C() -> void | 0 | 7 | FunctionAddress[String] | ir.cpp:663:5:663:5 | +| C::C() -> void | 0 | 8 | Invoke | ir.cpp:663:5:663:5 | +| C::C() -> void | 0 | 9 | FieldAddress[m_c] | ir.cpp:660:9:660:14 | +| C::C() -> void | 0 | 10 | Constant[3] | ir.cpp:660:13:660:13 | +| C::C() -> void | 0 | 11 | Store | ir.cpp:660:13:660:13 | +| C::C() -> void | 0 | 12 | FieldAddress[m_e] | ir.cpp:661:9:661:13 | +| C::C() -> void | 0 | 13 | Constant[0] | ir.cpp:661:9:661:13 | +| C::C() -> void | 0 | 14 | Store | ir.cpp:661:9:661:13 | +| C::C() -> void | 0 | 15 | FieldAddress[m_f] | ir.cpp:662:9:662:19 | +| C::C() -> void | 0 | 16 | FunctionAddress[String] | ir.cpp:662:9:662:19 | +| C::C() -> void | 0 | 17 | StringConstant["test"] | ir.cpp:662:13:662:18 | +| C::C() -> void | 0 | 18 | Convert | ir.cpp:662:13:662:18 | +| C::C() -> void | 0 | 19 | Invoke | ir.cpp:662:9:662:19 | +| C::C() -> void | 0 | 20 | NoOp | ir.cpp:664:5:664:5 | +| C::C() -> void | 0 | 21 | ReturnVoid | ir.cpp:658:5:658:5 | +| C::C() -> void | 0 | 22 | UnmodeledUse | ir.cpp:658:5:658:5 | +| C::C() -> void | 0 | 23 | ExitFunction | ir.cpp:658:5:658:5 | +| C::FieldAccess() -> void | 0 | 0 | EnterFunction | ir.cpp:642:10:642:20 | +| C::FieldAccess() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:642:10:642:20 | +| C::FieldAccess() -> void | 0 | 2 | InitializeThis | ir.cpp:642:10:642:20 | +| C::FieldAccess() -> void | 0 | 3 | Constant[0] | ir.cpp:643:21:643:21 | +| C::FieldAccess() -> void | 0 | 4 | CopyValue | ir.cpp:643:9:643:12 | +| C::FieldAccess() -> void | 0 | 5 | FieldAddress[m_a] | ir.cpp:643:15:643:17 | +| C::FieldAccess() -> void | 0 | 6 | Store | ir.cpp:643:9:643:21 | +| C::FieldAccess() -> void | 0 | 7 | Constant[1] | ir.cpp:644:23:644:23 | +| C::FieldAccess() -> void | 0 | 8 | CopyValue | ir.cpp:644:11:644:14 | +| C::FieldAccess() -> void | 0 | 9 | FieldAddress[m_a] | ir.cpp:644:17:644:19 | +| C::FieldAccess() -> void | 0 | 10 | Store | ir.cpp:644:9:644:23 | +| C::FieldAccess() -> void | 0 | 11 | Constant[2] | ir.cpp:645:15:645:15 | +| C::FieldAccess() -> void | 0 | 12 | CopyValue | file://:0:0:0:0 | +| C::FieldAccess() -> void | 0 | 13 | FieldAddress[m_a] | ir.cpp:645:9:645:11 | +| C::FieldAccess() -> void | 0 | 14 | Store | ir.cpp:645:9:645:15 | +| C::FieldAccess() -> void | 0 | 15 | VariableAddress[x] | ir.cpp:646:13:646:13 | +| C::FieldAccess() -> void | 0 | 16 | Uninitialized | ir.cpp:646:13:646:13 | +| C::FieldAccess() -> void | 0 | 17 | Store | ir.cpp:646:13:646:13 | +| C::FieldAccess() -> void | 0 | 18 | CopyValue | ir.cpp:647:13:647:16 | +| C::FieldAccess() -> void | 0 | 19 | FieldAddress[m_a] | ir.cpp:647:19:647:21 | +| C::FieldAccess() -> void | 0 | 20 | Load | ir.cpp:647:19:647:21 | +| C::FieldAccess() -> void | 0 | 21 | VariableAddress[x] | ir.cpp:647:9:647:9 | +| C::FieldAccess() -> void | 0 | 22 | Store | ir.cpp:647:9:647:21 | +| C::FieldAccess() -> void | 0 | 23 | CopyValue | ir.cpp:648:15:648:18 | +| C::FieldAccess() -> void | 0 | 24 | FieldAddress[m_a] | ir.cpp:648:21:648:23 | +| C::FieldAccess() -> void | 0 | 25 | Load | ir.cpp:648:21:648:23 | +| C::FieldAccess() -> void | 0 | 26 | VariableAddress[x] | ir.cpp:648:9:648:9 | +| C::FieldAccess() -> void | 0 | 27 | Store | ir.cpp:648:9:648:23 | +| C::FieldAccess() -> void | 0 | 28 | CopyValue | file://:0:0:0:0 | +| C::FieldAccess() -> void | 0 | 29 | FieldAddress[m_a] | ir.cpp:649:13:649:15 | +| C::FieldAccess() -> void | 0 | 30 | Load | ir.cpp:649:13:649:15 | +| C::FieldAccess() -> void | 0 | 31 | VariableAddress[x] | ir.cpp:649:9:649:9 | +| C::FieldAccess() -> void | 0 | 32 | Store | ir.cpp:649:9:649:15 | +| C::FieldAccess() -> void | 0 | 33 | NoOp | ir.cpp:650:5:650:5 | +| C::FieldAccess() -> void | 0 | 34 | ReturnVoid | ir.cpp:642:10:642:20 | +| C::FieldAccess() -> void | 0 | 35 | UnmodeledUse | ir.cpp:642:10:642:20 | +| C::FieldAccess() -> void | 0 | 36 | ExitFunction | ir.cpp:642:10:642:20 | +| C::InstanceMemberFunction(int) -> int | 0 | 0 | EnterFunction | ir.cpp:634:9:634:30 | +| C::InstanceMemberFunction(int) -> int | 0 | 1 | UnmodeledDefinition | ir.cpp:634:9:634:30 | +| C::InstanceMemberFunction(int) -> int | 0 | 2 | InitializeThis | ir.cpp:634:9:634:30 | +| C::InstanceMemberFunction(int) -> int | 0 | 3 | InitializeParameter[x] | ir.cpp:634:36:634:36 | +| C::InstanceMemberFunction(int) -> int | 0 | 4 | VariableAddress[x] | ir.cpp:634:36:634:36 | +| C::InstanceMemberFunction(int) -> int | 0 | 5 | Store | ir.cpp:634:36:634:36 | +| C::InstanceMemberFunction(int) -> int | 0 | 6 | VariableAddress[#return] | ir.cpp:635:9:635:17 | +| C::InstanceMemberFunction(int) -> int | 0 | 7 | VariableAddress[x] | ir.cpp:635:16:635:16 | +| C::InstanceMemberFunction(int) -> int | 0 | 8 | Load | ir.cpp:635:16:635:16 | +| C::InstanceMemberFunction(int) -> int | 0 | 9 | Store | ir.cpp:635:16:635:16 | +| C::InstanceMemberFunction(int) -> int | 0 | 10 | VariableAddress[#return] | ir.cpp:634:9:634:30 | +| C::InstanceMemberFunction(int) -> int | 0 | 11 | ReturnValue | ir.cpp:634:9:634:30 | +| C::InstanceMemberFunction(int) -> int | 0 | 12 | UnmodeledUse | ir.cpp:634:9:634:30 | +| C::InstanceMemberFunction(int) -> int | 0 | 13 | ExitFunction | ir.cpp:634:9:634:30 | +| C::MethodCalls() -> void | 0 | 0 | EnterFunction | ir.cpp:652:10:652:20 | +| C::MethodCalls() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:652:10:652:20 | +| C::MethodCalls() -> void | 0 | 2 | InitializeThis | ir.cpp:652:10:652:20 | +| C::MethodCalls() -> void | 0 | 3 | CopyValue | ir.cpp:653:9:653:12 | +| C::MethodCalls() -> void | 0 | 4 | FunctionAddress[InstanceMemberFunction] | ir.cpp:653:15:653:36 | +| C::MethodCalls() -> void | 0 | 5 | Constant[0] | ir.cpp:653:38:653:38 | +| C::MethodCalls() -> void | 0 | 6 | Invoke | ir.cpp:653:15:653:36 | +| C::MethodCalls() -> void | 0 | 7 | CopyValue | ir.cpp:654:11:654:14 | +| C::MethodCalls() -> void | 0 | 8 | FunctionAddress[InstanceMemberFunction] | ir.cpp:654:17:654:38 | +| C::MethodCalls() -> void | 0 | 9 | Constant[1] | ir.cpp:654:40:654:40 | +| C::MethodCalls() -> void | 0 | 10 | Invoke | ir.cpp:654:17:654:38 | +| C::MethodCalls() -> void | 0 | 11 | CopyValue | file://:0:0:0:0 | +| C::MethodCalls() -> void | 0 | 12 | FunctionAddress[InstanceMemberFunction] | ir.cpp:655:9:655:30 | +| C::MethodCalls() -> void | 0 | 13 | Constant[2] | ir.cpp:655:32:655:32 | +| C::MethodCalls() -> void | 0 | 14 | Invoke | ir.cpp:655:9:655:30 | +| C::MethodCalls() -> void | 0 | 15 | NoOp | ir.cpp:656:5:656:5 | +| C::MethodCalls() -> void | 0 | 16 | ReturnVoid | ir.cpp:652:10:652:20 | +| C::MethodCalls() -> void | 0 | 17 | UnmodeledUse | ir.cpp:652:10:652:20 | +| C::MethodCalls() -> void | 0 | 18 | ExitFunction | ir.cpp:652:10:652:20 | +| C::StaticMemberFunction(int) -> int | 0 | 0 | EnterFunction | ir.cpp:630:16:630:35 | +| C::StaticMemberFunction(int) -> int | 0 | 1 | UnmodeledDefinition | ir.cpp:630:16:630:35 | +| C::StaticMemberFunction(int) -> int | 0 | 2 | InitializeParameter[x] | ir.cpp:630:41:630:41 | +| C::StaticMemberFunction(int) -> int | 0 | 3 | VariableAddress[x] | ir.cpp:630:41:630:41 | +| C::StaticMemberFunction(int) -> int | 0 | 4 | Store | ir.cpp:630:41:630:41 | +| C::StaticMemberFunction(int) -> int | 0 | 5 | VariableAddress[#return] | ir.cpp:631:9:631:17 | +| C::StaticMemberFunction(int) -> int | 0 | 6 | VariableAddress[x] | ir.cpp:631:16:631:16 | +| C::StaticMemberFunction(int) -> int | 0 | 7 | Load | ir.cpp:631:16:631:16 | +| C::StaticMemberFunction(int) -> int | 0 | 8 | Store | ir.cpp:631:16:631:16 | +| C::StaticMemberFunction(int) -> int | 0 | 9 | VariableAddress[#return] | ir.cpp:630:16:630:35 | +| C::StaticMemberFunction(int) -> int | 0 | 10 | ReturnValue | ir.cpp:630:16:630:35 | +| C::StaticMemberFunction(int) -> int | 0 | 11 | UnmodeledUse | ir.cpp:630:16:630:35 | +| C::StaticMemberFunction(int) -> int | 0 | 12 | ExitFunction | ir.cpp:630:16:630:35 | +| C::VirtualMemberFunction(int) -> int | 0 | 0 | EnterFunction | ir.cpp:638:17:638:37 | +| C::VirtualMemberFunction(int) -> int | 0 | 1 | UnmodeledDefinition | ir.cpp:638:17:638:37 | +| C::VirtualMemberFunction(int) -> int | 0 | 2 | InitializeThis | ir.cpp:638:17:638:37 | +| C::VirtualMemberFunction(int) -> int | 0 | 3 | InitializeParameter[x] | ir.cpp:638:43:638:43 | +| C::VirtualMemberFunction(int) -> int | 0 | 4 | VariableAddress[x] | ir.cpp:638:43:638:43 | +| C::VirtualMemberFunction(int) -> int | 0 | 5 | Store | ir.cpp:638:43:638:43 | +| C::VirtualMemberFunction(int) -> int | 0 | 6 | VariableAddress[#return] | ir.cpp:639:9:639:17 | +| C::VirtualMemberFunction(int) -> int | 0 | 7 | VariableAddress[x] | ir.cpp:639:16:639:16 | +| C::VirtualMemberFunction(int) -> int | 0 | 8 | Load | ir.cpp:639:16:639:16 | +| C::VirtualMemberFunction(int) -> int | 0 | 9 | Store | ir.cpp:639:16:639:16 | +| C::VirtualMemberFunction(int) -> int | 0 | 10 | VariableAddress[#return] | ir.cpp:638:17:638:37 | +| C::VirtualMemberFunction(int) -> int | 0 | 11 | ReturnValue | ir.cpp:638:17:638:37 | +| C::VirtualMemberFunction(int) -> int | 0 | 12 | UnmodeledUse | ir.cpp:638:17:638:37 | +| C::VirtualMemberFunction(int) -> int | 0 | 13 | ExitFunction | ir.cpp:638:17:638:37 | +| Call() -> void | 0 | 0 | EnterFunction | ir.cpp:372:6:372:9 | +| Call() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:372:6:372:9 | +| Call() -> void | 0 | 2 | FunctionAddress[VoidFunc] | ir.cpp:373:5:373:12 | +| Call() -> void | 0 | 3 | Invoke | ir.cpp:373:5:373:12 | +| Call() -> void | 0 | 4 | NoOp | ir.cpp:374:1:374:1 | +| Call() -> void | 0 | 5 | ReturnVoid | ir.cpp:372:6:372:9 | +| Call() -> void | 0 | 6 | UnmodeledUse | ir.cpp:372:6:372:9 | +| Call() -> void | 0 | 7 | ExitFunction | ir.cpp:372:6:372:9 | +| CallAdd(int, int) -> int | 0 | 0 | EnterFunction | ir.cpp:376:5:376:11 | +| CallAdd(int, int) -> int | 0 | 1 | UnmodeledDefinition | ir.cpp:376:5:376:11 | +| CallAdd(int, int) -> int | 0 | 2 | InitializeParameter[x] | ir.cpp:376:17:376:17 | +| CallAdd(int, int) -> int | 0 | 3 | VariableAddress[x] | ir.cpp:376:17:376:17 | +| CallAdd(int, int) -> int | 0 | 4 | Store | ir.cpp:376:17:376:17 | +| CallAdd(int, int) -> int | 0 | 5 | InitializeParameter[y] | ir.cpp:376:24:376:24 | +| CallAdd(int, int) -> int | 0 | 6 | VariableAddress[y] | ir.cpp:376:24:376:24 | +| CallAdd(int, int) -> int | 0 | 7 | Store | ir.cpp:376:24:376:24 | +| CallAdd(int, int) -> int | 0 | 8 | VariableAddress[#return] | ir.cpp:377:5:377:21 | +| CallAdd(int, int) -> int | 0 | 9 | FunctionAddress[Add] | ir.cpp:377:12:377:14 | +| CallAdd(int, int) -> int | 0 | 10 | VariableAddress[x] | ir.cpp:377:16:377:16 | +| CallAdd(int, int) -> int | 0 | 11 | Load | ir.cpp:377:16:377:16 | +| CallAdd(int, int) -> int | 0 | 12 | VariableAddress[y] | ir.cpp:377:19:377:19 | +| CallAdd(int, int) -> int | 0 | 13 | Load | ir.cpp:377:19:377:19 | +| CallAdd(int, int) -> int | 0 | 14 | Invoke | ir.cpp:377:12:377:14 | +| CallAdd(int, int) -> int | 0 | 15 | Store | ir.cpp:377:12:377:14 | +| CallAdd(int, int) -> int | 0 | 16 | VariableAddress[#return] | ir.cpp:376:5:376:11 | +| CallAdd(int, int) -> int | 0 | 17 | ReturnValue | ir.cpp:376:5:376:11 | +| CallAdd(int, int) -> int | 0 | 18 | UnmodeledUse | ir.cpp:376:5:376:11 | +| CallAdd(int, int) -> int | 0 | 19 | ExitFunction | ir.cpp:376:5:376:11 | +| CallMethods(String &, String *, String) -> void | 0 | 0 | EnterFunction | ir.cpp:622:6:622:16 | +| CallMethods(String &, String *, String) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:622:6:622:16 | +| CallMethods(String &, String *, String) -> void | 0 | 2 | InitializeParameter[r] | ir.cpp:622:26:622:26 | +| CallMethods(String &, String *, String) -> void | 0 | 3 | VariableAddress[r] | ir.cpp:622:26:622:26 | +| CallMethods(String &, String *, String) -> void | 0 | 4 | Store | ir.cpp:622:26:622:26 | +| CallMethods(String &, String *, String) -> void | 0 | 5 | InitializeParameter[p] | ir.cpp:622:37:622:37 | +| CallMethods(String &, String *, String) -> void | 0 | 6 | VariableAddress[p] | ir.cpp:622:37:622:37 | +| CallMethods(String &, String *, String) -> void | 0 | 7 | Store | ir.cpp:622:37:622:37 | +| CallMethods(String &, String *, String) -> void | 0 | 8 | InitializeParameter[s] | ir.cpp:622:47:622:47 | +| CallMethods(String &, String *, String) -> void | 0 | 9 | VariableAddress[s] | ir.cpp:622:47:622:47 | +| CallMethods(String &, String *, String) -> void | 0 | 10 | Store | ir.cpp:622:47:622:47 | +| CallMethods(String &, String *, String) -> void | 0 | 11 | VariableAddress[r] | ir.cpp:623:5:623:5 | +| CallMethods(String &, String *, String) -> void | 0 | 12 | Load | ir.cpp:623:5:623:5 | +| CallMethods(String &, String *, String) -> void | 0 | 13 | Convert | ir.cpp:623:5:623:5 | +| CallMethods(String &, String *, String) -> void | 0 | 14 | FunctionAddress[c_str] | ir.cpp:623:7:623:11 | +| CallMethods(String &, String *, String) -> void | 0 | 15 | Invoke | ir.cpp:623:7:623:11 | +| CallMethods(String &, String *, String) -> void | 0 | 16 | VariableAddress[p] | ir.cpp:624:5:624:5 | +| CallMethods(String &, String *, String) -> void | 0 | 17 | Load | ir.cpp:624:5:624:5 | +| CallMethods(String &, String *, String) -> void | 0 | 18 | Convert | ir.cpp:624:5:624:5 | +| CallMethods(String &, String *, String) -> void | 0 | 19 | FunctionAddress[c_str] | ir.cpp:624:8:624:12 | +| CallMethods(String &, String *, String) -> void | 0 | 20 | Invoke | ir.cpp:624:8:624:12 | +| CallMethods(String &, String *, String) -> void | 0 | 21 | VariableAddress[s] | ir.cpp:625:5:625:5 | +| CallMethods(String &, String *, String) -> void | 0 | 22 | Convert | ir.cpp:625:5:625:5 | +| CallMethods(String &, String *, String) -> void | 0 | 23 | FunctionAddress[c_str] | ir.cpp:625:7:625:11 | +| CallMethods(String &, String *, String) -> void | 0 | 24 | Invoke | ir.cpp:625:7:625:11 | +| CallMethods(String &, String *, String) -> void | 0 | 25 | NoOp | ir.cpp:626:1:626:1 | +| CallMethods(String &, String *, String) -> void | 0 | 26 | ReturnVoid | ir.cpp:622:6:622:16 | +| CallMethods(String &, String *, String) -> void | 0 | 27 | UnmodeledUse | ir.cpp:622:6:622:16 | +| CallMethods(String &, String *, String) -> void | 0 | 28 | ExitFunction | ir.cpp:622:6:622:16 | +| CallMin(int, int) -> int | 0 | 0 | EnterFunction | ir.cpp:708:5:708:11 | +| CallMin(int, int) -> int | 0 | 1 | UnmodeledDefinition | ir.cpp:708:5:708:11 | +| CallMin(int, int) -> int | 0 | 2 | InitializeParameter[x] | ir.cpp:708:17:708:17 | +| CallMin(int, int) -> int | 0 | 3 | VariableAddress[x] | ir.cpp:708:17:708:17 | +| CallMin(int, int) -> int | 0 | 4 | Store | ir.cpp:708:17:708:17 | +| CallMin(int, int) -> int | 0 | 5 | InitializeParameter[y] | ir.cpp:708:24:708:24 | +| CallMin(int, int) -> int | 0 | 6 | VariableAddress[y] | ir.cpp:708:24:708:24 | +| CallMin(int, int) -> int | 0 | 7 | Store | ir.cpp:708:24:708:24 | +| CallMin(int, int) -> int | 0 | 8 | VariableAddress[#return] | ir.cpp:709:3:709:19 | +| CallMin(int, int) -> int | 0 | 9 | FunctionAddress[min] | ir.cpp:709:10:709:12 | +| CallMin(int, int) -> int | 0 | 10 | VariableAddress[x] | ir.cpp:709:14:709:14 | +| CallMin(int, int) -> int | 0 | 11 | Load | ir.cpp:709:14:709:14 | +| CallMin(int, int) -> int | 0 | 12 | VariableAddress[y] | ir.cpp:709:17:709:17 | +| CallMin(int, int) -> int | 0 | 13 | Load | ir.cpp:709:17:709:17 | +| CallMin(int, int) -> int | 0 | 14 | Invoke | ir.cpp:709:10:709:12 | +| CallMin(int, int) -> int | 0 | 15 | Store | ir.cpp:709:10:709:12 | +| CallMin(int, int) -> int | 0 | 16 | VariableAddress[#return] | ir.cpp:708:5:708:11 | +| CallMin(int, int) -> int | 0 | 17 | ReturnValue | ir.cpp:708:5:708:11 | +| CallMin(int, int) -> int | 0 | 18 | UnmodeledUse | ir.cpp:708:5:708:11 | +| CallMin(int, int) -> int | 0 | 19 | ExitFunction | ir.cpp:708:5:708:11 | +| CallNestedTemplateFunc() -> double | 0 | 0 | EnterFunction | ir.cpp:720:8:720:29 | +| CallNestedTemplateFunc() -> double | 0 | 1 | UnmodeledDefinition | ir.cpp:720:8:720:29 | +| CallNestedTemplateFunc() -> double | 0 | 2 | VariableAddress[#return] | ir.cpp:721:3:721:54 | +| CallNestedTemplateFunc() -> double | 0 | 3 | FunctionAddress[Func] | ir.cpp:721:10:721:39 | +| CallNestedTemplateFunc() -> double | 0 | 4 | Constant[0] | ir.cpp:721:41:721:47 | +| CallNestedTemplateFunc() -> double | 0 | 5 | Constant[111] | ir.cpp:721:50:721:52 | +| CallNestedTemplateFunc() -> double | 0 | 6 | Invoke | ir.cpp:721:10:721:39 | +| CallNestedTemplateFunc() -> double | 0 | 7 | Convert | ir.cpp:721:10:721:53 | +| CallNestedTemplateFunc() -> double | 0 | 8 | Store | ir.cpp:721:10:721:53 | +| CallNestedTemplateFunc() -> double | 0 | 9 | VariableAddress[#return] | ir.cpp:720:8:720:29 | +| CallNestedTemplateFunc() -> double | 0 | 10 | ReturnValue | ir.cpp:720:8:720:29 | +| CallNestedTemplateFunc() -> double | 0 | 11 | UnmodeledUse | ir.cpp:720:8:720:29 | +| CallNestedTemplateFunc() -> double | 0 | 12 | ExitFunction | ir.cpp:720:8:720:29 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 0 | EnterFunction | ir.cpp:551:5:551:18 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 1 | UnmodeledDefinition | ir.cpp:551:5:551:18 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 2 | InitializeParameter[pfn] | ir.cpp:551:26:551:28 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 3 | VariableAddress[pfn] | ir.cpp:551:26:551:28 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 4 | Store | ir.cpp:551:26:551:28 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 5 | VariableAddress[#return] | ir.cpp:552:5:552:18 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 6 | VariableAddress[pfn] | ir.cpp:552:12:552:14 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 7 | Load | ir.cpp:552:12:552:14 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 8 | Constant[5] | ir.cpp:552:16:552:16 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 9 | Invoke | ir.cpp:552:12:552:17 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 10 | Store | ir.cpp:552:12:552:17 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 11 | VariableAddress[#return] | ir.cpp:551:5:551:18 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 12 | ReturnValue | ir.cpp:551:5:551:18 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 13 | UnmodeledUse | ir.cpp:551:5:551:18 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 14 | ExitFunction | ir.cpp:551:5:551:18 | +| Comma(int, int) -> int | 0 | 0 | EnterFunction | ir.cpp:380:5:380:9 | +| Comma(int, int) -> int | 0 | 1 | UnmodeledDefinition | ir.cpp:380:5:380:9 | +| Comma(int, int) -> int | 0 | 2 | InitializeParameter[x] | ir.cpp:380:15:380:15 | +| Comma(int, int) -> int | 0 | 3 | VariableAddress[x] | ir.cpp:380:15:380:15 | +| Comma(int, int) -> int | 0 | 4 | Store | ir.cpp:380:15:380:15 | +| Comma(int, int) -> int | 0 | 5 | InitializeParameter[y] | ir.cpp:380:22:380:22 | +| Comma(int, int) -> int | 0 | 6 | VariableAddress[y] | ir.cpp:380:22:380:22 | +| Comma(int, int) -> int | 0 | 7 | Store | ir.cpp:380:22:380:22 | +| Comma(int, int) -> int | 0 | 8 | VariableAddress[#return] | ir.cpp:381:5:381:37 | +| Comma(int, int) -> int | 0 | 9 | FunctionAddress[VoidFunc] | ir.cpp:381:12:381:19 | +| Comma(int, int) -> int | 0 | 10 | Invoke | ir.cpp:381:12:381:19 | +| Comma(int, int) -> int | 0 | 11 | FunctionAddress[CallAdd] | ir.cpp:381:24:381:30 | +| Comma(int, int) -> int | 0 | 12 | VariableAddress[x] | ir.cpp:381:32:381:32 | +| Comma(int, int) -> int | 0 | 13 | Load | ir.cpp:381:32:381:32 | +| Comma(int, int) -> int | 0 | 14 | VariableAddress[y] | ir.cpp:381:35:381:35 | +| Comma(int, int) -> int | 0 | 15 | Load | ir.cpp:381:35:381:35 | +| Comma(int, int) -> int | 0 | 16 | Invoke | ir.cpp:381:24:381:30 | +| Comma(int, int) -> int | 0 | 17 | Store | ir.cpp:381:12:381:36 | +| Comma(int, int) -> int | 0 | 18 | VariableAddress[#return] | ir.cpp:380:5:380:9 | +| Comma(int, int) -> int | 0 | 19 | ReturnValue | ir.cpp:380:5:380:9 | +| Comma(int, int) -> int | 0 | 20 | UnmodeledUse | ir.cpp:380:5:380:9 | +| Comma(int, int) -> int | 0 | 21 | ExitFunction | ir.cpp:380:5:380:9 | +| CompoundAssignment() -> void | 0 | 0 | EnterFunction | ir.cpp:213:6:213:23 | +| CompoundAssignment() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:213:6:213:23 | +| CompoundAssignment() -> void | 0 | 2 | VariableAddress[x] | ir.cpp:215:9:215:9 | +| CompoundAssignment() -> void | 0 | 3 | Constant[5] | ir.cpp:215:12:215:13 | +| CompoundAssignment() -> void | 0 | 4 | Store | ir.cpp:215:12:215:13 | +| CompoundAssignment() -> void | 0 | 5 | Constant[7] | ir.cpp:216:10:216:10 | +| CompoundAssignment() -> void | 0 | 6 | VariableAddress[x] | ir.cpp:216:5:216:5 | +| CompoundAssignment() -> void | 0 | 7 | Load | ir.cpp:216:5:216:10 | +| CompoundAssignment() -> void | 0 | 8 | Add | ir.cpp:216:5:216:10 | +| CompoundAssignment() -> void | 0 | 9 | Store | ir.cpp:216:5:216:10 | +| CompoundAssignment() -> void | 0 | 10 | VariableAddress[y] | ir.cpp:219:11:219:11 | +| CompoundAssignment() -> void | 0 | 11 | Constant[5] | ir.cpp:219:15:219:15 | +| CompoundAssignment() -> void | 0 | 12 | Store | ir.cpp:219:15:219:15 | +| CompoundAssignment() -> void | 0 | 13 | VariableAddress[x] | ir.cpp:220:10:220:10 | +| CompoundAssignment() -> void | 0 | 14 | Load | ir.cpp:220:10:220:10 | +| CompoundAssignment() -> void | 0 | 15 | VariableAddress[y] | ir.cpp:220:5:220:5 | +| CompoundAssignment() -> void | 0 | 16 | Load | ir.cpp:220:5:220:10 | +| CompoundAssignment() -> void | 0 | 17 | Convert | ir.cpp:220:5:220:10 | +| CompoundAssignment() -> void | 0 | 18 | Add | ir.cpp:220:5:220:10 | +| CompoundAssignment() -> void | 0 | 19 | Convert | ir.cpp:220:5:220:10 | +| CompoundAssignment() -> void | 0 | 20 | Store | ir.cpp:220:5:220:10 | +| CompoundAssignment() -> void | 0 | 21 | Constant[1] | ir.cpp:223:11:223:11 | +| CompoundAssignment() -> void | 0 | 22 | VariableAddress[y] | ir.cpp:223:5:223:5 | +| CompoundAssignment() -> void | 0 | 23 | Load | ir.cpp:223:5:223:11 | +| CompoundAssignment() -> void | 0 | 24 | ShiftLeft | ir.cpp:223:5:223:11 | +| CompoundAssignment() -> void | 0 | 25 | Store | ir.cpp:223:5:223:11 | +| CompoundAssignment() -> void | 0 | 26 | VariableAddress[z] | ir.cpp:226:10:226:10 | +| CompoundAssignment() -> void | 0 | 27 | Constant[7] | ir.cpp:226:14:226:14 | +| CompoundAssignment() -> void | 0 | 28 | Store | ir.cpp:226:14:226:14 | +| CompoundAssignment() -> void | 0 | 29 | Constant[2.0] | ir.cpp:227:10:227:13 | +| CompoundAssignment() -> void | 0 | 30 | VariableAddress[z] | ir.cpp:227:5:227:5 | +| CompoundAssignment() -> void | 0 | 31 | Load | ir.cpp:227:5:227:13 | +| CompoundAssignment() -> void | 0 | 32 | Convert | ir.cpp:227:5:227:13 | +| CompoundAssignment() -> void | 0 | 33 | Add | ir.cpp:227:5:227:13 | +| CompoundAssignment() -> void | 0 | 34 | Convert | ir.cpp:227:5:227:13 | +| CompoundAssignment() -> void | 0 | 35 | Store | ir.cpp:227:5:227:13 | +| CompoundAssignment() -> void | 0 | 36 | NoOp | ir.cpp:228:1:228:1 | +| CompoundAssignment() -> void | 0 | 37 | ReturnVoid | ir.cpp:213:6:213:23 | +| CompoundAssignment() -> void | 0 | 38 | UnmodeledUse | ir.cpp:213:6:213:23 | +| CompoundAssignment() -> void | 0 | 39 | ExitFunction | ir.cpp:213:6:213:23 | +| ConditionValues(bool, bool) -> void | 0 | 0 | EnterFunction | ir.cpp:475:6:475:20 | +| ConditionValues(bool, bool) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:475:6:475:20 | +| ConditionValues(bool, bool) -> void | 0 | 2 | InitializeParameter[a] | ir.cpp:475:27:475:27 | +| ConditionValues(bool, bool) -> void | 0 | 3 | VariableAddress[a] | ir.cpp:475:27:475:27 | +| ConditionValues(bool, bool) -> void | 0 | 4 | Store | ir.cpp:475:27:475:27 | +| ConditionValues(bool, bool) -> void | 0 | 5 | InitializeParameter[b] | ir.cpp:475:35:475:35 | +| ConditionValues(bool, bool) -> void | 0 | 6 | VariableAddress[b] | ir.cpp:475:35:475:35 | +| ConditionValues(bool, bool) -> void | 0 | 7 | Store | ir.cpp:475:35:475:35 | +| ConditionValues(bool, bool) -> void | 0 | 8 | VariableAddress[x] | ir.cpp:476:10:476:10 | +| ConditionValues(bool, bool) -> void | 0 | 9 | Uninitialized | ir.cpp:476:10:476:10 | +| ConditionValues(bool, bool) -> void | 0 | 10 | Store | ir.cpp:476:10:476:10 | +| ConditionValues(bool, bool) -> void | 0 | 11 | VariableAddress[a] | ir.cpp:477:9:477:9 | +| ConditionValues(bool, bool) -> void | 0 | 12 | Load | ir.cpp:477:9:477:9 | +| ConditionValues(bool, bool) -> void | 0 | 13 | ConditionalBranch | ir.cpp:477:9:477:9 | +| ConditionValues(bool, bool) -> void | 1 | 0 | VariableAddress[b] | ir.cpp:477:14:477:14 | +| ConditionValues(bool, bool) -> void | 1 | 1 | Load | ir.cpp:477:14:477:14 | +| ConditionValues(bool, bool) -> void | 1 | 2 | ConditionalBranch | ir.cpp:477:14:477:14 | +| ConditionValues(bool, bool) -> void | 2 | 0 | VariableAddress[#temp478:9] | ir.cpp:478:9:478:14 | +| ConditionValues(bool, bool) -> void | 2 | 1 | Constant[0] | ir.cpp:478:9:478:14 | +| ConditionValues(bool, bool) -> void | 2 | 2 | Store | ir.cpp:478:9:478:14 | +| ConditionValues(bool, bool) -> void | 3 | 0 | Phi | ir.cpp:478:9:478:14 | +| ConditionValues(bool, bool) -> void | 3 | 1 | VariableAddress[#temp478:9] | ir.cpp:478:9:478:14 | +| ConditionValues(bool, bool) -> void | 3 | 2 | Load | ir.cpp:478:9:478:14 | +| ConditionValues(bool, bool) -> void | 3 | 3 | VariableAddress[x] | ir.cpp:478:5:478:5 | +| ConditionValues(bool, bool) -> void | 3 | 4 | Store | ir.cpp:478:5:478:14 | +| ConditionValues(bool, bool) -> void | 3 | 5 | VariableAddress[a] | ir.cpp:479:11:479:11 | +| ConditionValues(bool, bool) -> void | 3 | 6 | Load | ir.cpp:479:11:479:11 | +| ConditionValues(bool, bool) -> void | 3 | 7 | ConditionalBranch | ir.cpp:479:11:479:11 | +| ConditionValues(bool, bool) -> void | 4 | 0 | VariableAddress[#temp478:9] | ir.cpp:478:9:478:14 | +| ConditionValues(bool, bool) -> void | 4 | 1 | Constant[1] | ir.cpp:478:9:478:14 | +| ConditionValues(bool, bool) -> void | 4 | 2 | Store | ir.cpp:478:9:478:14 | +| ConditionValues(bool, bool) -> void | 5 | 0 | VariableAddress[b] | ir.cpp:478:14:478:14 | +| ConditionValues(bool, bool) -> void | 5 | 1 | Load | ir.cpp:478:14:478:14 | +| ConditionValues(bool, bool) -> void | 5 | 2 | ConditionalBranch | ir.cpp:478:14:478:14 | +| ConditionValues(bool, bool) -> void | 6 | 0 | VariableAddress[#temp479:11] | ir.cpp:479:11:479:16 | +| ConditionValues(bool, bool) -> void | 6 | 1 | Constant[0] | ir.cpp:479:11:479:16 | +| ConditionValues(bool, bool) -> void | 6 | 2 | Store | ir.cpp:479:11:479:16 | +| ConditionValues(bool, bool) -> void | 7 | 0 | Phi | ir.cpp:479:11:479:16 | +| ConditionValues(bool, bool) -> void | 7 | 1 | VariableAddress[#temp479:11] | ir.cpp:479:11:479:16 | +| ConditionValues(bool, bool) -> void | 7 | 2 | Load | ir.cpp:479:11:479:16 | +| ConditionValues(bool, bool) -> void | 7 | 3 | LogicalNot | ir.cpp:479:9:479:17 | +| ConditionValues(bool, bool) -> void | 7 | 4 | VariableAddress[x] | ir.cpp:479:5:479:5 | +| ConditionValues(bool, bool) -> void | 7 | 5 | Store | ir.cpp:479:5:479:17 | +| ConditionValues(bool, bool) -> void | 7 | 6 | NoOp | ir.cpp:480:1:480:1 | +| ConditionValues(bool, bool) -> void | 7 | 7 | ReturnVoid | ir.cpp:475:6:475:20 | +| ConditionValues(bool, bool) -> void | 7 | 8 | UnmodeledUse | ir.cpp:475:6:475:20 | +| ConditionValues(bool, bool) -> void | 7 | 9 | ExitFunction | ir.cpp:475:6:475:20 | +| ConditionValues(bool, bool) -> void | 8 | 0 | VariableAddress[#temp479:11] | ir.cpp:479:11:479:16 | +| ConditionValues(bool, bool) -> void | 8 | 1 | Constant[1] | ir.cpp:479:11:479:16 | +| ConditionValues(bool, bool) -> void | 8 | 2 | Store | ir.cpp:479:11:479:16 | +| ConditionValues(bool, bool) -> void | 9 | 0 | VariableAddress[b] | ir.cpp:479:16:479:16 | +| ConditionValues(bool, bool) -> void | 9 | 1 | Load | ir.cpp:479:16:479:16 | +| ConditionValues(bool, bool) -> void | 9 | 2 | ConditionalBranch | ir.cpp:479:16:479:16 | +| ConditionValues(bool, bool) -> void | 10 | 0 | VariableAddress[#temp477:9] | ir.cpp:477:9:477:14 | +| ConditionValues(bool, bool) -> void | 10 | 1 | Constant[0] | ir.cpp:477:9:477:14 | +| ConditionValues(bool, bool) -> void | 10 | 2 | Store | ir.cpp:477:9:477:14 | +| ConditionValues(bool, bool) -> void | 11 | 0 | Phi | ir.cpp:477:9:477:14 | +| ConditionValues(bool, bool) -> void | 11 | 1 | VariableAddress[#temp477:9] | ir.cpp:477:9:477:14 | +| ConditionValues(bool, bool) -> void | 11 | 2 | Load | ir.cpp:477:9:477:14 | +| ConditionValues(bool, bool) -> void | 11 | 3 | VariableAddress[x] | ir.cpp:477:5:477:5 | +| ConditionValues(bool, bool) -> void | 11 | 4 | Store | ir.cpp:477:5:477:14 | +| ConditionValues(bool, bool) -> void | 11 | 5 | VariableAddress[a] | ir.cpp:478:9:478:9 | +| ConditionValues(bool, bool) -> void | 11 | 6 | Load | ir.cpp:478:9:478:9 | +| ConditionValues(bool, bool) -> void | 11 | 7 | ConditionalBranch | ir.cpp:478:9:478:9 | +| ConditionValues(bool, bool) -> void | 12 | 0 | VariableAddress[#temp477:9] | ir.cpp:477:9:477:14 | +| ConditionValues(bool, bool) -> void | 12 | 1 | Constant[1] | ir.cpp:477:9:477:14 | +| ConditionValues(bool, bool) -> void | 12 | 2 | Store | ir.cpp:477:9:477:14 | +| Conditional(bool, int, int) -> void | 0 | 0 | EnterFunction | ir.cpp:482:6:482:16 | +| Conditional(bool, int, int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:482:6:482:16 | +| Conditional(bool, int, int) -> void | 0 | 2 | InitializeParameter[a] | ir.cpp:482:23:482:23 | +| Conditional(bool, int, int) -> void | 0 | 3 | VariableAddress[a] | ir.cpp:482:23:482:23 | +| Conditional(bool, int, int) -> void | 0 | 4 | Store | ir.cpp:482:23:482:23 | +| Conditional(bool, int, int) -> void | 0 | 5 | InitializeParameter[x] | ir.cpp:482:30:482:30 | +| Conditional(bool, int, int) -> void | 0 | 6 | VariableAddress[x] | ir.cpp:482:30:482:30 | +| Conditional(bool, int, int) -> void | 0 | 7 | Store | ir.cpp:482:30:482:30 | +| Conditional(bool, int, int) -> void | 0 | 8 | InitializeParameter[y] | ir.cpp:482:37:482:37 | +| Conditional(bool, int, int) -> void | 0 | 9 | VariableAddress[y] | ir.cpp:482:37:482:37 | +| Conditional(bool, int, int) -> void | 0 | 10 | Store | ir.cpp:482:37:482:37 | +| Conditional(bool, int, int) -> void | 0 | 11 | VariableAddress[z] | ir.cpp:483:9:483:9 | +| Conditional(bool, int, int) -> void | 0 | 12 | VariableAddress[a] | ir.cpp:483:13:483:13 | +| Conditional(bool, int, int) -> void | 0 | 13 | Load | ir.cpp:483:13:483:13 | +| Conditional(bool, int, int) -> void | 0 | 14 | ConditionalBranch | ir.cpp:483:13:483:13 | +| Conditional(bool, int, int) -> void | 1 | 0 | VariableAddress[x] | ir.cpp:483:17:483:17 | +| Conditional(bool, int, int) -> void | 1 | 1 | Load | ir.cpp:483:17:483:17 | +| Conditional(bool, int, int) -> void | 1 | 2 | VariableAddress[#temp483:13] | ir.cpp:483:13:483:21 | +| Conditional(bool, int, int) -> void | 1 | 3 | Store | ir.cpp:483:13:483:21 | +| Conditional(bool, int, int) -> void | 2 | 0 | VariableAddress[y] | ir.cpp:483:21:483:21 | +| Conditional(bool, int, int) -> void | 2 | 1 | Load | ir.cpp:483:21:483:21 | +| Conditional(bool, int, int) -> void | 2 | 2 | VariableAddress[#temp483:13] | ir.cpp:483:13:483:21 | +| Conditional(bool, int, int) -> void | 2 | 3 | Store | ir.cpp:483:13:483:21 | +| Conditional(bool, int, int) -> void | 3 | 0 | Phi | ir.cpp:483:13:483:21 | +| Conditional(bool, int, int) -> void | 3 | 1 | VariableAddress[#temp483:13] | ir.cpp:483:13:483:21 | +| Conditional(bool, int, int) -> void | 3 | 2 | Load | ir.cpp:483:13:483:21 | +| Conditional(bool, int, int) -> void | 3 | 3 | Store | ir.cpp:483:13:483:21 | +| Conditional(bool, int, int) -> void | 3 | 4 | NoOp | ir.cpp:484:1:484:1 | +| Conditional(bool, int, int) -> void | 3 | 5 | ReturnVoid | ir.cpp:482:6:482:16 | +| Conditional(bool, int, int) -> void | 3 | 6 | UnmodeledUse | ir.cpp:482:6:482:16 | +| Conditional(bool, int, int) -> void | 3 | 7 | ExitFunction | ir.cpp:482:6:482:16 | +| Conditional_LValue(bool) -> void | 0 | 0 | EnterFunction | ir.cpp:486:6:486:23 | +| Conditional_LValue(bool) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:486:6:486:23 | +| Conditional_LValue(bool) -> void | 0 | 2 | InitializeParameter[a] | ir.cpp:486:30:486:30 | +| Conditional_LValue(bool) -> void | 0 | 3 | VariableAddress[a] | ir.cpp:486:30:486:30 | +| Conditional_LValue(bool) -> void | 0 | 4 | Store | ir.cpp:486:30:486:30 | +| Conditional_LValue(bool) -> void | 0 | 5 | VariableAddress[x] | ir.cpp:487:9:487:9 | +| Conditional_LValue(bool) -> void | 0 | 6 | Uninitialized | ir.cpp:487:9:487:9 | +| Conditional_LValue(bool) -> void | 0 | 7 | Store | ir.cpp:487:9:487:9 | +| Conditional_LValue(bool) -> void | 0 | 8 | VariableAddress[y] | ir.cpp:488:9:488:9 | +| Conditional_LValue(bool) -> void | 0 | 9 | Uninitialized | ir.cpp:488:9:488:9 | +| Conditional_LValue(bool) -> void | 0 | 10 | Store | ir.cpp:488:9:488:9 | +| Conditional_LValue(bool) -> void | 0 | 11 | Constant[5] | ir.cpp:489:19:489:19 | +| Conditional_LValue(bool) -> void | 0 | 12 | VariableAddress[a] | ir.cpp:489:6:489:6 | +| Conditional_LValue(bool) -> void | 0 | 13 | Load | ir.cpp:489:6:489:6 | +| Conditional_LValue(bool) -> void | 0 | 14 | ConditionalBranch | ir.cpp:489:6:489:6 | +| Conditional_LValue(bool) -> void | 1 | 0 | Phi | ir.cpp:489:6:489:14 | +| Conditional_LValue(bool) -> void | 1 | 1 | VariableAddress[#temp489:6] | ir.cpp:489:6:489:14 | +| Conditional_LValue(bool) -> void | 1 | 2 | Load | ir.cpp:489:6:489:14 | +| Conditional_LValue(bool) -> void | 1 | 3 | Store | ir.cpp:489:5:489:19 | +| Conditional_LValue(bool) -> void | 1 | 4 | NoOp | ir.cpp:490:1:490:1 | +| Conditional_LValue(bool) -> void | 1 | 5 | ReturnVoid | ir.cpp:486:6:486:23 | +| Conditional_LValue(bool) -> void | 1 | 6 | UnmodeledUse | ir.cpp:486:6:486:23 | +| Conditional_LValue(bool) -> void | 1 | 7 | ExitFunction | ir.cpp:486:6:486:23 | +| Conditional_LValue(bool) -> void | 2 | 0 | VariableAddress[x] | ir.cpp:489:10:489:10 | +| Conditional_LValue(bool) -> void | 2 | 1 | VariableAddress[#temp489:6] | ir.cpp:489:6:489:14 | +| Conditional_LValue(bool) -> void | 2 | 2 | Store | ir.cpp:489:6:489:14 | +| Conditional_LValue(bool) -> void | 3 | 0 | VariableAddress[y] | ir.cpp:489:14:489:14 | +| Conditional_LValue(bool) -> void | 3 | 1 | VariableAddress[#temp489:6] | ir.cpp:489:6:489:14 | +| Conditional_LValue(bool) -> void | 3 | 2 | Store | ir.cpp:489:6:489:14 | +| Conditional_Void(bool) -> void | 0 | 0 | EnterFunction | ir.cpp:492:6:492:21 | +| Conditional_Void(bool) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:492:6:492:21 | +| Conditional_Void(bool) -> void | 0 | 2 | InitializeParameter[a] | ir.cpp:492:28:492:28 | +| Conditional_Void(bool) -> void | 0 | 3 | VariableAddress[a] | ir.cpp:492:28:492:28 | +| Conditional_Void(bool) -> void | 0 | 4 | Store | ir.cpp:492:28:492:28 | +| Conditional_Void(bool) -> void | 0 | 5 | VariableAddress[a] | ir.cpp:493:5:493:5 | +| Conditional_Void(bool) -> void | 0 | 6 | Load | ir.cpp:493:5:493:5 | +| Conditional_Void(bool) -> void | 0 | 7 | ConditionalBranch | ir.cpp:493:5:493:5 | +| Conditional_Void(bool) -> void | 1 | 0 | NoOp | ir.cpp:494:1:494:1 | +| Conditional_Void(bool) -> void | 1 | 1 | ReturnVoid | ir.cpp:492:6:492:21 | +| Conditional_Void(bool) -> void | 1 | 2 | UnmodeledUse | ir.cpp:492:6:492:21 | +| Conditional_Void(bool) -> void | 1 | 3 | ExitFunction | ir.cpp:492:6:492:21 | +| Conditional_Void(bool) -> void | 2 | 0 | FunctionAddress[VoidFunc] | ir.cpp:493:9:493:16 | +| Conditional_Void(bool) -> void | 2 | 1 | Invoke | ir.cpp:493:9:493:16 | +| Conditional_Void(bool) -> void | 3 | 0 | FunctionAddress[VoidFunc] | ir.cpp:493:22:493:29 | +| Conditional_Void(bool) -> void | 3 | 1 | Invoke | ir.cpp:493:22:493:29 | +| Constants() -> void | 0 | 0 | EnterFunction | ir.cpp:1:6:1:14 | +| Constants() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:1:6:1:14 | +| Constants() -> void | 0 | 2 | VariableAddress[c_i] | ir.cpp:2:10:2:12 | +| Constants() -> void | 0 | 3 | Constant[1] | ir.cpp:2:16:2:16 | +| Constants() -> void | 0 | 4 | Store | ir.cpp:2:16:2:16 | +| Constants() -> void | 0 | 5 | VariableAddress[c_c] | ir.cpp:3:10:3:12 | +| Constants() -> void | 0 | 6 | Constant[65] | ir.cpp:3:15:3:18 | +| Constants() -> void | 0 | 7 | Store | ir.cpp:3:15:3:18 | +| Constants() -> void | 0 | 8 | VariableAddress[sc_i] | ir.cpp:5:17:5:20 | +| Constants() -> void | 0 | 9 | Constant[-1] | ir.cpp:5:24:5:25 | +| Constants() -> void | 0 | 10 | Store | ir.cpp:5:24:5:25 | +| Constants() -> void | 0 | 11 | VariableAddress[sc_c] | ir.cpp:6:17:6:20 | +| Constants() -> void | 0 | 12 | Constant[65] | ir.cpp:6:24:6:26 | +| Constants() -> void | 0 | 13 | Store | ir.cpp:6:24:6:26 | +| Constants() -> void | 0 | 14 | VariableAddress[uc_i] | ir.cpp:8:19:8:22 | +| Constants() -> void | 0 | 15 | Constant[5] | ir.cpp:8:26:8:26 | +| Constants() -> void | 0 | 16 | Store | ir.cpp:8:26:8:26 | +| Constants() -> void | 0 | 17 | VariableAddress[uc_c] | ir.cpp:9:19:9:22 | +| Constants() -> void | 0 | 18 | Constant[65] | ir.cpp:9:26:9:28 | +| Constants() -> void | 0 | 19 | Store | ir.cpp:9:26:9:28 | +| Constants() -> void | 0 | 20 | VariableAddress[s] | ir.cpp:11:11:11:11 | +| Constants() -> void | 0 | 21 | Constant[5] | ir.cpp:11:15:11:15 | +| Constants() -> void | 0 | 22 | Store | ir.cpp:11:15:11:15 | +| Constants() -> void | 0 | 23 | VariableAddress[us] | ir.cpp:12:20:12:21 | +| Constants() -> void | 0 | 24 | Constant[5] | ir.cpp:12:25:12:25 | +| Constants() -> void | 0 | 25 | Store | ir.cpp:12:25:12:25 | +| Constants() -> void | 0 | 26 | VariableAddress[i] | ir.cpp:14:9:14:9 | +| Constants() -> void | 0 | 27 | Constant[5] | ir.cpp:14:12:14:13 | +| Constants() -> void | 0 | 28 | Store | ir.cpp:14:12:14:13 | +| Constants() -> void | 0 | 29 | VariableAddress[ui] | ir.cpp:15:18:15:19 | +| Constants() -> void | 0 | 30 | Constant[5] | ir.cpp:15:23:15:23 | +| Constants() -> void | 0 | 31 | Store | ir.cpp:15:23:15:23 | +| Constants() -> void | 0 | 32 | VariableAddress[l] | ir.cpp:17:10:17:10 | +| Constants() -> void | 0 | 33 | Constant[5] | ir.cpp:17:14:17:14 | +| Constants() -> void | 0 | 34 | Store | ir.cpp:17:14:17:14 | +| Constants() -> void | 0 | 35 | VariableAddress[ul] | ir.cpp:18:19:18:20 | +| Constants() -> void | 0 | 36 | Constant[5] | ir.cpp:18:24:18:24 | +| Constants() -> void | 0 | 37 | Store | ir.cpp:18:24:18:24 | +| Constants() -> void | 0 | 38 | VariableAddress[ll_i] | ir.cpp:20:15:20:18 | +| Constants() -> void | 0 | 39 | Constant[5] | ir.cpp:20:22:20:22 | +| Constants() -> void | 0 | 40 | Store | ir.cpp:20:22:20:22 | +| Constants() -> void | 0 | 41 | VariableAddress[ll_ll] | ir.cpp:21:15:21:19 | +| Constants() -> void | 0 | 42 | Constant[5] | ir.cpp:21:22:21:25 | +| Constants() -> void | 0 | 43 | Store | ir.cpp:21:22:21:25 | +| Constants() -> void | 0 | 44 | VariableAddress[ull_i] | ir.cpp:22:24:22:28 | +| Constants() -> void | 0 | 45 | Constant[5] | ir.cpp:22:32:22:32 | +| Constants() -> void | 0 | 46 | Store | ir.cpp:22:32:22:32 | +| Constants() -> void | 0 | 47 | VariableAddress[ull_ull] | ir.cpp:23:24:23:30 | +| Constants() -> void | 0 | 48 | Constant[5] | ir.cpp:23:33:23:37 | +| Constants() -> void | 0 | 49 | Store | ir.cpp:23:33:23:37 | +| Constants() -> void | 0 | 50 | VariableAddress[b_t] | ir.cpp:25:10:25:12 | +| Constants() -> void | 0 | 51 | Constant[1] | ir.cpp:25:15:25:19 | +| Constants() -> void | 0 | 52 | Store | ir.cpp:25:15:25:19 | +| Constants() -> void | 0 | 53 | VariableAddress[b_f] | ir.cpp:26:10:26:12 | +| Constants() -> void | 0 | 54 | Constant[0] | ir.cpp:26:15:26:20 | +| Constants() -> void | 0 | 55 | Store | ir.cpp:26:15:26:20 | +| Constants() -> void | 0 | 56 | VariableAddress[wc_i] | ir.cpp:28:13:28:16 | +| Constants() -> void | 0 | 57 | Constant[5] | ir.cpp:28:20:28:20 | +| Constants() -> void | 0 | 58 | Store | ir.cpp:28:20:28:20 | +| Constants() -> void | 0 | 59 | VariableAddress[wc_c] | ir.cpp:29:13:29:16 | +| Constants() -> void | 0 | 60 | Constant[65] | ir.cpp:29:19:29:23 | +| Constants() -> void | 0 | 61 | Store | ir.cpp:29:19:29:23 | +| Constants() -> void | 0 | 62 | VariableAddress[c16] | ir.cpp:31:14:31:16 | +| Constants() -> void | 0 | 63 | Constant[65] | ir.cpp:31:19:31:23 | +| Constants() -> void | 0 | 64 | Store | ir.cpp:31:19:31:23 | +| Constants() -> void | 0 | 65 | VariableAddress[c32] | ir.cpp:32:14:32:16 | +| Constants() -> void | 0 | 66 | Constant[65] | ir.cpp:32:19:32:23 | +| Constants() -> void | 0 | 67 | Store | ir.cpp:32:19:32:23 | +| Constants() -> void | 0 | 68 | VariableAddress[f_i] | ir.cpp:34:11:34:13 | +| Constants() -> void | 0 | 69 | Constant[1.0] | ir.cpp:34:17:34:17 | +| Constants() -> void | 0 | 70 | Store | ir.cpp:34:17:34:17 | +| Constants() -> void | 0 | 71 | VariableAddress[f_f] | ir.cpp:35:11:35:13 | +| Constants() -> void | 0 | 72 | Constant[1.0] | ir.cpp:35:16:35:20 | +| Constants() -> void | 0 | 73 | Store | ir.cpp:35:16:35:20 | +| Constants() -> void | 0 | 74 | VariableAddress[f_d] | ir.cpp:36:11:36:13 | +| Constants() -> void | 0 | 75 | Constant[1.0] | ir.cpp:36:17:36:19 | +| Constants() -> void | 0 | 76 | Store | ir.cpp:36:17:36:19 | +| Constants() -> void | 0 | 77 | VariableAddress[d_i] | ir.cpp:38:12:38:14 | +| Constants() -> void | 0 | 78 | Constant[1.0] | ir.cpp:38:18:38:18 | +| Constants() -> void | 0 | 79 | Store | ir.cpp:38:18:38:18 | +| Constants() -> void | 0 | 80 | VariableAddress[d_f] | ir.cpp:39:12:39:14 | +| Constants() -> void | 0 | 81 | Constant[1.0] | ir.cpp:39:18:39:21 | +| Constants() -> void | 0 | 82 | Store | ir.cpp:39:18:39:21 | +| Constants() -> void | 0 | 83 | VariableAddress[d_d] | ir.cpp:40:12:40:14 | +| Constants() -> void | 0 | 84 | Constant[1.0] | ir.cpp:40:17:40:20 | +| Constants() -> void | 0 | 85 | Store | ir.cpp:40:17:40:20 | +| Constants() -> void | 0 | 86 | NoOp | ir.cpp:41:1:41:1 | +| Constants() -> void | 0 | 87 | ReturnVoid | ir.cpp:1:6:1:14 | +| Constants() -> void | 0 | 88 | UnmodeledUse | ir.cpp:1:6:1:14 | +| Constants() -> void | 0 | 89 | ExitFunction | ir.cpp:1:6:1:14 | +| Continue(int) -> void | 0 | 0 | EnterFunction | ir.cpp:360:6:360:13 | +| Continue(int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:360:6:360:13 | +| Continue(int) -> void | 0 | 2 | InitializeParameter[n] | ir.cpp:360:19:360:19 | +| Continue(int) -> void | 0 | 3 | VariableAddress[n] | ir.cpp:360:19:360:19 | +| Continue(int) -> void | 0 | 4 | Store | ir.cpp:360:19:360:19 | +| Continue(int) -> void | 1 | 0 | Phi | ir.cpp:362:13:362:13 | +| Continue(int) -> void | 1 | 1 | VariableAddress[n] | ir.cpp:362:13:362:13 | +| Continue(int) -> void | 1 | 2 | Load | ir.cpp:362:13:362:13 | +| Continue(int) -> void | 1 | 3 | Constant[1] | ir.cpp:362:18:362:18 | +| Continue(int) -> void | 1 | 4 | CompareEQ | ir.cpp:362:13:362:18 | +| Continue(int) -> void | 1 | 5 | ConditionalBranch | ir.cpp:362:13:362:18 | +| Continue(int) -> void | 2 | 0 | NoOp | ir.cpp:363:13:363:21 | +| Continue(int) -> void | 3 | 0 | Constant[1] | ir.cpp:365:14:365:14 | +| Continue(int) -> void | 3 | 1 | VariableAddress[n] | ir.cpp:365:9:365:9 | +| Continue(int) -> void | 3 | 2 | Load | ir.cpp:365:9:365:14 | +| Continue(int) -> void | 3 | 3 | Sub | ir.cpp:365:9:365:14 | +| Continue(int) -> void | 3 | 4 | Store | ir.cpp:365:9:365:14 | +| Continue(int) -> void | 4 | 0 | Phi | ir.cpp:361:5:361:5 | +| Continue(int) -> void | 4 | 1 | NoOp | ir.cpp:361:5:361:5 | +| Continue(int) -> void | 4 | 2 | VariableAddress[n] | ir.cpp:366:14:366:14 | +| Continue(int) -> void | 4 | 3 | Load | ir.cpp:366:14:366:14 | +| Continue(int) -> void | 4 | 4 | Constant[0] | ir.cpp:366:18:366:18 | +| Continue(int) -> void | 4 | 5 | CompareGT | ir.cpp:366:14:366:18 | +| Continue(int) -> void | 4 | 6 | ConditionalBranch | ir.cpp:366:14:366:18 | +| Continue(int) -> void | 5 | 0 | NoOp | ir.cpp:367:1:367:1 | +| Continue(int) -> void | 5 | 1 | ReturnVoid | ir.cpp:360:6:360:13 | +| Continue(int) -> void | 5 | 2 | UnmodeledUse | ir.cpp:360:6:360:13 | +| Continue(int) -> void | 5 | 3 | ExitFunction | ir.cpp:360:6:360:13 | +| DeclareObject() -> void | 0 | 0 | EnterFunction | ir.cpp:615:6:615:18 | +| DeclareObject() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:615:6:615:18 | +| DeclareObject() -> void | 0 | 2 | VariableAddress[s1] | ir.cpp:616:12:616:13 | +| DeclareObject() -> void | 0 | 3 | FunctionAddress[String] | ir.cpp:616:12:616:13 | +| DeclareObject() -> void | 0 | 4 | Invoke | ir.cpp:616:12:616:13 | +| DeclareObject() -> void | 0 | 5 | VariableAddress[s2] | ir.cpp:617:12:617:13 | +| DeclareObject() -> void | 0 | 6 | FunctionAddress[String] | ir.cpp:617:15:617:22 | +| DeclareObject() -> void | 0 | 7 | StringConstant["hello"] | ir.cpp:617:15:617:21 | +| DeclareObject() -> void | 0 | 8 | Convert | ir.cpp:617:15:617:21 | +| DeclareObject() -> void | 0 | 9 | Invoke | ir.cpp:617:15:617:22 | +| DeclareObject() -> void | 0 | 10 | VariableAddress[s3] | ir.cpp:618:12:618:13 | +| DeclareObject() -> void | 0 | 11 | FunctionAddress[ReturnObject] | ir.cpp:618:17:618:28 | +| DeclareObject() -> void | 0 | 12 | Invoke | ir.cpp:618:17:618:28 | +| DeclareObject() -> void | 0 | 13 | Store | ir.cpp:618:17:618:28 | +| DeclareObject() -> void | 0 | 14 | VariableAddress[s4] | ir.cpp:619:12:619:13 | +| DeclareObject() -> void | 0 | 15 | FunctionAddress[String] | ir.cpp:619:16:619:30 | +| DeclareObject() -> void | 0 | 16 | StringConstant["test"] | ir.cpp:619:24:619:29 | +| DeclareObject() -> void | 0 | 17 | Convert | ir.cpp:619:24:619:29 | +| DeclareObject() -> void | 0 | 18 | Invoke | ir.cpp:619:16:619:30 | +| DeclareObject() -> void | 0 | 19 | NoOp | ir.cpp:620:1:620:1 | +| DeclareObject() -> void | 0 | 20 | ReturnVoid | ir.cpp:615:6:615:18 | +| DeclareObject() -> void | 0 | 21 | UnmodeledUse | ir.cpp:615:6:615:18 | +| DeclareObject() -> void | 0 | 22 | ExitFunction | ir.cpp:615:6:615:18 | +| DerefReference(int &) -> int | 0 | 0 | EnterFunction | ir.cpp:675:5:675:18 | +| DerefReference(int &) -> int | 0 | 1 | UnmodeledDefinition | ir.cpp:675:5:675:18 | +| DerefReference(int &) -> int | 0 | 2 | InitializeParameter[r] | ir.cpp:675:25:675:25 | +| DerefReference(int &) -> int | 0 | 3 | VariableAddress[r] | ir.cpp:675:25:675:25 | +| DerefReference(int &) -> int | 0 | 4 | Store | ir.cpp:675:25:675:25 | +| DerefReference(int &) -> int | 0 | 5 | VariableAddress[#return] | ir.cpp:676:5:676:13 | +| DerefReference(int &) -> int | 0 | 6 | VariableAddress[r] | ir.cpp:676:12:676:12 | +| DerefReference(int &) -> int | 0 | 7 | Load | ir.cpp:676:12:676:12 | +| DerefReference(int &) -> int | 0 | 8 | Load | ir.cpp:676:12:676:12 | +| DerefReference(int &) -> int | 0 | 9 | Store | ir.cpp:676:12:676:12 | +| DerefReference(int &) -> int | 0 | 10 | VariableAddress[#return] | ir.cpp:675:5:675:18 | +| DerefReference(int &) -> int | 0 | 11 | ReturnValue | ir.cpp:675:5:675:18 | +| DerefReference(int &) -> int | 0 | 12 | UnmodeledUse | ir.cpp:675:5:675:18 | +| DerefReference(int &) -> int | 0 | 13 | ExitFunction | ir.cpp:675:5:675:18 | +| Dereference(int *) -> int | 0 | 0 | EnterFunction | ir.cpp:341:5:341:15 | +| Dereference(int *) -> int | 0 | 1 | UnmodeledDefinition | ir.cpp:341:5:341:15 | +| Dereference(int *) -> int | 0 | 2 | InitializeParameter[p] | ir.cpp:341:22:341:22 | +| Dereference(int *) -> int | 0 | 3 | VariableAddress[p] | ir.cpp:341:22:341:22 | +| Dereference(int *) -> int | 0 | 4 | Store | ir.cpp:341:22:341:22 | +| Dereference(int *) -> int | 0 | 5 | Constant[1] | ir.cpp:342:10:342:10 | +| Dereference(int *) -> int | 0 | 6 | VariableAddress[p] | ir.cpp:342:6:342:6 | +| Dereference(int *) -> int | 0 | 7 | Load | ir.cpp:342:6:342:6 | +| Dereference(int *) -> int | 0 | 8 | Store | ir.cpp:342:5:342:10 | +| Dereference(int *) -> int | 0 | 9 | VariableAddress[#return] | ir.cpp:343:5:343:14 | +| Dereference(int *) -> int | 0 | 10 | VariableAddress[p] | ir.cpp:343:13:343:13 | +| Dereference(int *) -> int | 0 | 11 | Load | ir.cpp:343:13:343:13 | +| Dereference(int *) -> int | 0 | 12 | Load | ir.cpp:343:12:343:13 | +| Dereference(int *) -> int | 0 | 13 | Store | ir.cpp:343:12:343:13 | +| Dereference(int *) -> int | 0 | 14 | VariableAddress[#return] | ir.cpp:341:5:341:15 | +| Dereference(int *) -> int | 0 | 15 | ReturnValue | ir.cpp:341:5:341:15 | +| Dereference(int *) -> int | 0 | 16 | UnmodeledUse | ir.cpp:341:5:341:15 | +| Dereference(int *) -> int | 0 | 17 | ExitFunction | ir.cpp:341:5:341:15 | +| Derived::Derived() -> void | 0 | 0 | EnterFunction | ir.cpp:766:3:766:9 | +| Derived::Derived() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:766:3:766:9 | +| Derived::Derived() -> void | 0 | 2 | InitializeThis | ir.cpp:766:3:766:9 | +| Derived::Derived() -> void | 0 | 3 | ConvertToBase[Derived : Middle] | ir.cpp:766:13:766:13 | +| Derived::Derived() -> void | 0 | 4 | FunctionAddress[Middle] | ir.cpp:766:13:766:13 | +| Derived::Derived() -> void | 0 | 5 | Invoke | ir.cpp:766:13:766:13 | +| Derived::Derived() -> void | 0 | 6 | FieldAddress[derived_s] | ir.cpp:766:13:766:13 | +| Derived::Derived() -> void | 0 | 7 | FunctionAddress[String] | ir.cpp:766:13:766:13 | +| Derived::Derived() -> void | 0 | 8 | Invoke | ir.cpp:766:13:766:13 | +| Derived::Derived() -> void | 0 | 9 | NoOp | ir.cpp:767:3:767:3 | +| Derived::Derived() -> void | 0 | 10 | ReturnVoid | ir.cpp:766:3:766:9 | +| Derived::Derived() -> void | 0 | 11 | UnmodeledUse | ir.cpp:766:3:766:9 | +| Derived::Derived() -> void | 0 | 12 | ExitFunction | ir.cpp:766:3:766:9 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 0 | EnterFunction | ir.cpp:763:8:763:8 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 1 | UnmodeledDefinition | ir.cpp:763:8:763:8 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 2 | InitializeThis | ir.cpp:763:8:763:8 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 3 | InitializeParameter[p#0] | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 4 | VariableAddress[p#0] | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 5 | Store | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 6 | CopyValue | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 7 | ConvertToBase[Derived : Middle] | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 8 | FunctionAddress[operator=] | ir.cpp:763:8:763:8 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 9 | VariableAddress[p#0] | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 10 | Load | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 11 | ConvertToBase[Derived : Middle] | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 12 | Invoke | ir.cpp:763:8:763:8 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 13 | CopyValue | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 14 | FieldAddress[derived_s] | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 15 | FunctionAddress[operator=] | ir.cpp:763:8:763:8 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 16 | VariableAddress[p#0] | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 17 | Load | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 18 | FieldAddress[derived_s] | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 19 | Invoke | ir.cpp:763:8:763:8 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 20 | VariableAddress[#return] | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 21 | CopyValue | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 22 | Store | file://:0:0:0:0 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 23 | VariableAddress[#return] | ir.cpp:763:8:763:8 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 24 | ReturnValue | ir.cpp:763:8:763:8 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 25 | UnmodeledUse | ir.cpp:763:8:763:8 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 26 | ExitFunction | ir.cpp:763:8:763:8 | +| Derived::~Derived() -> void | 0 | 0 | EnterFunction | ir.cpp:768:3:768:10 | +| Derived::~Derived() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:768:3:768:10 | +| Derived::~Derived() -> void | 0 | 2 | InitializeThis | ir.cpp:768:3:768:10 | +| Derived::~Derived() -> void | 0 | 3 | NoOp | ir.cpp:769:3:769:3 | +| Derived::~Derived() -> void | 0 | 4 | FieldAddress[derived_s] | ir.cpp:769:3:769:3 | +| Derived::~Derived() -> void | 0 | 5 | FunctionAddress[~String] | ir.cpp:769:3:769:3 | +| Derived::~Derived() -> void | 0 | 6 | Invoke | ir.cpp:769:3:769:3 | +| Derived::~Derived() -> void | 0 | 7 | ConvertToBase[Derived : Middle] | ir.cpp:769:3:769:3 | +| Derived::~Derived() -> void | 0 | 8 | FunctionAddress[~Middle] | ir.cpp:769:3:769:3 | +| Derived::~Derived() -> void | 0 | 9 | Invoke | ir.cpp:769:3:769:3 | +| Derived::~Derived() -> void | 0 | 10 | ReturnVoid | ir.cpp:768:3:768:10 | +| Derived::~Derived() -> void | 0 | 11 | UnmodeledUse | ir.cpp:768:3:768:10 | +| Derived::~Derived() -> void | 0 | 12 | ExitFunction | ir.cpp:768:3:768:10 | +| DerivedVB::DerivedVB() -> void | 0 | 0 | EnterFunction | ir.cpp:793:3:793:11 | +| DerivedVB::DerivedVB() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:793:3:793:11 | +| DerivedVB::DerivedVB() -> void | 0 | 2 | InitializeThis | ir.cpp:793:3:793:11 | +| DerivedVB::DerivedVB() -> void | 0 | 3 | ConvertToBase[DerivedVB : Base] | ir.cpp:793:15:793:15 | +| DerivedVB::DerivedVB() -> void | 0 | 4 | FunctionAddress[Base] | ir.cpp:793:15:793:15 | +| DerivedVB::DerivedVB() -> void | 0 | 5 | Invoke | ir.cpp:793:15:793:15 | +| DerivedVB::DerivedVB() -> void | 0 | 6 | ConvertToBase[DerivedVB : MiddleVB1] | ir.cpp:793:15:793:15 | +| DerivedVB::DerivedVB() -> void | 0 | 7 | FunctionAddress[MiddleVB1] | ir.cpp:793:15:793:15 | +| DerivedVB::DerivedVB() -> void | 0 | 8 | Invoke | ir.cpp:793:15:793:15 | +| DerivedVB::DerivedVB() -> void | 0 | 9 | ConvertToBase[DerivedVB : MiddleVB2] | ir.cpp:793:15:793:15 | +| DerivedVB::DerivedVB() -> void | 0 | 10 | FunctionAddress[MiddleVB2] | ir.cpp:793:15:793:15 | +| DerivedVB::DerivedVB() -> void | 0 | 11 | Invoke | ir.cpp:793:15:793:15 | +| DerivedVB::DerivedVB() -> void | 0 | 12 | FieldAddress[derivedvb_s] | ir.cpp:793:15:793:15 | +| DerivedVB::DerivedVB() -> void | 0 | 13 | FunctionAddress[String] | ir.cpp:793:15:793:15 | +| DerivedVB::DerivedVB() -> void | 0 | 14 | Invoke | ir.cpp:793:15:793:15 | +| DerivedVB::DerivedVB() -> void | 0 | 15 | NoOp | ir.cpp:794:3:794:3 | +| DerivedVB::DerivedVB() -> void | 0 | 16 | ReturnVoid | ir.cpp:793:3:793:11 | +| DerivedVB::DerivedVB() -> void | 0 | 17 | UnmodeledUse | ir.cpp:793:3:793:11 | +| DerivedVB::DerivedVB() -> void | 0 | 18 | ExitFunction | ir.cpp:793:3:793:11 | +| DerivedVB::~DerivedVB() -> void | 0 | 0 | EnterFunction | ir.cpp:795:3:795:12 | +| DerivedVB::~DerivedVB() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:795:3:795:12 | +| DerivedVB::~DerivedVB() -> void | 0 | 2 | InitializeThis | ir.cpp:795:3:795:12 | +| DerivedVB::~DerivedVB() -> void | 0 | 3 | NoOp | ir.cpp:796:3:796:3 | +| DerivedVB::~DerivedVB() -> void | 0 | 4 | FieldAddress[derivedvb_s] | ir.cpp:796:3:796:3 | +| DerivedVB::~DerivedVB() -> void | 0 | 5 | FunctionAddress[~String] | ir.cpp:796:3:796:3 | +| DerivedVB::~DerivedVB() -> void | 0 | 6 | Invoke | ir.cpp:796:3:796:3 | +| DerivedVB::~DerivedVB() -> void | 0 | 7 | ConvertToBase[DerivedVB : MiddleVB2] | ir.cpp:796:3:796:3 | +| DerivedVB::~DerivedVB() -> void | 0 | 8 | FunctionAddress[~MiddleVB2] | ir.cpp:796:3:796:3 | +| DerivedVB::~DerivedVB() -> void | 0 | 9 | Invoke | ir.cpp:796:3:796:3 | +| DerivedVB::~DerivedVB() -> void | 0 | 10 | ConvertToBase[DerivedVB : MiddleVB1] | ir.cpp:796:3:796:3 | +| DerivedVB::~DerivedVB() -> void | 0 | 11 | FunctionAddress[~MiddleVB1] | ir.cpp:796:3:796:3 | +| DerivedVB::~DerivedVB() -> void | 0 | 12 | Invoke | ir.cpp:796:3:796:3 | +| DerivedVB::~DerivedVB() -> void | 0 | 13 | ConvertToBase[DerivedVB : Base] | ir.cpp:796:3:796:3 | +| DerivedVB::~DerivedVB() -> void | 0 | 14 | FunctionAddress[~Base] | ir.cpp:796:3:796:3 | +| DerivedVB::~DerivedVB() -> void | 0 | 15 | Invoke | ir.cpp:796:3:796:3 | +| DerivedVB::~DerivedVB() -> void | 0 | 16 | ReturnVoid | ir.cpp:795:3:795:12 | +| DerivedVB::~DerivedVB() -> void | 0 | 17 | UnmodeledUse | ir.cpp:795:3:795:12 | +| DerivedVB::~DerivedVB() -> void | 0 | 18 | ExitFunction | ir.cpp:795:3:795:12 | +| DoStatements(int) -> void | 0 | 0 | EnterFunction | ir.cpp:259:6:259:17 | +| DoStatements(int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:259:6:259:17 | +| DoStatements(int) -> void | 0 | 2 | InitializeParameter[n] | ir.cpp:259:23:259:23 | +| DoStatements(int) -> void | 0 | 3 | VariableAddress[n] | ir.cpp:259:23:259:23 | +| DoStatements(int) -> void | 0 | 4 | Store | ir.cpp:259:23:259:23 | +| DoStatements(int) -> void | 1 | 0 | Phi | ir.cpp:261:14:261:14 | +| DoStatements(int) -> void | 1 | 1 | Constant[1] | ir.cpp:261:14:261:14 | +| DoStatements(int) -> void | 1 | 2 | VariableAddress[n] | ir.cpp:261:9:261:9 | +| DoStatements(int) -> void | 1 | 3 | Load | ir.cpp:261:9:261:14 | +| DoStatements(int) -> void | 1 | 4 | Sub | ir.cpp:261:9:261:14 | +| DoStatements(int) -> void | 1 | 5 | Store | ir.cpp:261:9:261:14 | +| DoStatements(int) -> void | 1 | 6 | VariableAddress[n] | ir.cpp:262:14:262:14 | +| DoStatements(int) -> void | 1 | 7 | Load | ir.cpp:262:14:262:14 | +| DoStatements(int) -> void | 1 | 8 | Constant[0] | ir.cpp:262:18:262:18 | +| DoStatements(int) -> void | 1 | 9 | CompareGT | ir.cpp:262:14:262:18 | +| DoStatements(int) -> void | 1 | 10 | ConditionalBranch | ir.cpp:262:14:262:18 | +| DoStatements(int) -> void | 2 | 0 | NoOp | ir.cpp:263:1:263:1 | +| DoStatements(int) -> void | 2 | 1 | ReturnVoid | ir.cpp:259:6:259:17 | +| DoStatements(int) -> void | 2 | 2 | UnmodeledUse | ir.cpp:259:6:259:17 | +| DoStatements(int) -> void | 2 | 3 | ExitFunction | ir.cpp:259:6:259:17 | +| DynamicCast() -> void | 0 | 0 | EnterFunction | ir.cpp:849:6:849:16 | +| DynamicCast() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:849:6:849:16 | +| DynamicCast() -> void | 0 | 2 | VariableAddress[b] | ir.cpp:850:19:850:19 | +| DynamicCast() -> void | 0 | 3 | FunctionAddress[PolymorphicBase] | file://:0:0:0:0 | +| DynamicCast() -> void | 0 | 4 | Invoke | file://:0:0:0:0 | +| DynamicCast() -> void | 0 | 5 | VariableAddress[d] | ir.cpp:851:22:851:22 | +| DynamicCast() -> void | 0 | 6 | FunctionAddress[PolymorphicDerived] | ir.cpp:851:22:851:22 | +| DynamicCast() -> void | 0 | 7 | Invoke | ir.cpp:851:22:851:22 | +| DynamicCast() -> void | 0 | 8 | VariableAddress[pb] | ir.cpp:853:20:853:21 | +| DynamicCast() -> void | 0 | 9 | VariableAddress[b] | ir.cpp:853:26:853:26 | +| DynamicCast() -> void | 0 | 10 | Store | ir.cpp:853:25:853:26 | +| DynamicCast() -> void | 0 | 11 | VariableAddress[pd] | ir.cpp:854:23:854:24 | +| DynamicCast() -> void | 0 | 12 | VariableAddress[d] | ir.cpp:854:29:854:29 | +| DynamicCast() -> void | 0 | 13 | Store | ir.cpp:854:28:854:29 | +| DynamicCast() -> void | 0 | 14 | VariableAddress[pd] | ir.cpp:857:39:857:40 | +| DynamicCast() -> void | 0 | 15 | Load | ir.cpp:857:39:857:40 | +| DynamicCast() -> void | 0 | 16 | ConvertToBase[PolymorphicDerived : PolymorphicBase] | ir.cpp:857:8:857:41 | +| DynamicCast() -> void | 0 | 17 | VariableAddress[pb] | ir.cpp:857:3:857:4 | +| DynamicCast() -> void | 0 | 18 | Store | ir.cpp:857:3:857:41 | +| DynamicCast() -> void | 0 | 19 | VariableAddress[rb] | ir.cpp:858:20:858:21 | +| DynamicCast() -> void | 0 | 20 | VariableAddress[d] | ir.cpp:858:56:858:56 | +| DynamicCast() -> void | 0 | 21 | ConvertToBase[PolymorphicDerived : PolymorphicBase] | ir.cpp:858:25:858:57 | +| DynamicCast() -> void | 0 | 22 | Store | ir.cpp:858:25:858:57 | +| DynamicCast() -> void | 0 | 23 | VariableAddress[pb] | ir.cpp:860:42:860:43 | +| DynamicCast() -> void | 0 | 24 | Load | ir.cpp:860:42:860:43 | +| DynamicCast() -> void | 0 | 25 | CheckedConvertOrNull | ir.cpp:860:8:860:44 | +| DynamicCast() -> void | 0 | 26 | VariableAddress[pd] | ir.cpp:860:3:860:4 | +| DynamicCast() -> void | 0 | 27 | Store | ir.cpp:860:3:860:44 | +| DynamicCast() -> void | 0 | 28 | VariableAddress[rd] | ir.cpp:861:23:861:24 | +| DynamicCast() -> void | 0 | 29 | VariableAddress[b] | ir.cpp:861:62:861:62 | +| DynamicCast() -> void | 0 | 30 | CheckedConvertOrThrow | ir.cpp:861:28:861:63 | +| DynamicCast() -> void | 0 | 31 | Store | ir.cpp:861:28:861:63 | +| DynamicCast() -> void | 0 | 32 | VariableAddress[pv] | ir.cpp:863:9:863:10 | +| DynamicCast() -> void | 0 | 33 | VariableAddress[pb] | ir.cpp:863:34:863:35 | +| DynamicCast() -> void | 0 | 34 | Load | ir.cpp:863:34:863:35 | +| DynamicCast() -> void | 0 | 35 | DynamicCastToVoid | ir.cpp:863:14:863:36 | +| DynamicCast() -> void | 0 | 36 | Store | ir.cpp:863:14:863:36 | +| DynamicCast() -> void | 0 | 37 | VariableAddress[pcv] | ir.cpp:864:15:864:17 | +| DynamicCast() -> void | 0 | 38 | VariableAddress[pd] | ir.cpp:864:47:864:48 | +| DynamicCast() -> void | 0 | 39 | Load | ir.cpp:864:47:864:48 | +| DynamicCast() -> void | 0 | 40 | DynamicCastToVoid | ir.cpp:864:21:864:49 | +| DynamicCast() -> void | 0 | 41 | Store | ir.cpp:864:21:864:49 | +| DynamicCast() -> void | 0 | 42 | NoOp | ir.cpp:865:1:865:1 | +| DynamicCast() -> void | 0 | 43 | ReturnVoid | ir.cpp:849:6:849:16 | +| DynamicCast() -> void | 0 | 44 | UnmodeledUse | ir.cpp:849:6:849:16 | +| DynamicCast() -> void | 0 | 45 | ExitFunction | ir.cpp:849:6:849:16 | +| EarlyReturn(int, int) -> void | 0 | 0 | EnterFunction | ir.cpp:535:6:535:16 | +| EarlyReturn(int, int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:535:6:535:16 | +| EarlyReturn(int, int) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:535:22:535:22 | +| EarlyReturn(int, int) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:535:22:535:22 | +| EarlyReturn(int, int) -> void | 0 | 4 | Store | ir.cpp:535:22:535:22 | +| EarlyReturn(int, int) -> void | 0 | 5 | InitializeParameter[y] | ir.cpp:535:29:535:29 | +| EarlyReturn(int, int) -> void | 0 | 6 | VariableAddress[y] | ir.cpp:535:29:535:29 | +| EarlyReturn(int, int) -> void | 0 | 7 | Store | ir.cpp:535:29:535:29 | +| EarlyReturn(int, int) -> void | 0 | 8 | VariableAddress[x] | ir.cpp:536:9:536:9 | +| EarlyReturn(int, int) -> void | 0 | 9 | Load | ir.cpp:536:9:536:9 | +| EarlyReturn(int, int) -> void | 0 | 10 | VariableAddress[y] | ir.cpp:536:13:536:13 | +| EarlyReturn(int, int) -> void | 0 | 11 | Load | ir.cpp:536:13:536:13 | +| EarlyReturn(int, int) -> void | 0 | 12 | CompareLT | ir.cpp:536:9:536:13 | +| EarlyReturn(int, int) -> void | 0 | 13 | ConditionalBranch | ir.cpp:536:9:536:13 | +| EarlyReturn(int, int) -> void | 1 | 0 | ReturnVoid | ir.cpp:535:6:535:16 | +| EarlyReturn(int, int) -> void | 1 | 1 | UnmodeledUse | ir.cpp:535:6:535:16 | +| EarlyReturn(int, int) -> void | 1 | 2 | ExitFunction | ir.cpp:535:6:535:16 | +| EarlyReturn(int, int) -> void | 2 | 0 | NoOp | ir.cpp:537:9:537:15 | +| EarlyReturn(int, int) -> void | 3 | 0 | VariableAddress[x] | ir.cpp:540:9:540:9 | +| EarlyReturn(int, int) -> void | 3 | 1 | Load | ir.cpp:540:9:540:9 | +| EarlyReturn(int, int) -> void | 3 | 2 | VariableAddress[y] | ir.cpp:540:5:540:5 | +| EarlyReturn(int, int) -> void | 3 | 3 | Store | ir.cpp:540:5:540:9 | +| EarlyReturn(int, int) -> void | 3 | 4 | NoOp | ir.cpp:541:1:541:1 | +| EarlyReturnValue(int, int) -> int | 0 | 0 | EnterFunction | ir.cpp:543:5:543:20 | +| EarlyReturnValue(int, int) -> int | 0 | 1 | UnmodeledDefinition | ir.cpp:543:5:543:20 | +| EarlyReturnValue(int, int) -> int | 0 | 2 | InitializeParameter[x] | ir.cpp:543:26:543:26 | +| EarlyReturnValue(int, int) -> int | 0 | 3 | VariableAddress[x] | ir.cpp:543:26:543:26 | +| EarlyReturnValue(int, int) -> int | 0 | 4 | Store | ir.cpp:543:26:543:26 | +| EarlyReturnValue(int, int) -> int | 0 | 5 | InitializeParameter[y] | ir.cpp:543:33:543:33 | +| EarlyReturnValue(int, int) -> int | 0 | 6 | VariableAddress[y] | ir.cpp:543:33:543:33 | +| EarlyReturnValue(int, int) -> int | 0 | 7 | Store | ir.cpp:543:33:543:33 | +| EarlyReturnValue(int, int) -> int | 0 | 8 | VariableAddress[x] | ir.cpp:544:9:544:9 | +| EarlyReturnValue(int, int) -> int | 0 | 9 | Load | ir.cpp:544:9:544:9 | +| EarlyReturnValue(int, int) -> int | 0 | 10 | VariableAddress[y] | ir.cpp:544:13:544:13 | +| EarlyReturnValue(int, int) -> int | 0 | 11 | Load | ir.cpp:544:13:544:13 | +| EarlyReturnValue(int, int) -> int | 0 | 12 | CompareLT | ir.cpp:544:9:544:13 | +| EarlyReturnValue(int, int) -> int | 0 | 13 | ConditionalBranch | ir.cpp:544:9:544:13 | +| EarlyReturnValue(int, int) -> int | 1 | 0 | Phi | ir.cpp:543:5:543:20 | +| EarlyReturnValue(int, int) -> int | 1 | 1 | VariableAddress[#return] | ir.cpp:543:5:543:20 | +| EarlyReturnValue(int, int) -> int | 1 | 2 | ReturnValue | ir.cpp:543:5:543:20 | +| EarlyReturnValue(int, int) -> int | 1 | 3 | UnmodeledUse | ir.cpp:543:5:543:20 | +| EarlyReturnValue(int, int) -> int | 1 | 4 | ExitFunction | ir.cpp:543:5:543:20 | +| EarlyReturnValue(int, int) -> int | 2 | 0 | VariableAddress[#return] | ir.cpp:545:9:545:17 | +| EarlyReturnValue(int, int) -> int | 2 | 1 | VariableAddress[x] | ir.cpp:545:16:545:16 | +| EarlyReturnValue(int, int) -> int | 2 | 2 | Load | ir.cpp:545:16:545:16 | +| EarlyReturnValue(int, int) -> int | 2 | 3 | Store | ir.cpp:545:16:545:16 | +| EarlyReturnValue(int, int) -> int | 3 | 0 | VariableAddress[#return] | ir.cpp:548:5:548:17 | +| EarlyReturnValue(int, int) -> int | 3 | 1 | VariableAddress[x] | ir.cpp:548:12:548:12 | +| EarlyReturnValue(int, int) -> int | 3 | 2 | Load | ir.cpp:548:12:548:12 | +| EarlyReturnValue(int, int) -> int | 3 | 3 | VariableAddress[y] | ir.cpp:548:16:548:16 | +| EarlyReturnValue(int, int) -> int | 3 | 4 | Load | ir.cpp:548:16:548:16 | +| EarlyReturnValue(int, int) -> int | 3 | 5 | Add | ir.cpp:548:12:548:16 | +| EarlyReturnValue(int, int) -> int | 3 | 6 | Store | ir.cpp:548:12:548:16 | +| EnumSwitch(E) -> int | 0 | 0 | EnterFunction | ir.cpp:560:5:560:14 | +| EnumSwitch(E) -> int | 0 | 1 | UnmodeledDefinition | ir.cpp:560:5:560:14 | +| EnumSwitch(E) -> int | 0 | 2 | InitializeParameter[e] | ir.cpp:560:18:560:18 | +| EnumSwitch(E) -> int | 0 | 3 | VariableAddress[e] | ir.cpp:560:18:560:18 | +| EnumSwitch(E) -> int | 0 | 4 | Store | ir.cpp:560:18:560:18 | +| EnumSwitch(E) -> int | 0 | 5 | VariableAddress[e] | ir.cpp:561:13:561:13 | +| EnumSwitch(E) -> int | 0 | 6 | Load | ir.cpp:561:13:561:13 | +| EnumSwitch(E) -> int | 0 | 7 | Convert | ir.cpp:561:13:561:13 | +| EnumSwitch(E) -> int | 0 | 8 | Switch | ir.cpp:561:5:568:5 | +| EnumSwitch(E) -> int | 1 | 0 | Phi | ir.cpp:560:5:560:14 | +| EnumSwitch(E) -> int | 1 | 1 | VariableAddress[#return] | ir.cpp:560:5:560:14 | +| EnumSwitch(E) -> int | 1 | 2 | ReturnValue | ir.cpp:560:5:560:14 | +| EnumSwitch(E) -> int | 1 | 3 | UnmodeledUse | ir.cpp:560:5:560:14 | +| EnumSwitch(E) -> int | 1 | 4 | ExitFunction | ir.cpp:560:5:560:14 | +| EnumSwitch(E) -> int | 2 | 0 | NoOp | ir.cpp:564:9:564:17 | +| EnumSwitch(E) -> int | 2 | 1 | VariableAddress[#return] | ir.cpp:565:13:565:21 | +| EnumSwitch(E) -> int | 2 | 2 | Constant[1] | ir.cpp:565:20:565:20 | +| EnumSwitch(E) -> int | 2 | 3 | Store | ir.cpp:565:20:565:20 | +| EnumSwitch(E) -> int | 3 | 0 | NoOp | ir.cpp:566:9:566:16 | +| EnumSwitch(E) -> int | 3 | 1 | VariableAddress[#return] | ir.cpp:567:13:567:22 | +| EnumSwitch(E) -> int | 3 | 2 | Constant[-1] | ir.cpp:567:20:567:21 | +| EnumSwitch(E) -> int | 3 | 3 | Store | ir.cpp:567:20:567:21 | +| EnumSwitch(E) -> int | 4 | 0 | NoOp | ir.cpp:562:9:562:17 | +| EnumSwitch(E) -> int | 4 | 1 | VariableAddress[#return] | ir.cpp:563:13:563:21 | +| EnumSwitch(E) -> int | 4 | 2 | Constant[0] | ir.cpp:563:20:563:20 | +| EnumSwitch(E) -> int | 4 | 3 | Store | ir.cpp:563:20:563:20 | +| FieldAccess() -> void | 0 | 0 | EnterFunction | ir.cpp:426:6:426:16 | +| FieldAccess() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:426:6:426:16 | +| FieldAccess() -> void | 0 | 2 | VariableAddress[pt] | ir.cpp:427:11:427:12 | +| FieldAccess() -> void | 0 | 3 | Uninitialized | ir.cpp:427:11:427:12 | +| FieldAccess() -> void | 0 | 4 | Store | ir.cpp:427:11:427:12 | +| FieldAccess() -> void | 0 | 5 | Constant[5] | ir.cpp:428:12:428:12 | +| FieldAccess() -> void | 0 | 6 | VariableAddress[pt] | ir.cpp:428:5:428:6 | +| FieldAccess() -> void | 0 | 7 | FieldAddress[x] | ir.cpp:428:8:428:8 | +| FieldAccess() -> void | 0 | 8 | Store | ir.cpp:428:5:428:12 | +| FieldAccess() -> void | 0 | 9 | VariableAddress[pt] | ir.cpp:429:12:429:13 | +| FieldAccess() -> void | 0 | 10 | FieldAddress[x] | ir.cpp:429:15:429:15 | +| FieldAccess() -> void | 0 | 11 | Load | ir.cpp:429:15:429:15 | +| FieldAccess() -> void | 0 | 12 | VariableAddress[pt] | ir.cpp:429:5:429:6 | +| FieldAccess() -> void | 0 | 13 | FieldAddress[y] | ir.cpp:429:8:429:8 | +| FieldAccess() -> void | 0 | 14 | Store | ir.cpp:429:5:429:15 | +| FieldAccess() -> void | 0 | 15 | VariableAddress[p] | ir.cpp:430:10:430:10 | +| FieldAccess() -> void | 0 | 16 | VariableAddress[pt] | ir.cpp:430:15:430:16 | +| FieldAccess() -> void | 0 | 17 | FieldAddress[y] | ir.cpp:430:18:430:18 | +| FieldAccess() -> void | 0 | 18 | Store | ir.cpp:430:14:430:18 | +| FieldAccess() -> void | 0 | 19 | NoOp | ir.cpp:431:1:431:1 | +| FieldAccess() -> void | 0 | 20 | ReturnVoid | ir.cpp:426:6:426:16 | +| FieldAccess() -> void | 0 | 21 | UnmodeledUse | ir.cpp:426:6:426:16 | +| FieldAccess() -> void | 0 | 22 | ExitFunction | ir.cpp:426:6:426:16 | +| FloatCompare(double, double) -> void | 0 | 0 | EnterFunction | ir.cpp:133:6:133:17 | +| FloatCompare(double, double) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:133:6:133:17 | +| FloatCompare(double, double) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:133:26:133:26 | +| FloatCompare(double, double) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:133:26:133:26 | +| FloatCompare(double, double) -> void | 0 | 4 | Store | ir.cpp:133:26:133:26 | +| FloatCompare(double, double) -> void | 0 | 5 | InitializeParameter[y] | ir.cpp:133:36:133:36 | +| FloatCompare(double, double) -> void | 0 | 6 | VariableAddress[y] | ir.cpp:133:36:133:36 | +| FloatCompare(double, double) -> void | 0 | 7 | Store | ir.cpp:133:36:133:36 | +| FloatCompare(double, double) -> void | 0 | 8 | VariableAddress[b] | ir.cpp:134:10:134:10 | +| FloatCompare(double, double) -> void | 0 | 9 | Uninitialized | ir.cpp:134:10:134:10 | +| FloatCompare(double, double) -> void | 0 | 10 | Store | ir.cpp:134:10:134:10 | +| FloatCompare(double, double) -> void | 0 | 11 | VariableAddress[x] | ir.cpp:136:9:136:9 | +| FloatCompare(double, double) -> void | 0 | 12 | Load | ir.cpp:136:9:136:9 | +| FloatCompare(double, double) -> void | 0 | 13 | VariableAddress[y] | ir.cpp:136:14:136:14 | +| FloatCompare(double, double) -> void | 0 | 14 | Load | ir.cpp:136:14:136:14 | +| FloatCompare(double, double) -> void | 0 | 15 | CompareEQ | ir.cpp:136:9:136:14 | +| FloatCompare(double, double) -> void | 0 | 16 | VariableAddress[b] | ir.cpp:136:5:136:5 | +| FloatCompare(double, double) -> void | 0 | 17 | Store | ir.cpp:136:5:136:14 | +| FloatCompare(double, double) -> void | 0 | 18 | VariableAddress[x] | ir.cpp:137:9:137:9 | +| FloatCompare(double, double) -> void | 0 | 19 | Load | ir.cpp:137:9:137:9 | +| FloatCompare(double, double) -> void | 0 | 20 | VariableAddress[y] | ir.cpp:137:14:137:14 | +| FloatCompare(double, double) -> void | 0 | 21 | Load | ir.cpp:137:14:137:14 | +| FloatCompare(double, double) -> void | 0 | 22 | CompareNE | ir.cpp:137:9:137:14 | +| FloatCompare(double, double) -> void | 0 | 23 | VariableAddress[b] | ir.cpp:137:5:137:5 | +| FloatCompare(double, double) -> void | 0 | 24 | Store | ir.cpp:137:5:137:14 | +| FloatCompare(double, double) -> void | 0 | 25 | VariableAddress[x] | ir.cpp:138:9:138:9 | +| FloatCompare(double, double) -> void | 0 | 26 | Load | ir.cpp:138:9:138:9 | +| FloatCompare(double, double) -> void | 0 | 27 | VariableAddress[y] | ir.cpp:138:13:138:13 | +| FloatCompare(double, double) -> void | 0 | 28 | Load | ir.cpp:138:13:138:13 | +| FloatCompare(double, double) -> void | 0 | 29 | CompareLT | ir.cpp:138:9:138:13 | +| FloatCompare(double, double) -> void | 0 | 30 | VariableAddress[b] | ir.cpp:138:5:138:5 | +| FloatCompare(double, double) -> void | 0 | 31 | Store | ir.cpp:138:5:138:13 | +| FloatCompare(double, double) -> void | 0 | 32 | VariableAddress[x] | ir.cpp:139:9:139:9 | +| FloatCompare(double, double) -> void | 0 | 33 | Load | ir.cpp:139:9:139:9 | +| FloatCompare(double, double) -> void | 0 | 34 | VariableAddress[y] | ir.cpp:139:13:139:13 | +| FloatCompare(double, double) -> void | 0 | 35 | Load | ir.cpp:139:13:139:13 | +| FloatCompare(double, double) -> void | 0 | 36 | CompareGT | ir.cpp:139:9:139:13 | +| FloatCompare(double, double) -> void | 0 | 37 | VariableAddress[b] | ir.cpp:139:5:139:5 | +| FloatCompare(double, double) -> void | 0 | 38 | Store | ir.cpp:139:5:139:13 | +| FloatCompare(double, double) -> void | 0 | 39 | VariableAddress[x] | ir.cpp:140:9:140:9 | +| FloatCompare(double, double) -> void | 0 | 40 | Load | ir.cpp:140:9:140:9 | +| FloatCompare(double, double) -> void | 0 | 41 | VariableAddress[y] | ir.cpp:140:14:140:14 | +| FloatCompare(double, double) -> void | 0 | 42 | Load | ir.cpp:140:14:140:14 | +| FloatCompare(double, double) -> void | 0 | 43 | CompareLE | ir.cpp:140:9:140:14 | +| FloatCompare(double, double) -> void | 0 | 44 | VariableAddress[b] | ir.cpp:140:5:140:5 | +| FloatCompare(double, double) -> void | 0 | 45 | Store | ir.cpp:140:5:140:14 | +| FloatCompare(double, double) -> void | 0 | 46 | VariableAddress[x] | ir.cpp:141:9:141:9 | +| FloatCompare(double, double) -> void | 0 | 47 | Load | ir.cpp:141:9:141:9 | +| FloatCompare(double, double) -> void | 0 | 48 | VariableAddress[y] | ir.cpp:141:14:141:14 | +| FloatCompare(double, double) -> void | 0 | 49 | Load | ir.cpp:141:14:141:14 | +| FloatCompare(double, double) -> void | 0 | 50 | CompareGE | ir.cpp:141:9:141:14 | +| FloatCompare(double, double) -> void | 0 | 51 | VariableAddress[b] | ir.cpp:141:5:141:5 | +| FloatCompare(double, double) -> void | 0 | 52 | Store | ir.cpp:141:5:141:14 | +| FloatCompare(double, double) -> void | 0 | 53 | NoOp | ir.cpp:142:1:142:1 | +| FloatCompare(double, double) -> void | 0 | 54 | ReturnVoid | ir.cpp:133:6:133:17 | +| FloatCompare(double, double) -> void | 0 | 55 | UnmodeledUse | ir.cpp:133:6:133:17 | +| FloatCompare(double, double) -> void | 0 | 56 | ExitFunction | ir.cpp:133:6:133:17 | +| FloatCrement(float) -> void | 0 | 0 | EnterFunction | ir.cpp:144:6:144:17 | +| FloatCrement(float) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:144:6:144:17 | +| FloatCrement(float) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:144:25:144:25 | +| FloatCrement(float) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:144:25:144:25 | +| FloatCrement(float) -> void | 0 | 4 | Store | ir.cpp:144:25:144:25 | +| FloatCrement(float) -> void | 0 | 5 | VariableAddress[y] | ir.cpp:145:11:145:11 | +| FloatCrement(float) -> void | 0 | 6 | Uninitialized | ir.cpp:145:11:145:11 | +| FloatCrement(float) -> void | 0 | 7 | Store | ir.cpp:145:11:145:11 | +| FloatCrement(float) -> void | 0 | 8 | VariableAddress[x] | ir.cpp:147:11:147:11 | +| FloatCrement(float) -> void | 0 | 9 | Load | ir.cpp:147:9:147:11 | +| FloatCrement(float) -> void | 0 | 10 | Constant[1.0] | ir.cpp:147:9:147:11 | +| FloatCrement(float) -> void | 0 | 11 | Add | ir.cpp:147:9:147:11 | +| FloatCrement(float) -> void | 0 | 12 | Store | ir.cpp:147:9:147:11 | +| FloatCrement(float) -> void | 0 | 13 | VariableAddress[y] | ir.cpp:147:5:147:5 | +| FloatCrement(float) -> void | 0 | 14 | Store | ir.cpp:147:5:147:11 | +| FloatCrement(float) -> void | 0 | 15 | VariableAddress[x] | ir.cpp:148:11:148:11 | +| FloatCrement(float) -> void | 0 | 16 | Load | ir.cpp:148:9:148:11 | +| FloatCrement(float) -> void | 0 | 17 | Constant[1.0] | ir.cpp:148:9:148:11 | +| FloatCrement(float) -> void | 0 | 18 | Sub | ir.cpp:148:9:148:11 | +| FloatCrement(float) -> void | 0 | 19 | Store | ir.cpp:148:9:148:11 | +| FloatCrement(float) -> void | 0 | 20 | VariableAddress[y] | ir.cpp:148:5:148:5 | +| FloatCrement(float) -> void | 0 | 21 | Store | ir.cpp:148:5:148:11 | +| FloatCrement(float) -> void | 0 | 22 | VariableAddress[x] | ir.cpp:149:9:149:9 | +| FloatCrement(float) -> void | 0 | 23 | Load | ir.cpp:149:9:149:11 | +| FloatCrement(float) -> void | 0 | 24 | Constant[1.0] | ir.cpp:149:9:149:11 | +| FloatCrement(float) -> void | 0 | 25 | Add | ir.cpp:149:9:149:11 | +| FloatCrement(float) -> void | 0 | 26 | Store | ir.cpp:149:9:149:11 | +| FloatCrement(float) -> void | 0 | 27 | VariableAddress[y] | ir.cpp:149:5:149:5 | +| FloatCrement(float) -> void | 0 | 28 | Store | ir.cpp:149:5:149:11 | +| FloatCrement(float) -> void | 0 | 29 | VariableAddress[x] | ir.cpp:150:9:150:9 | +| FloatCrement(float) -> void | 0 | 30 | Load | ir.cpp:150:9:150:11 | +| FloatCrement(float) -> void | 0 | 31 | Constant[1.0] | ir.cpp:150:9:150:11 | +| FloatCrement(float) -> void | 0 | 32 | Sub | ir.cpp:150:9:150:11 | +| FloatCrement(float) -> void | 0 | 33 | Store | ir.cpp:150:9:150:11 | +| FloatCrement(float) -> void | 0 | 34 | VariableAddress[y] | ir.cpp:150:5:150:5 | +| FloatCrement(float) -> void | 0 | 35 | Store | ir.cpp:150:5:150:11 | +| FloatCrement(float) -> void | 0 | 36 | NoOp | ir.cpp:151:1:151:1 | +| FloatCrement(float) -> void | 0 | 37 | ReturnVoid | ir.cpp:144:6:144:17 | +| FloatCrement(float) -> void | 0 | 38 | UnmodeledUse | ir.cpp:144:6:144:17 | +| FloatCrement(float) -> void | 0 | 39 | ExitFunction | ir.cpp:144:6:144:17 | +| FloatOps(double, double) -> void | 0 | 0 | EnterFunction | ir.cpp:114:6:114:13 | +| FloatOps(double, double) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:114:6:114:13 | +| FloatOps(double, double) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:114:22:114:22 | +| FloatOps(double, double) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:114:22:114:22 | +| FloatOps(double, double) -> void | 0 | 4 | Store | ir.cpp:114:22:114:22 | +| FloatOps(double, double) -> void | 0 | 5 | InitializeParameter[y] | ir.cpp:114:32:114:32 | +| FloatOps(double, double) -> void | 0 | 6 | VariableAddress[y] | ir.cpp:114:32:114:32 | +| FloatOps(double, double) -> void | 0 | 7 | Store | ir.cpp:114:32:114:32 | +| FloatOps(double, double) -> void | 0 | 8 | VariableAddress[z] | ir.cpp:115:12:115:12 | +| FloatOps(double, double) -> void | 0 | 9 | Uninitialized | ir.cpp:115:12:115:12 | +| FloatOps(double, double) -> void | 0 | 10 | Store | ir.cpp:115:12:115:12 | +| FloatOps(double, double) -> void | 0 | 11 | VariableAddress[x] | ir.cpp:117:9:117:9 | +| FloatOps(double, double) -> void | 0 | 12 | Load | ir.cpp:117:9:117:9 | +| FloatOps(double, double) -> void | 0 | 13 | VariableAddress[y] | ir.cpp:117:13:117:13 | +| FloatOps(double, double) -> void | 0 | 14 | Load | ir.cpp:117:13:117:13 | +| FloatOps(double, double) -> void | 0 | 15 | Add | ir.cpp:117:9:117:13 | +| FloatOps(double, double) -> void | 0 | 16 | VariableAddress[z] | ir.cpp:117:5:117:5 | +| FloatOps(double, double) -> void | 0 | 17 | Store | ir.cpp:117:5:117:13 | +| FloatOps(double, double) -> void | 0 | 18 | VariableAddress[x] | ir.cpp:118:9:118:9 | +| FloatOps(double, double) -> void | 0 | 19 | Load | ir.cpp:118:9:118:9 | +| FloatOps(double, double) -> void | 0 | 20 | VariableAddress[y] | ir.cpp:118:13:118:13 | +| FloatOps(double, double) -> void | 0 | 21 | Load | ir.cpp:118:13:118:13 | +| FloatOps(double, double) -> void | 0 | 22 | Sub | ir.cpp:118:9:118:13 | +| FloatOps(double, double) -> void | 0 | 23 | VariableAddress[z] | ir.cpp:118:5:118:5 | +| FloatOps(double, double) -> void | 0 | 24 | Store | ir.cpp:118:5:118:13 | +| FloatOps(double, double) -> void | 0 | 25 | VariableAddress[x] | ir.cpp:119:9:119:9 | +| FloatOps(double, double) -> void | 0 | 26 | Load | ir.cpp:119:9:119:9 | +| FloatOps(double, double) -> void | 0 | 27 | VariableAddress[y] | ir.cpp:119:13:119:13 | +| FloatOps(double, double) -> void | 0 | 28 | Load | ir.cpp:119:13:119:13 | +| FloatOps(double, double) -> void | 0 | 29 | Mul | ir.cpp:119:9:119:13 | +| FloatOps(double, double) -> void | 0 | 30 | VariableAddress[z] | ir.cpp:119:5:119:5 | +| FloatOps(double, double) -> void | 0 | 31 | Store | ir.cpp:119:5:119:13 | +| FloatOps(double, double) -> void | 0 | 32 | VariableAddress[x] | ir.cpp:120:9:120:9 | +| FloatOps(double, double) -> void | 0 | 33 | Load | ir.cpp:120:9:120:9 | +| FloatOps(double, double) -> void | 0 | 34 | VariableAddress[y] | ir.cpp:120:13:120:13 | +| FloatOps(double, double) -> void | 0 | 35 | Load | ir.cpp:120:13:120:13 | +| FloatOps(double, double) -> void | 0 | 36 | Div | ir.cpp:120:9:120:13 | +| FloatOps(double, double) -> void | 0 | 37 | VariableAddress[z] | ir.cpp:120:5:120:5 | +| FloatOps(double, double) -> void | 0 | 38 | Store | ir.cpp:120:5:120:13 | +| FloatOps(double, double) -> void | 0 | 39 | VariableAddress[x] | ir.cpp:122:9:122:9 | +| FloatOps(double, double) -> void | 0 | 40 | Load | ir.cpp:122:9:122:9 | +| FloatOps(double, double) -> void | 0 | 41 | VariableAddress[z] | ir.cpp:122:5:122:5 | +| FloatOps(double, double) -> void | 0 | 42 | Store | ir.cpp:122:5:122:9 | +| FloatOps(double, double) -> void | 0 | 43 | VariableAddress[x] | ir.cpp:124:10:124:10 | +| FloatOps(double, double) -> void | 0 | 44 | Load | ir.cpp:124:10:124:10 | +| FloatOps(double, double) -> void | 0 | 45 | VariableAddress[z] | ir.cpp:124:5:124:5 | +| FloatOps(double, double) -> void | 0 | 46 | Load | ir.cpp:124:5:124:10 | +| FloatOps(double, double) -> void | 0 | 47 | Add | ir.cpp:124:5:124:10 | +| FloatOps(double, double) -> void | 0 | 48 | Store | ir.cpp:124:5:124:10 | +| FloatOps(double, double) -> void | 0 | 49 | VariableAddress[x] | ir.cpp:125:10:125:10 | +| FloatOps(double, double) -> void | 0 | 50 | Load | ir.cpp:125:10:125:10 | +| FloatOps(double, double) -> void | 0 | 51 | VariableAddress[z] | ir.cpp:125:5:125:5 | +| FloatOps(double, double) -> void | 0 | 52 | Load | ir.cpp:125:5:125:10 | +| FloatOps(double, double) -> void | 0 | 53 | Sub | ir.cpp:125:5:125:10 | +| FloatOps(double, double) -> void | 0 | 54 | Store | ir.cpp:125:5:125:10 | +| FloatOps(double, double) -> void | 0 | 55 | VariableAddress[x] | ir.cpp:126:10:126:10 | +| FloatOps(double, double) -> void | 0 | 56 | Load | ir.cpp:126:10:126:10 | +| FloatOps(double, double) -> void | 0 | 57 | VariableAddress[z] | ir.cpp:126:5:126:5 | +| FloatOps(double, double) -> void | 0 | 58 | Load | ir.cpp:126:5:126:10 | +| FloatOps(double, double) -> void | 0 | 59 | Mul | ir.cpp:126:5:126:10 | +| FloatOps(double, double) -> void | 0 | 60 | Store | ir.cpp:126:5:126:10 | +| FloatOps(double, double) -> void | 0 | 61 | VariableAddress[x] | ir.cpp:127:10:127:10 | +| FloatOps(double, double) -> void | 0 | 62 | Load | ir.cpp:127:10:127:10 | +| FloatOps(double, double) -> void | 0 | 63 | VariableAddress[z] | ir.cpp:127:5:127:5 | +| FloatOps(double, double) -> void | 0 | 64 | Load | ir.cpp:127:5:127:10 | +| FloatOps(double, double) -> void | 0 | 65 | Div | ir.cpp:127:5:127:10 | +| FloatOps(double, double) -> void | 0 | 66 | Store | ir.cpp:127:5:127:10 | +| FloatOps(double, double) -> void | 0 | 67 | VariableAddress[x] | ir.cpp:129:10:129:10 | +| FloatOps(double, double) -> void | 0 | 68 | Load | ir.cpp:129:10:129:10 | +| FloatOps(double, double) -> void | 0 | 69 | CopyValue | ir.cpp:129:9:129:10 | +| FloatOps(double, double) -> void | 0 | 70 | VariableAddress[z] | ir.cpp:129:5:129:5 | +| FloatOps(double, double) -> void | 0 | 71 | Store | ir.cpp:129:5:129:10 | +| FloatOps(double, double) -> void | 0 | 72 | VariableAddress[x] | ir.cpp:130:10:130:10 | +| FloatOps(double, double) -> void | 0 | 73 | Load | ir.cpp:130:10:130:10 | +| FloatOps(double, double) -> void | 0 | 74 | Negate | ir.cpp:130:9:130:10 | +| FloatOps(double, double) -> void | 0 | 75 | VariableAddress[z] | ir.cpp:130:5:130:5 | +| FloatOps(double, double) -> void | 0 | 76 | Store | ir.cpp:130:5:130:10 | +| FloatOps(double, double) -> void | 0 | 77 | NoOp | ir.cpp:131:1:131:1 | +| FloatOps(double, double) -> void | 0 | 78 | ReturnVoid | ir.cpp:114:6:114:13 | +| FloatOps(double, double) -> void | 0 | 79 | UnmodeledUse | ir.cpp:114:6:114:13 | +| FloatOps(double, double) -> void | 0 | 80 | ExitFunction | ir.cpp:114:6:114:13 | +| Foo() -> void | 0 | 0 | EnterFunction | ir.cpp:43:6:43:8 | +| Foo() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:43:6:43:8 | +| Foo() -> void | 0 | 2 | VariableAddress[x] | ir.cpp:44:9:44:9 | +| Foo() -> void | 0 | 3 | Constant[17] | ir.cpp:44:13:44:18 | +| Foo() -> void | 0 | 4 | Store | ir.cpp:44:13:44:18 | +| Foo() -> void | 0 | 5 | VariableAddress[y] | ir.cpp:45:11:45:11 | +| Foo() -> void | 0 | 6 | Constant[7] | ir.cpp:45:15:45:15 | +| Foo() -> void | 0 | 7 | Store | ir.cpp:45:15:45:15 | +| Foo() -> void | 0 | 8 | VariableAddress[x] | ir.cpp:46:9:46:9 | +| Foo() -> void | 0 | 9 | Load | ir.cpp:46:9:46:9 | +| Foo() -> void | 0 | 10 | VariableAddress[y] | ir.cpp:46:13:46:13 | +| Foo() -> void | 0 | 11 | Load | ir.cpp:46:13:46:13 | +| Foo() -> void | 0 | 12 | Convert | ir.cpp:46:13:46:13 | +| Foo() -> void | 0 | 13 | Add | ir.cpp:46:9:46:13 | +| Foo() -> void | 0 | 14 | Convert | ir.cpp:46:9:46:13 | +| Foo() -> void | 0 | 15 | VariableAddress[y] | ir.cpp:46:5:46:5 | +| Foo() -> void | 0 | 16 | Store | ir.cpp:46:5:46:13 | +| Foo() -> void | 0 | 17 | VariableAddress[x] | ir.cpp:47:9:47:9 | +| Foo() -> void | 0 | 18 | Load | ir.cpp:47:9:47:9 | +| Foo() -> void | 0 | 19 | VariableAddress[y] | ir.cpp:47:13:47:13 | +| Foo() -> void | 0 | 20 | Load | ir.cpp:47:13:47:13 | +| Foo() -> void | 0 | 21 | Convert | ir.cpp:47:13:47:13 | +| Foo() -> void | 0 | 22 | Mul | ir.cpp:47:9:47:13 | +| Foo() -> void | 0 | 23 | VariableAddress[x] | ir.cpp:47:5:47:5 | +| Foo() -> void | 0 | 24 | Store | ir.cpp:47:5:47:13 | +| Foo() -> void | 0 | 25 | NoOp | ir.cpp:48:1:48:1 | +| Foo() -> void | 0 | 26 | ReturnVoid | ir.cpp:43:6:43:8 | +| Foo() -> void | 0 | 27 | UnmodeledUse | ir.cpp:43:6:43:8 | +| Foo() -> void | 0 | 28 | ExitFunction | ir.cpp:43:6:43:8 | +| For_Break() -> void | 0 | 0 | EnterFunction | ir.cpp:317:6:317:14 | +| For_Break() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:317:6:317:14 | +| For_Break() -> void | 0 | 2 | VariableAddress[i] | ir.cpp:318:14:318:14 | +| For_Break() -> void | 0 | 3 | Constant[0] | ir.cpp:318:17:318:18 | +| For_Break() -> void | 0 | 4 | Store | ir.cpp:318:17:318:18 | +| For_Break() -> void | 1 | 0 | Phi | ir.cpp:318:21:318:21 | +| For_Break() -> void | 1 | 1 | VariableAddress[i] | ir.cpp:318:21:318:21 | +| For_Break() -> void | 1 | 2 | Load | ir.cpp:318:21:318:21 | +| For_Break() -> void | 1 | 3 | Constant[10] | ir.cpp:318:25:318:26 | +| For_Break() -> void | 1 | 4 | CompareLT | ir.cpp:318:21:318:26 | +| For_Break() -> void | 1 | 5 | ConditionalBranch | ir.cpp:318:21:318:26 | +| For_Break() -> void | 2 | 0 | Constant[1] | ir.cpp:318:34:318:34 | +| For_Break() -> void | 2 | 1 | VariableAddress[i] | ir.cpp:318:29:318:29 | +| For_Break() -> void | 2 | 2 | Load | ir.cpp:318:29:318:34 | +| For_Break() -> void | 2 | 3 | Add | ir.cpp:318:29:318:34 | +| For_Break() -> void | 2 | 4 | Store | ir.cpp:318:29:318:34 | +| For_Break() -> void | 3 | 0 | VariableAddress[i] | ir.cpp:319:13:319:13 | +| For_Break() -> void | 3 | 1 | Load | ir.cpp:319:13:319:13 | +| For_Break() -> void | 3 | 2 | Constant[5] | ir.cpp:319:18:319:18 | +| For_Break() -> void | 3 | 3 | CompareEQ | ir.cpp:319:13:319:18 | +| For_Break() -> void | 3 | 4 | ConditionalBranch | ir.cpp:319:13:319:18 | +| For_Break() -> void | 4 | 0 | NoOp | ir.cpp:320:13:320:18 | +| For_Break() -> void | 5 | 0 | NoOp | ir.cpp:322:5:322:5 | +| For_Break() -> void | 5 | 1 | NoOp | ir.cpp:323:1:323:1 | +| For_Break() -> void | 5 | 2 | ReturnVoid | ir.cpp:317:6:317:14 | +| For_Break() -> void | 5 | 3 | UnmodeledUse | ir.cpp:317:6:317:14 | +| For_Break() -> void | 5 | 4 | ExitFunction | ir.cpp:317:6:317:14 | +| For_Condition() -> void | 0 | 0 | EnterFunction | ir.cpp:278:6:278:18 | +| For_Condition() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:278:6:278:18 | +| For_Condition() -> void | 0 | 2 | VariableAddress[i] | ir.cpp:279:9:279:9 | +| For_Condition() -> void | 0 | 3 | Constant[0] | ir.cpp:279:12:279:13 | +| For_Condition() -> void | 0 | 4 | Store | ir.cpp:279:12:279:13 | +| For_Condition() -> void | 1 | 0 | VariableAddress[i] | ir.cpp:280:12:280:12 | +| For_Condition() -> void | 1 | 1 | Load | ir.cpp:280:12:280:12 | +| For_Condition() -> void | 1 | 2 | Constant[10] | ir.cpp:280:16:280:17 | +| For_Condition() -> void | 1 | 3 | CompareLT | ir.cpp:280:12:280:17 | +| For_Condition() -> void | 1 | 4 | ConditionalBranch | ir.cpp:280:12:280:17 | +| For_Condition() -> void | 2 | 0 | NoOp | ir.cpp:281:9:281:9 | +| For_Condition() -> void | 3 | 0 | NoOp | ir.cpp:283:1:283:1 | +| For_Condition() -> void | 3 | 1 | ReturnVoid | ir.cpp:278:6:278:18 | +| For_Condition() -> void | 3 | 2 | UnmodeledUse | ir.cpp:278:6:278:18 | +| For_Condition() -> void | 3 | 3 | ExitFunction | ir.cpp:278:6:278:18 | +| For_ConditionUpdate() -> void | 0 | 0 | EnterFunction | ir.cpp:304:6:304:24 | +| For_ConditionUpdate() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:304:6:304:24 | +| For_ConditionUpdate() -> void | 0 | 2 | VariableAddress[i] | ir.cpp:305:9:305:9 | +| For_ConditionUpdate() -> void | 0 | 3 | Constant[0] | ir.cpp:305:12:305:13 | +| For_ConditionUpdate() -> void | 0 | 4 | Store | ir.cpp:305:12:305:13 | +| For_ConditionUpdate() -> void | 1 | 0 | Phi | ir.cpp:306:12:306:12 | +| For_ConditionUpdate() -> void | 1 | 1 | VariableAddress[i] | ir.cpp:306:12:306:12 | +| For_ConditionUpdate() -> void | 1 | 2 | Load | ir.cpp:306:12:306:12 | +| For_ConditionUpdate() -> void | 1 | 3 | Constant[10] | ir.cpp:306:16:306:17 | +| For_ConditionUpdate() -> void | 1 | 4 | CompareLT | ir.cpp:306:12:306:17 | +| For_ConditionUpdate() -> void | 1 | 5 | ConditionalBranch | ir.cpp:306:12:306:17 | +| For_ConditionUpdate() -> void | 2 | 0 | NoOp | ir.cpp:307:9:307:9 | +| For_ConditionUpdate() -> void | 2 | 1 | Constant[1] | ir.cpp:306:25:306:25 | +| For_ConditionUpdate() -> void | 2 | 2 | VariableAddress[i] | ir.cpp:306:20:306:20 | +| For_ConditionUpdate() -> void | 2 | 3 | Load | ir.cpp:306:20:306:25 | +| For_ConditionUpdate() -> void | 2 | 4 | Add | ir.cpp:306:20:306:25 | +| For_ConditionUpdate() -> void | 2 | 5 | Store | ir.cpp:306:20:306:25 | +| For_ConditionUpdate() -> void | 3 | 0 | NoOp | ir.cpp:309:1:309:1 | +| For_ConditionUpdate() -> void | 3 | 1 | ReturnVoid | ir.cpp:304:6:304:24 | +| For_ConditionUpdate() -> void | 3 | 2 | UnmodeledUse | ir.cpp:304:6:304:24 | +| For_ConditionUpdate() -> void | 3 | 3 | ExitFunction | ir.cpp:304:6:304:24 | +| For_Continue_NoUpdate() -> void | 0 | 0 | EnterFunction | ir.cpp:333:6:333:26 | +| For_Continue_NoUpdate() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:333:6:333:26 | +| For_Continue_NoUpdate() -> void | 0 | 2 | VariableAddress[i] | ir.cpp:334:14:334:14 | +| For_Continue_NoUpdate() -> void | 0 | 3 | Constant[0] | ir.cpp:334:17:334:18 | +| For_Continue_NoUpdate() -> void | 0 | 4 | Store | ir.cpp:334:17:334:18 | +| For_Continue_NoUpdate() -> void | 1 | 0 | VariableAddress[i] | ir.cpp:334:21:334:21 | +| For_Continue_NoUpdate() -> void | 1 | 1 | Load | ir.cpp:334:21:334:21 | +| For_Continue_NoUpdate() -> void | 1 | 2 | Constant[10] | ir.cpp:334:25:334:26 | +| For_Continue_NoUpdate() -> void | 1 | 3 | CompareLT | ir.cpp:334:21:334:26 | +| For_Continue_NoUpdate() -> void | 1 | 4 | ConditionalBranch | ir.cpp:334:21:334:26 | +| For_Continue_NoUpdate() -> void | 2 | 0 | VariableAddress[i] | ir.cpp:335:13:335:13 | +| For_Continue_NoUpdate() -> void | 2 | 1 | Load | ir.cpp:335:13:335:13 | +| For_Continue_NoUpdate() -> void | 2 | 2 | Constant[5] | ir.cpp:335:18:335:18 | +| For_Continue_NoUpdate() -> void | 2 | 3 | CompareEQ | ir.cpp:335:13:335:18 | +| For_Continue_NoUpdate() -> void | 2 | 4 | ConditionalBranch | ir.cpp:335:13:335:18 | +| For_Continue_NoUpdate() -> void | 3 | 0 | NoOp | ir.cpp:336:13:336:21 | +| For_Continue_NoUpdate() -> void | 4 | 0 | NoOp | ir.cpp:334:5:334:5 | +| For_Continue_NoUpdate() -> void | 5 | 0 | NoOp | ir.cpp:339:1:339:1 | +| For_Continue_NoUpdate() -> void | 5 | 1 | ReturnVoid | ir.cpp:333:6:333:26 | +| For_Continue_NoUpdate() -> void | 5 | 2 | UnmodeledUse | ir.cpp:333:6:333:26 | +| For_Continue_NoUpdate() -> void | 5 | 3 | ExitFunction | ir.cpp:333:6:333:26 | +| For_Continue_Update() -> void | 0 | 0 | EnterFunction | ir.cpp:325:6:325:24 | +| For_Continue_Update() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:325:6:325:24 | +| For_Continue_Update() -> void | 0 | 2 | VariableAddress[i] | ir.cpp:326:14:326:14 | +| For_Continue_Update() -> void | 0 | 3 | Constant[0] | ir.cpp:326:17:326:18 | +| For_Continue_Update() -> void | 0 | 4 | Store | ir.cpp:326:17:326:18 | +| For_Continue_Update() -> void | 1 | 0 | Phi | ir.cpp:326:21:326:21 | +| For_Continue_Update() -> void | 1 | 1 | VariableAddress[i] | ir.cpp:326:21:326:21 | +| For_Continue_Update() -> void | 1 | 2 | Load | ir.cpp:326:21:326:21 | +| For_Continue_Update() -> void | 1 | 3 | Constant[10] | ir.cpp:326:25:326:26 | +| For_Continue_Update() -> void | 1 | 4 | CompareLT | ir.cpp:326:21:326:26 | +| For_Continue_Update() -> void | 1 | 5 | ConditionalBranch | ir.cpp:326:21:326:26 | +| For_Continue_Update() -> void | 2 | 0 | VariableAddress[i] | ir.cpp:327:13:327:13 | +| For_Continue_Update() -> void | 2 | 1 | Load | ir.cpp:327:13:327:13 | +| For_Continue_Update() -> void | 2 | 2 | Constant[5] | ir.cpp:327:18:327:18 | +| For_Continue_Update() -> void | 2 | 3 | CompareEQ | ir.cpp:327:13:327:18 | +| For_Continue_Update() -> void | 2 | 4 | ConditionalBranch | ir.cpp:327:13:327:18 | +| For_Continue_Update() -> void | 3 | 0 | NoOp | ir.cpp:328:13:328:21 | +| For_Continue_Update() -> void | 4 | 0 | NoOp | ir.cpp:326:5:326:5 | +| For_Continue_Update() -> void | 4 | 1 | Constant[1] | ir.cpp:326:34:326:34 | +| For_Continue_Update() -> void | 4 | 2 | VariableAddress[i] | ir.cpp:326:29:326:29 | +| For_Continue_Update() -> void | 4 | 3 | Load | ir.cpp:326:29:326:34 | +| For_Continue_Update() -> void | 4 | 4 | Add | ir.cpp:326:29:326:34 | +| For_Continue_Update() -> void | 4 | 5 | Store | ir.cpp:326:29:326:34 | +| For_Continue_Update() -> void | 5 | 0 | NoOp | ir.cpp:331:1:331:1 | +| For_Continue_Update() -> void | 5 | 1 | ReturnVoid | ir.cpp:325:6:325:24 | +| For_Continue_Update() -> void | 5 | 2 | UnmodeledUse | ir.cpp:325:6:325:24 | +| For_Continue_Update() -> void | 5 | 3 | ExitFunction | ir.cpp:325:6:325:24 | +| For_Empty() -> void | 0 | 0 | EnterFunction | ir.cpp:265:6:265:14 | +| For_Empty() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:265:6:265:14 | +| For_Empty() -> void | 0 | 2 | VariableAddress[j] | ir.cpp:266:9:266:9 | +| For_Empty() -> void | 0 | 3 | Uninitialized | ir.cpp:266:9:266:9 | +| For_Empty() -> void | 0 | 4 | Store | ir.cpp:266:9:266:9 | +| For_Empty() -> void | 1 | 0 | ReturnVoid | ir.cpp:265:6:265:14 | +| For_Empty() -> void | 1 | 1 | UnmodeledUse | ir.cpp:265:6:265:14 | +| For_Empty() -> void | 1 | 2 | ExitFunction | ir.cpp:265:6:265:14 | +| For_Empty() -> void | 2 | 0 | NoOp | ir.cpp:268:9:268:9 | +| For_Init() -> void | 0 | 0 | EnterFunction | ir.cpp:272:6:272:13 | +| For_Init() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:272:6:272:13 | +| For_Init() -> void | 0 | 2 | VariableAddress[i] | ir.cpp:273:14:273:14 | +| For_Init() -> void | 0 | 3 | Constant[0] | ir.cpp:273:17:273:18 | +| For_Init() -> void | 0 | 4 | Store | ir.cpp:273:17:273:18 | +| For_Init() -> void | 1 | 0 | ReturnVoid | ir.cpp:272:6:272:13 | +| For_Init() -> void | 1 | 1 | UnmodeledUse | ir.cpp:272:6:272:13 | +| For_Init() -> void | 1 | 2 | ExitFunction | ir.cpp:272:6:272:13 | +| For_Init() -> void | 2 | 0 | NoOp | ir.cpp:274:9:274:9 | +| For_InitCondition() -> void | 0 | 0 | EnterFunction | ir.cpp:292:6:292:22 | +| For_InitCondition() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:292:6:292:22 | +| For_InitCondition() -> void | 0 | 2 | VariableAddress[i] | ir.cpp:293:14:293:14 | +| For_InitCondition() -> void | 0 | 3 | Constant[0] | ir.cpp:293:17:293:18 | +| For_InitCondition() -> void | 0 | 4 | Store | ir.cpp:293:17:293:18 | +| For_InitCondition() -> void | 1 | 0 | VariableAddress[i] | ir.cpp:293:21:293:21 | +| For_InitCondition() -> void | 1 | 1 | Load | ir.cpp:293:21:293:21 | +| For_InitCondition() -> void | 1 | 2 | Constant[10] | ir.cpp:293:25:293:26 | +| For_InitCondition() -> void | 1 | 3 | CompareLT | ir.cpp:293:21:293:26 | +| For_InitCondition() -> void | 1 | 4 | ConditionalBranch | ir.cpp:293:21:293:26 | +| For_InitCondition() -> void | 2 | 0 | NoOp | ir.cpp:294:9:294:9 | +| For_InitCondition() -> void | 3 | 0 | NoOp | ir.cpp:296:1:296:1 | +| For_InitCondition() -> void | 3 | 1 | ReturnVoid | ir.cpp:292:6:292:22 | +| For_InitCondition() -> void | 3 | 2 | UnmodeledUse | ir.cpp:292:6:292:22 | +| For_InitCondition() -> void | 3 | 3 | ExitFunction | ir.cpp:292:6:292:22 | +| For_InitConditionUpdate() -> void | 0 | 0 | EnterFunction | ir.cpp:311:6:311:28 | +| For_InitConditionUpdate() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:311:6:311:28 | +| For_InitConditionUpdate() -> void | 0 | 2 | VariableAddress[i] | ir.cpp:312:14:312:14 | +| For_InitConditionUpdate() -> void | 0 | 3 | Constant[0] | ir.cpp:312:17:312:18 | +| For_InitConditionUpdate() -> void | 0 | 4 | Store | ir.cpp:312:17:312:18 | +| For_InitConditionUpdate() -> void | 1 | 0 | Phi | ir.cpp:312:21:312:21 | +| For_InitConditionUpdate() -> void | 1 | 1 | VariableAddress[i] | ir.cpp:312:21:312:21 | +| For_InitConditionUpdate() -> void | 1 | 2 | Load | ir.cpp:312:21:312:21 | +| For_InitConditionUpdate() -> void | 1 | 3 | Constant[10] | ir.cpp:312:25:312:26 | +| For_InitConditionUpdate() -> void | 1 | 4 | CompareLT | ir.cpp:312:21:312:26 | +| For_InitConditionUpdate() -> void | 1 | 5 | ConditionalBranch | ir.cpp:312:21:312:26 | +| For_InitConditionUpdate() -> void | 2 | 0 | NoOp | ir.cpp:313:9:313:9 | +| For_InitConditionUpdate() -> void | 2 | 1 | Constant[1] | ir.cpp:312:34:312:34 | +| For_InitConditionUpdate() -> void | 2 | 2 | VariableAddress[i] | ir.cpp:312:29:312:29 | +| For_InitConditionUpdate() -> void | 2 | 3 | Load | ir.cpp:312:29:312:34 | +| For_InitConditionUpdate() -> void | 2 | 4 | Add | ir.cpp:312:29:312:34 | +| For_InitConditionUpdate() -> void | 2 | 5 | Store | ir.cpp:312:29:312:34 | +| For_InitConditionUpdate() -> void | 3 | 0 | NoOp | ir.cpp:315:1:315:1 | +| For_InitConditionUpdate() -> void | 3 | 1 | ReturnVoid | ir.cpp:311:6:311:28 | +| For_InitConditionUpdate() -> void | 3 | 2 | UnmodeledUse | ir.cpp:311:6:311:28 | +| For_InitConditionUpdate() -> void | 3 | 3 | ExitFunction | ir.cpp:311:6:311:28 | +| For_InitUpdate() -> void | 0 | 0 | EnterFunction | ir.cpp:298:6:298:19 | +| For_InitUpdate() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:298:6:298:19 | +| For_InitUpdate() -> void | 0 | 2 | VariableAddress[i] | ir.cpp:299:14:299:14 | +| For_InitUpdate() -> void | 0 | 3 | Constant[0] | ir.cpp:299:17:299:18 | +| For_InitUpdate() -> void | 0 | 4 | Store | ir.cpp:299:17:299:18 | +| For_InitUpdate() -> void | 1 | 0 | ReturnVoid | ir.cpp:298:6:298:19 | +| For_InitUpdate() -> void | 1 | 1 | UnmodeledUse | ir.cpp:298:6:298:19 | +| For_InitUpdate() -> void | 1 | 2 | ExitFunction | ir.cpp:298:6:298:19 | +| For_InitUpdate() -> void | 2 | 0 | Phi | ir.cpp:300:9:300:9 | +| For_InitUpdate() -> void | 2 | 1 | NoOp | ir.cpp:300:9:300:9 | +| For_InitUpdate() -> void | 2 | 2 | Constant[1] | ir.cpp:299:27:299:27 | +| For_InitUpdate() -> void | 2 | 3 | VariableAddress[i] | ir.cpp:299:22:299:22 | +| For_InitUpdate() -> void | 2 | 4 | Load | ir.cpp:299:22:299:27 | +| For_InitUpdate() -> void | 2 | 5 | Add | ir.cpp:299:22:299:27 | +| For_InitUpdate() -> void | 2 | 6 | Store | ir.cpp:299:22:299:27 | +| For_Update() -> void | 0 | 0 | EnterFunction | ir.cpp:285:6:285:15 | +| For_Update() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:285:6:285:15 | +| For_Update() -> void | 0 | 2 | VariableAddress[i] | ir.cpp:286:9:286:9 | +| For_Update() -> void | 0 | 3 | Constant[0] | ir.cpp:286:12:286:13 | +| For_Update() -> void | 0 | 4 | Store | ir.cpp:286:12:286:13 | +| For_Update() -> void | 1 | 0 | ReturnVoid | ir.cpp:285:6:285:15 | +| For_Update() -> void | 1 | 1 | UnmodeledUse | ir.cpp:285:6:285:15 | +| For_Update() -> void | 1 | 2 | ExitFunction | ir.cpp:285:6:285:15 | +| For_Update() -> void | 2 | 0 | Phi | ir.cpp:288:9:288:9 | +| For_Update() -> void | 2 | 1 | NoOp | ir.cpp:288:9:288:9 | +| For_Update() -> void | 2 | 2 | Constant[1] | ir.cpp:287:18:287:18 | +| For_Update() -> void | 2 | 3 | VariableAddress[i] | ir.cpp:287:13:287:13 | +| For_Update() -> void | 2 | 4 | Load | ir.cpp:287:13:287:18 | +| For_Update() -> void | 2 | 5 | Add | ir.cpp:287:13:287:18 | +| For_Update() -> void | 2 | 6 | Store | ir.cpp:287:13:287:18 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 0 | EnterFunction | ir.cpp:883:6:883:23 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:883:6:883:23 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 2 | InitializeParameter[pfn] | ir.cpp:883:30:883:32 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 3 | VariableAddress[pfn] | ir.cpp:883:30:883:32 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 4 | Store | ir.cpp:883:30:883:32 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 5 | InitializeParameter[p] | ir.cpp:883:47:883:47 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 6 | VariableAddress[p] | ir.cpp:883:47:883:47 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 7 | Store | ir.cpp:883:47:883:47 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 8 | VariableAddress[pfn] | ir.cpp:884:14:884:16 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 9 | Load | ir.cpp:884:14:884:16 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 10 | Convert | ir.cpp:884:7:884:16 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 11 | VariableAddress[p] | ir.cpp:884:3:884:3 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 12 | Store | ir.cpp:884:3:884:16 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 13 | VariableAddress[p] | ir.cpp:885:22:885:22 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 14 | Load | ir.cpp:885:22:885:22 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 15 | Convert | ir.cpp:885:9:885:22 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 16 | VariableAddress[pfn] | ir.cpp:885:3:885:5 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 17 | Store | ir.cpp:885:3:885:22 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 18 | NoOp | ir.cpp:886:1:886:1 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 19 | ReturnVoid | ir.cpp:883:6:883:23 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 20 | UnmodeledUse | ir.cpp:883:6:883:23 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 21 | ExitFunction | ir.cpp:883:6:883:23 | +| FunctionReferences() -> void | 0 | 0 | EnterFunction | ir.cpp:697:6:697:23 | +| FunctionReferences() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:697:6:697:23 | +| FunctionReferences() -> void | 0 | 2 | VariableAddress[rfn] | ir.cpp:698:8:698:10 | +| FunctionReferences() -> void | 0 | 3 | FunctionAddress[FuncPtrTarget] | ir.cpp:698:20:698:32 | +| FunctionReferences() -> void | 0 | 4 | Store | ir.cpp:698:20:698:32 | +| FunctionReferences() -> void | 0 | 5 | VariableAddress[pfn] | ir.cpp:699:8:699:10 | +| FunctionReferences() -> void | 0 | 6 | VariableAddress[rfn] | ir.cpp:699:20:699:22 | +| FunctionReferences() -> void | 0 | 7 | Load | ir.cpp:699:20:699:22 | +| FunctionReferences() -> void | 0 | 8 | Store | ir.cpp:699:20:699:22 | +| FunctionReferences() -> void | 0 | 9 | VariableAddress[rfn] | ir.cpp:700:3:700:5 | +| FunctionReferences() -> void | 0 | 10 | Load | ir.cpp:700:3:700:5 | +| FunctionReferences() -> void | 0 | 11 | Constant[5] | ir.cpp:700:7:700:7 | +| FunctionReferences() -> void | 0 | 12 | Invoke | ir.cpp:700:3:700:8 | +| FunctionReferences() -> void | 0 | 13 | NoOp | ir.cpp:701:1:701:1 | +| FunctionReferences() -> void | 0 | 14 | ReturnVoid | ir.cpp:697:6:697:23 | +| FunctionReferences() -> void | 0 | 15 | UnmodeledUse | ir.cpp:697:6:697:23 | +| FunctionReferences() -> void | 0 | 16 | ExitFunction | ir.cpp:697:6:697:23 | +| HierarchyConversions() -> void | 0 | 0 | EnterFunction | ir.cpp:799:6:799:25 | +| HierarchyConversions() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:799:6:799:25 | +| HierarchyConversions() -> void | 0 | 2 | VariableAddress[b] | ir.cpp:800:8:800:8 | +| HierarchyConversions() -> void | 0 | 3 | FunctionAddress[Base] | ir.cpp:800:8:800:8 | +| HierarchyConversions() -> void | 0 | 4 | Invoke | ir.cpp:800:8:800:8 | +| HierarchyConversions() -> void | 0 | 5 | VariableAddress[m] | ir.cpp:801:10:801:10 | +| HierarchyConversions() -> void | 0 | 6 | FunctionAddress[Middle] | ir.cpp:801:10:801:10 | +| HierarchyConversions() -> void | 0 | 7 | Invoke | ir.cpp:801:10:801:10 | +| HierarchyConversions() -> void | 0 | 8 | VariableAddress[d] | ir.cpp:802:11:802:11 | +| HierarchyConversions() -> void | 0 | 9 | FunctionAddress[Derived] | ir.cpp:802:11:802:11 | +| HierarchyConversions() -> void | 0 | 10 | Invoke | ir.cpp:802:11:802:11 | +| HierarchyConversions() -> void | 0 | 11 | VariableAddress[pb] | ir.cpp:804:9:804:10 | +| HierarchyConversions() -> void | 0 | 12 | VariableAddress[b] | ir.cpp:804:15:804:15 | +| HierarchyConversions() -> void | 0 | 13 | Store | ir.cpp:804:14:804:15 | +| HierarchyConversions() -> void | 0 | 14 | VariableAddress[pm] | ir.cpp:805:11:805:12 | +| HierarchyConversions() -> void | 0 | 15 | VariableAddress[m] | ir.cpp:805:17:805:17 | +| HierarchyConversions() -> void | 0 | 16 | Store | ir.cpp:805:16:805:17 | +| HierarchyConversions() -> void | 0 | 17 | VariableAddress[pd] | ir.cpp:806:12:806:13 | +| HierarchyConversions() -> void | 0 | 18 | VariableAddress[d] | ir.cpp:806:18:806:18 | +| HierarchyConversions() -> void | 0 | 19 | Store | ir.cpp:806:17:806:18 | +| HierarchyConversions() -> void | 0 | 20 | VariableAddress[b] | ir.cpp:808:3:808:3 | +| HierarchyConversions() -> void | 0 | 21 | FunctionAddress[operator=] | ir.cpp:808:5:808:5 | +| HierarchyConversions() -> void | 0 | 22 | VariableAddress[m] | ir.cpp:808:7:808:7 | +| HierarchyConversions() -> void | 0 | 23 | ConvertToBase[Middle : Base] | ir.cpp:808:7:808:7 | +| HierarchyConversions() -> void | 0 | 24 | Invoke | ir.cpp:808:5:808:5 | +| HierarchyConversions() -> void | 0 | 25 | VariableAddress[b] | ir.cpp:809:3:809:3 | +| HierarchyConversions() -> void | 0 | 26 | FunctionAddress[operator=] | ir.cpp:809:5:809:5 | +| HierarchyConversions() -> void | 0 | 27 | FunctionAddress[Base] | ir.cpp:809:7:809:13 | +| HierarchyConversions() -> void | 0 | 28 | VariableAddress[m] | ir.cpp:809:13:809:13 | +| HierarchyConversions() -> void | 0 | 29 | ConvertToBase[Middle : Base] | ir.cpp:809:13:809:13 | +| HierarchyConversions() -> void | 0 | 30 | Invoke | ir.cpp:809:7:809:13 | +| HierarchyConversions() -> void | 0 | 31 | Convert | ir.cpp:809:7:809:13 | +| HierarchyConversions() -> void | 0 | 32 | Invoke | ir.cpp:809:5:809:5 | +| HierarchyConversions() -> void | 0 | 33 | VariableAddress[b] | ir.cpp:810:3:810:3 | +| HierarchyConversions() -> void | 0 | 34 | FunctionAddress[operator=] | ir.cpp:810:5:810:5 | +| HierarchyConversions() -> void | 0 | 35 | FunctionAddress[Base] | ir.cpp:810:7:810:26 | +| HierarchyConversions() -> void | 0 | 36 | VariableAddress[m] | ir.cpp:810:25:810:25 | +| HierarchyConversions() -> void | 0 | 37 | ConvertToBase[Middle : Base] | ir.cpp:810:25:810:25 | +| HierarchyConversions() -> void | 0 | 38 | Invoke | ir.cpp:810:7:810:26 | +| HierarchyConversions() -> void | 0 | 39 | Convert | ir.cpp:810:7:810:26 | +| HierarchyConversions() -> void | 0 | 40 | Invoke | ir.cpp:810:5:810:5 | +| HierarchyConversions() -> void | 0 | 41 | VariableAddress[pm] | ir.cpp:811:8:811:9 | +| HierarchyConversions() -> void | 0 | 42 | Load | ir.cpp:811:8:811:9 | +| HierarchyConversions() -> void | 0 | 43 | ConvertToBase[Middle : Base] | ir.cpp:811:8:811:9 | +| HierarchyConversions() -> void | 0 | 44 | VariableAddress[pb] | ir.cpp:811:3:811:4 | +| HierarchyConversions() -> void | 0 | 45 | Store | ir.cpp:811:3:811:9 | +| HierarchyConversions() -> void | 0 | 46 | VariableAddress[pm] | ir.cpp:812:15:812:16 | +| HierarchyConversions() -> void | 0 | 47 | Load | ir.cpp:812:15:812:16 | +| HierarchyConversions() -> void | 0 | 48 | ConvertToBase[Middle : Base] | ir.cpp:812:8:812:16 | +| HierarchyConversions() -> void | 0 | 49 | VariableAddress[pb] | ir.cpp:812:3:812:4 | +| HierarchyConversions() -> void | 0 | 50 | Store | ir.cpp:812:3:812:16 | +| HierarchyConversions() -> void | 0 | 51 | VariableAddress[pm] | ir.cpp:813:27:813:28 | +| HierarchyConversions() -> void | 0 | 52 | Load | ir.cpp:813:27:813:28 | +| HierarchyConversions() -> void | 0 | 53 | ConvertToBase[Middle : Base] | ir.cpp:813:8:813:29 | +| HierarchyConversions() -> void | 0 | 54 | VariableAddress[pb] | ir.cpp:813:3:813:4 | +| HierarchyConversions() -> void | 0 | 55 | Store | ir.cpp:813:3:813:29 | +| HierarchyConversions() -> void | 0 | 56 | VariableAddress[pm] | ir.cpp:814:32:814:33 | +| HierarchyConversions() -> void | 0 | 57 | Load | ir.cpp:814:32:814:33 | +| HierarchyConversions() -> void | 0 | 58 | Convert | ir.cpp:814:8:814:34 | +| HierarchyConversions() -> void | 0 | 59 | VariableAddress[pb] | ir.cpp:814:3:814:4 | +| HierarchyConversions() -> void | 0 | 60 | Store | ir.cpp:814:3:814:34 | +| HierarchyConversions() -> void | 0 | 61 | VariableAddress[m] | ir.cpp:816:3:816:3 | +| HierarchyConversions() -> void | 0 | 62 | FunctionAddress[operator=] | ir.cpp:816:5:816:5 | +| HierarchyConversions() -> void | 0 | 63 | VariableAddress[b] | ir.cpp:816:16:816:16 | +| HierarchyConversions() -> void | 0 | 64 | ConvertToDerived[Middle : Base] | ir.cpp:816:7:816:16 | +| HierarchyConversions() -> void | 0 | 65 | Convert | ir.cpp:816:7:816:16 | +| HierarchyConversions() -> void | 0 | 66 | Invoke | ir.cpp:816:5:816:5 | +| HierarchyConversions() -> void | 0 | 67 | VariableAddress[m] | ir.cpp:817:3:817:3 | +| HierarchyConversions() -> void | 0 | 68 | FunctionAddress[operator=] | ir.cpp:817:5:817:5 | +| HierarchyConversions() -> void | 0 | 69 | VariableAddress[b] | ir.cpp:817:28:817:28 | +| HierarchyConversions() -> void | 0 | 70 | ConvertToDerived[Middle : Base] | ir.cpp:817:7:817:29 | +| HierarchyConversions() -> void | 0 | 71 | Convert | ir.cpp:817:7:817:29 | +| HierarchyConversions() -> void | 0 | 72 | Invoke | ir.cpp:817:5:817:5 | +| HierarchyConversions() -> void | 0 | 73 | VariableAddress[pb] | ir.cpp:818:17:818:18 | +| HierarchyConversions() -> void | 0 | 74 | Load | ir.cpp:818:17:818:18 | +| HierarchyConversions() -> void | 0 | 75 | ConvertToDerived[Middle : Base] | ir.cpp:818:8:818:18 | +| HierarchyConversions() -> void | 0 | 76 | VariableAddress[pm] | ir.cpp:818:3:818:4 | +| HierarchyConversions() -> void | 0 | 77 | Store | ir.cpp:818:3:818:18 | +| HierarchyConversions() -> void | 0 | 78 | VariableAddress[pb] | ir.cpp:819:29:819:30 | +| HierarchyConversions() -> void | 0 | 79 | Load | ir.cpp:819:29:819:30 | +| HierarchyConversions() -> void | 0 | 80 | ConvertToDerived[Middle : Base] | ir.cpp:819:8:819:31 | +| HierarchyConversions() -> void | 0 | 81 | VariableAddress[pm] | ir.cpp:819:3:819:4 | +| HierarchyConversions() -> void | 0 | 82 | Store | ir.cpp:819:3:819:31 | +| HierarchyConversions() -> void | 0 | 83 | VariableAddress[pb] | ir.cpp:820:34:820:35 | +| HierarchyConversions() -> void | 0 | 84 | Load | ir.cpp:820:34:820:35 | +| HierarchyConversions() -> void | 0 | 85 | Convert | ir.cpp:820:8:820:36 | +| HierarchyConversions() -> void | 0 | 86 | VariableAddress[pm] | ir.cpp:820:3:820:4 | +| HierarchyConversions() -> void | 0 | 87 | Store | ir.cpp:820:3:820:36 | +| HierarchyConversions() -> void | 0 | 88 | VariableAddress[b] | ir.cpp:822:3:822:3 | +| HierarchyConversions() -> void | 0 | 89 | FunctionAddress[operator=] | ir.cpp:822:5:822:5 | +| HierarchyConversions() -> void | 0 | 90 | VariableAddress[d] | ir.cpp:822:7:822:7 | +| HierarchyConversions() -> void | 0 | 91 | ConvertToBase[Derived : Middle] | ir.cpp:822:7:822:7 | +| HierarchyConversions() -> void | 0 | 92 | ConvertToBase[Middle : Base] | ir.cpp:822:7:822:7 | +| HierarchyConversions() -> void | 0 | 93 | Invoke | ir.cpp:822:5:822:5 | +| HierarchyConversions() -> void | 0 | 94 | VariableAddress[b] | ir.cpp:823:3:823:3 | +| HierarchyConversions() -> void | 0 | 95 | FunctionAddress[operator=] | ir.cpp:823:5:823:5 | +| HierarchyConversions() -> void | 0 | 96 | FunctionAddress[Base] | ir.cpp:823:7:823:13 | +| HierarchyConversions() -> void | 0 | 97 | VariableAddress[d] | ir.cpp:823:13:823:13 | +| HierarchyConversions() -> void | 0 | 98 | ConvertToBase[Derived : Middle] | ir.cpp:823:13:823:13 | +| HierarchyConversions() -> void | 0 | 99 | ConvertToBase[Middle : Base] | ir.cpp:823:13:823:13 | +| HierarchyConversions() -> void | 0 | 100 | Invoke | ir.cpp:823:7:823:13 | +| HierarchyConversions() -> void | 0 | 101 | Convert | ir.cpp:823:7:823:13 | +| HierarchyConversions() -> void | 0 | 102 | Invoke | ir.cpp:823:5:823:5 | +| HierarchyConversions() -> void | 0 | 103 | VariableAddress[b] | ir.cpp:824:3:824:3 | +| HierarchyConversions() -> void | 0 | 104 | FunctionAddress[operator=] | ir.cpp:824:5:824:5 | +| HierarchyConversions() -> void | 0 | 105 | FunctionAddress[Base] | ir.cpp:824:7:824:26 | +| HierarchyConversions() -> void | 0 | 106 | VariableAddress[d] | ir.cpp:824:25:824:25 | +| HierarchyConversions() -> void | 0 | 107 | ConvertToBase[Derived : Middle] | ir.cpp:824:25:824:25 | +| HierarchyConversions() -> void | 0 | 108 | ConvertToBase[Middle : Base] | ir.cpp:824:25:824:25 | +| HierarchyConversions() -> void | 0 | 109 | Invoke | ir.cpp:824:7:824:26 | +| HierarchyConversions() -> void | 0 | 110 | Convert | ir.cpp:824:7:824:26 | +| HierarchyConversions() -> void | 0 | 111 | Invoke | ir.cpp:824:5:824:5 | +| HierarchyConversions() -> void | 0 | 112 | VariableAddress[pd] | ir.cpp:825:8:825:9 | +| HierarchyConversions() -> void | 0 | 113 | Load | ir.cpp:825:8:825:9 | +| HierarchyConversions() -> void | 0 | 114 | ConvertToBase[Derived : Middle] | ir.cpp:825:8:825:9 | +| HierarchyConversions() -> void | 0 | 115 | ConvertToBase[Middle : Base] | ir.cpp:825:8:825:9 | +| HierarchyConversions() -> void | 0 | 116 | VariableAddress[pb] | ir.cpp:825:3:825:4 | +| HierarchyConversions() -> void | 0 | 117 | Store | ir.cpp:825:3:825:9 | +| HierarchyConversions() -> void | 0 | 118 | VariableAddress[pd] | ir.cpp:826:15:826:16 | +| HierarchyConversions() -> void | 0 | 119 | Load | ir.cpp:826:15:826:16 | +| HierarchyConversions() -> void | 0 | 120 | ConvertToBase[Derived : Middle] | ir.cpp:826:15:826:16 | +| HierarchyConversions() -> void | 0 | 121 | ConvertToBase[Middle : Base] | ir.cpp:826:8:826:16 | +| HierarchyConversions() -> void | 0 | 122 | VariableAddress[pb] | ir.cpp:826:3:826:4 | +| HierarchyConversions() -> void | 0 | 123 | Store | ir.cpp:826:3:826:16 | +| HierarchyConversions() -> void | 0 | 124 | VariableAddress[pd] | ir.cpp:827:27:827:28 | +| HierarchyConversions() -> void | 0 | 125 | Load | ir.cpp:827:27:827:28 | +| HierarchyConversions() -> void | 0 | 126 | ConvertToBase[Derived : Middle] | ir.cpp:827:27:827:28 | +| HierarchyConversions() -> void | 0 | 127 | ConvertToBase[Middle : Base] | ir.cpp:827:8:827:29 | +| HierarchyConversions() -> void | 0 | 128 | VariableAddress[pb] | ir.cpp:827:3:827:4 | +| HierarchyConversions() -> void | 0 | 129 | Store | ir.cpp:827:3:827:29 | +| HierarchyConversions() -> void | 0 | 130 | VariableAddress[pd] | ir.cpp:828:32:828:33 | +| HierarchyConversions() -> void | 0 | 131 | Load | ir.cpp:828:32:828:33 | +| HierarchyConversions() -> void | 0 | 132 | Convert | ir.cpp:828:8:828:34 | +| HierarchyConversions() -> void | 0 | 133 | VariableAddress[pb] | ir.cpp:828:3:828:4 | +| HierarchyConversions() -> void | 0 | 134 | Store | ir.cpp:828:3:828:34 | +| HierarchyConversions() -> void | 0 | 135 | VariableAddress[d] | ir.cpp:830:3:830:3 | +| HierarchyConversions() -> void | 0 | 136 | FunctionAddress[operator=] | ir.cpp:830:5:830:5 | +| HierarchyConversions() -> void | 0 | 137 | VariableAddress[b] | ir.cpp:830:17:830:17 | +| HierarchyConversions() -> void | 0 | 138 | ConvertToDerived[Middle : Base] | ir.cpp:830:17:830:17 | +| HierarchyConversions() -> void | 0 | 139 | ConvertToDerived[Derived : Middle] | ir.cpp:830:7:830:17 | +| HierarchyConversions() -> void | 0 | 140 | Convert | ir.cpp:830:7:830:17 | +| HierarchyConversions() -> void | 0 | 141 | Invoke | ir.cpp:830:5:830:5 | +| HierarchyConversions() -> void | 0 | 142 | VariableAddress[d] | ir.cpp:831:3:831:3 | +| HierarchyConversions() -> void | 0 | 143 | FunctionAddress[operator=] | ir.cpp:831:5:831:5 | +| HierarchyConversions() -> void | 0 | 144 | VariableAddress[b] | ir.cpp:831:29:831:29 | +| HierarchyConversions() -> void | 0 | 145 | ConvertToDerived[Middle : Base] | ir.cpp:831:29:831:29 | +| HierarchyConversions() -> void | 0 | 146 | ConvertToDerived[Derived : Middle] | ir.cpp:831:7:831:30 | +| HierarchyConversions() -> void | 0 | 147 | Convert | ir.cpp:831:7:831:30 | +| HierarchyConversions() -> void | 0 | 148 | Invoke | ir.cpp:831:5:831:5 | +| HierarchyConversions() -> void | 0 | 149 | VariableAddress[pb] | ir.cpp:832:18:832:19 | +| HierarchyConversions() -> void | 0 | 150 | Load | ir.cpp:832:18:832:19 | +| HierarchyConversions() -> void | 0 | 151 | ConvertToDerived[Middle : Base] | ir.cpp:832:18:832:19 | +| HierarchyConversions() -> void | 0 | 152 | ConvertToDerived[Derived : Middle] | ir.cpp:832:8:832:19 | +| HierarchyConversions() -> void | 0 | 153 | VariableAddress[pd] | ir.cpp:832:3:832:4 | +| HierarchyConversions() -> void | 0 | 154 | Store | ir.cpp:832:3:832:19 | +| HierarchyConversions() -> void | 0 | 155 | VariableAddress[pb] | ir.cpp:833:30:833:31 | +| HierarchyConversions() -> void | 0 | 156 | Load | ir.cpp:833:30:833:31 | +| HierarchyConversions() -> void | 0 | 157 | ConvertToDerived[Middle : Base] | ir.cpp:833:30:833:31 | +| HierarchyConversions() -> void | 0 | 158 | ConvertToDerived[Derived : Middle] | ir.cpp:833:8:833:32 | +| HierarchyConversions() -> void | 0 | 159 | VariableAddress[pd] | ir.cpp:833:3:833:4 | +| HierarchyConversions() -> void | 0 | 160 | Store | ir.cpp:833:3:833:32 | +| HierarchyConversions() -> void | 0 | 161 | VariableAddress[pb] | ir.cpp:834:35:834:36 | +| HierarchyConversions() -> void | 0 | 162 | Load | ir.cpp:834:35:834:36 | +| HierarchyConversions() -> void | 0 | 163 | Convert | ir.cpp:834:8:834:37 | +| HierarchyConversions() -> void | 0 | 164 | VariableAddress[pd] | ir.cpp:834:3:834:4 | +| HierarchyConversions() -> void | 0 | 165 | Store | ir.cpp:834:3:834:37 | +| HierarchyConversions() -> void | 0 | 166 | VariableAddress[pmv] | ir.cpp:836:14:836:16 | +| HierarchyConversions() -> void | 0 | 167 | Constant[0] | ir.cpp:836:20:836:26 | +| HierarchyConversions() -> void | 0 | 168 | Store | ir.cpp:836:20:836:26 | +| HierarchyConversions() -> void | 0 | 169 | VariableAddress[pdv] | ir.cpp:837:14:837:16 | +| HierarchyConversions() -> void | 0 | 170 | Constant[0] | ir.cpp:837:20:837:26 | +| HierarchyConversions() -> void | 0 | 171 | Store | ir.cpp:837:20:837:26 | +| HierarchyConversions() -> void | 0 | 172 | VariableAddress[pmv] | ir.cpp:838:8:838:10 | +| HierarchyConversions() -> void | 0 | 173 | Load | ir.cpp:838:8:838:10 | +| HierarchyConversions() -> void | 0 | 174 | ConvertToVirtualBase[MiddleVB1 : Base] | ir.cpp:838:8:838:10 | +| HierarchyConversions() -> void | 0 | 175 | VariableAddress[pb] | ir.cpp:838:3:838:4 | +| HierarchyConversions() -> void | 0 | 176 | Store | ir.cpp:838:3:838:10 | +| HierarchyConversions() -> void | 0 | 177 | VariableAddress[pdv] | ir.cpp:839:8:839:10 | +| HierarchyConversions() -> void | 0 | 178 | Load | ir.cpp:839:8:839:10 | +| HierarchyConversions() -> void | 0 | 179 | ConvertToVirtualBase[DerivedVB : Base] | ir.cpp:839:8:839:10 | +| HierarchyConversions() -> void | 0 | 180 | VariableAddress[pb] | ir.cpp:839:3:839:4 | +| HierarchyConversions() -> void | 0 | 181 | Store | ir.cpp:839:3:839:10 | +| HierarchyConversions() -> void | 0 | 182 | NoOp | ir.cpp:840:1:840:1 | +| HierarchyConversions() -> void | 0 | 183 | ReturnVoid | ir.cpp:799:6:799:25 | +| HierarchyConversions() -> void | 0 | 184 | UnmodeledUse | ir.cpp:799:6:799:25 | +| HierarchyConversions() -> void | 0 | 185 | ExitFunction | ir.cpp:799:6:799:25 | +| IfStatements(bool, int, int) -> void | 0 | 0 | EnterFunction | ir.cpp:239:6:239:17 | +| IfStatements(bool, int, int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:239:6:239:17 | +| IfStatements(bool, int, int) -> void | 0 | 2 | InitializeParameter[b] | ir.cpp:239:24:239:24 | +| IfStatements(bool, int, int) -> void | 0 | 3 | VariableAddress[b] | ir.cpp:239:24:239:24 | +| IfStatements(bool, int, int) -> void | 0 | 4 | Store | ir.cpp:239:24:239:24 | +| IfStatements(bool, int, int) -> void | 0 | 5 | InitializeParameter[x] | ir.cpp:239:31:239:31 | +| IfStatements(bool, int, int) -> void | 0 | 6 | VariableAddress[x] | ir.cpp:239:31:239:31 | +| IfStatements(bool, int, int) -> void | 0 | 7 | Store | ir.cpp:239:31:239:31 | +| IfStatements(bool, int, int) -> void | 0 | 8 | InitializeParameter[y] | ir.cpp:239:38:239:38 | +| IfStatements(bool, int, int) -> void | 0 | 9 | VariableAddress[y] | ir.cpp:239:38:239:38 | +| IfStatements(bool, int, int) -> void | 0 | 10 | Store | ir.cpp:239:38:239:38 | +| IfStatements(bool, int, int) -> void | 0 | 11 | VariableAddress[b] | ir.cpp:240:9:240:9 | +| IfStatements(bool, int, int) -> void | 0 | 12 | Load | ir.cpp:240:9:240:9 | +| IfStatements(bool, int, int) -> void | 0 | 13 | ConditionalBranch | ir.cpp:240:9:240:9 | +| IfStatements(bool, int, int) -> void | 1 | 0 | VariableAddress[b] | ir.cpp:243:9:243:9 | +| IfStatements(bool, int, int) -> void | 1 | 1 | Load | ir.cpp:243:9:243:9 | +| IfStatements(bool, int, int) -> void | 1 | 2 | ConditionalBranch | ir.cpp:243:9:243:9 | +| IfStatements(bool, int, int) -> void | 2 | 0 | VariableAddress[y] | ir.cpp:244:13:244:13 | +| IfStatements(bool, int, int) -> void | 2 | 1 | Load | ir.cpp:244:13:244:13 | +| IfStatements(bool, int, int) -> void | 2 | 2 | VariableAddress[x] | ir.cpp:244:9:244:9 | +| IfStatements(bool, int, int) -> void | 2 | 3 | Store | ir.cpp:244:9:244:13 | +| IfStatements(bool, int, int) -> void | 3 | 0 | Phi | ir.cpp:247:9:247:9 | +| IfStatements(bool, int, int) -> void | 3 | 1 | VariableAddress[x] | ir.cpp:247:9:247:9 | +| IfStatements(bool, int, int) -> void | 3 | 2 | Load | ir.cpp:247:9:247:9 | +| IfStatements(bool, int, int) -> void | 3 | 3 | Constant[7] | ir.cpp:247:13:247:13 | +| IfStatements(bool, int, int) -> void | 3 | 4 | CompareLT | ir.cpp:247:9:247:13 | +| IfStatements(bool, int, int) -> void | 3 | 5 | ConditionalBranch | ir.cpp:247:9:247:13 | +| IfStatements(bool, int, int) -> void | 4 | 0 | Constant[2] | ir.cpp:248:13:248:13 | +| IfStatements(bool, int, int) -> void | 4 | 1 | VariableAddress[x] | ir.cpp:248:9:248:9 | +| IfStatements(bool, int, int) -> void | 4 | 2 | Store | ir.cpp:248:9:248:13 | +| IfStatements(bool, int, int) -> void | 5 | 0 | Constant[7] | ir.cpp:250:13:250:13 | +| IfStatements(bool, int, int) -> void | 5 | 1 | VariableAddress[x] | ir.cpp:250:9:250:9 | +| IfStatements(bool, int, int) -> void | 5 | 2 | Store | ir.cpp:250:9:250:13 | +| IfStatements(bool, int, int) -> void | 6 | 0 | NoOp | ir.cpp:251:1:251:1 | +| IfStatements(bool, int, int) -> void | 6 | 1 | ReturnVoid | ir.cpp:239:6:239:17 | +| IfStatements(bool, int, int) -> void | 6 | 2 | UnmodeledUse | ir.cpp:239:6:239:17 | +| IfStatements(bool, int, int) -> void | 6 | 3 | ExitFunction | ir.cpp:239:6:239:17 | +| IfStatements(bool, int, int) -> void | 7 | 0 | NoOp | ir.cpp:240:12:241:5 | +| InitArray() -> void | 0 | 0 | EnterFunction | ir.cpp:571:6:571:14 | +| InitArray() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:571:6:571:14 | +| InitArray() -> void | 0 | 2 | VariableAddress[a_pad] | ir.cpp:572:10:572:14 | +| InitArray() -> void | 0 | 3 | StringConstant[""] | ir.cpp:572:22:572:23 | +| InitArray() -> void | 0 | 4 | Load | ir.cpp:572:22:572:23 | +| InitArray() -> void | 0 | 5 | Store | ir.cpp:572:22:572:23 | +| InitArray() -> void | 0 | 6 | Constant[0] | ir.cpp:572:22:572:23 | +| InitArray() -> void | 0 | 7 | Constant[1] | ir.cpp:572:22:572:23 | +| InitArray() -> void | 0 | 8 | PointerAdd | ir.cpp:572:22:572:23 | +| InitArray() -> void | 0 | 9 | Store | ir.cpp:572:22:572:23 | +| InitArray() -> void | 0 | 10 | VariableAddress[a_nopad] | ir.cpp:573:10:573:16 | +| InitArray() -> void | 0 | 11 | StringConstant["foo"] | ir.cpp:573:23:573:27 | +| InitArray() -> void | 0 | 12 | Load | ir.cpp:573:23:573:27 | +| InitArray() -> void | 0 | 13 | Store | ir.cpp:573:23:573:27 | +| InitArray() -> void | 0 | 14 | VariableAddress[a_infer] | ir.cpp:574:10:574:16 | +| InitArray() -> void | 0 | 15 | StringConstant["blah"] | ir.cpp:574:22:574:27 | +| InitArray() -> void | 0 | 16 | Load | ir.cpp:574:22:574:27 | +| InitArray() -> void | 0 | 17 | Store | ir.cpp:574:22:574:27 | +| InitArray() -> void | 0 | 18 | VariableAddress[b] | ir.cpp:575:10:575:10 | +| InitArray() -> void | 0 | 19 | Uninitialized | ir.cpp:575:10:575:10 | +| InitArray() -> void | 0 | 20 | Store | ir.cpp:575:10:575:10 | +| InitArray() -> void | 0 | 21 | VariableAddress[c] | ir.cpp:576:10:576:10 | +| InitArray() -> void | 0 | 22 | Constant[0] | ir.cpp:576:16:576:18 | +| InitArray() -> void | 0 | 23 | PointerAdd | ir.cpp:576:16:576:18 | +| InitArray() -> void | 0 | 24 | Constant[0] | ir.cpp:576:16:576:18 | +| InitArray() -> void | 0 | 25 | Store | ir.cpp:576:16:576:18 | +| InitArray() -> void | 0 | 26 | VariableAddress[d] | ir.cpp:577:10:577:10 | +| InitArray() -> void | 0 | 27 | Constant[0] | ir.cpp:577:16:577:21 | +| InitArray() -> void | 0 | 28 | PointerAdd | ir.cpp:577:16:577:21 | +| InitArray() -> void | 0 | 29 | Constant[0] | ir.cpp:577:19:577:19 | +| InitArray() -> void | 0 | 30 | Store | ir.cpp:577:19:577:19 | +| InitArray() -> void | 0 | 31 | Constant[1] | ir.cpp:577:16:577:21 | +| InitArray() -> void | 0 | 32 | PointerAdd | ir.cpp:577:16:577:21 | +| InitArray() -> void | 0 | 33 | Constant[0] | ir.cpp:577:16:577:21 | +| InitArray() -> void | 0 | 34 | Store | ir.cpp:577:16:577:21 | +| InitArray() -> void | 0 | 35 | VariableAddress[e] | ir.cpp:578:10:578:10 | +| InitArray() -> void | 0 | 36 | Constant[0] | ir.cpp:578:16:578:24 | +| InitArray() -> void | 0 | 37 | PointerAdd | ir.cpp:578:16:578:24 | +| InitArray() -> void | 0 | 38 | Constant[0] | ir.cpp:578:19:578:19 | +| InitArray() -> void | 0 | 39 | Store | ir.cpp:578:19:578:19 | +| InitArray() -> void | 0 | 40 | Constant[1] | ir.cpp:578:16:578:24 | +| InitArray() -> void | 0 | 41 | PointerAdd | ir.cpp:578:16:578:24 | +| InitArray() -> void | 0 | 42 | Constant[1] | ir.cpp:578:22:578:22 | +| InitArray() -> void | 0 | 43 | Store | ir.cpp:578:22:578:22 | +| InitArray() -> void | 0 | 44 | VariableAddress[f] | ir.cpp:579:10:579:10 | +| InitArray() -> void | 0 | 45 | Constant[0] | ir.cpp:579:16:579:21 | +| InitArray() -> void | 0 | 46 | PointerAdd | ir.cpp:579:16:579:21 | +| InitArray() -> void | 0 | 47 | Constant[0] | ir.cpp:579:19:579:19 | +| InitArray() -> void | 0 | 48 | Store | ir.cpp:579:19:579:19 | +| InitArray() -> void | 0 | 49 | Constant[1] | ir.cpp:579:16:579:21 | +| InitArray() -> void | 0 | 50 | PointerAdd | ir.cpp:579:16:579:21 | +| InitArray() -> void | 0 | 51 | Constant[0] | ir.cpp:579:16:579:21 | +| InitArray() -> void | 0 | 52 | Store | ir.cpp:579:16:579:21 | +| InitArray() -> void | 0 | 53 | NoOp | ir.cpp:580:1:580:1 | +| InitArray() -> void | 0 | 54 | ReturnVoid | ir.cpp:571:6:571:14 | +| InitArray() -> void | 0 | 55 | UnmodeledUse | ir.cpp:571:6:571:14 | +| InitArray() -> void | 0 | 56 | ExitFunction | ir.cpp:571:6:571:14 | +| InitList(int, float) -> void | 0 | 0 | EnterFunction | ir.cpp:503:6:503:13 | +| InitList(int, float) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:503:6:503:13 | +| InitList(int, float) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:503:19:503:19 | +| InitList(int, float) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:503:19:503:19 | +| InitList(int, float) -> void | 0 | 4 | Store | ir.cpp:503:19:503:19 | +| InitList(int, float) -> void | 0 | 5 | InitializeParameter[f] | ir.cpp:503:28:503:28 | +| InitList(int, float) -> void | 0 | 6 | VariableAddress[f] | ir.cpp:503:28:503:28 | +| InitList(int, float) -> void | 0 | 7 | Store | ir.cpp:503:28:503:28 | +| InitList(int, float) -> void | 0 | 8 | VariableAddress[pt1] | ir.cpp:504:11:504:13 | +| InitList(int, float) -> void | 0 | 9 | FieldAddress[x] | ir.cpp:504:16:504:24 | +| InitList(int, float) -> void | 0 | 10 | VariableAddress[x] | ir.cpp:504:19:504:19 | +| InitList(int, float) -> void | 0 | 11 | Load | ir.cpp:504:19:504:19 | +| InitList(int, float) -> void | 0 | 12 | Store | ir.cpp:504:19:504:19 | +| InitList(int, float) -> void | 0 | 13 | FieldAddress[y] | ir.cpp:504:16:504:24 | +| InitList(int, float) -> void | 0 | 14 | VariableAddress[f] | ir.cpp:504:22:504:22 | +| InitList(int, float) -> void | 0 | 15 | Load | ir.cpp:504:22:504:22 | +| InitList(int, float) -> void | 0 | 16 | Convert | ir.cpp:504:22:504:22 | +| InitList(int, float) -> void | 0 | 17 | Store | ir.cpp:504:22:504:22 | +| InitList(int, float) -> void | 0 | 18 | VariableAddress[pt2] | ir.cpp:505:11:505:13 | +| InitList(int, float) -> void | 0 | 19 | FieldAddress[x] | ir.cpp:505:16:505:21 | +| InitList(int, float) -> void | 0 | 20 | VariableAddress[x] | ir.cpp:505:19:505:19 | +| InitList(int, float) -> void | 0 | 21 | Load | ir.cpp:505:19:505:19 | +| InitList(int, float) -> void | 0 | 22 | Store | ir.cpp:505:19:505:19 | +| InitList(int, float) -> void | 0 | 23 | FieldAddress[y] | ir.cpp:505:16:505:21 | +| InitList(int, float) -> void | 0 | 24 | Constant[0] | ir.cpp:505:16:505:21 | +| InitList(int, float) -> void | 0 | 25 | Store | ir.cpp:505:16:505:21 | +| InitList(int, float) -> void | 0 | 26 | VariableAddress[pt3] | ir.cpp:506:11:506:13 | +| InitList(int, float) -> void | 0 | 27 | FieldAddress[x] | ir.cpp:506:16:506:18 | +| InitList(int, float) -> void | 0 | 28 | Constant[0] | ir.cpp:506:16:506:18 | +| InitList(int, float) -> void | 0 | 29 | Store | ir.cpp:506:16:506:18 | +| InitList(int, float) -> void | 0 | 30 | FieldAddress[y] | ir.cpp:506:16:506:18 | +| InitList(int, float) -> void | 0 | 31 | Constant[0] | ir.cpp:506:16:506:18 | +| InitList(int, float) -> void | 0 | 32 | Store | ir.cpp:506:16:506:18 | +| InitList(int, float) -> void | 0 | 33 | VariableAddress[x1] | ir.cpp:508:9:508:10 | +| InitList(int, float) -> void | 0 | 34 | Constant[1] | ir.cpp:508:13:508:18 | +| InitList(int, float) -> void | 0 | 35 | Store | ir.cpp:508:13:508:18 | +| InitList(int, float) -> void | 0 | 36 | VariableAddress[x2] | ir.cpp:509:9:509:10 | +| InitList(int, float) -> void | 0 | 37 | Constant[0] | ir.cpp:509:13:509:15 | +| InitList(int, float) -> void | 0 | 38 | Store | ir.cpp:509:13:509:15 | +| InitList(int, float) -> void | 0 | 39 | NoOp | ir.cpp:510:1:510:1 | +| InitList(int, float) -> void | 0 | 40 | ReturnVoid | ir.cpp:503:6:503:13 | +| InitList(int, float) -> void | 0 | 41 | UnmodeledUse | ir.cpp:503:6:503:13 | +| InitList(int, float) -> void | 0 | 42 | ExitFunction | ir.cpp:503:6:503:13 | +| InitReference(int) -> void | 0 | 0 | EnterFunction | ir.cpp:685:6:685:18 | +| InitReference(int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:685:6:685:18 | +| InitReference(int) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:685:24:685:24 | +| InitReference(int) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:685:24:685:24 | +| InitReference(int) -> void | 0 | 4 | Store | ir.cpp:685:24:685:24 | +| InitReference(int) -> void | 0 | 5 | VariableAddress[r] | ir.cpp:686:10:686:10 | +| InitReference(int) -> void | 0 | 6 | VariableAddress[x] | ir.cpp:686:14:686:14 | +| InitReference(int) -> void | 0 | 7 | Store | ir.cpp:686:14:686:14 | +| InitReference(int) -> void | 0 | 8 | VariableAddress[r2] | ir.cpp:687:10:687:11 | +| InitReference(int) -> void | 0 | 9 | VariableAddress[r] | ir.cpp:687:15:687:15 | +| InitReference(int) -> void | 0 | 10 | Load | ir.cpp:687:15:687:15 | +| InitReference(int) -> void | 0 | 11 | Store | ir.cpp:687:15:687:15 | +| InitReference(int) -> void | 0 | 12 | VariableAddress[r3] | ir.cpp:688:19:688:20 | +| InitReference(int) -> void | 0 | 13 | FunctionAddress[ReturnReference] | ir.cpp:688:24:688:38 | +| InitReference(int) -> void | 0 | 14 | Invoke | ir.cpp:688:24:688:38 | +| InitReference(int) -> void | 0 | 15 | Convert | ir.cpp:688:24:688:41 | +| InitReference(int) -> void | 0 | 16 | Store | ir.cpp:688:24:688:41 | +| InitReference(int) -> void | 0 | 17 | NoOp | ir.cpp:689:1:689:1 | +| InitReference(int) -> void | 0 | 18 | ReturnVoid | ir.cpp:685:6:685:18 | +| InitReference(int) -> void | 0 | 19 | UnmodeledUse | ir.cpp:685:6:685:18 | +| InitReference(int) -> void | 0 | 20 | ExitFunction | ir.cpp:685:6:685:18 | +| IntegerCompare(int, int) -> void | 0 | 0 | EnterFunction | ir.cpp:87:6:87:19 | +| IntegerCompare(int, int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:87:6:87:19 | +| IntegerCompare(int, int) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:87:25:87:25 | +| IntegerCompare(int, int) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:87:25:87:25 | +| IntegerCompare(int, int) -> void | 0 | 4 | Store | ir.cpp:87:25:87:25 | +| IntegerCompare(int, int) -> void | 0 | 5 | InitializeParameter[y] | ir.cpp:87:32:87:32 | +| IntegerCompare(int, int) -> void | 0 | 6 | VariableAddress[y] | ir.cpp:87:32:87:32 | +| IntegerCompare(int, int) -> void | 0 | 7 | Store | ir.cpp:87:32:87:32 | +| IntegerCompare(int, int) -> void | 0 | 8 | VariableAddress[b] | ir.cpp:88:10:88:10 | +| IntegerCompare(int, int) -> void | 0 | 9 | Uninitialized | ir.cpp:88:10:88:10 | +| IntegerCompare(int, int) -> void | 0 | 10 | Store | ir.cpp:88:10:88:10 | +| IntegerCompare(int, int) -> void | 0 | 11 | VariableAddress[x] | ir.cpp:90:9:90:9 | +| IntegerCompare(int, int) -> void | 0 | 12 | Load | ir.cpp:90:9:90:9 | +| IntegerCompare(int, int) -> void | 0 | 13 | VariableAddress[y] | ir.cpp:90:14:90:14 | +| IntegerCompare(int, int) -> void | 0 | 14 | Load | ir.cpp:90:14:90:14 | +| IntegerCompare(int, int) -> void | 0 | 15 | CompareEQ | ir.cpp:90:9:90:14 | +| IntegerCompare(int, int) -> void | 0 | 16 | VariableAddress[b] | ir.cpp:90:5:90:5 | +| IntegerCompare(int, int) -> void | 0 | 17 | Store | ir.cpp:90:5:90:14 | +| IntegerCompare(int, int) -> void | 0 | 18 | VariableAddress[x] | ir.cpp:91:9:91:9 | +| IntegerCompare(int, int) -> void | 0 | 19 | Load | ir.cpp:91:9:91:9 | +| IntegerCompare(int, int) -> void | 0 | 20 | VariableAddress[y] | ir.cpp:91:14:91:14 | +| IntegerCompare(int, int) -> void | 0 | 21 | Load | ir.cpp:91:14:91:14 | +| IntegerCompare(int, int) -> void | 0 | 22 | CompareNE | ir.cpp:91:9:91:14 | +| IntegerCompare(int, int) -> void | 0 | 23 | VariableAddress[b] | ir.cpp:91:5:91:5 | +| IntegerCompare(int, int) -> void | 0 | 24 | Store | ir.cpp:91:5:91:14 | +| IntegerCompare(int, int) -> void | 0 | 25 | VariableAddress[x] | ir.cpp:92:9:92:9 | +| IntegerCompare(int, int) -> void | 0 | 26 | Load | ir.cpp:92:9:92:9 | +| IntegerCompare(int, int) -> void | 0 | 27 | VariableAddress[y] | ir.cpp:92:13:92:13 | +| IntegerCompare(int, int) -> void | 0 | 28 | Load | ir.cpp:92:13:92:13 | +| IntegerCompare(int, int) -> void | 0 | 29 | CompareLT | ir.cpp:92:9:92:13 | +| IntegerCompare(int, int) -> void | 0 | 30 | VariableAddress[b] | ir.cpp:92:5:92:5 | +| IntegerCompare(int, int) -> void | 0 | 31 | Store | ir.cpp:92:5:92:13 | +| IntegerCompare(int, int) -> void | 0 | 32 | VariableAddress[x] | ir.cpp:93:9:93:9 | +| IntegerCompare(int, int) -> void | 0 | 33 | Load | ir.cpp:93:9:93:9 | +| IntegerCompare(int, int) -> void | 0 | 34 | VariableAddress[y] | ir.cpp:93:13:93:13 | +| IntegerCompare(int, int) -> void | 0 | 35 | Load | ir.cpp:93:13:93:13 | +| IntegerCompare(int, int) -> void | 0 | 36 | CompareGT | ir.cpp:93:9:93:13 | +| IntegerCompare(int, int) -> void | 0 | 37 | VariableAddress[b] | ir.cpp:93:5:93:5 | +| IntegerCompare(int, int) -> void | 0 | 38 | Store | ir.cpp:93:5:93:13 | +| IntegerCompare(int, int) -> void | 0 | 39 | VariableAddress[x] | ir.cpp:94:9:94:9 | +| IntegerCompare(int, int) -> void | 0 | 40 | Load | ir.cpp:94:9:94:9 | +| IntegerCompare(int, int) -> void | 0 | 41 | VariableAddress[y] | ir.cpp:94:14:94:14 | +| IntegerCompare(int, int) -> void | 0 | 42 | Load | ir.cpp:94:14:94:14 | +| IntegerCompare(int, int) -> void | 0 | 43 | CompareLE | ir.cpp:94:9:94:14 | +| IntegerCompare(int, int) -> void | 0 | 44 | VariableAddress[b] | ir.cpp:94:5:94:5 | +| IntegerCompare(int, int) -> void | 0 | 45 | Store | ir.cpp:94:5:94:14 | +| IntegerCompare(int, int) -> void | 0 | 46 | VariableAddress[x] | ir.cpp:95:9:95:9 | +| IntegerCompare(int, int) -> void | 0 | 47 | Load | ir.cpp:95:9:95:9 | +| IntegerCompare(int, int) -> void | 0 | 48 | VariableAddress[y] | ir.cpp:95:14:95:14 | +| IntegerCompare(int, int) -> void | 0 | 49 | Load | ir.cpp:95:14:95:14 | +| IntegerCompare(int, int) -> void | 0 | 50 | CompareGE | ir.cpp:95:9:95:14 | +| IntegerCompare(int, int) -> void | 0 | 51 | VariableAddress[b] | ir.cpp:95:5:95:5 | +| IntegerCompare(int, int) -> void | 0 | 52 | Store | ir.cpp:95:5:95:14 | +| IntegerCompare(int, int) -> void | 0 | 53 | NoOp | ir.cpp:96:1:96:1 | +| IntegerCompare(int, int) -> void | 0 | 54 | ReturnVoid | ir.cpp:87:6:87:19 | +| IntegerCompare(int, int) -> void | 0 | 55 | UnmodeledUse | ir.cpp:87:6:87:19 | +| IntegerCompare(int, int) -> void | 0 | 56 | ExitFunction | ir.cpp:87:6:87:19 | +| IntegerCrement(int) -> void | 0 | 0 | EnterFunction | ir.cpp:98:6:98:19 | +| IntegerCrement(int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:98:6:98:19 | +| IntegerCrement(int) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:98:25:98:25 | +| IntegerCrement(int) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:98:25:98:25 | +| IntegerCrement(int) -> void | 0 | 4 | Store | ir.cpp:98:25:98:25 | +| IntegerCrement(int) -> void | 0 | 5 | VariableAddress[y] | ir.cpp:99:9:99:9 | +| IntegerCrement(int) -> void | 0 | 6 | Uninitialized | ir.cpp:99:9:99:9 | +| IntegerCrement(int) -> void | 0 | 7 | Store | ir.cpp:99:9:99:9 | +| IntegerCrement(int) -> void | 0 | 8 | VariableAddress[x] | ir.cpp:101:11:101:11 | +| IntegerCrement(int) -> void | 0 | 9 | Load | ir.cpp:101:9:101:11 | +| IntegerCrement(int) -> void | 0 | 10 | Constant[1] | ir.cpp:101:9:101:11 | +| IntegerCrement(int) -> void | 0 | 11 | Add | ir.cpp:101:9:101:11 | +| IntegerCrement(int) -> void | 0 | 12 | Store | ir.cpp:101:9:101:11 | +| IntegerCrement(int) -> void | 0 | 13 | VariableAddress[y] | ir.cpp:101:5:101:5 | +| IntegerCrement(int) -> void | 0 | 14 | Store | ir.cpp:101:5:101:11 | +| IntegerCrement(int) -> void | 0 | 15 | VariableAddress[x] | ir.cpp:102:11:102:11 | +| IntegerCrement(int) -> void | 0 | 16 | Load | ir.cpp:102:9:102:11 | +| IntegerCrement(int) -> void | 0 | 17 | Constant[1] | ir.cpp:102:9:102:11 | +| IntegerCrement(int) -> void | 0 | 18 | Sub | ir.cpp:102:9:102:11 | +| IntegerCrement(int) -> void | 0 | 19 | Store | ir.cpp:102:9:102:11 | +| IntegerCrement(int) -> void | 0 | 20 | VariableAddress[y] | ir.cpp:102:5:102:5 | +| IntegerCrement(int) -> void | 0 | 21 | Store | ir.cpp:102:5:102:11 | +| IntegerCrement(int) -> void | 0 | 22 | VariableAddress[x] | ir.cpp:103:9:103:9 | +| IntegerCrement(int) -> void | 0 | 23 | Load | ir.cpp:103:9:103:11 | +| IntegerCrement(int) -> void | 0 | 24 | Constant[1] | ir.cpp:103:9:103:11 | +| IntegerCrement(int) -> void | 0 | 25 | Add | ir.cpp:103:9:103:11 | +| IntegerCrement(int) -> void | 0 | 26 | Store | ir.cpp:103:9:103:11 | +| IntegerCrement(int) -> void | 0 | 27 | VariableAddress[y] | ir.cpp:103:5:103:5 | +| IntegerCrement(int) -> void | 0 | 28 | Store | ir.cpp:103:5:103:11 | +| IntegerCrement(int) -> void | 0 | 29 | VariableAddress[x] | ir.cpp:104:9:104:9 | +| IntegerCrement(int) -> void | 0 | 30 | Load | ir.cpp:104:9:104:11 | +| IntegerCrement(int) -> void | 0 | 31 | Constant[1] | ir.cpp:104:9:104:11 | +| IntegerCrement(int) -> void | 0 | 32 | Sub | ir.cpp:104:9:104:11 | +| IntegerCrement(int) -> void | 0 | 33 | Store | ir.cpp:104:9:104:11 | +| IntegerCrement(int) -> void | 0 | 34 | VariableAddress[y] | ir.cpp:104:5:104:5 | +| IntegerCrement(int) -> void | 0 | 35 | Store | ir.cpp:104:5:104:11 | +| IntegerCrement(int) -> void | 0 | 36 | NoOp | ir.cpp:105:1:105:1 | +| IntegerCrement(int) -> void | 0 | 37 | ReturnVoid | ir.cpp:98:6:98:19 | +| IntegerCrement(int) -> void | 0 | 38 | UnmodeledUse | ir.cpp:98:6:98:19 | +| IntegerCrement(int) -> void | 0 | 39 | ExitFunction | ir.cpp:98:6:98:19 | +| IntegerCrement_LValue(int) -> void | 0 | 0 | EnterFunction | ir.cpp:107:6:107:26 | +| IntegerCrement_LValue(int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:107:6:107:26 | +| IntegerCrement_LValue(int) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:107:32:107:32 | +| IntegerCrement_LValue(int) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:107:32:107:32 | +| IntegerCrement_LValue(int) -> void | 0 | 4 | Store | ir.cpp:107:32:107:32 | +| IntegerCrement_LValue(int) -> void | 0 | 5 | VariableAddress[p] | ir.cpp:108:10:108:10 | +| IntegerCrement_LValue(int) -> void | 0 | 6 | Uninitialized | ir.cpp:108:10:108:10 | +| IntegerCrement_LValue(int) -> void | 0 | 7 | Store | ir.cpp:108:10:108:10 | +| IntegerCrement_LValue(int) -> void | 0 | 8 | VariableAddress[x] | ir.cpp:110:13:110:13 | +| IntegerCrement_LValue(int) -> void | 0 | 9 | Load | ir.cpp:110:11:110:13 | +| IntegerCrement_LValue(int) -> void | 0 | 10 | Constant[1] | ir.cpp:110:11:110:13 | +| IntegerCrement_LValue(int) -> void | 0 | 11 | Add | ir.cpp:110:11:110:13 | +| IntegerCrement_LValue(int) -> void | 0 | 12 | Store | ir.cpp:110:11:110:13 | +| IntegerCrement_LValue(int) -> void | 0 | 13 | VariableAddress[p] | ir.cpp:110:5:110:5 | +| IntegerCrement_LValue(int) -> void | 0 | 14 | Store | ir.cpp:110:5:110:14 | +| IntegerCrement_LValue(int) -> void | 0 | 15 | VariableAddress[x] | ir.cpp:111:13:111:13 | +| IntegerCrement_LValue(int) -> void | 0 | 16 | Load | ir.cpp:111:11:111:13 | +| IntegerCrement_LValue(int) -> void | 0 | 17 | Constant[1] | ir.cpp:111:11:111:13 | +| IntegerCrement_LValue(int) -> void | 0 | 18 | Sub | ir.cpp:111:11:111:13 | +| IntegerCrement_LValue(int) -> void | 0 | 19 | Store | ir.cpp:111:11:111:13 | +| IntegerCrement_LValue(int) -> void | 0 | 20 | VariableAddress[p] | ir.cpp:111:5:111:5 | +| IntegerCrement_LValue(int) -> void | 0 | 21 | Store | ir.cpp:111:5:111:14 | +| IntegerCrement_LValue(int) -> void | 0 | 22 | NoOp | ir.cpp:112:1:112:1 | +| IntegerCrement_LValue(int) -> void | 0 | 23 | ReturnVoid | ir.cpp:107:6:107:26 | +| IntegerCrement_LValue(int) -> void | 0 | 24 | UnmodeledUse | ir.cpp:107:6:107:26 | +| IntegerCrement_LValue(int) -> void | 0 | 25 | ExitFunction | ir.cpp:107:6:107:26 | +| IntegerOps(int, int) -> void | 0 | 0 | EnterFunction | ir.cpp:50:6:50:15 | +| IntegerOps(int, int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:50:6:50:15 | +| IntegerOps(int, int) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:50:21:50:21 | +| IntegerOps(int, int) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:50:21:50:21 | +| IntegerOps(int, int) -> void | 0 | 4 | Store | ir.cpp:50:21:50:21 | +| IntegerOps(int, int) -> void | 0 | 5 | InitializeParameter[y] | ir.cpp:50:28:50:28 | +| IntegerOps(int, int) -> void | 0 | 6 | VariableAddress[y] | ir.cpp:50:28:50:28 | +| IntegerOps(int, int) -> void | 0 | 7 | Store | ir.cpp:50:28:50:28 | +| IntegerOps(int, int) -> void | 0 | 8 | VariableAddress[z] | ir.cpp:51:9:51:9 | +| IntegerOps(int, int) -> void | 0 | 9 | Uninitialized | ir.cpp:51:9:51:9 | +| IntegerOps(int, int) -> void | 0 | 10 | Store | ir.cpp:51:9:51:9 | +| IntegerOps(int, int) -> void | 0 | 11 | VariableAddress[x] | ir.cpp:53:9:53:9 | +| IntegerOps(int, int) -> void | 0 | 12 | Load | ir.cpp:53:9:53:9 | +| IntegerOps(int, int) -> void | 0 | 13 | VariableAddress[y] | ir.cpp:53:13:53:13 | +| IntegerOps(int, int) -> void | 0 | 14 | Load | ir.cpp:53:13:53:13 | +| IntegerOps(int, int) -> void | 0 | 15 | Add | ir.cpp:53:9:53:13 | +| IntegerOps(int, int) -> void | 0 | 16 | VariableAddress[z] | ir.cpp:53:5:53:5 | +| IntegerOps(int, int) -> void | 0 | 17 | Store | ir.cpp:53:5:53:13 | +| IntegerOps(int, int) -> void | 0 | 18 | VariableAddress[x] | ir.cpp:54:9:54:9 | +| IntegerOps(int, int) -> void | 0 | 19 | Load | ir.cpp:54:9:54:9 | +| IntegerOps(int, int) -> void | 0 | 20 | VariableAddress[y] | ir.cpp:54:13:54:13 | +| IntegerOps(int, int) -> void | 0 | 21 | Load | ir.cpp:54:13:54:13 | +| IntegerOps(int, int) -> void | 0 | 22 | Sub | ir.cpp:54:9:54:13 | +| IntegerOps(int, int) -> void | 0 | 23 | VariableAddress[z] | ir.cpp:54:5:54:5 | +| IntegerOps(int, int) -> void | 0 | 24 | Store | ir.cpp:54:5:54:13 | +| IntegerOps(int, int) -> void | 0 | 25 | VariableAddress[x] | ir.cpp:55:9:55:9 | +| IntegerOps(int, int) -> void | 0 | 26 | Load | ir.cpp:55:9:55:9 | +| IntegerOps(int, int) -> void | 0 | 27 | VariableAddress[y] | ir.cpp:55:13:55:13 | +| IntegerOps(int, int) -> void | 0 | 28 | Load | ir.cpp:55:13:55:13 | +| IntegerOps(int, int) -> void | 0 | 29 | Mul | ir.cpp:55:9:55:13 | +| IntegerOps(int, int) -> void | 0 | 30 | VariableAddress[z] | ir.cpp:55:5:55:5 | +| IntegerOps(int, int) -> void | 0 | 31 | Store | ir.cpp:55:5:55:13 | +| IntegerOps(int, int) -> void | 0 | 32 | VariableAddress[x] | ir.cpp:56:9:56:9 | +| IntegerOps(int, int) -> void | 0 | 33 | Load | ir.cpp:56:9:56:9 | +| IntegerOps(int, int) -> void | 0 | 34 | VariableAddress[y] | ir.cpp:56:13:56:13 | +| IntegerOps(int, int) -> void | 0 | 35 | Load | ir.cpp:56:13:56:13 | +| IntegerOps(int, int) -> void | 0 | 36 | Div | ir.cpp:56:9:56:13 | +| IntegerOps(int, int) -> void | 0 | 37 | VariableAddress[z] | ir.cpp:56:5:56:5 | +| IntegerOps(int, int) -> void | 0 | 38 | Store | ir.cpp:56:5:56:13 | +| IntegerOps(int, int) -> void | 0 | 39 | VariableAddress[x] | ir.cpp:57:9:57:9 | +| IntegerOps(int, int) -> void | 0 | 40 | Load | ir.cpp:57:9:57:9 | +| IntegerOps(int, int) -> void | 0 | 41 | VariableAddress[y] | ir.cpp:57:13:57:13 | +| IntegerOps(int, int) -> void | 0 | 42 | Load | ir.cpp:57:13:57:13 | +| IntegerOps(int, int) -> void | 0 | 43 | Rem | ir.cpp:57:9:57:13 | +| IntegerOps(int, int) -> void | 0 | 44 | VariableAddress[z] | ir.cpp:57:5:57:5 | +| IntegerOps(int, int) -> void | 0 | 45 | Store | ir.cpp:57:5:57:13 | +| IntegerOps(int, int) -> void | 0 | 46 | VariableAddress[x] | ir.cpp:59:9:59:9 | +| IntegerOps(int, int) -> void | 0 | 47 | Load | ir.cpp:59:9:59:9 | +| IntegerOps(int, int) -> void | 0 | 48 | VariableAddress[y] | ir.cpp:59:13:59:13 | +| IntegerOps(int, int) -> void | 0 | 49 | Load | ir.cpp:59:13:59:13 | +| IntegerOps(int, int) -> void | 0 | 50 | BitAnd | ir.cpp:59:9:59:13 | +| IntegerOps(int, int) -> void | 0 | 51 | VariableAddress[z] | ir.cpp:59:5:59:5 | +| IntegerOps(int, int) -> void | 0 | 52 | Store | ir.cpp:59:5:59:13 | +| IntegerOps(int, int) -> void | 0 | 53 | VariableAddress[x] | ir.cpp:60:9:60:9 | +| IntegerOps(int, int) -> void | 0 | 54 | Load | ir.cpp:60:9:60:9 | +| IntegerOps(int, int) -> void | 0 | 55 | VariableAddress[y] | ir.cpp:60:13:60:13 | +| IntegerOps(int, int) -> void | 0 | 56 | Load | ir.cpp:60:13:60:13 | +| IntegerOps(int, int) -> void | 0 | 57 | BitOr | ir.cpp:60:9:60:13 | +| IntegerOps(int, int) -> void | 0 | 58 | VariableAddress[z] | ir.cpp:60:5:60:5 | +| IntegerOps(int, int) -> void | 0 | 59 | Store | ir.cpp:60:5:60:13 | +| IntegerOps(int, int) -> void | 0 | 60 | VariableAddress[x] | ir.cpp:61:9:61:9 | +| IntegerOps(int, int) -> void | 0 | 61 | Load | ir.cpp:61:9:61:9 | +| IntegerOps(int, int) -> void | 0 | 62 | VariableAddress[y] | ir.cpp:61:13:61:13 | +| IntegerOps(int, int) -> void | 0 | 63 | Load | ir.cpp:61:13:61:13 | +| IntegerOps(int, int) -> void | 0 | 64 | BitXor | ir.cpp:61:9:61:13 | +| IntegerOps(int, int) -> void | 0 | 65 | VariableAddress[z] | ir.cpp:61:5:61:5 | +| IntegerOps(int, int) -> void | 0 | 66 | Store | ir.cpp:61:5:61:13 | +| IntegerOps(int, int) -> void | 0 | 67 | VariableAddress[x] | ir.cpp:63:9:63:9 | +| IntegerOps(int, int) -> void | 0 | 68 | Load | ir.cpp:63:9:63:9 | +| IntegerOps(int, int) -> void | 0 | 69 | VariableAddress[y] | ir.cpp:63:14:63:14 | +| IntegerOps(int, int) -> void | 0 | 70 | Load | ir.cpp:63:14:63:14 | +| IntegerOps(int, int) -> void | 0 | 71 | ShiftLeft | ir.cpp:63:9:63:14 | +| IntegerOps(int, int) -> void | 0 | 72 | VariableAddress[z] | ir.cpp:63:5:63:5 | +| IntegerOps(int, int) -> void | 0 | 73 | Store | ir.cpp:63:5:63:14 | +| IntegerOps(int, int) -> void | 0 | 74 | VariableAddress[x] | ir.cpp:64:9:64:9 | +| IntegerOps(int, int) -> void | 0 | 75 | Load | ir.cpp:64:9:64:9 | +| IntegerOps(int, int) -> void | 0 | 76 | VariableAddress[y] | ir.cpp:64:14:64:14 | +| IntegerOps(int, int) -> void | 0 | 77 | Load | ir.cpp:64:14:64:14 | +| IntegerOps(int, int) -> void | 0 | 78 | ShiftRight | ir.cpp:64:9:64:14 | +| IntegerOps(int, int) -> void | 0 | 79 | VariableAddress[z] | ir.cpp:64:5:64:5 | +| IntegerOps(int, int) -> void | 0 | 80 | Store | ir.cpp:64:5:64:14 | +| IntegerOps(int, int) -> void | 0 | 81 | VariableAddress[x] | ir.cpp:66:9:66:9 | +| IntegerOps(int, int) -> void | 0 | 82 | Load | ir.cpp:66:9:66:9 | +| IntegerOps(int, int) -> void | 0 | 83 | VariableAddress[z] | ir.cpp:66:5:66:5 | +| IntegerOps(int, int) -> void | 0 | 84 | Store | ir.cpp:66:5:66:9 | +| IntegerOps(int, int) -> void | 0 | 85 | VariableAddress[x] | ir.cpp:68:10:68:10 | +| IntegerOps(int, int) -> void | 0 | 86 | Load | ir.cpp:68:10:68:10 | +| IntegerOps(int, int) -> void | 0 | 87 | VariableAddress[z] | ir.cpp:68:5:68:5 | +| IntegerOps(int, int) -> void | 0 | 88 | Load | ir.cpp:68:5:68:10 | +| IntegerOps(int, int) -> void | 0 | 89 | Add | ir.cpp:68:5:68:10 | +| IntegerOps(int, int) -> void | 0 | 90 | Store | ir.cpp:68:5:68:10 | +| IntegerOps(int, int) -> void | 0 | 91 | VariableAddress[x] | ir.cpp:69:10:69:10 | +| IntegerOps(int, int) -> void | 0 | 92 | Load | ir.cpp:69:10:69:10 | +| IntegerOps(int, int) -> void | 0 | 93 | VariableAddress[z] | ir.cpp:69:5:69:5 | +| IntegerOps(int, int) -> void | 0 | 94 | Load | ir.cpp:69:5:69:10 | +| IntegerOps(int, int) -> void | 0 | 95 | Sub | ir.cpp:69:5:69:10 | +| IntegerOps(int, int) -> void | 0 | 96 | Store | ir.cpp:69:5:69:10 | +| IntegerOps(int, int) -> void | 0 | 97 | VariableAddress[x] | ir.cpp:70:10:70:10 | +| IntegerOps(int, int) -> void | 0 | 98 | Load | ir.cpp:70:10:70:10 | +| IntegerOps(int, int) -> void | 0 | 99 | VariableAddress[z] | ir.cpp:70:5:70:5 | +| IntegerOps(int, int) -> void | 0 | 100 | Load | ir.cpp:70:5:70:10 | +| IntegerOps(int, int) -> void | 0 | 101 | Mul | ir.cpp:70:5:70:10 | +| IntegerOps(int, int) -> void | 0 | 102 | Store | ir.cpp:70:5:70:10 | +| IntegerOps(int, int) -> void | 0 | 103 | VariableAddress[x] | ir.cpp:71:10:71:10 | +| IntegerOps(int, int) -> void | 0 | 104 | Load | ir.cpp:71:10:71:10 | +| IntegerOps(int, int) -> void | 0 | 105 | VariableAddress[z] | ir.cpp:71:5:71:5 | +| IntegerOps(int, int) -> void | 0 | 106 | Load | ir.cpp:71:5:71:10 | +| IntegerOps(int, int) -> void | 0 | 107 | Div | ir.cpp:71:5:71:10 | +| IntegerOps(int, int) -> void | 0 | 108 | Store | ir.cpp:71:5:71:10 | +| IntegerOps(int, int) -> void | 0 | 109 | VariableAddress[x] | ir.cpp:72:10:72:10 | +| IntegerOps(int, int) -> void | 0 | 110 | Load | ir.cpp:72:10:72:10 | +| IntegerOps(int, int) -> void | 0 | 111 | VariableAddress[z] | ir.cpp:72:5:72:5 | +| IntegerOps(int, int) -> void | 0 | 112 | Load | ir.cpp:72:5:72:10 | +| IntegerOps(int, int) -> void | 0 | 113 | Rem | ir.cpp:72:5:72:10 | +| IntegerOps(int, int) -> void | 0 | 114 | Store | ir.cpp:72:5:72:10 | +| IntegerOps(int, int) -> void | 0 | 115 | VariableAddress[x] | ir.cpp:74:10:74:10 | +| IntegerOps(int, int) -> void | 0 | 116 | Load | ir.cpp:74:10:74:10 | +| IntegerOps(int, int) -> void | 0 | 117 | VariableAddress[z] | ir.cpp:74:5:74:5 | +| IntegerOps(int, int) -> void | 0 | 118 | Load | ir.cpp:74:5:74:10 | +| IntegerOps(int, int) -> void | 0 | 119 | BitAnd | ir.cpp:74:5:74:10 | +| IntegerOps(int, int) -> void | 0 | 120 | Store | ir.cpp:74:5:74:10 | +| IntegerOps(int, int) -> void | 0 | 121 | VariableAddress[x] | ir.cpp:75:10:75:10 | +| IntegerOps(int, int) -> void | 0 | 122 | Load | ir.cpp:75:10:75:10 | +| IntegerOps(int, int) -> void | 0 | 123 | VariableAddress[z] | ir.cpp:75:5:75:5 | +| IntegerOps(int, int) -> void | 0 | 124 | Load | ir.cpp:75:5:75:10 | +| IntegerOps(int, int) -> void | 0 | 125 | BitOr | ir.cpp:75:5:75:10 | +| IntegerOps(int, int) -> void | 0 | 126 | Store | ir.cpp:75:5:75:10 | +| IntegerOps(int, int) -> void | 0 | 127 | VariableAddress[x] | ir.cpp:76:10:76:10 | +| IntegerOps(int, int) -> void | 0 | 128 | Load | ir.cpp:76:10:76:10 | +| IntegerOps(int, int) -> void | 0 | 129 | VariableAddress[z] | ir.cpp:76:5:76:5 | +| IntegerOps(int, int) -> void | 0 | 130 | Load | ir.cpp:76:5:76:10 | +| IntegerOps(int, int) -> void | 0 | 131 | BitXor | ir.cpp:76:5:76:10 | +| IntegerOps(int, int) -> void | 0 | 132 | Store | ir.cpp:76:5:76:10 | +| IntegerOps(int, int) -> void | 0 | 133 | VariableAddress[x] | ir.cpp:78:11:78:11 | +| IntegerOps(int, int) -> void | 0 | 134 | Load | ir.cpp:78:11:78:11 | +| IntegerOps(int, int) -> void | 0 | 135 | VariableAddress[z] | ir.cpp:78:5:78:5 | +| IntegerOps(int, int) -> void | 0 | 136 | Load | ir.cpp:78:5:78:11 | +| IntegerOps(int, int) -> void | 0 | 137 | ShiftLeft | ir.cpp:78:5:78:11 | +| IntegerOps(int, int) -> void | 0 | 138 | Store | ir.cpp:78:5:78:11 | +| IntegerOps(int, int) -> void | 0 | 139 | VariableAddress[x] | ir.cpp:79:11:79:11 | +| IntegerOps(int, int) -> void | 0 | 140 | Load | ir.cpp:79:11:79:11 | +| IntegerOps(int, int) -> void | 0 | 141 | VariableAddress[z] | ir.cpp:79:5:79:5 | +| IntegerOps(int, int) -> void | 0 | 142 | Load | ir.cpp:79:5:79:11 | +| IntegerOps(int, int) -> void | 0 | 143 | ShiftRight | ir.cpp:79:5:79:11 | +| IntegerOps(int, int) -> void | 0 | 144 | Store | ir.cpp:79:5:79:11 | +| IntegerOps(int, int) -> void | 0 | 145 | VariableAddress[x] | ir.cpp:81:10:81:10 | +| IntegerOps(int, int) -> void | 0 | 146 | Load | ir.cpp:81:10:81:10 | +| IntegerOps(int, int) -> void | 0 | 147 | CopyValue | ir.cpp:81:9:81:10 | +| IntegerOps(int, int) -> void | 0 | 148 | VariableAddress[z] | ir.cpp:81:5:81:5 | +| IntegerOps(int, int) -> void | 0 | 149 | Store | ir.cpp:81:5:81:10 | +| IntegerOps(int, int) -> void | 0 | 150 | VariableAddress[x] | ir.cpp:82:10:82:10 | +| IntegerOps(int, int) -> void | 0 | 151 | Load | ir.cpp:82:10:82:10 | +| IntegerOps(int, int) -> void | 0 | 152 | Negate | ir.cpp:82:9:82:10 | +| IntegerOps(int, int) -> void | 0 | 153 | VariableAddress[z] | ir.cpp:82:5:82:5 | +| IntegerOps(int, int) -> void | 0 | 154 | Store | ir.cpp:82:5:82:10 | +| IntegerOps(int, int) -> void | 0 | 155 | VariableAddress[x] | ir.cpp:83:10:83:10 | +| IntegerOps(int, int) -> void | 0 | 156 | Load | ir.cpp:83:10:83:10 | +| IntegerOps(int, int) -> void | 0 | 157 | BitComplement | ir.cpp:83:9:83:10 | +| IntegerOps(int, int) -> void | 0 | 158 | VariableAddress[z] | ir.cpp:83:5:83:5 | +| IntegerOps(int, int) -> void | 0 | 159 | Store | ir.cpp:83:5:83:10 | +| IntegerOps(int, int) -> void | 0 | 160 | VariableAddress[x] | ir.cpp:84:10:84:10 | +| IntegerOps(int, int) -> void | 0 | 161 | Load | ir.cpp:84:10:84:10 | +| IntegerOps(int, int) -> void | 0 | 162 | Constant[0] | ir.cpp:84:10:84:10 | +| IntegerOps(int, int) -> void | 0 | 163 | CompareNE | ir.cpp:84:10:84:10 | +| IntegerOps(int, int) -> void | 0 | 164 | LogicalNot | ir.cpp:84:9:84:10 | +| IntegerOps(int, int) -> void | 0 | 165 | Convert | ir.cpp:84:9:84:10 | +| IntegerOps(int, int) -> void | 0 | 166 | VariableAddress[z] | ir.cpp:84:5:84:5 | +| IntegerOps(int, int) -> void | 0 | 167 | Store | ir.cpp:84:5:84:10 | +| IntegerOps(int, int) -> void | 0 | 168 | NoOp | ir.cpp:85:1:85:1 | +| IntegerOps(int, int) -> void | 0 | 169 | ReturnVoid | ir.cpp:50:6:50:15 | +| IntegerOps(int, int) -> void | 0 | 170 | UnmodeledUse | ir.cpp:50:6:50:15 | +| IntegerOps(int, int) -> void | 0 | 171 | ExitFunction | ir.cpp:50:6:50:15 | +| LogicalAnd(bool, bool) -> void | 0 | 0 | EnterFunction | ir.cpp:447:6:447:15 | +| LogicalAnd(bool, bool) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:447:6:447:15 | +| LogicalAnd(bool, bool) -> void | 0 | 2 | InitializeParameter[a] | ir.cpp:447:22:447:22 | +| LogicalAnd(bool, bool) -> void | 0 | 3 | VariableAddress[a] | ir.cpp:447:22:447:22 | +| LogicalAnd(bool, bool) -> void | 0 | 4 | Store | ir.cpp:447:22:447:22 | +| LogicalAnd(bool, bool) -> void | 0 | 5 | InitializeParameter[b] | ir.cpp:447:30:447:30 | +| LogicalAnd(bool, bool) -> void | 0 | 6 | VariableAddress[b] | ir.cpp:447:30:447:30 | +| LogicalAnd(bool, bool) -> void | 0 | 7 | Store | ir.cpp:447:30:447:30 | +| LogicalAnd(bool, bool) -> void | 0 | 8 | VariableAddress[x] | ir.cpp:448:9:448:9 | +| LogicalAnd(bool, bool) -> void | 0 | 9 | Uninitialized | ir.cpp:448:9:448:9 | +| LogicalAnd(bool, bool) -> void | 0 | 10 | Store | ir.cpp:448:9:448:9 | +| LogicalAnd(bool, bool) -> void | 0 | 11 | VariableAddress[a] | ir.cpp:449:9:449:9 | +| LogicalAnd(bool, bool) -> void | 0 | 12 | Load | ir.cpp:449:9:449:9 | +| LogicalAnd(bool, bool) -> void | 0 | 13 | ConditionalBranch | ir.cpp:449:9:449:9 | +| LogicalAnd(bool, bool) -> void | 1 | 0 | VariableAddress[b] | ir.cpp:449:14:449:14 | +| LogicalAnd(bool, bool) -> void | 1 | 1 | Load | ir.cpp:449:14:449:14 | +| LogicalAnd(bool, bool) -> void | 1 | 2 | ConditionalBranch | ir.cpp:449:14:449:14 | +| LogicalAnd(bool, bool) -> void | 2 | 0 | Constant[7] | ir.cpp:450:13:450:13 | +| LogicalAnd(bool, bool) -> void | 2 | 1 | VariableAddress[x] | ir.cpp:450:9:450:9 | +| LogicalAnd(bool, bool) -> void | 2 | 2 | Store | ir.cpp:450:9:450:13 | +| LogicalAnd(bool, bool) -> void | 3 | 0 | VariableAddress[a] | ir.cpp:453:9:453:9 | +| LogicalAnd(bool, bool) -> void | 3 | 1 | Load | ir.cpp:453:9:453:9 | +| LogicalAnd(bool, bool) -> void | 3 | 2 | ConditionalBranch | ir.cpp:453:9:453:9 | +| LogicalAnd(bool, bool) -> void | 4 | 0 | VariableAddress[b] | ir.cpp:453:14:453:14 | +| LogicalAnd(bool, bool) -> void | 4 | 1 | Load | ir.cpp:453:14:453:14 | +| LogicalAnd(bool, bool) -> void | 4 | 2 | ConditionalBranch | ir.cpp:453:14:453:14 | +| LogicalAnd(bool, bool) -> void | 5 | 0 | Constant[1] | ir.cpp:454:13:454:13 | +| LogicalAnd(bool, bool) -> void | 5 | 1 | VariableAddress[x] | ir.cpp:454:9:454:9 | +| LogicalAnd(bool, bool) -> void | 5 | 2 | Store | ir.cpp:454:9:454:13 | +| LogicalAnd(bool, bool) -> void | 6 | 0 | Constant[5] | ir.cpp:457:13:457:13 | +| LogicalAnd(bool, bool) -> void | 6 | 1 | VariableAddress[x] | ir.cpp:457:9:457:9 | +| LogicalAnd(bool, bool) -> void | 6 | 2 | Store | ir.cpp:457:9:457:13 | +| LogicalAnd(bool, bool) -> void | 7 | 0 | NoOp | ir.cpp:459:1:459:1 | +| LogicalAnd(bool, bool) -> void | 7 | 1 | ReturnVoid | ir.cpp:447:6:447:15 | +| LogicalAnd(bool, bool) -> void | 7 | 2 | UnmodeledUse | ir.cpp:447:6:447:15 | +| LogicalAnd(bool, bool) -> void | 7 | 3 | ExitFunction | ir.cpp:447:6:447:15 | +| LogicalNot(bool, bool) -> void | 0 | 0 | EnterFunction | ir.cpp:461:6:461:15 | +| LogicalNot(bool, bool) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:461:6:461:15 | +| LogicalNot(bool, bool) -> void | 0 | 2 | InitializeParameter[a] | ir.cpp:461:22:461:22 | +| LogicalNot(bool, bool) -> void | 0 | 3 | VariableAddress[a] | ir.cpp:461:22:461:22 | +| LogicalNot(bool, bool) -> void | 0 | 4 | Store | ir.cpp:461:22:461:22 | +| LogicalNot(bool, bool) -> void | 0 | 5 | InitializeParameter[b] | ir.cpp:461:30:461:30 | +| LogicalNot(bool, bool) -> void | 0 | 6 | VariableAddress[b] | ir.cpp:461:30:461:30 | +| LogicalNot(bool, bool) -> void | 0 | 7 | Store | ir.cpp:461:30:461:30 | +| LogicalNot(bool, bool) -> void | 0 | 8 | VariableAddress[x] | ir.cpp:462:9:462:9 | +| LogicalNot(bool, bool) -> void | 0 | 9 | Uninitialized | ir.cpp:462:9:462:9 | +| LogicalNot(bool, bool) -> void | 0 | 10 | Store | ir.cpp:462:9:462:9 | +| LogicalNot(bool, bool) -> void | 0 | 11 | VariableAddress[a] | ir.cpp:463:10:463:10 | +| LogicalNot(bool, bool) -> void | 0 | 12 | Load | ir.cpp:463:10:463:10 | +| LogicalNot(bool, bool) -> void | 0 | 13 | ConditionalBranch | ir.cpp:463:10:463:10 | +| LogicalNot(bool, bool) -> void | 1 | 0 | Constant[1] | ir.cpp:464:13:464:13 | +| LogicalNot(bool, bool) -> void | 1 | 1 | VariableAddress[x] | ir.cpp:464:9:464:9 | +| LogicalNot(bool, bool) -> void | 1 | 2 | Store | ir.cpp:464:9:464:13 | +| LogicalNot(bool, bool) -> void | 2 | 0 | VariableAddress[a] | ir.cpp:467:11:467:11 | +| LogicalNot(bool, bool) -> void | 2 | 1 | Load | ir.cpp:467:11:467:11 | +| LogicalNot(bool, bool) -> void | 2 | 2 | ConditionalBranch | ir.cpp:467:11:467:11 | +| LogicalNot(bool, bool) -> void | 3 | 0 | VariableAddress[b] | ir.cpp:467:16:467:16 | +| LogicalNot(bool, bool) -> void | 3 | 1 | Load | ir.cpp:467:16:467:16 | +| LogicalNot(bool, bool) -> void | 3 | 2 | ConditionalBranch | ir.cpp:467:16:467:16 | +| LogicalNot(bool, bool) -> void | 4 | 0 | Constant[2] | ir.cpp:468:13:468:13 | +| LogicalNot(bool, bool) -> void | 4 | 1 | VariableAddress[x] | ir.cpp:468:9:468:9 | +| LogicalNot(bool, bool) -> void | 4 | 2 | Store | ir.cpp:468:9:468:13 | +| LogicalNot(bool, bool) -> void | 5 | 0 | Constant[3] | ir.cpp:471:13:471:13 | +| LogicalNot(bool, bool) -> void | 5 | 1 | VariableAddress[x] | ir.cpp:471:9:471:9 | +| LogicalNot(bool, bool) -> void | 5 | 2 | Store | ir.cpp:471:9:471:13 | +| LogicalNot(bool, bool) -> void | 6 | 0 | NoOp | ir.cpp:473:1:473:1 | +| LogicalNot(bool, bool) -> void | 6 | 1 | ReturnVoid | ir.cpp:461:6:461:15 | +| LogicalNot(bool, bool) -> void | 6 | 2 | UnmodeledUse | ir.cpp:461:6:461:15 | +| LogicalNot(bool, bool) -> void | 6 | 3 | ExitFunction | ir.cpp:461:6:461:15 | +| LogicalOr(bool, bool) -> void | 0 | 0 | EnterFunction | ir.cpp:433:6:433:14 | +| LogicalOr(bool, bool) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:433:6:433:14 | +| LogicalOr(bool, bool) -> void | 0 | 2 | InitializeParameter[a] | ir.cpp:433:21:433:21 | +| LogicalOr(bool, bool) -> void | 0 | 3 | VariableAddress[a] | ir.cpp:433:21:433:21 | +| LogicalOr(bool, bool) -> void | 0 | 4 | Store | ir.cpp:433:21:433:21 | +| LogicalOr(bool, bool) -> void | 0 | 5 | InitializeParameter[b] | ir.cpp:433:29:433:29 | +| LogicalOr(bool, bool) -> void | 0 | 6 | VariableAddress[b] | ir.cpp:433:29:433:29 | +| LogicalOr(bool, bool) -> void | 0 | 7 | Store | ir.cpp:433:29:433:29 | +| LogicalOr(bool, bool) -> void | 0 | 8 | VariableAddress[x] | ir.cpp:434:9:434:9 | +| LogicalOr(bool, bool) -> void | 0 | 9 | Uninitialized | ir.cpp:434:9:434:9 | +| LogicalOr(bool, bool) -> void | 0 | 10 | Store | ir.cpp:434:9:434:9 | +| LogicalOr(bool, bool) -> void | 0 | 11 | VariableAddress[a] | ir.cpp:435:9:435:9 | +| LogicalOr(bool, bool) -> void | 0 | 12 | Load | ir.cpp:435:9:435:9 | +| LogicalOr(bool, bool) -> void | 0 | 13 | ConditionalBranch | ir.cpp:435:9:435:9 | +| LogicalOr(bool, bool) -> void | 1 | 0 | VariableAddress[b] | ir.cpp:435:14:435:14 | +| LogicalOr(bool, bool) -> void | 1 | 1 | Load | ir.cpp:435:14:435:14 | +| LogicalOr(bool, bool) -> void | 1 | 2 | ConditionalBranch | ir.cpp:435:14:435:14 | +| LogicalOr(bool, bool) -> void | 2 | 0 | Constant[7] | ir.cpp:436:13:436:13 | +| LogicalOr(bool, bool) -> void | 2 | 1 | VariableAddress[x] | ir.cpp:436:9:436:9 | +| LogicalOr(bool, bool) -> void | 2 | 2 | Store | ir.cpp:436:9:436:13 | +| LogicalOr(bool, bool) -> void | 3 | 0 | VariableAddress[a] | ir.cpp:439:9:439:9 | +| LogicalOr(bool, bool) -> void | 3 | 1 | Load | ir.cpp:439:9:439:9 | +| LogicalOr(bool, bool) -> void | 3 | 2 | ConditionalBranch | ir.cpp:439:9:439:9 | +| LogicalOr(bool, bool) -> void | 4 | 0 | VariableAddress[b] | ir.cpp:439:14:439:14 | +| LogicalOr(bool, bool) -> void | 4 | 1 | Load | ir.cpp:439:14:439:14 | +| LogicalOr(bool, bool) -> void | 4 | 2 | ConditionalBranch | ir.cpp:439:14:439:14 | +| LogicalOr(bool, bool) -> void | 5 | 0 | Constant[1] | ir.cpp:440:13:440:13 | +| LogicalOr(bool, bool) -> void | 5 | 1 | VariableAddress[x] | ir.cpp:440:9:440:9 | +| LogicalOr(bool, bool) -> void | 5 | 2 | Store | ir.cpp:440:9:440:13 | +| LogicalOr(bool, bool) -> void | 6 | 0 | Constant[5] | ir.cpp:443:13:443:13 | +| LogicalOr(bool, bool) -> void | 6 | 1 | VariableAddress[x] | ir.cpp:443:9:443:9 | +| LogicalOr(bool, bool) -> void | 6 | 2 | Store | ir.cpp:443:9:443:13 | +| LogicalOr(bool, bool) -> void | 7 | 0 | NoOp | ir.cpp:445:1:445:1 | +| LogicalOr(bool, bool) -> void | 7 | 1 | ReturnVoid | ir.cpp:433:6:433:14 | +| LogicalOr(bool, bool) -> void | 7 | 2 | UnmodeledUse | ir.cpp:433:6:433:14 | +| LogicalOr(bool, bool) -> void | 7 | 3 | ExitFunction | ir.cpp:433:6:433:14 | +| Middle::Middle() -> void | 0 | 0 | EnterFunction | ir.cpp:757:3:757:8 | +| Middle::Middle() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:757:3:757:8 | +| Middle::Middle() -> void | 0 | 2 | InitializeThis | ir.cpp:757:3:757:8 | +| Middle::Middle() -> void | 0 | 3 | ConvertToBase[Middle : Base] | ir.cpp:757:12:757:12 | +| Middle::Middle() -> void | 0 | 4 | FunctionAddress[Base] | ir.cpp:757:12:757:12 | +| Middle::Middle() -> void | 0 | 5 | Invoke | ir.cpp:757:12:757:12 | +| Middle::Middle() -> void | 0 | 6 | FieldAddress[middle_s] | ir.cpp:757:12:757:12 | +| Middle::Middle() -> void | 0 | 7 | FunctionAddress[String] | ir.cpp:757:12:757:12 | +| Middle::Middle() -> void | 0 | 8 | Invoke | ir.cpp:757:12:757:12 | +| Middle::Middle() -> void | 0 | 9 | NoOp | ir.cpp:758:3:758:3 | +| Middle::Middle() -> void | 0 | 10 | ReturnVoid | ir.cpp:757:3:757:8 | +| Middle::Middle() -> void | 0 | 11 | UnmodeledUse | ir.cpp:757:3:757:8 | +| Middle::Middle() -> void | 0 | 12 | ExitFunction | ir.cpp:757:3:757:8 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 0 | EnterFunction | ir.cpp:754:8:754:8 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 1 | UnmodeledDefinition | ir.cpp:754:8:754:8 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 2 | InitializeThis | ir.cpp:754:8:754:8 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 3 | InitializeParameter[p#0] | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 4 | VariableAddress[p#0] | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 5 | Store | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 6 | CopyValue | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 7 | ConvertToBase[Middle : Base] | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 8 | FunctionAddress[operator=] | ir.cpp:754:8:754:8 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 9 | VariableAddress[p#0] | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 10 | Load | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 11 | ConvertToBase[Middle : Base] | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 12 | Invoke | ir.cpp:754:8:754:8 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 13 | CopyValue | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 14 | FieldAddress[middle_s] | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 15 | FunctionAddress[operator=] | ir.cpp:754:8:754:8 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 16 | VariableAddress[p#0] | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 17 | Load | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 18 | FieldAddress[middle_s] | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 19 | Invoke | ir.cpp:754:8:754:8 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 20 | VariableAddress[#return] | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 21 | CopyValue | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 22 | Store | file://:0:0:0:0 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 23 | VariableAddress[#return] | ir.cpp:754:8:754:8 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 24 | ReturnValue | ir.cpp:754:8:754:8 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 25 | UnmodeledUse | ir.cpp:754:8:754:8 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 26 | ExitFunction | ir.cpp:754:8:754:8 | +| Middle::~Middle() -> void | 0 | 0 | EnterFunction | ir.cpp:759:3:759:9 | +| Middle::~Middle() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:759:3:759:9 | +| Middle::~Middle() -> void | 0 | 2 | InitializeThis | ir.cpp:759:3:759:9 | +| Middle::~Middle() -> void | 0 | 3 | NoOp | ir.cpp:760:3:760:3 | +| Middle::~Middle() -> void | 0 | 4 | FieldAddress[middle_s] | ir.cpp:760:3:760:3 | +| Middle::~Middle() -> void | 0 | 5 | FunctionAddress[~String] | ir.cpp:760:3:760:3 | +| Middle::~Middle() -> void | 0 | 6 | Invoke | ir.cpp:760:3:760:3 | +| Middle::~Middle() -> void | 0 | 7 | ConvertToBase[Middle : Base] | ir.cpp:760:3:760:3 | +| Middle::~Middle() -> void | 0 | 8 | FunctionAddress[~Base] | ir.cpp:760:3:760:3 | +| Middle::~Middle() -> void | 0 | 9 | Invoke | ir.cpp:760:3:760:3 | +| Middle::~Middle() -> void | 0 | 10 | ReturnVoid | ir.cpp:759:3:759:9 | +| Middle::~Middle() -> void | 0 | 11 | UnmodeledUse | ir.cpp:759:3:759:9 | +| Middle::~Middle() -> void | 0 | 12 | ExitFunction | ir.cpp:759:3:759:9 | +| MiddleVB1::MiddleVB1() -> void | 0 | 0 | EnterFunction | ir.cpp:775:3:775:11 | +| MiddleVB1::MiddleVB1() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:775:3:775:11 | +| MiddleVB1::MiddleVB1() -> void | 0 | 2 | InitializeThis | ir.cpp:775:3:775:11 | +| MiddleVB1::MiddleVB1() -> void | 0 | 3 | ConvertToBase[MiddleVB1 : Base] | ir.cpp:775:15:775:15 | +| MiddleVB1::MiddleVB1() -> void | 0 | 4 | FunctionAddress[Base] | ir.cpp:775:15:775:15 | +| MiddleVB1::MiddleVB1() -> void | 0 | 5 | Invoke | ir.cpp:775:15:775:15 | +| MiddleVB1::MiddleVB1() -> void | 0 | 6 | FieldAddress[middlevb1_s] | ir.cpp:775:15:775:15 | +| MiddleVB1::MiddleVB1() -> void | 0 | 7 | FunctionAddress[String] | ir.cpp:775:15:775:15 | +| MiddleVB1::MiddleVB1() -> void | 0 | 8 | Invoke | ir.cpp:775:15:775:15 | +| MiddleVB1::MiddleVB1() -> void | 0 | 9 | NoOp | ir.cpp:776:3:776:3 | +| MiddleVB1::MiddleVB1() -> void | 0 | 10 | ReturnVoid | ir.cpp:775:3:775:11 | +| MiddleVB1::MiddleVB1() -> void | 0 | 11 | UnmodeledUse | ir.cpp:775:3:775:11 | +| MiddleVB1::MiddleVB1() -> void | 0 | 12 | ExitFunction | ir.cpp:775:3:775:11 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 0 | EnterFunction | ir.cpp:777:3:777:12 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:777:3:777:12 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 2 | InitializeThis | ir.cpp:777:3:777:12 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 3 | NoOp | ir.cpp:778:3:778:3 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 4 | FieldAddress[middlevb1_s] | ir.cpp:778:3:778:3 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 5 | FunctionAddress[~String] | ir.cpp:778:3:778:3 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 6 | Invoke | ir.cpp:778:3:778:3 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 7 | ConvertToBase[MiddleVB1 : Base] | ir.cpp:778:3:778:3 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 8 | FunctionAddress[~Base] | ir.cpp:778:3:778:3 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 9 | Invoke | ir.cpp:778:3:778:3 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 10 | ReturnVoid | ir.cpp:777:3:777:12 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 11 | UnmodeledUse | ir.cpp:777:3:777:12 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 12 | ExitFunction | ir.cpp:777:3:777:12 | +| MiddleVB2::MiddleVB2() -> void | 0 | 0 | EnterFunction | ir.cpp:784:3:784:11 | +| MiddleVB2::MiddleVB2() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:784:3:784:11 | +| MiddleVB2::MiddleVB2() -> void | 0 | 2 | InitializeThis | ir.cpp:784:3:784:11 | +| MiddleVB2::MiddleVB2() -> void | 0 | 3 | ConvertToBase[MiddleVB2 : Base] | ir.cpp:784:15:784:15 | +| MiddleVB2::MiddleVB2() -> void | 0 | 4 | FunctionAddress[Base] | ir.cpp:784:15:784:15 | +| MiddleVB2::MiddleVB2() -> void | 0 | 5 | Invoke | ir.cpp:784:15:784:15 | +| MiddleVB2::MiddleVB2() -> void | 0 | 6 | FieldAddress[middlevb2_s] | ir.cpp:784:15:784:15 | +| MiddleVB2::MiddleVB2() -> void | 0 | 7 | FunctionAddress[String] | ir.cpp:784:15:784:15 | +| MiddleVB2::MiddleVB2() -> void | 0 | 8 | Invoke | ir.cpp:784:15:784:15 | +| MiddleVB2::MiddleVB2() -> void | 0 | 9 | NoOp | ir.cpp:785:3:785:3 | +| MiddleVB2::MiddleVB2() -> void | 0 | 10 | ReturnVoid | ir.cpp:784:3:784:11 | +| MiddleVB2::MiddleVB2() -> void | 0 | 11 | UnmodeledUse | ir.cpp:784:3:784:11 | +| MiddleVB2::MiddleVB2() -> void | 0 | 12 | ExitFunction | ir.cpp:784:3:784:11 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 0 | EnterFunction | ir.cpp:786:3:786:12 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:786:3:786:12 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 2 | InitializeThis | ir.cpp:786:3:786:12 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 3 | NoOp | ir.cpp:787:3:787:3 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 4 | FieldAddress[middlevb2_s] | ir.cpp:787:3:787:3 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 5 | FunctionAddress[~String] | ir.cpp:787:3:787:3 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 6 | Invoke | ir.cpp:787:3:787:3 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 7 | ConvertToBase[MiddleVB2 : Base] | ir.cpp:787:3:787:3 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 8 | FunctionAddress[~Base] | ir.cpp:787:3:787:3 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 9 | Invoke | ir.cpp:787:3:787:3 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 10 | ReturnVoid | ir.cpp:786:3:786:12 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 11 | UnmodeledUse | ir.cpp:786:3:786:12 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 12 | ExitFunction | ir.cpp:786:3:786:12 | +| NestedInitList(int, float) -> void | 0 | 0 | EnterFunction | ir.cpp:512:6:512:19 | +| NestedInitList(int, float) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:512:6:512:19 | +| NestedInitList(int, float) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:512:25:512:25 | +| NestedInitList(int, float) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:512:25:512:25 | +| NestedInitList(int, float) -> void | 0 | 4 | Store | ir.cpp:512:25:512:25 | +| NestedInitList(int, float) -> void | 0 | 5 | InitializeParameter[f] | ir.cpp:512:34:512:34 | +| NestedInitList(int, float) -> void | 0 | 6 | VariableAddress[f] | ir.cpp:512:34:512:34 | +| NestedInitList(int, float) -> void | 0 | 7 | Store | ir.cpp:512:34:512:34 | +| NestedInitList(int, float) -> void | 0 | 8 | VariableAddress[r1] | ir.cpp:513:10:513:11 | +| NestedInitList(int, float) -> void | 0 | 9 | FieldAddress[topLeft] | ir.cpp:513:14:513:16 | +| NestedInitList(int, float) -> void | 0 | 10 | Constant[0] | ir.cpp:513:14:513:16 | +| NestedInitList(int, float) -> void | 0 | 11 | Store | ir.cpp:513:14:513:16 | +| NestedInitList(int, float) -> void | 0 | 12 | FieldAddress[bottomRight] | ir.cpp:513:14:513:16 | +| NestedInitList(int, float) -> void | 0 | 13 | Constant[0] | ir.cpp:513:14:513:16 | +| NestedInitList(int, float) -> void | 0 | 14 | Store | ir.cpp:513:14:513:16 | +| NestedInitList(int, float) -> void | 0 | 15 | VariableAddress[r2] | ir.cpp:514:10:514:11 | +| NestedInitList(int, float) -> void | 0 | 16 | FieldAddress[topLeft] | ir.cpp:514:14:514:26 | +| NestedInitList(int, float) -> void | 0 | 17 | FieldAddress[x] | ir.cpp:514:17:514:24 | +| NestedInitList(int, float) -> void | 0 | 18 | VariableAddress[x] | ir.cpp:514:19:514:19 | +| NestedInitList(int, float) -> void | 0 | 19 | Load | ir.cpp:514:19:514:19 | +| NestedInitList(int, float) -> void | 0 | 20 | Store | ir.cpp:514:19:514:19 | +| NestedInitList(int, float) -> void | 0 | 21 | FieldAddress[y] | ir.cpp:514:17:514:24 | +| NestedInitList(int, float) -> void | 0 | 22 | VariableAddress[f] | ir.cpp:514:22:514:22 | +| NestedInitList(int, float) -> void | 0 | 23 | Load | ir.cpp:514:22:514:22 | +| NestedInitList(int, float) -> void | 0 | 24 | Convert | ir.cpp:514:22:514:22 | +| NestedInitList(int, float) -> void | 0 | 25 | Store | ir.cpp:514:22:514:22 | +| NestedInitList(int, float) -> void | 0 | 26 | FieldAddress[bottomRight] | ir.cpp:514:14:514:26 | +| NestedInitList(int, float) -> void | 0 | 27 | Constant[0] | ir.cpp:514:14:514:26 | +| NestedInitList(int, float) -> void | 0 | 28 | Store | ir.cpp:514:14:514:26 | +| NestedInitList(int, float) -> void | 0 | 29 | VariableAddress[r3] | ir.cpp:515:10:515:11 | +| NestedInitList(int, float) -> void | 0 | 30 | FieldAddress[topLeft] | ir.cpp:515:14:515:36 | +| NestedInitList(int, float) -> void | 0 | 31 | FieldAddress[x] | ir.cpp:515:17:515:24 | +| NestedInitList(int, float) -> void | 0 | 32 | VariableAddress[x] | ir.cpp:515:19:515:19 | +| NestedInitList(int, float) -> void | 0 | 33 | Load | ir.cpp:515:19:515:19 | +| NestedInitList(int, float) -> void | 0 | 34 | Store | ir.cpp:515:19:515:19 | +| NestedInitList(int, float) -> void | 0 | 35 | FieldAddress[y] | ir.cpp:515:17:515:24 | +| NestedInitList(int, float) -> void | 0 | 36 | VariableAddress[f] | ir.cpp:515:22:515:22 | +| NestedInitList(int, float) -> void | 0 | 37 | Load | ir.cpp:515:22:515:22 | +| NestedInitList(int, float) -> void | 0 | 38 | Convert | ir.cpp:515:22:515:22 | +| NestedInitList(int, float) -> void | 0 | 39 | Store | ir.cpp:515:22:515:22 | +| NestedInitList(int, float) -> void | 0 | 40 | FieldAddress[bottomRight] | ir.cpp:515:14:515:36 | +| NestedInitList(int, float) -> void | 0 | 41 | FieldAddress[x] | ir.cpp:515:27:515:34 | +| NestedInitList(int, float) -> void | 0 | 42 | VariableAddress[x] | ir.cpp:515:29:515:29 | +| NestedInitList(int, float) -> void | 0 | 43 | Load | ir.cpp:515:29:515:29 | +| NestedInitList(int, float) -> void | 0 | 44 | Store | ir.cpp:515:29:515:29 | +| NestedInitList(int, float) -> void | 0 | 45 | FieldAddress[y] | ir.cpp:515:27:515:34 | +| NestedInitList(int, float) -> void | 0 | 46 | VariableAddress[f] | ir.cpp:515:32:515:32 | +| NestedInitList(int, float) -> void | 0 | 47 | Load | ir.cpp:515:32:515:32 | +| NestedInitList(int, float) -> void | 0 | 48 | Convert | ir.cpp:515:32:515:32 | +| NestedInitList(int, float) -> void | 0 | 49 | Store | ir.cpp:515:32:515:32 | +| NestedInitList(int, float) -> void | 0 | 50 | VariableAddress[r4] | ir.cpp:516:10:516:11 | +| NestedInitList(int, float) -> void | 0 | 51 | FieldAddress[topLeft] | ir.cpp:516:14:516:30 | +| NestedInitList(int, float) -> void | 0 | 52 | FieldAddress[x] | ir.cpp:516:17:516:21 | +| NestedInitList(int, float) -> void | 0 | 53 | VariableAddress[x] | ir.cpp:516:19:516:19 | +| NestedInitList(int, float) -> void | 0 | 54 | Load | ir.cpp:516:19:516:19 | +| NestedInitList(int, float) -> void | 0 | 55 | Store | ir.cpp:516:19:516:19 | +| NestedInitList(int, float) -> void | 0 | 56 | FieldAddress[y] | ir.cpp:516:17:516:21 | +| NestedInitList(int, float) -> void | 0 | 57 | Constant[0] | ir.cpp:516:17:516:21 | +| NestedInitList(int, float) -> void | 0 | 58 | Store | ir.cpp:516:17:516:21 | +| NestedInitList(int, float) -> void | 0 | 59 | FieldAddress[bottomRight] | ir.cpp:516:14:516:30 | +| NestedInitList(int, float) -> void | 0 | 60 | FieldAddress[x] | ir.cpp:516:24:516:28 | +| NestedInitList(int, float) -> void | 0 | 61 | VariableAddress[x] | ir.cpp:516:26:516:26 | +| NestedInitList(int, float) -> void | 0 | 62 | Load | ir.cpp:516:26:516:26 | +| NestedInitList(int, float) -> void | 0 | 63 | Store | ir.cpp:516:26:516:26 | +| NestedInitList(int, float) -> void | 0 | 64 | FieldAddress[y] | ir.cpp:516:24:516:28 | +| NestedInitList(int, float) -> void | 0 | 65 | Constant[0] | ir.cpp:516:24:516:28 | +| NestedInitList(int, float) -> void | 0 | 66 | Store | ir.cpp:516:24:516:28 | +| NestedInitList(int, float) -> void | 0 | 67 | NoOp | ir.cpp:517:1:517:1 | +| NestedInitList(int, float) -> void | 0 | 68 | ReturnVoid | ir.cpp:512:6:512:19 | +| NestedInitList(int, float) -> void | 0 | 69 | UnmodeledUse | ir.cpp:512:6:512:19 | +| NestedInitList(int, float) -> void | 0 | 70 | ExitFunction | ir.cpp:512:6:512:19 | +| Nullptr() -> void | 0 | 0 | EnterFunction | ir.cpp:496:6:496:12 | +| Nullptr() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:496:6:496:12 | +| Nullptr() -> void | 0 | 2 | VariableAddress[p] | ir.cpp:497:10:497:10 | +| Nullptr() -> void | 0 | 3 | Constant[0] | ir.cpp:497:14:497:20 | +| Nullptr() -> void | 0 | 4 | Store | ir.cpp:497:14:497:20 | +| Nullptr() -> void | 0 | 5 | VariableAddress[q] | ir.cpp:498:10:498:10 | +| Nullptr() -> void | 0 | 6 | Constant[0] | ir.cpp:498:14:498:14 | +| Nullptr() -> void | 0 | 7 | Store | ir.cpp:498:14:498:14 | +| Nullptr() -> void | 0 | 8 | Constant[0] | ir.cpp:499:9:499:15 | +| Nullptr() -> void | 0 | 9 | VariableAddress[p] | ir.cpp:499:5:499:5 | +| Nullptr() -> void | 0 | 10 | Store | ir.cpp:499:5:499:15 | +| Nullptr() -> void | 0 | 11 | Constant[0] | ir.cpp:500:9:500:9 | +| Nullptr() -> void | 0 | 12 | VariableAddress[q] | ir.cpp:500:5:500:5 | +| Nullptr() -> void | 0 | 13 | Store | ir.cpp:500:5:500:9 | +| Nullptr() -> void | 0 | 14 | NoOp | ir.cpp:501:1:501:1 | +| Nullptr() -> void | 0 | 15 | ReturnVoid | ir.cpp:496:6:496:12 | +| Nullptr() -> void | 0 | 16 | UnmodeledUse | ir.cpp:496:6:496:12 | +| Nullptr() -> void | 0 | 17 | ExitFunction | ir.cpp:496:6:496:12 | +| Outer::Func(void *, char) -> long | 0 | 0 | EnterFunction | ir.cpp:715:12:715:15 | +| Outer::Func(void *, char) -> long | 0 | 1 | UnmodeledDefinition | ir.cpp:715:12:715:15 | +| Outer::Func(void *, char) -> long | 0 | 2 | InitializeParameter[x] | ir.cpp:715:19:715:19 | +| Outer::Func(void *, char) -> long | 0 | 3 | VariableAddress[x] | ir.cpp:715:19:715:19 | +| Outer::Func(void *, char) -> long | 0 | 4 | Store | ir.cpp:715:19:715:19 | +| Outer::Func(void *, char) -> long | 0 | 5 | InitializeParameter[y] | ir.cpp:715:24:715:24 | +| Outer::Func(void *, char) -> long | 0 | 6 | VariableAddress[y] | ir.cpp:715:24:715:24 | +| Outer::Func(void *, char) -> long | 0 | 7 | Store | ir.cpp:715:24:715:24 | +| Outer::Func(void *, char) -> long | 0 | 8 | VariableAddress[#return] | ir.cpp:716:5:716:15 | +| Outer::Func(void *, char) -> long | 0 | 9 | Constant[0] | ir.cpp:716:12:716:14 | +| Outer::Func(void *, char) -> long | 0 | 10 | Store | ir.cpp:716:12:716:14 | +| Outer::Func(void *, char) -> long | 0 | 11 | VariableAddress[#return] | ir.cpp:715:12:715:15 | +| Outer::Func(void *, char) -> long | 0 | 12 | ReturnValue | ir.cpp:715:12:715:15 | +| Outer::Func(void *, char) -> long | 0 | 13 | UnmodeledUse | ir.cpp:715:12:715:15 | +| Outer::Func(void *, char) -> long | 0 | 14 | ExitFunction | ir.cpp:715:12:715:15 | +| Parameters(int, int) -> int | 0 | 0 | EnterFunction | ir.cpp:235:5:235:14 | +| Parameters(int, int) -> int | 0 | 1 | UnmodeledDefinition | ir.cpp:235:5:235:14 | +| Parameters(int, int) -> int | 0 | 2 | InitializeParameter[x] | ir.cpp:235:20:235:20 | +| Parameters(int, int) -> int | 0 | 3 | VariableAddress[x] | ir.cpp:235:20:235:20 | +| Parameters(int, int) -> int | 0 | 4 | Store | ir.cpp:235:20:235:20 | +| Parameters(int, int) -> int | 0 | 5 | InitializeParameter[y] | ir.cpp:235:27:235:27 | +| Parameters(int, int) -> int | 0 | 6 | VariableAddress[y] | ir.cpp:235:27:235:27 | +| Parameters(int, int) -> int | 0 | 7 | Store | ir.cpp:235:27:235:27 | +| Parameters(int, int) -> int | 0 | 8 | VariableAddress[#return] | ir.cpp:236:5:236:17 | +| Parameters(int, int) -> int | 0 | 9 | VariableAddress[x] | ir.cpp:236:12:236:12 | +| Parameters(int, int) -> int | 0 | 10 | Load | ir.cpp:236:12:236:12 | +| Parameters(int, int) -> int | 0 | 11 | VariableAddress[y] | ir.cpp:236:16:236:16 | +| Parameters(int, int) -> int | 0 | 12 | Load | ir.cpp:236:16:236:16 | +| Parameters(int, int) -> int | 0 | 13 | Rem | ir.cpp:236:12:236:16 | +| Parameters(int, int) -> int | 0 | 14 | Store | ir.cpp:236:12:236:16 | +| Parameters(int, int) -> int | 0 | 15 | VariableAddress[#return] | ir.cpp:235:5:235:14 | +| Parameters(int, int) -> int | 0 | 16 | ReturnValue | ir.cpp:235:5:235:14 | +| Parameters(int, int) -> int | 0 | 17 | UnmodeledUse | ir.cpp:235:5:235:14 | +| Parameters(int, int) -> int | 0 | 18 | ExitFunction | ir.cpp:235:5:235:14 | +| PointerCompare(int *, int *) -> void | 0 | 0 | EnterFunction | ir.cpp:193:6:193:19 | +| PointerCompare(int *, int *) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:193:6:193:19 | +| PointerCompare(int *, int *) -> void | 0 | 2 | InitializeParameter[p] | ir.cpp:193:26:193:26 | +| PointerCompare(int *, int *) -> void | 0 | 3 | VariableAddress[p] | ir.cpp:193:26:193:26 | +| PointerCompare(int *, int *) -> void | 0 | 4 | Store | ir.cpp:193:26:193:26 | +| PointerCompare(int *, int *) -> void | 0 | 5 | InitializeParameter[q] | ir.cpp:193:34:193:34 | +| PointerCompare(int *, int *) -> void | 0 | 6 | VariableAddress[q] | ir.cpp:193:34:193:34 | +| PointerCompare(int *, int *) -> void | 0 | 7 | Store | ir.cpp:193:34:193:34 | +| PointerCompare(int *, int *) -> void | 0 | 8 | VariableAddress[b] | ir.cpp:194:10:194:10 | +| PointerCompare(int *, int *) -> void | 0 | 9 | Uninitialized | ir.cpp:194:10:194:10 | +| PointerCompare(int *, int *) -> void | 0 | 10 | Store | ir.cpp:194:10:194:10 | +| PointerCompare(int *, int *) -> void | 0 | 11 | VariableAddress[p] | ir.cpp:196:9:196:9 | +| PointerCompare(int *, int *) -> void | 0 | 12 | Load | ir.cpp:196:9:196:9 | +| PointerCompare(int *, int *) -> void | 0 | 13 | VariableAddress[q] | ir.cpp:196:14:196:14 | +| PointerCompare(int *, int *) -> void | 0 | 14 | Load | ir.cpp:196:14:196:14 | +| PointerCompare(int *, int *) -> void | 0 | 15 | CompareEQ | ir.cpp:196:9:196:14 | +| PointerCompare(int *, int *) -> void | 0 | 16 | VariableAddress[b] | ir.cpp:196:5:196:5 | +| PointerCompare(int *, int *) -> void | 0 | 17 | Store | ir.cpp:196:5:196:14 | +| PointerCompare(int *, int *) -> void | 0 | 18 | VariableAddress[p] | ir.cpp:197:9:197:9 | +| PointerCompare(int *, int *) -> void | 0 | 19 | Load | ir.cpp:197:9:197:9 | +| PointerCompare(int *, int *) -> void | 0 | 20 | VariableAddress[q] | ir.cpp:197:14:197:14 | +| PointerCompare(int *, int *) -> void | 0 | 21 | Load | ir.cpp:197:14:197:14 | +| PointerCompare(int *, int *) -> void | 0 | 22 | CompareNE | ir.cpp:197:9:197:14 | +| PointerCompare(int *, int *) -> void | 0 | 23 | VariableAddress[b] | ir.cpp:197:5:197:5 | +| PointerCompare(int *, int *) -> void | 0 | 24 | Store | ir.cpp:197:5:197:14 | +| PointerCompare(int *, int *) -> void | 0 | 25 | VariableAddress[p] | ir.cpp:198:9:198:9 | +| PointerCompare(int *, int *) -> void | 0 | 26 | Load | ir.cpp:198:9:198:9 | +| PointerCompare(int *, int *) -> void | 0 | 27 | VariableAddress[q] | ir.cpp:198:13:198:13 | +| PointerCompare(int *, int *) -> void | 0 | 28 | Load | ir.cpp:198:13:198:13 | +| PointerCompare(int *, int *) -> void | 0 | 29 | CompareLT | ir.cpp:198:9:198:13 | +| PointerCompare(int *, int *) -> void | 0 | 30 | VariableAddress[b] | ir.cpp:198:5:198:5 | +| PointerCompare(int *, int *) -> void | 0 | 31 | Store | ir.cpp:198:5:198:13 | +| PointerCompare(int *, int *) -> void | 0 | 32 | VariableAddress[p] | ir.cpp:199:9:199:9 | +| PointerCompare(int *, int *) -> void | 0 | 33 | Load | ir.cpp:199:9:199:9 | +| PointerCompare(int *, int *) -> void | 0 | 34 | VariableAddress[q] | ir.cpp:199:13:199:13 | +| PointerCompare(int *, int *) -> void | 0 | 35 | Load | ir.cpp:199:13:199:13 | +| PointerCompare(int *, int *) -> void | 0 | 36 | CompareGT | ir.cpp:199:9:199:13 | +| PointerCompare(int *, int *) -> void | 0 | 37 | VariableAddress[b] | ir.cpp:199:5:199:5 | +| PointerCompare(int *, int *) -> void | 0 | 38 | Store | ir.cpp:199:5:199:13 | +| PointerCompare(int *, int *) -> void | 0 | 39 | VariableAddress[p] | ir.cpp:200:9:200:9 | +| PointerCompare(int *, int *) -> void | 0 | 40 | Load | ir.cpp:200:9:200:9 | +| PointerCompare(int *, int *) -> void | 0 | 41 | VariableAddress[q] | ir.cpp:200:14:200:14 | +| PointerCompare(int *, int *) -> void | 0 | 42 | Load | ir.cpp:200:14:200:14 | +| PointerCompare(int *, int *) -> void | 0 | 43 | CompareLE | ir.cpp:200:9:200:14 | +| PointerCompare(int *, int *) -> void | 0 | 44 | VariableAddress[b] | ir.cpp:200:5:200:5 | +| PointerCompare(int *, int *) -> void | 0 | 45 | Store | ir.cpp:200:5:200:14 | +| PointerCompare(int *, int *) -> void | 0 | 46 | VariableAddress[p] | ir.cpp:201:9:201:9 | +| PointerCompare(int *, int *) -> void | 0 | 47 | Load | ir.cpp:201:9:201:9 | +| PointerCompare(int *, int *) -> void | 0 | 48 | VariableAddress[q] | ir.cpp:201:14:201:14 | +| PointerCompare(int *, int *) -> void | 0 | 49 | Load | ir.cpp:201:14:201:14 | +| PointerCompare(int *, int *) -> void | 0 | 50 | CompareGE | ir.cpp:201:9:201:14 | +| PointerCompare(int *, int *) -> void | 0 | 51 | VariableAddress[b] | ir.cpp:201:5:201:5 | +| PointerCompare(int *, int *) -> void | 0 | 52 | Store | ir.cpp:201:5:201:14 | +| PointerCompare(int *, int *) -> void | 0 | 53 | NoOp | ir.cpp:202:1:202:1 | +| PointerCompare(int *, int *) -> void | 0 | 54 | ReturnVoid | ir.cpp:193:6:193:19 | +| PointerCompare(int *, int *) -> void | 0 | 55 | UnmodeledUse | ir.cpp:193:6:193:19 | +| PointerCompare(int *, int *) -> void | 0 | 56 | ExitFunction | ir.cpp:193:6:193:19 | +| PointerCrement(int *) -> void | 0 | 0 | EnterFunction | ir.cpp:204:6:204:19 | +| PointerCrement(int *) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:204:6:204:19 | +| PointerCrement(int *) -> void | 0 | 2 | InitializeParameter[p] | ir.cpp:204:26:204:26 | +| PointerCrement(int *) -> void | 0 | 3 | VariableAddress[p] | ir.cpp:204:26:204:26 | +| PointerCrement(int *) -> void | 0 | 4 | Store | ir.cpp:204:26:204:26 | +| PointerCrement(int *) -> void | 0 | 5 | VariableAddress[q] | ir.cpp:205:10:205:10 | +| PointerCrement(int *) -> void | 0 | 6 | Uninitialized | ir.cpp:205:10:205:10 | +| PointerCrement(int *) -> void | 0 | 7 | Store | ir.cpp:205:10:205:10 | +| PointerCrement(int *) -> void | 0 | 8 | VariableAddress[p] | ir.cpp:207:11:207:11 | +| PointerCrement(int *) -> void | 0 | 9 | Load | ir.cpp:207:9:207:11 | +| PointerCrement(int *) -> void | 0 | 10 | Constant[1] | ir.cpp:207:9:207:11 | +| PointerCrement(int *) -> void | 0 | 11 | PointerAdd[4] | ir.cpp:207:9:207:11 | +| PointerCrement(int *) -> void | 0 | 12 | Store | ir.cpp:207:9:207:11 | +| PointerCrement(int *) -> void | 0 | 13 | VariableAddress[q] | ir.cpp:207:5:207:5 | +| PointerCrement(int *) -> void | 0 | 14 | Store | ir.cpp:207:5:207:11 | +| PointerCrement(int *) -> void | 0 | 15 | VariableAddress[p] | ir.cpp:208:11:208:11 | +| PointerCrement(int *) -> void | 0 | 16 | Load | ir.cpp:208:9:208:11 | +| PointerCrement(int *) -> void | 0 | 17 | Constant[1] | ir.cpp:208:9:208:11 | +| PointerCrement(int *) -> void | 0 | 18 | PointerSub[4] | ir.cpp:208:9:208:11 | +| PointerCrement(int *) -> void | 0 | 19 | Store | ir.cpp:208:9:208:11 | +| PointerCrement(int *) -> void | 0 | 20 | VariableAddress[q] | ir.cpp:208:5:208:5 | +| PointerCrement(int *) -> void | 0 | 21 | Store | ir.cpp:208:5:208:11 | +| PointerCrement(int *) -> void | 0 | 22 | VariableAddress[p] | ir.cpp:209:9:209:9 | +| PointerCrement(int *) -> void | 0 | 23 | Load | ir.cpp:209:9:209:11 | +| PointerCrement(int *) -> void | 0 | 24 | Constant[1] | ir.cpp:209:9:209:11 | +| PointerCrement(int *) -> void | 0 | 25 | PointerAdd[4] | ir.cpp:209:9:209:11 | +| PointerCrement(int *) -> void | 0 | 26 | Store | ir.cpp:209:9:209:11 | +| PointerCrement(int *) -> void | 0 | 27 | VariableAddress[q] | ir.cpp:209:5:209:5 | +| PointerCrement(int *) -> void | 0 | 28 | Store | ir.cpp:209:5:209:11 | +| PointerCrement(int *) -> void | 0 | 29 | VariableAddress[p] | ir.cpp:210:9:210:9 | +| PointerCrement(int *) -> void | 0 | 30 | Load | ir.cpp:210:9:210:11 | +| PointerCrement(int *) -> void | 0 | 31 | Constant[1] | ir.cpp:210:9:210:11 | +| PointerCrement(int *) -> void | 0 | 32 | PointerSub[4] | ir.cpp:210:9:210:11 | +| PointerCrement(int *) -> void | 0 | 33 | Store | ir.cpp:210:9:210:11 | +| PointerCrement(int *) -> void | 0 | 34 | VariableAddress[q] | ir.cpp:210:5:210:5 | +| PointerCrement(int *) -> void | 0 | 35 | Store | ir.cpp:210:5:210:11 | +| PointerCrement(int *) -> void | 0 | 36 | NoOp | ir.cpp:211:1:211:1 | +| PointerCrement(int *) -> void | 0 | 37 | ReturnVoid | ir.cpp:204:6:204:19 | +| PointerCrement(int *) -> void | 0 | 38 | UnmodeledUse | ir.cpp:204:6:204:19 | +| PointerCrement(int *) -> void | 0 | 39 | ExitFunction | ir.cpp:204:6:204:19 | +| PointerOps(int *, int) -> void | 0 | 0 | EnterFunction | ir.cpp:153:6:153:15 | +| PointerOps(int *, int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:153:6:153:15 | +| PointerOps(int *, int) -> void | 0 | 2 | InitializeParameter[p] | ir.cpp:153:22:153:22 | +| PointerOps(int *, int) -> void | 0 | 3 | VariableAddress[p] | ir.cpp:153:22:153:22 | +| PointerOps(int *, int) -> void | 0 | 4 | Store | ir.cpp:153:22:153:22 | +| PointerOps(int *, int) -> void | 0 | 5 | InitializeParameter[i] | ir.cpp:153:29:153:29 | +| PointerOps(int *, int) -> void | 0 | 6 | VariableAddress[i] | ir.cpp:153:29:153:29 | +| PointerOps(int *, int) -> void | 0 | 7 | Store | ir.cpp:153:29:153:29 | +| PointerOps(int *, int) -> void | 0 | 8 | VariableAddress[q] | ir.cpp:154:10:154:10 | +| PointerOps(int *, int) -> void | 0 | 9 | Uninitialized | ir.cpp:154:10:154:10 | +| PointerOps(int *, int) -> void | 0 | 10 | Store | ir.cpp:154:10:154:10 | +| PointerOps(int *, int) -> void | 0 | 11 | VariableAddress[b] | ir.cpp:155:10:155:10 | +| PointerOps(int *, int) -> void | 0 | 12 | Uninitialized | ir.cpp:155:10:155:10 | +| PointerOps(int *, int) -> void | 0 | 13 | Store | ir.cpp:155:10:155:10 | +| PointerOps(int *, int) -> void | 0 | 14 | VariableAddress[p] | ir.cpp:157:9:157:9 | +| PointerOps(int *, int) -> void | 0 | 15 | Load | ir.cpp:157:9:157:9 | +| PointerOps(int *, int) -> void | 0 | 16 | VariableAddress[i] | ir.cpp:157:13:157:13 | +| PointerOps(int *, int) -> void | 0 | 17 | Load | ir.cpp:157:13:157:13 | +| PointerOps(int *, int) -> void | 0 | 18 | PointerAdd[4] | ir.cpp:157:9:157:13 | +| PointerOps(int *, int) -> void | 0 | 19 | VariableAddress[q] | ir.cpp:157:5:157:5 | +| PointerOps(int *, int) -> void | 0 | 20 | Store | ir.cpp:157:5:157:13 | +| PointerOps(int *, int) -> void | 0 | 21 | VariableAddress[i] | ir.cpp:158:9:158:9 | +| PointerOps(int *, int) -> void | 0 | 22 | Load | ir.cpp:158:9:158:9 | +| PointerOps(int *, int) -> void | 0 | 23 | VariableAddress[p] | ir.cpp:158:13:158:13 | +| PointerOps(int *, int) -> void | 0 | 24 | Load | ir.cpp:158:13:158:13 | +| PointerOps(int *, int) -> void | 0 | 25 | PointerAdd[4] | ir.cpp:158:9:158:13 | +| PointerOps(int *, int) -> void | 0 | 26 | VariableAddress[q] | ir.cpp:158:5:158:5 | +| PointerOps(int *, int) -> void | 0 | 27 | Store | ir.cpp:158:5:158:13 | +| PointerOps(int *, int) -> void | 0 | 28 | VariableAddress[p] | ir.cpp:159:9:159:9 | +| PointerOps(int *, int) -> void | 0 | 29 | Load | ir.cpp:159:9:159:9 | +| PointerOps(int *, int) -> void | 0 | 30 | VariableAddress[i] | ir.cpp:159:13:159:13 | +| PointerOps(int *, int) -> void | 0 | 31 | Load | ir.cpp:159:13:159:13 | +| PointerOps(int *, int) -> void | 0 | 32 | PointerSub[4] | ir.cpp:159:9:159:13 | +| PointerOps(int *, int) -> void | 0 | 33 | VariableAddress[q] | ir.cpp:159:5:159:5 | +| PointerOps(int *, int) -> void | 0 | 34 | Store | ir.cpp:159:5:159:13 | +| PointerOps(int *, int) -> void | 0 | 35 | VariableAddress[p] | ir.cpp:160:9:160:9 | +| PointerOps(int *, int) -> void | 0 | 36 | Load | ir.cpp:160:9:160:9 | +| PointerOps(int *, int) -> void | 0 | 37 | VariableAddress[q] | ir.cpp:160:13:160:13 | +| PointerOps(int *, int) -> void | 0 | 38 | Load | ir.cpp:160:13:160:13 | +| PointerOps(int *, int) -> void | 0 | 39 | PointerDiff[4] | ir.cpp:160:9:160:13 | +| PointerOps(int *, int) -> void | 0 | 40 | Convert | ir.cpp:160:9:160:13 | +| PointerOps(int *, int) -> void | 0 | 41 | VariableAddress[i] | ir.cpp:160:5:160:5 | +| PointerOps(int *, int) -> void | 0 | 42 | Store | ir.cpp:160:5:160:13 | +| PointerOps(int *, int) -> void | 0 | 43 | VariableAddress[p] | ir.cpp:162:9:162:9 | +| PointerOps(int *, int) -> void | 0 | 44 | Load | ir.cpp:162:9:162:9 | +| PointerOps(int *, int) -> void | 0 | 45 | VariableAddress[q] | ir.cpp:162:5:162:5 | +| PointerOps(int *, int) -> void | 0 | 46 | Store | ir.cpp:162:5:162:9 | +| PointerOps(int *, int) -> void | 0 | 47 | VariableAddress[i] | ir.cpp:164:10:164:10 | +| PointerOps(int *, int) -> void | 0 | 48 | Load | ir.cpp:164:10:164:10 | +| PointerOps(int *, int) -> void | 0 | 49 | VariableAddress[q] | ir.cpp:164:5:164:5 | +| PointerOps(int *, int) -> void | 0 | 50 | Load | ir.cpp:164:5:164:10 | +| PointerOps(int *, int) -> void | 0 | 51 | PointerAdd[4] | ir.cpp:164:5:164:10 | +| PointerOps(int *, int) -> void | 0 | 52 | Store | ir.cpp:164:5:164:10 | +| PointerOps(int *, int) -> void | 0 | 53 | VariableAddress[i] | ir.cpp:165:10:165:10 | +| PointerOps(int *, int) -> void | 0 | 54 | Load | ir.cpp:165:10:165:10 | +| PointerOps(int *, int) -> void | 0 | 55 | VariableAddress[q] | ir.cpp:165:5:165:5 | +| PointerOps(int *, int) -> void | 0 | 56 | Load | ir.cpp:165:5:165:10 | +| PointerOps(int *, int) -> void | 0 | 57 | PointerSub[4] | ir.cpp:165:5:165:10 | +| PointerOps(int *, int) -> void | 0 | 58 | Store | ir.cpp:165:5:165:10 | +| PointerOps(int *, int) -> void | 0 | 59 | VariableAddress[p] | ir.cpp:167:9:167:9 | +| PointerOps(int *, int) -> void | 0 | 60 | Load | ir.cpp:167:9:167:9 | +| PointerOps(int *, int) -> void | 0 | 61 | Constant[0] | ir.cpp:167:9:167:9 | +| PointerOps(int *, int) -> void | 0 | 62 | CompareNE | ir.cpp:167:9:167:9 | +| PointerOps(int *, int) -> void | 0 | 63 | VariableAddress[b] | ir.cpp:167:5:167:5 | +| PointerOps(int *, int) -> void | 0 | 64 | Store | ir.cpp:167:5:167:9 | +| PointerOps(int *, int) -> void | 0 | 65 | VariableAddress[p] | ir.cpp:168:10:168:10 | +| PointerOps(int *, int) -> void | 0 | 66 | Load | ir.cpp:168:10:168:10 | +| PointerOps(int *, int) -> void | 0 | 67 | Constant[0] | ir.cpp:168:10:168:10 | +| PointerOps(int *, int) -> void | 0 | 68 | CompareNE | ir.cpp:168:10:168:10 | +| PointerOps(int *, int) -> void | 0 | 69 | LogicalNot | ir.cpp:168:9:168:10 | +| PointerOps(int *, int) -> void | 0 | 70 | VariableAddress[b] | ir.cpp:168:5:168:5 | +| PointerOps(int *, int) -> void | 0 | 71 | Store | ir.cpp:168:5:168:10 | +| PointerOps(int *, int) -> void | 0 | 72 | NoOp | ir.cpp:169:1:169:1 | +| PointerOps(int *, int) -> void | 0 | 73 | ReturnVoid | ir.cpp:153:6:153:15 | +| PointerOps(int *, int) -> void | 0 | 74 | UnmodeledUse | ir.cpp:153:6:153:15 | +| PointerOps(int *, int) -> void | 0 | 75 | ExitFunction | ir.cpp:153:6:153:15 | +| PolymorphicBase::PolymorphicBase() -> void | 0 | 0 | EnterFunction | ir.cpp:842:8:842:8 | +| PolymorphicBase::PolymorphicBase() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:842:8:842:8 | +| PolymorphicBase::PolymorphicBase() -> void | 0 | 2 | InitializeThis | ir.cpp:842:8:842:8 | +| PolymorphicBase::PolymorphicBase() -> void | 0 | 3 | NoOp | ir.cpp:842:8:842:8 | +| PolymorphicBase::PolymorphicBase() -> void | 0 | 4 | ReturnVoid | ir.cpp:842:8:842:8 | +| PolymorphicBase::PolymorphicBase() -> void | 0 | 5 | UnmodeledUse | ir.cpp:842:8:842:8 | +| PolymorphicBase::PolymorphicBase() -> void | 0 | 6 | ExitFunction | ir.cpp:842:8:842:8 | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 0 | EnterFunction | ir.cpp:846:8:846:8 | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:846:8:846:8 | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 2 | InitializeThis | ir.cpp:846:8:846:8 | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 3 | ConvertToBase[PolymorphicDerived : PolymorphicBase] | ir.cpp:846:8:846:8 | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 4 | FunctionAddress[PolymorphicBase] | ir.cpp:846:8:846:8 | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 5 | Invoke | ir.cpp:846:8:846:8 | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 6 | NoOp | ir.cpp:846:8:846:8 | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 7 | ReturnVoid | ir.cpp:846:8:846:8 | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 8 | UnmodeledUse | ir.cpp:846:8:846:8 | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 9 | ExitFunction | ir.cpp:846:8:846:8 | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 0 | EnterFunction | ir.cpp:846:8:846:8 | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:846:8:846:8 | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 2 | InitializeThis | ir.cpp:846:8:846:8 | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 3 | NoOp | file://:0:0:0:0 | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 4 | ConvertToBase[PolymorphicDerived : PolymorphicBase] | ir.cpp:846:8:846:8 | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 5 | FunctionAddress[~PolymorphicBase] | ir.cpp:846:8:846:8 | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 6 | Invoke | ir.cpp:846:8:846:8 | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 7 | ReturnVoid | ir.cpp:846:8:846:8 | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 8 | UnmodeledUse | ir.cpp:846:8:846:8 | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 9 | ExitFunction | ir.cpp:846:8:846:8 | +| ReturnStruct(Point) -> Point | 0 | 0 | EnterFunction | ir.cpp:422:7:422:18 | +| ReturnStruct(Point) -> Point | 0 | 1 | UnmodeledDefinition | ir.cpp:422:7:422:18 | +| ReturnStruct(Point) -> Point | 0 | 2 | InitializeParameter[pt] | ir.cpp:422:26:422:27 | +| ReturnStruct(Point) -> Point | 0 | 3 | VariableAddress[pt] | ir.cpp:422:26:422:27 | +| ReturnStruct(Point) -> Point | 0 | 4 | Store | ir.cpp:422:26:422:27 | +| ReturnStruct(Point) -> Point | 0 | 5 | VariableAddress[#return] | ir.cpp:423:5:423:14 | +| ReturnStruct(Point) -> Point | 0 | 6 | VariableAddress[pt] | ir.cpp:423:12:423:13 | +| ReturnStruct(Point) -> Point | 0 | 7 | Load | ir.cpp:423:12:423:13 | +| ReturnStruct(Point) -> Point | 0 | 8 | Store | ir.cpp:423:12:423:13 | +| ReturnStruct(Point) -> Point | 0 | 9 | VariableAddress[#return] | ir.cpp:422:7:422:18 | +| ReturnStruct(Point) -> Point | 0 | 10 | ReturnValue | ir.cpp:422:7:422:18 | +| ReturnStruct(Point) -> Point | 0 | 11 | UnmodeledUse | ir.cpp:422:7:422:18 | +| ReturnStruct(Point) -> Point | 0 | 12 | ExitFunction | ir.cpp:422:7:422:18 | +| SetFuncPtr() -> void | 0 | 0 | EnterFunction | ir.cpp:590:6:590:15 | +| SetFuncPtr() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:590:6:590:15 | +| SetFuncPtr() -> void | 0 | 2 | VariableAddress[pfn] | ir.cpp:591:11:591:13 | +| SetFuncPtr() -> void | 0 | 3 | FunctionAddress[FuncPtrTarget] | ir.cpp:591:23:591:35 | +| SetFuncPtr() -> void | 0 | 4 | Store | ir.cpp:591:23:591:35 | +| SetFuncPtr() -> void | 0 | 5 | FunctionAddress[FuncPtrTarget] | ir.cpp:592:12:592:24 | +| SetFuncPtr() -> void | 0 | 6 | VariableAddress[pfn] | ir.cpp:592:5:592:7 | +| SetFuncPtr() -> void | 0 | 7 | Store | ir.cpp:592:5:592:24 | +| SetFuncPtr() -> void | 0 | 8 | FunctionAddress[FuncPtrTarget] | ir.cpp:593:12:593:24 | +| SetFuncPtr() -> void | 0 | 9 | VariableAddress[pfn] | ir.cpp:593:5:593:7 | +| SetFuncPtr() -> void | 0 | 10 | Store | ir.cpp:593:5:593:24 | +| SetFuncPtr() -> void | 0 | 11 | FunctionAddress[FuncPtrTarget] | ir.cpp:594:15:594:27 | +| SetFuncPtr() -> void | 0 | 12 | VariableAddress[pfn] | ir.cpp:594:5:594:7 | +| SetFuncPtr() -> void | 0 | 13 | Store | ir.cpp:594:5:594:27 | +| SetFuncPtr() -> void | 0 | 14 | NoOp | ir.cpp:595:1:595:1 | +| SetFuncPtr() -> void | 0 | 15 | ReturnVoid | ir.cpp:590:6:590:15 | +| SetFuncPtr() -> void | 0 | 16 | UnmodeledUse | ir.cpp:590:6:590:15 | +| SetFuncPtr() -> void | 0 | 17 | ExitFunction | ir.cpp:590:6:590:15 | +| String::String() -> void | 0 | 0 | EnterFunction | ir.cpp:867:1:867:14 | +| String::String() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:867:1:867:14 | +| String::String() -> void | 0 | 2 | InitializeThis | ir.cpp:867:1:867:14 | +| String::String() -> void | 0 | 3 | FunctionAddress[String] | ir.cpp:868:3:868:12 | +| String::String() -> void | 0 | 4 | StringConstant[""] | ir.cpp:868:10:868:11 | +| String::String() -> void | 0 | 5 | Convert | ir.cpp:868:10:868:11 | +| String::String() -> void | 0 | 6 | Invoke | ir.cpp:868:3:868:12 | +| String::String() -> void | 0 | 7 | NoOp | ir.cpp:869:1:869:1 | +| String::String() -> void | 0 | 8 | ReturnVoid | ir.cpp:867:1:867:14 | +| String::String() -> void | 0 | 9 | UnmodeledUse | ir.cpp:867:1:867:14 | +| String::String() -> void | 0 | 10 | ExitFunction | ir.cpp:867:1:867:14 | +| StringLiteral(int) -> void | 0 | 0 | EnterFunction | ir.cpp:187:6:187:18 | +| StringLiteral(int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:187:6:187:18 | +| StringLiteral(int) -> void | 0 | 2 | InitializeParameter[i] | ir.cpp:187:24:187:24 | +| StringLiteral(int) -> void | 0 | 3 | VariableAddress[i] | ir.cpp:187:24:187:24 | +| StringLiteral(int) -> void | 0 | 4 | Store | ir.cpp:187:24:187:24 | +| StringLiteral(int) -> void | 0 | 5 | VariableAddress[c] | ir.cpp:188:10:188:10 | +| StringLiteral(int) -> void | 0 | 6 | StringConstant["Foo"] | ir.cpp:188:14:188:18 | +| StringLiteral(int) -> void | 0 | 7 | Convert | ir.cpp:188:14:188:18 | +| StringLiteral(int) -> void | 0 | 8 | VariableAddress[i] | ir.cpp:188:20:188:20 | +| StringLiteral(int) -> void | 0 | 9 | Load | ir.cpp:188:20:188:20 | +| StringLiteral(int) -> void | 0 | 10 | PointerAdd[1] | ir.cpp:188:14:188:21 | +| StringLiteral(int) -> void | 0 | 11 | Load | ir.cpp:188:14:188:21 | +| StringLiteral(int) -> void | 0 | 12 | Store | ir.cpp:188:14:188:21 | +| StringLiteral(int) -> void | 0 | 13 | VariableAddress[pwc] | ir.cpp:189:14:189:16 | +| StringLiteral(int) -> void | 0 | 14 | StringConstant[L"Bar"] | ir.cpp:189:20:189:25 | +| StringLiteral(int) -> void | 0 | 15 | Convert | ir.cpp:189:20:189:25 | +| StringLiteral(int) -> void | 0 | 16 | Convert | ir.cpp:189:20:189:25 | +| StringLiteral(int) -> void | 0 | 17 | Store | ir.cpp:189:20:189:25 | +| StringLiteral(int) -> void | 0 | 18 | VariableAddress[wc] | ir.cpp:190:13:190:14 | +| StringLiteral(int) -> void | 0 | 19 | VariableAddress[pwc] | ir.cpp:190:18:190:20 | +| StringLiteral(int) -> void | 0 | 20 | Load | ir.cpp:190:18:190:20 | +| StringLiteral(int) -> void | 0 | 21 | VariableAddress[i] | ir.cpp:190:22:190:22 | +| StringLiteral(int) -> void | 0 | 22 | Load | ir.cpp:190:22:190:22 | +| StringLiteral(int) -> void | 0 | 23 | PointerAdd[4] | ir.cpp:190:18:190:23 | +| StringLiteral(int) -> void | 0 | 24 | Load | ir.cpp:190:18:190:23 | +| StringLiteral(int) -> void | 0 | 25 | Store | ir.cpp:190:18:190:23 | +| StringLiteral(int) -> void | 0 | 26 | NoOp | ir.cpp:191:1:191:1 | +| StringLiteral(int) -> void | 0 | 27 | ReturnVoid | ir.cpp:187:6:187:18 | +| StringLiteral(int) -> void | 0 | 28 | UnmodeledUse | ir.cpp:187:6:187:18 | +| StringLiteral(int) -> void | 0 | 29 | ExitFunction | ir.cpp:187:6:187:18 | +| Switch(int) -> void | 0 | 0 | EnterFunction | ir.cpp:384:6:384:11 | +| Switch(int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:384:6:384:11 | +| Switch(int) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:384:17:384:17 | +| Switch(int) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:384:17:384:17 | +| Switch(int) -> void | 0 | 4 | Store | ir.cpp:384:17:384:17 | +| Switch(int) -> void | 0 | 5 | VariableAddress[y] | ir.cpp:385:9:385:9 | +| Switch(int) -> void | 0 | 6 | Uninitialized | ir.cpp:385:9:385:9 | +| Switch(int) -> void | 0 | 7 | Store | ir.cpp:385:9:385:9 | +| Switch(int) -> void | 0 | 8 | VariableAddress[x] | ir.cpp:386:13:386:13 | +| Switch(int) -> void | 0 | 9 | Load | ir.cpp:386:13:386:13 | +| Switch(int) -> void | 0 | 10 | Switch | ir.cpp:386:5:409:5 | +| Switch(int) -> void | 1 | 0 | Constant[1234] | ir.cpp:387:13:387:16 | +| Switch(int) -> void | 1 | 1 | VariableAddress[y] | ir.cpp:387:9:387:9 | +| Switch(int) -> void | 1 | 2 | Store | ir.cpp:387:9:387:16 | +| Switch(int) -> void | 2 | 0 | NoOp | ir.cpp:389:9:389:16 | +| Switch(int) -> void | 2 | 1 | Constant[-1] | ir.cpp:390:17:390:18 | +| Switch(int) -> void | 2 | 2 | VariableAddress[y] | ir.cpp:390:13:390:13 | +| Switch(int) -> void | 2 | 3 | Store | ir.cpp:390:13:390:18 | +| Switch(int) -> void | 2 | 4 | NoOp | ir.cpp:391:13:391:18 | +| Switch(int) -> void | 3 | 0 | NoOp | ir.cpp:393:9:393:15 | +| Switch(int) -> void | 4 | 0 | NoOp | ir.cpp:394:9:394:15 | +| Switch(int) -> void | 4 | 1 | Constant[1] | ir.cpp:395:17:395:17 | +| Switch(int) -> void | 4 | 2 | VariableAddress[y] | ir.cpp:395:13:395:13 | +| Switch(int) -> void | 4 | 3 | Store | ir.cpp:395:13:395:17 | +| Switch(int) -> void | 4 | 4 | NoOp | ir.cpp:396:13:396:18 | +| Switch(int) -> void | 5 | 0 | NoOp | ir.cpp:398:9:398:15 | +| Switch(int) -> void | 5 | 1 | Constant[3] | ir.cpp:399:17:399:17 | +| Switch(int) -> void | 5 | 2 | VariableAddress[y] | ir.cpp:399:13:399:13 | +| Switch(int) -> void | 5 | 3 | Store | ir.cpp:399:13:399:17 | +| Switch(int) -> void | 6 | 0 | NoOp | ir.cpp:400:9:400:15 | +| Switch(int) -> void | 6 | 1 | Constant[4] | ir.cpp:401:17:401:17 | +| Switch(int) -> void | 6 | 2 | VariableAddress[y] | ir.cpp:401:13:401:13 | +| Switch(int) -> void | 6 | 3 | Store | ir.cpp:401:13:401:17 | +| Switch(int) -> void | 6 | 4 | NoOp | ir.cpp:402:13:402:18 | +| Switch(int) -> void | 7 | 0 | NoOp | ir.cpp:404:9:404:16 | +| Switch(int) -> void | 7 | 1 | Constant[0] | ir.cpp:405:17:405:17 | +| Switch(int) -> void | 7 | 2 | VariableAddress[y] | ir.cpp:405:13:405:13 | +| Switch(int) -> void | 7 | 3 | Store | ir.cpp:405:13:405:17 | +| Switch(int) -> void | 7 | 4 | NoOp | ir.cpp:406:13:406:18 | +| Switch(int) -> void | 8 | 0 | Constant[5678] | ir.cpp:408:13:408:16 | +| Switch(int) -> void | 8 | 1 | VariableAddress[y] | ir.cpp:408:9:408:9 | +| Switch(int) -> void | 8 | 2 | Store | ir.cpp:408:9:408:16 | +| Switch(int) -> void | 9 | 0 | NoOp | ir.cpp:409:5:409:5 | +| Switch(int) -> void | 9 | 1 | NoOp | ir.cpp:410:1:410:1 | +| Switch(int) -> void | 9 | 2 | ReturnVoid | ir.cpp:384:6:384:11 | +| Switch(int) -> void | 9 | 3 | UnmodeledUse | ir.cpp:384:6:384:11 | +| Switch(int) -> void | 9 | 4 | ExitFunction | ir.cpp:384:6:384:11 | +| TakeReference() -> int & | 0 | 0 | EnterFunction | ir.cpp:679:6:679:18 | +| TakeReference() -> int & | 0 | 1 | UnmodeledDefinition | ir.cpp:679:6:679:18 | +| TakeReference() -> int & | 0 | 2 | VariableAddress[#return] | ir.cpp:680:5:680:13 | +| TakeReference() -> int & | 0 | 3 | VariableAddress[g] | ir.cpp:680:12:680:12 | +| TakeReference() -> int & | 0 | 4 | Store | ir.cpp:680:12:680:12 | +| TakeReference() -> int & | 0 | 5 | VariableAddress[#return] | ir.cpp:679:6:679:18 | +| TakeReference() -> int & | 0 | 6 | ReturnValue | ir.cpp:679:6:679:18 | +| TakeReference() -> int & | 0 | 7 | UnmodeledUse | ir.cpp:679:6:679:18 | +| TakeReference() -> int & | 0 | 8 | ExitFunction | ir.cpp:679:6:679:18 | +| TryCatch(bool) -> void | 0 | 0 | EnterFunction | ir.cpp:724:6:724:13 | +| TryCatch(bool) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:724:6:724:13 | +| TryCatch(bool) -> void | 0 | 2 | InitializeParameter[b] | ir.cpp:724:20:724:20 | +| TryCatch(bool) -> void | 0 | 3 | VariableAddress[b] | ir.cpp:724:20:724:20 | +| TryCatch(bool) -> void | 0 | 4 | Store | ir.cpp:724:20:724:20 | +| TryCatch(bool) -> void | 0 | 5 | VariableAddress[x] | ir.cpp:726:9:726:9 | +| TryCatch(bool) -> void | 0 | 6 | Constant[5] | ir.cpp:726:12:726:13 | +| TryCatch(bool) -> void | 0 | 7 | Store | ir.cpp:726:12:726:13 | +| TryCatch(bool) -> void | 0 | 8 | VariableAddress[b] | ir.cpp:727:9:727:9 | +| TryCatch(bool) -> void | 0 | 9 | Load | ir.cpp:727:9:727:9 | +| TryCatch(bool) -> void | 0 | 10 | ConditionalBranch | ir.cpp:727:9:727:9 | +| TryCatch(bool) -> void | 1 | 0 | UnmodeledUse | ir.cpp:724:6:724:13 | +| TryCatch(bool) -> void | 1 | 1 | ExitFunction | ir.cpp:724:6:724:13 | +| TryCatch(bool) -> void | 2 | 0 | Unwind | ir.cpp:724:6:724:13 | +| TryCatch(bool) -> void | 3 | 0 | VariableAddress[#throw728:7] | ir.cpp:728:7:728:28 | +| TryCatch(bool) -> void | 3 | 1 | StringConstant["string literal"] | ir.cpp:728:13:728:28 | +| TryCatch(bool) -> void | 3 | 2 | Convert | ir.cpp:728:13:728:28 | +| TryCatch(bool) -> void | 3 | 3 | Store | ir.cpp:728:13:728:28 | +| TryCatch(bool) -> void | 3 | 4 | ThrowValue | ir.cpp:728:7:728:28 | +| TryCatch(bool) -> void | 4 | 0 | VariableAddress[x] | ir.cpp:730:14:730:14 | +| TryCatch(bool) -> void | 4 | 1 | Load | ir.cpp:730:14:730:14 | +| TryCatch(bool) -> void | 4 | 2 | Constant[2] | ir.cpp:730:18:730:18 | +| TryCatch(bool) -> void | 4 | 3 | CompareLT | ir.cpp:730:14:730:18 | +| TryCatch(bool) -> void | 4 | 4 | ConditionalBranch | ir.cpp:730:14:730:18 | +| TryCatch(bool) -> void | 5 | 0 | VariableAddress[b] | ir.cpp:731:11:731:11 | +| TryCatch(bool) -> void | 5 | 1 | Load | ir.cpp:731:11:731:11 | +| TryCatch(bool) -> void | 5 | 2 | ConditionalBranch | ir.cpp:731:11:731:11 | +| TryCatch(bool) -> void | 6 | 0 | Constant[7] | ir.cpp:731:15:731:15 | +| TryCatch(bool) -> void | 6 | 1 | VariableAddress[#temp731:11] | ir.cpp:731:11:731:47 | +| TryCatch(bool) -> void | 6 | 2 | Store | ir.cpp:731:11:731:47 | +| TryCatch(bool) -> void | 6 | 3 | VariableAddress[#temp731:11] | ir.cpp:731:11:731:47 | +| TryCatch(bool) -> void | 6 | 4 | Load | ir.cpp:731:11:731:47 | +| TryCatch(bool) -> void | 6 | 5 | VariableAddress[x] | ir.cpp:731:7:731:7 | +| TryCatch(bool) -> void | 6 | 6 | Store | ir.cpp:731:7:731:47 | +| TryCatch(bool) -> void | 7 | 0 | VariableAddress[#throw731:19] | ir.cpp:731:19:731:47 | +| TryCatch(bool) -> void | 7 | 1 | FunctionAddress[String] | ir.cpp:731:19:731:47 | +| TryCatch(bool) -> void | 7 | 2 | StringConstant["String object"] | ir.cpp:731:32:731:46 | +| TryCatch(bool) -> void | 7 | 3 | Convert | ir.cpp:731:32:731:46 | +| TryCatch(bool) -> void | 7 | 4 | Invoke | ir.cpp:731:19:731:47 | +| TryCatch(bool) -> void | 7 | 5 | ThrowValue | ir.cpp:731:19:731:47 | +| TryCatch(bool) -> void | 8 | 0 | Constant[7] | ir.cpp:733:9:733:9 | +| TryCatch(bool) -> void | 8 | 1 | VariableAddress[x] | ir.cpp:733:5:733:5 | +| TryCatch(bool) -> void | 8 | 2 | Store | ir.cpp:733:5:733:9 | +| TryCatch(bool) -> void | 9 | 0 | CatchByType[const char *] | ir.cpp:735:25:737:3 | +| TryCatch(bool) -> void | 10 | 0 | InitializeParameter[s] | ir.cpp:735:22:735:22 | +| TryCatch(bool) -> void | 10 | 1 | VariableAddress[s] | ir.cpp:735:22:735:22 | +| TryCatch(bool) -> void | 10 | 2 | Store | ir.cpp:735:22:735:22 | +| TryCatch(bool) -> void | 10 | 3 | VariableAddress[#throw736:5] | ir.cpp:736:5:736:19 | +| TryCatch(bool) -> void | 10 | 4 | FunctionAddress[String] | ir.cpp:736:5:736:19 | +| TryCatch(bool) -> void | 10 | 5 | VariableAddress[s] | ir.cpp:736:18:736:18 | +| TryCatch(bool) -> void | 10 | 6 | Load | ir.cpp:736:18:736:18 | +| TryCatch(bool) -> void | 10 | 7 | Invoke | ir.cpp:736:5:736:19 | +| TryCatch(bool) -> void | 10 | 8 | ThrowValue | ir.cpp:736:5:736:19 | +| TryCatch(bool) -> void | 11 | 0 | CatchByType[const String &] | ir.cpp:738:27:739:3 | +| TryCatch(bool) -> void | 12 | 0 | InitializeParameter[e] | ir.cpp:738:24:738:24 | +| TryCatch(bool) -> void | 12 | 1 | VariableAddress[e] | ir.cpp:738:24:738:24 | +| TryCatch(bool) -> void | 12 | 2 | Store | ir.cpp:738:24:738:24 | +| TryCatch(bool) -> void | 12 | 3 | NoOp | ir.cpp:738:27:739:3 | +| TryCatch(bool) -> void | 13 | 0 | CatchAny | ir.cpp:740:15:742:3 | +| TryCatch(bool) -> void | 13 | 1 | ReThrow | ir.cpp:741:5:741:9 | +| TryCatch(bool) -> void | 14 | 0 | NoOp | ir.cpp:743:1:743:1 | +| TryCatch(bool) -> void | 14 | 1 | ReturnVoid | ir.cpp:724:6:724:13 | +| UninitializedVariables() -> void | 0 | 0 | EnterFunction | ir.cpp:230:6:230:27 | +| UninitializedVariables() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:230:6:230:27 | +| UninitializedVariables() -> void | 0 | 2 | VariableAddress[x] | ir.cpp:231:9:231:9 | +| UninitializedVariables() -> void | 0 | 3 | Uninitialized | ir.cpp:231:9:231:9 | +| UninitializedVariables() -> void | 0 | 4 | Store | ir.cpp:231:9:231:9 | +| UninitializedVariables() -> void | 0 | 5 | VariableAddress[y] | ir.cpp:232:9:232:9 | +| UninitializedVariables() -> void | 0 | 6 | VariableAddress[x] | ir.cpp:232:13:232:13 | +| UninitializedVariables() -> void | 0 | 7 | Load | ir.cpp:232:13:232:13 | +| UninitializedVariables() -> void | 0 | 8 | Store | ir.cpp:232:13:232:13 | +| UninitializedVariables() -> void | 0 | 9 | NoOp | ir.cpp:233:1:233:1 | +| UninitializedVariables() -> void | 0 | 10 | ReturnVoid | ir.cpp:230:6:230:27 | +| UninitializedVariables() -> void | 0 | 11 | UnmodeledUse | ir.cpp:230:6:230:27 | +| UninitializedVariables() -> void | 0 | 12 | ExitFunction | ir.cpp:230:6:230:27 | +| UnionInit(int, float) -> void | 0 | 0 | EnterFunction | ir.cpp:530:6:530:14 | +| UnionInit(int, float) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:530:6:530:14 | +| UnionInit(int, float) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:530:20:530:20 | +| UnionInit(int, float) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:530:20:530:20 | +| UnionInit(int, float) -> void | 0 | 4 | Store | ir.cpp:530:20:530:20 | +| UnionInit(int, float) -> void | 0 | 5 | InitializeParameter[f] | ir.cpp:530:29:530:29 | +| UnionInit(int, float) -> void | 0 | 6 | VariableAddress[f] | ir.cpp:530:29:530:29 | +| UnionInit(int, float) -> void | 0 | 7 | Store | ir.cpp:530:29:530:29 | +| UnionInit(int, float) -> void | 0 | 8 | VariableAddress[u1] | ir.cpp:531:7:531:8 | +| UnionInit(int, float) -> void | 0 | 9 | FieldAddress[d] | ir.cpp:531:11:531:16 | +| UnionInit(int, float) -> void | 0 | 10 | VariableAddress[f] | ir.cpp:531:14:531:14 | +| UnionInit(int, float) -> void | 0 | 11 | Load | ir.cpp:531:14:531:14 | +| UnionInit(int, float) -> void | 0 | 12 | Convert | ir.cpp:531:14:531:14 | +| UnionInit(int, float) -> void | 0 | 13 | Store | ir.cpp:531:14:531:14 | +| UnionInit(int, float) -> void | 0 | 14 | NoOp | ir.cpp:533:1:533:1 | +| UnionInit(int, float) -> void | 0 | 15 | ReturnVoid | ir.cpp:530:6:530:14 | +| UnionInit(int, float) -> void | 0 | 16 | UnmodeledUse | ir.cpp:530:6:530:14 | +| UnionInit(int, float) -> void | 0 | 17 | ExitFunction | ir.cpp:530:6:530:14 | +| VarArgUsage(int) -> void | 0 | 0 | EnterFunction | ir.cpp:888:6:888:16 | +| VarArgUsage(int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:888:6:888:16 | +| VarArgUsage(int) -> void | 0 | 2 | InitializeParameter[x] | ir.cpp:888:22:888:22 | +| VarArgUsage(int) -> void | 0 | 3 | VariableAddress[x] | ir.cpp:888:22:888:22 | +| VarArgUsage(int) -> void | 0 | 4 | Store | ir.cpp:888:22:888:22 | +| VarArgUsage(int) -> void | 0 | 5 | VariableAddress[args] | ir.cpp:889:21:889:24 | +| VarArgUsage(int) -> void | 0 | 6 | Uninitialized | ir.cpp:889:21:889:24 | +| VarArgUsage(int) -> void | 0 | 7 | Store | ir.cpp:889:21:889:24 | +| VarArgUsage(int) -> void | 0 | 8 | VariableAddress[args] | ir.cpp:891:22:891:25 | +| VarArgUsage(int) -> void | 0 | 9 | Convert | ir.cpp:891:22:891:25 | +| VarArgUsage(int) -> void | 0 | 10 | VariableAddress[x] | ir.cpp:891:28:891:28 | +| VarArgUsage(int) -> void | 0 | 11 | VarArgsStart | ir.cpp:891:3:891:29 | +| VarArgUsage(int) -> void | 0 | 12 | VariableAddress[args2] | ir.cpp:892:21:892:25 | +| VarArgUsage(int) -> void | 0 | 13 | Uninitialized | ir.cpp:892:21:892:25 | +| VarArgUsage(int) -> void | 0 | 14 | Store | ir.cpp:892:21:892:25 | +| VarArgUsage(int) -> void | 0 | 15 | VariableAddress[args2] | ir.cpp:893:22:893:26 | +| VarArgUsage(int) -> void | 0 | 16 | Convert | ir.cpp:893:22:893:26 | +| VarArgUsage(int) -> void | 0 | 17 | VariableAddress[args] | ir.cpp:893:29:893:32 | +| VarArgUsage(int) -> void | 0 | 18 | Convert | ir.cpp:893:29:893:32 | +| VarArgUsage(int) -> void | 0 | 19 | VarArgsStart | ir.cpp:893:3:893:33 | +| VarArgUsage(int) -> void | 0 | 20 | VariableAddress[d] | ir.cpp:894:10:894:10 | +| VarArgUsage(int) -> void | 0 | 21 | VariableAddress[args] | ir.cpp:894:31:894:34 | +| VarArgUsage(int) -> void | 0 | 22 | Convert | ir.cpp:894:31:894:34 | +| VarArgUsage(int) -> void | 0 | 23 | VarArg | ir.cpp:894:14:894:43 | +| VarArgUsage(int) -> void | 0 | 24 | Load | ir.cpp:894:14:894:43 | +| VarArgUsage(int) -> void | 0 | 25 | Store | ir.cpp:894:14:894:43 | +| VarArgUsage(int) -> void | 0 | 26 | VariableAddress[f] | ir.cpp:895:9:895:9 | +| VarArgUsage(int) -> void | 0 | 27 | VariableAddress[args] | ir.cpp:895:30:895:33 | +| VarArgUsage(int) -> void | 0 | 28 | Convert | ir.cpp:895:30:895:33 | +| VarArgUsage(int) -> void | 0 | 29 | VarArg | ir.cpp:895:30:895:33 | +| VarArgUsage(int) -> void | 0 | 30 | Load | ir.cpp:895:30:895:33 | +| VarArgUsage(int) -> void | 0 | 31 | Convert | ir.cpp:895:13:895:41 | +| VarArgUsage(int) -> void | 0 | 32 | Store | ir.cpp:895:13:895:41 | +| VarArgUsage(int) -> void | 0 | 33 | VariableAddress[args] | ir.cpp:896:20:896:23 | +| VarArgUsage(int) -> void | 0 | 34 | Convert | ir.cpp:896:20:896:23 | +| VarArgUsage(int) -> void | 0 | 35 | VarArgsEnd | ir.cpp:896:3:896:24 | +| VarArgUsage(int) -> void | 0 | 36 | VariableAddress[args2] | ir.cpp:897:20:897:24 | +| VarArgUsage(int) -> void | 0 | 37 | Convert | ir.cpp:897:20:897:24 | +| VarArgUsage(int) -> void | 0 | 38 | VarArgsEnd | ir.cpp:897:3:897:25 | +| VarArgUsage(int) -> void | 0 | 39 | NoOp | ir.cpp:898:1:898:1 | +| VarArgUsage(int) -> void | 0 | 40 | ReturnVoid | ir.cpp:888:6:888:16 | +| VarArgUsage(int) -> void | 0 | 41 | UnmodeledUse | ir.cpp:888:6:888:16 | +| VarArgUsage(int) -> void | 0 | 42 | ExitFunction | ir.cpp:888:6:888:16 | +| VarArgs() -> void | 0 | 0 | EnterFunction | ir.cpp:584:6:584:12 | +| VarArgs() -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:584:6:584:12 | +| VarArgs() -> void | 0 | 2 | FunctionAddress[VarArgFunction] | ir.cpp:585:5:585:18 | +| VarArgs() -> void | 0 | 3 | StringConstant["%d %s"] | ir.cpp:585:20:585:26 | +| VarArgs() -> void | 0 | 4 | Convert | ir.cpp:585:20:585:26 | +| VarArgs() -> void | 0 | 5 | Constant[1] | ir.cpp:585:29:585:29 | +| VarArgs() -> void | 0 | 6 | StringConstant["string"] | ir.cpp:585:32:585:39 | +| VarArgs() -> void | 0 | 7 | Convert | ir.cpp:585:32:585:39 | +| VarArgs() -> void | 0 | 8 | Invoke | ir.cpp:585:5:585:18 | +| VarArgs() -> void | 0 | 9 | NoOp | ir.cpp:586:1:586:1 | +| VarArgs() -> void | 0 | 10 | ReturnVoid | ir.cpp:584:6:584:12 | +| VarArgs() -> void | 0 | 11 | UnmodeledUse | ir.cpp:584:6:584:12 | +| VarArgs() -> void | 0 | 12 | ExitFunction | ir.cpp:584:6:584:12 | +| WhileStatements(int) -> void | 0 | 0 | EnterFunction | ir.cpp:253:6:253:20 | +| WhileStatements(int) -> void | 0 | 1 | UnmodeledDefinition | ir.cpp:253:6:253:20 | +| WhileStatements(int) -> void | 0 | 2 | InitializeParameter[n] | ir.cpp:253:26:253:26 | +| WhileStatements(int) -> void | 0 | 3 | VariableAddress[n] | ir.cpp:253:26:253:26 | +| WhileStatements(int) -> void | 0 | 4 | Store | ir.cpp:253:26:253:26 | +| WhileStatements(int) -> void | 1 | 0 | Constant[1] | ir.cpp:255:14:255:14 | +| WhileStatements(int) -> void | 1 | 1 | VariableAddress[n] | ir.cpp:255:9:255:9 | +| WhileStatements(int) -> void | 1 | 2 | Load | ir.cpp:255:9:255:14 | +| WhileStatements(int) -> void | 1 | 3 | Sub | ir.cpp:255:9:255:14 | +| WhileStatements(int) -> void | 1 | 4 | Store | ir.cpp:255:9:255:14 | +| WhileStatements(int) -> void | 2 | 0 | NoOp | ir.cpp:257:1:257:1 | +| WhileStatements(int) -> void | 2 | 1 | ReturnVoid | ir.cpp:253:6:253:20 | +| WhileStatements(int) -> void | 2 | 2 | UnmodeledUse | ir.cpp:253:6:253:20 | +| WhileStatements(int) -> void | 2 | 3 | ExitFunction | ir.cpp:253:6:253:20 | +| WhileStatements(int) -> void | 3 | 0 | Phi | ir.cpp:254:12:254:12 | +| WhileStatements(int) -> void | 3 | 1 | VariableAddress[n] | ir.cpp:254:12:254:12 | +| WhileStatements(int) -> void | 3 | 2 | Load | ir.cpp:254:12:254:12 | +| WhileStatements(int) -> void | 3 | 3 | Constant[0] | ir.cpp:254:16:254:16 | +| WhileStatements(int) -> void | 3 | 4 | CompareGT | ir.cpp:254:12:254:16 | +| WhileStatements(int) -> void | 3 | 5 | ConditionalBranch | ir.cpp:254:12:254:16 | +| min(int, int) -> int | 0 | 0 | EnterFunction | ir.cpp:704:3:704:5 | +| min(int, int) -> int | 0 | 1 | UnmodeledDefinition | ir.cpp:704:3:704:5 | +| min(int, int) -> int | 0 | 2 | InitializeParameter[x] | ir.cpp:704:9:704:9 | +| min(int, int) -> int | 0 | 3 | VariableAddress[x] | ir.cpp:704:9:704:9 | +| min(int, int) -> int | 0 | 4 | Store | ir.cpp:704:9:704:9 | +| min(int, int) -> int | 0 | 5 | InitializeParameter[y] | ir.cpp:704:14:704:14 | +| min(int, int) -> int | 0 | 6 | VariableAddress[y] | ir.cpp:704:14:704:14 | +| min(int, int) -> int | 0 | 7 | Store | ir.cpp:704:14:704:14 | +| min(int, int) -> int | 0 | 8 | VariableAddress[#return] | ir.cpp:705:3:705:25 | +| min(int, int) -> int | 0 | 9 | VariableAddress[x] | ir.cpp:705:11:705:11 | +| min(int, int) -> int | 0 | 10 | Load | ir.cpp:705:11:705:11 | +| min(int, int) -> int | 0 | 11 | VariableAddress[y] | ir.cpp:705:15:705:15 | +| min(int, int) -> int | 0 | 12 | Load | ir.cpp:705:15:705:15 | +| min(int, int) -> int | 0 | 13 | CompareLT | ir.cpp:705:11:705:15 | +| min(int, int) -> int | 0 | 14 | ConditionalBranch | ir.cpp:705:11:705:15 | +| min(int, int) -> int | 1 | 0 | VariableAddress[x] | ir.cpp:705:20:705:20 | +| min(int, int) -> int | 1 | 1 | Load | ir.cpp:705:20:705:20 | +| min(int, int) -> int | 1 | 2 | VariableAddress[#temp705:10] | ir.cpp:705:10:705:24 | +| min(int, int) -> int | 1 | 3 | Store | ir.cpp:705:10:705:24 | +| min(int, int) -> int | 2 | 0 | VariableAddress[y] | ir.cpp:705:24:705:24 | +| min(int, int) -> int | 2 | 1 | Load | ir.cpp:705:24:705:24 | +| min(int, int) -> int | 2 | 2 | VariableAddress[#temp705:10] | ir.cpp:705:10:705:24 | +| min(int, int) -> int | 2 | 3 | Store | ir.cpp:705:10:705:24 | +| min(int, int) -> int | 3 | 0 | Phi | ir.cpp:705:10:705:24 | +| min(int, int) -> int | 3 | 1 | VariableAddress[#temp705:10] | ir.cpp:705:10:705:24 | +| min(int, int) -> int | 3 | 2 | Load | ir.cpp:705:10:705:24 | +| min(int, int) -> int | 3 | 3 | Store | ir.cpp:705:10:705:24 | +| min(int, int) -> int | 3 | 4 | VariableAddress[#return] | ir.cpp:704:3:704:5 | +| min(int, int) -> int | 3 | 5 | ReturnValue | ir.cpp:704:3:704:5 | +| min(int, int) -> int | 3 | 6 | UnmodeledUse | ir.cpp:704:3:704:5 | +| min(int, int) -> int | 3 | 7 | ExitFunction | ir.cpp:704:3:704:5 | +printIRGraphEdges +| Break(int) -> void | 0 | 5 | Goto | +| Break(int) -> void | 1 | 2 | True | +| Break(int) -> void | 1 | 3 | False | +| Break(int) -> void | 2 | 4 | Goto | +| Break(int) -> void | 3 | 5 | Goto | +| Break(int) -> void | 5 | 1 | True | +| Break(int) -> void | 5 | 4 | False | +| ConditionValues(bool, bool) -> void | 0 | 1 | True | +| ConditionValues(bool, bool) -> void | 0 | 10 | False | +| ConditionValues(bool, bool) -> void | 1 | 10 | False | +| ConditionValues(bool, bool) -> void | 1 | 12 | True | +| ConditionValues(bool, bool) -> void | 2 | 3 | Goto | +| ConditionValues(bool, bool) -> void | 3 | 8 | True | +| ConditionValues(bool, bool) -> void | 3 | 9 | False | +| ConditionValues(bool, bool) -> void | 4 | 3 | Goto | +| ConditionValues(bool, bool) -> void | 5 | 2 | False | +| ConditionValues(bool, bool) -> void | 5 | 4 | True | +| ConditionValues(bool, bool) -> void | 6 | 7 | Goto | +| ConditionValues(bool, bool) -> void | 8 | 7 | Goto | +| ConditionValues(bool, bool) -> void | 9 | 6 | False | +| ConditionValues(bool, bool) -> void | 9 | 8 | True | +| ConditionValues(bool, bool) -> void | 10 | 11 | Goto | +| ConditionValues(bool, bool) -> void | 11 | 4 | True | +| ConditionValues(bool, bool) -> void | 11 | 5 | False | +| ConditionValues(bool, bool) -> void | 12 | 11 | Goto | +| Conditional(bool, int, int) -> void | 0 | 1 | True | +| Conditional(bool, int, int) -> void | 0 | 2 | False | +| Conditional(bool, int, int) -> void | 1 | 3 | Goto | +| Conditional(bool, int, int) -> void | 2 | 3 | Goto | +| Conditional_LValue(bool) -> void | 0 | 2 | True | +| Conditional_LValue(bool) -> void | 0 | 3 | False | +| Conditional_LValue(bool) -> void | 2 | 1 | Goto | +| Conditional_LValue(bool) -> void | 3 | 1 | Goto | +| Conditional_Void(bool) -> void | 0 | 2 | True | +| Conditional_Void(bool) -> void | 0 | 3 | False | +| Conditional_Void(bool) -> void | 2 | 1 | Goto | +| Conditional_Void(bool) -> void | 3 | 1 | Goto | +| Continue(int) -> void | 0 | 1 | Goto | +| Continue(int) -> void | 1 | 2 | True | +| Continue(int) -> void | 1 | 3 | False | +| Continue(int) -> void | 2 | 4 | Goto | +| Continue(int) -> void | 3 | 4 | Goto | +| Continue(int) -> void | 4 | 1 | True | +| Continue(int) -> void | 4 | 5 | False | +| DoStatements(int) -> void | 0 | 1 | Goto | +| DoStatements(int) -> void | 1 | 1 | True | +| DoStatements(int) -> void | 1 | 2 | False | +| EarlyReturn(int, int) -> void | 0 | 2 | True | +| EarlyReturn(int, int) -> void | 0 | 3 | False | +| EarlyReturn(int, int) -> void | 2 | 1 | Goto | +| EarlyReturn(int, int) -> void | 3 | 1 | Goto | +| EarlyReturnValue(int, int) -> int | 0 | 2 | True | +| EarlyReturnValue(int, int) -> int | 0 | 3 | False | +| EarlyReturnValue(int, int) -> int | 2 | 1 | Goto | +| EarlyReturnValue(int, int) -> int | 3 | 1 | Goto | +| EnumSwitch(E) -> int | 0 | 2 | Case[1] | +| EnumSwitch(E) -> int | 0 | 3 | Default | +| EnumSwitch(E) -> int | 0 | 4 | Case[0] | +| EnumSwitch(E) -> int | 2 | 1 | Goto | +| EnumSwitch(E) -> int | 3 | 1 | Goto | +| EnumSwitch(E) -> int | 4 | 1 | Goto | +| For_Break() -> void | 0 | 1 | Goto | +| For_Break() -> void | 1 | 3 | True | +| For_Break() -> void | 1 | 5 | False | +| For_Break() -> void | 2 | 1 | Goto | +| For_Break() -> void | 3 | 2 | False | +| For_Break() -> void | 3 | 4 | True | +| For_Break() -> void | 4 | 5 | Goto | +| For_Condition() -> void | 0 | 1 | Goto | +| For_Condition() -> void | 1 | 2 | True | +| For_Condition() -> void | 1 | 3 | False | +| For_Condition() -> void | 2 | 1 | Goto | +| For_ConditionUpdate() -> void | 0 | 1 | Goto | +| For_ConditionUpdate() -> void | 1 | 2 | True | +| For_ConditionUpdate() -> void | 1 | 3 | False | +| For_ConditionUpdate() -> void | 2 | 1 | Goto | +| For_Continue_NoUpdate() -> void | 0 | 1 | Goto | +| For_Continue_NoUpdate() -> void | 1 | 2 | True | +| For_Continue_NoUpdate() -> void | 1 | 5 | False | +| For_Continue_NoUpdate() -> void | 2 | 3 | True | +| For_Continue_NoUpdate() -> void | 2 | 4 | False | +| For_Continue_NoUpdate() -> void | 3 | 4 | Goto | +| For_Continue_NoUpdate() -> void | 4 | 1 | Goto | +| For_Continue_Update() -> void | 0 | 1 | Goto | +| For_Continue_Update() -> void | 1 | 2 | True | +| For_Continue_Update() -> void | 1 | 5 | False | +| For_Continue_Update() -> void | 2 | 3 | True | +| For_Continue_Update() -> void | 2 | 4 | False | +| For_Continue_Update() -> void | 3 | 4 | Goto | +| For_Continue_Update() -> void | 4 | 1 | Goto | +| For_Empty() -> void | 0 | 2 | Goto | +| For_Empty() -> void | 2 | 2 | Goto | +| For_Init() -> void | 0 | 2 | Goto | +| For_Init() -> void | 2 | 2 | Goto | +| For_InitCondition() -> void | 0 | 1 | Goto | +| For_InitCondition() -> void | 1 | 2 | True | +| For_InitCondition() -> void | 1 | 3 | False | +| For_InitCondition() -> void | 2 | 1 | Goto | +| For_InitConditionUpdate() -> void | 0 | 1 | Goto | +| For_InitConditionUpdate() -> void | 1 | 2 | True | +| For_InitConditionUpdate() -> void | 1 | 3 | False | +| For_InitConditionUpdate() -> void | 2 | 1 | Goto | +| For_InitUpdate() -> void | 0 | 2 | Goto | +| For_InitUpdate() -> void | 2 | 2 | Goto | +| For_Update() -> void | 0 | 2 | Goto | +| For_Update() -> void | 2 | 2 | Goto | +| IfStatements(bool, int, int) -> void | 0 | 1 | False | +| IfStatements(bool, int, int) -> void | 0 | 7 | True | +| IfStatements(bool, int, int) -> void | 1 | 2 | True | +| IfStatements(bool, int, int) -> void | 1 | 3 | False | +| IfStatements(bool, int, int) -> void | 2 | 3 | Goto | +| IfStatements(bool, int, int) -> void | 3 | 4 | True | +| IfStatements(bool, int, int) -> void | 3 | 5 | False | +| IfStatements(bool, int, int) -> void | 4 | 6 | Goto | +| IfStatements(bool, int, int) -> void | 5 | 6 | Goto | +| IfStatements(bool, int, int) -> void | 7 | 1 | Goto | +| LogicalAnd(bool, bool) -> void | 0 | 1 | True | +| LogicalAnd(bool, bool) -> void | 0 | 3 | False | +| LogicalAnd(bool, bool) -> void | 1 | 2 | True | +| LogicalAnd(bool, bool) -> void | 1 | 3 | False | +| LogicalAnd(bool, bool) -> void | 2 | 3 | Goto | +| LogicalAnd(bool, bool) -> void | 3 | 4 | True | +| LogicalAnd(bool, bool) -> void | 3 | 6 | False | +| LogicalAnd(bool, bool) -> void | 4 | 5 | True | +| LogicalAnd(bool, bool) -> void | 4 | 6 | False | +| LogicalAnd(bool, bool) -> void | 5 | 7 | Goto | +| LogicalAnd(bool, bool) -> void | 6 | 7 | Goto | +| LogicalNot(bool, bool) -> void | 0 | 1 | False | +| LogicalNot(bool, bool) -> void | 0 | 2 | True | +| LogicalNot(bool, bool) -> void | 1 | 2 | Goto | +| LogicalNot(bool, bool) -> void | 2 | 3 | True | +| LogicalNot(bool, bool) -> void | 2 | 4 | False | +| LogicalNot(bool, bool) -> void | 3 | 4 | False | +| LogicalNot(bool, bool) -> void | 3 | 5 | True | +| LogicalNot(bool, bool) -> void | 4 | 6 | Goto | +| LogicalNot(bool, bool) -> void | 5 | 6 | Goto | +| LogicalOr(bool, bool) -> void | 0 | 1 | False | +| LogicalOr(bool, bool) -> void | 0 | 2 | True | +| LogicalOr(bool, bool) -> void | 1 | 2 | True | +| LogicalOr(bool, bool) -> void | 1 | 3 | False | +| LogicalOr(bool, bool) -> void | 2 | 3 | Goto | +| LogicalOr(bool, bool) -> void | 3 | 4 | False | +| LogicalOr(bool, bool) -> void | 3 | 5 | True | +| LogicalOr(bool, bool) -> void | 4 | 5 | True | +| LogicalOr(bool, bool) -> void | 4 | 6 | False | +| LogicalOr(bool, bool) -> void | 5 | 7 | Goto | +| LogicalOr(bool, bool) -> void | 6 | 7 | Goto | +| Switch(int) -> void | 0 | 2 | Case[-1] | +| Switch(int) -> void | 0 | 3 | Case[1] | +| Switch(int) -> void | 0 | 4 | Case[2] | +| Switch(int) -> void | 0 | 5 | Case[3] | +| Switch(int) -> void | 0 | 6 | Case[4] | +| Switch(int) -> void | 0 | 7 | Default | +| Switch(int) -> void | 1 | 2 | Goto | +| Switch(int) -> void | 2 | 9 | Goto | +| Switch(int) -> void | 3 | 4 | Goto | +| Switch(int) -> void | 4 | 9 | Goto | +| Switch(int) -> void | 5 | 6 | Goto | +| Switch(int) -> void | 6 | 9 | Goto | +| Switch(int) -> void | 7 | 9 | Goto | +| Switch(int) -> void | 8 | 9 | Goto | +| TryCatch(bool) -> void | 0 | 3 | True | +| TryCatch(bool) -> void | 0 | 4 | False | +| TryCatch(bool) -> void | 2 | 1 | Goto | +| TryCatch(bool) -> void | 3 | 9 | Exception | +| TryCatch(bool) -> void | 4 | 5 | True | +| TryCatch(bool) -> void | 4 | 8 | False | +| TryCatch(bool) -> void | 5 | 6 | True | +| TryCatch(bool) -> void | 5 | 7 | False | +| TryCatch(bool) -> void | 6 | 8 | Goto | +| TryCatch(bool) -> void | 7 | 9 | Exception | +| TryCatch(bool) -> void | 8 | 14 | Goto | +| TryCatch(bool) -> void | 9 | 10 | Goto | +| TryCatch(bool) -> void | 9 | 11 | Exception | +| TryCatch(bool) -> void | 10 | 2 | Exception | +| TryCatch(bool) -> void | 11 | 12 | Goto | +| TryCatch(bool) -> void | 11 | 13 | Exception | +| TryCatch(bool) -> void | 12 | 14 | Goto | +| TryCatch(bool) -> void | 13 | 2 | Exception | +| TryCatch(bool) -> void | 14 | 1 | Goto | +| WhileStatements(int) -> void | 0 | 3 | Goto | +| WhileStatements(int) -> void | 1 | 3 | Goto | +| WhileStatements(int) -> void | 3 | 1 | True | +| WhileStatements(int) -> void | 3 | 2 | False | +| min(int, int) -> int | 0 | 1 | True | +| min(int, int) -> int | 0 | 2 | False | +| min(int, int) -> int | 1 | 3 | Goto | +| min(int, int) -> int | 2 | 3 | Goto | +printIRGraphDestinationOperands +| AddressOf() -> int * | 0 | 1 | 0 | @mu0_1(unknown) | +| AddressOf() -> int * | 0 | 2 | 0 | @r0_2(glval:int *) | +| AddressOf() -> int * | 0 | 3 | 0 | @r0_3(glval:int) | +| AddressOf() -> int * | 0 | 4 | 0 | @m0_4(int *) | +| AddressOf() -> int * | 0 | 5 | 0 | @r0_5(glval:int *) | +| ArrayAccess(int *, int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| ArrayAccess(int *, int) -> void | 0 | 2 | 0 | @r0_2(int *) | +| ArrayAccess(int *, int) -> void | 0 | 3 | 0 | @r0_3(glval:int *) | +| ArrayAccess(int *, int) -> void | 0 | 4 | 0 | @m0_4(int *) | +| ArrayAccess(int *, int) -> void | 0 | 5 | 0 | @r0_5(int) | +| ArrayAccess(int *, int) -> void | 0 | 6 | 0 | @r0_6(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 7 | 0 | @m0_7(int) | +| ArrayAccess(int *, int) -> void | 0 | 8 | 0 | @r0_8(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 9 | 0 | @r0_9(int) | +| ArrayAccess(int *, int) -> void | 0 | 10 | 0 | @m0_10(int) | +| ArrayAccess(int *, int) -> void | 0 | 11 | 0 | @r0_11(glval:int *) | +| ArrayAccess(int *, int) -> void | 0 | 12 | 0 | @r0_12(int *) | +| ArrayAccess(int *, int) -> void | 0 | 13 | 0 | @r0_13(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 14 | 0 | @r0_14(int) | +| ArrayAccess(int *, int) -> void | 0 | 15 | 0 | @r0_15(int *) | +| ArrayAccess(int *, int) -> void | 0 | 16 | 0 | @r0_16(int) | +| ArrayAccess(int *, int) -> void | 0 | 17 | 0 | @r0_17(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 18 | 0 | @m0_18(int) | +| ArrayAccess(int *, int) -> void | 0 | 19 | 0 | @r0_19(glval:int *) | +| ArrayAccess(int *, int) -> void | 0 | 20 | 0 | @r0_20(int *) | +| ArrayAccess(int *, int) -> void | 0 | 21 | 0 | @r0_21(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 22 | 0 | @r0_22(int) | +| ArrayAccess(int *, int) -> void | 0 | 23 | 0 | @r0_23(int *) | +| ArrayAccess(int *, int) -> void | 0 | 24 | 0 | @r0_24(int) | +| ArrayAccess(int *, int) -> void | 0 | 25 | 0 | @r0_25(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 26 | 0 | @m0_26(int) | +| ArrayAccess(int *, int) -> void | 0 | 27 | 0 | @r0_27(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 28 | 0 | @r0_28(int) | +| ArrayAccess(int *, int) -> void | 0 | 29 | 0 | @r0_29(glval:int *) | +| ArrayAccess(int *, int) -> void | 0 | 30 | 0 | @r0_30(int *) | +| ArrayAccess(int *, int) -> void | 0 | 31 | 0 | @r0_31(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 32 | 0 | @r0_32(int) | +| ArrayAccess(int *, int) -> void | 0 | 33 | 0 | @r0_33(int *) | +| ArrayAccess(int *, int) -> void | 0 | 34 | 0 | @mu0_34(int) | +| ArrayAccess(int *, int) -> void | 0 | 35 | 0 | @r0_35(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 36 | 0 | @r0_36(int) | +| ArrayAccess(int *, int) -> void | 0 | 37 | 0 | @r0_37(glval:int *) | +| ArrayAccess(int *, int) -> void | 0 | 38 | 0 | @r0_38(int *) | +| ArrayAccess(int *, int) -> void | 0 | 39 | 0 | @r0_39(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 40 | 0 | @r0_40(int) | +| ArrayAccess(int *, int) -> void | 0 | 41 | 0 | @r0_41(int *) | +| ArrayAccess(int *, int) -> void | 0 | 42 | 0 | @mu0_42(int) | +| ArrayAccess(int *, int) -> void | 0 | 43 | 0 | @r0_43(glval:int[10]) | +| ArrayAccess(int *, int) -> void | 0 | 44 | 0 | @r0_44(int[10]) | +| ArrayAccess(int *, int) -> void | 0 | 45 | 0 | @m0_45(int[10]) | +| ArrayAccess(int *, int) -> void | 0 | 46 | 0 | @r0_46(glval:int[10]) | +| ArrayAccess(int *, int) -> void | 0 | 47 | 0 | @r0_47(int *) | +| ArrayAccess(int *, int) -> void | 0 | 48 | 0 | @r0_48(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 49 | 0 | @r0_49(int) | +| ArrayAccess(int *, int) -> void | 0 | 50 | 0 | @r0_50(int *) | +| ArrayAccess(int *, int) -> void | 0 | 51 | 0 | @r0_51(int) | +| ArrayAccess(int *, int) -> void | 0 | 52 | 0 | @r0_52(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 53 | 0 | @m0_53(int) | +| ArrayAccess(int *, int) -> void | 0 | 54 | 0 | @r0_54(glval:int[10]) | +| ArrayAccess(int *, int) -> void | 0 | 55 | 0 | @r0_55(int *) | +| ArrayAccess(int *, int) -> void | 0 | 56 | 0 | @r0_56(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 57 | 0 | @r0_57(int) | +| ArrayAccess(int *, int) -> void | 0 | 58 | 0 | @r0_58(int *) | +| ArrayAccess(int *, int) -> void | 0 | 59 | 0 | @r0_59(int) | +| ArrayAccess(int *, int) -> void | 0 | 60 | 0 | @r0_60(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 61 | 0 | @m0_61(int) | +| ArrayAccess(int *, int) -> void | 0 | 62 | 0 | @r0_62(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 63 | 0 | @r0_63(int) | +| ArrayAccess(int *, int) -> void | 0 | 64 | 0 | @r0_64(glval:int[10]) | +| ArrayAccess(int *, int) -> void | 0 | 65 | 0 | @r0_65(int *) | +| ArrayAccess(int *, int) -> void | 0 | 66 | 0 | @r0_66(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 67 | 0 | @r0_67(int) | +| ArrayAccess(int *, int) -> void | 0 | 68 | 0 | @r0_68(int *) | +| ArrayAccess(int *, int) -> void | 0 | 69 | 0 | @mu0_69(int) | +| ArrayAccess(int *, int) -> void | 0 | 70 | 0 | @r0_70(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 71 | 0 | @r0_71(int) | +| ArrayAccess(int *, int) -> void | 0 | 72 | 0 | @r0_72(glval:int[10]) | +| ArrayAccess(int *, int) -> void | 0 | 73 | 0 | @r0_73(int *) | +| ArrayAccess(int *, int) -> void | 0 | 74 | 0 | @r0_74(glval:int) | +| ArrayAccess(int *, int) -> void | 0 | 75 | 0 | @r0_75(int) | +| ArrayAccess(int *, int) -> void | 0 | 76 | 0 | @r0_76(int *) | +| ArrayAccess(int *, int) -> void | 0 | 77 | 0 | @mu0_77(int) | +| ArrayConversions() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| ArrayConversions() -> void | 0 | 2 | 0 | @r0_2(glval:char[5]) | +| ArrayConversions() -> void | 0 | 3 | 0 | @r0_3(char[5]) | +| ArrayConversions() -> void | 0 | 4 | 0 | @mu0_4(char[5]) | +| ArrayConversions() -> void | 0 | 5 | 0 | @r0_5(glval:char *) | +| ArrayConversions() -> void | 0 | 6 | 0 | @r0_6(glval:char[5]) | +| ArrayConversions() -> void | 0 | 7 | 0 | @r0_7(char *) | +| ArrayConversions() -> void | 0 | 8 | 0 | @r0_8(char *) | +| ArrayConversions() -> void | 0 | 9 | 0 | @m0_9(char *) | +| ArrayConversions() -> void | 0 | 10 | 0 | @r0_10(glval:char[5]) | +| ArrayConversions() -> void | 0 | 11 | 0 | @r0_11(char *) | +| ArrayConversions() -> void | 0 | 12 | 0 | @r0_12(glval:char *) | +| ArrayConversions() -> void | 0 | 13 | 0 | @m0_13(char *) | +| ArrayConversions() -> void | 0 | 14 | 0 | @r0_14(glval:char[5]) | +| ArrayConversions() -> void | 0 | 15 | 0 | @r0_15(char *) | +| ArrayConversions() -> void | 0 | 16 | 0 | @r0_16(int) | +| ArrayConversions() -> void | 0 | 17 | 0 | @r0_17(char *) | +| ArrayConversions() -> void | 0 | 18 | 0 | @r0_18(char *) | +| ArrayConversions() -> void | 0 | 19 | 0 | @r0_19(glval:char *) | +| ArrayConversions() -> void | 0 | 20 | 0 | @m0_20(char *) | +| ArrayConversions() -> void | 0 | 21 | 0 | @r0_21(glval:char[5]) | +| ArrayConversions() -> void | 0 | 22 | 0 | @r0_22(char *) | +| ArrayConversions() -> void | 0 | 23 | 0 | @r0_23(int) | +| ArrayConversions() -> void | 0 | 24 | 0 | @r0_24(char *) | +| ArrayConversions() -> void | 0 | 25 | 0 | @r0_25(glval:char *) | +| ArrayConversions() -> void | 0 | 26 | 0 | @m0_26(char *) | +| ArrayConversions() -> void | 0 | 27 | 0 | @r0_27(glval:char(&)[5]) | +| ArrayConversions() -> void | 0 | 28 | 0 | @r0_28(glval:char[5]) | +| ArrayConversions() -> void | 0 | 29 | 0 | @m0_29(char(&)[5]) | +| ArrayConversions() -> void | 0 | 30 | 0 | @r0_30(glval:char(&)[5]) | +| ArrayConversions() -> void | 0 | 31 | 0 | @r0_31(glval:char[5]) | +| ArrayConversions() -> void | 0 | 32 | 0 | @m0_32(char(&)[5]) | +| ArrayConversions() -> void | 0 | 33 | 0 | @r0_33(glval:char(*)[5]) | +| ArrayConversions() -> void | 0 | 34 | 0 | @r0_34(glval:char[5]) | +| ArrayConversions() -> void | 0 | 35 | 0 | @r0_35(char(*)[5]) | +| ArrayConversions() -> void | 0 | 36 | 0 | @m0_36(char(*)[5]) | +| ArrayConversions() -> void | 0 | 37 | 0 | @r0_37(glval:char[5]) | +| ArrayConversions() -> void | 0 | 38 | 0 | @r0_38(glval:char(*)[5]) | +| ArrayConversions() -> void | 0 | 39 | 0 | @m0_39(char(*)[5]) | +| ArrayInit(int, float) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| ArrayInit(int, float) -> void | 0 | 2 | 0 | @r0_2(int) | +| ArrayInit(int, float) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| ArrayInit(int, float) -> void | 0 | 4 | 0 | @m0_4(int) | +| ArrayInit(int, float) -> void | 0 | 5 | 0 | @r0_5(float) | +| ArrayInit(int, float) -> void | 0 | 6 | 0 | @r0_6(glval:float) | +| ArrayInit(int, float) -> void | 0 | 7 | 0 | @m0_7(float) | +| ArrayInit(int, float) -> void | 0 | 8 | 0 | @r0_8(glval:int[3]) | +| ArrayInit(int, float) -> void | 0 | 9 | 0 | @r0_9(int) | +| ArrayInit(int, float) -> void | 0 | 10 | 0 | @r0_10(glval:int) | +| ArrayInit(int, float) -> void | 0 | 11 | 0 | @r0_11(unknown[12]) | +| ArrayInit(int, float) -> void | 0 | 12 | 0 | @mu0_12(unknown[12]) | +| ArrayInit(int, float) -> void | 0 | 13 | 0 | @r0_13(glval:int[3]) | +| ArrayInit(int, float) -> void | 0 | 14 | 0 | @r0_14(int) | +| ArrayInit(int, float) -> void | 0 | 15 | 0 | @r0_15(glval:int) | +| ArrayInit(int, float) -> void | 0 | 16 | 0 | @r0_16(glval:int) | +| ArrayInit(int, float) -> void | 0 | 17 | 0 | @r0_17(int) | +| ArrayInit(int, float) -> void | 0 | 18 | 0 | @mu0_18(int) | +| ArrayInit(int, float) -> void | 0 | 19 | 0 | @r0_19(int) | +| ArrayInit(int, float) -> void | 0 | 20 | 0 | @r0_20(glval:int) | +| ArrayInit(int, float) -> void | 0 | 21 | 0 | @r0_21(glval:float) | +| ArrayInit(int, float) -> void | 0 | 22 | 0 | @r0_22(float) | +| ArrayInit(int, float) -> void | 0 | 23 | 0 | @r0_23(int) | +| ArrayInit(int, float) -> void | 0 | 24 | 0 | @mu0_24(int) | +| ArrayInit(int, float) -> void | 0 | 25 | 0 | @r0_25(int) | +| ArrayInit(int, float) -> void | 0 | 26 | 0 | @r0_26(glval:int) | +| ArrayInit(int, float) -> void | 0 | 27 | 0 | @r0_27(int) | +| ArrayInit(int, float) -> void | 0 | 28 | 0 | @mu0_28(int) | +| ArrayInit(int, float) -> void | 0 | 29 | 0 | @r0_29(glval:int[3]) | +| ArrayInit(int, float) -> void | 0 | 30 | 0 | @r0_30(int) | +| ArrayInit(int, float) -> void | 0 | 31 | 0 | @r0_31(glval:int) | +| ArrayInit(int, float) -> void | 0 | 32 | 0 | @r0_32(glval:int) | +| ArrayInit(int, float) -> void | 0 | 33 | 0 | @r0_33(int) | +| ArrayInit(int, float) -> void | 0 | 34 | 0 | @mu0_34(int) | +| ArrayInit(int, float) -> void | 0 | 35 | 0 | @r0_35(int) | +| ArrayInit(int, float) -> void | 0 | 36 | 0 | @r0_36(glval:int) | +| ArrayInit(int, float) -> void | 0 | 37 | 0 | @r0_37(unknown[8]) | +| ArrayInit(int, float) -> void | 0 | 38 | 0 | @mu0_38(unknown[8]) | +| ArrayReferences() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| ArrayReferences() -> void | 0 | 2 | 0 | @r0_2(glval:int[10]) | +| ArrayReferences() -> void | 0 | 3 | 0 | @r0_3(int[10]) | +| ArrayReferences() -> void | 0 | 4 | 0 | @mu0_4(int[10]) | +| ArrayReferences() -> void | 0 | 5 | 0 | @r0_5(glval:int(&)[10]) | +| ArrayReferences() -> void | 0 | 6 | 0 | @r0_6(glval:int[10]) | +| ArrayReferences() -> void | 0 | 7 | 0 | @m0_7(int(&)[10]) | +| ArrayReferences() -> void | 0 | 8 | 0 | @r0_8(glval:int) | +| ArrayReferences() -> void | 0 | 9 | 0 | @r0_9(glval:int(&)[10]) | +| ArrayReferences() -> void | 0 | 10 | 0 | @r0_10(int(&)[10]) | +| ArrayReferences() -> void | 0 | 11 | 0 | @r0_11(int *) | +| ArrayReferences() -> void | 0 | 12 | 0 | @r0_12(int) | +| ArrayReferences() -> void | 0 | 13 | 0 | @r0_13(int *) | +| ArrayReferences() -> void | 0 | 14 | 0 | @r0_14(int) | +| ArrayReferences() -> void | 0 | 15 | 0 | @m0_15(int) | +| Base::Base() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Base::Base() -> void | 0 | 2 | 0 | @r0_2(glval:Base) | +| Base::Base() -> void | 0 | 3 | 0 | @r0_3(glval:String) | +| Base::Base() -> void | 0 | 4 | 0 | @r0_4(bool) | +| Base::Base(const Base &) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Base::Base(const Base &) -> void | 0 | 2 | 0 | @r0_2(glval:Base) | +| Base::Base(const Base &) -> void | 0 | 3 | 0 | @r0_3(Base &) | +| Base::Base(const Base &) -> void | 0 | 4 | 0 | @r0_4(glval:Base &) | +| Base::Base(const Base &) -> void | 0 | 5 | 0 | @m0_5(Base &) | +| Base::Base(const Base &) -> void | 0 | 6 | 0 | @r0_6(glval:String) | +| Base::Base(const Base &) -> void | 0 | 7 | 0 | @r0_7(bool) | +| Base::operator=(const Base &) -> Base & | 0 | 1 | 0 | @mu0_1(unknown) | +| Base::operator=(const Base &) -> Base & | 0 | 2 | 0 | @r0_2(glval:Base) | +| Base::operator=(const Base &) -> Base & | 0 | 3 | 0 | @r0_3(Base &) | +| Base::operator=(const Base &) -> Base & | 0 | 4 | 0 | @r0_4(glval:Base &) | +| Base::operator=(const Base &) -> Base & | 0 | 5 | 0 | @m0_5(Base &) | +| Base::operator=(const Base &) -> Base & | 0 | 6 | 0 | @r0_6(Base *) | +| Base::operator=(const Base &) -> Base & | 0 | 7 | 0 | @r0_7(glval:String) | +| Base::operator=(const Base &) -> Base & | 0 | 8 | 0 | @r0_8(bool) | +| Base::operator=(const Base &) -> Base & | 0 | 9 | 0 | @r0_9(glval:Base &) | +| Base::operator=(const Base &) -> Base & | 0 | 10 | 0 | @r0_10(Base &) | +| Base::operator=(const Base &) -> Base & | 0 | 11 | 0 | @r0_11(glval:String) | +| Base::operator=(const Base &) -> Base & | 0 | 12 | 0 | @r0_12(String &) | +| Base::operator=(const Base &) -> Base & | 0 | 13 | 0 | @r0_13(glval:Base &) | +| Base::operator=(const Base &) -> Base & | 0 | 14 | 0 | @r0_14(Base *) | +| Base::operator=(const Base &) -> Base & | 0 | 15 | 0 | @m0_15(Base &) | +| Base::operator=(const Base &) -> Base & | 0 | 16 | 0 | @r0_16(glval:Base &) | +| Base::~Base() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Base::~Base() -> void | 0 | 2 | 0 | @r0_2(glval:Base) | +| Base::~Base() -> void | 0 | 4 | 0 | @r0_4(glval:String) | +| Base::~Base() -> void | 0 | 5 | 0 | @r0_5(bool) | +| Break(int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Break(int) -> void | 0 | 2 | 0 | @r0_2(int) | +| Break(int) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| Break(int) -> void | 0 | 4 | 0 | @m0_4(int) | +| Break(int) -> void | 1 | 0 | 0 | @r1_0(glval:int) | +| Break(int) -> void | 1 | 1 | 0 | @r1_1(int) | +| Break(int) -> void | 1 | 2 | 0 | @r1_2(int) | +| Break(int) -> void | 1 | 3 | 0 | @r1_3(bool) | +| Break(int) -> void | 3 | 0 | 0 | @r3_0(int) | +| Break(int) -> void | 3 | 1 | 0 | @r3_1(glval:int) | +| Break(int) -> void | 3 | 2 | 0 | @r3_2(int) | +| Break(int) -> void | 3 | 3 | 0 | @r3_3(int) | +| Break(int) -> void | 3 | 4 | 0 | @m3_4(int) | +| Break(int) -> void | 5 | 0 | 0 | @m5_0(int) | +| Break(int) -> void | 5 | 1 | 0 | @r5_1(glval:int) | +| Break(int) -> void | 5 | 2 | 0 | @r5_2(int) | +| Break(int) -> void | 5 | 3 | 0 | @r5_3(int) | +| Break(int) -> void | 5 | 4 | 0 | @r5_4(bool) | +| C::C() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| C::C() -> void | 0 | 2 | 0 | @r0_2(glval:C) | +| C::C() -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| C::C() -> void | 0 | 4 | 0 | @r0_4(int) | +| C::C() -> void | 0 | 5 | 0 | @mu0_5(int) | +| C::C() -> void | 0 | 6 | 0 | @r0_6(glval:String) | +| C::C() -> void | 0 | 7 | 0 | @r0_7(bool) | +| C::C() -> void | 0 | 9 | 0 | @r0_9(glval:char) | +| C::C() -> void | 0 | 10 | 0 | @r0_10(char) | +| C::C() -> void | 0 | 11 | 0 | @mu0_11(char) | +| C::C() -> void | 0 | 12 | 0 | @r0_12(glval:void *) | +| C::C() -> void | 0 | 13 | 0 | @r0_13(void *) | +| C::C() -> void | 0 | 14 | 0 | @mu0_14(void *) | +| C::C() -> void | 0 | 15 | 0 | @r0_15(glval:String) | +| C::C() -> void | 0 | 16 | 0 | @r0_16(bool) | +| C::C() -> void | 0 | 17 | 0 | @r0_17(glval:char[5]) | +| C::C() -> void | 0 | 18 | 0 | @r0_18(char *) | +| C::FieldAccess() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| C::FieldAccess() -> void | 0 | 2 | 0 | @r0_2(glval:C) | +| C::FieldAccess() -> void | 0 | 3 | 0 | @r0_3(int) | +| C::FieldAccess() -> void | 0 | 4 | 0 | @r0_4(C *) | +| C::FieldAccess() -> void | 0 | 5 | 0 | @r0_5(glval:int) | +| C::FieldAccess() -> void | 0 | 6 | 0 | @mu0_6(int) | +| C::FieldAccess() -> void | 0 | 7 | 0 | @r0_7(int) | +| C::FieldAccess() -> void | 0 | 8 | 0 | @r0_8(C *) | +| C::FieldAccess() -> void | 0 | 9 | 0 | @r0_9(glval:int) | +| C::FieldAccess() -> void | 0 | 10 | 0 | @mu0_10(int) | +| C::FieldAccess() -> void | 0 | 11 | 0 | @r0_11(int) | +| C::FieldAccess() -> void | 0 | 12 | 0 | @r0_12(C *) | +| C::FieldAccess() -> void | 0 | 13 | 0 | @r0_13(glval:int) | +| C::FieldAccess() -> void | 0 | 14 | 0 | @mu0_14(int) | +| C::FieldAccess() -> void | 0 | 15 | 0 | @r0_15(glval:int) | +| C::FieldAccess() -> void | 0 | 16 | 0 | @r0_16(int) | +| C::FieldAccess() -> void | 0 | 17 | 0 | @m0_17(int) | +| C::FieldAccess() -> void | 0 | 18 | 0 | @r0_18(C *) | +| C::FieldAccess() -> void | 0 | 19 | 0 | @r0_19(glval:int) | +| C::FieldAccess() -> void | 0 | 20 | 0 | @r0_20(int) | +| C::FieldAccess() -> void | 0 | 21 | 0 | @r0_21(glval:int) | +| C::FieldAccess() -> void | 0 | 22 | 0 | @m0_22(int) | +| C::FieldAccess() -> void | 0 | 23 | 0 | @r0_23(C *) | +| C::FieldAccess() -> void | 0 | 24 | 0 | @r0_24(glval:int) | +| C::FieldAccess() -> void | 0 | 25 | 0 | @r0_25(int) | +| C::FieldAccess() -> void | 0 | 26 | 0 | @r0_26(glval:int) | +| C::FieldAccess() -> void | 0 | 27 | 0 | @m0_27(int) | +| C::FieldAccess() -> void | 0 | 28 | 0 | @r0_28(C *) | +| C::FieldAccess() -> void | 0 | 29 | 0 | @r0_29(glval:int) | +| C::FieldAccess() -> void | 0 | 30 | 0 | @r0_30(int) | +| C::FieldAccess() -> void | 0 | 31 | 0 | @r0_31(glval:int) | +| C::FieldAccess() -> void | 0 | 32 | 0 | @m0_32(int) | +| C::InstanceMemberFunction(int) -> int | 0 | 1 | 0 | @mu0_1(unknown) | +| C::InstanceMemberFunction(int) -> int | 0 | 2 | 0 | @r0_2(glval:C) | +| C::InstanceMemberFunction(int) -> int | 0 | 3 | 0 | @r0_3(int) | +| C::InstanceMemberFunction(int) -> int | 0 | 4 | 0 | @r0_4(glval:int) | +| C::InstanceMemberFunction(int) -> int | 0 | 5 | 0 | @m0_5(int) | +| C::InstanceMemberFunction(int) -> int | 0 | 6 | 0 | @r0_6(glval:int) | +| C::InstanceMemberFunction(int) -> int | 0 | 7 | 0 | @r0_7(glval:int) | +| C::InstanceMemberFunction(int) -> int | 0 | 8 | 0 | @r0_8(int) | +| C::InstanceMemberFunction(int) -> int | 0 | 9 | 0 | @m0_9(int) | +| C::InstanceMemberFunction(int) -> int | 0 | 10 | 0 | @r0_10(glval:int) | +| C::MethodCalls() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| C::MethodCalls() -> void | 0 | 2 | 0 | @r0_2(glval:C) | +| C::MethodCalls() -> void | 0 | 3 | 0 | @r0_3(C *) | +| C::MethodCalls() -> void | 0 | 4 | 0 | @r0_4(bool) | +| C::MethodCalls() -> void | 0 | 5 | 0 | @r0_5(int) | +| C::MethodCalls() -> void | 0 | 6 | 0 | @r0_6(int) | +| C::MethodCalls() -> void | 0 | 7 | 0 | @r0_7(C *) | +| C::MethodCalls() -> void | 0 | 8 | 0 | @r0_8(bool) | +| C::MethodCalls() -> void | 0 | 9 | 0 | @r0_9(int) | +| C::MethodCalls() -> void | 0 | 10 | 0 | @r0_10(int) | +| C::MethodCalls() -> void | 0 | 11 | 0 | @r0_11(C *) | +| C::MethodCalls() -> void | 0 | 12 | 0 | @r0_12(bool) | +| C::MethodCalls() -> void | 0 | 13 | 0 | @r0_13(int) | +| C::MethodCalls() -> void | 0 | 14 | 0 | @r0_14(int) | +| C::StaticMemberFunction(int) -> int | 0 | 1 | 0 | @mu0_1(unknown) | +| C::StaticMemberFunction(int) -> int | 0 | 2 | 0 | @r0_2(int) | +| C::StaticMemberFunction(int) -> int | 0 | 3 | 0 | @r0_3(glval:int) | +| C::StaticMemberFunction(int) -> int | 0 | 4 | 0 | @m0_4(int) | +| C::StaticMemberFunction(int) -> int | 0 | 5 | 0 | @r0_5(glval:int) | +| C::StaticMemberFunction(int) -> int | 0 | 6 | 0 | @r0_6(glval:int) | +| C::StaticMemberFunction(int) -> int | 0 | 7 | 0 | @r0_7(int) | +| C::StaticMemberFunction(int) -> int | 0 | 8 | 0 | @m0_8(int) | +| C::StaticMemberFunction(int) -> int | 0 | 9 | 0 | @r0_9(glval:int) | +| C::VirtualMemberFunction(int) -> int | 0 | 1 | 0 | @mu0_1(unknown) | +| C::VirtualMemberFunction(int) -> int | 0 | 2 | 0 | @r0_2(glval:C) | +| C::VirtualMemberFunction(int) -> int | 0 | 3 | 0 | @r0_3(int) | +| C::VirtualMemberFunction(int) -> int | 0 | 4 | 0 | @r0_4(glval:int) | +| C::VirtualMemberFunction(int) -> int | 0 | 5 | 0 | @m0_5(int) | +| C::VirtualMemberFunction(int) -> int | 0 | 6 | 0 | @r0_6(glval:int) | +| C::VirtualMemberFunction(int) -> int | 0 | 7 | 0 | @r0_7(glval:int) | +| C::VirtualMemberFunction(int) -> int | 0 | 8 | 0 | @r0_8(int) | +| C::VirtualMemberFunction(int) -> int | 0 | 9 | 0 | @m0_9(int) | +| C::VirtualMemberFunction(int) -> int | 0 | 10 | 0 | @r0_10(glval:int) | +| Call() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Call() -> void | 0 | 2 | 0 | @r0_2(bool) | +| CallAdd(int, int) -> int | 0 | 1 | 0 | @mu0_1(unknown) | +| CallAdd(int, int) -> int | 0 | 2 | 0 | @r0_2(int) | +| CallAdd(int, int) -> int | 0 | 3 | 0 | @r0_3(glval:int) | +| CallAdd(int, int) -> int | 0 | 4 | 0 | @m0_4(int) | +| CallAdd(int, int) -> int | 0 | 5 | 0 | @r0_5(int) | +| CallAdd(int, int) -> int | 0 | 6 | 0 | @r0_6(glval:int) | +| CallAdd(int, int) -> int | 0 | 7 | 0 | @m0_7(int) | +| CallAdd(int, int) -> int | 0 | 8 | 0 | @r0_8(glval:int) | +| CallAdd(int, int) -> int | 0 | 9 | 0 | @r0_9(bool) | +| CallAdd(int, int) -> int | 0 | 10 | 0 | @r0_10(glval:int) | +| CallAdd(int, int) -> int | 0 | 11 | 0 | @r0_11(int) | +| CallAdd(int, int) -> int | 0 | 12 | 0 | @r0_12(glval:int) | +| CallAdd(int, int) -> int | 0 | 13 | 0 | @r0_13(int) | +| CallAdd(int, int) -> int | 0 | 14 | 0 | @r0_14(int) | +| CallAdd(int, int) -> int | 0 | 15 | 0 | @m0_15(int) | +| CallAdd(int, int) -> int | 0 | 16 | 0 | @r0_16(glval:int) | +| CallMethods(String &, String *, String) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| CallMethods(String &, String *, String) -> void | 0 | 2 | 0 | @r0_2(String &) | +| CallMethods(String &, String *, String) -> void | 0 | 3 | 0 | @r0_3(glval:String &) | +| CallMethods(String &, String *, String) -> void | 0 | 4 | 0 | @m0_4(String &) | +| CallMethods(String &, String *, String) -> void | 0 | 5 | 0 | @r0_5(String *) | +| CallMethods(String &, String *, String) -> void | 0 | 6 | 0 | @r0_6(glval:String *) | +| CallMethods(String &, String *, String) -> void | 0 | 7 | 0 | @m0_7(String *) | +| CallMethods(String &, String *, String) -> void | 0 | 8 | 0 | @r0_8(String) | +| CallMethods(String &, String *, String) -> void | 0 | 9 | 0 | @r0_9(glval:String) | +| CallMethods(String &, String *, String) -> void | 0 | 10 | 0 | @mu0_10(String) | +| CallMethods(String &, String *, String) -> void | 0 | 11 | 0 | @r0_11(glval:String &) | +| CallMethods(String &, String *, String) -> void | 0 | 12 | 0 | @r0_12(String &) | +| CallMethods(String &, String *, String) -> void | 0 | 13 | 0 | @r0_13(glval:String) | +| CallMethods(String &, String *, String) -> void | 0 | 14 | 0 | @r0_14(bool) | +| CallMethods(String &, String *, String) -> void | 0 | 15 | 0 | @r0_15(char *) | +| CallMethods(String &, String *, String) -> void | 0 | 16 | 0 | @r0_16(glval:String *) | +| CallMethods(String &, String *, String) -> void | 0 | 17 | 0 | @r0_17(String *) | +| CallMethods(String &, String *, String) -> void | 0 | 18 | 0 | @r0_18(String *) | +| CallMethods(String &, String *, String) -> void | 0 | 19 | 0 | @r0_19(bool) | +| CallMethods(String &, String *, String) -> void | 0 | 20 | 0 | @r0_20(char *) | +| CallMethods(String &, String *, String) -> void | 0 | 21 | 0 | @r0_21(glval:String) | +| CallMethods(String &, String *, String) -> void | 0 | 22 | 0 | @r0_22(glval:String) | +| CallMethods(String &, String *, String) -> void | 0 | 23 | 0 | @r0_23(bool) | +| CallMethods(String &, String *, String) -> void | 0 | 24 | 0 | @r0_24(char *) | +| CallMin(int, int) -> int | 0 | 1 | 0 | @mu0_1(unknown) | +| CallMin(int, int) -> int | 0 | 2 | 0 | @r0_2(int) | +| CallMin(int, int) -> int | 0 | 3 | 0 | @r0_3(glval:int) | +| CallMin(int, int) -> int | 0 | 4 | 0 | @m0_4(int) | +| CallMin(int, int) -> int | 0 | 5 | 0 | @r0_5(int) | +| CallMin(int, int) -> int | 0 | 6 | 0 | @r0_6(glval:int) | +| CallMin(int, int) -> int | 0 | 7 | 0 | @m0_7(int) | +| CallMin(int, int) -> int | 0 | 8 | 0 | @r0_8(glval:int) | +| CallMin(int, int) -> int | 0 | 9 | 0 | @r0_9(bool) | +| CallMin(int, int) -> int | 0 | 10 | 0 | @r0_10(glval:int) | +| CallMin(int, int) -> int | 0 | 11 | 0 | @r0_11(int) | +| CallMin(int, int) -> int | 0 | 12 | 0 | @r0_12(glval:int) | +| CallMin(int, int) -> int | 0 | 13 | 0 | @r0_13(int) | +| CallMin(int, int) -> int | 0 | 14 | 0 | @r0_14(int) | +| CallMin(int, int) -> int | 0 | 15 | 0 | @m0_15(int) | +| CallMin(int, int) -> int | 0 | 16 | 0 | @r0_16(glval:int) | +| CallNestedTemplateFunc() -> double | 0 | 1 | 0 | @mu0_1(unknown) | +| CallNestedTemplateFunc() -> double | 0 | 2 | 0 | @r0_2(glval:double) | +| CallNestedTemplateFunc() -> double | 0 | 3 | 0 | @r0_3(bool) | +| CallNestedTemplateFunc() -> double | 0 | 4 | 0 | @r0_4(void *) | +| CallNestedTemplateFunc() -> double | 0 | 5 | 0 | @r0_5(char) | +| CallNestedTemplateFunc() -> double | 0 | 6 | 0 | @r0_6(long) | +| CallNestedTemplateFunc() -> double | 0 | 7 | 0 | @r0_7(double) | +| CallNestedTemplateFunc() -> double | 0 | 8 | 0 | @m0_8(double) | +| CallNestedTemplateFunc() -> double | 0 | 9 | 0 | @r0_9(glval:double) | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 1 | 0 | @mu0_1(unknown) | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 2 | 0 | @r0_2(..(*)(..)) | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 3 | 0 | @r0_3(glval:..(*)(..)) | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 4 | 0 | @m0_4(..(*)(..)) | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 5 | 0 | @r0_5(glval:int) | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 6 | 0 | @r0_6(glval:..(*)(..)) | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 7 | 0 | @r0_7(..(*)(..)) | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 8 | 0 | @r0_8(int) | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 9 | 0 | @r0_9(int) | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 10 | 0 | @m0_10(int) | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 11 | 0 | @r0_11(glval:int) | +| Comma(int, int) -> int | 0 | 1 | 0 | @mu0_1(unknown) | +| Comma(int, int) -> int | 0 | 2 | 0 | @r0_2(int) | +| Comma(int, int) -> int | 0 | 3 | 0 | @r0_3(glval:int) | +| Comma(int, int) -> int | 0 | 4 | 0 | @m0_4(int) | +| Comma(int, int) -> int | 0 | 5 | 0 | @r0_5(int) | +| Comma(int, int) -> int | 0 | 6 | 0 | @r0_6(glval:int) | +| Comma(int, int) -> int | 0 | 7 | 0 | @m0_7(int) | +| Comma(int, int) -> int | 0 | 8 | 0 | @r0_8(glval:int) | +| Comma(int, int) -> int | 0 | 9 | 0 | @r0_9(bool) | +| Comma(int, int) -> int | 0 | 11 | 0 | @r0_11(bool) | +| Comma(int, int) -> int | 0 | 12 | 0 | @r0_12(glval:int) | +| Comma(int, int) -> int | 0 | 13 | 0 | @r0_13(int) | +| Comma(int, int) -> int | 0 | 14 | 0 | @r0_14(glval:int) | +| Comma(int, int) -> int | 0 | 15 | 0 | @r0_15(int) | +| Comma(int, int) -> int | 0 | 16 | 0 | @r0_16(int) | +| Comma(int, int) -> int | 0 | 17 | 0 | @m0_17(int) | +| Comma(int, int) -> int | 0 | 18 | 0 | @r0_18(glval:int) | +| CompoundAssignment() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| CompoundAssignment() -> void | 0 | 2 | 0 | @r0_2(glval:int) | +| CompoundAssignment() -> void | 0 | 3 | 0 | @r0_3(int) | +| CompoundAssignment() -> void | 0 | 4 | 0 | @m0_4(int) | +| CompoundAssignment() -> void | 0 | 5 | 0 | @r0_5(int) | +| CompoundAssignment() -> void | 0 | 6 | 0 | @r0_6(glval:int) | +| CompoundAssignment() -> void | 0 | 7 | 0 | @r0_7(int) | +| CompoundAssignment() -> void | 0 | 8 | 0 | @r0_8(int) | +| CompoundAssignment() -> void | 0 | 9 | 0 | @m0_9(int) | +| CompoundAssignment() -> void | 0 | 10 | 0 | @r0_10(glval:short) | +| CompoundAssignment() -> void | 0 | 11 | 0 | @r0_11(short) | +| CompoundAssignment() -> void | 0 | 12 | 0 | @m0_12(short) | +| CompoundAssignment() -> void | 0 | 13 | 0 | @r0_13(glval:int) | +| CompoundAssignment() -> void | 0 | 14 | 0 | @r0_14(int) | +| CompoundAssignment() -> void | 0 | 15 | 0 | @r0_15(glval:short) | +| CompoundAssignment() -> void | 0 | 16 | 0 | @r0_16(short) | +| CompoundAssignment() -> void | 0 | 17 | 0 | @r0_17(int) | +| CompoundAssignment() -> void | 0 | 18 | 0 | @r0_18(int) | +| CompoundAssignment() -> void | 0 | 19 | 0 | @r0_19(short) | +| CompoundAssignment() -> void | 0 | 20 | 0 | @m0_20(short) | +| CompoundAssignment() -> void | 0 | 21 | 0 | @r0_21(int) | +| CompoundAssignment() -> void | 0 | 22 | 0 | @r0_22(glval:short) | +| CompoundAssignment() -> void | 0 | 23 | 0 | @r0_23(short) | +| CompoundAssignment() -> void | 0 | 24 | 0 | @r0_24(short) | +| CompoundAssignment() -> void | 0 | 25 | 0 | @m0_25(short) | +| CompoundAssignment() -> void | 0 | 26 | 0 | @r0_26(glval:long) | +| CompoundAssignment() -> void | 0 | 27 | 0 | @r0_27(long) | +| CompoundAssignment() -> void | 0 | 28 | 0 | @m0_28(long) | +| CompoundAssignment() -> void | 0 | 29 | 0 | @r0_29(float) | +| CompoundAssignment() -> void | 0 | 30 | 0 | @r0_30(glval:long) | +| CompoundAssignment() -> void | 0 | 31 | 0 | @r0_31(long) | +| CompoundAssignment() -> void | 0 | 32 | 0 | @r0_32(float) | +| CompoundAssignment() -> void | 0 | 33 | 0 | @r0_33(float) | +| CompoundAssignment() -> void | 0 | 34 | 0 | @r0_34(long) | +| CompoundAssignment() -> void | 0 | 35 | 0 | @m0_35(long) | +| ConditionValues(bool, bool) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| ConditionValues(bool, bool) -> void | 0 | 2 | 0 | @r0_2(bool) | +| ConditionValues(bool, bool) -> void | 0 | 3 | 0 | @r0_3(glval:bool) | +| ConditionValues(bool, bool) -> void | 0 | 4 | 0 | @m0_4(bool) | +| ConditionValues(bool, bool) -> void | 0 | 5 | 0 | @r0_5(bool) | +| ConditionValues(bool, bool) -> void | 0 | 6 | 0 | @r0_6(glval:bool) | +| ConditionValues(bool, bool) -> void | 0 | 7 | 0 | @m0_7(bool) | +| ConditionValues(bool, bool) -> void | 0 | 8 | 0 | @r0_8(glval:bool) | +| ConditionValues(bool, bool) -> void | 0 | 9 | 0 | @r0_9(bool) | +| ConditionValues(bool, bool) -> void | 0 | 10 | 0 | @m0_10(bool) | +| ConditionValues(bool, bool) -> void | 0 | 11 | 0 | @r0_11(glval:bool) | +| ConditionValues(bool, bool) -> void | 0 | 12 | 0 | @r0_12(bool) | +| ConditionValues(bool, bool) -> void | 1 | 0 | 0 | @r1_0(glval:bool) | +| ConditionValues(bool, bool) -> void | 1 | 1 | 0 | @r1_1(bool) | +| ConditionValues(bool, bool) -> void | 2 | 0 | 0 | @r2_0(glval:bool) | +| ConditionValues(bool, bool) -> void | 2 | 1 | 0 | @r2_1(bool) | +| ConditionValues(bool, bool) -> void | 2 | 2 | 0 | @m2_2(bool) | +| ConditionValues(bool, bool) -> void | 3 | 0 | 0 | @m3_0(bool) | +| ConditionValues(bool, bool) -> void | 3 | 1 | 0 | @r3_1(glval:bool) | +| ConditionValues(bool, bool) -> void | 3 | 2 | 0 | @r3_2(bool) | +| ConditionValues(bool, bool) -> void | 3 | 3 | 0 | @r3_3(glval:bool) | +| ConditionValues(bool, bool) -> void | 3 | 4 | 0 | @m3_4(bool) | +| ConditionValues(bool, bool) -> void | 3 | 5 | 0 | @r3_5(glval:bool) | +| ConditionValues(bool, bool) -> void | 3 | 6 | 0 | @r3_6(bool) | +| ConditionValues(bool, bool) -> void | 4 | 0 | 0 | @r4_0(glval:bool) | +| ConditionValues(bool, bool) -> void | 4 | 1 | 0 | @r4_1(bool) | +| ConditionValues(bool, bool) -> void | 4 | 2 | 0 | @m4_2(bool) | +| ConditionValues(bool, bool) -> void | 5 | 0 | 0 | @r5_0(glval:bool) | +| ConditionValues(bool, bool) -> void | 5 | 1 | 0 | @r5_1(bool) | +| ConditionValues(bool, bool) -> void | 6 | 0 | 0 | @r6_0(glval:bool) | +| ConditionValues(bool, bool) -> void | 6 | 1 | 0 | @r6_1(bool) | +| ConditionValues(bool, bool) -> void | 6 | 2 | 0 | @m6_2(bool) | +| ConditionValues(bool, bool) -> void | 7 | 0 | 0 | @m7_0(bool) | +| ConditionValues(bool, bool) -> void | 7 | 1 | 0 | @r7_1(glval:bool) | +| ConditionValues(bool, bool) -> void | 7 | 2 | 0 | @r7_2(bool) | +| ConditionValues(bool, bool) -> void | 7 | 3 | 0 | @r7_3(bool) | +| ConditionValues(bool, bool) -> void | 7 | 4 | 0 | @r7_4(glval:bool) | +| ConditionValues(bool, bool) -> void | 7 | 5 | 0 | @m7_5(bool) | +| ConditionValues(bool, bool) -> void | 8 | 0 | 0 | @r8_0(glval:bool) | +| ConditionValues(bool, bool) -> void | 8 | 1 | 0 | @r8_1(bool) | +| ConditionValues(bool, bool) -> void | 8 | 2 | 0 | @m8_2(bool) | +| ConditionValues(bool, bool) -> void | 9 | 0 | 0 | @r9_0(glval:bool) | +| ConditionValues(bool, bool) -> void | 9 | 1 | 0 | @r9_1(bool) | +| ConditionValues(bool, bool) -> void | 10 | 0 | 0 | @r10_0(glval:bool) | +| ConditionValues(bool, bool) -> void | 10 | 1 | 0 | @r10_1(bool) | +| ConditionValues(bool, bool) -> void | 10 | 2 | 0 | @m10_2(bool) | +| ConditionValues(bool, bool) -> void | 11 | 0 | 0 | @m11_0(bool) | +| ConditionValues(bool, bool) -> void | 11 | 1 | 0 | @r11_1(glval:bool) | +| ConditionValues(bool, bool) -> void | 11 | 2 | 0 | @r11_2(bool) | +| ConditionValues(bool, bool) -> void | 11 | 3 | 0 | @r11_3(glval:bool) | +| ConditionValues(bool, bool) -> void | 11 | 4 | 0 | @m11_4(bool) | +| ConditionValues(bool, bool) -> void | 11 | 5 | 0 | @r11_5(glval:bool) | +| ConditionValues(bool, bool) -> void | 11 | 6 | 0 | @r11_6(bool) | +| ConditionValues(bool, bool) -> void | 12 | 0 | 0 | @r12_0(glval:bool) | +| ConditionValues(bool, bool) -> void | 12 | 1 | 0 | @r12_1(bool) | +| ConditionValues(bool, bool) -> void | 12 | 2 | 0 | @m12_2(bool) | +| Conditional(bool, int, int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Conditional(bool, int, int) -> void | 0 | 2 | 0 | @r0_2(bool) | +| Conditional(bool, int, int) -> void | 0 | 3 | 0 | @r0_3(glval:bool) | +| Conditional(bool, int, int) -> void | 0 | 4 | 0 | @m0_4(bool) | +| Conditional(bool, int, int) -> void | 0 | 5 | 0 | @r0_5(int) | +| Conditional(bool, int, int) -> void | 0 | 6 | 0 | @r0_6(glval:int) | +| Conditional(bool, int, int) -> void | 0 | 7 | 0 | @m0_7(int) | +| Conditional(bool, int, int) -> void | 0 | 8 | 0 | @r0_8(int) | +| Conditional(bool, int, int) -> void | 0 | 9 | 0 | @r0_9(glval:int) | +| Conditional(bool, int, int) -> void | 0 | 10 | 0 | @m0_10(int) | +| Conditional(bool, int, int) -> void | 0 | 11 | 0 | @r0_11(glval:int) | +| Conditional(bool, int, int) -> void | 0 | 12 | 0 | @r0_12(glval:bool) | +| Conditional(bool, int, int) -> void | 0 | 13 | 0 | @r0_13(bool) | +| Conditional(bool, int, int) -> void | 1 | 0 | 0 | @r1_0(glval:int) | +| Conditional(bool, int, int) -> void | 1 | 1 | 0 | @r1_1(int) | +| Conditional(bool, int, int) -> void | 1 | 2 | 0 | @r1_2(glval:int) | +| Conditional(bool, int, int) -> void | 1 | 3 | 0 | @m1_3(int) | +| Conditional(bool, int, int) -> void | 2 | 0 | 0 | @r2_0(glval:int) | +| Conditional(bool, int, int) -> void | 2 | 1 | 0 | @r2_1(int) | +| Conditional(bool, int, int) -> void | 2 | 2 | 0 | @r2_2(glval:int) | +| Conditional(bool, int, int) -> void | 2 | 3 | 0 | @m2_3(int) | +| Conditional(bool, int, int) -> void | 3 | 0 | 0 | @m3_0(int) | +| Conditional(bool, int, int) -> void | 3 | 1 | 0 | @r3_1(glval:int) | +| Conditional(bool, int, int) -> void | 3 | 2 | 0 | @r3_2(int) | +| Conditional(bool, int, int) -> void | 3 | 3 | 0 | @m3_3(int) | +| Conditional_LValue(bool) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Conditional_LValue(bool) -> void | 0 | 2 | 0 | @r0_2(bool) | +| Conditional_LValue(bool) -> void | 0 | 3 | 0 | @r0_3(glval:bool) | +| Conditional_LValue(bool) -> void | 0 | 4 | 0 | @m0_4(bool) | +| Conditional_LValue(bool) -> void | 0 | 5 | 0 | @r0_5(glval:int) | +| Conditional_LValue(bool) -> void | 0 | 6 | 0 | @r0_6(int) | +| Conditional_LValue(bool) -> void | 0 | 7 | 0 | @mu0_7(int) | +| Conditional_LValue(bool) -> void | 0 | 8 | 0 | @r0_8(glval:int) | +| Conditional_LValue(bool) -> void | 0 | 9 | 0 | @r0_9(int) | +| Conditional_LValue(bool) -> void | 0 | 10 | 0 | @mu0_10(int) | +| Conditional_LValue(bool) -> void | 0 | 11 | 0 | @r0_11(int) | +| Conditional_LValue(bool) -> void | 0 | 12 | 0 | @r0_12(glval:bool) | +| Conditional_LValue(bool) -> void | 0 | 13 | 0 | @r0_13(bool) | +| Conditional_LValue(bool) -> void | 1 | 0 | 0 | @m1_0(int) | +| Conditional_LValue(bool) -> void | 1 | 1 | 0 | @r1_1(glval:int) | +| Conditional_LValue(bool) -> void | 1 | 2 | 0 | @r1_2(glval:int) | +| Conditional_LValue(bool) -> void | 1 | 3 | 0 | @mu1_3(int) | +| Conditional_LValue(bool) -> void | 2 | 0 | 0 | @r2_0(glval:int) | +| Conditional_LValue(bool) -> void | 2 | 1 | 0 | @r2_1(glval:int) | +| Conditional_LValue(bool) -> void | 2 | 2 | 0 | @m2_2(int) | +| Conditional_LValue(bool) -> void | 3 | 0 | 0 | @r3_0(glval:int) | +| Conditional_LValue(bool) -> void | 3 | 1 | 0 | @r3_1(glval:int) | +| Conditional_LValue(bool) -> void | 3 | 2 | 0 | @m3_2(int) | +| Conditional_Void(bool) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Conditional_Void(bool) -> void | 0 | 2 | 0 | @r0_2(bool) | +| Conditional_Void(bool) -> void | 0 | 3 | 0 | @r0_3(glval:bool) | +| Conditional_Void(bool) -> void | 0 | 4 | 0 | @m0_4(bool) | +| Conditional_Void(bool) -> void | 0 | 5 | 0 | @r0_5(glval:bool) | +| Conditional_Void(bool) -> void | 0 | 6 | 0 | @r0_6(bool) | +| Conditional_Void(bool) -> void | 2 | 0 | 0 | @r2_0(bool) | +| Conditional_Void(bool) -> void | 3 | 0 | 0 | @r3_0(bool) | +| Constants() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Constants() -> void | 0 | 2 | 0 | @r0_2(glval:char) | +| Constants() -> void | 0 | 3 | 0 | @r0_3(char) | +| Constants() -> void | 0 | 4 | 0 | @m0_4(char) | +| Constants() -> void | 0 | 5 | 0 | @r0_5(glval:char) | +| Constants() -> void | 0 | 6 | 0 | @r0_6(char) | +| Constants() -> void | 0 | 7 | 0 | @m0_7(char) | +| Constants() -> void | 0 | 8 | 0 | @r0_8(glval:signed char) | +| Constants() -> void | 0 | 9 | 0 | @r0_9(signed char) | +| Constants() -> void | 0 | 10 | 0 | @m0_10(signed char) | +| Constants() -> void | 0 | 11 | 0 | @r0_11(glval:signed char) | +| Constants() -> void | 0 | 12 | 0 | @r0_12(signed char) | +| Constants() -> void | 0 | 13 | 0 | @m0_13(signed char) | +| Constants() -> void | 0 | 14 | 0 | @r0_14(glval:unsigned char) | +| Constants() -> void | 0 | 15 | 0 | @r0_15(unsigned char) | +| Constants() -> void | 0 | 16 | 0 | @m0_16(unsigned char) | +| Constants() -> void | 0 | 17 | 0 | @r0_17(glval:unsigned char) | +| Constants() -> void | 0 | 18 | 0 | @r0_18(unsigned char) | +| Constants() -> void | 0 | 19 | 0 | @m0_19(unsigned char) | +| Constants() -> void | 0 | 20 | 0 | @r0_20(glval:short) | +| Constants() -> void | 0 | 21 | 0 | @r0_21(short) | +| Constants() -> void | 0 | 22 | 0 | @m0_22(short) | +| Constants() -> void | 0 | 23 | 0 | @r0_23(glval:unsigned short) | +| Constants() -> void | 0 | 24 | 0 | @r0_24(unsigned short) | +| Constants() -> void | 0 | 25 | 0 | @m0_25(unsigned short) | +| Constants() -> void | 0 | 26 | 0 | @r0_26(glval:int) | +| Constants() -> void | 0 | 27 | 0 | @r0_27(int) | +| Constants() -> void | 0 | 28 | 0 | @m0_28(int) | +| Constants() -> void | 0 | 29 | 0 | @r0_29(glval:unsigned int) | +| Constants() -> void | 0 | 30 | 0 | @r0_30(unsigned int) | +| Constants() -> void | 0 | 31 | 0 | @m0_31(unsigned int) | +| Constants() -> void | 0 | 32 | 0 | @r0_32(glval:long) | +| Constants() -> void | 0 | 33 | 0 | @r0_33(long) | +| Constants() -> void | 0 | 34 | 0 | @m0_34(long) | +| Constants() -> void | 0 | 35 | 0 | @r0_35(glval:unsigned long) | +| Constants() -> void | 0 | 36 | 0 | @r0_36(unsigned long) | +| Constants() -> void | 0 | 37 | 0 | @m0_37(unsigned long) | +| Constants() -> void | 0 | 38 | 0 | @r0_38(glval:long long) | +| Constants() -> void | 0 | 39 | 0 | @r0_39(long long) | +| Constants() -> void | 0 | 40 | 0 | @m0_40(long long) | +| Constants() -> void | 0 | 41 | 0 | @r0_41(glval:long long) | +| Constants() -> void | 0 | 42 | 0 | @r0_42(long long) | +| Constants() -> void | 0 | 43 | 0 | @m0_43(long long) | +| Constants() -> void | 0 | 44 | 0 | @r0_44(glval:unsigned long long) | +| Constants() -> void | 0 | 45 | 0 | @r0_45(unsigned long long) | +| Constants() -> void | 0 | 46 | 0 | @m0_46(unsigned long long) | +| Constants() -> void | 0 | 47 | 0 | @r0_47(glval:unsigned long long) | +| Constants() -> void | 0 | 48 | 0 | @r0_48(unsigned long long) | +| Constants() -> void | 0 | 49 | 0 | @m0_49(unsigned long long) | +| Constants() -> void | 0 | 50 | 0 | @r0_50(glval:bool) | +| Constants() -> void | 0 | 51 | 0 | @r0_51(bool) | +| Constants() -> void | 0 | 52 | 0 | @m0_52(bool) | +| Constants() -> void | 0 | 53 | 0 | @r0_53(glval:bool) | +| Constants() -> void | 0 | 54 | 0 | @r0_54(bool) | +| Constants() -> void | 0 | 55 | 0 | @m0_55(bool) | +| Constants() -> void | 0 | 56 | 0 | @r0_56(glval:wchar_t) | +| Constants() -> void | 0 | 57 | 0 | @r0_57(wchar_t) | +| Constants() -> void | 0 | 58 | 0 | @m0_58(wchar_t) | +| Constants() -> void | 0 | 59 | 0 | @r0_59(glval:wchar_t) | +| Constants() -> void | 0 | 60 | 0 | @r0_60(wchar_t) | +| Constants() -> void | 0 | 61 | 0 | @m0_61(wchar_t) | +| Constants() -> void | 0 | 62 | 0 | @r0_62(glval:char16_t) | +| Constants() -> void | 0 | 63 | 0 | @r0_63(char16_t) | +| Constants() -> void | 0 | 64 | 0 | @m0_64(char16_t) | +| Constants() -> void | 0 | 65 | 0 | @r0_65(glval:char32_t) | +| Constants() -> void | 0 | 66 | 0 | @r0_66(char32_t) | +| Constants() -> void | 0 | 67 | 0 | @m0_67(char32_t) | +| Constants() -> void | 0 | 68 | 0 | @r0_68(glval:float) | +| Constants() -> void | 0 | 69 | 0 | @r0_69(float) | +| Constants() -> void | 0 | 70 | 0 | @m0_70(float) | +| Constants() -> void | 0 | 71 | 0 | @r0_71(glval:float) | +| Constants() -> void | 0 | 72 | 0 | @r0_72(float) | +| Constants() -> void | 0 | 73 | 0 | @m0_73(float) | +| Constants() -> void | 0 | 74 | 0 | @r0_74(glval:float) | +| Constants() -> void | 0 | 75 | 0 | @r0_75(float) | +| Constants() -> void | 0 | 76 | 0 | @m0_76(float) | +| Constants() -> void | 0 | 77 | 0 | @r0_77(glval:double) | +| Constants() -> void | 0 | 78 | 0 | @r0_78(double) | +| Constants() -> void | 0 | 79 | 0 | @m0_79(double) | +| Constants() -> void | 0 | 80 | 0 | @r0_80(glval:double) | +| Constants() -> void | 0 | 81 | 0 | @r0_81(double) | +| Constants() -> void | 0 | 82 | 0 | @m0_82(double) | +| Constants() -> void | 0 | 83 | 0 | @r0_83(glval:double) | +| Constants() -> void | 0 | 84 | 0 | @r0_84(double) | +| Constants() -> void | 0 | 85 | 0 | @m0_85(double) | +| Continue(int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Continue(int) -> void | 0 | 2 | 0 | @r0_2(int) | +| Continue(int) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| Continue(int) -> void | 0 | 4 | 0 | @m0_4(int) | +| Continue(int) -> void | 1 | 0 | 0 | @m1_0(int) | +| Continue(int) -> void | 1 | 1 | 0 | @r1_1(glval:int) | +| Continue(int) -> void | 1 | 2 | 0 | @r1_2(int) | +| Continue(int) -> void | 1 | 3 | 0 | @r1_3(int) | +| Continue(int) -> void | 1 | 4 | 0 | @r1_4(bool) | +| Continue(int) -> void | 3 | 0 | 0 | @r3_0(int) | +| Continue(int) -> void | 3 | 1 | 0 | @r3_1(glval:int) | +| Continue(int) -> void | 3 | 2 | 0 | @r3_2(int) | +| Continue(int) -> void | 3 | 3 | 0 | @r3_3(int) | +| Continue(int) -> void | 3 | 4 | 0 | @m3_4(int) | +| Continue(int) -> void | 4 | 0 | 0 | @m4_0(int) | +| Continue(int) -> void | 4 | 2 | 0 | @r4_2(glval:int) | +| Continue(int) -> void | 4 | 3 | 0 | @r4_3(int) | +| Continue(int) -> void | 4 | 4 | 0 | @r4_4(int) | +| Continue(int) -> void | 4 | 5 | 0 | @r4_5(bool) | +| DeclareObject() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| DeclareObject() -> void | 0 | 2 | 0 | @r0_2(glval:String) | +| DeclareObject() -> void | 0 | 3 | 0 | @r0_3(bool) | +| DeclareObject() -> void | 0 | 5 | 0 | @r0_5(glval:String) | +| DeclareObject() -> void | 0 | 6 | 0 | @r0_6(bool) | +| DeclareObject() -> void | 0 | 7 | 0 | @r0_7(glval:char[6]) | +| DeclareObject() -> void | 0 | 8 | 0 | @r0_8(char *) | +| DeclareObject() -> void | 0 | 10 | 0 | @r0_10(glval:String) | +| DeclareObject() -> void | 0 | 11 | 0 | @r0_11(bool) | +| DeclareObject() -> void | 0 | 12 | 0 | @r0_12(String) | +| DeclareObject() -> void | 0 | 13 | 0 | @m0_13(String) | +| DeclareObject() -> void | 0 | 14 | 0 | @r0_14(glval:String) | +| DeclareObject() -> void | 0 | 15 | 0 | @r0_15(bool) | +| DeclareObject() -> void | 0 | 16 | 0 | @r0_16(glval:char[5]) | +| DeclareObject() -> void | 0 | 17 | 0 | @r0_17(char *) | +| DerefReference(int &) -> int | 0 | 1 | 0 | @mu0_1(unknown) | +| DerefReference(int &) -> int | 0 | 2 | 0 | @r0_2(int &) | +| DerefReference(int &) -> int | 0 | 3 | 0 | @r0_3(glval:int &) | +| DerefReference(int &) -> int | 0 | 4 | 0 | @m0_4(int &) | +| DerefReference(int &) -> int | 0 | 5 | 0 | @r0_5(glval:int) | +| DerefReference(int &) -> int | 0 | 6 | 0 | @r0_6(glval:int &) | +| DerefReference(int &) -> int | 0 | 7 | 0 | @r0_7(int &) | +| DerefReference(int &) -> int | 0 | 8 | 0 | @r0_8(int) | +| DerefReference(int &) -> int | 0 | 9 | 0 | @m0_9(int) | +| DerefReference(int &) -> int | 0 | 10 | 0 | @r0_10(glval:int) | +| Dereference(int *) -> int | 0 | 1 | 0 | @mu0_1(unknown) | +| Dereference(int *) -> int | 0 | 2 | 0 | @r0_2(int *) | +| Dereference(int *) -> int | 0 | 3 | 0 | @r0_3(glval:int *) | +| Dereference(int *) -> int | 0 | 4 | 0 | @m0_4(int *) | +| Dereference(int *) -> int | 0 | 5 | 0 | @r0_5(int) | +| Dereference(int *) -> int | 0 | 6 | 0 | @r0_6(glval:int *) | +| Dereference(int *) -> int | 0 | 7 | 0 | @r0_7(int *) | +| Dereference(int *) -> int | 0 | 8 | 0 | @mu0_8(int) | +| Dereference(int *) -> int | 0 | 9 | 0 | @r0_9(glval:int) | +| Dereference(int *) -> int | 0 | 10 | 0 | @r0_10(glval:int *) | +| Dereference(int *) -> int | 0 | 11 | 0 | @r0_11(int *) | +| Dereference(int *) -> int | 0 | 12 | 0 | @r0_12(int) | +| Dereference(int *) -> int | 0 | 13 | 0 | @m0_13(int) | +| Dereference(int *) -> int | 0 | 14 | 0 | @r0_14(glval:int) | +| Derived::Derived() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Derived::Derived() -> void | 0 | 2 | 0 | @r0_2(glval:Derived) | +| Derived::Derived() -> void | 0 | 3 | 0 | @r0_3(glval:Middle) | +| Derived::Derived() -> void | 0 | 4 | 0 | @r0_4(bool) | +| Derived::Derived() -> void | 0 | 6 | 0 | @r0_6(glval:String) | +| Derived::Derived() -> void | 0 | 7 | 0 | @r0_7(bool) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 1 | 0 | @mu0_1(unknown) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 2 | 0 | @r0_2(glval:Derived) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 3 | 0 | @r0_3(Derived &) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 4 | 0 | @r0_4(glval:Derived &) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 5 | 0 | @m0_5(Derived &) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 6 | 0 | @r0_6(Derived *) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 7 | 0 | @r0_7(Middle *) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 8 | 0 | @r0_8(bool) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 9 | 0 | @r0_9(glval:Derived &) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 10 | 0 | @r0_10(Derived &) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 11 | 0 | @r0_11(Middle *) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 12 | 0 | @r0_12(Middle &) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 13 | 0 | @r0_13(Derived *) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 14 | 0 | @r0_14(glval:String) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 15 | 0 | @r0_15(bool) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 16 | 0 | @r0_16(glval:Derived &) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 17 | 0 | @r0_17(Derived &) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 18 | 0 | @r0_18(glval:String) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 19 | 0 | @r0_19(String &) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 20 | 0 | @r0_20(glval:Derived &) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 21 | 0 | @r0_21(Derived *) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 22 | 0 | @m0_22(Derived &) | +| Derived::operator=(const Derived &) -> Derived & | 0 | 23 | 0 | @r0_23(glval:Derived &) | +| Derived::~Derived() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Derived::~Derived() -> void | 0 | 2 | 0 | @r0_2(glval:Derived) | +| Derived::~Derived() -> void | 0 | 4 | 0 | @r0_4(glval:String) | +| Derived::~Derived() -> void | 0 | 5 | 0 | @r0_5(bool) | +| Derived::~Derived() -> void | 0 | 7 | 0 | @r0_7(glval:Middle) | +| Derived::~Derived() -> void | 0 | 8 | 0 | @r0_8(bool) | +| DerivedVB::DerivedVB() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| DerivedVB::DerivedVB() -> void | 0 | 2 | 0 | @r0_2(glval:DerivedVB) | +| DerivedVB::DerivedVB() -> void | 0 | 3 | 0 | @r0_3(glval:Base) | +| DerivedVB::DerivedVB() -> void | 0 | 4 | 0 | @r0_4(bool) | +| DerivedVB::DerivedVB() -> void | 0 | 6 | 0 | @r0_6(glval:MiddleVB1) | +| DerivedVB::DerivedVB() -> void | 0 | 7 | 0 | @r0_7(bool) | +| DerivedVB::DerivedVB() -> void | 0 | 9 | 0 | @r0_9(glval:MiddleVB2) | +| DerivedVB::DerivedVB() -> void | 0 | 10 | 0 | @r0_10(bool) | +| DerivedVB::DerivedVB() -> void | 0 | 12 | 0 | @r0_12(glval:String) | +| DerivedVB::DerivedVB() -> void | 0 | 13 | 0 | @r0_13(bool) | +| DerivedVB::~DerivedVB() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| DerivedVB::~DerivedVB() -> void | 0 | 2 | 0 | @r0_2(glval:DerivedVB) | +| DerivedVB::~DerivedVB() -> void | 0 | 4 | 0 | @r0_4(glval:String) | +| DerivedVB::~DerivedVB() -> void | 0 | 5 | 0 | @r0_5(bool) | +| DerivedVB::~DerivedVB() -> void | 0 | 7 | 0 | @r0_7(glval:MiddleVB2) | +| DerivedVB::~DerivedVB() -> void | 0 | 8 | 0 | @r0_8(bool) | +| DerivedVB::~DerivedVB() -> void | 0 | 10 | 0 | @r0_10(glval:MiddleVB1) | +| DerivedVB::~DerivedVB() -> void | 0 | 11 | 0 | @r0_11(bool) | +| DerivedVB::~DerivedVB() -> void | 0 | 13 | 0 | @r0_13(glval:Base) | +| DerivedVB::~DerivedVB() -> void | 0 | 14 | 0 | @r0_14(bool) | +| DoStatements(int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| DoStatements(int) -> void | 0 | 2 | 0 | @r0_2(int) | +| DoStatements(int) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| DoStatements(int) -> void | 0 | 4 | 0 | @m0_4(int) | +| DoStatements(int) -> void | 1 | 0 | 0 | @m1_0(int) | +| DoStatements(int) -> void | 1 | 1 | 0 | @r1_1(int) | +| DoStatements(int) -> void | 1 | 2 | 0 | @r1_2(glval:int) | +| DoStatements(int) -> void | 1 | 3 | 0 | @r1_3(int) | +| DoStatements(int) -> void | 1 | 4 | 0 | @r1_4(int) | +| DoStatements(int) -> void | 1 | 5 | 0 | @m1_5(int) | +| DoStatements(int) -> void | 1 | 6 | 0 | @r1_6(glval:int) | +| DoStatements(int) -> void | 1 | 7 | 0 | @r1_7(int) | +| DoStatements(int) -> void | 1 | 8 | 0 | @r1_8(int) | +| DoStatements(int) -> void | 1 | 9 | 0 | @r1_9(bool) | +| DynamicCast() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| DynamicCast() -> void | 0 | 2 | 0 | @r0_2(glval:PolymorphicBase) | +| DynamicCast() -> void | 0 | 3 | 0 | @r0_3(bool) | +| DynamicCast() -> void | 0 | 5 | 0 | @r0_5(glval:PolymorphicDerived) | +| DynamicCast() -> void | 0 | 6 | 0 | @r0_6(bool) | +| DynamicCast() -> void | 0 | 8 | 0 | @r0_8(glval:PolymorphicBase *) | +| DynamicCast() -> void | 0 | 9 | 0 | @r0_9(glval:PolymorphicBase) | +| DynamicCast() -> void | 0 | 10 | 0 | @m0_10(PolymorphicBase *) | +| DynamicCast() -> void | 0 | 11 | 0 | @r0_11(glval:PolymorphicDerived *) | +| DynamicCast() -> void | 0 | 12 | 0 | @r0_12(glval:PolymorphicDerived) | +| DynamicCast() -> void | 0 | 13 | 0 | @m0_13(PolymorphicDerived *) | +| DynamicCast() -> void | 0 | 14 | 0 | @r0_14(glval:PolymorphicDerived *) | +| DynamicCast() -> void | 0 | 15 | 0 | @r0_15(PolymorphicDerived *) | +| DynamicCast() -> void | 0 | 16 | 0 | @r0_16(PolymorphicBase *) | +| DynamicCast() -> void | 0 | 17 | 0 | @r0_17(glval:PolymorphicBase *) | +| DynamicCast() -> void | 0 | 18 | 0 | @m0_18(PolymorphicBase *) | +| DynamicCast() -> void | 0 | 19 | 0 | @r0_19(glval:PolymorphicBase &) | +| DynamicCast() -> void | 0 | 20 | 0 | @r0_20(glval:PolymorphicDerived) | +| DynamicCast() -> void | 0 | 21 | 0 | @r0_21(glval:PolymorphicBase) | +| DynamicCast() -> void | 0 | 22 | 0 | @m0_22(PolymorphicBase &) | +| DynamicCast() -> void | 0 | 23 | 0 | @r0_23(glval:PolymorphicBase *) | +| DynamicCast() -> void | 0 | 24 | 0 | @r0_24(PolymorphicBase *) | +| DynamicCast() -> void | 0 | 25 | 0 | @r0_25(PolymorphicDerived *) | +| DynamicCast() -> void | 0 | 26 | 0 | @r0_26(glval:PolymorphicDerived *) | +| DynamicCast() -> void | 0 | 27 | 0 | @m0_27(PolymorphicDerived *) | +| DynamicCast() -> void | 0 | 28 | 0 | @r0_28(glval:PolymorphicDerived &) | +| DynamicCast() -> void | 0 | 29 | 0 | @r0_29(glval:PolymorphicBase) | +| DynamicCast() -> void | 0 | 30 | 0 | @r0_30(glval:PolymorphicDerived) | +| DynamicCast() -> void | 0 | 31 | 0 | @m0_31(PolymorphicDerived &) | +| DynamicCast() -> void | 0 | 32 | 0 | @r0_32(glval:void *) | +| DynamicCast() -> void | 0 | 33 | 0 | @r0_33(glval:PolymorphicBase *) | +| DynamicCast() -> void | 0 | 34 | 0 | @r0_34(PolymorphicBase *) | +| DynamicCast() -> void | 0 | 35 | 0 | @r0_35(void *) | +| DynamicCast() -> void | 0 | 36 | 0 | @m0_36(void *) | +| DynamicCast() -> void | 0 | 37 | 0 | @r0_37(glval:void *) | +| DynamicCast() -> void | 0 | 38 | 0 | @r0_38(glval:PolymorphicDerived *) | +| DynamicCast() -> void | 0 | 39 | 0 | @r0_39(PolymorphicDerived *) | +| DynamicCast() -> void | 0 | 40 | 0 | @r0_40(void *) | +| DynamicCast() -> void | 0 | 41 | 0 | @m0_41(void *) | +| EarlyReturn(int, int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| EarlyReturn(int, int) -> void | 0 | 2 | 0 | @r0_2(int) | +| EarlyReturn(int, int) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| EarlyReturn(int, int) -> void | 0 | 4 | 0 | @m0_4(int) | +| EarlyReturn(int, int) -> void | 0 | 5 | 0 | @r0_5(int) | +| EarlyReturn(int, int) -> void | 0 | 6 | 0 | @r0_6(glval:int) | +| EarlyReturn(int, int) -> void | 0 | 7 | 0 | @m0_7(int) | +| EarlyReturn(int, int) -> void | 0 | 8 | 0 | @r0_8(glval:int) | +| EarlyReturn(int, int) -> void | 0 | 9 | 0 | @r0_9(int) | +| EarlyReturn(int, int) -> void | 0 | 10 | 0 | @r0_10(glval:int) | +| EarlyReturn(int, int) -> void | 0 | 11 | 0 | @r0_11(int) | +| EarlyReturn(int, int) -> void | 0 | 12 | 0 | @r0_12(bool) | +| EarlyReturn(int, int) -> void | 3 | 0 | 0 | @r3_0(glval:int) | +| EarlyReturn(int, int) -> void | 3 | 1 | 0 | @r3_1(int) | +| EarlyReturn(int, int) -> void | 3 | 2 | 0 | @r3_2(glval:int) | +| EarlyReturn(int, int) -> void | 3 | 3 | 0 | @m3_3(int) | +| EarlyReturnValue(int, int) -> int | 0 | 1 | 0 | @mu0_1(unknown) | +| EarlyReturnValue(int, int) -> int | 0 | 2 | 0 | @r0_2(int) | +| EarlyReturnValue(int, int) -> int | 0 | 3 | 0 | @r0_3(glval:int) | +| EarlyReturnValue(int, int) -> int | 0 | 4 | 0 | @m0_4(int) | +| EarlyReturnValue(int, int) -> int | 0 | 5 | 0 | @r0_5(int) | +| EarlyReturnValue(int, int) -> int | 0 | 6 | 0 | @r0_6(glval:int) | +| EarlyReturnValue(int, int) -> int | 0 | 7 | 0 | @m0_7(int) | +| EarlyReturnValue(int, int) -> int | 0 | 8 | 0 | @r0_8(glval:int) | +| EarlyReturnValue(int, int) -> int | 0 | 9 | 0 | @r0_9(int) | +| EarlyReturnValue(int, int) -> int | 0 | 10 | 0 | @r0_10(glval:int) | +| EarlyReturnValue(int, int) -> int | 0 | 11 | 0 | @r0_11(int) | +| EarlyReturnValue(int, int) -> int | 0 | 12 | 0 | @r0_12(bool) | +| EarlyReturnValue(int, int) -> int | 1 | 0 | 0 | @m1_0(int) | +| EarlyReturnValue(int, int) -> int | 1 | 1 | 0 | @r1_1(glval:int) | +| EarlyReturnValue(int, int) -> int | 2 | 0 | 0 | @r2_0(glval:int) | +| EarlyReturnValue(int, int) -> int | 2 | 1 | 0 | @r2_1(glval:int) | +| EarlyReturnValue(int, int) -> int | 2 | 2 | 0 | @r2_2(int) | +| EarlyReturnValue(int, int) -> int | 2 | 3 | 0 | @m2_3(int) | +| EarlyReturnValue(int, int) -> int | 3 | 0 | 0 | @r3_0(glval:int) | +| EarlyReturnValue(int, int) -> int | 3 | 1 | 0 | @r3_1(glval:int) | +| EarlyReturnValue(int, int) -> int | 3 | 2 | 0 | @r3_2(int) | +| EarlyReturnValue(int, int) -> int | 3 | 3 | 0 | @r3_3(glval:int) | +| EarlyReturnValue(int, int) -> int | 3 | 4 | 0 | @r3_4(int) | +| EarlyReturnValue(int, int) -> int | 3 | 5 | 0 | @r3_5(int) | +| EarlyReturnValue(int, int) -> int | 3 | 6 | 0 | @m3_6(int) | +| EnumSwitch(E) -> int | 0 | 1 | 0 | @mu0_1(unknown) | +| EnumSwitch(E) -> int | 0 | 2 | 0 | @r0_2(E) | +| EnumSwitch(E) -> int | 0 | 3 | 0 | @r0_3(glval:E) | +| EnumSwitch(E) -> int | 0 | 4 | 0 | @m0_4(E) | +| EnumSwitch(E) -> int | 0 | 5 | 0 | @r0_5(glval:E) | +| EnumSwitch(E) -> int | 0 | 6 | 0 | @r0_6(E) | +| EnumSwitch(E) -> int | 0 | 7 | 0 | @r0_7(int) | +| EnumSwitch(E) -> int | 1 | 0 | 0 | @m1_0(int) | +| EnumSwitch(E) -> int | 1 | 1 | 0 | @r1_1(glval:int) | +| EnumSwitch(E) -> int | 2 | 1 | 0 | @r2_1(glval:int) | +| EnumSwitch(E) -> int | 2 | 2 | 0 | @r2_2(int) | +| EnumSwitch(E) -> int | 2 | 3 | 0 | @m2_3(int) | +| EnumSwitch(E) -> int | 3 | 1 | 0 | @r3_1(glval:int) | +| EnumSwitch(E) -> int | 3 | 2 | 0 | @r3_2(int) | +| EnumSwitch(E) -> int | 3 | 3 | 0 | @m3_3(int) | +| EnumSwitch(E) -> int | 4 | 1 | 0 | @r4_1(glval:int) | +| EnumSwitch(E) -> int | 4 | 2 | 0 | @r4_2(int) | +| EnumSwitch(E) -> int | 4 | 3 | 0 | @m4_3(int) | +| FieldAccess() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| FieldAccess() -> void | 0 | 2 | 0 | @r0_2(glval:Point) | +| FieldAccess() -> void | 0 | 3 | 0 | @r0_3(Point) | +| FieldAccess() -> void | 0 | 4 | 0 | @mu0_4(Point) | +| FieldAccess() -> void | 0 | 5 | 0 | @r0_5(int) | +| FieldAccess() -> void | 0 | 6 | 0 | @r0_6(glval:Point) | +| FieldAccess() -> void | 0 | 7 | 0 | @r0_7(glval:int) | +| FieldAccess() -> void | 0 | 8 | 0 | @mu0_8(int) | +| FieldAccess() -> void | 0 | 9 | 0 | @r0_9(glval:Point) | +| FieldAccess() -> void | 0 | 10 | 0 | @r0_10(glval:int) | +| FieldAccess() -> void | 0 | 11 | 0 | @r0_11(int) | +| FieldAccess() -> void | 0 | 12 | 0 | @r0_12(glval:Point) | +| FieldAccess() -> void | 0 | 13 | 0 | @r0_13(glval:int) | +| FieldAccess() -> void | 0 | 14 | 0 | @mu0_14(int) | +| FieldAccess() -> void | 0 | 15 | 0 | @r0_15(glval:int *) | +| FieldAccess() -> void | 0 | 16 | 0 | @r0_16(glval:Point) | +| FieldAccess() -> void | 0 | 17 | 0 | @r0_17(glval:int) | +| FieldAccess() -> void | 0 | 18 | 0 | @m0_18(int *) | +| FloatCompare(double, double) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| FloatCompare(double, double) -> void | 0 | 2 | 0 | @r0_2(double) | +| FloatCompare(double, double) -> void | 0 | 3 | 0 | @r0_3(glval:double) | +| FloatCompare(double, double) -> void | 0 | 4 | 0 | @m0_4(double) | +| FloatCompare(double, double) -> void | 0 | 5 | 0 | @r0_5(double) | +| FloatCompare(double, double) -> void | 0 | 6 | 0 | @r0_6(glval:double) | +| FloatCompare(double, double) -> void | 0 | 7 | 0 | @m0_7(double) | +| FloatCompare(double, double) -> void | 0 | 8 | 0 | @r0_8(glval:bool) | +| FloatCompare(double, double) -> void | 0 | 9 | 0 | @r0_9(bool) | +| FloatCompare(double, double) -> void | 0 | 10 | 0 | @m0_10(bool) | +| FloatCompare(double, double) -> void | 0 | 11 | 0 | @r0_11(glval:double) | +| FloatCompare(double, double) -> void | 0 | 12 | 0 | @r0_12(double) | +| FloatCompare(double, double) -> void | 0 | 13 | 0 | @r0_13(glval:double) | +| FloatCompare(double, double) -> void | 0 | 14 | 0 | @r0_14(double) | +| FloatCompare(double, double) -> void | 0 | 15 | 0 | @r0_15(bool) | +| FloatCompare(double, double) -> void | 0 | 16 | 0 | @r0_16(glval:bool) | +| FloatCompare(double, double) -> void | 0 | 17 | 0 | @m0_17(bool) | +| FloatCompare(double, double) -> void | 0 | 18 | 0 | @r0_18(glval:double) | +| FloatCompare(double, double) -> void | 0 | 19 | 0 | @r0_19(double) | +| FloatCompare(double, double) -> void | 0 | 20 | 0 | @r0_20(glval:double) | +| FloatCompare(double, double) -> void | 0 | 21 | 0 | @r0_21(double) | +| FloatCompare(double, double) -> void | 0 | 22 | 0 | @r0_22(bool) | +| FloatCompare(double, double) -> void | 0 | 23 | 0 | @r0_23(glval:bool) | +| FloatCompare(double, double) -> void | 0 | 24 | 0 | @m0_24(bool) | +| FloatCompare(double, double) -> void | 0 | 25 | 0 | @r0_25(glval:double) | +| FloatCompare(double, double) -> void | 0 | 26 | 0 | @r0_26(double) | +| FloatCompare(double, double) -> void | 0 | 27 | 0 | @r0_27(glval:double) | +| FloatCompare(double, double) -> void | 0 | 28 | 0 | @r0_28(double) | +| FloatCompare(double, double) -> void | 0 | 29 | 0 | @r0_29(bool) | +| FloatCompare(double, double) -> void | 0 | 30 | 0 | @r0_30(glval:bool) | +| FloatCompare(double, double) -> void | 0 | 31 | 0 | @m0_31(bool) | +| FloatCompare(double, double) -> void | 0 | 32 | 0 | @r0_32(glval:double) | +| FloatCompare(double, double) -> void | 0 | 33 | 0 | @r0_33(double) | +| FloatCompare(double, double) -> void | 0 | 34 | 0 | @r0_34(glval:double) | +| FloatCompare(double, double) -> void | 0 | 35 | 0 | @r0_35(double) | +| FloatCompare(double, double) -> void | 0 | 36 | 0 | @r0_36(bool) | +| FloatCompare(double, double) -> void | 0 | 37 | 0 | @r0_37(glval:bool) | +| FloatCompare(double, double) -> void | 0 | 38 | 0 | @m0_38(bool) | +| FloatCompare(double, double) -> void | 0 | 39 | 0 | @r0_39(glval:double) | +| FloatCompare(double, double) -> void | 0 | 40 | 0 | @r0_40(double) | +| FloatCompare(double, double) -> void | 0 | 41 | 0 | @r0_41(glval:double) | +| FloatCompare(double, double) -> void | 0 | 42 | 0 | @r0_42(double) | +| FloatCompare(double, double) -> void | 0 | 43 | 0 | @r0_43(bool) | +| FloatCompare(double, double) -> void | 0 | 44 | 0 | @r0_44(glval:bool) | +| FloatCompare(double, double) -> void | 0 | 45 | 0 | @m0_45(bool) | +| FloatCompare(double, double) -> void | 0 | 46 | 0 | @r0_46(glval:double) | +| FloatCompare(double, double) -> void | 0 | 47 | 0 | @r0_47(double) | +| FloatCompare(double, double) -> void | 0 | 48 | 0 | @r0_48(glval:double) | +| FloatCompare(double, double) -> void | 0 | 49 | 0 | @r0_49(double) | +| FloatCompare(double, double) -> void | 0 | 50 | 0 | @r0_50(bool) | +| FloatCompare(double, double) -> void | 0 | 51 | 0 | @r0_51(glval:bool) | +| FloatCompare(double, double) -> void | 0 | 52 | 0 | @m0_52(bool) | +| FloatCrement(float) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| FloatCrement(float) -> void | 0 | 2 | 0 | @r0_2(float) | +| FloatCrement(float) -> void | 0 | 3 | 0 | @r0_3(glval:float) | +| FloatCrement(float) -> void | 0 | 4 | 0 | @m0_4(float) | +| FloatCrement(float) -> void | 0 | 5 | 0 | @r0_5(glval:float) | +| FloatCrement(float) -> void | 0 | 6 | 0 | @r0_6(float) | +| FloatCrement(float) -> void | 0 | 7 | 0 | @m0_7(float) | +| FloatCrement(float) -> void | 0 | 8 | 0 | @r0_8(glval:float) | +| FloatCrement(float) -> void | 0 | 9 | 0 | @r0_9(float) | +| FloatCrement(float) -> void | 0 | 10 | 0 | @r0_10(float) | +| FloatCrement(float) -> void | 0 | 11 | 0 | @r0_11(float) | +| FloatCrement(float) -> void | 0 | 12 | 0 | @m0_12(float) | +| FloatCrement(float) -> void | 0 | 13 | 0 | @r0_13(glval:float) | +| FloatCrement(float) -> void | 0 | 14 | 0 | @m0_14(float) | +| FloatCrement(float) -> void | 0 | 15 | 0 | @r0_15(glval:float) | +| FloatCrement(float) -> void | 0 | 16 | 0 | @r0_16(float) | +| FloatCrement(float) -> void | 0 | 17 | 0 | @r0_17(float) | +| FloatCrement(float) -> void | 0 | 18 | 0 | @r0_18(float) | +| FloatCrement(float) -> void | 0 | 19 | 0 | @m0_19(float) | +| FloatCrement(float) -> void | 0 | 20 | 0 | @r0_20(glval:float) | +| FloatCrement(float) -> void | 0 | 21 | 0 | @m0_21(float) | +| FloatCrement(float) -> void | 0 | 22 | 0 | @r0_22(glval:float) | +| FloatCrement(float) -> void | 0 | 23 | 0 | @r0_23(float) | +| FloatCrement(float) -> void | 0 | 24 | 0 | @r0_24(float) | +| FloatCrement(float) -> void | 0 | 25 | 0 | @r0_25(float) | +| FloatCrement(float) -> void | 0 | 26 | 0 | @m0_26(float) | +| FloatCrement(float) -> void | 0 | 27 | 0 | @r0_27(glval:float) | +| FloatCrement(float) -> void | 0 | 28 | 0 | @m0_28(float) | +| FloatCrement(float) -> void | 0 | 29 | 0 | @r0_29(glval:float) | +| FloatCrement(float) -> void | 0 | 30 | 0 | @r0_30(float) | +| FloatCrement(float) -> void | 0 | 31 | 0 | @r0_31(float) | +| FloatCrement(float) -> void | 0 | 32 | 0 | @r0_32(float) | +| FloatCrement(float) -> void | 0 | 33 | 0 | @m0_33(float) | +| FloatCrement(float) -> void | 0 | 34 | 0 | @r0_34(glval:float) | +| FloatCrement(float) -> void | 0 | 35 | 0 | @m0_35(float) | +| FloatOps(double, double) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| FloatOps(double, double) -> void | 0 | 2 | 0 | @r0_2(double) | +| FloatOps(double, double) -> void | 0 | 3 | 0 | @r0_3(glval:double) | +| FloatOps(double, double) -> void | 0 | 4 | 0 | @m0_4(double) | +| FloatOps(double, double) -> void | 0 | 5 | 0 | @r0_5(double) | +| FloatOps(double, double) -> void | 0 | 6 | 0 | @r0_6(glval:double) | +| FloatOps(double, double) -> void | 0 | 7 | 0 | @m0_7(double) | +| FloatOps(double, double) -> void | 0 | 8 | 0 | @r0_8(glval:double) | +| FloatOps(double, double) -> void | 0 | 9 | 0 | @r0_9(double) | +| FloatOps(double, double) -> void | 0 | 10 | 0 | @m0_10(double) | +| FloatOps(double, double) -> void | 0 | 11 | 0 | @r0_11(glval:double) | +| FloatOps(double, double) -> void | 0 | 12 | 0 | @r0_12(double) | +| FloatOps(double, double) -> void | 0 | 13 | 0 | @r0_13(glval:double) | +| FloatOps(double, double) -> void | 0 | 14 | 0 | @r0_14(double) | +| FloatOps(double, double) -> void | 0 | 15 | 0 | @r0_15(double) | +| FloatOps(double, double) -> void | 0 | 16 | 0 | @r0_16(glval:double) | +| FloatOps(double, double) -> void | 0 | 17 | 0 | @m0_17(double) | +| FloatOps(double, double) -> void | 0 | 18 | 0 | @r0_18(glval:double) | +| FloatOps(double, double) -> void | 0 | 19 | 0 | @r0_19(double) | +| FloatOps(double, double) -> void | 0 | 20 | 0 | @r0_20(glval:double) | +| FloatOps(double, double) -> void | 0 | 21 | 0 | @r0_21(double) | +| FloatOps(double, double) -> void | 0 | 22 | 0 | @r0_22(double) | +| FloatOps(double, double) -> void | 0 | 23 | 0 | @r0_23(glval:double) | +| FloatOps(double, double) -> void | 0 | 24 | 0 | @m0_24(double) | +| FloatOps(double, double) -> void | 0 | 25 | 0 | @r0_25(glval:double) | +| FloatOps(double, double) -> void | 0 | 26 | 0 | @r0_26(double) | +| FloatOps(double, double) -> void | 0 | 27 | 0 | @r0_27(glval:double) | +| FloatOps(double, double) -> void | 0 | 28 | 0 | @r0_28(double) | +| FloatOps(double, double) -> void | 0 | 29 | 0 | @r0_29(double) | +| FloatOps(double, double) -> void | 0 | 30 | 0 | @r0_30(glval:double) | +| FloatOps(double, double) -> void | 0 | 31 | 0 | @m0_31(double) | +| FloatOps(double, double) -> void | 0 | 32 | 0 | @r0_32(glval:double) | +| FloatOps(double, double) -> void | 0 | 33 | 0 | @r0_33(double) | +| FloatOps(double, double) -> void | 0 | 34 | 0 | @r0_34(glval:double) | +| FloatOps(double, double) -> void | 0 | 35 | 0 | @r0_35(double) | +| FloatOps(double, double) -> void | 0 | 36 | 0 | @r0_36(double) | +| FloatOps(double, double) -> void | 0 | 37 | 0 | @r0_37(glval:double) | +| FloatOps(double, double) -> void | 0 | 38 | 0 | @m0_38(double) | +| FloatOps(double, double) -> void | 0 | 39 | 0 | @r0_39(glval:double) | +| FloatOps(double, double) -> void | 0 | 40 | 0 | @r0_40(double) | +| FloatOps(double, double) -> void | 0 | 41 | 0 | @r0_41(glval:double) | +| FloatOps(double, double) -> void | 0 | 42 | 0 | @m0_42(double) | +| FloatOps(double, double) -> void | 0 | 43 | 0 | @r0_43(glval:double) | +| FloatOps(double, double) -> void | 0 | 44 | 0 | @r0_44(double) | +| FloatOps(double, double) -> void | 0 | 45 | 0 | @r0_45(glval:double) | +| FloatOps(double, double) -> void | 0 | 46 | 0 | @r0_46(double) | +| FloatOps(double, double) -> void | 0 | 47 | 0 | @r0_47(double) | +| FloatOps(double, double) -> void | 0 | 48 | 0 | @m0_48(double) | +| FloatOps(double, double) -> void | 0 | 49 | 0 | @r0_49(glval:double) | +| FloatOps(double, double) -> void | 0 | 50 | 0 | @r0_50(double) | +| FloatOps(double, double) -> void | 0 | 51 | 0 | @r0_51(glval:double) | +| FloatOps(double, double) -> void | 0 | 52 | 0 | @r0_52(double) | +| FloatOps(double, double) -> void | 0 | 53 | 0 | @r0_53(double) | +| FloatOps(double, double) -> void | 0 | 54 | 0 | @m0_54(double) | +| FloatOps(double, double) -> void | 0 | 55 | 0 | @r0_55(glval:double) | +| FloatOps(double, double) -> void | 0 | 56 | 0 | @r0_56(double) | +| FloatOps(double, double) -> void | 0 | 57 | 0 | @r0_57(glval:double) | +| FloatOps(double, double) -> void | 0 | 58 | 0 | @r0_58(double) | +| FloatOps(double, double) -> void | 0 | 59 | 0 | @r0_59(double) | +| FloatOps(double, double) -> void | 0 | 60 | 0 | @m0_60(double) | +| FloatOps(double, double) -> void | 0 | 61 | 0 | @r0_61(glval:double) | +| FloatOps(double, double) -> void | 0 | 62 | 0 | @r0_62(double) | +| FloatOps(double, double) -> void | 0 | 63 | 0 | @r0_63(glval:double) | +| FloatOps(double, double) -> void | 0 | 64 | 0 | @r0_64(double) | +| FloatOps(double, double) -> void | 0 | 65 | 0 | @r0_65(double) | +| FloatOps(double, double) -> void | 0 | 66 | 0 | @m0_66(double) | +| FloatOps(double, double) -> void | 0 | 67 | 0 | @r0_67(glval:double) | +| FloatOps(double, double) -> void | 0 | 68 | 0 | @r0_68(double) | +| FloatOps(double, double) -> void | 0 | 69 | 0 | @r0_69(double) | +| FloatOps(double, double) -> void | 0 | 70 | 0 | @r0_70(glval:double) | +| FloatOps(double, double) -> void | 0 | 71 | 0 | @m0_71(double) | +| FloatOps(double, double) -> void | 0 | 72 | 0 | @r0_72(glval:double) | +| FloatOps(double, double) -> void | 0 | 73 | 0 | @r0_73(double) | +| FloatOps(double, double) -> void | 0 | 74 | 0 | @r0_74(double) | +| FloatOps(double, double) -> void | 0 | 75 | 0 | @r0_75(glval:double) | +| FloatOps(double, double) -> void | 0 | 76 | 0 | @m0_76(double) | +| Foo() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Foo() -> void | 0 | 2 | 0 | @r0_2(glval:int) | +| Foo() -> void | 0 | 3 | 0 | @r0_3(int) | +| Foo() -> void | 0 | 4 | 0 | @m0_4(int) | +| Foo() -> void | 0 | 5 | 0 | @r0_5(glval:short) | +| Foo() -> void | 0 | 6 | 0 | @r0_6(short) | +| Foo() -> void | 0 | 7 | 0 | @m0_7(short) | +| Foo() -> void | 0 | 8 | 0 | @r0_8(glval:int) | +| Foo() -> void | 0 | 9 | 0 | @r0_9(int) | +| Foo() -> void | 0 | 10 | 0 | @r0_10(glval:short) | +| Foo() -> void | 0 | 11 | 0 | @r0_11(short) | +| Foo() -> void | 0 | 12 | 0 | @r0_12(int) | +| Foo() -> void | 0 | 13 | 0 | @r0_13(int) | +| Foo() -> void | 0 | 14 | 0 | @r0_14(short) | +| Foo() -> void | 0 | 15 | 0 | @r0_15(glval:short) | +| Foo() -> void | 0 | 16 | 0 | @m0_16(short) | +| Foo() -> void | 0 | 17 | 0 | @r0_17(glval:int) | +| Foo() -> void | 0 | 18 | 0 | @r0_18(int) | +| Foo() -> void | 0 | 19 | 0 | @r0_19(glval:short) | +| Foo() -> void | 0 | 20 | 0 | @r0_20(short) | +| Foo() -> void | 0 | 21 | 0 | @r0_21(int) | +| Foo() -> void | 0 | 22 | 0 | @r0_22(int) | +| Foo() -> void | 0 | 23 | 0 | @r0_23(glval:int) | +| Foo() -> void | 0 | 24 | 0 | @m0_24(int) | +| For_Break() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| For_Break() -> void | 0 | 2 | 0 | @r0_2(glval:int) | +| For_Break() -> void | 0 | 3 | 0 | @r0_3(int) | +| For_Break() -> void | 0 | 4 | 0 | @m0_4(int) | +| For_Break() -> void | 1 | 0 | 0 | @m1_0(int) | +| For_Break() -> void | 1 | 1 | 0 | @r1_1(glval:int) | +| For_Break() -> void | 1 | 2 | 0 | @r1_2(int) | +| For_Break() -> void | 1 | 3 | 0 | @r1_3(int) | +| For_Break() -> void | 1 | 4 | 0 | @r1_4(bool) | +| For_Break() -> void | 2 | 0 | 0 | @r2_0(int) | +| For_Break() -> void | 2 | 1 | 0 | @r2_1(glval:int) | +| For_Break() -> void | 2 | 2 | 0 | @r2_2(int) | +| For_Break() -> void | 2 | 3 | 0 | @r2_3(int) | +| For_Break() -> void | 2 | 4 | 0 | @m2_4(int) | +| For_Break() -> void | 3 | 0 | 0 | @r3_0(glval:int) | +| For_Break() -> void | 3 | 1 | 0 | @r3_1(int) | +| For_Break() -> void | 3 | 2 | 0 | @r3_2(int) | +| For_Break() -> void | 3 | 3 | 0 | @r3_3(bool) | +| For_Condition() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| For_Condition() -> void | 0 | 2 | 0 | @r0_2(glval:int) | +| For_Condition() -> void | 0 | 3 | 0 | @r0_3(int) | +| For_Condition() -> void | 0 | 4 | 0 | @m0_4(int) | +| For_Condition() -> void | 1 | 0 | 0 | @r1_0(glval:int) | +| For_Condition() -> void | 1 | 1 | 0 | @r1_1(int) | +| For_Condition() -> void | 1 | 2 | 0 | @r1_2(int) | +| For_Condition() -> void | 1 | 3 | 0 | @r1_3(bool) | +| For_ConditionUpdate() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| For_ConditionUpdate() -> void | 0 | 2 | 0 | @r0_2(glval:int) | +| For_ConditionUpdate() -> void | 0 | 3 | 0 | @r0_3(int) | +| For_ConditionUpdate() -> void | 0 | 4 | 0 | @m0_4(int) | +| For_ConditionUpdate() -> void | 1 | 0 | 0 | @m1_0(int) | +| For_ConditionUpdate() -> void | 1 | 1 | 0 | @r1_1(glval:int) | +| For_ConditionUpdate() -> void | 1 | 2 | 0 | @r1_2(int) | +| For_ConditionUpdate() -> void | 1 | 3 | 0 | @r1_3(int) | +| For_ConditionUpdate() -> void | 1 | 4 | 0 | @r1_4(bool) | +| For_ConditionUpdate() -> void | 2 | 1 | 0 | @r2_1(int) | +| For_ConditionUpdate() -> void | 2 | 2 | 0 | @r2_2(glval:int) | +| For_ConditionUpdate() -> void | 2 | 3 | 0 | @r2_3(int) | +| For_ConditionUpdate() -> void | 2 | 4 | 0 | @r2_4(int) | +| For_ConditionUpdate() -> void | 2 | 5 | 0 | @m2_5(int) | +| For_Continue_NoUpdate() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| For_Continue_NoUpdate() -> void | 0 | 2 | 0 | @r0_2(glval:int) | +| For_Continue_NoUpdate() -> void | 0 | 3 | 0 | @r0_3(int) | +| For_Continue_NoUpdate() -> void | 0 | 4 | 0 | @m0_4(int) | +| For_Continue_NoUpdate() -> void | 1 | 0 | 0 | @r1_0(glval:int) | +| For_Continue_NoUpdate() -> void | 1 | 1 | 0 | @r1_1(int) | +| For_Continue_NoUpdate() -> void | 1 | 2 | 0 | @r1_2(int) | +| For_Continue_NoUpdate() -> void | 1 | 3 | 0 | @r1_3(bool) | +| For_Continue_NoUpdate() -> void | 2 | 0 | 0 | @r2_0(glval:int) | +| For_Continue_NoUpdate() -> void | 2 | 1 | 0 | @r2_1(int) | +| For_Continue_NoUpdate() -> void | 2 | 2 | 0 | @r2_2(int) | +| For_Continue_NoUpdate() -> void | 2 | 3 | 0 | @r2_3(bool) | +| For_Continue_Update() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| For_Continue_Update() -> void | 0 | 2 | 0 | @r0_2(glval:int) | +| For_Continue_Update() -> void | 0 | 3 | 0 | @r0_3(int) | +| For_Continue_Update() -> void | 0 | 4 | 0 | @m0_4(int) | +| For_Continue_Update() -> void | 1 | 0 | 0 | @m1_0(int) | +| For_Continue_Update() -> void | 1 | 1 | 0 | @r1_1(glval:int) | +| For_Continue_Update() -> void | 1 | 2 | 0 | @r1_2(int) | +| For_Continue_Update() -> void | 1 | 3 | 0 | @r1_3(int) | +| For_Continue_Update() -> void | 1 | 4 | 0 | @r1_4(bool) | +| For_Continue_Update() -> void | 2 | 0 | 0 | @r2_0(glval:int) | +| For_Continue_Update() -> void | 2 | 1 | 0 | @r2_1(int) | +| For_Continue_Update() -> void | 2 | 2 | 0 | @r2_2(int) | +| For_Continue_Update() -> void | 2 | 3 | 0 | @r2_3(bool) | +| For_Continue_Update() -> void | 4 | 1 | 0 | @r4_1(int) | +| For_Continue_Update() -> void | 4 | 2 | 0 | @r4_2(glval:int) | +| For_Continue_Update() -> void | 4 | 3 | 0 | @r4_3(int) | +| For_Continue_Update() -> void | 4 | 4 | 0 | @r4_4(int) | +| For_Continue_Update() -> void | 4 | 5 | 0 | @m4_5(int) | +| For_Empty() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| For_Empty() -> void | 0 | 2 | 0 | @r0_2(glval:int) | +| For_Empty() -> void | 0 | 3 | 0 | @r0_3(int) | +| For_Empty() -> void | 0 | 4 | 0 | @m0_4(int) | +| For_Init() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| For_Init() -> void | 0 | 2 | 0 | @r0_2(glval:int) | +| For_Init() -> void | 0 | 3 | 0 | @r0_3(int) | +| For_Init() -> void | 0 | 4 | 0 | @m0_4(int) | +| For_InitCondition() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| For_InitCondition() -> void | 0 | 2 | 0 | @r0_2(glval:int) | +| For_InitCondition() -> void | 0 | 3 | 0 | @r0_3(int) | +| For_InitCondition() -> void | 0 | 4 | 0 | @m0_4(int) | +| For_InitCondition() -> void | 1 | 0 | 0 | @r1_0(glval:int) | +| For_InitCondition() -> void | 1 | 1 | 0 | @r1_1(int) | +| For_InitCondition() -> void | 1 | 2 | 0 | @r1_2(int) | +| For_InitCondition() -> void | 1 | 3 | 0 | @r1_3(bool) | +| For_InitConditionUpdate() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| For_InitConditionUpdate() -> void | 0 | 2 | 0 | @r0_2(glval:int) | +| For_InitConditionUpdate() -> void | 0 | 3 | 0 | @r0_3(int) | +| For_InitConditionUpdate() -> void | 0 | 4 | 0 | @m0_4(int) | +| For_InitConditionUpdate() -> void | 1 | 0 | 0 | @m1_0(int) | +| For_InitConditionUpdate() -> void | 1 | 1 | 0 | @r1_1(glval:int) | +| For_InitConditionUpdate() -> void | 1 | 2 | 0 | @r1_2(int) | +| For_InitConditionUpdate() -> void | 1 | 3 | 0 | @r1_3(int) | +| For_InitConditionUpdate() -> void | 1 | 4 | 0 | @r1_4(bool) | +| For_InitConditionUpdate() -> void | 2 | 1 | 0 | @r2_1(int) | +| For_InitConditionUpdate() -> void | 2 | 2 | 0 | @r2_2(glval:int) | +| For_InitConditionUpdate() -> void | 2 | 3 | 0 | @r2_3(int) | +| For_InitConditionUpdate() -> void | 2 | 4 | 0 | @r2_4(int) | +| For_InitConditionUpdate() -> void | 2 | 5 | 0 | @m2_5(int) | +| For_InitUpdate() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| For_InitUpdate() -> void | 0 | 2 | 0 | @r0_2(glval:int) | +| For_InitUpdate() -> void | 0 | 3 | 0 | @r0_3(int) | +| For_InitUpdate() -> void | 0 | 4 | 0 | @m0_4(int) | +| For_InitUpdate() -> void | 2 | 0 | 0 | @m2_0(int) | +| For_InitUpdate() -> void | 2 | 2 | 0 | @r2_2(int) | +| For_InitUpdate() -> void | 2 | 3 | 0 | @r2_3(glval:int) | +| For_InitUpdate() -> void | 2 | 4 | 0 | @r2_4(int) | +| For_InitUpdate() -> void | 2 | 5 | 0 | @r2_5(int) | +| For_InitUpdate() -> void | 2 | 6 | 0 | @m2_6(int) | +| For_Update() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| For_Update() -> void | 0 | 2 | 0 | @r0_2(glval:int) | +| For_Update() -> void | 0 | 3 | 0 | @r0_3(int) | +| For_Update() -> void | 0 | 4 | 0 | @m0_4(int) | +| For_Update() -> void | 2 | 0 | 0 | @m2_0(int) | +| For_Update() -> void | 2 | 2 | 0 | @r2_2(int) | +| For_Update() -> void | 2 | 3 | 0 | @r2_3(glval:int) | +| For_Update() -> void | 2 | 4 | 0 | @r2_4(int) | +| For_Update() -> void | 2 | 5 | 0 | @r2_5(int) | +| For_Update() -> void | 2 | 6 | 0 | @m2_6(int) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 2 | 0 | @r0_2(..(*)(..)) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 3 | 0 | @r0_3(glval:..(*)(..)) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 4 | 0 | @m0_4(..(*)(..)) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 5 | 0 | @r0_5(void *) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 6 | 0 | @r0_6(glval:void *) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 7 | 0 | @m0_7(void *) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 8 | 0 | @r0_8(glval:..(*)(..)) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 9 | 0 | @r0_9(..(*)(..)) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 10 | 0 | @r0_10(void *) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 11 | 0 | @r0_11(glval:void *) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 12 | 0 | @m0_12(void *) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 13 | 0 | @r0_13(glval:void *) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 14 | 0 | @r0_14(void *) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 15 | 0 | @r0_15(..(*)(..)) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 16 | 0 | @r0_16(glval:..(*)(..)) | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 17 | 0 | @m0_17(..(*)(..)) | +| FunctionReferences() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| FunctionReferences() -> void | 0 | 2 | 0 | @r0_2(glval:..(&)(..)) | +| FunctionReferences() -> void | 0 | 3 | 0 | @r0_3(glval:..()(..)) | +| FunctionReferences() -> void | 0 | 4 | 0 | @m0_4(..(&)(..)) | +| FunctionReferences() -> void | 0 | 5 | 0 | @r0_5(glval:..(*)(..)) | +| FunctionReferences() -> void | 0 | 6 | 0 | @r0_6(glval:..(&)(..)) | +| FunctionReferences() -> void | 0 | 7 | 0 | @r0_7(..(&)(..)) | +| FunctionReferences() -> void | 0 | 8 | 0 | @m0_8(..(*)(..)) | +| FunctionReferences() -> void | 0 | 9 | 0 | @r0_9(glval:..(&)(..)) | +| FunctionReferences() -> void | 0 | 10 | 0 | @r0_10(..(&)(..)) | +| FunctionReferences() -> void | 0 | 11 | 0 | @r0_11(int) | +| FunctionReferences() -> void | 0 | 12 | 0 | @r0_12(int) | +| HierarchyConversions() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| HierarchyConversions() -> void | 0 | 2 | 0 | @r0_2(glval:Base) | +| HierarchyConversions() -> void | 0 | 3 | 0 | @r0_3(bool) | +| HierarchyConversions() -> void | 0 | 5 | 0 | @r0_5(glval:Middle) | +| HierarchyConversions() -> void | 0 | 6 | 0 | @r0_6(bool) | +| HierarchyConversions() -> void | 0 | 8 | 0 | @r0_8(glval:Derived) | +| HierarchyConversions() -> void | 0 | 9 | 0 | @r0_9(bool) | +| HierarchyConversions() -> void | 0 | 11 | 0 | @r0_11(glval:Base *) | +| HierarchyConversions() -> void | 0 | 12 | 0 | @r0_12(glval:Base) | +| HierarchyConversions() -> void | 0 | 13 | 0 | @m0_13(Base *) | +| HierarchyConversions() -> void | 0 | 14 | 0 | @r0_14(glval:Middle *) | +| HierarchyConversions() -> void | 0 | 15 | 0 | @r0_15(glval:Middle) | +| HierarchyConversions() -> void | 0 | 16 | 0 | @m0_16(Middle *) | +| HierarchyConversions() -> void | 0 | 17 | 0 | @r0_17(glval:Derived *) | +| HierarchyConversions() -> void | 0 | 18 | 0 | @r0_18(glval:Derived) | +| HierarchyConversions() -> void | 0 | 19 | 0 | @m0_19(Derived *) | +| HierarchyConversions() -> void | 0 | 20 | 0 | @r0_20(glval:Base) | +| HierarchyConversions() -> void | 0 | 21 | 0 | @r0_21(bool) | +| HierarchyConversions() -> void | 0 | 22 | 0 | @r0_22(glval:Middle) | +| HierarchyConversions() -> void | 0 | 23 | 0 | @r0_23(glval:Base) | +| HierarchyConversions() -> void | 0 | 24 | 0 | @r0_24(Base &) | +| HierarchyConversions() -> void | 0 | 25 | 0 | @r0_25(glval:Base) | +| HierarchyConversions() -> void | 0 | 26 | 0 | @r0_26(bool) | +| HierarchyConversions() -> void | 0 | 27 | 0 | @r0_27(bool) | +| HierarchyConversions() -> void | 0 | 28 | 0 | @r0_28(glval:Middle) | +| HierarchyConversions() -> void | 0 | 29 | 0 | @r0_29(glval:Base) | +| HierarchyConversions() -> void | 0 | 31 | 0 | @r0_31(Base) | +| HierarchyConversions() -> void | 0 | 32 | 0 | @r0_32(Base &) | +| HierarchyConversions() -> void | 0 | 33 | 0 | @r0_33(glval:Base) | +| HierarchyConversions() -> void | 0 | 34 | 0 | @r0_34(bool) | +| HierarchyConversions() -> void | 0 | 35 | 0 | @r0_35(bool) | +| HierarchyConversions() -> void | 0 | 36 | 0 | @r0_36(glval:Middle) | +| HierarchyConversions() -> void | 0 | 37 | 0 | @r0_37(glval:Base) | +| HierarchyConversions() -> void | 0 | 39 | 0 | @r0_39(Base) | +| HierarchyConversions() -> void | 0 | 40 | 0 | @r0_40(Base &) | +| HierarchyConversions() -> void | 0 | 41 | 0 | @r0_41(glval:Middle *) | +| HierarchyConversions() -> void | 0 | 42 | 0 | @r0_42(Middle *) | +| HierarchyConversions() -> void | 0 | 43 | 0 | @r0_43(Base *) | +| HierarchyConversions() -> void | 0 | 44 | 0 | @r0_44(glval:Base *) | +| HierarchyConversions() -> void | 0 | 45 | 0 | @m0_45(Base *) | +| HierarchyConversions() -> void | 0 | 46 | 0 | @r0_46(glval:Middle *) | +| HierarchyConversions() -> void | 0 | 47 | 0 | @r0_47(Middle *) | +| HierarchyConversions() -> void | 0 | 48 | 0 | @r0_48(Base *) | +| HierarchyConversions() -> void | 0 | 49 | 0 | @r0_49(glval:Base *) | +| HierarchyConversions() -> void | 0 | 50 | 0 | @m0_50(Base *) | +| HierarchyConversions() -> void | 0 | 51 | 0 | @r0_51(glval:Middle *) | +| HierarchyConversions() -> void | 0 | 52 | 0 | @r0_52(Middle *) | +| HierarchyConversions() -> void | 0 | 53 | 0 | @r0_53(Base *) | +| HierarchyConversions() -> void | 0 | 54 | 0 | @r0_54(glval:Base *) | +| HierarchyConversions() -> void | 0 | 55 | 0 | @m0_55(Base *) | +| HierarchyConversions() -> void | 0 | 56 | 0 | @r0_56(glval:Middle *) | +| HierarchyConversions() -> void | 0 | 57 | 0 | @r0_57(Middle *) | +| HierarchyConversions() -> void | 0 | 58 | 0 | @r0_58(Base *) | +| HierarchyConversions() -> void | 0 | 59 | 0 | @r0_59(glval:Base *) | +| HierarchyConversions() -> void | 0 | 60 | 0 | @m0_60(Base *) | +| HierarchyConversions() -> void | 0 | 61 | 0 | @r0_61(glval:Middle) | +| HierarchyConversions() -> void | 0 | 62 | 0 | @r0_62(bool) | +| HierarchyConversions() -> void | 0 | 63 | 0 | @r0_63(glval:Base) | +| HierarchyConversions() -> void | 0 | 64 | 0 | @r0_64(glval:Middle) | +| HierarchyConversions() -> void | 0 | 65 | 0 | @r0_65(glval:Middle) | +| HierarchyConversions() -> void | 0 | 66 | 0 | @r0_66(Middle &) | +| HierarchyConversions() -> void | 0 | 67 | 0 | @r0_67(glval:Middle) | +| HierarchyConversions() -> void | 0 | 68 | 0 | @r0_68(bool) | +| HierarchyConversions() -> void | 0 | 69 | 0 | @r0_69(glval:Base) | +| HierarchyConversions() -> void | 0 | 70 | 0 | @r0_70(glval:Middle) | +| HierarchyConversions() -> void | 0 | 71 | 0 | @r0_71(glval:Middle) | +| HierarchyConversions() -> void | 0 | 72 | 0 | @r0_72(Middle &) | +| HierarchyConversions() -> void | 0 | 73 | 0 | @r0_73(glval:Base *) | +| HierarchyConversions() -> void | 0 | 74 | 0 | @r0_74(Base *) | +| HierarchyConversions() -> void | 0 | 75 | 0 | @r0_75(Middle *) | +| HierarchyConversions() -> void | 0 | 76 | 0 | @r0_76(glval:Middle *) | +| HierarchyConversions() -> void | 0 | 77 | 0 | @m0_77(Middle *) | +| HierarchyConversions() -> void | 0 | 78 | 0 | @r0_78(glval:Base *) | +| HierarchyConversions() -> void | 0 | 79 | 0 | @r0_79(Base *) | +| HierarchyConversions() -> void | 0 | 80 | 0 | @r0_80(Middle *) | +| HierarchyConversions() -> void | 0 | 81 | 0 | @r0_81(glval:Middle *) | +| HierarchyConversions() -> void | 0 | 82 | 0 | @m0_82(Middle *) | +| HierarchyConversions() -> void | 0 | 83 | 0 | @r0_83(glval:Base *) | +| HierarchyConversions() -> void | 0 | 84 | 0 | @r0_84(Base *) | +| HierarchyConversions() -> void | 0 | 85 | 0 | @r0_85(Middle *) | +| HierarchyConversions() -> void | 0 | 86 | 0 | @r0_86(glval:Middle *) | +| HierarchyConversions() -> void | 0 | 87 | 0 | @m0_87(Middle *) | +| HierarchyConversions() -> void | 0 | 88 | 0 | @r0_88(glval:Base) | +| HierarchyConversions() -> void | 0 | 89 | 0 | @r0_89(bool) | +| HierarchyConversions() -> void | 0 | 90 | 0 | @r0_90(glval:Derived) | +| HierarchyConversions() -> void | 0 | 91 | 0 | @r0_91(glval:Middle) | +| HierarchyConversions() -> void | 0 | 92 | 0 | @r0_92(glval:Base) | +| HierarchyConversions() -> void | 0 | 93 | 0 | @r0_93(Base &) | +| HierarchyConversions() -> void | 0 | 94 | 0 | @r0_94(glval:Base) | +| HierarchyConversions() -> void | 0 | 95 | 0 | @r0_95(bool) | +| HierarchyConversions() -> void | 0 | 96 | 0 | @r0_96(bool) | +| HierarchyConversions() -> void | 0 | 97 | 0 | @r0_97(glval:Derived) | +| HierarchyConversions() -> void | 0 | 98 | 0 | @r0_98(glval:Middle) | +| HierarchyConversions() -> void | 0 | 99 | 0 | @r0_99(glval:Base) | +| HierarchyConversions() -> void | 0 | 101 | 0 | @r0_101(Base) | +| HierarchyConversions() -> void | 0 | 102 | 0 | @r0_102(Base &) | +| HierarchyConversions() -> void | 0 | 103 | 0 | @r0_103(glval:Base) | +| HierarchyConversions() -> void | 0 | 104 | 0 | @r0_104(bool) | +| HierarchyConversions() -> void | 0 | 105 | 0 | @r0_105(bool) | +| HierarchyConversions() -> void | 0 | 106 | 0 | @r0_106(glval:Derived) | +| HierarchyConversions() -> void | 0 | 107 | 0 | @r0_107(glval:Middle) | +| HierarchyConversions() -> void | 0 | 108 | 0 | @r0_108(glval:Base) | +| HierarchyConversions() -> void | 0 | 110 | 0 | @r0_110(Base) | +| HierarchyConversions() -> void | 0 | 111 | 0 | @r0_111(Base &) | +| HierarchyConversions() -> void | 0 | 112 | 0 | @r0_112(glval:Derived *) | +| HierarchyConversions() -> void | 0 | 113 | 0 | @r0_113(Derived *) | +| HierarchyConversions() -> void | 0 | 114 | 0 | @r0_114(Middle *) | +| HierarchyConversions() -> void | 0 | 115 | 0 | @r0_115(Base *) | +| HierarchyConversions() -> void | 0 | 116 | 0 | @r0_116(glval:Base *) | +| HierarchyConversions() -> void | 0 | 117 | 0 | @m0_117(Base *) | +| HierarchyConversions() -> void | 0 | 118 | 0 | @r0_118(glval:Derived *) | +| HierarchyConversions() -> void | 0 | 119 | 0 | @r0_119(Derived *) | +| HierarchyConversions() -> void | 0 | 120 | 0 | @r0_120(Middle *) | +| HierarchyConversions() -> void | 0 | 121 | 0 | @r0_121(Base *) | +| HierarchyConversions() -> void | 0 | 122 | 0 | @r0_122(glval:Base *) | +| HierarchyConversions() -> void | 0 | 123 | 0 | @m0_123(Base *) | +| HierarchyConversions() -> void | 0 | 124 | 0 | @r0_124(glval:Derived *) | +| HierarchyConversions() -> void | 0 | 125 | 0 | @r0_125(Derived *) | +| HierarchyConversions() -> void | 0 | 126 | 0 | @r0_126(Middle *) | +| HierarchyConversions() -> void | 0 | 127 | 0 | @r0_127(Base *) | +| HierarchyConversions() -> void | 0 | 128 | 0 | @r0_128(glval:Base *) | +| HierarchyConversions() -> void | 0 | 129 | 0 | @m0_129(Base *) | +| HierarchyConversions() -> void | 0 | 130 | 0 | @r0_130(glval:Derived *) | +| HierarchyConversions() -> void | 0 | 131 | 0 | @r0_131(Derived *) | +| HierarchyConversions() -> void | 0 | 132 | 0 | @r0_132(Base *) | +| HierarchyConversions() -> void | 0 | 133 | 0 | @r0_133(glval:Base *) | +| HierarchyConversions() -> void | 0 | 134 | 0 | @m0_134(Base *) | +| HierarchyConversions() -> void | 0 | 135 | 0 | @r0_135(glval:Derived) | +| HierarchyConversions() -> void | 0 | 136 | 0 | @r0_136(bool) | +| HierarchyConversions() -> void | 0 | 137 | 0 | @r0_137(glval:Base) | +| HierarchyConversions() -> void | 0 | 138 | 0 | @r0_138(glval:Middle) | +| HierarchyConversions() -> void | 0 | 139 | 0 | @r0_139(glval:Derived) | +| HierarchyConversions() -> void | 0 | 140 | 0 | @r0_140(glval:Derived) | +| HierarchyConversions() -> void | 0 | 141 | 0 | @r0_141(Derived &) | +| HierarchyConversions() -> void | 0 | 142 | 0 | @r0_142(glval:Derived) | +| HierarchyConversions() -> void | 0 | 143 | 0 | @r0_143(bool) | +| HierarchyConversions() -> void | 0 | 144 | 0 | @r0_144(glval:Base) | +| HierarchyConversions() -> void | 0 | 145 | 0 | @r0_145(glval:Middle) | +| HierarchyConversions() -> void | 0 | 146 | 0 | @r0_146(glval:Derived) | +| HierarchyConversions() -> void | 0 | 147 | 0 | @r0_147(glval:Derived) | +| HierarchyConversions() -> void | 0 | 148 | 0 | @r0_148(Derived &) | +| HierarchyConversions() -> void | 0 | 149 | 0 | @r0_149(glval:Base *) | +| HierarchyConversions() -> void | 0 | 150 | 0 | @r0_150(Base *) | +| HierarchyConversions() -> void | 0 | 151 | 0 | @r0_151(Middle *) | +| HierarchyConversions() -> void | 0 | 152 | 0 | @r0_152(Derived *) | +| HierarchyConversions() -> void | 0 | 153 | 0 | @r0_153(glval:Derived *) | +| HierarchyConversions() -> void | 0 | 154 | 0 | @m0_154(Derived *) | +| HierarchyConversions() -> void | 0 | 155 | 0 | @r0_155(glval:Base *) | +| HierarchyConversions() -> void | 0 | 156 | 0 | @r0_156(Base *) | +| HierarchyConversions() -> void | 0 | 157 | 0 | @r0_157(Middle *) | +| HierarchyConversions() -> void | 0 | 158 | 0 | @r0_158(Derived *) | +| HierarchyConversions() -> void | 0 | 159 | 0 | @r0_159(glval:Derived *) | +| HierarchyConversions() -> void | 0 | 160 | 0 | @m0_160(Derived *) | +| HierarchyConversions() -> void | 0 | 161 | 0 | @r0_161(glval:Base *) | +| HierarchyConversions() -> void | 0 | 162 | 0 | @r0_162(Base *) | +| HierarchyConversions() -> void | 0 | 163 | 0 | @r0_163(Derived *) | +| HierarchyConversions() -> void | 0 | 164 | 0 | @r0_164(glval:Derived *) | +| HierarchyConversions() -> void | 0 | 165 | 0 | @m0_165(Derived *) | +| HierarchyConversions() -> void | 0 | 166 | 0 | @r0_166(glval:MiddleVB1 *) | +| HierarchyConversions() -> void | 0 | 167 | 0 | @r0_167(MiddleVB1 *) | +| HierarchyConversions() -> void | 0 | 168 | 0 | @m0_168(MiddleVB1 *) | +| HierarchyConversions() -> void | 0 | 169 | 0 | @r0_169(glval:DerivedVB *) | +| HierarchyConversions() -> void | 0 | 170 | 0 | @r0_170(DerivedVB *) | +| HierarchyConversions() -> void | 0 | 171 | 0 | @m0_171(DerivedVB *) | +| HierarchyConversions() -> void | 0 | 172 | 0 | @r0_172(glval:MiddleVB1 *) | +| HierarchyConversions() -> void | 0 | 173 | 0 | @r0_173(MiddleVB1 *) | +| HierarchyConversions() -> void | 0 | 174 | 0 | @r0_174(Base *) | +| HierarchyConversions() -> void | 0 | 175 | 0 | @r0_175(glval:Base *) | +| HierarchyConversions() -> void | 0 | 176 | 0 | @m0_176(Base *) | +| HierarchyConversions() -> void | 0 | 177 | 0 | @r0_177(glval:DerivedVB *) | +| HierarchyConversions() -> void | 0 | 178 | 0 | @r0_178(DerivedVB *) | +| HierarchyConversions() -> void | 0 | 179 | 0 | @r0_179(Base *) | +| HierarchyConversions() -> void | 0 | 180 | 0 | @r0_180(glval:Base *) | +| HierarchyConversions() -> void | 0 | 181 | 0 | @m0_181(Base *) | +| IfStatements(bool, int, int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| IfStatements(bool, int, int) -> void | 0 | 2 | 0 | @r0_2(bool) | +| IfStatements(bool, int, int) -> void | 0 | 3 | 0 | @r0_3(glval:bool) | +| IfStatements(bool, int, int) -> void | 0 | 4 | 0 | @m0_4(bool) | +| IfStatements(bool, int, int) -> void | 0 | 5 | 0 | @r0_5(int) | +| IfStatements(bool, int, int) -> void | 0 | 6 | 0 | @r0_6(glval:int) | +| IfStatements(bool, int, int) -> void | 0 | 7 | 0 | @m0_7(int) | +| IfStatements(bool, int, int) -> void | 0 | 8 | 0 | @r0_8(int) | +| IfStatements(bool, int, int) -> void | 0 | 9 | 0 | @r0_9(glval:int) | +| IfStatements(bool, int, int) -> void | 0 | 10 | 0 | @m0_10(int) | +| IfStatements(bool, int, int) -> void | 0 | 11 | 0 | @r0_11(glval:bool) | +| IfStatements(bool, int, int) -> void | 0 | 12 | 0 | @r0_12(bool) | +| IfStatements(bool, int, int) -> void | 1 | 0 | 0 | @r1_0(glval:bool) | +| IfStatements(bool, int, int) -> void | 1 | 1 | 0 | @r1_1(bool) | +| IfStatements(bool, int, int) -> void | 2 | 0 | 0 | @r2_0(glval:int) | +| IfStatements(bool, int, int) -> void | 2 | 1 | 0 | @r2_1(int) | +| IfStatements(bool, int, int) -> void | 2 | 2 | 0 | @r2_2(glval:int) | +| IfStatements(bool, int, int) -> void | 2 | 3 | 0 | @m2_3(int) | +| IfStatements(bool, int, int) -> void | 3 | 0 | 0 | @m3_0(int) | +| IfStatements(bool, int, int) -> void | 3 | 1 | 0 | @r3_1(glval:int) | +| IfStatements(bool, int, int) -> void | 3 | 2 | 0 | @r3_2(int) | +| IfStatements(bool, int, int) -> void | 3 | 3 | 0 | @r3_3(int) | +| IfStatements(bool, int, int) -> void | 3 | 4 | 0 | @r3_4(bool) | +| IfStatements(bool, int, int) -> void | 4 | 0 | 0 | @r4_0(int) | +| IfStatements(bool, int, int) -> void | 4 | 1 | 0 | @r4_1(glval:int) | +| IfStatements(bool, int, int) -> void | 4 | 2 | 0 | @m4_2(int) | +| IfStatements(bool, int, int) -> void | 5 | 0 | 0 | @r5_0(int) | +| IfStatements(bool, int, int) -> void | 5 | 1 | 0 | @r5_1(glval:int) | +| IfStatements(bool, int, int) -> void | 5 | 2 | 0 | @m5_2(int) | +| InitArray() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| InitArray() -> void | 0 | 2 | 0 | @r0_2(glval:char[32]) | +| InitArray() -> void | 0 | 3 | 0 | @r0_3(glval:char[1]) | +| InitArray() -> void | 0 | 4 | 0 | @r0_4(char[1]) | +| InitArray() -> void | 0 | 5 | 0 | @mu0_5(char[1]) | +| InitArray() -> void | 0 | 6 | 0 | @r0_6(unknown[31]) | +| InitArray() -> void | 0 | 7 | 0 | @r0_7(int) | +| InitArray() -> void | 0 | 8 | 0 | @r0_8(glval:char) | +| InitArray() -> void | 0 | 9 | 0 | @mu0_9(unknown[31]) | +| InitArray() -> void | 0 | 10 | 0 | @r0_10(glval:char[4]) | +| InitArray() -> void | 0 | 11 | 0 | @r0_11(glval:char[4]) | +| InitArray() -> void | 0 | 12 | 0 | @r0_12(char[4]) | +| InitArray() -> void | 0 | 13 | 0 | @m0_13(char[4]) | +| InitArray() -> void | 0 | 14 | 0 | @r0_14(glval:char[]) | +| InitArray() -> void | 0 | 15 | 0 | @r0_15(glval:char[5]) | +| InitArray() -> void | 0 | 16 | 0 | @r0_16(char[5]) | +| InitArray() -> void | 0 | 17 | 0 | @m0_17(char[5]) | +| InitArray() -> void | 0 | 18 | 0 | @r0_18(glval:char[2]) | +| InitArray() -> void | 0 | 19 | 0 | @r0_19(char[2]) | +| InitArray() -> void | 0 | 20 | 0 | @m0_20(char[2]) | +| InitArray() -> void | 0 | 21 | 0 | @r0_21(glval:char[2]) | +| InitArray() -> void | 0 | 22 | 0 | @r0_22(int) | +| InitArray() -> void | 0 | 23 | 0 | @r0_23(glval:char) | +| InitArray() -> void | 0 | 24 | 0 | @r0_24(unknown[2]) | +| InitArray() -> void | 0 | 25 | 0 | @mu0_25(unknown[2]) | +| InitArray() -> void | 0 | 26 | 0 | @r0_26(glval:char[2]) | +| InitArray() -> void | 0 | 27 | 0 | @r0_27(int) | +| InitArray() -> void | 0 | 28 | 0 | @r0_28(glval:char) | +| InitArray() -> void | 0 | 29 | 0 | @r0_29(char) | +| InitArray() -> void | 0 | 30 | 0 | @mu0_30(char) | +| InitArray() -> void | 0 | 31 | 0 | @r0_31(int) | +| InitArray() -> void | 0 | 32 | 0 | @r0_32(glval:char) | +| InitArray() -> void | 0 | 33 | 0 | @r0_33(char) | +| InitArray() -> void | 0 | 34 | 0 | @mu0_34(char) | +| InitArray() -> void | 0 | 35 | 0 | @r0_35(glval:char[2]) | +| InitArray() -> void | 0 | 36 | 0 | @r0_36(int) | +| InitArray() -> void | 0 | 37 | 0 | @r0_37(glval:char) | +| InitArray() -> void | 0 | 38 | 0 | @r0_38(char) | +| InitArray() -> void | 0 | 39 | 0 | @mu0_39(char) | +| InitArray() -> void | 0 | 40 | 0 | @r0_40(int) | +| InitArray() -> void | 0 | 41 | 0 | @r0_41(glval:char) | +| InitArray() -> void | 0 | 42 | 0 | @r0_42(char) | +| InitArray() -> void | 0 | 43 | 0 | @mu0_43(char) | +| InitArray() -> void | 0 | 44 | 0 | @r0_44(glval:char[3]) | +| InitArray() -> void | 0 | 45 | 0 | @r0_45(int) | +| InitArray() -> void | 0 | 46 | 0 | @r0_46(glval:char) | +| InitArray() -> void | 0 | 47 | 0 | @r0_47(char) | +| InitArray() -> void | 0 | 48 | 0 | @mu0_48(char) | +| InitArray() -> void | 0 | 49 | 0 | @r0_49(int) | +| InitArray() -> void | 0 | 50 | 0 | @r0_50(glval:char) | +| InitArray() -> void | 0 | 51 | 0 | @r0_51(unknown[2]) | +| InitArray() -> void | 0 | 52 | 0 | @mu0_52(unknown[2]) | +| InitList(int, float) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| InitList(int, float) -> void | 0 | 2 | 0 | @r0_2(int) | +| InitList(int, float) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| InitList(int, float) -> void | 0 | 4 | 0 | @m0_4(int) | +| InitList(int, float) -> void | 0 | 5 | 0 | @r0_5(float) | +| InitList(int, float) -> void | 0 | 6 | 0 | @r0_6(glval:float) | +| InitList(int, float) -> void | 0 | 7 | 0 | @m0_7(float) | +| InitList(int, float) -> void | 0 | 8 | 0 | @r0_8(glval:Point) | +| InitList(int, float) -> void | 0 | 9 | 0 | @r0_9(glval:int) | +| InitList(int, float) -> void | 0 | 10 | 0 | @r0_10(glval:int) | +| InitList(int, float) -> void | 0 | 11 | 0 | @r0_11(int) | +| InitList(int, float) -> void | 0 | 12 | 0 | @m0_12(int) | +| InitList(int, float) -> void | 0 | 13 | 0 | @r0_13(glval:int) | +| InitList(int, float) -> void | 0 | 14 | 0 | @r0_14(glval:float) | +| InitList(int, float) -> void | 0 | 15 | 0 | @r0_15(float) | +| InitList(int, float) -> void | 0 | 16 | 0 | @r0_16(int) | +| InitList(int, float) -> void | 0 | 17 | 0 | @mu0_17(int) | +| InitList(int, float) -> void | 0 | 18 | 0 | @r0_18(glval:Point) | +| InitList(int, float) -> void | 0 | 19 | 0 | @r0_19(glval:int) | +| InitList(int, float) -> void | 0 | 20 | 0 | @r0_20(glval:int) | +| InitList(int, float) -> void | 0 | 21 | 0 | @r0_21(int) | +| InitList(int, float) -> void | 0 | 22 | 0 | @m0_22(int) | +| InitList(int, float) -> void | 0 | 23 | 0 | @r0_23(glval:int) | +| InitList(int, float) -> void | 0 | 24 | 0 | @r0_24(int) | +| InitList(int, float) -> void | 0 | 25 | 0 | @mu0_25(int) | +| InitList(int, float) -> void | 0 | 26 | 0 | @r0_26(glval:Point) | +| InitList(int, float) -> void | 0 | 27 | 0 | @r0_27(glval:int) | +| InitList(int, float) -> void | 0 | 28 | 0 | @r0_28(int) | +| InitList(int, float) -> void | 0 | 29 | 0 | @m0_29(int) | +| InitList(int, float) -> void | 0 | 30 | 0 | @r0_30(glval:int) | +| InitList(int, float) -> void | 0 | 31 | 0 | @r0_31(int) | +| InitList(int, float) -> void | 0 | 32 | 0 | @mu0_32(int) | +| InitList(int, float) -> void | 0 | 33 | 0 | @r0_33(glval:int) | +| InitList(int, float) -> void | 0 | 34 | 0 | @r0_34(int) | +| InitList(int, float) -> void | 0 | 35 | 0 | @m0_35(int) | +| InitList(int, float) -> void | 0 | 36 | 0 | @r0_36(glval:int) | +| InitList(int, float) -> void | 0 | 37 | 0 | @r0_37(int) | +| InitList(int, float) -> void | 0 | 38 | 0 | @m0_38(int) | +| InitReference(int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| InitReference(int) -> void | 0 | 2 | 0 | @r0_2(int) | +| InitReference(int) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| InitReference(int) -> void | 0 | 4 | 0 | @mu0_4(int) | +| InitReference(int) -> void | 0 | 5 | 0 | @r0_5(glval:int &) | +| InitReference(int) -> void | 0 | 6 | 0 | @r0_6(glval:int) | +| InitReference(int) -> void | 0 | 7 | 0 | @m0_7(int &) | +| InitReference(int) -> void | 0 | 8 | 0 | @r0_8(glval:int &) | +| InitReference(int) -> void | 0 | 9 | 0 | @r0_9(glval:int &) | +| InitReference(int) -> void | 0 | 10 | 0 | @r0_10(int &) | +| InitReference(int) -> void | 0 | 11 | 0 | @m0_11(int &) | +| InitReference(int) -> void | 0 | 12 | 0 | @r0_12(glval:String &) | +| InitReference(int) -> void | 0 | 13 | 0 | @r0_13(bool) | +| InitReference(int) -> void | 0 | 14 | 0 | @r0_14(String &) | +| InitReference(int) -> void | 0 | 15 | 0 | @r0_15(glval:String) | +| InitReference(int) -> void | 0 | 16 | 0 | @m0_16(String &) | +| IntegerCompare(int, int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| IntegerCompare(int, int) -> void | 0 | 2 | 0 | @r0_2(int) | +| IntegerCompare(int, int) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| IntegerCompare(int, int) -> void | 0 | 4 | 0 | @m0_4(int) | +| IntegerCompare(int, int) -> void | 0 | 5 | 0 | @r0_5(int) | +| IntegerCompare(int, int) -> void | 0 | 6 | 0 | @r0_6(glval:int) | +| IntegerCompare(int, int) -> void | 0 | 7 | 0 | @m0_7(int) | +| IntegerCompare(int, int) -> void | 0 | 8 | 0 | @r0_8(glval:bool) | +| IntegerCompare(int, int) -> void | 0 | 9 | 0 | @r0_9(bool) | +| IntegerCompare(int, int) -> void | 0 | 10 | 0 | @m0_10(bool) | +| IntegerCompare(int, int) -> void | 0 | 11 | 0 | @r0_11(glval:int) | +| IntegerCompare(int, int) -> void | 0 | 12 | 0 | @r0_12(int) | +| IntegerCompare(int, int) -> void | 0 | 13 | 0 | @r0_13(glval:int) | +| IntegerCompare(int, int) -> void | 0 | 14 | 0 | @r0_14(int) | +| IntegerCompare(int, int) -> void | 0 | 15 | 0 | @r0_15(bool) | +| IntegerCompare(int, int) -> void | 0 | 16 | 0 | @r0_16(glval:bool) | +| IntegerCompare(int, int) -> void | 0 | 17 | 0 | @m0_17(bool) | +| IntegerCompare(int, int) -> void | 0 | 18 | 0 | @r0_18(glval:int) | +| IntegerCompare(int, int) -> void | 0 | 19 | 0 | @r0_19(int) | +| IntegerCompare(int, int) -> void | 0 | 20 | 0 | @r0_20(glval:int) | +| IntegerCompare(int, int) -> void | 0 | 21 | 0 | @r0_21(int) | +| IntegerCompare(int, int) -> void | 0 | 22 | 0 | @r0_22(bool) | +| IntegerCompare(int, int) -> void | 0 | 23 | 0 | @r0_23(glval:bool) | +| IntegerCompare(int, int) -> void | 0 | 24 | 0 | @m0_24(bool) | +| IntegerCompare(int, int) -> void | 0 | 25 | 0 | @r0_25(glval:int) | +| IntegerCompare(int, int) -> void | 0 | 26 | 0 | @r0_26(int) | +| IntegerCompare(int, int) -> void | 0 | 27 | 0 | @r0_27(glval:int) | +| IntegerCompare(int, int) -> void | 0 | 28 | 0 | @r0_28(int) | +| IntegerCompare(int, int) -> void | 0 | 29 | 0 | @r0_29(bool) | +| IntegerCompare(int, int) -> void | 0 | 30 | 0 | @r0_30(glval:bool) | +| IntegerCompare(int, int) -> void | 0 | 31 | 0 | @m0_31(bool) | +| IntegerCompare(int, int) -> void | 0 | 32 | 0 | @r0_32(glval:int) | +| IntegerCompare(int, int) -> void | 0 | 33 | 0 | @r0_33(int) | +| IntegerCompare(int, int) -> void | 0 | 34 | 0 | @r0_34(glval:int) | +| IntegerCompare(int, int) -> void | 0 | 35 | 0 | @r0_35(int) | +| IntegerCompare(int, int) -> void | 0 | 36 | 0 | @r0_36(bool) | +| IntegerCompare(int, int) -> void | 0 | 37 | 0 | @r0_37(glval:bool) | +| IntegerCompare(int, int) -> void | 0 | 38 | 0 | @m0_38(bool) | +| IntegerCompare(int, int) -> void | 0 | 39 | 0 | @r0_39(glval:int) | +| IntegerCompare(int, int) -> void | 0 | 40 | 0 | @r0_40(int) | +| IntegerCompare(int, int) -> void | 0 | 41 | 0 | @r0_41(glval:int) | +| IntegerCompare(int, int) -> void | 0 | 42 | 0 | @r0_42(int) | +| IntegerCompare(int, int) -> void | 0 | 43 | 0 | @r0_43(bool) | +| IntegerCompare(int, int) -> void | 0 | 44 | 0 | @r0_44(glval:bool) | +| IntegerCompare(int, int) -> void | 0 | 45 | 0 | @m0_45(bool) | +| IntegerCompare(int, int) -> void | 0 | 46 | 0 | @r0_46(glval:int) | +| IntegerCompare(int, int) -> void | 0 | 47 | 0 | @r0_47(int) | +| IntegerCompare(int, int) -> void | 0 | 48 | 0 | @r0_48(glval:int) | +| IntegerCompare(int, int) -> void | 0 | 49 | 0 | @r0_49(int) | +| IntegerCompare(int, int) -> void | 0 | 50 | 0 | @r0_50(bool) | +| IntegerCompare(int, int) -> void | 0 | 51 | 0 | @r0_51(glval:bool) | +| IntegerCompare(int, int) -> void | 0 | 52 | 0 | @m0_52(bool) | +| IntegerCrement(int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| IntegerCrement(int) -> void | 0 | 2 | 0 | @r0_2(int) | +| IntegerCrement(int) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| IntegerCrement(int) -> void | 0 | 4 | 0 | @m0_4(int) | +| IntegerCrement(int) -> void | 0 | 5 | 0 | @r0_5(glval:int) | +| IntegerCrement(int) -> void | 0 | 6 | 0 | @r0_6(int) | +| IntegerCrement(int) -> void | 0 | 7 | 0 | @m0_7(int) | +| IntegerCrement(int) -> void | 0 | 8 | 0 | @r0_8(glval:int) | +| IntegerCrement(int) -> void | 0 | 9 | 0 | @r0_9(int) | +| IntegerCrement(int) -> void | 0 | 10 | 0 | @r0_10(int) | +| IntegerCrement(int) -> void | 0 | 11 | 0 | @r0_11(int) | +| IntegerCrement(int) -> void | 0 | 12 | 0 | @m0_12(int) | +| IntegerCrement(int) -> void | 0 | 13 | 0 | @r0_13(glval:int) | +| IntegerCrement(int) -> void | 0 | 14 | 0 | @m0_14(int) | +| IntegerCrement(int) -> void | 0 | 15 | 0 | @r0_15(glval:int) | +| IntegerCrement(int) -> void | 0 | 16 | 0 | @r0_16(int) | +| IntegerCrement(int) -> void | 0 | 17 | 0 | @r0_17(int) | +| IntegerCrement(int) -> void | 0 | 18 | 0 | @r0_18(int) | +| IntegerCrement(int) -> void | 0 | 19 | 0 | @m0_19(int) | +| IntegerCrement(int) -> void | 0 | 20 | 0 | @r0_20(glval:int) | +| IntegerCrement(int) -> void | 0 | 21 | 0 | @m0_21(int) | +| IntegerCrement(int) -> void | 0 | 22 | 0 | @r0_22(glval:int) | +| IntegerCrement(int) -> void | 0 | 23 | 0 | @r0_23(int) | +| IntegerCrement(int) -> void | 0 | 24 | 0 | @r0_24(int) | +| IntegerCrement(int) -> void | 0 | 25 | 0 | @r0_25(int) | +| IntegerCrement(int) -> void | 0 | 26 | 0 | @m0_26(int) | +| IntegerCrement(int) -> void | 0 | 27 | 0 | @r0_27(glval:int) | +| IntegerCrement(int) -> void | 0 | 28 | 0 | @m0_28(int) | +| IntegerCrement(int) -> void | 0 | 29 | 0 | @r0_29(glval:int) | +| IntegerCrement(int) -> void | 0 | 30 | 0 | @r0_30(int) | +| IntegerCrement(int) -> void | 0 | 31 | 0 | @r0_31(int) | +| IntegerCrement(int) -> void | 0 | 32 | 0 | @r0_32(int) | +| IntegerCrement(int) -> void | 0 | 33 | 0 | @m0_33(int) | +| IntegerCrement(int) -> void | 0 | 34 | 0 | @r0_34(glval:int) | +| IntegerCrement(int) -> void | 0 | 35 | 0 | @m0_35(int) | +| IntegerCrement_LValue(int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| IntegerCrement_LValue(int) -> void | 0 | 2 | 0 | @r0_2(int) | +| IntegerCrement_LValue(int) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| IntegerCrement_LValue(int) -> void | 0 | 4 | 0 | @mu0_4(int) | +| IntegerCrement_LValue(int) -> void | 0 | 5 | 0 | @r0_5(glval:int *) | +| IntegerCrement_LValue(int) -> void | 0 | 6 | 0 | @r0_6(int *) | +| IntegerCrement_LValue(int) -> void | 0 | 7 | 0 | @m0_7(int *) | +| IntegerCrement_LValue(int) -> void | 0 | 8 | 0 | @r0_8(glval:int) | +| IntegerCrement_LValue(int) -> void | 0 | 9 | 0 | @r0_9(int) | +| IntegerCrement_LValue(int) -> void | 0 | 10 | 0 | @r0_10(int) | +| IntegerCrement_LValue(int) -> void | 0 | 11 | 0 | @r0_11(int) | +| IntegerCrement_LValue(int) -> void | 0 | 12 | 0 | @mu0_12(int) | +| IntegerCrement_LValue(int) -> void | 0 | 13 | 0 | @r0_13(glval:int *) | +| IntegerCrement_LValue(int) -> void | 0 | 14 | 0 | @m0_14(int *) | +| IntegerCrement_LValue(int) -> void | 0 | 15 | 0 | @r0_15(glval:int) | +| IntegerCrement_LValue(int) -> void | 0 | 16 | 0 | @r0_16(int) | +| IntegerCrement_LValue(int) -> void | 0 | 17 | 0 | @r0_17(int) | +| IntegerCrement_LValue(int) -> void | 0 | 18 | 0 | @r0_18(int) | +| IntegerCrement_LValue(int) -> void | 0 | 19 | 0 | @mu0_19(int) | +| IntegerCrement_LValue(int) -> void | 0 | 20 | 0 | @r0_20(glval:int *) | +| IntegerCrement_LValue(int) -> void | 0 | 21 | 0 | @m0_21(int *) | +| IntegerOps(int, int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| IntegerOps(int, int) -> void | 0 | 2 | 0 | @r0_2(int) | +| IntegerOps(int, int) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| IntegerOps(int, int) -> void | 0 | 4 | 0 | @m0_4(int) | +| IntegerOps(int, int) -> void | 0 | 5 | 0 | @r0_5(int) | +| IntegerOps(int, int) -> void | 0 | 6 | 0 | @r0_6(glval:int) | +| IntegerOps(int, int) -> void | 0 | 7 | 0 | @m0_7(int) | +| IntegerOps(int, int) -> void | 0 | 8 | 0 | @r0_8(glval:int) | +| IntegerOps(int, int) -> void | 0 | 9 | 0 | @r0_9(int) | +| IntegerOps(int, int) -> void | 0 | 10 | 0 | @m0_10(int) | +| IntegerOps(int, int) -> void | 0 | 11 | 0 | @r0_11(glval:int) | +| IntegerOps(int, int) -> void | 0 | 12 | 0 | @r0_12(int) | +| IntegerOps(int, int) -> void | 0 | 13 | 0 | @r0_13(glval:int) | +| IntegerOps(int, int) -> void | 0 | 14 | 0 | @r0_14(int) | +| IntegerOps(int, int) -> void | 0 | 15 | 0 | @r0_15(int) | +| IntegerOps(int, int) -> void | 0 | 16 | 0 | @r0_16(glval:int) | +| IntegerOps(int, int) -> void | 0 | 17 | 0 | @m0_17(int) | +| IntegerOps(int, int) -> void | 0 | 18 | 0 | @r0_18(glval:int) | +| IntegerOps(int, int) -> void | 0 | 19 | 0 | @r0_19(int) | +| IntegerOps(int, int) -> void | 0 | 20 | 0 | @r0_20(glval:int) | +| IntegerOps(int, int) -> void | 0 | 21 | 0 | @r0_21(int) | +| IntegerOps(int, int) -> void | 0 | 22 | 0 | @r0_22(int) | +| IntegerOps(int, int) -> void | 0 | 23 | 0 | @r0_23(glval:int) | +| IntegerOps(int, int) -> void | 0 | 24 | 0 | @m0_24(int) | +| IntegerOps(int, int) -> void | 0 | 25 | 0 | @r0_25(glval:int) | +| IntegerOps(int, int) -> void | 0 | 26 | 0 | @r0_26(int) | +| IntegerOps(int, int) -> void | 0 | 27 | 0 | @r0_27(glval:int) | +| IntegerOps(int, int) -> void | 0 | 28 | 0 | @r0_28(int) | +| IntegerOps(int, int) -> void | 0 | 29 | 0 | @r0_29(int) | +| IntegerOps(int, int) -> void | 0 | 30 | 0 | @r0_30(glval:int) | +| IntegerOps(int, int) -> void | 0 | 31 | 0 | @m0_31(int) | +| IntegerOps(int, int) -> void | 0 | 32 | 0 | @r0_32(glval:int) | +| IntegerOps(int, int) -> void | 0 | 33 | 0 | @r0_33(int) | +| IntegerOps(int, int) -> void | 0 | 34 | 0 | @r0_34(glval:int) | +| IntegerOps(int, int) -> void | 0 | 35 | 0 | @r0_35(int) | +| IntegerOps(int, int) -> void | 0 | 36 | 0 | @r0_36(int) | +| IntegerOps(int, int) -> void | 0 | 37 | 0 | @r0_37(glval:int) | +| IntegerOps(int, int) -> void | 0 | 38 | 0 | @m0_38(int) | +| IntegerOps(int, int) -> void | 0 | 39 | 0 | @r0_39(glval:int) | +| IntegerOps(int, int) -> void | 0 | 40 | 0 | @r0_40(int) | +| IntegerOps(int, int) -> void | 0 | 41 | 0 | @r0_41(glval:int) | +| IntegerOps(int, int) -> void | 0 | 42 | 0 | @r0_42(int) | +| IntegerOps(int, int) -> void | 0 | 43 | 0 | @r0_43(int) | +| IntegerOps(int, int) -> void | 0 | 44 | 0 | @r0_44(glval:int) | +| IntegerOps(int, int) -> void | 0 | 45 | 0 | @m0_45(int) | +| IntegerOps(int, int) -> void | 0 | 46 | 0 | @r0_46(glval:int) | +| IntegerOps(int, int) -> void | 0 | 47 | 0 | @r0_47(int) | +| IntegerOps(int, int) -> void | 0 | 48 | 0 | @r0_48(glval:int) | +| IntegerOps(int, int) -> void | 0 | 49 | 0 | @r0_49(int) | +| IntegerOps(int, int) -> void | 0 | 50 | 0 | @r0_50(int) | +| IntegerOps(int, int) -> void | 0 | 51 | 0 | @r0_51(glval:int) | +| IntegerOps(int, int) -> void | 0 | 52 | 0 | @m0_52(int) | +| IntegerOps(int, int) -> void | 0 | 53 | 0 | @r0_53(glval:int) | +| IntegerOps(int, int) -> void | 0 | 54 | 0 | @r0_54(int) | +| IntegerOps(int, int) -> void | 0 | 55 | 0 | @r0_55(glval:int) | +| IntegerOps(int, int) -> void | 0 | 56 | 0 | @r0_56(int) | +| IntegerOps(int, int) -> void | 0 | 57 | 0 | @r0_57(int) | +| IntegerOps(int, int) -> void | 0 | 58 | 0 | @r0_58(glval:int) | +| IntegerOps(int, int) -> void | 0 | 59 | 0 | @m0_59(int) | +| IntegerOps(int, int) -> void | 0 | 60 | 0 | @r0_60(glval:int) | +| IntegerOps(int, int) -> void | 0 | 61 | 0 | @r0_61(int) | +| IntegerOps(int, int) -> void | 0 | 62 | 0 | @r0_62(glval:int) | +| IntegerOps(int, int) -> void | 0 | 63 | 0 | @r0_63(int) | +| IntegerOps(int, int) -> void | 0 | 64 | 0 | @r0_64(int) | +| IntegerOps(int, int) -> void | 0 | 65 | 0 | @r0_65(glval:int) | +| IntegerOps(int, int) -> void | 0 | 66 | 0 | @m0_66(int) | +| IntegerOps(int, int) -> void | 0 | 67 | 0 | @r0_67(glval:int) | +| IntegerOps(int, int) -> void | 0 | 68 | 0 | @r0_68(int) | +| IntegerOps(int, int) -> void | 0 | 69 | 0 | @r0_69(glval:int) | +| IntegerOps(int, int) -> void | 0 | 70 | 0 | @r0_70(int) | +| IntegerOps(int, int) -> void | 0 | 71 | 0 | @r0_71(int) | +| IntegerOps(int, int) -> void | 0 | 72 | 0 | @r0_72(glval:int) | +| IntegerOps(int, int) -> void | 0 | 73 | 0 | @m0_73(int) | +| IntegerOps(int, int) -> void | 0 | 74 | 0 | @r0_74(glval:int) | +| IntegerOps(int, int) -> void | 0 | 75 | 0 | @r0_75(int) | +| IntegerOps(int, int) -> void | 0 | 76 | 0 | @r0_76(glval:int) | +| IntegerOps(int, int) -> void | 0 | 77 | 0 | @r0_77(int) | +| IntegerOps(int, int) -> void | 0 | 78 | 0 | @r0_78(int) | +| IntegerOps(int, int) -> void | 0 | 79 | 0 | @r0_79(glval:int) | +| IntegerOps(int, int) -> void | 0 | 80 | 0 | @m0_80(int) | +| IntegerOps(int, int) -> void | 0 | 81 | 0 | @r0_81(glval:int) | +| IntegerOps(int, int) -> void | 0 | 82 | 0 | @r0_82(int) | +| IntegerOps(int, int) -> void | 0 | 83 | 0 | @r0_83(glval:int) | +| IntegerOps(int, int) -> void | 0 | 84 | 0 | @m0_84(int) | +| IntegerOps(int, int) -> void | 0 | 85 | 0 | @r0_85(glval:int) | +| IntegerOps(int, int) -> void | 0 | 86 | 0 | @r0_86(int) | +| IntegerOps(int, int) -> void | 0 | 87 | 0 | @r0_87(glval:int) | +| IntegerOps(int, int) -> void | 0 | 88 | 0 | @r0_88(int) | +| IntegerOps(int, int) -> void | 0 | 89 | 0 | @r0_89(int) | +| IntegerOps(int, int) -> void | 0 | 90 | 0 | @m0_90(int) | +| IntegerOps(int, int) -> void | 0 | 91 | 0 | @r0_91(glval:int) | +| IntegerOps(int, int) -> void | 0 | 92 | 0 | @r0_92(int) | +| IntegerOps(int, int) -> void | 0 | 93 | 0 | @r0_93(glval:int) | +| IntegerOps(int, int) -> void | 0 | 94 | 0 | @r0_94(int) | +| IntegerOps(int, int) -> void | 0 | 95 | 0 | @r0_95(int) | +| IntegerOps(int, int) -> void | 0 | 96 | 0 | @m0_96(int) | +| IntegerOps(int, int) -> void | 0 | 97 | 0 | @r0_97(glval:int) | +| IntegerOps(int, int) -> void | 0 | 98 | 0 | @r0_98(int) | +| IntegerOps(int, int) -> void | 0 | 99 | 0 | @r0_99(glval:int) | +| IntegerOps(int, int) -> void | 0 | 100 | 0 | @r0_100(int) | +| IntegerOps(int, int) -> void | 0 | 101 | 0 | @r0_101(int) | +| IntegerOps(int, int) -> void | 0 | 102 | 0 | @m0_102(int) | +| IntegerOps(int, int) -> void | 0 | 103 | 0 | @r0_103(glval:int) | +| IntegerOps(int, int) -> void | 0 | 104 | 0 | @r0_104(int) | +| IntegerOps(int, int) -> void | 0 | 105 | 0 | @r0_105(glval:int) | +| IntegerOps(int, int) -> void | 0 | 106 | 0 | @r0_106(int) | +| IntegerOps(int, int) -> void | 0 | 107 | 0 | @r0_107(int) | +| IntegerOps(int, int) -> void | 0 | 108 | 0 | @m0_108(int) | +| IntegerOps(int, int) -> void | 0 | 109 | 0 | @r0_109(glval:int) | +| IntegerOps(int, int) -> void | 0 | 110 | 0 | @r0_110(int) | +| IntegerOps(int, int) -> void | 0 | 111 | 0 | @r0_111(glval:int) | +| IntegerOps(int, int) -> void | 0 | 112 | 0 | @r0_112(int) | +| IntegerOps(int, int) -> void | 0 | 113 | 0 | @r0_113(int) | +| IntegerOps(int, int) -> void | 0 | 114 | 0 | @m0_114(int) | +| IntegerOps(int, int) -> void | 0 | 115 | 0 | @r0_115(glval:int) | +| IntegerOps(int, int) -> void | 0 | 116 | 0 | @r0_116(int) | +| IntegerOps(int, int) -> void | 0 | 117 | 0 | @r0_117(glval:int) | +| IntegerOps(int, int) -> void | 0 | 118 | 0 | @r0_118(int) | +| IntegerOps(int, int) -> void | 0 | 119 | 0 | @r0_119(int) | +| IntegerOps(int, int) -> void | 0 | 120 | 0 | @m0_120(int) | +| IntegerOps(int, int) -> void | 0 | 121 | 0 | @r0_121(glval:int) | +| IntegerOps(int, int) -> void | 0 | 122 | 0 | @r0_122(int) | +| IntegerOps(int, int) -> void | 0 | 123 | 0 | @r0_123(glval:int) | +| IntegerOps(int, int) -> void | 0 | 124 | 0 | @r0_124(int) | +| IntegerOps(int, int) -> void | 0 | 125 | 0 | @r0_125(int) | +| IntegerOps(int, int) -> void | 0 | 126 | 0 | @m0_126(int) | +| IntegerOps(int, int) -> void | 0 | 127 | 0 | @r0_127(glval:int) | +| IntegerOps(int, int) -> void | 0 | 128 | 0 | @r0_128(int) | +| IntegerOps(int, int) -> void | 0 | 129 | 0 | @r0_129(glval:int) | +| IntegerOps(int, int) -> void | 0 | 130 | 0 | @r0_130(int) | +| IntegerOps(int, int) -> void | 0 | 131 | 0 | @r0_131(int) | +| IntegerOps(int, int) -> void | 0 | 132 | 0 | @m0_132(int) | +| IntegerOps(int, int) -> void | 0 | 133 | 0 | @r0_133(glval:int) | +| IntegerOps(int, int) -> void | 0 | 134 | 0 | @r0_134(int) | +| IntegerOps(int, int) -> void | 0 | 135 | 0 | @r0_135(glval:int) | +| IntegerOps(int, int) -> void | 0 | 136 | 0 | @r0_136(int) | +| IntegerOps(int, int) -> void | 0 | 137 | 0 | @r0_137(int) | +| IntegerOps(int, int) -> void | 0 | 138 | 0 | @m0_138(int) | +| IntegerOps(int, int) -> void | 0 | 139 | 0 | @r0_139(glval:int) | +| IntegerOps(int, int) -> void | 0 | 140 | 0 | @r0_140(int) | +| IntegerOps(int, int) -> void | 0 | 141 | 0 | @r0_141(glval:int) | +| IntegerOps(int, int) -> void | 0 | 142 | 0 | @r0_142(int) | +| IntegerOps(int, int) -> void | 0 | 143 | 0 | @r0_143(int) | +| IntegerOps(int, int) -> void | 0 | 144 | 0 | @m0_144(int) | +| IntegerOps(int, int) -> void | 0 | 145 | 0 | @r0_145(glval:int) | +| IntegerOps(int, int) -> void | 0 | 146 | 0 | @r0_146(int) | +| IntegerOps(int, int) -> void | 0 | 147 | 0 | @r0_147(int) | +| IntegerOps(int, int) -> void | 0 | 148 | 0 | @r0_148(glval:int) | +| IntegerOps(int, int) -> void | 0 | 149 | 0 | @m0_149(int) | +| IntegerOps(int, int) -> void | 0 | 150 | 0 | @r0_150(glval:int) | +| IntegerOps(int, int) -> void | 0 | 151 | 0 | @r0_151(int) | +| IntegerOps(int, int) -> void | 0 | 152 | 0 | @r0_152(int) | +| IntegerOps(int, int) -> void | 0 | 153 | 0 | @r0_153(glval:int) | +| IntegerOps(int, int) -> void | 0 | 154 | 0 | @m0_154(int) | +| IntegerOps(int, int) -> void | 0 | 155 | 0 | @r0_155(glval:int) | +| IntegerOps(int, int) -> void | 0 | 156 | 0 | @r0_156(int) | +| IntegerOps(int, int) -> void | 0 | 157 | 0 | @r0_157(int) | +| IntegerOps(int, int) -> void | 0 | 158 | 0 | @r0_158(glval:int) | +| IntegerOps(int, int) -> void | 0 | 159 | 0 | @m0_159(int) | +| IntegerOps(int, int) -> void | 0 | 160 | 0 | @r0_160(glval:int) | +| IntegerOps(int, int) -> void | 0 | 161 | 0 | @r0_161(int) | +| IntegerOps(int, int) -> void | 0 | 162 | 0 | @r0_162(int) | +| IntegerOps(int, int) -> void | 0 | 163 | 0 | @r0_163(bool) | +| IntegerOps(int, int) -> void | 0 | 164 | 0 | @r0_164(bool) | +| IntegerOps(int, int) -> void | 0 | 165 | 0 | @r0_165(int) | +| IntegerOps(int, int) -> void | 0 | 166 | 0 | @r0_166(glval:int) | +| IntegerOps(int, int) -> void | 0 | 167 | 0 | @m0_167(int) | +| LogicalAnd(bool, bool) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| LogicalAnd(bool, bool) -> void | 0 | 2 | 0 | @r0_2(bool) | +| LogicalAnd(bool, bool) -> void | 0 | 3 | 0 | @r0_3(glval:bool) | +| LogicalAnd(bool, bool) -> void | 0 | 4 | 0 | @m0_4(bool) | +| LogicalAnd(bool, bool) -> void | 0 | 5 | 0 | @r0_5(bool) | +| LogicalAnd(bool, bool) -> void | 0 | 6 | 0 | @r0_6(glval:bool) | +| LogicalAnd(bool, bool) -> void | 0 | 7 | 0 | @m0_7(bool) | +| LogicalAnd(bool, bool) -> void | 0 | 8 | 0 | @r0_8(glval:int) | +| LogicalAnd(bool, bool) -> void | 0 | 9 | 0 | @r0_9(int) | +| LogicalAnd(bool, bool) -> void | 0 | 10 | 0 | @m0_10(int) | +| LogicalAnd(bool, bool) -> void | 0 | 11 | 0 | @r0_11(glval:bool) | +| LogicalAnd(bool, bool) -> void | 0 | 12 | 0 | @r0_12(bool) | +| LogicalAnd(bool, bool) -> void | 1 | 0 | 0 | @r1_0(glval:bool) | +| LogicalAnd(bool, bool) -> void | 1 | 1 | 0 | @r1_1(bool) | +| LogicalAnd(bool, bool) -> void | 2 | 0 | 0 | @r2_0(int) | +| LogicalAnd(bool, bool) -> void | 2 | 1 | 0 | @r2_1(glval:int) | +| LogicalAnd(bool, bool) -> void | 2 | 2 | 0 | @m2_2(int) | +| LogicalAnd(bool, bool) -> void | 3 | 0 | 0 | @r3_0(glval:bool) | +| LogicalAnd(bool, bool) -> void | 3 | 1 | 0 | @r3_1(bool) | +| LogicalAnd(bool, bool) -> void | 4 | 0 | 0 | @r4_0(glval:bool) | +| LogicalAnd(bool, bool) -> void | 4 | 1 | 0 | @r4_1(bool) | +| LogicalAnd(bool, bool) -> void | 5 | 0 | 0 | @r5_0(int) | +| LogicalAnd(bool, bool) -> void | 5 | 1 | 0 | @r5_1(glval:int) | +| LogicalAnd(bool, bool) -> void | 5 | 2 | 0 | @m5_2(int) | +| LogicalAnd(bool, bool) -> void | 6 | 0 | 0 | @r6_0(int) | +| LogicalAnd(bool, bool) -> void | 6 | 1 | 0 | @r6_1(glval:int) | +| LogicalAnd(bool, bool) -> void | 6 | 2 | 0 | @m6_2(int) | +| LogicalNot(bool, bool) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| LogicalNot(bool, bool) -> void | 0 | 2 | 0 | @r0_2(bool) | +| LogicalNot(bool, bool) -> void | 0 | 3 | 0 | @r0_3(glval:bool) | +| LogicalNot(bool, bool) -> void | 0 | 4 | 0 | @m0_4(bool) | +| LogicalNot(bool, bool) -> void | 0 | 5 | 0 | @r0_5(bool) | +| LogicalNot(bool, bool) -> void | 0 | 6 | 0 | @r0_6(glval:bool) | +| LogicalNot(bool, bool) -> void | 0 | 7 | 0 | @m0_7(bool) | +| LogicalNot(bool, bool) -> void | 0 | 8 | 0 | @r0_8(glval:int) | +| LogicalNot(bool, bool) -> void | 0 | 9 | 0 | @r0_9(int) | +| LogicalNot(bool, bool) -> void | 0 | 10 | 0 | @m0_10(int) | +| LogicalNot(bool, bool) -> void | 0 | 11 | 0 | @r0_11(glval:bool) | +| LogicalNot(bool, bool) -> void | 0 | 12 | 0 | @r0_12(bool) | +| LogicalNot(bool, bool) -> void | 1 | 0 | 0 | @r1_0(int) | +| LogicalNot(bool, bool) -> void | 1 | 1 | 0 | @r1_1(glval:int) | +| LogicalNot(bool, bool) -> void | 1 | 2 | 0 | @m1_2(int) | +| LogicalNot(bool, bool) -> void | 2 | 0 | 0 | @r2_0(glval:bool) | +| LogicalNot(bool, bool) -> void | 2 | 1 | 0 | @r2_1(bool) | +| LogicalNot(bool, bool) -> void | 3 | 0 | 0 | @r3_0(glval:bool) | +| LogicalNot(bool, bool) -> void | 3 | 1 | 0 | @r3_1(bool) | +| LogicalNot(bool, bool) -> void | 4 | 0 | 0 | @r4_0(int) | +| LogicalNot(bool, bool) -> void | 4 | 1 | 0 | @r4_1(glval:int) | +| LogicalNot(bool, bool) -> void | 4 | 2 | 0 | @m4_2(int) | +| LogicalNot(bool, bool) -> void | 5 | 0 | 0 | @r5_0(int) | +| LogicalNot(bool, bool) -> void | 5 | 1 | 0 | @r5_1(glval:int) | +| LogicalNot(bool, bool) -> void | 5 | 2 | 0 | @m5_2(int) | +| LogicalOr(bool, bool) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| LogicalOr(bool, bool) -> void | 0 | 2 | 0 | @r0_2(bool) | +| LogicalOr(bool, bool) -> void | 0 | 3 | 0 | @r0_3(glval:bool) | +| LogicalOr(bool, bool) -> void | 0 | 4 | 0 | @m0_4(bool) | +| LogicalOr(bool, bool) -> void | 0 | 5 | 0 | @r0_5(bool) | +| LogicalOr(bool, bool) -> void | 0 | 6 | 0 | @r0_6(glval:bool) | +| LogicalOr(bool, bool) -> void | 0 | 7 | 0 | @m0_7(bool) | +| LogicalOr(bool, bool) -> void | 0 | 8 | 0 | @r0_8(glval:int) | +| LogicalOr(bool, bool) -> void | 0 | 9 | 0 | @r0_9(int) | +| LogicalOr(bool, bool) -> void | 0 | 10 | 0 | @m0_10(int) | +| LogicalOr(bool, bool) -> void | 0 | 11 | 0 | @r0_11(glval:bool) | +| LogicalOr(bool, bool) -> void | 0 | 12 | 0 | @r0_12(bool) | +| LogicalOr(bool, bool) -> void | 1 | 0 | 0 | @r1_0(glval:bool) | +| LogicalOr(bool, bool) -> void | 1 | 1 | 0 | @r1_1(bool) | +| LogicalOr(bool, bool) -> void | 2 | 0 | 0 | @r2_0(int) | +| LogicalOr(bool, bool) -> void | 2 | 1 | 0 | @r2_1(glval:int) | +| LogicalOr(bool, bool) -> void | 2 | 2 | 0 | @m2_2(int) | +| LogicalOr(bool, bool) -> void | 3 | 0 | 0 | @r3_0(glval:bool) | +| LogicalOr(bool, bool) -> void | 3 | 1 | 0 | @r3_1(bool) | +| LogicalOr(bool, bool) -> void | 4 | 0 | 0 | @r4_0(glval:bool) | +| LogicalOr(bool, bool) -> void | 4 | 1 | 0 | @r4_1(bool) | +| LogicalOr(bool, bool) -> void | 5 | 0 | 0 | @r5_0(int) | +| LogicalOr(bool, bool) -> void | 5 | 1 | 0 | @r5_1(glval:int) | +| LogicalOr(bool, bool) -> void | 5 | 2 | 0 | @m5_2(int) | +| LogicalOr(bool, bool) -> void | 6 | 0 | 0 | @r6_0(int) | +| LogicalOr(bool, bool) -> void | 6 | 1 | 0 | @r6_1(glval:int) | +| LogicalOr(bool, bool) -> void | 6 | 2 | 0 | @m6_2(int) | +| Middle::Middle() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Middle::Middle() -> void | 0 | 2 | 0 | @r0_2(glval:Middle) | +| Middle::Middle() -> void | 0 | 3 | 0 | @r0_3(glval:Base) | +| Middle::Middle() -> void | 0 | 4 | 0 | @r0_4(bool) | +| Middle::Middle() -> void | 0 | 6 | 0 | @r0_6(glval:String) | +| Middle::Middle() -> void | 0 | 7 | 0 | @r0_7(bool) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 1 | 0 | @mu0_1(unknown) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 2 | 0 | @r0_2(glval:Middle) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 3 | 0 | @r0_3(Middle &) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 4 | 0 | @r0_4(glval:Middle &) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 5 | 0 | @m0_5(Middle &) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 6 | 0 | @r0_6(Middle *) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 7 | 0 | @r0_7(Base *) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 8 | 0 | @r0_8(bool) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 9 | 0 | @r0_9(glval:Middle &) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 10 | 0 | @r0_10(Middle &) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 11 | 0 | @r0_11(Base *) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 12 | 0 | @r0_12(Base &) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 13 | 0 | @r0_13(Middle *) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 14 | 0 | @r0_14(glval:String) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 15 | 0 | @r0_15(bool) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 16 | 0 | @r0_16(glval:Middle &) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 17 | 0 | @r0_17(Middle &) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 18 | 0 | @r0_18(glval:String) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 19 | 0 | @r0_19(String &) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 20 | 0 | @r0_20(glval:Middle &) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 21 | 0 | @r0_21(Middle *) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 22 | 0 | @m0_22(Middle &) | +| Middle::operator=(const Middle &) -> Middle & | 0 | 23 | 0 | @r0_23(glval:Middle &) | +| Middle::~Middle() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Middle::~Middle() -> void | 0 | 2 | 0 | @r0_2(glval:Middle) | +| Middle::~Middle() -> void | 0 | 4 | 0 | @r0_4(glval:String) | +| Middle::~Middle() -> void | 0 | 5 | 0 | @r0_5(bool) | +| Middle::~Middle() -> void | 0 | 7 | 0 | @r0_7(glval:Base) | +| Middle::~Middle() -> void | 0 | 8 | 0 | @r0_8(bool) | +| MiddleVB1::MiddleVB1() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| MiddleVB1::MiddleVB1() -> void | 0 | 2 | 0 | @r0_2(glval:MiddleVB1) | +| MiddleVB1::MiddleVB1() -> void | 0 | 3 | 0 | @r0_3(glval:Base) | +| MiddleVB1::MiddleVB1() -> void | 0 | 4 | 0 | @r0_4(bool) | +| MiddleVB1::MiddleVB1() -> void | 0 | 6 | 0 | @r0_6(glval:String) | +| MiddleVB1::MiddleVB1() -> void | 0 | 7 | 0 | @r0_7(bool) | +| MiddleVB1::~MiddleVB1() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| MiddleVB1::~MiddleVB1() -> void | 0 | 2 | 0 | @r0_2(glval:MiddleVB1) | +| MiddleVB1::~MiddleVB1() -> void | 0 | 4 | 0 | @r0_4(glval:String) | +| MiddleVB1::~MiddleVB1() -> void | 0 | 5 | 0 | @r0_5(bool) | +| MiddleVB1::~MiddleVB1() -> void | 0 | 7 | 0 | @r0_7(glval:Base) | +| MiddleVB1::~MiddleVB1() -> void | 0 | 8 | 0 | @r0_8(bool) | +| MiddleVB2::MiddleVB2() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| MiddleVB2::MiddleVB2() -> void | 0 | 2 | 0 | @r0_2(glval:MiddleVB2) | +| MiddleVB2::MiddleVB2() -> void | 0 | 3 | 0 | @r0_3(glval:Base) | +| MiddleVB2::MiddleVB2() -> void | 0 | 4 | 0 | @r0_4(bool) | +| MiddleVB2::MiddleVB2() -> void | 0 | 6 | 0 | @r0_6(glval:String) | +| MiddleVB2::MiddleVB2() -> void | 0 | 7 | 0 | @r0_7(bool) | +| MiddleVB2::~MiddleVB2() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| MiddleVB2::~MiddleVB2() -> void | 0 | 2 | 0 | @r0_2(glval:MiddleVB2) | +| MiddleVB2::~MiddleVB2() -> void | 0 | 4 | 0 | @r0_4(glval:String) | +| MiddleVB2::~MiddleVB2() -> void | 0 | 5 | 0 | @r0_5(bool) | +| MiddleVB2::~MiddleVB2() -> void | 0 | 7 | 0 | @r0_7(glval:Base) | +| MiddleVB2::~MiddleVB2() -> void | 0 | 8 | 0 | @r0_8(bool) | +| NestedInitList(int, float) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| NestedInitList(int, float) -> void | 0 | 2 | 0 | @r0_2(int) | +| NestedInitList(int, float) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| NestedInitList(int, float) -> void | 0 | 4 | 0 | @m0_4(int) | +| NestedInitList(int, float) -> void | 0 | 5 | 0 | @r0_5(float) | +| NestedInitList(int, float) -> void | 0 | 6 | 0 | @r0_6(glval:float) | +| NestedInitList(int, float) -> void | 0 | 7 | 0 | @m0_7(float) | +| NestedInitList(int, float) -> void | 0 | 8 | 0 | @r0_8(glval:Rect) | +| NestedInitList(int, float) -> void | 0 | 9 | 0 | @r0_9(glval:Point) | +| NestedInitList(int, float) -> void | 0 | 10 | 0 | @r0_10(Point) | +| NestedInitList(int, float) -> void | 0 | 11 | 0 | @m0_11(Point) | +| NestedInitList(int, float) -> void | 0 | 12 | 0 | @r0_12(glval:Point) | +| NestedInitList(int, float) -> void | 0 | 13 | 0 | @r0_13(Point) | +| NestedInitList(int, float) -> void | 0 | 14 | 0 | @mu0_14(Point) | +| NestedInitList(int, float) -> void | 0 | 15 | 0 | @r0_15(glval:Rect) | +| NestedInitList(int, float) -> void | 0 | 16 | 0 | @r0_16(glval:Point) | +| NestedInitList(int, float) -> void | 0 | 17 | 0 | @r0_17(glval:int) | +| NestedInitList(int, float) -> void | 0 | 18 | 0 | @r0_18(glval:int) | +| NestedInitList(int, float) -> void | 0 | 19 | 0 | @r0_19(int) | +| NestedInitList(int, float) -> void | 0 | 20 | 0 | @m0_20(int) | +| NestedInitList(int, float) -> void | 0 | 21 | 0 | @r0_21(glval:int) | +| NestedInitList(int, float) -> void | 0 | 22 | 0 | @r0_22(glval:float) | +| NestedInitList(int, float) -> void | 0 | 23 | 0 | @r0_23(float) | +| NestedInitList(int, float) -> void | 0 | 24 | 0 | @r0_24(int) | +| NestedInitList(int, float) -> void | 0 | 25 | 0 | @mu0_25(int) | +| NestedInitList(int, float) -> void | 0 | 26 | 0 | @r0_26(glval:Point) | +| NestedInitList(int, float) -> void | 0 | 27 | 0 | @r0_27(Point) | +| NestedInitList(int, float) -> void | 0 | 28 | 0 | @mu0_28(Point) | +| NestedInitList(int, float) -> void | 0 | 29 | 0 | @r0_29(glval:Rect) | +| NestedInitList(int, float) -> void | 0 | 30 | 0 | @r0_30(glval:Point) | +| NestedInitList(int, float) -> void | 0 | 31 | 0 | @r0_31(glval:int) | +| NestedInitList(int, float) -> void | 0 | 32 | 0 | @r0_32(glval:int) | +| NestedInitList(int, float) -> void | 0 | 33 | 0 | @r0_33(int) | +| NestedInitList(int, float) -> void | 0 | 34 | 0 | @m0_34(int) | +| NestedInitList(int, float) -> void | 0 | 35 | 0 | @r0_35(glval:int) | +| NestedInitList(int, float) -> void | 0 | 36 | 0 | @r0_36(glval:float) | +| NestedInitList(int, float) -> void | 0 | 37 | 0 | @r0_37(float) | +| NestedInitList(int, float) -> void | 0 | 38 | 0 | @r0_38(int) | +| NestedInitList(int, float) -> void | 0 | 39 | 0 | @mu0_39(int) | +| NestedInitList(int, float) -> void | 0 | 40 | 0 | @r0_40(glval:Point) | +| NestedInitList(int, float) -> void | 0 | 41 | 0 | @r0_41(glval:int) | +| NestedInitList(int, float) -> void | 0 | 42 | 0 | @r0_42(glval:int) | +| NestedInitList(int, float) -> void | 0 | 43 | 0 | @r0_43(int) | +| NestedInitList(int, float) -> void | 0 | 44 | 0 | @mu0_44(int) | +| NestedInitList(int, float) -> void | 0 | 45 | 0 | @r0_45(glval:int) | +| NestedInitList(int, float) -> void | 0 | 46 | 0 | @r0_46(glval:float) | +| NestedInitList(int, float) -> void | 0 | 47 | 0 | @r0_47(float) | +| NestedInitList(int, float) -> void | 0 | 48 | 0 | @r0_48(int) | +| NestedInitList(int, float) -> void | 0 | 49 | 0 | @mu0_49(int) | +| NestedInitList(int, float) -> void | 0 | 50 | 0 | @r0_50(glval:Rect) | +| NestedInitList(int, float) -> void | 0 | 51 | 0 | @r0_51(glval:Point) | +| NestedInitList(int, float) -> void | 0 | 52 | 0 | @r0_52(glval:int) | +| NestedInitList(int, float) -> void | 0 | 53 | 0 | @r0_53(glval:int) | +| NestedInitList(int, float) -> void | 0 | 54 | 0 | @r0_54(int) | +| NestedInitList(int, float) -> void | 0 | 55 | 0 | @m0_55(int) | +| NestedInitList(int, float) -> void | 0 | 56 | 0 | @r0_56(glval:int) | +| NestedInitList(int, float) -> void | 0 | 57 | 0 | @r0_57(int) | +| NestedInitList(int, float) -> void | 0 | 58 | 0 | @mu0_58(int) | +| NestedInitList(int, float) -> void | 0 | 59 | 0 | @r0_59(glval:Point) | +| NestedInitList(int, float) -> void | 0 | 60 | 0 | @r0_60(glval:int) | +| NestedInitList(int, float) -> void | 0 | 61 | 0 | @r0_61(glval:int) | +| NestedInitList(int, float) -> void | 0 | 62 | 0 | @r0_62(int) | +| NestedInitList(int, float) -> void | 0 | 63 | 0 | @mu0_63(int) | +| NestedInitList(int, float) -> void | 0 | 64 | 0 | @r0_64(glval:int) | +| NestedInitList(int, float) -> void | 0 | 65 | 0 | @r0_65(int) | +| NestedInitList(int, float) -> void | 0 | 66 | 0 | @mu0_66(int) | +| Nullptr() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Nullptr() -> void | 0 | 2 | 0 | @r0_2(glval:int *) | +| Nullptr() -> void | 0 | 3 | 0 | @r0_3(int *) | +| Nullptr() -> void | 0 | 4 | 0 | @m0_4(int *) | +| Nullptr() -> void | 0 | 5 | 0 | @r0_5(glval:int *) | +| Nullptr() -> void | 0 | 6 | 0 | @r0_6(int *) | +| Nullptr() -> void | 0 | 7 | 0 | @m0_7(int *) | +| Nullptr() -> void | 0 | 8 | 0 | @r0_8(int *) | +| Nullptr() -> void | 0 | 9 | 0 | @r0_9(glval:int *) | +| Nullptr() -> void | 0 | 10 | 0 | @m0_10(int *) | +| Nullptr() -> void | 0 | 11 | 0 | @r0_11(int *) | +| Nullptr() -> void | 0 | 12 | 0 | @r0_12(glval:int *) | +| Nullptr() -> void | 0 | 13 | 0 | @m0_13(int *) | +| Outer::Func(void *, char) -> long | 0 | 1 | 0 | @mu0_1(unknown) | +| Outer::Func(void *, char) -> long | 0 | 2 | 0 | @r0_2(void *) | +| Outer::Func(void *, char) -> long | 0 | 3 | 0 | @r0_3(glval:void *) | +| Outer::Func(void *, char) -> long | 0 | 4 | 0 | @m0_4(void *) | +| Outer::Func(void *, char) -> long | 0 | 5 | 0 | @r0_5(char) | +| Outer::Func(void *, char) -> long | 0 | 6 | 0 | @r0_6(glval:char) | +| Outer::Func(void *, char) -> long | 0 | 7 | 0 | @m0_7(char) | +| Outer::Func(void *, char) -> long | 0 | 8 | 0 | @r0_8(glval:long) | +| Outer::Func(void *, char) -> long | 0 | 9 | 0 | @r0_9(long) | +| Outer::Func(void *, char) -> long | 0 | 10 | 0 | @m0_10(long) | +| Outer::Func(void *, char) -> long | 0 | 11 | 0 | @r0_11(glval:long) | +| Parameters(int, int) -> int | 0 | 1 | 0 | @mu0_1(unknown) | +| Parameters(int, int) -> int | 0 | 2 | 0 | @r0_2(int) | +| Parameters(int, int) -> int | 0 | 3 | 0 | @r0_3(glval:int) | +| Parameters(int, int) -> int | 0 | 4 | 0 | @m0_4(int) | +| Parameters(int, int) -> int | 0 | 5 | 0 | @r0_5(int) | +| Parameters(int, int) -> int | 0 | 6 | 0 | @r0_6(glval:int) | +| Parameters(int, int) -> int | 0 | 7 | 0 | @m0_7(int) | +| Parameters(int, int) -> int | 0 | 8 | 0 | @r0_8(glval:int) | +| Parameters(int, int) -> int | 0 | 9 | 0 | @r0_9(glval:int) | +| Parameters(int, int) -> int | 0 | 10 | 0 | @r0_10(int) | +| Parameters(int, int) -> int | 0 | 11 | 0 | @r0_11(glval:int) | +| Parameters(int, int) -> int | 0 | 12 | 0 | @r0_12(int) | +| Parameters(int, int) -> int | 0 | 13 | 0 | @r0_13(int) | +| Parameters(int, int) -> int | 0 | 14 | 0 | @m0_14(int) | +| Parameters(int, int) -> int | 0 | 15 | 0 | @r0_15(glval:int) | +| PointerCompare(int *, int *) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| PointerCompare(int *, int *) -> void | 0 | 2 | 0 | @r0_2(int *) | +| PointerCompare(int *, int *) -> void | 0 | 3 | 0 | @r0_3(glval:int *) | +| PointerCompare(int *, int *) -> void | 0 | 4 | 0 | @m0_4(int *) | +| PointerCompare(int *, int *) -> void | 0 | 5 | 0 | @r0_5(int *) | +| PointerCompare(int *, int *) -> void | 0 | 6 | 0 | @r0_6(glval:int *) | +| PointerCompare(int *, int *) -> void | 0 | 7 | 0 | @m0_7(int *) | +| PointerCompare(int *, int *) -> void | 0 | 8 | 0 | @r0_8(glval:bool) | +| PointerCompare(int *, int *) -> void | 0 | 9 | 0 | @r0_9(bool) | +| PointerCompare(int *, int *) -> void | 0 | 10 | 0 | @m0_10(bool) | +| PointerCompare(int *, int *) -> void | 0 | 11 | 0 | @r0_11(glval:int *) | +| PointerCompare(int *, int *) -> void | 0 | 12 | 0 | @r0_12(int *) | +| PointerCompare(int *, int *) -> void | 0 | 13 | 0 | @r0_13(glval:int *) | +| PointerCompare(int *, int *) -> void | 0 | 14 | 0 | @r0_14(int *) | +| PointerCompare(int *, int *) -> void | 0 | 15 | 0 | @r0_15(bool) | +| PointerCompare(int *, int *) -> void | 0 | 16 | 0 | @r0_16(glval:bool) | +| PointerCompare(int *, int *) -> void | 0 | 17 | 0 | @m0_17(bool) | +| PointerCompare(int *, int *) -> void | 0 | 18 | 0 | @r0_18(glval:int *) | +| PointerCompare(int *, int *) -> void | 0 | 19 | 0 | @r0_19(int *) | +| PointerCompare(int *, int *) -> void | 0 | 20 | 0 | @r0_20(glval:int *) | +| PointerCompare(int *, int *) -> void | 0 | 21 | 0 | @r0_21(int *) | +| PointerCompare(int *, int *) -> void | 0 | 22 | 0 | @r0_22(bool) | +| PointerCompare(int *, int *) -> void | 0 | 23 | 0 | @r0_23(glval:bool) | +| PointerCompare(int *, int *) -> void | 0 | 24 | 0 | @m0_24(bool) | +| PointerCompare(int *, int *) -> void | 0 | 25 | 0 | @r0_25(glval:int *) | +| PointerCompare(int *, int *) -> void | 0 | 26 | 0 | @r0_26(int *) | +| PointerCompare(int *, int *) -> void | 0 | 27 | 0 | @r0_27(glval:int *) | +| PointerCompare(int *, int *) -> void | 0 | 28 | 0 | @r0_28(int *) | +| PointerCompare(int *, int *) -> void | 0 | 29 | 0 | @r0_29(bool) | +| PointerCompare(int *, int *) -> void | 0 | 30 | 0 | @r0_30(glval:bool) | +| PointerCompare(int *, int *) -> void | 0 | 31 | 0 | @m0_31(bool) | +| PointerCompare(int *, int *) -> void | 0 | 32 | 0 | @r0_32(glval:int *) | +| PointerCompare(int *, int *) -> void | 0 | 33 | 0 | @r0_33(int *) | +| PointerCompare(int *, int *) -> void | 0 | 34 | 0 | @r0_34(glval:int *) | +| PointerCompare(int *, int *) -> void | 0 | 35 | 0 | @r0_35(int *) | +| PointerCompare(int *, int *) -> void | 0 | 36 | 0 | @r0_36(bool) | +| PointerCompare(int *, int *) -> void | 0 | 37 | 0 | @r0_37(glval:bool) | +| PointerCompare(int *, int *) -> void | 0 | 38 | 0 | @m0_38(bool) | +| PointerCompare(int *, int *) -> void | 0 | 39 | 0 | @r0_39(glval:int *) | +| PointerCompare(int *, int *) -> void | 0 | 40 | 0 | @r0_40(int *) | +| PointerCompare(int *, int *) -> void | 0 | 41 | 0 | @r0_41(glval:int *) | +| PointerCompare(int *, int *) -> void | 0 | 42 | 0 | @r0_42(int *) | +| PointerCompare(int *, int *) -> void | 0 | 43 | 0 | @r0_43(bool) | +| PointerCompare(int *, int *) -> void | 0 | 44 | 0 | @r0_44(glval:bool) | +| PointerCompare(int *, int *) -> void | 0 | 45 | 0 | @m0_45(bool) | +| PointerCompare(int *, int *) -> void | 0 | 46 | 0 | @r0_46(glval:int *) | +| PointerCompare(int *, int *) -> void | 0 | 47 | 0 | @r0_47(int *) | +| PointerCompare(int *, int *) -> void | 0 | 48 | 0 | @r0_48(glval:int *) | +| PointerCompare(int *, int *) -> void | 0 | 49 | 0 | @r0_49(int *) | +| PointerCompare(int *, int *) -> void | 0 | 50 | 0 | @r0_50(bool) | +| PointerCompare(int *, int *) -> void | 0 | 51 | 0 | @r0_51(glval:bool) | +| PointerCompare(int *, int *) -> void | 0 | 52 | 0 | @m0_52(bool) | +| PointerCrement(int *) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| PointerCrement(int *) -> void | 0 | 2 | 0 | @r0_2(int *) | +| PointerCrement(int *) -> void | 0 | 3 | 0 | @r0_3(glval:int *) | +| PointerCrement(int *) -> void | 0 | 4 | 0 | @m0_4(int *) | +| PointerCrement(int *) -> void | 0 | 5 | 0 | @r0_5(glval:int *) | +| PointerCrement(int *) -> void | 0 | 6 | 0 | @r0_6(int *) | +| PointerCrement(int *) -> void | 0 | 7 | 0 | @m0_7(int *) | +| PointerCrement(int *) -> void | 0 | 8 | 0 | @r0_8(glval:int *) | +| PointerCrement(int *) -> void | 0 | 9 | 0 | @r0_9(int *) | +| PointerCrement(int *) -> void | 0 | 10 | 0 | @r0_10(int) | +| PointerCrement(int *) -> void | 0 | 11 | 0 | @r0_11(int *) | +| PointerCrement(int *) -> void | 0 | 12 | 0 | @m0_12(int *) | +| PointerCrement(int *) -> void | 0 | 13 | 0 | @r0_13(glval:int *) | +| PointerCrement(int *) -> void | 0 | 14 | 0 | @m0_14(int *) | +| PointerCrement(int *) -> void | 0 | 15 | 0 | @r0_15(glval:int *) | +| PointerCrement(int *) -> void | 0 | 16 | 0 | @r0_16(int *) | +| PointerCrement(int *) -> void | 0 | 17 | 0 | @r0_17(int) | +| PointerCrement(int *) -> void | 0 | 18 | 0 | @r0_18(int *) | +| PointerCrement(int *) -> void | 0 | 19 | 0 | @m0_19(int *) | +| PointerCrement(int *) -> void | 0 | 20 | 0 | @r0_20(glval:int *) | +| PointerCrement(int *) -> void | 0 | 21 | 0 | @m0_21(int *) | +| PointerCrement(int *) -> void | 0 | 22 | 0 | @r0_22(glval:int *) | +| PointerCrement(int *) -> void | 0 | 23 | 0 | @r0_23(int *) | +| PointerCrement(int *) -> void | 0 | 24 | 0 | @r0_24(int) | +| PointerCrement(int *) -> void | 0 | 25 | 0 | @r0_25(int *) | +| PointerCrement(int *) -> void | 0 | 26 | 0 | @m0_26(int *) | +| PointerCrement(int *) -> void | 0 | 27 | 0 | @r0_27(glval:int *) | +| PointerCrement(int *) -> void | 0 | 28 | 0 | @m0_28(int *) | +| PointerCrement(int *) -> void | 0 | 29 | 0 | @r0_29(glval:int *) | +| PointerCrement(int *) -> void | 0 | 30 | 0 | @r0_30(int *) | +| PointerCrement(int *) -> void | 0 | 31 | 0 | @r0_31(int) | +| PointerCrement(int *) -> void | 0 | 32 | 0 | @r0_32(int *) | +| PointerCrement(int *) -> void | 0 | 33 | 0 | @m0_33(int *) | +| PointerCrement(int *) -> void | 0 | 34 | 0 | @r0_34(glval:int *) | +| PointerCrement(int *) -> void | 0 | 35 | 0 | @m0_35(int *) | +| PointerOps(int *, int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| PointerOps(int *, int) -> void | 0 | 2 | 0 | @r0_2(int *) | +| PointerOps(int *, int) -> void | 0 | 3 | 0 | @r0_3(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 4 | 0 | @m0_4(int *) | +| PointerOps(int *, int) -> void | 0 | 5 | 0 | @r0_5(int) | +| PointerOps(int *, int) -> void | 0 | 6 | 0 | @r0_6(glval:int) | +| PointerOps(int *, int) -> void | 0 | 7 | 0 | @m0_7(int) | +| PointerOps(int *, int) -> void | 0 | 8 | 0 | @r0_8(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 9 | 0 | @r0_9(int *) | +| PointerOps(int *, int) -> void | 0 | 10 | 0 | @m0_10(int *) | +| PointerOps(int *, int) -> void | 0 | 11 | 0 | @r0_11(glval:bool) | +| PointerOps(int *, int) -> void | 0 | 12 | 0 | @r0_12(bool) | +| PointerOps(int *, int) -> void | 0 | 13 | 0 | @m0_13(bool) | +| PointerOps(int *, int) -> void | 0 | 14 | 0 | @r0_14(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 15 | 0 | @r0_15(int *) | +| PointerOps(int *, int) -> void | 0 | 16 | 0 | @r0_16(glval:int) | +| PointerOps(int *, int) -> void | 0 | 17 | 0 | @r0_17(int) | +| PointerOps(int *, int) -> void | 0 | 18 | 0 | @r0_18(int *) | +| PointerOps(int *, int) -> void | 0 | 19 | 0 | @r0_19(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 20 | 0 | @m0_20(int *) | +| PointerOps(int *, int) -> void | 0 | 21 | 0 | @r0_21(glval:int) | +| PointerOps(int *, int) -> void | 0 | 22 | 0 | @r0_22(int) | +| PointerOps(int *, int) -> void | 0 | 23 | 0 | @r0_23(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 24 | 0 | @r0_24(int *) | +| PointerOps(int *, int) -> void | 0 | 25 | 0 | @r0_25(int *) | +| PointerOps(int *, int) -> void | 0 | 26 | 0 | @r0_26(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 27 | 0 | @m0_27(int *) | +| PointerOps(int *, int) -> void | 0 | 28 | 0 | @r0_28(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 29 | 0 | @r0_29(int *) | +| PointerOps(int *, int) -> void | 0 | 30 | 0 | @r0_30(glval:int) | +| PointerOps(int *, int) -> void | 0 | 31 | 0 | @r0_31(int) | +| PointerOps(int *, int) -> void | 0 | 32 | 0 | @r0_32(int *) | +| PointerOps(int *, int) -> void | 0 | 33 | 0 | @r0_33(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 34 | 0 | @m0_34(int *) | +| PointerOps(int *, int) -> void | 0 | 35 | 0 | @r0_35(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 36 | 0 | @r0_36(int *) | +| PointerOps(int *, int) -> void | 0 | 37 | 0 | @r0_37(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 38 | 0 | @r0_38(int *) | +| PointerOps(int *, int) -> void | 0 | 39 | 0 | @r0_39(long) | +| PointerOps(int *, int) -> void | 0 | 40 | 0 | @r0_40(int) | +| PointerOps(int *, int) -> void | 0 | 41 | 0 | @r0_41(glval:int) | +| PointerOps(int *, int) -> void | 0 | 42 | 0 | @m0_42(int) | +| PointerOps(int *, int) -> void | 0 | 43 | 0 | @r0_43(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 44 | 0 | @r0_44(int *) | +| PointerOps(int *, int) -> void | 0 | 45 | 0 | @r0_45(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 46 | 0 | @m0_46(int *) | +| PointerOps(int *, int) -> void | 0 | 47 | 0 | @r0_47(glval:int) | +| PointerOps(int *, int) -> void | 0 | 48 | 0 | @r0_48(int) | +| PointerOps(int *, int) -> void | 0 | 49 | 0 | @r0_49(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 50 | 0 | @r0_50(int *) | +| PointerOps(int *, int) -> void | 0 | 51 | 0 | @r0_51(int *) | +| PointerOps(int *, int) -> void | 0 | 52 | 0 | @m0_52(int *) | +| PointerOps(int *, int) -> void | 0 | 53 | 0 | @r0_53(glval:int) | +| PointerOps(int *, int) -> void | 0 | 54 | 0 | @r0_54(int) | +| PointerOps(int *, int) -> void | 0 | 55 | 0 | @r0_55(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 56 | 0 | @r0_56(int *) | +| PointerOps(int *, int) -> void | 0 | 57 | 0 | @r0_57(int *) | +| PointerOps(int *, int) -> void | 0 | 58 | 0 | @m0_58(int *) | +| PointerOps(int *, int) -> void | 0 | 59 | 0 | @r0_59(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 60 | 0 | @r0_60(int *) | +| PointerOps(int *, int) -> void | 0 | 61 | 0 | @r0_61(int *) | +| PointerOps(int *, int) -> void | 0 | 62 | 0 | @r0_62(bool) | +| PointerOps(int *, int) -> void | 0 | 63 | 0 | @r0_63(glval:bool) | +| PointerOps(int *, int) -> void | 0 | 64 | 0 | @m0_64(bool) | +| PointerOps(int *, int) -> void | 0 | 65 | 0 | @r0_65(glval:int *) | +| PointerOps(int *, int) -> void | 0 | 66 | 0 | @r0_66(int *) | +| PointerOps(int *, int) -> void | 0 | 67 | 0 | @r0_67(int *) | +| PointerOps(int *, int) -> void | 0 | 68 | 0 | @r0_68(bool) | +| PointerOps(int *, int) -> void | 0 | 69 | 0 | @r0_69(bool) | +| PointerOps(int *, int) -> void | 0 | 70 | 0 | @r0_70(glval:bool) | +| PointerOps(int *, int) -> void | 0 | 71 | 0 | @m0_71(bool) | +| PolymorphicBase::PolymorphicBase() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| PolymorphicBase::PolymorphicBase() -> void | 0 | 2 | 0 | @r0_2(glval:PolymorphicBase) | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 2 | 0 | @r0_2(glval:PolymorphicDerived) | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 3 | 0 | @r0_3(glval:PolymorphicBase) | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 4 | 0 | @r0_4(bool) | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 2 | 0 | @r0_2(glval:PolymorphicDerived) | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 4 | 0 | @r0_4(glval:PolymorphicBase) | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 5 | 0 | @r0_5(bool) | +| ReturnStruct(Point) -> Point | 0 | 1 | 0 | @mu0_1(unknown) | +| ReturnStruct(Point) -> Point | 0 | 2 | 0 | @r0_2(Point) | +| ReturnStruct(Point) -> Point | 0 | 3 | 0 | @r0_3(glval:Point) | +| ReturnStruct(Point) -> Point | 0 | 4 | 0 | @m0_4(Point) | +| ReturnStruct(Point) -> Point | 0 | 5 | 0 | @r0_5(glval:Point) | +| ReturnStruct(Point) -> Point | 0 | 6 | 0 | @r0_6(glval:Point) | +| ReturnStruct(Point) -> Point | 0 | 7 | 0 | @r0_7(Point) | +| ReturnStruct(Point) -> Point | 0 | 8 | 0 | @m0_8(Point) | +| ReturnStruct(Point) -> Point | 0 | 9 | 0 | @r0_9(glval:Point) | +| SetFuncPtr() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| SetFuncPtr() -> void | 0 | 2 | 0 | @r0_2(glval:..(*)(..)) | +| SetFuncPtr() -> void | 0 | 3 | 0 | @r0_3(glval:..(*)(..)) | +| SetFuncPtr() -> void | 0 | 4 | 0 | @m0_4(..(*)(..)) | +| SetFuncPtr() -> void | 0 | 5 | 0 | @r0_5(glval:..()(..)) | +| SetFuncPtr() -> void | 0 | 6 | 0 | @r0_6(glval:..(*)(..)) | +| SetFuncPtr() -> void | 0 | 7 | 0 | @m0_7(..(*)(..)) | +| SetFuncPtr() -> void | 0 | 8 | 0 | @r0_8(glval:..(*)(..)) | +| SetFuncPtr() -> void | 0 | 9 | 0 | @r0_9(glval:..(*)(..)) | +| SetFuncPtr() -> void | 0 | 10 | 0 | @m0_10(..(*)(..)) | +| SetFuncPtr() -> void | 0 | 11 | 0 | @r0_11(glval:..()(..)) | +| SetFuncPtr() -> void | 0 | 12 | 0 | @r0_12(glval:..(*)(..)) | +| SetFuncPtr() -> void | 0 | 13 | 0 | @m0_13(..(*)(..)) | +| String::String() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| String::String() -> void | 0 | 2 | 0 | @r0_2(glval:String) | +| String::String() -> void | 0 | 3 | 0 | @r0_3(bool) | +| String::String() -> void | 0 | 4 | 0 | @r0_4(glval:char[1]) | +| String::String() -> void | 0 | 5 | 0 | @r0_5(char *) | +| StringLiteral(int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| StringLiteral(int) -> void | 0 | 2 | 0 | @r0_2(int) | +| StringLiteral(int) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| StringLiteral(int) -> void | 0 | 4 | 0 | @m0_4(int) | +| StringLiteral(int) -> void | 0 | 5 | 0 | @r0_5(glval:char) | +| StringLiteral(int) -> void | 0 | 6 | 0 | @r0_6(glval:char[4]) | +| StringLiteral(int) -> void | 0 | 7 | 0 | @r0_7(char *) | +| StringLiteral(int) -> void | 0 | 8 | 0 | @r0_8(glval:int) | +| StringLiteral(int) -> void | 0 | 9 | 0 | @r0_9(int) | +| StringLiteral(int) -> void | 0 | 10 | 0 | @r0_10(char *) | +| StringLiteral(int) -> void | 0 | 11 | 0 | @r0_11(char) | +| StringLiteral(int) -> void | 0 | 12 | 0 | @m0_12(char) | +| StringLiteral(int) -> void | 0 | 13 | 0 | @r0_13(glval:wchar_t *) | +| StringLiteral(int) -> void | 0 | 14 | 0 | @r0_14(glval:wchar_t[4]) | +| StringLiteral(int) -> void | 0 | 15 | 0 | @r0_15(wchar_t *) | +| StringLiteral(int) -> void | 0 | 16 | 0 | @r0_16(wchar_t *) | +| StringLiteral(int) -> void | 0 | 17 | 0 | @m0_17(wchar_t *) | +| StringLiteral(int) -> void | 0 | 18 | 0 | @r0_18(glval:wchar_t) | +| StringLiteral(int) -> void | 0 | 19 | 0 | @r0_19(glval:wchar_t *) | +| StringLiteral(int) -> void | 0 | 20 | 0 | @r0_20(wchar_t *) | +| StringLiteral(int) -> void | 0 | 21 | 0 | @r0_21(glval:int) | +| StringLiteral(int) -> void | 0 | 22 | 0 | @r0_22(int) | +| StringLiteral(int) -> void | 0 | 23 | 0 | @r0_23(wchar_t *) | +| StringLiteral(int) -> void | 0 | 24 | 0 | @r0_24(wchar_t) | +| StringLiteral(int) -> void | 0 | 25 | 0 | @m0_25(wchar_t) | +| Switch(int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| Switch(int) -> void | 0 | 2 | 0 | @r0_2(int) | +| Switch(int) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| Switch(int) -> void | 0 | 4 | 0 | @m0_4(int) | +| Switch(int) -> void | 0 | 5 | 0 | @r0_5(glval:int) | +| Switch(int) -> void | 0 | 6 | 0 | @r0_6(int) | +| Switch(int) -> void | 0 | 7 | 0 | @m0_7(int) | +| Switch(int) -> void | 0 | 8 | 0 | @r0_8(glval:int) | +| Switch(int) -> void | 0 | 9 | 0 | @r0_9(int) | +| Switch(int) -> void | 1 | 0 | 0 | @r1_0(int) | +| Switch(int) -> void | 1 | 1 | 0 | @r1_1(glval:int) | +| Switch(int) -> void | 1 | 2 | 0 | @m1_2(int) | +| Switch(int) -> void | 2 | 1 | 0 | @r2_1(int) | +| Switch(int) -> void | 2 | 2 | 0 | @r2_2(glval:int) | +| Switch(int) -> void | 2 | 3 | 0 | @m2_3(int) | +| Switch(int) -> void | 4 | 1 | 0 | @r4_1(int) | +| Switch(int) -> void | 4 | 2 | 0 | @r4_2(glval:int) | +| Switch(int) -> void | 4 | 3 | 0 | @m4_3(int) | +| Switch(int) -> void | 5 | 1 | 0 | @r5_1(int) | +| Switch(int) -> void | 5 | 2 | 0 | @r5_2(glval:int) | +| Switch(int) -> void | 5 | 3 | 0 | @m5_3(int) | +| Switch(int) -> void | 6 | 1 | 0 | @r6_1(int) | +| Switch(int) -> void | 6 | 2 | 0 | @r6_2(glval:int) | +| Switch(int) -> void | 6 | 3 | 0 | @m6_3(int) | +| Switch(int) -> void | 7 | 1 | 0 | @r7_1(int) | +| Switch(int) -> void | 7 | 2 | 0 | @r7_2(glval:int) | +| Switch(int) -> void | 7 | 3 | 0 | @m7_3(int) | +| Switch(int) -> void | 8 | 0 | 0 | @r8_0(int) | +| Switch(int) -> void | 8 | 1 | 0 | @r8_1(glval:int) | +| Switch(int) -> void | 8 | 2 | 0 | @m8_2(int) | +| TakeReference() -> int & | 0 | 1 | 0 | @mu0_1(unknown) | +| TakeReference() -> int & | 0 | 2 | 0 | @r0_2(glval:int &) | +| TakeReference() -> int & | 0 | 3 | 0 | @r0_3(glval:int) | +| TakeReference() -> int & | 0 | 4 | 0 | @m0_4(int &) | +| TakeReference() -> int & | 0 | 5 | 0 | @r0_5(glval:int &) | +| TryCatch(bool) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| TryCatch(bool) -> void | 0 | 2 | 0 | @r0_2(bool) | +| TryCatch(bool) -> void | 0 | 3 | 0 | @r0_3(glval:bool) | +| TryCatch(bool) -> void | 0 | 4 | 0 | @m0_4(bool) | +| TryCatch(bool) -> void | 0 | 5 | 0 | @r0_5(glval:int) | +| TryCatch(bool) -> void | 0 | 6 | 0 | @r0_6(int) | +| TryCatch(bool) -> void | 0 | 7 | 0 | @m0_7(int) | +| TryCatch(bool) -> void | 0 | 8 | 0 | @r0_8(glval:bool) | +| TryCatch(bool) -> void | 0 | 9 | 0 | @r0_9(bool) | +| TryCatch(bool) -> void | 3 | 0 | 0 | @r3_0(glval:char *) | +| TryCatch(bool) -> void | 3 | 1 | 0 | @r3_1(glval:char[15]) | +| TryCatch(bool) -> void | 3 | 2 | 0 | @r3_2(char *) | +| TryCatch(bool) -> void | 3 | 3 | 0 | @m3_3(char *) | +| TryCatch(bool) -> void | 4 | 0 | 0 | @r4_0(glval:int) | +| TryCatch(bool) -> void | 4 | 1 | 0 | @r4_1(int) | +| TryCatch(bool) -> void | 4 | 2 | 0 | @r4_2(int) | +| TryCatch(bool) -> void | 4 | 3 | 0 | @r4_3(bool) | +| TryCatch(bool) -> void | 5 | 0 | 0 | @r5_0(glval:bool) | +| TryCatch(bool) -> void | 5 | 1 | 0 | @r5_1(bool) | +| TryCatch(bool) -> void | 6 | 0 | 0 | @r6_0(int) | +| TryCatch(bool) -> void | 6 | 1 | 0 | @r6_1(glval:int) | +| TryCatch(bool) -> void | 6 | 2 | 0 | @m6_2(int) | +| TryCatch(bool) -> void | 6 | 3 | 0 | @r6_3(glval:int) | +| TryCatch(bool) -> void | 6 | 4 | 0 | @r6_4(int) | +| TryCatch(bool) -> void | 6 | 5 | 0 | @r6_5(glval:int) | +| TryCatch(bool) -> void | 6 | 6 | 0 | @m6_6(int) | +| TryCatch(bool) -> void | 7 | 0 | 0 | @r7_0(glval:String) | +| TryCatch(bool) -> void | 7 | 1 | 0 | @r7_1(bool) | +| TryCatch(bool) -> void | 7 | 2 | 0 | @r7_2(glval:char[14]) | +| TryCatch(bool) -> void | 7 | 3 | 0 | @r7_3(char *) | +| TryCatch(bool) -> void | 8 | 0 | 0 | @r8_0(int) | +| TryCatch(bool) -> void | 8 | 1 | 0 | @r8_1(glval:int) | +| TryCatch(bool) -> void | 8 | 2 | 0 | @m8_2(int) | +| TryCatch(bool) -> void | 10 | 0 | 0 | @r10_0(char *) | +| TryCatch(bool) -> void | 10 | 1 | 0 | @r10_1(glval:char *) | +| TryCatch(bool) -> void | 10 | 2 | 0 | @m10_2(char *) | +| TryCatch(bool) -> void | 10 | 3 | 0 | @r10_3(glval:String) | +| TryCatch(bool) -> void | 10 | 4 | 0 | @r10_4(bool) | +| TryCatch(bool) -> void | 10 | 5 | 0 | @r10_5(glval:char *) | +| TryCatch(bool) -> void | 10 | 6 | 0 | @r10_6(char *) | +| TryCatch(bool) -> void | 12 | 0 | 0 | @r12_0(String &) | +| TryCatch(bool) -> void | 12 | 1 | 0 | @r12_1(glval:String &) | +| TryCatch(bool) -> void | 12 | 2 | 0 | @m12_2(String &) | +| UninitializedVariables() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| UninitializedVariables() -> void | 0 | 2 | 0 | @r0_2(glval:int) | +| UninitializedVariables() -> void | 0 | 3 | 0 | @r0_3(int) | +| UninitializedVariables() -> void | 0 | 4 | 0 | @m0_4(int) | +| UninitializedVariables() -> void | 0 | 5 | 0 | @r0_5(glval:int) | +| UninitializedVariables() -> void | 0 | 6 | 0 | @r0_6(glval:int) | +| UninitializedVariables() -> void | 0 | 7 | 0 | @r0_7(int) | +| UninitializedVariables() -> void | 0 | 8 | 0 | @m0_8(int) | +| UnionInit(int, float) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| UnionInit(int, float) -> void | 0 | 2 | 0 | @r0_2(int) | +| UnionInit(int, float) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| UnionInit(int, float) -> void | 0 | 4 | 0 | @m0_4(int) | +| UnionInit(int, float) -> void | 0 | 5 | 0 | @r0_5(float) | +| UnionInit(int, float) -> void | 0 | 6 | 0 | @r0_6(glval:float) | +| UnionInit(int, float) -> void | 0 | 7 | 0 | @m0_7(float) | +| UnionInit(int, float) -> void | 0 | 8 | 0 | @r0_8(glval:U) | +| UnionInit(int, float) -> void | 0 | 9 | 0 | @r0_9(glval:double) | +| UnionInit(int, float) -> void | 0 | 10 | 0 | @r0_10(glval:float) | +| UnionInit(int, float) -> void | 0 | 11 | 0 | @r0_11(float) | +| UnionInit(int, float) -> void | 0 | 12 | 0 | @r0_12(double) | +| UnionInit(int, float) -> void | 0 | 13 | 0 | @m0_13(double) | +| VarArgUsage(int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| VarArgUsage(int) -> void | 0 | 2 | 0 | @r0_2(int) | +| VarArgUsage(int) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| VarArgUsage(int) -> void | 0 | 4 | 0 | @mu0_4(int) | +| VarArgUsage(int) -> void | 0 | 5 | 0 | @r0_5(glval:__va_list_tag[1]) | +| VarArgUsage(int) -> void | 0 | 6 | 0 | @r0_6(__va_list_tag[1]) | +| VarArgUsage(int) -> void | 0 | 7 | 0 | @mu0_7(__va_list_tag[1]) | +| VarArgUsage(int) -> void | 0 | 8 | 0 | @r0_8(glval:__va_list_tag[1]) | +| VarArgUsage(int) -> void | 0 | 9 | 0 | @r0_9(__va_list_tag *) | +| VarArgUsage(int) -> void | 0 | 10 | 0 | @r0_10(glval:int) | +| VarArgUsage(int) -> void | 0 | 12 | 0 | @r0_12(glval:__va_list_tag[1]) | +| VarArgUsage(int) -> void | 0 | 13 | 0 | @r0_13(__va_list_tag[1]) | +| VarArgUsage(int) -> void | 0 | 14 | 0 | @mu0_14(__va_list_tag[1]) | +| VarArgUsage(int) -> void | 0 | 15 | 0 | @r0_15(glval:__va_list_tag[1]) | +| VarArgUsage(int) -> void | 0 | 16 | 0 | @r0_16(__va_list_tag *) | +| VarArgUsage(int) -> void | 0 | 17 | 0 | @r0_17(glval:__va_list_tag[1]) | +| VarArgUsage(int) -> void | 0 | 18 | 0 | @r0_18(__va_list_tag *) | +| VarArgUsage(int) -> void | 0 | 20 | 0 | @r0_20(glval:double) | +| VarArgUsage(int) -> void | 0 | 21 | 0 | @r0_21(glval:__va_list_tag[1]) | +| VarArgUsage(int) -> void | 0 | 22 | 0 | @r0_22(__va_list_tag *) | +| VarArgUsage(int) -> void | 0 | 23 | 0 | @r0_23(glval:double) | +| VarArgUsage(int) -> void | 0 | 24 | 0 | @r0_24(double) | +| VarArgUsage(int) -> void | 0 | 25 | 0 | @m0_25(double) | +| VarArgUsage(int) -> void | 0 | 26 | 0 | @r0_26(glval:float) | +| VarArgUsage(int) -> void | 0 | 27 | 0 | @r0_27(glval:__va_list_tag[1]) | +| VarArgUsage(int) -> void | 0 | 28 | 0 | @r0_28(__va_list_tag *) | +| VarArgUsage(int) -> void | 0 | 29 | 0 | @r0_29(glval:double) | +| VarArgUsage(int) -> void | 0 | 30 | 0 | @r0_30(double) | +| VarArgUsage(int) -> void | 0 | 31 | 0 | @r0_31(float) | +| VarArgUsage(int) -> void | 0 | 32 | 0 | @m0_32(float) | +| VarArgUsage(int) -> void | 0 | 33 | 0 | @r0_33(glval:__va_list_tag[1]) | +| VarArgUsage(int) -> void | 0 | 34 | 0 | @r0_34(__va_list_tag *) | +| VarArgUsage(int) -> void | 0 | 36 | 0 | @r0_36(glval:__va_list_tag[1]) | +| VarArgUsage(int) -> void | 0 | 37 | 0 | @r0_37(__va_list_tag *) | +| VarArgs() -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| VarArgs() -> void | 0 | 2 | 0 | @r0_2(bool) | +| VarArgs() -> void | 0 | 3 | 0 | @r0_3(glval:char[6]) | +| VarArgs() -> void | 0 | 4 | 0 | @r0_4(char *) | +| VarArgs() -> void | 0 | 5 | 0 | @r0_5(int) | +| VarArgs() -> void | 0 | 6 | 0 | @r0_6(glval:char[7]) | +| VarArgs() -> void | 0 | 7 | 0 | @r0_7(char *) | +| WhileStatements(int) -> void | 0 | 1 | 0 | @mu0_1(unknown) | +| WhileStatements(int) -> void | 0 | 2 | 0 | @r0_2(int) | +| WhileStatements(int) -> void | 0 | 3 | 0 | @r0_3(glval:int) | +| WhileStatements(int) -> void | 0 | 4 | 0 | @m0_4(int) | +| WhileStatements(int) -> void | 1 | 0 | 0 | @r1_0(int) | +| WhileStatements(int) -> void | 1 | 1 | 0 | @r1_1(glval:int) | +| WhileStatements(int) -> void | 1 | 2 | 0 | @r1_2(int) | +| WhileStatements(int) -> void | 1 | 3 | 0 | @r1_3(int) | +| WhileStatements(int) -> void | 1 | 4 | 0 | @m1_4(int) | +| WhileStatements(int) -> void | 3 | 0 | 0 | @m3_0(int) | +| WhileStatements(int) -> void | 3 | 1 | 0 | @r3_1(glval:int) | +| WhileStatements(int) -> void | 3 | 2 | 0 | @r3_2(int) | +| WhileStatements(int) -> void | 3 | 3 | 0 | @r3_3(int) | +| WhileStatements(int) -> void | 3 | 4 | 0 | @r3_4(bool) | +| min(int, int) -> int | 0 | 1 | 0 | @mu0_1(unknown) | +| min(int, int) -> int | 0 | 2 | 0 | @r0_2(int) | +| min(int, int) -> int | 0 | 3 | 0 | @r0_3(glval:int) | +| min(int, int) -> int | 0 | 4 | 0 | @m0_4(int) | +| min(int, int) -> int | 0 | 5 | 0 | @r0_5(int) | +| min(int, int) -> int | 0 | 6 | 0 | @r0_6(glval:int) | +| min(int, int) -> int | 0 | 7 | 0 | @m0_7(int) | +| min(int, int) -> int | 0 | 8 | 0 | @r0_8(glval:int) | +| min(int, int) -> int | 0 | 9 | 0 | @r0_9(glval:int) | +| min(int, int) -> int | 0 | 10 | 0 | @r0_10(int) | +| min(int, int) -> int | 0 | 11 | 0 | @r0_11(glval:int) | +| min(int, int) -> int | 0 | 12 | 0 | @r0_12(int) | +| min(int, int) -> int | 0 | 13 | 0 | @r0_13(bool) | +| min(int, int) -> int | 1 | 0 | 0 | @r1_0(glval:int) | +| min(int, int) -> int | 1 | 1 | 0 | @r1_1(int) | +| min(int, int) -> int | 1 | 2 | 0 | @r1_2(glval:int) | +| min(int, int) -> int | 1 | 3 | 0 | @m1_3(int) | +| min(int, int) -> int | 2 | 0 | 0 | @r2_0(glval:int) | +| min(int, int) -> int | 2 | 1 | 0 | @r2_1(int) | +| min(int, int) -> int | 2 | 2 | 0 | @r2_2(glval:int) | +| min(int, int) -> int | 2 | 3 | 0 | @m2_3(int) | +| min(int, int) -> int | 3 | 0 | 0 | @m3_0(int) | +| min(int, int) -> int | 3 | 1 | 0 | @r3_1(glval:int) | +| min(int, int) -> int | 3 | 2 | 0 | @r3_2(int) | +| min(int, int) -> int | 3 | 3 | 0 | @m3_3(int) | +| min(int, int) -> int | 3 | 4 | 0 | @r3_4(glval:int) | +printIRGraphSourceOperands +| AddressOf() -> int * | 0 | 4 | 0 | @r0_2 | +| AddressOf() -> int * | 0 | 4 | 1 | @r0_3 | +| AddressOf() -> int * | 0 | 6 | 0 | @r0_5 | +| AddressOf() -> int * | 0 | 6 | 5 | @m0_4 | +| AddressOf() -> int * | 0 | 7 | 0 | @mu* | +| ArrayAccess(int *, int) -> void | 0 | 4 | 0 | @r0_3 | +| ArrayAccess(int *, int) -> void | 0 | 4 | 1 | @r0_2 | +| ArrayAccess(int *, int) -> void | 0 | 7 | 0 | @r0_6 | +| ArrayAccess(int *, int) -> void | 0 | 7 | 1 | @r0_5 | +| ArrayAccess(int *, int) -> void | 0 | 10 | 0 | @r0_8 | +| ArrayAccess(int *, int) -> void | 0 | 10 | 1 | @r0_9 | +| ArrayAccess(int *, int) -> void | 0 | 12 | 0 | @r0_11 | +| ArrayAccess(int *, int) -> void | 0 | 12 | 1 | @m0_4 | +| ArrayAccess(int *, int) -> void | 0 | 14 | 0 | @r0_13 | +| ArrayAccess(int *, int) -> void | 0 | 14 | 1 | @m0_7 | +| ArrayAccess(int *, int) -> void | 0 | 15 | 3 | @r0_12 | +| ArrayAccess(int *, int) -> void | 0 | 15 | 4 | @r0_14 | +| ArrayAccess(int *, int) -> void | 0 | 16 | 0 | @r0_15 | +| ArrayAccess(int *, int) -> void | 0 | 16 | 1 | @mu0_1 | +| ArrayAccess(int *, int) -> void | 0 | 18 | 0 | @r0_17 | +| ArrayAccess(int *, int) -> void | 0 | 18 | 1 | @r0_16 | +| ArrayAccess(int *, int) -> void | 0 | 20 | 0 | @r0_19 | +| ArrayAccess(int *, int) -> void | 0 | 20 | 1 | @m0_4 | +| ArrayAccess(int *, int) -> void | 0 | 22 | 0 | @r0_21 | +| ArrayAccess(int *, int) -> void | 0 | 22 | 1 | @m0_7 | +| ArrayAccess(int *, int) -> void | 0 | 23 | 3 | @r0_20 | +| ArrayAccess(int *, int) -> void | 0 | 23 | 4 | @r0_22 | +| ArrayAccess(int *, int) -> void | 0 | 24 | 0 | @r0_23 | +| ArrayAccess(int *, int) -> void | 0 | 24 | 1 | @mu0_1 | +| ArrayAccess(int *, int) -> void | 0 | 26 | 0 | @r0_25 | +| ArrayAccess(int *, int) -> void | 0 | 26 | 1 | @r0_24 | +| ArrayAccess(int *, int) -> void | 0 | 28 | 0 | @r0_27 | +| ArrayAccess(int *, int) -> void | 0 | 28 | 1 | @m0_26 | +| ArrayAccess(int *, int) -> void | 0 | 30 | 0 | @r0_29 | +| ArrayAccess(int *, int) -> void | 0 | 30 | 1 | @m0_4 | +| ArrayAccess(int *, int) -> void | 0 | 32 | 0 | @r0_31 | +| ArrayAccess(int *, int) -> void | 0 | 32 | 1 | @m0_7 | +| ArrayAccess(int *, int) -> void | 0 | 33 | 3 | @r0_30 | +| ArrayAccess(int *, int) -> void | 0 | 33 | 4 | @r0_32 | +| ArrayAccess(int *, int) -> void | 0 | 34 | 0 | @r0_33 | +| ArrayAccess(int *, int) -> void | 0 | 34 | 1 | @r0_28 | +| ArrayAccess(int *, int) -> void | 0 | 36 | 0 | @r0_35 | +| ArrayAccess(int *, int) -> void | 0 | 36 | 1 | @m0_26 | +| ArrayAccess(int *, int) -> void | 0 | 38 | 0 | @r0_37 | +| ArrayAccess(int *, int) -> void | 0 | 38 | 1 | @m0_4 | +| ArrayAccess(int *, int) -> void | 0 | 40 | 0 | @r0_39 | +| ArrayAccess(int *, int) -> void | 0 | 40 | 1 | @m0_7 | +| ArrayAccess(int *, int) -> void | 0 | 41 | 3 | @r0_38 | +| ArrayAccess(int *, int) -> void | 0 | 41 | 4 | @r0_40 | +| ArrayAccess(int *, int) -> void | 0 | 42 | 0 | @r0_41 | +| ArrayAccess(int *, int) -> void | 0 | 42 | 1 | @r0_36 | +| ArrayAccess(int *, int) -> void | 0 | 45 | 0 | @r0_43 | +| ArrayAccess(int *, int) -> void | 0 | 45 | 1 | @r0_44 | +| ArrayAccess(int *, int) -> void | 0 | 47 | 2 | @r0_46 | +| ArrayAccess(int *, int) -> void | 0 | 49 | 0 | @r0_48 | +| ArrayAccess(int *, int) -> void | 0 | 49 | 1 | @m0_7 | +| ArrayAccess(int *, int) -> void | 0 | 50 | 3 | @r0_47 | +| ArrayAccess(int *, int) -> void | 0 | 50 | 4 | @r0_49 | +| ArrayAccess(int *, int) -> void | 0 | 51 | 0 | @r0_50 | +| ArrayAccess(int *, int) -> void | 0 | 51 | 1 | @mu0_1 | +| ArrayAccess(int *, int) -> void | 0 | 53 | 0 | @r0_52 | +| ArrayAccess(int *, int) -> void | 0 | 53 | 1 | @r0_51 | +| ArrayAccess(int *, int) -> void | 0 | 55 | 2 | @r0_54 | +| ArrayAccess(int *, int) -> void | 0 | 57 | 0 | @r0_56 | +| ArrayAccess(int *, int) -> void | 0 | 57 | 1 | @m0_7 | +| ArrayAccess(int *, int) -> void | 0 | 58 | 3 | @r0_55 | +| ArrayAccess(int *, int) -> void | 0 | 58 | 4 | @r0_57 | +| ArrayAccess(int *, int) -> void | 0 | 59 | 0 | @r0_58 | +| ArrayAccess(int *, int) -> void | 0 | 59 | 1 | @mu0_1 | +| ArrayAccess(int *, int) -> void | 0 | 61 | 0 | @r0_60 | +| ArrayAccess(int *, int) -> void | 0 | 61 | 1 | @r0_59 | +| ArrayAccess(int *, int) -> void | 0 | 63 | 0 | @r0_62 | +| ArrayAccess(int *, int) -> void | 0 | 63 | 1 | @m0_61 | +| ArrayAccess(int *, int) -> void | 0 | 65 | 2 | @r0_64 | +| ArrayAccess(int *, int) -> void | 0 | 67 | 0 | @r0_66 | +| ArrayAccess(int *, int) -> void | 0 | 67 | 1 | @m0_7 | +| ArrayAccess(int *, int) -> void | 0 | 68 | 3 | @r0_65 | +| ArrayAccess(int *, int) -> void | 0 | 68 | 4 | @r0_67 | +| ArrayAccess(int *, int) -> void | 0 | 69 | 0 | @r0_68 | +| ArrayAccess(int *, int) -> void | 0 | 69 | 1 | @r0_63 | +| ArrayAccess(int *, int) -> void | 0 | 71 | 0 | @r0_70 | +| ArrayAccess(int *, int) -> void | 0 | 71 | 1 | @m0_61 | +| ArrayAccess(int *, int) -> void | 0 | 73 | 2 | @r0_72 | +| ArrayAccess(int *, int) -> void | 0 | 75 | 0 | @r0_74 | +| ArrayAccess(int *, int) -> void | 0 | 75 | 1 | @m0_7 | +| ArrayAccess(int *, int) -> void | 0 | 76 | 3 | @r0_73 | +| ArrayAccess(int *, int) -> void | 0 | 76 | 4 | @r0_75 | +| ArrayAccess(int *, int) -> void | 0 | 77 | 0 | @r0_76 | +| ArrayAccess(int *, int) -> void | 0 | 77 | 1 | @r0_71 | +| ArrayAccess(int *, int) -> void | 0 | 80 | 0 | @mu* | +| ArrayConversions() -> void | 0 | 4 | 0 | @r0_2 | +| ArrayConversions() -> void | 0 | 4 | 1 | @r0_3 | +| ArrayConversions() -> void | 0 | 7 | 2 | @r0_6 | +| ArrayConversions() -> void | 0 | 8 | 2 | @r0_7 | +| ArrayConversions() -> void | 0 | 9 | 0 | @r0_5 | +| ArrayConversions() -> void | 0 | 9 | 1 | @r0_8 | +| ArrayConversions() -> void | 0 | 11 | 2 | @r0_10 | +| ArrayConversions() -> void | 0 | 13 | 0 | @r0_12 | +| ArrayConversions() -> void | 0 | 13 | 1 | @r0_11 | +| ArrayConversions() -> void | 0 | 15 | 2 | @r0_14 | +| ArrayConversions() -> void | 0 | 17 | 3 | @r0_15 | +| ArrayConversions() -> void | 0 | 17 | 4 | @r0_16 | +| ArrayConversions() -> void | 0 | 18 | 2 | @r0_17 | +| ArrayConversions() -> void | 0 | 20 | 0 | @r0_19 | +| ArrayConversions() -> void | 0 | 20 | 1 | @r0_18 | +| ArrayConversions() -> void | 0 | 22 | 2 | @r0_21 | +| ArrayConversions() -> void | 0 | 24 | 3 | @r0_22 | +| ArrayConversions() -> void | 0 | 24 | 4 | @r0_23 | +| ArrayConversions() -> void | 0 | 26 | 0 | @r0_25 | +| ArrayConversions() -> void | 0 | 26 | 1 | @r0_24 | +| ArrayConversions() -> void | 0 | 29 | 0 | @r0_27 | +| ArrayConversions() -> void | 0 | 29 | 1 | @r0_28 | +| ArrayConversions() -> void | 0 | 32 | 0 | @r0_30 | +| ArrayConversions() -> void | 0 | 32 | 1 | @r0_31 | +| ArrayConversions() -> void | 0 | 35 | 2 | @r0_34 | +| ArrayConversions() -> void | 0 | 36 | 0 | @r0_33 | +| ArrayConversions() -> void | 0 | 36 | 1 | @r0_35 | +| ArrayConversions() -> void | 0 | 39 | 0 | @r0_38 | +| ArrayConversions() -> void | 0 | 39 | 1 | @r0_37 | +| ArrayConversions() -> void | 0 | 42 | 0 | @mu* | +| ArrayInit(int, float) -> void | 0 | 4 | 0 | @r0_3 | +| ArrayInit(int, float) -> void | 0 | 4 | 1 | @r0_2 | +| ArrayInit(int, float) -> void | 0 | 7 | 0 | @r0_6 | +| ArrayInit(int, float) -> void | 0 | 7 | 1 | @r0_5 | +| ArrayInit(int, float) -> void | 0 | 10 | 3 | @r0_8 | +| ArrayInit(int, float) -> void | 0 | 10 | 4 | @r0_9 | +| ArrayInit(int, float) -> void | 0 | 12 | 0 | @r0_10 | +| ArrayInit(int, float) -> void | 0 | 12 | 1 | @r0_11 | +| ArrayInit(int, float) -> void | 0 | 15 | 3 | @r0_13 | +| ArrayInit(int, float) -> void | 0 | 15 | 4 | @r0_14 | +| ArrayInit(int, float) -> void | 0 | 17 | 0 | @r0_16 | +| ArrayInit(int, float) -> void | 0 | 17 | 1 | @m0_4 | +| ArrayInit(int, float) -> void | 0 | 18 | 0 | @r0_15 | +| ArrayInit(int, float) -> void | 0 | 18 | 1 | @r0_17 | +| ArrayInit(int, float) -> void | 0 | 20 | 3 | @r0_13 | +| ArrayInit(int, float) -> void | 0 | 20 | 4 | @r0_19 | +| ArrayInit(int, float) -> void | 0 | 22 | 0 | @r0_21 | +| ArrayInit(int, float) -> void | 0 | 22 | 1 | @m0_7 | +| ArrayInit(int, float) -> void | 0 | 23 | 2 | @r0_22 | +| ArrayInit(int, float) -> void | 0 | 24 | 0 | @r0_20 | +| ArrayInit(int, float) -> void | 0 | 24 | 1 | @r0_23 | +| ArrayInit(int, float) -> void | 0 | 26 | 3 | @r0_13 | +| ArrayInit(int, float) -> void | 0 | 26 | 4 | @r0_25 | +| ArrayInit(int, float) -> void | 0 | 28 | 0 | @r0_26 | +| ArrayInit(int, float) -> void | 0 | 28 | 1 | @r0_27 | +| ArrayInit(int, float) -> void | 0 | 31 | 3 | @r0_29 | +| ArrayInit(int, float) -> void | 0 | 31 | 4 | @r0_30 | +| ArrayInit(int, float) -> void | 0 | 33 | 0 | @r0_32 | +| ArrayInit(int, float) -> void | 0 | 33 | 1 | @m0_4 | +| ArrayInit(int, float) -> void | 0 | 34 | 0 | @r0_31 | +| ArrayInit(int, float) -> void | 0 | 34 | 1 | @r0_33 | +| ArrayInit(int, float) -> void | 0 | 36 | 3 | @r0_29 | +| ArrayInit(int, float) -> void | 0 | 36 | 4 | @r0_35 | +| ArrayInit(int, float) -> void | 0 | 38 | 0 | @r0_36 | +| ArrayInit(int, float) -> void | 0 | 38 | 1 | @r0_37 | +| ArrayInit(int, float) -> void | 0 | 41 | 0 | @mu* | +| ArrayReferences() -> void | 0 | 4 | 0 | @r0_2 | +| ArrayReferences() -> void | 0 | 4 | 1 | @r0_3 | +| ArrayReferences() -> void | 0 | 7 | 0 | @r0_5 | +| ArrayReferences() -> void | 0 | 7 | 1 | @r0_6 | +| ArrayReferences() -> void | 0 | 10 | 0 | @r0_9 | +| ArrayReferences() -> void | 0 | 10 | 1 | @m0_7 | +| ArrayReferences() -> void | 0 | 11 | 2 | @r0_10 | +| ArrayReferences() -> void | 0 | 13 | 3 | @r0_11 | +| ArrayReferences() -> void | 0 | 13 | 4 | @r0_12 | +| ArrayReferences() -> void | 0 | 14 | 0 | @r0_13 | +| ArrayReferences() -> void | 0 | 14 | 1 | @mu0_1 | +| ArrayReferences() -> void | 0 | 15 | 0 | @r0_8 | +| ArrayReferences() -> void | 0 | 15 | 1 | @r0_14 | +| ArrayReferences() -> void | 0 | 18 | 0 | @mu* | +| Base::Base() -> void | 0 | 3 | 2 | @r0_2 | +| Base::Base() -> void | 0 | 5 | 9 | @r0_4 | +| Base::Base() -> void | 0 | 5 | 10 | this:@r0_3 | +| Base::Base() -> void | 0 | 8 | 0 | @mu* | +| Base::Base(const Base &) -> void | 0 | 5 | 0 | @r0_4 | +| Base::Base(const Base &) -> void | 0 | 5 | 1 | @r0_3 | +| Base::Base(const Base &) -> void | 0 | 6 | 2 | @r0_2 | +| Base::Base(const Base &) -> void | 0 | 8 | 9 | @r0_7 | +| Base::Base(const Base &) -> void | 0 | 8 | 10 | this:@r0_6 | +| Base::Base(const Base &) -> void | 0 | 11 | 0 | @mu* | +| Base::operator=(const Base &) -> Base & | 0 | 5 | 0 | @r0_4 | +| Base::operator=(const Base &) -> Base & | 0 | 5 | 1 | @r0_3 | +| Base::operator=(const Base &) -> Base & | 0 | 6 | 1 | @r0_2 | +| Base::operator=(const Base &) -> Base & | 0 | 7 | 2 | @r0_6 | +| Base::operator=(const Base &) -> Base & | 0 | 10 | 0 | @r0_9 | +| Base::operator=(const Base &) -> Base & | 0 | 10 | 1 | @m0_5 | +| Base::operator=(const Base &) -> Base & | 0 | 11 | 2 | @r0_10 | +| Base::operator=(const Base &) -> Base & | 0 | 12 | 9 | @r0_8 | +| Base::operator=(const Base &) -> Base & | 0 | 12 | 10 | this:@r0_7 | +| Base::operator=(const Base &) -> Base & | 0 | 12 | 11 | @r0_11 | +| Base::operator=(const Base &) -> Base & | 0 | 14 | 1 | @r0_2 | +| Base::operator=(const Base &) -> Base & | 0 | 15 | 0 | @r0_13 | +| Base::operator=(const Base &) -> Base & | 0 | 15 | 1 | @r0_14 | +| Base::operator=(const Base &) -> Base & | 0 | 17 | 0 | @r0_16 | +| Base::operator=(const Base &) -> Base & | 0 | 17 | 5 | @m0_15 | +| Base::operator=(const Base &) -> Base & | 0 | 18 | 0 | @mu* | +| Base::~Base() -> void | 0 | 4 | 2 | @r0_2 | +| Base::~Base() -> void | 0 | 6 | 9 | @r0_5 | +| Base::~Base() -> void | 0 | 6 | 10 | this:@r0_4 | +| Base::~Base() -> void | 0 | 8 | 0 | @mu* | +| Break(int) -> void | 0 | 4 | 0 | @r0_3 | +| Break(int) -> void | 0 | 4 | 1 | @r0_2 | +| Break(int) -> void | 1 | 1 | 0 | @r1_0 | +| Break(int) -> void | 1 | 1 | 1 | @m5_0 | +| Break(int) -> void | 1 | 3 | 3 | @r1_1 | +| Break(int) -> void | 1 | 3 | 4 | @r1_2 | +| Break(int) -> void | 1 | 4 | 7 | @r1_3 | +| Break(int) -> void | 3 | 2 | 0 | @r3_1 | +| Break(int) -> void | 3 | 2 | 1 | @m5_0 | +| Break(int) -> void | 3 | 3 | 3 | @r3_2 | +| Break(int) -> void | 3 | 3 | 4 | @r3_0 | +| Break(int) -> void | 3 | 4 | 0 | @r3_1 | +| Break(int) -> void | 3 | 4 | 1 | @r3_3 | +| Break(int) -> void | 4 | 3 | 0 | @mu* | +| Break(int) -> void | 5 | 0 | 11 | from 0: @m0_4 | +| Break(int) -> void | 5 | 0 | 11 | from 3: @m3_4 | +| Break(int) -> void | 5 | 2 | 0 | @r5_1 | +| Break(int) -> void | 5 | 2 | 1 | @m5_0 | +| Break(int) -> void | 5 | 4 | 3 | @r5_2 | +| Break(int) -> void | 5 | 4 | 4 | @r5_3 | +| Break(int) -> void | 5 | 5 | 7 | @r5_4 | +| C::C() -> void | 0 | 3 | 2 | @r0_2 | +| C::C() -> void | 0 | 5 | 0 | @r0_3 | +| C::C() -> void | 0 | 5 | 1 | @r0_4 | +| C::C() -> void | 0 | 6 | 2 | @r0_2 | +| C::C() -> void | 0 | 8 | 9 | @r0_7 | +| C::C() -> void | 0 | 8 | 10 | this:@r0_6 | +| C::C() -> void | 0 | 9 | 2 | @r0_2 | +| C::C() -> void | 0 | 11 | 0 | @r0_9 | +| C::C() -> void | 0 | 11 | 1 | @r0_10 | +| C::C() -> void | 0 | 12 | 2 | @r0_2 | +| C::C() -> void | 0 | 14 | 0 | @r0_12 | +| C::C() -> void | 0 | 14 | 1 | @r0_13 | +| C::C() -> void | 0 | 15 | 2 | @r0_2 | +| C::C() -> void | 0 | 18 | 2 | @r0_17 | +| C::C() -> void | 0 | 19 | 9 | @r0_16 | +| C::C() -> void | 0 | 19 | 10 | this:@r0_15 | +| C::C() -> void | 0 | 19 | 11 | @r0_18 | +| C::C() -> void | 0 | 22 | 0 | @mu* | +| C::FieldAccess() -> void | 0 | 4 | 1 | @r0_2 | +| C::FieldAccess() -> void | 0 | 5 | 2 | @r0_4 | +| C::FieldAccess() -> void | 0 | 6 | 0 | @r0_5 | +| C::FieldAccess() -> void | 0 | 6 | 1 | @r0_3 | +| C::FieldAccess() -> void | 0 | 8 | 1 | @r0_2 | +| C::FieldAccess() -> void | 0 | 9 | 2 | @r0_8 | +| C::FieldAccess() -> void | 0 | 10 | 0 | @r0_9 | +| C::FieldAccess() -> void | 0 | 10 | 1 | @r0_7 | +| C::FieldAccess() -> void | 0 | 12 | 1 | @r0_2 | +| C::FieldAccess() -> void | 0 | 13 | 2 | @r0_12 | +| C::FieldAccess() -> void | 0 | 14 | 0 | @r0_13 | +| C::FieldAccess() -> void | 0 | 14 | 1 | @r0_11 | +| C::FieldAccess() -> void | 0 | 17 | 0 | @r0_15 | +| C::FieldAccess() -> void | 0 | 17 | 1 | @r0_16 | +| C::FieldAccess() -> void | 0 | 18 | 1 | @r0_2 | +| C::FieldAccess() -> void | 0 | 19 | 2 | @r0_18 | +| C::FieldAccess() -> void | 0 | 20 | 0 | @r0_19 | +| C::FieldAccess() -> void | 0 | 20 | 1 | @mu0_1 | +| C::FieldAccess() -> void | 0 | 22 | 0 | @r0_21 | +| C::FieldAccess() -> void | 0 | 22 | 1 | @r0_20 | +| C::FieldAccess() -> void | 0 | 23 | 1 | @r0_2 | +| C::FieldAccess() -> void | 0 | 24 | 2 | @r0_23 | +| C::FieldAccess() -> void | 0 | 25 | 0 | @r0_24 | +| C::FieldAccess() -> void | 0 | 25 | 1 | @mu0_1 | +| C::FieldAccess() -> void | 0 | 27 | 0 | @r0_26 | +| C::FieldAccess() -> void | 0 | 27 | 1 | @r0_25 | +| C::FieldAccess() -> void | 0 | 28 | 1 | @r0_2 | +| C::FieldAccess() -> void | 0 | 29 | 2 | @r0_28 | +| C::FieldAccess() -> void | 0 | 30 | 0 | @r0_29 | +| C::FieldAccess() -> void | 0 | 30 | 1 | @mu0_1 | +| C::FieldAccess() -> void | 0 | 32 | 0 | @r0_31 | +| C::FieldAccess() -> void | 0 | 32 | 1 | @r0_30 | +| C::FieldAccess() -> void | 0 | 35 | 0 | @mu* | +| C::InstanceMemberFunction(int) -> int | 0 | 5 | 0 | @r0_4 | +| C::InstanceMemberFunction(int) -> int | 0 | 5 | 1 | @r0_3 | +| C::InstanceMemberFunction(int) -> int | 0 | 8 | 0 | @r0_7 | +| C::InstanceMemberFunction(int) -> int | 0 | 8 | 1 | @m0_5 | +| C::InstanceMemberFunction(int) -> int | 0 | 9 | 0 | @r0_6 | +| C::InstanceMemberFunction(int) -> int | 0 | 9 | 1 | @r0_8 | +| C::InstanceMemberFunction(int) -> int | 0 | 11 | 0 | @r0_10 | +| C::InstanceMemberFunction(int) -> int | 0 | 11 | 5 | @m0_9 | +| C::InstanceMemberFunction(int) -> int | 0 | 12 | 0 | @mu* | +| C::MethodCalls() -> void | 0 | 3 | 1 | @r0_2 | +| C::MethodCalls() -> void | 0 | 6 | 9 | @r0_4 | +| C::MethodCalls() -> void | 0 | 6 | 10 | this:@r0_3 | +| C::MethodCalls() -> void | 0 | 6 | 11 | @r0_5 | +| C::MethodCalls() -> void | 0 | 7 | 1 | @r0_2 | +| C::MethodCalls() -> void | 0 | 10 | 9 | @r0_8 | +| C::MethodCalls() -> void | 0 | 10 | 10 | this:@r0_7 | +| C::MethodCalls() -> void | 0 | 10 | 11 | @r0_9 | +| C::MethodCalls() -> void | 0 | 11 | 1 | @r0_2 | +| C::MethodCalls() -> void | 0 | 14 | 9 | @r0_12 | +| C::MethodCalls() -> void | 0 | 14 | 10 | this:@r0_11 | +| C::MethodCalls() -> void | 0 | 14 | 11 | @r0_13 | +| C::MethodCalls() -> void | 0 | 17 | 0 | @mu* | +| C::StaticMemberFunction(int) -> int | 0 | 4 | 0 | @r0_3 | +| C::StaticMemberFunction(int) -> int | 0 | 4 | 1 | @r0_2 | +| C::StaticMemberFunction(int) -> int | 0 | 7 | 0 | @r0_6 | +| C::StaticMemberFunction(int) -> int | 0 | 7 | 1 | @m0_4 | +| C::StaticMemberFunction(int) -> int | 0 | 8 | 0 | @r0_5 | +| C::StaticMemberFunction(int) -> int | 0 | 8 | 1 | @r0_7 | +| C::StaticMemberFunction(int) -> int | 0 | 10 | 0 | @r0_9 | +| C::StaticMemberFunction(int) -> int | 0 | 10 | 5 | @m0_8 | +| C::StaticMemberFunction(int) -> int | 0 | 11 | 0 | @mu* | +| C::VirtualMemberFunction(int) -> int | 0 | 5 | 0 | @r0_4 | +| C::VirtualMemberFunction(int) -> int | 0 | 5 | 1 | @r0_3 | +| C::VirtualMemberFunction(int) -> int | 0 | 8 | 0 | @r0_7 | +| C::VirtualMemberFunction(int) -> int | 0 | 8 | 1 | @m0_5 | +| C::VirtualMemberFunction(int) -> int | 0 | 9 | 0 | @r0_6 | +| C::VirtualMemberFunction(int) -> int | 0 | 9 | 1 | @r0_8 | +| C::VirtualMemberFunction(int) -> int | 0 | 11 | 0 | @r0_10 | +| C::VirtualMemberFunction(int) -> int | 0 | 11 | 5 | @m0_9 | +| C::VirtualMemberFunction(int) -> int | 0 | 12 | 0 | @mu* | +| Call() -> void | 0 | 3 | 9 | @r0_2 | +| Call() -> void | 0 | 6 | 0 | @mu* | +| CallAdd(int, int) -> int | 0 | 4 | 0 | @r0_3 | +| CallAdd(int, int) -> int | 0 | 4 | 1 | @r0_2 | +| CallAdd(int, int) -> int | 0 | 7 | 0 | @r0_6 | +| CallAdd(int, int) -> int | 0 | 7 | 1 | @r0_5 | +| CallAdd(int, int) -> int | 0 | 11 | 0 | @r0_10 | +| CallAdd(int, int) -> int | 0 | 11 | 1 | @m0_4 | +| CallAdd(int, int) -> int | 0 | 13 | 0 | @r0_12 | +| CallAdd(int, int) -> int | 0 | 13 | 1 | @m0_7 | +| CallAdd(int, int) -> int | 0 | 14 | 9 | @r0_9 | +| CallAdd(int, int) -> int | 0 | 14 | 11 | @r0_11 | +| CallAdd(int, int) -> int | 0 | 14 | 12 | @r0_13 | +| CallAdd(int, int) -> int | 0 | 15 | 0 | @r0_8 | +| CallAdd(int, int) -> int | 0 | 15 | 1 | @r0_14 | +| CallAdd(int, int) -> int | 0 | 17 | 0 | @r0_16 | +| CallAdd(int, int) -> int | 0 | 17 | 5 | @m0_15 | +| CallAdd(int, int) -> int | 0 | 18 | 0 | @mu* | +| CallMethods(String &, String *, String) -> void | 0 | 4 | 0 | @r0_3 | +| CallMethods(String &, String *, String) -> void | 0 | 4 | 1 | @r0_2 | +| CallMethods(String &, String *, String) -> void | 0 | 7 | 0 | @r0_6 | +| CallMethods(String &, String *, String) -> void | 0 | 7 | 1 | @r0_5 | +| CallMethods(String &, String *, String) -> void | 0 | 10 | 0 | @r0_9 | +| CallMethods(String &, String *, String) -> void | 0 | 10 | 1 | @r0_8 | +| CallMethods(String &, String *, String) -> void | 0 | 12 | 0 | @r0_11 | +| CallMethods(String &, String *, String) -> void | 0 | 12 | 1 | @m0_4 | +| CallMethods(String &, String *, String) -> void | 0 | 13 | 2 | @r0_12 | +| CallMethods(String &, String *, String) -> void | 0 | 15 | 9 | @r0_14 | +| CallMethods(String &, String *, String) -> void | 0 | 15 | 10 | this:@r0_13 | +| CallMethods(String &, String *, String) -> void | 0 | 17 | 0 | @r0_16 | +| CallMethods(String &, String *, String) -> void | 0 | 17 | 1 | @m0_7 | +| CallMethods(String &, String *, String) -> void | 0 | 18 | 2 | @r0_17 | +| CallMethods(String &, String *, String) -> void | 0 | 20 | 9 | @r0_19 | +| CallMethods(String &, String *, String) -> void | 0 | 20 | 10 | this:@r0_18 | +| CallMethods(String &, String *, String) -> void | 0 | 22 | 2 | @r0_21 | +| CallMethods(String &, String *, String) -> void | 0 | 24 | 9 | @r0_23 | +| CallMethods(String &, String *, String) -> void | 0 | 24 | 10 | this:@r0_22 | +| CallMethods(String &, String *, String) -> void | 0 | 27 | 0 | @mu* | +| CallMin(int, int) -> int | 0 | 4 | 0 | @r0_3 | +| CallMin(int, int) -> int | 0 | 4 | 1 | @r0_2 | +| CallMin(int, int) -> int | 0 | 7 | 0 | @r0_6 | +| CallMin(int, int) -> int | 0 | 7 | 1 | @r0_5 | +| CallMin(int, int) -> int | 0 | 11 | 0 | @r0_10 | +| CallMin(int, int) -> int | 0 | 11 | 1 | @m0_4 | +| CallMin(int, int) -> int | 0 | 13 | 0 | @r0_12 | +| CallMin(int, int) -> int | 0 | 13 | 1 | @m0_7 | +| CallMin(int, int) -> int | 0 | 14 | 9 | @r0_9 | +| CallMin(int, int) -> int | 0 | 14 | 11 | @r0_11 | +| CallMin(int, int) -> int | 0 | 14 | 12 | @r0_13 | +| CallMin(int, int) -> int | 0 | 15 | 0 | @r0_8 | +| CallMin(int, int) -> int | 0 | 15 | 1 | @r0_14 | +| CallMin(int, int) -> int | 0 | 17 | 0 | @r0_16 | +| CallMin(int, int) -> int | 0 | 17 | 5 | @m0_15 | +| CallMin(int, int) -> int | 0 | 18 | 0 | @mu* | +| CallNestedTemplateFunc() -> double | 0 | 6 | 9 | @r0_3 | +| CallNestedTemplateFunc() -> double | 0 | 6 | 11 | @r0_4 | +| CallNestedTemplateFunc() -> double | 0 | 6 | 12 | @r0_5 | +| CallNestedTemplateFunc() -> double | 0 | 7 | 2 | @r0_6 | +| CallNestedTemplateFunc() -> double | 0 | 8 | 0 | @r0_2 | +| CallNestedTemplateFunc() -> double | 0 | 8 | 1 | @r0_7 | +| CallNestedTemplateFunc() -> double | 0 | 10 | 0 | @r0_9 | +| CallNestedTemplateFunc() -> double | 0 | 10 | 5 | @m0_8 | +| CallNestedTemplateFunc() -> double | 0 | 11 | 0 | @mu* | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 4 | 0 | @r0_3 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 4 | 1 | @r0_2 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 7 | 0 | @r0_6 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 7 | 1 | @m0_4 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 9 | 9 | @r0_7 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 9 | 11 | @r0_8 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 10 | 0 | @r0_5 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 10 | 1 | @r0_9 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 12 | 0 | @r0_11 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 12 | 5 | @m0_10 | +| CallViaFuncPtr(..(*)(..)) -> int | 0 | 13 | 0 | @mu* | +| Comma(int, int) -> int | 0 | 4 | 0 | @r0_3 | +| Comma(int, int) -> int | 0 | 4 | 1 | @r0_2 | +| Comma(int, int) -> int | 0 | 7 | 0 | @r0_6 | +| Comma(int, int) -> int | 0 | 7 | 1 | @r0_5 | +| Comma(int, int) -> int | 0 | 10 | 9 | @r0_9 | +| Comma(int, int) -> int | 0 | 13 | 0 | @r0_12 | +| Comma(int, int) -> int | 0 | 13 | 1 | @m0_4 | +| Comma(int, int) -> int | 0 | 15 | 0 | @r0_14 | +| Comma(int, int) -> int | 0 | 15 | 1 | @m0_7 | +| Comma(int, int) -> int | 0 | 16 | 9 | @r0_11 | +| Comma(int, int) -> int | 0 | 16 | 11 | @r0_13 | +| Comma(int, int) -> int | 0 | 16 | 12 | @r0_15 | +| Comma(int, int) -> int | 0 | 17 | 0 | @r0_8 | +| Comma(int, int) -> int | 0 | 17 | 1 | @r0_16 | +| Comma(int, int) -> int | 0 | 19 | 0 | @r0_18 | +| Comma(int, int) -> int | 0 | 19 | 5 | @m0_17 | +| Comma(int, int) -> int | 0 | 20 | 0 | @mu* | +| CompoundAssignment() -> void | 0 | 4 | 0 | @r0_2 | +| CompoundAssignment() -> void | 0 | 4 | 1 | @r0_3 | +| CompoundAssignment() -> void | 0 | 7 | 0 | @r0_6 | +| CompoundAssignment() -> void | 0 | 7 | 1 | @m0_4 | +| CompoundAssignment() -> void | 0 | 8 | 3 | @r0_7 | +| CompoundAssignment() -> void | 0 | 8 | 4 | @r0_5 | +| CompoundAssignment() -> void | 0 | 9 | 0 | @r0_6 | +| CompoundAssignment() -> void | 0 | 9 | 1 | @r0_8 | +| CompoundAssignment() -> void | 0 | 12 | 0 | @r0_10 | +| CompoundAssignment() -> void | 0 | 12 | 1 | @r0_11 | +| CompoundAssignment() -> void | 0 | 14 | 0 | @r0_13 | +| CompoundAssignment() -> void | 0 | 14 | 1 | @m0_9 | +| CompoundAssignment() -> void | 0 | 16 | 0 | @r0_15 | +| CompoundAssignment() -> void | 0 | 16 | 1 | @m0_12 | +| CompoundAssignment() -> void | 0 | 17 | 2 | @r0_16 | +| CompoundAssignment() -> void | 0 | 18 | 3 | @r0_17 | +| CompoundAssignment() -> void | 0 | 18 | 4 | @r0_14 | +| CompoundAssignment() -> void | 0 | 19 | 2 | @r0_18 | +| CompoundAssignment() -> void | 0 | 20 | 0 | @r0_15 | +| CompoundAssignment() -> void | 0 | 20 | 1 | @r0_19 | +| CompoundAssignment() -> void | 0 | 23 | 0 | @r0_22 | +| CompoundAssignment() -> void | 0 | 23 | 1 | @m0_20 | +| CompoundAssignment() -> void | 0 | 24 | 3 | @r0_23 | +| CompoundAssignment() -> void | 0 | 24 | 4 | @r0_21 | +| CompoundAssignment() -> void | 0 | 25 | 0 | @r0_22 | +| CompoundAssignment() -> void | 0 | 25 | 1 | @r0_24 | +| CompoundAssignment() -> void | 0 | 28 | 0 | @r0_26 | +| CompoundAssignment() -> void | 0 | 28 | 1 | @r0_27 | +| CompoundAssignment() -> void | 0 | 31 | 0 | @r0_30 | +| CompoundAssignment() -> void | 0 | 31 | 1 | @m0_28 | +| CompoundAssignment() -> void | 0 | 32 | 2 | @r0_31 | +| CompoundAssignment() -> void | 0 | 33 | 3 | @r0_32 | +| CompoundAssignment() -> void | 0 | 33 | 4 | @r0_29 | +| CompoundAssignment() -> void | 0 | 34 | 2 | @r0_33 | +| CompoundAssignment() -> void | 0 | 35 | 0 | @r0_30 | +| CompoundAssignment() -> void | 0 | 35 | 1 | @r0_34 | +| CompoundAssignment() -> void | 0 | 38 | 0 | @mu* | +| ConditionValues(bool, bool) -> void | 0 | 4 | 0 | @r0_3 | +| ConditionValues(bool, bool) -> void | 0 | 4 | 1 | @r0_2 | +| ConditionValues(bool, bool) -> void | 0 | 7 | 0 | @r0_6 | +| ConditionValues(bool, bool) -> void | 0 | 7 | 1 | @r0_5 | +| ConditionValues(bool, bool) -> void | 0 | 10 | 0 | @r0_8 | +| ConditionValues(bool, bool) -> void | 0 | 10 | 1 | @r0_9 | +| ConditionValues(bool, bool) -> void | 0 | 12 | 0 | @r0_11 | +| ConditionValues(bool, bool) -> void | 0 | 12 | 1 | @m0_4 | +| ConditionValues(bool, bool) -> void | 0 | 13 | 7 | @r0_12 | +| ConditionValues(bool, bool) -> void | 1 | 1 | 0 | @r1_0 | +| ConditionValues(bool, bool) -> void | 1 | 1 | 1 | @m0_7 | +| ConditionValues(bool, bool) -> void | 1 | 2 | 7 | @r1_1 | +| ConditionValues(bool, bool) -> void | 2 | 2 | 0 | @r2_0 | +| ConditionValues(bool, bool) -> void | 2 | 2 | 1 | @r2_1 | +| ConditionValues(bool, bool) -> void | 3 | 0 | 11 | from 2: @m2_2 | +| ConditionValues(bool, bool) -> void | 3 | 0 | 11 | from 4: @m4_2 | +| ConditionValues(bool, bool) -> void | 3 | 2 | 0 | @r3_1 | +| ConditionValues(bool, bool) -> void | 3 | 2 | 1 | @m3_0 | +| ConditionValues(bool, bool) -> void | 3 | 4 | 0 | @r3_3 | +| ConditionValues(bool, bool) -> void | 3 | 4 | 1 | @r3_2 | +| ConditionValues(bool, bool) -> void | 3 | 6 | 0 | @r3_5 | +| ConditionValues(bool, bool) -> void | 3 | 6 | 1 | @m0_4 | +| ConditionValues(bool, bool) -> void | 3 | 7 | 7 | @r3_6 | +| ConditionValues(bool, bool) -> void | 4 | 2 | 0 | @r4_0 | +| ConditionValues(bool, bool) -> void | 4 | 2 | 1 | @r4_1 | +| ConditionValues(bool, bool) -> void | 5 | 1 | 0 | @r5_0 | +| ConditionValues(bool, bool) -> void | 5 | 1 | 1 | @m0_7 | +| ConditionValues(bool, bool) -> void | 5 | 2 | 7 | @r5_1 | +| ConditionValues(bool, bool) -> void | 6 | 2 | 0 | @r6_0 | +| ConditionValues(bool, bool) -> void | 6 | 2 | 1 | @r6_1 | +| ConditionValues(bool, bool) -> void | 7 | 0 | 11 | from 6: @m6_2 | +| ConditionValues(bool, bool) -> void | 7 | 0 | 11 | from 8: @m8_2 | +| ConditionValues(bool, bool) -> void | 7 | 2 | 0 | @r7_1 | +| ConditionValues(bool, bool) -> void | 7 | 2 | 1 | @m7_0 | +| ConditionValues(bool, bool) -> void | 7 | 3 | 2 | @r7_2 | +| ConditionValues(bool, bool) -> void | 7 | 5 | 0 | @r7_4 | +| ConditionValues(bool, bool) -> void | 7 | 5 | 1 | @r7_3 | +| ConditionValues(bool, bool) -> void | 7 | 8 | 0 | @mu* | +| ConditionValues(bool, bool) -> void | 8 | 2 | 0 | @r8_0 | +| ConditionValues(bool, bool) -> void | 8 | 2 | 1 | @r8_1 | +| ConditionValues(bool, bool) -> void | 9 | 1 | 0 | @r9_0 | +| ConditionValues(bool, bool) -> void | 9 | 1 | 1 | @m0_7 | +| ConditionValues(bool, bool) -> void | 9 | 2 | 7 | @r9_1 | +| ConditionValues(bool, bool) -> void | 10 | 2 | 0 | @r10_0 | +| ConditionValues(bool, bool) -> void | 10 | 2 | 1 | @r10_1 | +| ConditionValues(bool, bool) -> void | 11 | 0 | 11 | from 10: @m10_2 | +| ConditionValues(bool, bool) -> void | 11 | 0 | 11 | from 12: @m12_2 | +| ConditionValues(bool, bool) -> void | 11 | 2 | 0 | @r11_1 | +| ConditionValues(bool, bool) -> void | 11 | 2 | 1 | @m11_0 | +| ConditionValues(bool, bool) -> void | 11 | 4 | 0 | @r11_3 | +| ConditionValues(bool, bool) -> void | 11 | 4 | 1 | @r11_2 | +| ConditionValues(bool, bool) -> void | 11 | 6 | 0 | @r11_5 | +| ConditionValues(bool, bool) -> void | 11 | 6 | 1 | @m0_4 | +| ConditionValues(bool, bool) -> void | 11 | 7 | 7 | @r11_6 | +| ConditionValues(bool, bool) -> void | 12 | 2 | 0 | @r12_0 | +| ConditionValues(bool, bool) -> void | 12 | 2 | 1 | @r12_1 | +| Conditional(bool, int, int) -> void | 0 | 4 | 0 | @r0_3 | +| Conditional(bool, int, int) -> void | 0 | 4 | 1 | @r0_2 | +| Conditional(bool, int, int) -> void | 0 | 7 | 0 | @r0_6 | +| Conditional(bool, int, int) -> void | 0 | 7 | 1 | @r0_5 | +| Conditional(bool, int, int) -> void | 0 | 10 | 0 | @r0_9 | +| Conditional(bool, int, int) -> void | 0 | 10 | 1 | @r0_8 | +| Conditional(bool, int, int) -> void | 0 | 13 | 0 | @r0_12 | +| Conditional(bool, int, int) -> void | 0 | 13 | 1 | @m0_4 | +| Conditional(bool, int, int) -> void | 0 | 14 | 7 | @r0_13 | +| Conditional(bool, int, int) -> void | 1 | 1 | 0 | @r1_0 | +| Conditional(bool, int, int) -> void | 1 | 1 | 1 | @m0_7 | +| Conditional(bool, int, int) -> void | 1 | 3 | 0 | @r1_2 | +| Conditional(bool, int, int) -> void | 1 | 3 | 1 | @r1_1 | +| Conditional(bool, int, int) -> void | 2 | 1 | 0 | @r2_0 | +| Conditional(bool, int, int) -> void | 2 | 1 | 1 | @m0_10 | +| Conditional(bool, int, int) -> void | 2 | 3 | 0 | @r2_2 | +| Conditional(bool, int, int) -> void | 2 | 3 | 1 | @r2_1 | +| Conditional(bool, int, int) -> void | 3 | 0 | 11 | from 1: @m1_3 | +| Conditional(bool, int, int) -> void | 3 | 0 | 11 | from 2: @m2_3 | +| Conditional(bool, int, int) -> void | 3 | 2 | 0 | @r3_1 | +| Conditional(bool, int, int) -> void | 3 | 2 | 1 | @m3_0 | +| Conditional(bool, int, int) -> void | 3 | 3 | 0 | @r0_11 | +| Conditional(bool, int, int) -> void | 3 | 3 | 1 | @r3_2 | +| Conditional(bool, int, int) -> void | 3 | 6 | 0 | @mu* | +| Conditional_LValue(bool) -> void | 0 | 4 | 0 | @r0_3 | +| Conditional_LValue(bool) -> void | 0 | 4 | 1 | @r0_2 | +| Conditional_LValue(bool) -> void | 0 | 7 | 0 | @r0_5 | +| Conditional_LValue(bool) -> void | 0 | 7 | 1 | @r0_6 | +| Conditional_LValue(bool) -> void | 0 | 10 | 0 | @r0_8 | +| Conditional_LValue(bool) -> void | 0 | 10 | 1 | @r0_9 | +| Conditional_LValue(bool) -> void | 0 | 13 | 0 | @r0_12 | +| Conditional_LValue(bool) -> void | 0 | 13 | 1 | @m0_4 | +| Conditional_LValue(bool) -> void | 0 | 14 | 7 | @r0_13 | +| Conditional_LValue(bool) -> void | 1 | 0 | 11 | from 2: @m2_2 | +| Conditional_LValue(bool) -> void | 1 | 0 | 11 | from 3: @m3_2 | +| Conditional_LValue(bool) -> void | 1 | 2 | 0 | @r1_1 | +| Conditional_LValue(bool) -> void | 1 | 2 | 1 | @m1_0 | +| Conditional_LValue(bool) -> void | 1 | 3 | 0 | @r1_2 | +| Conditional_LValue(bool) -> void | 1 | 3 | 1 | @r0_11 | +| Conditional_LValue(bool) -> void | 1 | 6 | 0 | @mu* | +| Conditional_LValue(bool) -> void | 2 | 2 | 0 | @r2_1 | +| Conditional_LValue(bool) -> void | 2 | 2 | 1 | @r2_0 | +| Conditional_LValue(bool) -> void | 3 | 2 | 0 | @r3_1 | +| Conditional_LValue(bool) -> void | 3 | 2 | 1 | @r3_0 | +| Conditional_Void(bool) -> void | 0 | 4 | 0 | @r0_3 | +| Conditional_Void(bool) -> void | 0 | 4 | 1 | @r0_2 | +| Conditional_Void(bool) -> void | 0 | 6 | 0 | @r0_5 | +| Conditional_Void(bool) -> void | 0 | 6 | 1 | @m0_4 | +| Conditional_Void(bool) -> void | 0 | 7 | 7 | @r0_6 | +| Conditional_Void(bool) -> void | 1 | 2 | 0 | @mu* | +| Conditional_Void(bool) -> void | 2 | 1 | 9 | @r2_0 | +| Conditional_Void(bool) -> void | 3 | 1 | 9 | @r3_0 | +| Constants() -> void | 0 | 4 | 0 | @r0_2 | +| Constants() -> void | 0 | 4 | 1 | @r0_3 | +| Constants() -> void | 0 | 7 | 0 | @r0_5 | +| Constants() -> void | 0 | 7 | 1 | @r0_6 | +| Constants() -> void | 0 | 10 | 0 | @r0_8 | +| Constants() -> void | 0 | 10 | 1 | @r0_9 | +| Constants() -> void | 0 | 13 | 0 | @r0_11 | +| Constants() -> void | 0 | 13 | 1 | @r0_12 | +| Constants() -> void | 0 | 16 | 0 | @r0_14 | +| Constants() -> void | 0 | 16 | 1 | @r0_15 | +| Constants() -> void | 0 | 19 | 0 | @r0_17 | +| Constants() -> void | 0 | 19 | 1 | @r0_18 | +| Constants() -> void | 0 | 22 | 0 | @r0_20 | +| Constants() -> void | 0 | 22 | 1 | @r0_21 | +| Constants() -> void | 0 | 25 | 0 | @r0_23 | +| Constants() -> void | 0 | 25 | 1 | @r0_24 | +| Constants() -> void | 0 | 28 | 0 | @r0_26 | +| Constants() -> void | 0 | 28 | 1 | @r0_27 | +| Constants() -> void | 0 | 31 | 0 | @r0_29 | +| Constants() -> void | 0 | 31 | 1 | @r0_30 | +| Constants() -> void | 0 | 34 | 0 | @r0_32 | +| Constants() -> void | 0 | 34 | 1 | @r0_33 | +| Constants() -> void | 0 | 37 | 0 | @r0_35 | +| Constants() -> void | 0 | 37 | 1 | @r0_36 | +| Constants() -> void | 0 | 40 | 0 | @r0_38 | +| Constants() -> void | 0 | 40 | 1 | @r0_39 | +| Constants() -> void | 0 | 43 | 0 | @r0_41 | +| Constants() -> void | 0 | 43 | 1 | @r0_42 | +| Constants() -> void | 0 | 46 | 0 | @r0_44 | +| Constants() -> void | 0 | 46 | 1 | @r0_45 | +| Constants() -> void | 0 | 49 | 0 | @r0_47 | +| Constants() -> void | 0 | 49 | 1 | @r0_48 | +| Constants() -> void | 0 | 52 | 0 | @r0_50 | +| Constants() -> void | 0 | 52 | 1 | @r0_51 | +| Constants() -> void | 0 | 55 | 0 | @r0_53 | +| Constants() -> void | 0 | 55 | 1 | @r0_54 | +| Constants() -> void | 0 | 58 | 0 | @r0_56 | +| Constants() -> void | 0 | 58 | 1 | @r0_57 | +| Constants() -> void | 0 | 61 | 0 | @r0_59 | +| Constants() -> void | 0 | 61 | 1 | @r0_60 | +| Constants() -> void | 0 | 64 | 0 | @r0_62 | +| Constants() -> void | 0 | 64 | 1 | @r0_63 | +| Constants() -> void | 0 | 67 | 0 | @r0_65 | +| Constants() -> void | 0 | 67 | 1 | @r0_66 | +| Constants() -> void | 0 | 70 | 0 | @r0_68 | +| Constants() -> void | 0 | 70 | 1 | @r0_69 | +| Constants() -> void | 0 | 73 | 0 | @r0_71 | +| Constants() -> void | 0 | 73 | 1 | @r0_72 | +| Constants() -> void | 0 | 76 | 0 | @r0_74 | +| Constants() -> void | 0 | 76 | 1 | @r0_75 | +| Constants() -> void | 0 | 79 | 0 | @r0_77 | +| Constants() -> void | 0 | 79 | 1 | @r0_78 | +| Constants() -> void | 0 | 82 | 0 | @r0_80 | +| Constants() -> void | 0 | 82 | 1 | @r0_81 | +| Constants() -> void | 0 | 85 | 0 | @r0_83 | +| Constants() -> void | 0 | 85 | 1 | @r0_84 | +| Constants() -> void | 0 | 88 | 0 | @mu* | +| Continue(int) -> void | 0 | 4 | 0 | @r0_3 | +| Continue(int) -> void | 0 | 4 | 1 | @r0_2 | +| Continue(int) -> void | 1 | 0 | 11 | from 0: @m0_4 | +| Continue(int) -> void | 1 | 0 | 11 | from 4: @m4_0 | +| Continue(int) -> void | 1 | 2 | 0 | @r1_1 | +| Continue(int) -> void | 1 | 2 | 1 | @m1_0 | +| Continue(int) -> void | 1 | 4 | 3 | @r1_2 | +| Continue(int) -> void | 1 | 4 | 4 | @r1_3 | +| Continue(int) -> void | 1 | 5 | 7 | @r1_4 | +| Continue(int) -> void | 3 | 2 | 0 | @r3_1 | +| Continue(int) -> void | 3 | 2 | 1 | @m1_0 | +| Continue(int) -> void | 3 | 3 | 3 | @r3_2 | +| Continue(int) -> void | 3 | 3 | 4 | @r3_0 | +| Continue(int) -> void | 3 | 4 | 0 | @r3_1 | +| Continue(int) -> void | 3 | 4 | 1 | @r3_3 | +| Continue(int) -> void | 4 | 0 | 11 | from 2: @m1_0 | +| Continue(int) -> void | 4 | 0 | 11 | from 3: @m3_4 | +| Continue(int) -> void | 4 | 3 | 0 | @r4_2 | +| Continue(int) -> void | 4 | 3 | 1 | @m4_0 | +| Continue(int) -> void | 4 | 5 | 3 | @r4_3 | +| Continue(int) -> void | 4 | 5 | 4 | @r4_4 | +| Continue(int) -> void | 4 | 6 | 7 | @r4_5 | +| Continue(int) -> void | 5 | 2 | 0 | @mu* | +| DeclareObject() -> void | 0 | 4 | 9 | @r0_3 | +| DeclareObject() -> void | 0 | 4 | 10 | this:@r0_2 | +| DeclareObject() -> void | 0 | 8 | 2 | @r0_7 | +| DeclareObject() -> void | 0 | 9 | 9 | @r0_6 | +| DeclareObject() -> void | 0 | 9 | 10 | this:@r0_5 | +| DeclareObject() -> void | 0 | 9 | 11 | @r0_8 | +| DeclareObject() -> void | 0 | 12 | 9 | @r0_11 | +| DeclareObject() -> void | 0 | 13 | 0 | @r0_10 | +| DeclareObject() -> void | 0 | 13 | 1 | @r0_12 | +| DeclareObject() -> void | 0 | 17 | 2 | @r0_16 | +| DeclareObject() -> void | 0 | 18 | 9 | @r0_15 | +| DeclareObject() -> void | 0 | 18 | 10 | this:@r0_14 | +| DeclareObject() -> void | 0 | 18 | 11 | @r0_17 | +| DeclareObject() -> void | 0 | 21 | 0 | @mu* | +| DerefReference(int &) -> int | 0 | 4 | 0 | @r0_3 | +| DerefReference(int &) -> int | 0 | 4 | 1 | @r0_2 | +| DerefReference(int &) -> int | 0 | 7 | 0 | @r0_6 | +| DerefReference(int &) -> int | 0 | 7 | 1 | @m0_4 | +| DerefReference(int &) -> int | 0 | 8 | 0 | @r0_7 | +| DerefReference(int &) -> int | 0 | 8 | 1 | @mu0_1 | +| DerefReference(int &) -> int | 0 | 9 | 0 | @r0_5 | +| DerefReference(int &) -> int | 0 | 9 | 1 | @r0_8 | +| DerefReference(int &) -> int | 0 | 11 | 0 | @r0_10 | +| DerefReference(int &) -> int | 0 | 11 | 5 | @m0_9 | +| DerefReference(int &) -> int | 0 | 12 | 0 | @mu* | +| Dereference(int *) -> int | 0 | 4 | 0 | @r0_3 | +| Dereference(int *) -> int | 0 | 4 | 1 | @r0_2 | +| Dereference(int *) -> int | 0 | 7 | 0 | @r0_6 | +| Dereference(int *) -> int | 0 | 7 | 1 | @m0_4 | +| Dereference(int *) -> int | 0 | 8 | 0 | @r0_7 | +| Dereference(int *) -> int | 0 | 8 | 1 | @r0_5 | +| Dereference(int *) -> int | 0 | 11 | 0 | @r0_10 | +| Dereference(int *) -> int | 0 | 11 | 1 | @m0_4 | +| Dereference(int *) -> int | 0 | 12 | 0 | @r0_11 | +| Dereference(int *) -> int | 0 | 12 | 1 | @mu0_1 | +| Dereference(int *) -> int | 0 | 13 | 0 | @r0_9 | +| Dereference(int *) -> int | 0 | 13 | 1 | @r0_12 | +| Dereference(int *) -> int | 0 | 15 | 0 | @r0_14 | +| Dereference(int *) -> int | 0 | 15 | 5 | @m0_13 | +| Dereference(int *) -> int | 0 | 16 | 0 | @mu* | +| Derived::Derived() -> void | 0 | 3 | 2 | @r0_2 | +| Derived::Derived() -> void | 0 | 5 | 9 | @r0_4 | +| Derived::Derived() -> void | 0 | 5 | 10 | this:@r0_3 | +| Derived::Derived() -> void | 0 | 6 | 2 | @r0_2 | +| Derived::Derived() -> void | 0 | 8 | 9 | @r0_7 | +| Derived::Derived() -> void | 0 | 8 | 10 | this:@r0_6 | +| Derived::Derived() -> void | 0 | 11 | 0 | @mu* | +| Derived::operator=(const Derived &) -> Derived & | 0 | 5 | 0 | @r0_4 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 5 | 1 | @r0_3 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 6 | 1 | @r0_2 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 7 | 2 | @r0_6 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 10 | 0 | @r0_9 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 10 | 1 | @m0_5 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 11 | 2 | @r0_10 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 12 | 9 | @r0_8 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 12 | 10 | this:@r0_7 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 12 | 11 | @r0_11 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 13 | 1 | @r0_2 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 14 | 2 | @r0_13 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 17 | 0 | @r0_16 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 17 | 1 | @m0_5 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 18 | 2 | @r0_17 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 19 | 9 | @r0_15 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 19 | 10 | this:@r0_14 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 19 | 11 | @r0_18 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 21 | 1 | @r0_2 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 22 | 0 | @r0_20 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 22 | 1 | @r0_21 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 24 | 0 | @r0_23 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 24 | 5 | @m0_22 | +| Derived::operator=(const Derived &) -> Derived & | 0 | 25 | 0 | @mu* | +| Derived::~Derived() -> void | 0 | 4 | 2 | @r0_2 | +| Derived::~Derived() -> void | 0 | 6 | 9 | @r0_5 | +| Derived::~Derived() -> void | 0 | 6 | 10 | this:@r0_4 | +| Derived::~Derived() -> void | 0 | 7 | 2 | @r0_2 | +| Derived::~Derived() -> void | 0 | 9 | 9 | @r0_8 | +| Derived::~Derived() -> void | 0 | 9 | 10 | this:@r0_7 | +| Derived::~Derived() -> void | 0 | 11 | 0 | @mu* | +| DerivedVB::DerivedVB() -> void | 0 | 3 | 2 | @r0_2 | +| DerivedVB::DerivedVB() -> void | 0 | 5 | 9 | @r0_4 | +| DerivedVB::DerivedVB() -> void | 0 | 5 | 10 | this:@r0_3 | +| DerivedVB::DerivedVB() -> void | 0 | 6 | 2 | @r0_2 | +| DerivedVB::DerivedVB() -> void | 0 | 8 | 9 | @r0_7 | +| DerivedVB::DerivedVB() -> void | 0 | 8 | 10 | this:@r0_6 | +| DerivedVB::DerivedVB() -> void | 0 | 9 | 2 | @r0_2 | +| DerivedVB::DerivedVB() -> void | 0 | 11 | 9 | @r0_10 | +| DerivedVB::DerivedVB() -> void | 0 | 11 | 10 | this:@r0_9 | +| DerivedVB::DerivedVB() -> void | 0 | 12 | 2 | @r0_2 | +| DerivedVB::DerivedVB() -> void | 0 | 14 | 9 | @r0_13 | +| DerivedVB::DerivedVB() -> void | 0 | 14 | 10 | this:@r0_12 | +| DerivedVB::DerivedVB() -> void | 0 | 17 | 0 | @mu* | +| DerivedVB::~DerivedVB() -> void | 0 | 4 | 2 | @r0_2 | +| DerivedVB::~DerivedVB() -> void | 0 | 6 | 9 | @r0_5 | +| DerivedVB::~DerivedVB() -> void | 0 | 6 | 10 | this:@r0_4 | +| DerivedVB::~DerivedVB() -> void | 0 | 7 | 2 | @r0_2 | +| DerivedVB::~DerivedVB() -> void | 0 | 9 | 9 | @r0_8 | +| DerivedVB::~DerivedVB() -> void | 0 | 9 | 10 | this:@r0_7 | +| DerivedVB::~DerivedVB() -> void | 0 | 10 | 2 | @r0_2 | +| DerivedVB::~DerivedVB() -> void | 0 | 12 | 9 | @r0_11 | +| DerivedVB::~DerivedVB() -> void | 0 | 12 | 10 | this:@r0_10 | +| DerivedVB::~DerivedVB() -> void | 0 | 13 | 2 | @r0_2 | +| DerivedVB::~DerivedVB() -> void | 0 | 15 | 9 | @r0_14 | +| DerivedVB::~DerivedVB() -> void | 0 | 15 | 10 | this:@r0_13 | +| DerivedVB::~DerivedVB() -> void | 0 | 17 | 0 | @mu* | +| DoStatements(int) -> void | 0 | 4 | 0 | @r0_3 | +| DoStatements(int) -> void | 0 | 4 | 1 | @r0_2 | +| DoStatements(int) -> void | 1 | 0 | 11 | from 0: @m0_4 | +| DoStatements(int) -> void | 1 | 0 | 11 | from 1: @m1_5 | +| DoStatements(int) -> void | 1 | 3 | 0 | @r1_2 | +| DoStatements(int) -> void | 1 | 3 | 1 | @m1_0 | +| DoStatements(int) -> void | 1 | 4 | 3 | @r1_3 | +| DoStatements(int) -> void | 1 | 4 | 4 | @r1_1 | +| DoStatements(int) -> void | 1 | 5 | 0 | @r1_2 | +| DoStatements(int) -> void | 1 | 5 | 1 | @r1_4 | +| DoStatements(int) -> void | 1 | 7 | 0 | @r1_6 | +| DoStatements(int) -> void | 1 | 7 | 1 | @m1_5 | +| DoStatements(int) -> void | 1 | 9 | 3 | @r1_7 | +| DoStatements(int) -> void | 1 | 9 | 4 | @r1_8 | +| DoStatements(int) -> void | 1 | 10 | 7 | @r1_9 | +| DoStatements(int) -> void | 2 | 2 | 0 | @mu* | +| DynamicCast() -> void | 0 | 4 | 9 | @r0_3 | +| DynamicCast() -> void | 0 | 4 | 10 | this:@r0_2 | +| DynamicCast() -> void | 0 | 7 | 9 | @r0_6 | +| DynamicCast() -> void | 0 | 7 | 10 | this:@r0_5 | +| DynamicCast() -> void | 0 | 10 | 0 | @r0_8 | +| DynamicCast() -> void | 0 | 10 | 1 | @r0_9 | +| DynamicCast() -> void | 0 | 13 | 0 | @r0_11 | +| DynamicCast() -> void | 0 | 13 | 1 | @r0_12 | +| DynamicCast() -> void | 0 | 15 | 0 | @r0_14 | +| DynamicCast() -> void | 0 | 15 | 1 | @m0_13 | +| DynamicCast() -> void | 0 | 16 | 2 | @r0_15 | +| DynamicCast() -> void | 0 | 18 | 0 | @r0_17 | +| DynamicCast() -> void | 0 | 18 | 1 | @r0_16 | +| DynamicCast() -> void | 0 | 21 | 2 | @r0_20 | +| DynamicCast() -> void | 0 | 22 | 0 | @r0_19 | +| DynamicCast() -> void | 0 | 22 | 1 | @r0_21 | +| DynamicCast() -> void | 0 | 24 | 0 | @r0_23 | +| DynamicCast() -> void | 0 | 24 | 1 | @m0_18 | +| DynamicCast() -> void | 0 | 25 | 2 | @r0_24 | +| DynamicCast() -> void | 0 | 27 | 0 | @r0_26 | +| DynamicCast() -> void | 0 | 27 | 1 | @r0_25 | +| DynamicCast() -> void | 0 | 30 | 2 | @r0_29 | +| DynamicCast() -> void | 0 | 31 | 0 | @r0_28 | +| DynamicCast() -> void | 0 | 31 | 1 | @r0_30 | +| DynamicCast() -> void | 0 | 34 | 0 | @r0_33 | +| DynamicCast() -> void | 0 | 34 | 1 | @m0_18 | +| DynamicCast() -> void | 0 | 35 | 2 | @r0_34 | +| DynamicCast() -> void | 0 | 36 | 0 | @r0_32 | +| DynamicCast() -> void | 0 | 36 | 1 | @r0_35 | +| DynamicCast() -> void | 0 | 39 | 0 | @r0_38 | +| DynamicCast() -> void | 0 | 39 | 1 | @m0_27 | +| DynamicCast() -> void | 0 | 40 | 2 | @r0_39 | +| DynamicCast() -> void | 0 | 41 | 0 | @r0_37 | +| DynamicCast() -> void | 0 | 41 | 1 | @r0_40 | +| DynamicCast() -> void | 0 | 44 | 0 | @mu* | +| EarlyReturn(int, int) -> void | 0 | 4 | 0 | @r0_3 | +| EarlyReturn(int, int) -> void | 0 | 4 | 1 | @r0_2 | +| EarlyReturn(int, int) -> void | 0 | 7 | 0 | @r0_6 | +| EarlyReturn(int, int) -> void | 0 | 7 | 1 | @r0_5 | +| EarlyReturn(int, int) -> void | 0 | 9 | 0 | @r0_8 | +| EarlyReturn(int, int) -> void | 0 | 9 | 1 | @m0_4 | +| EarlyReturn(int, int) -> void | 0 | 11 | 0 | @r0_10 | +| EarlyReturn(int, int) -> void | 0 | 11 | 1 | @m0_7 | +| EarlyReturn(int, int) -> void | 0 | 12 | 3 | @r0_9 | +| EarlyReturn(int, int) -> void | 0 | 12 | 4 | @r0_11 | +| EarlyReturn(int, int) -> void | 0 | 13 | 7 | @r0_12 | +| EarlyReturn(int, int) -> void | 1 | 1 | 0 | @mu* | +| EarlyReturn(int, int) -> void | 3 | 1 | 0 | @r3_0 | +| EarlyReturn(int, int) -> void | 3 | 1 | 1 | @m0_4 | +| EarlyReturn(int, int) -> void | 3 | 3 | 0 | @r3_2 | +| EarlyReturn(int, int) -> void | 3 | 3 | 1 | @r3_1 | +| EarlyReturnValue(int, int) -> int | 0 | 4 | 0 | @r0_3 | +| EarlyReturnValue(int, int) -> int | 0 | 4 | 1 | @r0_2 | +| EarlyReturnValue(int, int) -> int | 0 | 7 | 0 | @r0_6 | +| EarlyReturnValue(int, int) -> int | 0 | 7 | 1 | @r0_5 | +| EarlyReturnValue(int, int) -> int | 0 | 9 | 0 | @r0_8 | +| EarlyReturnValue(int, int) -> int | 0 | 9 | 1 | @m0_4 | +| EarlyReturnValue(int, int) -> int | 0 | 11 | 0 | @r0_10 | +| EarlyReturnValue(int, int) -> int | 0 | 11 | 1 | @m0_7 | +| EarlyReturnValue(int, int) -> int | 0 | 12 | 3 | @r0_9 | +| EarlyReturnValue(int, int) -> int | 0 | 12 | 4 | @r0_11 | +| EarlyReturnValue(int, int) -> int | 0 | 13 | 7 | @r0_12 | +| EarlyReturnValue(int, int) -> int | 1 | 0 | 11 | from 2: @m2_3 | +| EarlyReturnValue(int, int) -> int | 1 | 0 | 11 | from 3: @m3_6 | +| EarlyReturnValue(int, int) -> int | 1 | 2 | 0 | @r1_1 | +| EarlyReturnValue(int, int) -> int | 1 | 2 | 5 | @m1_0 | +| EarlyReturnValue(int, int) -> int | 1 | 3 | 0 | @mu* | +| EarlyReturnValue(int, int) -> int | 2 | 2 | 0 | @r2_1 | +| EarlyReturnValue(int, int) -> int | 2 | 2 | 1 | @m0_4 | +| EarlyReturnValue(int, int) -> int | 2 | 3 | 0 | @r2_0 | +| EarlyReturnValue(int, int) -> int | 2 | 3 | 1 | @r2_2 | +| EarlyReturnValue(int, int) -> int | 3 | 2 | 0 | @r3_1 | +| EarlyReturnValue(int, int) -> int | 3 | 2 | 1 | @m0_4 | +| EarlyReturnValue(int, int) -> int | 3 | 4 | 0 | @r3_3 | +| EarlyReturnValue(int, int) -> int | 3 | 4 | 1 | @m0_7 | +| EarlyReturnValue(int, int) -> int | 3 | 5 | 3 | @r3_2 | +| EarlyReturnValue(int, int) -> int | 3 | 5 | 4 | @r3_4 | +| EarlyReturnValue(int, int) -> int | 3 | 6 | 0 | @r3_0 | +| EarlyReturnValue(int, int) -> int | 3 | 6 | 1 | @r3_5 | +| EnumSwitch(E) -> int | 0 | 4 | 0 | @r0_3 | +| EnumSwitch(E) -> int | 0 | 4 | 1 | @r0_2 | +| EnumSwitch(E) -> int | 0 | 6 | 0 | @r0_5 | +| EnumSwitch(E) -> int | 0 | 6 | 1 | @m0_4 | +| EnumSwitch(E) -> int | 0 | 7 | 2 | @r0_6 | +| EnumSwitch(E) -> int | 0 | 8 | 7 | @r0_7 | +| EnumSwitch(E) -> int | 1 | 0 | 11 | from 2: @m2_3 | +| EnumSwitch(E) -> int | 1 | 0 | 11 | from 3: @m3_3 | +| EnumSwitch(E) -> int | 1 | 0 | 11 | from 4: @m4_3 | +| EnumSwitch(E) -> int | 1 | 2 | 0 | @r1_1 | +| EnumSwitch(E) -> int | 1 | 2 | 5 | @m1_0 | +| EnumSwitch(E) -> int | 1 | 3 | 0 | @mu* | +| EnumSwitch(E) -> int | 2 | 3 | 0 | @r2_1 | +| EnumSwitch(E) -> int | 2 | 3 | 1 | @r2_2 | +| EnumSwitch(E) -> int | 3 | 3 | 0 | @r3_1 | +| EnumSwitch(E) -> int | 3 | 3 | 1 | @r3_2 | +| EnumSwitch(E) -> int | 4 | 3 | 0 | @r4_1 | +| EnumSwitch(E) -> int | 4 | 3 | 1 | @r4_2 | +| FieldAccess() -> void | 0 | 4 | 0 | @r0_2 | +| FieldAccess() -> void | 0 | 4 | 1 | @r0_3 | +| FieldAccess() -> void | 0 | 7 | 2 | @r0_6 | +| FieldAccess() -> void | 0 | 8 | 0 | @r0_7 | +| FieldAccess() -> void | 0 | 8 | 1 | @r0_5 | +| FieldAccess() -> void | 0 | 10 | 2 | @r0_9 | +| FieldAccess() -> void | 0 | 11 | 0 | @r0_10 | +| FieldAccess() -> void | 0 | 11 | 1 | @mu0_1 | +| FieldAccess() -> void | 0 | 13 | 2 | @r0_12 | +| FieldAccess() -> void | 0 | 14 | 0 | @r0_13 | +| FieldAccess() -> void | 0 | 14 | 1 | @r0_11 | +| FieldAccess() -> void | 0 | 17 | 2 | @r0_16 | +| FieldAccess() -> void | 0 | 18 | 0 | @r0_15 | +| FieldAccess() -> void | 0 | 18 | 1 | @r0_17 | +| FieldAccess() -> void | 0 | 21 | 0 | @mu* | +| FloatCompare(double, double) -> void | 0 | 4 | 0 | @r0_3 | +| FloatCompare(double, double) -> void | 0 | 4 | 1 | @r0_2 | +| FloatCompare(double, double) -> void | 0 | 7 | 0 | @r0_6 | +| FloatCompare(double, double) -> void | 0 | 7 | 1 | @r0_5 | +| FloatCompare(double, double) -> void | 0 | 10 | 0 | @r0_8 | +| FloatCompare(double, double) -> void | 0 | 10 | 1 | @r0_9 | +| FloatCompare(double, double) -> void | 0 | 12 | 0 | @r0_11 | +| FloatCompare(double, double) -> void | 0 | 12 | 1 | @m0_4 | +| FloatCompare(double, double) -> void | 0 | 14 | 0 | @r0_13 | +| FloatCompare(double, double) -> void | 0 | 14 | 1 | @m0_7 | +| FloatCompare(double, double) -> void | 0 | 15 | 3 | @r0_12 | +| FloatCompare(double, double) -> void | 0 | 15 | 4 | @r0_14 | +| FloatCompare(double, double) -> void | 0 | 17 | 0 | @r0_16 | +| FloatCompare(double, double) -> void | 0 | 17 | 1 | @r0_15 | +| FloatCompare(double, double) -> void | 0 | 19 | 0 | @r0_18 | +| FloatCompare(double, double) -> void | 0 | 19 | 1 | @m0_4 | +| FloatCompare(double, double) -> void | 0 | 21 | 0 | @r0_20 | +| FloatCompare(double, double) -> void | 0 | 21 | 1 | @m0_7 | +| FloatCompare(double, double) -> void | 0 | 22 | 3 | @r0_19 | +| FloatCompare(double, double) -> void | 0 | 22 | 4 | @r0_21 | +| FloatCompare(double, double) -> void | 0 | 24 | 0 | @r0_23 | +| FloatCompare(double, double) -> void | 0 | 24 | 1 | @r0_22 | +| FloatCompare(double, double) -> void | 0 | 26 | 0 | @r0_25 | +| FloatCompare(double, double) -> void | 0 | 26 | 1 | @m0_4 | +| FloatCompare(double, double) -> void | 0 | 28 | 0 | @r0_27 | +| FloatCompare(double, double) -> void | 0 | 28 | 1 | @m0_7 | +| FloatCompare(double, double) -> void | 0 | 29 | 3 | @r0_26 | +| FloatCompare(double, double) -> void | 0 | 29 | 4 | @r0_28 | +| FloatCompare(double, double) -> void | 0 | 31 | 0 | @r0_30 | +| FloatCompare(double, double) -> void | 0 | 31 | 1 | @r0_29 | +| FloatCompare(double, double) -> void | 0 | 33 | 0 | @r0_32 | +| FloatCompare(double, double) -> void | 0 | 33 | 1 | @m0_4 | +| FloatCompare(double, double) -> void | 0 | 35 | 0 | @r0_34 | +| FloatCompare(double, double) -> void | 0 | 35 | 1 | @m0_7 | +| FloatCompare(double, double) -> void | 0 | 36 | 3 | @r0_33 | +| FloatCompare(double, double) -> void | 0 | 36 | 4 | @r0_35 | +| FloatCompare(double, double) -> void | 0 | 38 | 0 | @r0_37 | +| FloatCompare(double, double) -> void | 0 | 38 | 1 | @r0_36 | +| FloatCompare(double, double) -> void | 0 | 40 | 0 | @r0_39 | +| FloatCompare(double, double) -> void | 0 | 40 | 1 | @m0_4 | +| FloatCompare(double, double) -> void | 0 | 42 | 0 | @r0_41 | +| FloatCompare(double, double) -> void | 0 | 42 | 1 | @m0_7 | +| FloatCompare(double, double) -> void | 0 | 43 | 3 | @r0_40 | +| FloatCompare(double, double) -> void | 0 | 43 | 4 | @r0_42 | +| FloatCompare(double, double) -> void | 0 | 45 | 0 | @r0_44 | +| FloatCompare(double, double) -> void | 0 | 45 | 1 | @r0_43 | +| FloatCompare(double, double) -> void | 0 | 47 | 0 | @r0_46 | +| FloatCompare(double, double) -> void | 0 | 47 | 1 | @m0_4 | +| FloatCompare(double, double) -> void | 0 | 49 | 0 | @r0_48 | +| FloatCompare(double, double) -> void | 0 | 49 | 1 | @m0_7 | +| FloatCompare(double, double) -> void | 0 | 50 | 3 | @r0_47 | +| FloatCompare(double, double) -> void | 0 | 50 | 4 | @r0_49 | +| FloatCompare(double, double) -> void | 0 | 52 | 0 | @r0_51 | +| FloatCompare(double, double) -> void | 0 | 52 | 1 | @r0_50 | +| FloatCompare(double, double) -> void | 0 | 55 | 0 | @mu* | +| FloatCrement(float) -> void | 0 | 4 | 0 | @r0_3 | +| FloatCrement(float) -> void | 0 | 4 | 1 | @r0_2 | +| FloatCrement(float) -> void | 0 | 7 | 0 | @r0_5 | +| FloatCrement(float) -> void | 0 | 7 | 1 | @r0_6 | +| FloatCrement(float) -> void | 0 | 9 | 0 | @r0_8 | +| FloatCrement(float) -> void | 0 | 9 | 1 | @m0_4 | +| FloatCrement(float) -> void | 0 | 11 | 3 | @r0_9 | +| FloatCrement(float) -> void | 0 | 11 | 4 | @r0_10 | +| FloatCrement(float) -> void | 0 | 12 | 0 | @r0_8 | +| FloatCrement(float) -> void | 0 | 12 | 1 | @r0_11 | +| FloatCrement(float) -> void | 0 | 14 | 0 | @r0_13 | +| FloatCrement(float) -> void | 0 | 14 | 1 | @r0_11 | +| FloatCrement(float) -> void | 0 | 16 | 0 | @r0_15 | +| FloatCrement(float) -> void | 0 | 16 | 1 | @m0_12 | +| FloatCrement(float) -> void | 0 | 18 | 3 | @r0_16 | +| FloatCrement(float) -> void | 0 | 18 | 4 | @r0_17 | +| FloatCrement(float) -> void | 0 | 19 | 0 | @r0_15 | +| FloatCrement(float) -> void | 0 | 19 | 1 | @r0_18 | +| FloatCrement(float) -> void | 0 | 21 | 0 | @r0_20 | +| FloatCrement(float) -> void | 0 | 21 | 1 | @r0_18 | +| FloatCrement(float) -> void | 0 | 23 | 0 | @r0_22 | +| FloatCrement(float) -> void | 0 | 23 | 1 | @m0_19 | +| FloatCrement(float) -> void | 0 | 25 | 3 | @r0_23 | +| FloatCrement(float) -> void | 0 | 25 | 4 | @r0_24 | +| FloatCrement(float) -> void | 0 | 26 | 0 | @r0_22 | +| FloatCrement(float) -> void | 0 | 26 | 1 | @r0_25 | +| FloatCrement(float) -> void | 0 | 28 | 0 | @r0_27 | +| FloatCrement(float) -> void | 0 | 28 | 1 | @r0_23 | +| FloatCrement(float) -> void | 0 | 30 | 0 | @r0_29 | +| FloatCrement(float) -> void | 0 | 30 | 1 | @m0_26 | +| FloatCrement(float) -> void | 0 | 32 | 3 | @r0_30 | +| FloatCrement(float) -> void | 0 | 32 | 4 | @r0_31 | +| FloatCrement(float) -> void | 0 | 33 | 0 | @r0_29 | +| FloatCrement(float) -> void | 0 | 33 | 1 | @r0_32 | +| FloatCrement(float) -> void | 0 | 35 | 0 | @r0_34 | +| FloatCrement(float) -> void | 0 | 35 | 1 | @r0_30 | +| FloatCrement(float) -> void | 0 | 38 | 0 | @mu* | +| FloatOps(double, double) -> void | 0 | 4 | 0 | @r0_3 | +| FloatOps(double, double) -> void | 0 | 4 | 1 | @r0_2 | +| FloatOps(double, double) -> void | 0 | 7 | 0 | @r0_6 | +| FloatOps(double, double) -> void | 0 | 7 | 1 | @r0_5 | +| FloatOps(double, double) -> void | 0 | 10 | 0 | @r0_8 | +| FloatOps(double, double) -> void | 0 | 10 | 1 | @r0_9 | +| FloatOps(double, double) -> void | 0 | 12 | 0 | @r0_11 | +| FloatOps(double, double) -> void | 0 | 12 | 1 | @m0_4 | +| FloatOps(double, double) -> void | 0 | 14 | 0 | @r0_13 | +| FloatOps(double, double) -> void | 0 | 14 | 1 | @m0_7 | +| FloatOps(double, double) -> void | 0 | 15 | 3 | @r0_12 | +| FloatOps(double, double) -> void | 0 | 15 | 4 | @r0_14 | +| FloatOps(double, double) -> void | 0 | 17 | 0 | @r0_16 | +| FloatOps(double, double) -> void | 0 | 17 | 1 | @r0_15 | +| FloatOps(double, double) -> void | 0 | 19 | 0 | @r0_18 | +| FloatOps(double, double) -> void | 0 | 19 | 1 | @m0_4 | +| FloatOps(double, double) -> void | 0 | 21 | 0 | @r0_20 | +| FloatOps(double, double) -> void | 0 | 21 | 1 | @m0_7 | +| FloatOps(double, double) -> void | 0 | 22 | 3 | @r0_19 | +| FloatOps(double, double) -> void | 0 | 22 | 4 | @r0_21 | +| FloatOps(double, double) -> void | 0 | 24 | 0 | @r0_23 | +| FloatOps(double, double) -> void | 0 | 24 | 1 | @r0_22 | +| FloatOps(double, double) -> void | 0 | 26 | 0 | @r0_25 | +| FloatOps(double, double) -> void | 0 | 26 | 1 | @m0_4 | +| FloatOps(double, double) -> void | 0 | 28 | 0 | @r0_27 | +| FloatOps(double, double) -> void | 0 | 28 | 1 | @m0_7 | +| FloatOps(double, double) -> void | 0 | 29 | 3 | @r0_26 | +| FloatOps(double, double) -> void | 0 | 29 | 4 | @r0_28 | +| FloatOps(double, double) -> void | 0 | 31 | 0 | @r0_30 | +| FloatOps(double, double) -> void | 0 | 31 | 1 | @r0_29 | +| FloatOps(double, double) -> void | 0 | 33 | 0 | @r0_32 | +| FloatOps(double, double) -> void | 0 | 33 | 1 | @m0_4 | +| FloatOps(double, double) -> void | 0 | 35 | 0 | @r0_34 | +| FloatOps(double, double) -> void | 0 | 35 | 1 | @m0_7 | +| FloatOps(double, double) -> void | 0 | 36 | 3 | @r0_33 | +| FloatOps(double, double) -> void | 0 | 36 | 4 | @r0_35 | +| FloatOps(double, double) -> void | 0 | 38 | 0 | @r0_37 | +| FloatOps(double, double) -> void | 0 | 38 | 1 | @r0_36 | +| FloatOps(double, double) -> void | 0 | 40 | 0 | @r0_39 | +| FloatOps(double, double) -> void | 0 | 40 | 1 | @m0_4 | +| FloatOps(double, double) -> void | 0 | 42 | 0 | @r0_41 | +| FloatOps(double, double) -> void | 0 | 42 | 1 | @r0_40 | +| FloatOps(double, double) -> void | 0 | 44 | 0 | @r0_43 | +| FloatOps(double, double) -> void | 0 | 44 | 1 | @m0_4 | +| FloatOps(double, double) -> void | 0 | 46 | 0 | @r0_45 | +| FloatOps(double, double) -> void | 0 | 46 | 1 | @m0_42 | +| FloatOps(double, double) -> void | 0 | 47 | 3 | @r0_46 | +| FloatOps(double, double) -> void | 0 | 47 | 4 | @r0_44 | +| FloatOps(double, double) -> void | 0 | 48 | 0 | @r0_45 | +| FloatOps(double, double) -> void | 0 | 48 | 1 | @r0_47 | +| FloatOps(double, double) -> void | 0 | 50 | 0 | @r0_49 | +| FloatOps(double, double) -> void | 0 | 50 | 1 | @m0_4 | +| FloatOps(double, double) -> void | 0 | 52 | 0 | @r0_51 | +| FloatOps(double, double) -> void | 0 | 52 | 1 | @m0_48 | +| FloatOps(double, double) -> void | 0 | 53 | 3 | @r0_52 | +| FloatOps(double, double) -> void | 0 | 53 | 4 | @r0_50 | +| FloatOps(double, double) -> void | 0 | 54 | 0 | @r0_51 | +| FloatOps(double, double) -> void | 0 | 54 | 1 | @r0_53 | +| FloatOps(double, double) -> void | 0 | 56 | 0 | @r0_55 | +| FloatOps(double, double) -> void | 0 | 56 | 1 | @m0_4 | +| FloatOps(double, double) -> void | 0 | 58 | 0 | @r0_57 | +| FloatOps(double, double) -> void | 0 | 58 | 1 | @m0_54 | +| FloatOps(double, double) -> void | 0 | 59 | 3 | @r0_58 | +| FloatOps(double, double) -> void | 0 | 59 | 4 | @r0_56 | +| FloatOps(double, double) -> void | 0 | 60 | 0 | @r0_57 | +| FloatOps(double, double) -> void | 0 | 60 | 1 | @r0_59 | +| FloatOps(double, double) -> void | 0 | 62 | 0 | @r0_61 | +| FloatOps(double, double) -> void | 0 | 62 | 1 | @m0_4 | +| FloatOps(double, double) -> void | 0 | 64 | 0 | @r0_63 | +| FloatOps(double, double) -> void | 0 | 64 | 1 | @m0_60 | +| FloatOps(double, double) -> void | 0 | 65 | 3 | @r0_64 | +| FloatOps(double, double) -> void | 0 | 65 | 4 | @r0_62 | +| FloatOps(double, double) -> void | 0 | 66 | 0 | @r0_63 | +| FloatOps(double, double) -> void | 0 | 66 | 1 | @r0_65 | +| FloatOps(double, double) -> void | 0 | 68 | 0 | @r0_67 | +| FloatOps(double, double) -> void | 0 | 68 | 1 | @m0_4 | +| FloatOps(double, double) -> void | 0 | 69 | 1 | @r0_68 | +| FloatOps(double, double) -> void | 0 | 71 | 0 | @r0_70 | +| FloatOps(double, double) -> void | 0 | 71 | 1 | @r0_69 | +| FloatOps(double, double) -> void | 0 | 73 | 0 | @r0_72 | +| FloatOps(double, double) -> void | 0 | 73 | 1 | @m0_4 | +| FloatOps(double, double) -> void | 0 | 74 | 2 | @r0_73 | +| FloatOps(double, double) -> void | 0 | 76 | 0 | @r0_75 | +| FloatOps(double, double) -> void | 0 | 76 | 1 | @r0_74 | +| FloatOps(double, double) -> void | 0 | 79 | 0 | @mu* | +| Foo() -> void | 0 | 4 | 0 | @r0_2 | +| Foo() -> void | 0 | 4 | 1 | @r0_3 | +| Foo() -> void | 0 | 7 | 0 | @r0_5 | +| Foo() -> void | 0 | 7 | 1 | @r0_6 | +| Foo() -> void | 0 | 9 | 0 | @r0_8 | +| Foo() -> void | 0 | 9 | 1 | @m0_4 | +| Foo() -> void | 0 | 11 | 0 | @r0_10 | +| Foo() -> void | 0 | 11 | 1 | @m0_7 | +| Foo() -> void | 0 | 12 | 2 | @r0_11 | +| Foo() -> void | 0 | 13 | 3 | @r0_9 | +| Foo() -> void | 0 | 13 | 4 | @r0_12 | +| Foo() -> void | 0 | 14 | 2 | @r0_13 | +| Foo() -> void | 0 | 16 | 0 | @r0_15 | +| Foo() -> void | 0 | 16 | 1 | @r0_14 | +| Foo() -> void | 0 | 18 | 0 | @r0_17 | +| Foo() -> void | 0 | 18 | 1 | @m0_4 | +| Foo() -> void | 0 | 20 | 0 | @r0_19 | +| Foo() -> void | 0 | 20 | 1 | @m0_16 | +| Foo() -> void | 0 | 21 | 2 | @r0_20 | +| Foo() -> void | 0 | 22 | 3 | @r0_18 | +| Foo() -> void | 0 | 22 | 4 | @r0_21 | +| Foo() -> void | 0 | 24 | 0 | @r0_23 | +| Foo() -> void | 0 | 24 | 1 | @r0_22 | +| Foo() -> void | 0 | 27 | 0 | @mu* | +| For_Break() -> void | 0 | 4 | 0 | @r0_2 | +| For_Break() -> void | 0 | 4 | 1 | @r0_3 | +| For_Break() -> void | 1 | 0 | 11 | from 0: @m0_4 | +| For_Break() -> void | 1 | 0 | 11 | from 2: @m2_4 | +| For_Break() -> void | 1 | 2 | 0 | @r1_1 | +| For_Break() -> void | 1 | 2 | 1 | @m1_0 | +| For_Break() -> void | 1 | 4 | 3 | @r1_2 | +| For_Break() -> void | 1 | 4 | 4 | @r1_3 | +| For_Break() -> void | 1 | 5 | 7 | @r1_4 | +| For_Break() -> void | 2 | 2 | 0 | @r2_1 | +| For_Break() -> void | 2 | 2 | 1 | @m1_0 | +| For_Break() -> void | 2 | 3 | 3 | @r2_2 | +| For_Break() -> void | 2 | 3 | 4 | @r2_0 | +| For_Break() -> void | 2 | 4 | 0 | @r2_1 | +| For_Break() -> void | 2 | 4 | 1 | @r2_3 | +| For_Break() -> void | 3 | 1 | 0 | @r3_0 | +| For_Break() -> void | 3 | 1 | 1 | @m1_0 | +| For_Break() -> void | 3 | 3 | 3 | @r3_1 | +| For_Break() -> void | 3 | 3 | 4 | @r3_2 | +| For_Break() -> void | 3 | 4 | 7 | @r3_3 | +| For_Break() -> void | 5 | 3 | 0 | @mu* | +| For_Condition() -> void | 0 | 4 | 0 | @r0_2 | +| For_Condition() -> void | 0 | 4 | 1 | @r0_3 | +| For_Condition() -> void | 1 | 1 | 0 | @r1_0 | +| For_Condition() -> void | 1 | 1 | 1 | @m0_4 | +| For_Condition() -> void | 1 | 3 | 3 | @r1_1 | +| For_Condition() -> void | 1 | 3 | 4 | @r1_2 | +| For_Condition() -> void | 1 | 4 | 7 | @r1_3 | +| For_Condition() -> void | 3 | 2 | 0 | @mu* | +| For_ConditionUpdate() -> void | 0 | 4 | 0 | @r0_2 | +| For_ConditionUpdate() -> void | 0 | 4 | 1 | @r0_3 | +| For_ConditionUpdate() -> void | 1 | 0 | 11 | from 0: @m0_4 | +| For_ConditionUpdate() -> void | 1 | 0 | 11 | from 2: @m2_5 | +| For_ConditionUpdate() -> void | 1 | 2 | 0 | @r1_1 | +| For_ConditionUpdate() -> void | 1 | 2 | 1 | @m1_0 | +| For_ConditionUpdate() -> void | 1 | 4 | 3 | @r1_2 | +| For_ConditionUpdate() -> void | 1 | 4 | 4 | @r1_3 | +| For_ConditionUpdate() -> void | 1 | 5 | 7 | @r1_4 | +| For_ConditionUpdate() -> void | 2 | 3 | 0 | @r2_2 | +| For_ConditionUpdate() -> void | 2 | 3 | 1 | @m1_0 | +| For_ConditionUpdate() -> void | 2 | 4 | 3 | @r2_3 | +| For_ConditionUpdate() -> void | 2 | 4 | 4 | @r2_1 | +| For_ConditionUpdate() -> void | 2 | 5 | 0 | @r2_2 | +| For_ConditionUpdate() -> void | 2 | 5 | 1 | @r2_4 | +| For_ConditionUpdate() -> void | 3 | 2 | 0 | @mu* | +| For_Continue_NoUpdate() -> void | 0 | 4 | 0 | @r0_2 | +| For_Continue_NoUpdate() -> void | 0 | 4 | 1 | @r0_3 | +| For_Continue_NoUpdate() -> void | 1 | 1 | 0 | @r1_0 | +| For_Continue_NoUpdate() -> void | 1 | 1 | 1 | @m0_4 | +| For_Continue_NoUpdate() -> void | 1 | 3 | 3 | @r1_1 | +| For_Continue_NoUpdate() -> void | 1 | 3 | 4 | @r1_2 | +| For_Continue_NoUpdate() -> void | 1 | 4 | 7 | @r1_3 | +| For_Continue_NoUpdate() -> void | 2 | 1 | 0 | @r2_0 | +| For_Continue_NoUpdate() -> void | 2 | 1 | 1 | @m0_4 | +| For_Continue_NoUpdate() -> void | 2 | 3 | 3 | @r2_1 | +| For_Continue_NoUpdate() -> void | 2 | 3 | 4 | @r2_2 | +| For_Continue_NoUpdate() -> void | 2 | 4 | 7 | @r2_3 | +| For_Continue_NoUpdate() -> void | 5 | 2 | 0 | @mu* | +| For_Continue_Update() -> void | 0 | 4 | 0 | @r0_2 | +| For_Continue_Update() -> void | 0 | 4 | 1 | @r0_3 | +| For_Continue_Update() -> void | 1 | 0 | 11 | from 0: @m0_4 | +| For_Continue_Update() -> void | 1 | 0 | 11 | from 4: @m4_5 | +| For_Continue_Update() -> void | 1 | 2 | 0 | @r1_1 | +| For_Continue_Update() -> void | 1 | 2 | 1 | @m1_0 | +| For_Continue_Update() -> void | 1 | 4 | 3 | @r1_2 | +| For_Continue_Update() -> void | 1 | 4 | 4 | @r1_3 | +| For_Continue_Update() -> void | 1 | 5 | 7 | @r1_4 | +| For_Continue_Update() -> void | 2 | 1 | 0 | @r2_0 | +| For_Continue_Update() -> void | 2 | 1 | 1 | @m1_0 | +| For_Continue_Update() -> void | 2 | 3 | 3 | @r2_1 | +| For_Continue_Update() -> void | 2 | 3 | 4 | @r2_2 | +| For_Continue_Update() -> void | 2 | 4 | 7 | @r2_3 | +| For_Continue_Update() -> void | 4 | 3 | 0 | @r4_2 | +| For_Continue_Update() -> void | 4 | 3 | 1 | @m1_0 | +| For_Continue_Update() -> void | 4 | 4 | 3 | @r4_3 | +| For_Continue_Update() -> void | 4 | 4 | 4 | @r4_1 | +| For_Continue_Update() -> void | 4 | 5 | 0 | @r4_2 | +| For_Continue_Update() -> void | 4 | 5 | 1 | @r4_4 | +| For_Continue_Update() -> void | 5 | 2 | 0 | @mu* | +| For_Empty() -> void | 0 | 4 | 0 | @r0_2 | +| For_Empty() -> void | 0 | 4 | 1 | @r0_3 | +| For_Empty() -> void | 1 | 1 | 0 | @mu* | +| For_Init() -> void | 0 | 4 | 0 | @r0_2 | +| For_Init() -> void | 0 | 4 | 1 | @r0_3 | +| For_Init() -> void | 1 | 1 | 0 | @mu* | +| For_InitCondition() -> void | 0 | 4 | 0 | @r0_2 | +| For_InitCondition() -> void | 0 | 4 | 1 | @r0_3 | +| For_InitCondition() -> void | 1 | 1 | 0 | @r1_0 | +| For_InitCondition() -> void | 1 | 1 | 1 | @m0_4 | +| For_InitCondition() -> void | 1 | 3 | 3 | @r1_1 | +| For_InitCondition() -> void | 1 | 3 | 4 | @r1_2 | +| For_InitCondition() -> void | 1 | 4 | 7 | @r1_3 | +| For_InitCondition() -> void | 3 | 2 | 0 | @mu* | +| For_InitConditionUpdate() -> void | 0 | 4 | 0 | @r0_2 | +| For_InitConditionUpdate() -> void | 0 | 4 | 1 | @r0_3 | +| For_InitConditionUpdate() -> void | 1 | 0 | 11 | from 0: @m0_4 | +| For_InitConditionUpdate() -> void | 1 | 0 | 11 | from 2: @m2_5 | +| For_InitConditionUpdate() -> void | 1 | 2 | 0 | @r1_1 | +| For_InitConditionUpdate() -> void | 1 | 2 | 1 | @m1_0 | +| For_InitConditionUpdate() -> void | 1 | 4 | 3 | @r1_2 | +| For_InitConditionUpdate() -> void | 1 | 4 | 4 | @r1_3 | +| For_InitConditionUpdate() -> void | 1 | 5 | 7 | @r1_4 | +| For_InitConditionUpdate() -> void | 2 | 3 | 0 | @r2_2 | +| For_InitConditionUpdate() -> void | 2 | 3 | 1 | @m1_0 | +| For_InitConditionUpdate() -> void | 2 | 4 | 3 | @r2_3 | +| For_InitConditionUpdate() -> void | 2 | 4 | 4 | @r2_1 | +| For_InitConditionUpdate() -> void | 2 | 5 | 0 | @r2_2 | +| For_InitConditionUpdate() -> void | 2 | 5 | 1 | @r2_4 | +| For_InitConditionUpdate() -> void | 3 | 2 | 0 | @mu* | +| For_InitUpdate() -> void | 0 | 4 | 0 | @r0_2 | +| For_InitUpdate() -> void | 0 | 4 | 1 | @r0_3 | +| For_InitUpdate() -> void | 1 | 1 | 0 | @mu* | +| For_InitUpdate() -> void | 2 | 0 | 11 | from 0: @m0_4 | +| For_InitUpdate() -> void | 2 | 0 | 11 | from 2: @m2_6 | +| For_InitUpdate() -> void | 2 | 4 | 0 | @r2_3 | +| For_InitUpdate() -> void | 2 | 4 | 1 | @m2_0 | +| For_InitUpdate() -> void | 2 | 5 | 3 | @r2_4 | +| For_InitUpdate() -> void | 2 | 5 | 4 | @r2_2 | +| For_InitUpdate() -> void | 2 | 6 | 0 | @r2_3 | +| For_InitUpdate() -> void | 2 | 6 | 1 | @r2_5 | +| For_Update() -> void | 0 | 4 | 0 | @r0_2 | +| For_Update() -> void | 0 | 4 | 1 | @r0_3 | +| For_Update() -> void | 1 | 1 | 0 | @mu* | +| For_Update() -> void | 2 | 0 | 11 | from 0: @m0_4 | +| For_Update() -> void | 2 | 0 | 11 | from 2: @m2_6 | +| For_Update() -> void | 2 | 4 | 0 | @r2_3 | +| For_Update() -> void | 2 | 4 | 1 | @m2_0 | +| For_Update() -> void | 2 | 5 | 3 | @r2_4 | +| For_Update() -> void | 2 | 5 | 4 | @r2_2 | +| For_Update() -> void | 2 | 6 | 0 | @r2_3 | +| For_Update() -> void | 2 | 6 | 1 | @r2_5 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 4 | 0 | @r0_3 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 4 | 1 | @r0_2 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 7 | 0 | @r0_6 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 7 | 1 | @r0_5 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 9 | 0 | @r0_8 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 9 | 1 | @m0_4 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 10 | 2 | @r0_9 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 12 | 0 | @r0_11 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 12 | 1 | @r0_10 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 14 | 0 | @r0_13 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 14 | 1 | @m0_12 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 15 | 2 | @r0_14 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 17 | 0 | @r0_16 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 17 | 1 | @r0_15 | +| FuncPtrConversions(..(*)(..), void *) -> void | 0 | 20 | 0 | @mu* | +| FunctionReferences() -> void | 0 | 4 | 0 | @r0_2 | +| FunctionReferences() -> void | 0 | 4 | 1 | @r0_3 | +| FunctionReferences() -> void | 0 | 7 | 0 | @r0_6 | +| FunctionReferences() -> void | 0 | 7 | 1 | @m0_4 | +| FunctionReferences() -> void | 0 | 8 | 0 | @r0_5 | +| FunctionReferences() -> void | 0 | 8 | 1 | @r0_7 | +| FunctionReferences() -> void | 0 | 10 | 0 | @r0_9 | +| FunctionReferences() -> void | 0 | 10 | 1 | @m0_4 | +| FunctionReferences() -> void | 0 | 12 | 9 | @r0_10 | +| FunctionReferences() -> void | 0 | 12 | 11 | @r0_11 | +| FunctionReferences() -> void | 0 | 15 | 0 | @mu* | +| HierarchyConversions() -> void | 0 | 4 | 9 | @r0_3 | +| HierarchyConversions() -> void | 0 | 4 | 10 | this:@r0_2 | +| HierarchyConversions() -> void | 0 | 7 | 9 | @r0_6 | +| HierarchyConversions() -> void | 0 | 7 | 10 | this:@r0_5 | +| HierarchyConversions() -> void | 0 | 10 | 9 | @r0_9 | +| HierarchyConversions() -> void | 0 | 10 | 10 | this:@r0_8 | +| HierarchyConversions() -> void | 0 | 13 | 0 | @r0_11 | +| HierarchyConversions() -> void | 0 | 13 | 1 | @r0_12 | +| HierarchyConversions() -> void | 0 | 16 | 0 | @r0_14 | +| HierarchyConversions() -> void | 0 | 16 | 1 | @r0_15 | +| HierarchyConversions() -> void | 0 | 19 | 0 | @r0_17 | +| HierarchyConversions() -> void | 0 | 19 | 1 | @r0_18 | +| HierarchyConversions() -> void | 0 | 23 | 2 | @r0_22 | +| HierarchyConversions() -> void | 0 | 24 | 9 | @r0_21 | +| HierarchyConversions() -> void | 0 | 24 | 10 | this:@r0_20 | +| HierarchyConversions() -> void | 0 | 24 | 11 | @r0_23 | +| HierarchyConversions() -> void | 0 | 29 | 2 | @r0_28 | +| HierarchyConversions() -> void | 0 | 30 | 9 | @r0_27 | +| HierarchyConversions() -> void | 0 | 30 | 11 | @r0_29 | +| HierarchyConversions() -> void | 0 | 31 | 2 | @r0_30 | +| HierarchyConversions() -> void | 0 | 32 | 9 | @r0_26 | +| HierarchyConversions() -> void | 0 | 32 | 10 | this:@r0_25 | +| HierarchyConversions() -> void | 0 | 32 | 11 | @r0_31 | +| HierarchyConversions() -> void | 0 | 37 | 2 | @r0_36 | +| HierarchyConversions() -> void | 0 | 38 | 9 | @r0_35 | +| HierarchyConversions() -> void | 0 | 38 | 11 | @r0_37 | +| HierarchyConversions() -> void | 0 | 39 | 2 | @r0_38 | +| HierarchyConversions() -> void | 0 | 40 | 9 | @r0_34 | +| HierarchyConversions() -> void | 0 | 40 | 10 | this:@r0_33 | +| HierarchyConversions() -> void | 0 | 40 | 11 | @r0_39 | +| HierarchyConversions() -> void | 0 | 42 | 0 | @r0_41 | +| HierarchyConversions() -> void | 0 | 42 | 1 | @m0_16 | +| HierarchyConversions() -> void | 0 | 43 | 2 | @r0_42 | +| HierarchyConversions() -> void | 0 | 45 | 0 | @r0_44 | +| HierarchyConversions() -> void | 0 | 45 | 1 | @r0_43 | +| HierarchyConversions() -> void | 0 | 47 | 0 | @r0_46 | +| HierarchyConversions() -> void | 0 | 47 | 1 | @m0_16 | +| HierarchyConversions() -> void | 0 | 48 | 2 | @r0_47 | +| HierarchyConversions() -> void | 0 | 50 | 0 | @r0_49 | +| HierarchyConversions() -> void | 0 | 50 | 1 | @r0_48 | +| HierarchyConversions() -> void | 0 | 52 | 0 | @r0_51 | +| HierarchyConversions() -> void | 0 | 52 | 1 | @m0_16 | +| HierarchyConversions() -> void | 0 | 53 | 2 | @r0_52 | +| HierarchyConversions() -> void | 0 | 55 | 0 | @r0_54 | +| HierarchyConversions() -> void | 0 | 55 | 1 | @r0_53 | +| HierarchyConversions() -> void | 0 | 57 | 0 | @r0_56 | +| HierarchyConversions() -> void | 0 | 57 | 1 | @m0_16 | +| HierarchyConversions() -> void | 0 | 58 | 2 | @r0_57 | +| HierarchyConversions() -> void | 0 | 60 | 0 | @r0_59 | +| HierarchyConversions() -> void | 0 | 60 | 1 | @r0_58 | +| HierarchyConversions() -> void | 0 | 64 | 2 | @r0_63 | +| HierarchyConversions() -> void | 0 | 65 | 2 | @r0_64 | +| HierarchyConversions() -> void | 0 | 66 | 9 | @r0_62 | +| HierarchyConversions() -> void | 0 | 66 | 10 | this:@r0_61 | +| HierarchyConversions() -> void | 0 | 66 | 11 | @r0_65 | +| HierarchyConversions() -> void | 0 | 70 | 2 | @r0_69 | +| HierarchyConversions() -> void | 0 | 71 | 2 | @r0_70 | +| HierarchyConversions() -> void | 0 | 72 | 9 | @r0_68 | +| HierarchyConversions() -> void | 0 | 72 | 10 | this:@r0_67 | +| HierarchyConversions() -> void | 0 | 72 | 11 | @r0_71 | +| HierarchyConversions() -> void | 0 | 74 | 0 | @r0_73 | +| HierarchyConversions() -> void | 0 | 74 | 1 | @m0_60 | +| HierarchyConversions() -> void | 0 | 75 | 2 | @r0_74 | +| HierarchyConversions() -> void | 0 | 77 | 0 | @r0_76 | +| HierarchyConversions() -> void | 0 | 77 | 1 | @r0_75 | +| HierarchyConversions() -> void | 0 | 79 | 0 | @r0_78 | +| HierarchyConversions() -> void | 0 | 79 | 1 | @m0_60 | +| HierarchyConversions() -> void | 0 | 80 | 2 | @r0_79 | +| HierarchyConversions() -> void | 0 | 82 | 0 | @r0_81 | +| HierarchyConversions() -> void | 0 | 82 | 1 | @r0_80 | +| HierarchyConversions() -> void | 0 | 84 | 0 | @r0_83 | +| HierarchyConversions() -> void | 0 | 84 | 1 | @m0_60 | +| HierarchyConversions() -> void | 0 | 85 | 2 | @r0_84 | +| HierarchyConversions() -> void | 0 | 87 | 0 | @r0_86 | +| HierarchyConversions() -> void | 0 | 87 | 1 | @r0_85 | +| HierarchyConversions() -> void | 0 | 91 | 2 | @r0_90 | +| HierarchyConversions() -> void | 0 | 92 | 2 | @r0_91 | +| HierarchyConversions() -> void | 0 | 93 | 9 | @r0_89 | +| HierarchyConversions() -> void | 0 | 93 | 10 | this:@r0_88 | +| HierarchyConversions() -> void | 0 | 93 | 11 | @r0_92 | +| HierarchyConversions() -> void | 0 | 98 | 2 | @r0_97 | +| HierarchyConversions() -> void | 0 | 99 | 2 | @r0_98 | +| HierarchyConversions() -> void | 0 | 100 | 9 | @r0_96 | +| HierarchyConversions() -> void | 0 | 100 | 11 | @r0_99 | +| HierarchyConversions() -> void | 0 | 101 | 2 | @r0_100 | +| HierarchyConversions() -> void | 0 | 102 | 9 | @r0_95 | +| HierarchyConversions() -> void | 0 | 102 | 10 | this:@r0_94 | +| HierarchyConversions() -> void | 0 | 102 | 11 | @r0_101 | +| HierarchyConversions() -> void | 0 | 107 | 2 | @r0_106 | +| HierarchyConversions() -> void | 0 | 108 | 2 | @r0_107 | +| HierarchyConversions() -> void | 0 | 109 | 9 | @r0_105 | +| HierarchyConversions() -> void | 0 | 109 | 11 | @r0_108 | +| HierarchyConversions() -> void | 0 | 110 | 2 | @r0_109 | +| HierarchyConversions() -> void | 0 | 111 | 9 | @r0_104 | +| HierarchyConversions() -> void | 0 | 111 | 10 | this:@r0_103 | +| HierarchyConversions() -> void | 0 | 111 | 11 | @r0_110 | +| HierarchyConversions() -> void | 0 | 113 | 0 | @r0_112 | +| HierarchyConversions() -> void | 0 | 113 | 1 | @m0_19 | +| HierarchyConversions() -> void | 0 | 114 | 2 | @r0_113 | +| HierarchyConversions() -> void | 0 | 115 | 2 | @r0_114 | +| HierarchyConversions() -> void | 0 | 117 | 0 | @r0_116 | +| HierarchyConversions() -> void | 0 | 117 | 1 | @r0_115 | +| HierarchyConversions() -> void | 0 | 119 | 0 | @r0_118 | +| HierarchyConversions() -> void | 0 | 119 | 1 | @m0_19 | +| HierarchyConversions() -> void | 0 | 120 | 2 | @r0_119 | +| HierarchyConversions() -> void | 0 | 121 | 2 | @r0_120 | +| HierarchyConversions() -> void | 0 | 123 | 0 | @r0_122 | +| HierarchyConversions() -> void | 0 | 123 | 1 | @r0_121 | +| HierarchyConversions() -> void | 0 | 125 | 0 | @r0_124 | +| HierarchyConversions() -> void | 0 | 125 | 1 | @m0_19 | +| HierarchyConversions() -> void | 0 | 126 | 2 | @r0_125 | +| HierarchyConversions() -> void | 0 | 127 | 2 | @r0_126 | +| HierarchyConversions() -> void | 0 | 129 | 0 | @r0_128 | +| HierarchyConversions() -> void | 0 | 129 | 1 | @r0_127 | +| HierarchyConversions() -> void | 0 | 131 | 0 | @r0_130 | +| HierarchyConversions() -> void | 0 | 131 | 1 | @m0_19 | +| HierarchyConversions() -> void | 0 | 132 | 2 | @r0_131 | +| HierarchyConversions() -> void | 0 | 134 | 0 | @r0_133 | +| HierarchyConversions() -> void | 0 | 134 | 1 | @r0_132 | +| HierarchyConversions() -> void | 0 | 138 | 2 | @r0_137 | +| HierarchyConversions() -> void | 0 | 139 | 2 | @r0_138 | +| HierarchyConversions() -> void | 0 | 140 | 2 | @r0_139 | +| HierarchyConversions() -> void | 0 | 141 | 9 | @r0_136 | +| HierarchyConversions() -> void | 0 | 141 | 10 | this:@r0_135 | +| HierarchyConversions() -> void | 0 | 141 | 11 | @r0_140 | +| HierarchyConversions() -> void | 0 | 145 | 2 | @r0_144 | +| HierarchyConversions() -> void | 0 | 146 | 2 | @r0_145 | +| HierarchyConversions() -> void | 0 | 147 | 2 | @r0_146 | +| HierarchyConversions() -> void | 0 | 148 | 9 | @r0_143 | +| HierarchyConversions() -> void | 0 | 148 | 10 | this:@r0_142 | +| HierarchyConversions() -> void | 0 | 148 | 11 | @r0_147 | +| HierarchyConversions() -> void | 0 | 150 | 0 | @r0_149 | +| HierarchyConversions() -> void | 0 | 150 | 1 | @m0_134 | +| HierarchyConversions() -> void | 0 | 151 | 2 | @r0_150 | +| HierarchyConversions() -> void | 0 | 152 | 2 | @r0_151 | +| HierarchyConversions() -> void | 0 | 154 | 0 | @r0_153 | +| HierarchyConversions() -> void | 0 | 154 | 1 | @r0_152 | +| HierarchyConversions() -> void | 0 | 156 | 0 | @r0_155 | +| HierarchyConversions() -> void | 0 | 156 | 1 | @m0_134 | +| HierarchyConversions() -> void | 0 | 157 | 2 | @r0_156 | +| HierarchyConversions() -> void | 0 | 158 | 2 | @r0_157 | +| HierarchyConversions() -> void | 0 | 160 | 0 | @r0_159 | +| HierarchyConversions() -> void | 0 | 160 | 1 | @r0_158 | +| HierarchyConversions() -> void | 0 | 162 | 0 | @r0_161 | +| HierarchyConversions() -> void | 0 | 162 | 1 | @m0_134 | +| HierarchyConversions() -> void | 0 | 163 | 2 | @r0_162 | +| HierarchyConversions() -> void | 0 | 165 | 0 | @r0_164 | +| HierarchyConversions() -> void | 0 | 165 | 1 | @r0_163 | +| HierarchyConversions() -> void | 0 | 168 | 0 | @r0_166 | +| HierarchyConversions() -> void | 0 | 168 | 1 | @r0_167 | +| HierarchyConversions() -> void | 0 | 171 | 0 | @r0_169 | +| HierarchyConversions() -> void | 0 | 171 | 1 | @r0_170 | +| HierarchyConversions() -> void | 0 | 173 | 0 | @r0_172 | +| HierarchyConversions() -> void | 0 | 173 | 1 | @m0_168 | +| HierarchyConversions() -> void | 0 | 174 | 2 | @r0_173 | +| HierarchyConversions() -> void | 0 | 176 | 0 | @r0_175 | +| HierarchyConversions() -> void | 0 | 176 | 1 | @r0_174 | +| HierarchyConversions() -> void | 0 | 178 | 0 | @r0_177 | +| HierarchyConversions() -> void | 0 | 178 | 1 | @m0_171 | +| HierarchyConversions() -> void | 0 | 179 | 2 | @r0_178 | +| HierarchyConversions() -> void | 0 | 181 | 0 | @r0_180 | +| HierarchyConversions() -> void | 0 | 181 | 1 | @r0_179 | +| HierarchyConversions() -> void | 0 | 184 | 0 | @mu* | +| IfStatements(bool, int, int) -> void | 0 | 4 | 0 | @r0_3 | +| IfStatements(bool, int, int) -> void | 0 | 4 | 1 | @r0_2 | +| IfStatements(bool, int, int) -> void | 0 | 7 | 0 | @r0_6 | +| IfStatements(bool, int, int) -> void | 0 | 7 | 1 | @r0_5 | +| IfStatements(bool, int, int) -> void | 0 | 10 | 0 | @r0_9 | +| IfStatements(bool, int, int) -> void | 0 | 10 | 1 | @r0_8 | +| IfStatements(bool, int, int) -> void | 0 | 12 | 0 | @r0_11 | +| IfStatements(bool, int, int) -> void | 0 | 12 | 1 | @m0_4 | +| IfStatements(bool, int, int) -> void | 0 | 13 | 7 | @r0_12 | +| IfStatements(bool, int, int) -> void | 1 | 1 | 0 | @r1_0 | +| IfStatements(bool, int, int) -> void | 1 | 1 | 1 | @m0_4 | +| IfStatements(bool, int, int) -> void | 1 | 2 | 7 | @r1_1 | +| IfStatements(bool, int, int) -> void | 2 | 1 | 0 | @r2_0 | +| IfStatements(bool, int, int) -> void | 2 | 1 | 1 | @m0_10 | +| IfStatements(bool, int, int) -> void | 2 | 3 | 0 | @r2_2 | +| IfStatements(bool, int, int) -> void | 2 | 3 | 1 | @r2_1 | +| IfStatements(bool, int, int) -> void | 3 | 0 | 11 | from 1: @m0_7 | +| IfStatements(bool, int, int) -> void | 3 | 0 | 11 | from 2: @m2_3 | +| IfStatements(bool, int, int) -> void | 3 | 2 | 0 | @r3_1 | +| IfStatements(bool, int, int) -> void | 3 | 2 | 1 | @m3_0 | +| IfStatements(bool, int, int) -> void | 3 | 4 | 3 | @r3_2 | +| IfStatements(bool, int, int) -> void | 3 | 4 | 4 | @r3_3 | +| IfStatements(bool, int, int) -> void | 3 | 5 | 7 | @r3_4 | +| IfStatements(bool, int, int) -> void | 4 | 2 | 0 | @r4_1 | +| IfStatements(bool, int, int) -> void | 4 | 2 | 1 | @r4_0 | +| IfStatements(bool, int, int) -> void | 5 | 2 | 0 | @r5_1 | +| IfStatements(bool, int, int) -> void | 5 | 2 | 1 | @r5_0 | +| IfStatements(bool, int, int) -> void | 6 | 2 | 0 | @mu* | +| InitArray() -> void | 0 | 4 | 0 | @r0_3 | +| InitArray() -> void | 0 | 4 | 1 | @mu0_1 | +| InitArray() -> void | 0 | 5 | 0 | @r0_2 | +| InitArray() -> void | 0 | 5 | 1 | @r0_4 | +| InitArray() -> void | 0 | 8 | 3 | @r0_2 | +| InitArray() -> void | 0 | 8 | 4 | @r0_7 | +| InitArray() -> void | 0 | 9 | 0 | @r0_8 | +| InitArray() -> void | 0 | 9 | 1 | @r0_6 | +| InitArray() -> void | 0 | 12 | 0 | @r0_11 | +| InitArray() -> void | 0 | 12 | 1 | @mu0_1 | +| InitArray() -> void | 0 | 13 | 0 | @r0_10 | +| InitArray() -> void | 0 | 13 | 1 | @r0_12 | +| InitArray() -> void | 0 | 16 | 0 | @r0_15 | +| InitArray() -> void | 0 | 16 | 1 | @mu0_1 | +| InitArray() -> void | 0 | 17 | 0 | @r0_14 | +| InitArray() -> void | 0 | 17 | 1 | @r0_16 | +| InitArray() -> void | 0 | 20 | 0 | @r0_18 | +| InitArray() -> void | 0 | 20 | 1 | @r0_19 | +| InitArray() -> void | 0 | 23 | 3 | @r0_21 | +| InitArray() -> void | 0 | 23 | 4 | @r0_22 | +| InitArray() -> void | 0 | 25 | 0 | @r0_23 | +| InitArray() -> void | 0 | 25 | 1 | @r0_24 | +| InitArray() -> void | 0 | 28 | 3 | @r0_26 | +| InitArray() -> void | 0 | 28 | 4 | @r0_27 | +| InitArray() -> void | 0 | 30 | 0 | @r0_28 | +| InitArray() -> void | 0 | 30 | 1 | @r0_29 | +| InitArray() -> void | 0 | 32 | 3 | @r0_26 | +| InitArray() -> void | 0 | 32 | 4 | @r0_31 | +| InitArray() -> void | 0 | 34 | 0 | @r0_32 | +| InitArray() -> void | 0 | 34 | 1 | @r0_33 | +| InitArray() -> void | 0 | 37 | 3 | @r0_35 | +| InitArray() -> void | 0 | 37 | 4 | @r0_36 | +| InitArray() -> void | 0 | 39 | 0 | @r0_37 | +| InitArray() -> void | 0 | 39 | 1 | @r0_38 | +| InitArray() -> void | 0 | 41 | 3 | @r0_35 | +| InitArray() -> void | 0 | 41 | 4 | @r0_40 | +| InitArray() -> void | 0 | 43 | 0 | @r0_41 | +| InitArray() -> void | 0 | 43 | 1 | @r0_42 | +| InitArray() -> void | 0 | 46 | 3 | @r0_44 | +| InitArray() -> void | 0 | 46 | 4 | @r0_45 | +| InitArray() -> void | 0 | 48 | 0 | @r0_46 | +| InitArray() -> void | 0 | 48 | 1 | @r0_47 | +| InitArray() -> void | 0 | 50 | 3 | @r0_44 | +| InitArray() -> void | 0 | 50 | 4 | @r0_49 | +| InitArray() -> void | 0 | 52 | 0 | @r0_50 | +| InitArray() -> void | 0 | 52 | 1 | @r0_51 | +| InitArray() -> void | 0 | 55 | 0 | @mu* | +| InitList(int, float) -> void | 0 | 4 | 0 | @r0_3 | +| InitList(int, float) -> void | 0 | 4 | 1 | @r0_2 | +| InitList(int, float) -> void | 0 | 7 | 0 | @r0_6 | +| InitList(int, float) -> void | 0 | 7 | 1 | @r0_5 | +| InitList(int, float) -> void | 0 | 9 | 2 | @r0_8 | +| InitList(int, float) -> void | 0 | 11 | 0 | @r0_10 | +| InitList(int, float) -> void | 0 | 11 | 1 | @m0_4 | +| InitList(int, float) -> void | 0 | 12 | 0 | @r0_9 | +| InitList(int, float) -> void | 0 | 12 | 1 | @r0_11 | +| InitList(int, float) -> void | 0 | 13 | 2 | @r0_8 | +| InitList(int, float) -> void | 0 | 15 | 0 | @r0_14 | +| InitList(int, float) -> void | 0 | 15 | 1 | @m0_7 | +| InitList(int, float) -> void | 0 | 16 | 2 | @r0_15 | +| InitList(int, float) -> void | 0 | 17 | 0 | @r0_13 | +| InitList(int, float) -> void | 0 | 17 | 1 | @r0_16 | +| InitList(int, float) -> void | 0 | 19 | 2 | @r0_18 | +| InitList(int, float) -> void | 0 | 21 | 0 | @r0_20 | +| InitList(int, float) -> void | 0 | 21 | 1 | @m0_4 | +| InitList(int, float) -> void | 0 | 22 | 0 | @r0_19 | +| InitList(int, float) -> void | 0 | 22 | 1 | @r0_21 | +| InitList(int, float) -> void | 0 | 23 | 2 | @r0_18 | +| InitList(int, float) -> void | 0 | 25 | 0 | @r0_23 | +| InitList(int, float) -> void | 0 | 25 | 1 | @r0_24 | +| InitList(int, float) -> void | 0 | 27 | 2 | @r0_26 | +| InitList(int, float) -> void | 0 | 29 | 0 | @r0_27 | +| InitList(int, float) -> void | 0 | 29 | 1 | @r0_28 | +| InitList(int, float) -> void | 0 | 30 | 2 | @r0_26 | +| InitList(int, float) -> void | 0 | 32 | 0 | @r0_30 | +| InitList(int, float) -> void | 0 | 32 | 1 | @r0_31 | +| InitList(int, float) -> void | 0 | 35 | 0 | @r0_33 | +| InitList(int, float) -> void | 0 | 35 | 1 | @r0_34 | +| InitList(int, float) -> void | 0 | 38 | 0 | @r0_36 | +| InitList(int, float) -> void | 0 | 38 | 1 | @r0_37 | +| InitList(int, float) -> void | 0 | 41 | 0 | @mu* | +| InitReference(int) -> void | 0 | 4 | 0 | @r0_3 | +| InitReference(int) -> void | 0 | 4 | 1 | @r0_2 | +| InitReference(int) -> void | 0 | 7 | 0 | @r0_5 | +| InitReference(int) -> void | 0 | 7 | 1 | @r0_6 | +| InitReference(int) -> void | 0 | 10 | 0 | @r0_9 | +| InitReference(int) -> void | 0 | 10 | 1 | @m0_7 | +| InitReference(int) -> void | 0 | 11 | 0 | @r0_8 | +| InitReference(int) -> void | 0 | 11 | 1 | @r0_10 | +| InitReference(int) -> void | 0 | 14 | 9 | @r0_13 | +| InitReference(int) -> void | 0 | 15 | 2 | @r0_14 | +| InitReference(int) -> void | 0 | 16 | 0 | @r0_12 | +| InitReference(int) -> void | 0 | 16 | 1 | @r0_15 | +| InitReference(int) -> void | 0 | 19 | 0 | @mu* | +| IntegerCompare(int, int) -> void | 0 | 4 | 0 | @r0_3 | +| IntegerCompare(int, int) -> void | 0 | 4 | 1 | @r0_2 | +| IntegerCompare(int, int) -> void | 0 | 7 | 0 | @r0_6 | +| IntegerCompare(int, int) -> void | 0 | 7 | 1 | @r0_5 | +| IntegerCompare(int, int) -> void | 0 | 10 | 0 | @r0_8 | +| IntegerCompare(int, int) -> void | 0 | 10 | 1 | @r0_9 | +| IntegerCompare(int, int) -> void | 0 | 12 | 0 | @r0_11 | +| IntegerCompare(int, int) -> void | 0 | 12 | 1 | @m0_4 | +| IntegerCompare(int, int) -> void | 0 | 14 | 0 | @r0_13 | +| IntegerCompare(int, int) -> void | 0 | 14 | 1 | @m0_7 | +| IntegerCompare(int, int) -> void | 0 | 15 | 3 | @r0_12 | +| IntegerCompare(int, int) -> void | 0 | 15 | 4 | @r0_14 | +| IntegerCompare(int, int) -> void | 0 | 17 | 0 | @r0_16 | +| IntegerCompare(int, int) -> void | 0 | 17 | 1 | @r0_15 | +| IntegerCompare(int, int) -> void | 0 | 19 | 0 | @r0_18 | +| IntegerCompare(int, int) -> void | 0 | 19 | 1 | @m0_4 | +| IntegerCompare(int, int) -> void | 0 | 21 | 0 | @r0_20 | +| IntegerCompare(int, int) -> void | 0 | 21 | 1 | @m0_7 | +| IntegerCompare(int, int) -> void | 0 | 22 | 3 | @r0_19 | +| IntegerCompare(int, int) -> void | 0 | 22 | 4 | @r0_21 | +| IntegerCompare(int, int) -> void | 0 | 24 | 0 | @r0_23 | +| IntegerCompare(int, int) -> void | 0 | 24 | 1 | @r0_22 | +| IntegerCompare(int, int) -> void | 0 | 26 | 0 | @r0_25 | +| IntegerCompare(int, int) -> void | 0 | 26 | 1 | @m0_4 | +| IntegerCompare(int, int) -> void | 0 | 28 | 0 | @r0_27 | +| IntegerCompare(int, int) -> void | 0 | 28 | 1 | @m0_7 | +| IntegerCompare(int, int) -> void | 0 | 29 | 3 | @r0_26 | +| IntegerCompare(int, int) -> void | 0 | 29 | 4 | @r0_28 | +| IntegerCompare(int, int) -> void | 0 | 31 | 0 | @r0_30 | +| IntegerCompare(int, int) -> void | 0 | 31 | 1 | @r0_29 | +| IntegerCompare(int, int) -> void | 0 | 33 | 0 | @r0_32 | +| IntegerCompare(int, int) -> void | 0 | 33 | 1 | @m0_4 | +| IntegerCompare(int, int) -> void | 0 | 35 | 0 | @r0_34 | +| IntegerCompare(int, int) -> void | 0 | 35 | 1 | @m0_7 | +| IntegerCompare(int, int) -> void | 0 | 36 | 3 | @r0_33 | +| IntegerCompare(int, int) -> void | 0 | 36 | 4 | @r0_35 | +| IntegerCompare(int, int) -> void | 0 | 38 | 0 | @r0_37 | +| IntegerCompare(int, int) -> void | 0 | 38 | 1 | @r0_36 | +| IntegerCompare(int, int) -> void | 0 | 40 | 0 | @r0_39 | +| IntegerCompare(int, int) -> void | 0 | 40 | 1 | @m0_4 | +| IntegerCompare(int, int) -> void | 0 | 42 | 0 | @r0_41 | +| IntegerCompare(int, int) -> void | 0 | 42 | 1 | @m0_7 | +| IntegerCompare(int, int) -> void | 0 | 43 | 3 | @r0_40 | +| IntegerCompare(int, int) -> void | 0 | 43 | 4 | @r0_42 | +| IntegerCompare(int, int) -> void | 0 | 45 | 0 | @r0_44 | +| IntegerCompare(int, int) -> void | 0 | 45 | 1 | @r0_43 | +| IntegerCompare(int, int) -> void | 0 | 47 | 0 | @r0_46 | +| IntegerCompare(int, int) -> void | 0 | 47 | 1 | @m0_4 | +| IntegerCompare(int, int) -> void | 0 | 49 | 0 | @r0_48 | +| IntegerCompare(int, int) -> void | 0 | 49 | 1 | @m0_7 | +| IntegerCompare(int, int) -> void | 0 | 50 | 3 | @r0_47 | +| IntegerCompare(int, int) -> void | 0 | 50 | 4 | @r0_49 | +| IntegerCompare(int, int) -> void | 0 | 52 | 0 | @r0_51 | +| IntegerCompare(int, int) -> void | 0 | 52 | 1 | @r0_50 | +| IntegerCompare(int, int) -> void | 0 | 55 | 0 | @mu* | +| IntegerCrement(int) -> void | 0 | 4 | 0 | @r0_3 | +| IntegerCrement(int) -> void | 0 | 4 | 1 | @r0_2 | +| IntegerCrement(int) -> void | 0 | 7 | 0 | @r0_5 | +| IntegerCrement(int) -> void | 0 | 7 | 1 | @r0_6 | +| IntegerCrement(int) -> void | 0 | 9 | 0 | @r0_8 | +| IntegerCrement(int) -> void | 0 | 9 | 1 | @m0_4 | +| IntegerCrement(int) -> void | 0 | 11 | 3 | @r0_9 | +| IntegerCrement(int) -> void | 0 | 11 | 4 | @r0_10 | +| IntegerCrement(int) -> void | 0 | 12 | 0 | @r0_8 | +| IntegerCrement(int) -> void | 0 | 12 | 1 | @r0_11 | +| IntegerCrement(int) -> void | 0 | 14 | 0 | @r0_13 | +| IntegerCrement(int) -> void | 0 | 14 | 1 | @r0_11 | +| IntegerCrement(int) -> void | 0 | 16 | 0 | @r0_15 | +| IntegerCrement(int) -> void | 0 | 16 | 1 | @m0_12 | +| IntegerCrement(int) -> void | 0 | 18 | 3 | @r0_16 | +| IntegerCrement(int) -> void | 0 | 18 | 4 | @r0_17 | +| IntegerCrement(int) -> void | 0 | 19 | 0 | @r0_15 | +| IntegerCrement(int) -> void | 0 | 19 | 1 | @r0_18 | +| IntegerCrement(int) -> void | 0 | 21 | 0 | @r0_20 | +| IntegerCrement(int) -> void | 0 | 21 | 1 | @r0_18 | +| IntegerCrement(int) -> void | 0 | 23 | 0 | @r0_22 | +| IntegerCrement(int) -> void | 0 | 23 | 1 | @m0_19 | +| IntegerCrement(int) -> void | 0 | 25 | 3 | @r0_23 | +| IntegerCrement(int) -> void | 0 | 25 | 4 | @r0_24 | +| IntegerCrement(int) -> void | 0 | 26 | 0 | @r0_22 | +| IntegerCrement(int) -> void | 0 | 26 | 1 | @r0_25 | +| IntegerCrement(int) -> void | 0 | 28 | 0 | @r0_27 | +| IntegerCrement(int) -> void | 0 | 28 | 1 | @r0_23 | +| IntegerCrement(int) -> void | 0 | 30 | 0 | @r0_29 | +| IntegerCrement(int) -> void | 0 | 30 | 1 | @m0_26 | +| IntegerCrement(int) -> void | 0 | 32 | 3 | @r0_30 | +| IntegerCrement(int) -> void | 0 | 32 | 4 | @r0_31 | +| IntegerCrement(int) -> void | 0 | 33 | 0 | @r0_29 | +| IntegerCrement(int) -> void | 0 | 33 | 1 | @r0_32 | +| IntegerCrement(int) -> void | 0 | 35 | 0 | @r0_34 | +| IntegerCrement(int) -> void | 0 | 35 | 1 | @r0_30 | +| IntegerCrement(int) -> void | 0 | 38 | 0 | @mu* | +| IntegerCrement_LValue(int) -> void | 0 | 4 | 0 | @r0_3 | +| IntegerCrement_LValue(int) -> void | 0 | 4 | 1 | @r0_2 | +| IntegerCrement_LValue(int) -> void | 0 | 7 | 0 | @r0_5 | +| IntegerCrement_LValue(int) -> void | 0 | 7 | 1 | @r0_6 | +| IntegerCrement_LValue(int) -> void | 0 | 9 | 0 | @r0_8 | +| IntegerCrement_LValue(int) -> void | 0 | 9 | 1 | @mu0_1 | +| IntegerCrement_LValue(int) -> void | 0 | 11 | 3 | @r0_9 | +| IntegerCrement_LValue(int) -> void | 0 | 11 | 4 | @r0_10 | +| IntegerCrement_LValue(int) -> void | 0 | 12 | 0 | @r0_8 | +| IntegerCrement_LValue(int) -> void | 0 | 12 | 1 | @r0_11 | +| IntegerCrement_LValue(int) -> void | 0 | 14 | 0 | @r0_13 | +| IntegerCrement_LValue(int) -> void | 0 | 14 | 1 | @r0_8 | +| IntegerCrement_LValue(int) -> void | 0 | 16 | 0 | @r0_15 | +| IntegerCrement_LValue(int) -> void | 0 | 16 | 1 | @mu0_1 | +| IntegerCrement_LValue(int) -> void | 0 | 18 | 3 | @r0_16 | +| IntegerCrement_LValue(int) -> void | 0 | 18 | 4 | @r0_17 | +| IntegerCrement_LValue(int) -> void | 0 | 19 | 0 | @r0_15 | +| IntegerCrement_LValue(int) -> void | 0 | 19 | 1 | @r0_18 | +| IntegerCrement_LValue(int) -> void | 0 | 21 | 0 | @r0_20 | +| IntegerCrement_LValue(int) -> void | 0 | 21 | 1 | @r0_15 | +| IntegerCrement_LValue(int) -> void | 0 | 24 | 0 | @mu* | +| IntegerOps(int, int) -> void | 0 | 4 | 0 | @r0_3 | +| IntegerOps(int, int) -> void | 0 | 4 | 1 | @r0_2 | +| IntegerOps(int, int) -> void | 0 | 7 | 0 | @r0_6 | +| IntegerOps(int, int) -> void | 0 | 7 | 1 | @r0_5 | +| IntegerOps(int, int) -> void | 0 | 10 | 0 | @r0_8 | +| IntegerOps(int, int) -> void | 0 | 10 | 1 | @r0_9 | +| IntegerOps(int, int) -> void | 0 | 12 | 0 | @r0_11 | +| IntegerOps(int, int) -> void | 0 | 12 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 14 | 0 | @r0_13 | +| IntegerOps(int, int) -> void | 0 | 14 | 1 | @m0_7 | +| IntegerOps(int, int) -> void | 0 | 15 | 3 | @r0_12 | +| IntegerOps(int, int) -> void | 0 | 15 | 4 | @r0_14 | +| IntegerOps(int, int) -> void | 0 | 17 | 0 | @r0_16 | +| IntegerOps(int, int) -> void | 0 | 17 | 1 | @r0_15 | +| IntegerOps(int, int) -> void | 0 | 19 | 0 | @r0_18 | +| IntegerOps(int, int) -> void | 0 | 19 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 21 | 0 | @r0_20 | +| IntegerOps(int, int) -> void | 0 | 21 | 1 | @m0_7 | +| IntegerOps(int, int) -> void | 0 | 22 | 3 | @r0_19 | +| IntegerOps(int, int) -> void | 0 | 22 | 4 | @r0_21 | +| IntegerOps(int, int) -> void | 0 | 24 | 0 | @r0_23 | +| IntegerOps(int, int) -> void | 0 | 24 | 1 | @r0_22 | +| IntegerOps(int, int) -> void | 0 | 26 | 0 | @r0_25 | +| IntegerOps(int, int) -> void | 0 | 26 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 28 | 0 | @r0_27 | +| IntegerOps(int, int) -> void | 0 | 28 | 1 | @m0_7 | +| IntegerOps(int, int) -> void | 0 | 29 | 3 | @r0_26 | +| IntegerOps(int, int) -> void | 0 | 29 | 4 | @r0_28 | +| IntegerOps(int, int) -> void | 0 | 31 | 0 | @r0_30 | +| IntegerOps(int, int) -> void | 0 | 31 | 1 | @r0_29 | +| IntegerOps(int, int) -> void | 0 | 33 | 0 | @r0_32 | +| IntegerOps(int, int) -> void | 0 | 33 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 35 | 0 | @r0_34 | +| IntegerOps(int, int) -> void | 0 | 35 | 1 | @m0_7 | +| IntegerOps(int, int) -> void | 0 | 36 | 3 | @r0_33 | +| IntegerOps(int, int) -> void | 0 | 36 | 4 | @r0_35 | +| IntegerOps(int, int) -> void | 0 | 38 | 0 | @r0_37 | +| IntegerOps(int, int) -> void | 0 | 38 | 1 | @r0_36 | +| IntegerOps(int, int) -> void | 0 | 40 | 0 | @r0_39 | +| IntegerOps(int, int) -> void | 0 | 40 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 42 | 0 | @r0_41 | +| IntegerOps(int, int) -> void | 0 | 42 | 1 | @m0_7 | +| IntegerOps(int, int) -> void | 0 | 43 | 3 | @r0_40 | +| IntegerOps(int, int) -> void | 0 | 43 | 4 | @r0_42 | +| IntegerOps(int, int) -> void | 0 | 45 | 0 | @r0_44 | +| IntegerOps(int, int) -> void | 0 | 45 | 1 | @r0_43 | +| IntegerOps(int, int) -> void | 0 | 47 | 0 | @r0_46 | +| IntegerOps(int, int) -> void | 0 | 47 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 49 | 0 | @r0_48 | +| IntegerOps(int, int) -> void | 0 | 49 | 1 | @m0_7 | +| IntegerOps(int, int) -> void | 0 | 50 | 3 | @r0_47 | +| IntegerOps(int, int) -> void | 0 | 50 | 4 | @r0_49 | +| IntegerOps(int, int) -> void | 0 | 52 | 0 | @r0_51 | +| IntegerOps(int, int) -> void | 0 | 52 | 1 | @r0_50 | +| IntegerOps(int, int) -> void | 0 | 54 | 0 | @r0_53 | +| IntegerOps(int, int) -> void | 0 | 54 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 56 | 0 | @r0_55 | +| IntegerOps(int, int) -> void | 0 | 56 | 1 | @m0_7 | +| IntegerOps(int, int) -> void | 0 | 57 | 3 | @r0_54 | +| IntegerOps(int, int) -> void | 0 | 57 | 4 | @r0_56 | +| IntegerOps(int, int) -> void | 0 | 59 | 0 | @r0_58 | +| IntegerOps(int, int) -> void | 0 | 59 | 1 | @r0_57 | +| IntegerOps(int, int) -> void | 0 | 61 | 0 | @r0_60 | +| IntegerOps(int, int) -> void | 0 | 61 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 63 | 0 | @r0_62 | +| IntegerOps(int, int) -> void | 0 | 63 | 1 | @m0_7 | +| IntegerOps(int, int) -> void | 0 | 64 | 3 | @r0_61 | +| IntegerOps(int, int) -> void | 0 | 64 | 4 | @r0_63 | +| IntegerOps(int, int) -> void | 0 | 66 | 0 | @r0_65 | +| IntegerOps(int, int) -> void | 0 | 66 | 1 | @r0_64 | +| IntegerOps(int, int) -> void | 0 | 68 | 0 | @r0_67 | +| IntegerOps(int, int) -> void | 0 | 68 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 70 | 0 | @r0_69 | +| IntegerOps(int, int) -> void | 0 | 70 | 1 | @m0_7 | +| IntegerOps(int, int) -> void | 0 | 71 | 3 | @r0_68 | +| IntegerOps(int, int) -> void | 0 | 71 | 4 | @r0_70 | +| IntegerOps(int, int) -> void | 0 | 73 | 0 | @r0_72 | +| IntegerOps(int, int) -> void | 0 | 73 | 1 | @r0_71 | +| IntegerOps(int, int) -> void | 0 | 75 | 0 | @r0_74 | +| IntegerOps(int, int) -> void | 0 | 75 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 77 | 0 | @r0_76 | +| IntegerOps(int, int) -> void | 0 | 77 | 1 | @m0_7 | +| IntegerOps(int, int) -> void | 0 | 78 | 3 | @r0_75 | +| IntegerOps(int, int) -> void | 0 | 78 | 4 | @r0_77 | +| IntegerOps(int, int) -> void | 0 | 80 | 0 | @r0_79 | +| IntegerOps(int, int) -> void | 0 | 80 | 1 | @r0_78 | +| IntegerOps(int, int) -> void | 0 | 82 | 0 | @r0_81 | +| IntegerOps(int, int) -> void | 0 | 82 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 84 | 0 | @r0_83 | +| IntegerOps(int, int) -> void | 0 | 84 | 1 | @r0_82 | +| IntegerOps(int, int) -> void | 0 | 86 | 0 | @r0_85 | +| IntegerOps(int, int) -> void | 0 | 86 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 88 | 0 | @r0_87 | +| IntegerOps(int, int) -> void | 0 | 88 | 1 | @m0_84 | +| IntegerOps(int, int) -> void | 0 | 89 | 3 | @r0_88 | +| IntegerOps(int, int) -> void | 0 | 89 | 4 | @r0_86 | +| IntegerOps(int, int) -> void | 0 | 90 | 0 | @r0_87 | +| IntegerOps(int, int) -> void | 0 | 90 | 1 | @r0_89 | +| IntegerOps(int, int) -> void | 0 | 92 | 0 | @r0_91 | +| IntegerOps(int, int) -> void | 0 | 92 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 94 | 0 | @r0_93 | +| IntegerOps(int, int) -> void | 0 | 94 | 1 | @m0_90 | +| IntegerOps(int, int) -> void | 0 | 95 | 3 | @r0_94 | +| IntegerOps(int, int) -> void | 0 | 95 | 4 | @r0_92 | +| IntegerOps(int, int) -> void | 0 | 96 | 0 | @r0_93 | +| IntegerOps(int, int) -> void | 0 | 96 | 1 | @r0_95 | +| IntegerOps(int, int) -> void | 0 | 98 | 0 | @r0_97 | +| IntegerOps(int, int) -> void | 0 | 98 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 100 | 0 | @r0_99 | +| IntegerOps(int, int) -> void | 0 | 100 | 1 | @m0_96 | +| IntegerOps(int, int) -> void | 0 | 101 | 3 | @r0_100 | +| IntegerOps(int, int) -> void | 0 | 101 | 4 | @r0_98 | +| IntegerOps(int, int) -> void | 0 | 102 | 0 | @r0_99 | +| IntegerOps(int, int) -> void | 0 | 102 | 1 | @r0_101 | +| IntegerOps(int, int) -> void | 0 | 104 | 0 | @r0_103 | +| IntegerOps(int, int) -> void | 0 | 104 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 106 | 0 | @r0_105 | +| IntegerOps(int, int) -> void | 0 | 106 | 1 | @m0_102 | +| IntegerOps(int, int) -> void | 0 | 107 | 3 | @r0_106 | +| IntegerOps(int, int) -> void | 0 | 107 | 4 | @r0_104 | +| IntegerOps(int, int) -> void | 0 | 108 | 0 | @r0_105 | +| IntegerOps(int, int) -> void | 0 | 108 | 1 | @r0_107 | +| IntegerOps(int, int) -> void | 0 | 110 | 0 | @r0_109 | +| IntegerOps(int, int) -> void | 0 | 110 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 112 | 0 | @r0_111 | +| IntegerOps(int, int) -> void | 0 | 112 | 1 | @m0_108 | +| IntegerOps(int, int) -> void | 0 | 113 | 3 | @r0_112 | +| IntegerOps(int, int) -> void | 0 | 113 | 4 | @r0_110 | +| IntegerOps(int, int) -> void | 0 | 114 | 0 | @r0_111 | +| IntegerOps(int, int) -> void | 0 | 114 | 1 | @r0_113 | +| IntegerOps(int, int) -> void | 0 | 116 | 0 | @r0_115 | +| IntegerOps(int, int) -> void | 0 | 116 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 118 | 0 | @r0_117 | +| IntegerOps(int, int) -> void | 0 | 118 | 1 | @m0_114 | +| IntegerOps(int, int) -> void | 0 | 119 | 3 | @r0_118 | +| IntegerOps(int, int) -> void | 0 | 119 | 4 | @r0_116 | +| IntegerOps(int, int) -> void | 0 | 120 | 0 | @r0_117 | +| IntegerOps(int, int) -> void | 0 | 120 | 1 | @r0_119 | +| IntegerOps(int, int) -> void | 0 | 122 | 0 | @r0_121 | +| IntegerOps(int, int) -> void | 0 | 122 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 124 | 0 | @r0_123 | +| IntegerOps(int, int) -> void | 0 | 124 | 1 | @m0_120 | +| IntegerOps(int, int) -> void | 0 | 125 | 3 | @r0_124 | +| IntegerOps(int, int) -> void | 0 | 125 | 4 | @r0_122 | +| IntegerOps(int, int) -> void | 0 | 126 | 0 | @r0_123 | +| IntegerOps(int, int) -> void | 0 | 126 | 1 | @r0_125 | +| IntegerOps(int, int) -> void | 0 | 128 | 0 | @r0_127 | +| IntegerOps(int, int) -> void | 0 | 128 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 130 | 0 | @r0_129 | +| IntegerOps(int, int) -> void | 0 | 130 | 1 | @m0_126 | +| IntegerOps(int, int) -> void | 0 | 131 | 3 | @r0_130 | +| IntegerOps(int, int) -> void | 0 | 131 | 4 | @r0_128 | +| IntegerOps(int, int) -> void | 0 | 132 | 0 | @r0_129 | +| IntegerOps(int, int) -> void | 0 | 132 | 1 | @r0_131 | +| IntegerOps(int, int) -> void | 0 | 134 | 0 | @r0_133 | +| IntegerOps(int, int) -> void | 0 | 134 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 136 | 0 | @r0_135 | +| IntegerOps(int, int) -> void | 0 | 136 | 1 | @m0_132 | +| IntegerOps(int, int) -> void | 0 | 137 | 3 | @r0_136 | +| IntegerOps(int, int) -> void | 0 | 137 | 4 | @r0_134 | +| IntegerOps(int, int) -> void | 0 | 138 | 0 | @r0_135 | +| IntegerOps(int, int) -> void | 0 | 138 | 1 | @r0_137 | +| IntegerOps(int, int) -> void | 0 | 140 | 0 | @r0_139 | +| IntegerOps(int, int) -> void | 0 | 140 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 142 | 0 | @r0_141 | +| IntegerOps(int, int) -> void | 0 | 142 | 1 | @m0_138 | +| IntegerOps(int, int) -> void | 0 | 143 | 3 | @r0_142 | +| IntegerOps(int, int) -> void | 0 | 143 | 4 | @r0_140 | +| IntegerOps(int, int) -> void | 0 | 144 | 0 | @r0_141 | +| IntegerOps(int, int) -> void | 0 | 144 | 1 | @r0_143 | +| IntegerOps(int, int) -> void | 0 | 146 | 0 | @r0_145 | +| IntegerOps(int, int) -> void | 0 | 146 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 147 | 1 | @r0_146 | +| IntegerOps(int, int) -> void | 0 | 149 | 0 | @r0_148 | +| IntegerOps(int, int) -> void | 0 | 149 | 1 | @r0_147 | +| IntegerOps(int, int) -> void | 0 | 151 | 0 | @r0_150 | +| IntegerOps(int, int) -> void | 0 | 151 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 152 | 2 | @r0_151 | +| IntegerOps(int, int) -> void | 0 | 154 | 0 | @r0_153 | +| IntegerOps(int, int) -> void | 0 | 154 | 1 | @r0_152 | +| IntegerOps(int, int) -> void | 0 | 156 | 0 | @r0_155 | +| IntegerOps(int, int) -> void | 0 | 156 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 157 | 2 | @r0_156 | +| IntegerOps(int, int) -> void | 0 | 159 | 0 | @r0_158 | +| IntegerOps(int, int) -> void | 0 | 159 | 1 | @r0_157 | +| IntegerOps(int, int) -> void | 0 | 161 | 0 | @r0_160 | +| IntegerOps(int, int) -> void | 0 | 161 | 1 | @m0_4 | +| IntegerOps(int, int) -> void | 0 | 163 | 3 | @r0_161 | +| IntegerOps(int, int) -> void | 0 | 163 | 4 | @r0_162 | +| IntegerOps(int, int) -> void | 0 | 164 | 2 | @r0_163 | +| IntegerOps(int, int) -> void | 0 | 165 | 2 | @r0_164 | +| IntegerOps(int, int) -> void | 0 | 167 | 0 | @r0_166 | +| IntegerOps(int, int) -> void | 0 | 167 | 1 | @r0_165 | +| IntegerOps(int, int) -> void | 0 | 170 | 0 | @mu* | +| LogicalAnd(bool, bool) -> void | 0 | 4 | 0 | @r0_3 | +| LogicalAnd(bool, bool) -> void | 0 | 4 | 1 | @r0_2 | +| LogicalAnd(bool, bool) -> void | 0 | 7 | 0 | @r0_6 | +| LogicalAnd(bool, bool) -> void | 0 | 7 | 1 | @r0_5 | +| LogicalAnd(bool, bool) -> void | 0 | 10 | 0 | @r0_8 | +| LogicalAnd(bool, bool) -> void | 0 | 10 | 1 | @r0_9 | +| LogicalAnd(bool, bool) -> void | 0 | 12 | 0 | @r0_11 | +| LogicalAnd(bool, bool) -> void | 0 | 12 | 1 | @m0_4 | +| LogicalAnd(bool, bool) -> void | 0 | 13 | 7 | @r0_12 | +| LogicalAnd(bool, bool) -> void | 1 | 1 | 0 | @r1_0 | +| LogicalAnd(bool, bool) -> void | 1 | 1 | 1 | @m0_7 | +| LogicalAnd(bool, bool) -> void | 1 | 2 | 7 | @r1_1 | +| LogicalAnd(bool, bool) -> void | 2 | 2 | 0 | @r2_1 | +| LogicalAnd(bool, bool) -> void | 2 | 2 | 1 | @r2_0 | +| LogicalAnd(bool, bool) -> void | 3 | 1 | 0 | @r3_0 | +| LogicalAnd(bool, bool) -> void | 3 | 1 | 1 | @m0_4 | +| LogicalAnd(bool, bool) -> void | 3 | 2 | 7 | @r3_1 | +| LogicalAnd(bool, bool) -> void | 4 | 1 | 0 | @r4_0 | +| LogicalAnd(bool, bool) -> void | 4 | 1 | 1 | @m0_7 | +| LogicalAnd(bool, bool) -> void | 4 | 2 | 7 | @r4_1 | +| LogicalAnd(bool, bool) -> void | 5 | 2 | 0 | @r5_1 | +| LogicalAnd(bool, bool) -> void | 5 | 2 | 1 | @r5_0 | +| LogicalAnd(bool, bool) -> void | 6 | 2 | 0 | @r6_1 | +| LogicalAnd(bool, bool) -> void | 6 | 2 | 1 | @r6_0 | +| LogicalAnd(bool, bool) -> void | 7 | 2 | 0 | @mu* | +| LogicalNot(bool, bool) -> void | 0 | 4 | 0 | @r0_3 | +| LogicalNot(bool, bool) -> void | 0 | 4 | 1 | @r0_2 | +| LogicalNot(bool, bool) -> void | 0 | 7 | 0 | @r0_6 | +| LogicalNot(bool, bool) -> void | 0 | 7 | 1 | @r0_5 | +| LogicalNot(bool, bool) -> void | 0 | 10 | 0 | @r0_8 | +| LogicalNot(bool, bool) -> void | 0 | 10 | 1 | @r0_9 | +| LogicalNot(bool, bool) -> void | 0 | 12 | 0 | @r0_11 | +| LogicalNot(bool, bool) -> void | 0 | 12 | 1 | @m0_4 | +| LogicalNot(bool, bool) -> void | 0 | 13 | 7 | @r0_12 | +| LogicalNot(bool, bool) -> void | 1 | 2 | 0 | @r1_1 | +| LogicalNot(bool, bool) -> void | 1 | 2 | 1 | @r1_0 | +| LogicalNot(bool, bool) -> void | 2 | 1 | 0 | @r2_0 | +| LogicalNot(bool, bool) -> void | 2 | 1 | 1 | @m0_4 | +| LogicalNot(bool, bool) -> void | 2 | 2 | 7 | @r2_1 | +| LogicalNot(bool, bool) -> void | 3 | 1 | 0 | @r3_0 | +| LogicalNot(bool, bool) -> void | 3 | 1 | 1 | @m0_7 | +| LogicalNot(bool, bool) -> void | 3 | 2 | 7 | @r3_1 | +| LogicalNot(bool, bool) -> void | 4 | 2 | 0 | @r4_1 | +| LogicalNot(bool, bool) -> void | 4 | 2 | 1 | @r4_0 | +| LogicalNot(bool, bool) -> void | 5 | 2 | 0 | @r5_1 | +| LogicalNot(bool, bool) -> void | 5 | 2 | 1 | @r5_0 | +| LogicalNot(bool, bool) -> void | 6 | 2 | 0 | @mu* | +| LogicalOr(bool, bool) -> void | 0 | 4 | 0 | @r0_3 | +| LogicalOr(bool, bool) -> void | 0 | 4 | 1 | @r0_2 | +| LogicalOr(bool, bool) -> void | 0 | 7 | 0 | @r0_6 | +| LogicalOr(bool, bool) -> void | 0 | 7 | 1 | @r0_5 | +| LogicalOr(bool, bool) -> void | 0 | 10 | 0 | @r0_8 | +| LogicalOr(bool, bool) -> void | 0 | 10 | 1 | @r0_9 | +| LogicalOr(bool, bool) -> void | 0 | 12 | 0 | @r0_11 | +| LogicalOr(bool, bool) -> void | 0 | 12 | 1 | @m0_4 | +| LogicalOr(bool, bool) -> void | 0 | 13 | 7 | @r0_12 | +| LogicalOr(bool, bool) -> void | 1 | 1 | 0 | @r1_0 | +| LogicalOr(bool, bool) -> void | 1 | 1 | 1 | @m0_7 | +| LogicalOr(bool, bool) -> void | 1 | 2 | 7 | @r1_1 | +| LogicalOr(bool, bool) -> void | 2 | 2 | 0 | @r2_1 | +| LogicalOr(bool, bool) -> void | 2 | 2 | 1 | @r2_0 | +| LogicalOr(bool, bool) -> void | 3 | 1 | 0 | @r3_0 | +| LogicalOr(bool, bool) -> void | 3 | 1 | 1 | @m0_4 | +| LogicalOr(bool, bool) -> void | 3 | 2 | 7 | @r3_1 | +| LogicalOr(bool, bool) -> void | 4 | 1 | 0 | @r4_0 | +| LogicalOr(bool, bool) -> void | 4 | 1 | 1 | @m0_7 | +| LogicalOr(bool, bool) -> void | 4 | 2 | 7 | @r4_1 | +| LogicalOr(bool, bool) -> void | 5 | 2 | 0 | @r5_1 | +| LogicalOr(bool, bool) -> void | 5 | 2 | 1 | @r5_0 | +| LogicalOr(bool, bool) -> void | 6 | 2 | 0 | @r6_1 | +| LogicalOr(bool, bool) -> void | 6 | 2 | 1 | @r6_0 | +| LogicalOr(bool, bool) -> void | 7 | 2 | 0 | @mu* | +| Middle::Middle() -> void | 0 | 3 | 2 | @r0_2 | +| Middle::Middle() -> void | 0 | 5 | 9 | @r0_4 | +| Middle::Middle() -> void | 0 | 5 | 10 | this:@r0_3 | +| Middle::Middle() -> void | 0 | 6 | 2 | @r0_2 | +| Middle::Middle() -> void | 0 | 8 | 9 | @r0_7 | +| Middle::Middle() -> void | 0 | 8 | 10 | this:@r0_6 | +| Middle::Middle() -> void | 0 | 11 | 0 | @mu* | +| Middle::operator=(const Middle &) -> Middle & | 0 | 5 | 0 | @r0_4 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 5 | 1 | @r0_3 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 6 | 1 | @r0_2 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 7 | 2 | @r0_6 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 10 | 0 | @r0_9 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 10 | 1 | @m0_5 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 11 | 2 | @r0_10 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 12 | 9 | @r0_8 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 12 | 10 | this:@r0_7 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 12 | 11 | @r0_11 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 13 | 1 | @r0_2 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 14 | 2 | @r0_13 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 17 | 0 | @r0_16 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 17 | 1 | @m0_5 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 18 | 2 | @r0_17 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 19 | 9 | @r0_15 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 19 | 10 | this:@r0_14 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 19 | 11 | @r0_18 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 21 | 1 | @r0_2 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 22 | 0 | @r0_20 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 22 | 1 | @r0_21 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 24 | 0 | @r0_23 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 24 | 5 | @m0_22 | +| Middle::operator=(const Middle &) -> Middle & | 0 | 25 | 0 | @mu* | +| Middle::~Middle() -> void | 0 | 4 | 2 | @r0_2 | +| Middle::~Middle() -> void | 0 | 6 | 9 | @r0_5 | +| Middle::~Middle() -> void | 0 | 6 | 10 | this:@r0_4 | +| Middle::~Middle() -> void | 0 | 7 | 2 | @r0_2 | +| Middle::~Middle() -> void | 0 | 9 | 9 | @r0_8 | +| Middle::~Middle() -> void | 0 | 9 | 10 | this:@r0_7 | +| Middle::~Middle() -> void | 0 | 11 | 0 | @mu* | +| MiddleVB1::MiddleVB1() -> void | 0 | 3 | 2 | @r0_2 | +| MiddleVB1::MiddleVB1() -> void | 0 | 5 | 9 | @r0_4 | +| MiddleVB1::MiddleVB1() -> void | 0 | 5 | 10 | this:@r0_3 | +| MiddleVB1::MiddleVB1() -> void | 0 | 6 | 2 | @r0_2 | +| MiddleVB1::MiddleVB1() -> void | 0 | 8 | 9 | @r0_7 | +| MiddleVB1::MiddleVB1() -> void | 0 | 8 | 10 | this:@r0_6 | +| MiddleVB1::MiddleVB1() -> void | 0 | 11 | 0 | @mu* | +| MiddleVB1::~MiddleVB1() -> void | 0 | 4 | 2 | @r0_2 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 6 | 9 | @r0_5 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 6 | 10 | this:@r0_4 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 7 | 2 | @r0_2 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 9 | 9 | @r0_8 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 9 | 10 | this:@r0_7 | +| MiddleVB1::~MiddleVB1() -> void | 0 | 11 | 0 | @mu* | +| MiddleVB2::MiddleVB2() -> void | 0 | 3 | 2 | @r0_2 | +| MiddleVB2::MiddleVB2() -> void | 0 | 5 | 9 | @r0_4 | +| MiddleVB2::MiddleVB2() -> void | 0 | 5 | 10 | this:@r0_3 | +| MiddleVB2::MiddleVB2() -> void | 0 | 6 | 2 | @r0_2 | +| MiddleVB2::MiddleVB2() -> void | 0 | 8 | 9 | @r0_7 | +| MiddleVB2::MiddleVB2() -> void | 0 | 8 | 10 | this:@r0_6 | +| MiddleVB2::MiddleVB2() -> void | 0 | 11 | 0 | @mu* | +| MiddleVB2::~MiddleVB2() -> void | 0 | 4 | 2 | @r0_2 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 6 | 9 | @r0_5 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 6 | 10 | this:@r0_4 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 7 | 2 | @r0_2 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 9 | 9 | @r0_8 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 9 | 10 | this:@r0_7 | +| MiddleVB2::~MiddleVB2() -> void | 0 | 11 | 0 | @mu* | +| NestedInitList(int, float) -> void | 0 | 4 | 0 | @r0_3 | +| NestedInitList(int, float) -> void | 0 | 4 | 1 | @r0_2 | +| NestedInitList(int, float) -> void | 0 | 7 | 0 | @r0_6 | +| NestedInitList(int, float) -> void | 0 | 7 | 1 | @r0_5 | +| NestedInitList(int, float) -> void | 0 | 9 | 2 | @r0_8 | +| NestedInitList(int, float) -> void | 0 | 11 | 0 | @r0_9 | +| NestedInitList(int, float) -> void | 0 | 11 | 1 | @r0_10 | +| NestedInitList(int, float) -> void | 0 | 12 | 2 | @r0_8 | +| NestedInitList(int, float) -> void | 0 | 14 | 0 | @r0_12 | +| NestedInitList(int, float) -> void | 0 | 14 | 1 | @r0_13 | +| NestedInitList(int, float) -> void | 0 | 16 | 2 | @r0_15 | +| NestedInitList(int, float) -> void | 0 | 17 | 2 | @r0_16 | +| NestedInitList(int, float) -> void | 0 | 19 | 0 | @r0_18 | +| NestedInitList(int, float) -> void | 0 | 19 | 1 | @m0_4 | +| NestedInitList(int, float) -> void | 0 | 20 | 0 | @r0_17 | +| NestedInitList(int, float) -> void | 0 | 20 | 1 | @r0_19 | +| NestedInitList(int, float) -> void | 0 | 21 | 2 | @r0_16 | +| NestedInitList(int, float) -> void | 0 | 23 | 0 | @r0_22 | +| NestedInitList(int, float) -> void | 0 | 23 | 1 | @m0_7 | +| NestedInitList(int, float) -> void | 0 | 24 | 2 | @r0_23 | +| NestedInitList(int, float) -> void | 0 | 25 | 0 | @r0_21 | +| NestedInitList(int, float) -> void | 0 | 25 | 1 | @r0_24 | +| NestedInitList(int, float) -> void | 0 | 26 | 2 | @r0_15 | +| NestedInitList(int, float) -> void | 0 | 28 | 0 | @r0_26 | +| NestedInitList(int, float) -> void | 0 | 28 | 1 | @r0_27 | +| NestedInitList(int, float) -> void | 0 | 30 | 2 | @r0_29 | +| NestedInitList(int, float) -> void | 0 | 31 | 2 | @r0_30 | +| NestedInitList(int, float) -> void | 0 | 33 | 0 | @r0_32 | +| NestedInitList(int, float) -> void | 0 | 33 | 1 | @m0_4 | +| NestedInitList(int, float) -> void | 0 | 34 | 0 | @r0_31 | +| NestedInitList(int, float) -> void | 0 | 34 | 1 | @r0_33 | +| NestedInitList(int, float) -> void | 0 | 35 | 2 | @r0_30 | +| NestedInitList(int, float) -> void | 0 | 37 | 0 | @r0_36 | +| NestedInitList(int, float) -> void | 0 | 37 | 1 | @m0_7 | +| NestedInitList(int, float) -> void | 0 | 38 | 2 | @r0_37 | +| NestedInitList(int, float) -> void | 0 | 39 | 0 | @r0_35 | +| NestedInitList(int, float) -> void | 0 | 39 | 1 | @r0_38 | +| NestedInitList(int, float) -> void | 0 | 40 | 2 | @r0_29 | +| NestedInitList(int, float) -> void | 0 | 41 | 2 | @r0_40 | +| NestedInitList(int, float) -> void | 0 | 43 | 0 | @r0_42 | +| NestedInitList(int, float) -> void | 0 | 43 | 1 | @m0_4 | +| NestedInitList(int, float) -> void | 0 | 44 | 0 | @r0_41 | +| NestedInitList(int, float) -> void | 0 | 44 | 1 | @r0_43 | +| NestedInitList(int, float) -> void | 0 | 45 | 2 | @r0_40 | +| NestedInitList(int, float) -> void | 0 | 47 | 0 | @r0_46 | +| NestedInitList(int, float) -> void | 0 | 47 | 1 | @m0_7 | +| NestedInitList(int, float) -> void | 0 | 48 | 2 | @r0_47 | +| NestedInitList(int, float) -> void | 0 | 49 | 0 | @r0_45 | +| NestedInitList(int, float) -> void | 0 | 49 | 1 | @r0_48 | +| NestedInitList(int, float) -> void | 0 | 51 | 2 | @r0_50 | +| NestedInitList(int, float) -> void | 0 | 52 | 2 | @r0_51 | +| NestedInitList(int, float) -> void | 0 | 54 | 0 | @r0_53 | +| NestedInitList(int, float) -> void | 0 | 54 | 1 | @m0_4 | +| NestedInitList(int, float) -> void | 0 | 55 | 0 | @r0_52 | +| NestedInitList(int, float) -> void | 0 | 55 | 1 | @r0_54 | +| NestedInitList(int, float) -> void | 0 | 56 | 2 | @r0_51 | +| NestedInitList(int, float) -> void | 0 | 58 | 0 | @r0_56 | +| NestedInitList(int, float) -> void | 0 | 58 | 1 | @r0_57 | +| NestedInitList(int, float) -> void | 0 | 59 | 2 | @r0_50 | +| NestedInitList(int, float) -> void | 0 | 60 | 2 | @r0_59 | +| NestedInitList(int, float) -> void | 0 | 62 | 0 | @r0_61 | +| NestedInitList(int, float) -> void | 0 | 62 | 1 | @m0_4 | +| NestedInitList(int, float) -> void | 0 | 63 | 0 | @r0_60 | +| NestedInitList(int, float) -> void | 0 | 63 | 1 | @r0_62 | +| NestedInitList(int, float) -> void | 0 | 64 | 2 | @r0_59 | +| NestedInitList(int, float) -> void | 0 | 66 | 0 | @r0_64 | +| NestedInitList(int, float) -> void | 0 | 66 | 1 | @r0_65 | +| NestedInitList(int, float) -> void | 0 | 69 | 0 | @mu* | +| Nullptr() -> void | 0 | 4 | 0 | @r0_2 | +| Nullptr() -> void | 0 | 4 | 1 | @r0_3 | +| Nullptr() -> void | 0 | 7 | 0 | @r0_5 | +| Nullptr() -> void | 0 | 7 | 1 | @r0_6 | +| Nullptr() -> void | 0 | 10 | 0 | @r0_9 | +| Nullptr() -> void | 0 | 10 | 1 | @r0_8 | +| Nullptr() -> void | 0 | 13 | 0 | @r0_12 | +| Nullptr() -> void | 0 | 13 | 1 | @r0_11 | +| Nullptr() -> void | 0 | 16 | 0 | @mu* | +| Outer::Func(void *, char) -> long | 0 | 4 | 0 | @r0_3 | +| Outer::Func(void *, char) -> long | 0 | 4 | 1 | @r0_2 | +| Outer::Func(void *, char) -> long | 0 | 7 | 0 | @r0_6 | +| Outer::Func(void *, char) -> long | 0 | 7 | 1 | @r0_5 | +| Outer::Func(void *, char) -> long | 0 | 10 | 0 | @r0_8 | +| Outer::Func(void *, char) -> long | 0 | 10 | 1 | @r0_9 | +| Outer::Func(void *, char) -> long | 0 | 12 | 0 | @r0_11 | +| Outer::Func(void *, char) -> long | 0 | 12 | 5 | @m0_10 | +| Outer::Func(void *, char) -> long | 0 | 13 | 0 | @mu* | +| Parameters(int, int) -> int | 0 | 4 | 0 | @r0_3 | +| Parameters(int, int) -> int | 0 | 4 | 1 | @r0_2 | +| Parameters(int, int) -> int | 0 | 7 | 0 | @r0_6 | +| Parameters(int, int) -> int | 0 | 7 | 1 | @r0_5 | +| Parameters(int, int) -> int | 0 | 10 | 0 | @r0_9 | +| Parameters(int, int) -> int | 0 | 10 | 1 | @m0_4 | +| Parameters(int, int) -> int | 0 | 12 | 0 | @r0_11 | +| Parameters(int, int) -> int | 0 | 12 | 1 | @m0_7 | +| Parameters(int, int) -> int | 0 | 13 | 3 | @r0_10 | +| Parameters(int, int) -> int | 0 | 13 | 4 | @r0_12 | +| Parameters(int, int) -> int | 0 | 14 | 0 | @r0_8 | +| Parameters(int, int) -> int | 0 | 14 | 1 | @r0_13 | +| Parameters(int, int) -> int | 0 | 16 | 0 | @r0_15 | +| Parameters(int, int) -> int | 0 | 16 | 5 | @m0_14 | +| Parameters(int, int) -> int | 0 | 17 | 0 | @mu* | +| PointerCompare(int *, int *) -> void | 0 | 4 | 0 | @r0_3 | +| PointerCompare(int *, int *) -> void | 0 | 4 | 1 | @r0_2 | +| PointerCompare(int *, int *) -> void | 0 | 7 | 0 | @r0_6 | +| PointerCompare(int *, int *) -> void | 0 | 7 | 1 | @r0_5 | +| PointerCompare(int *, int *) -> void | 0 | 10 | 0 | @r0_8 | +| PointerCompare(int *, int *) -> void | 0 | 10 | 1 | @r0_9 | +| PointerCompare(int *, int *) -> void | 0 | 12 | 0 | @r0_11 | +| PointerCompare(int *, int *) -> void | 0 | 12 | 1 | @m0_4 | +| PointerCompare(int *, int *) -> void | 0 | 14 | 0 | @r0_13 | +| PointerCompare(int *, int *) -> void | 0 | 14 | 1 | @m0_7 | +| PointerCompare(int *, int *) -> void | 0 | 15 | 3 | @r0_12 | +| PointerCompare(int *, int *) -> void | 0 | 15 | 4 | @r0_14 | +| PointerCompare(int *, int *) -> void | 0 | 17 | 0 | @r0_16 | +| PointerCompare(int *, int *) -> void | 0 | 17 | 1 | @r0_15 | +| PointerCompare(int *, int *) -> void | 0 | 19 | 0 | @r0_18 | +| PointerCompare(int *, int *) -> void | 0 | 19 | 1 | @m0_4 | +| PointerCompare(int *, int *) -> void | 0 | 21 | 0 | @r0_20 | +| PointerCompare(int *, int *) -> void | 0 | 21 | 1 | @m0_7 | +| PointerCompare(int *, int *) -> void | 0 | 22 | 3 | @r0_19 | +| PointerCompare(int *, int *) -> void | 0 | 22 | 4 | @r0_21 | +| PointerCompare(int *, int *) -> void | 0 | 24 | 0 | @r0_23 | +| PointerCompare(int *, int *) -> void | 0 | 24 | 1 | @r0_22 | +| PointerCompare(int *, int *) -> void | 0 | 26 | 0 | @r0_25 | +| PointerCompare(int *, int *) -> void | 0 | 26 | 1 | @m0_4 | +| PointerCompare(int *, int *) -> void | 0 | 28 | 0 | @r0_27 | +| PointerCompare(int *, int *) -> void | 0 | 28 | 1 | @m0_7 | +| PointerCompare(int *, int *) -> void | 0 | 29 | 3 | @r0_26 | +| PointerCompare(int *, int *) -> void | 0 | 29 | 4 | @r0_28 | +| PointerCompare(int *, int *) -> void | 0 | 31 | 0 | @r0_30 | +| PointerCompare(int *, int *) -> void | 0 | 31 | 1 | @r0_29 | +| PointerCompare(int *, int *) -> void | 0 | 33 | 0 | @r0_32 | +| PointerCompare(int *, int *) -> void | 0 | 33 | 1 | @m0_4 | +| PointerCompare(int *, int *) -> void | 0 | 35 | 0 | @r0_34 | +| PointerCompare(int *, int *) -> void | 0 | 35 | 1 | @m0_7 | +| PointerCompare(int *, int *) -> void | 0 | 36 | 3 | @r0_33 | +| PointerCompare(int *, int *) -> void | 0 | 36 | 4 | @r0_35 | +| PointerCompare(int *, int *) -> void | 0 | 38 | 0 | @r0_37 | +| PointerCompare(int *, int *) -> void | 0 | 38 | 1 | @r0_36 | +| PointerCompare(int *, int *) -> void | 0 | 40 | 0 | @r0_39 | +| PointerCompare(int *, int *) -> void | 0 | 40 | 1 | @m0_4 | +| PointerCompare(int *, int *) -> void | 0 | 42 | 0 | @r0_41 | +| PointerCompare(int *, int *) -> void | 0 | 42 | 1 | @m0_7 | +| PointerCompare(int *, int *) -> void | 0 | 43 | 3 | @r0_40 | +| PointerCompare(int *, int *) -> void | 0 | 43 | 4 | @r0_42 | +| PointerCompare(int *, int *) -> void | 0 | 45 | 0 | @r0_44 | +| PointerCompare(int *, int *) -> void | 0 | 45 | 1 | @r0_43 | +| PointerCompare(int *, int *) -> void | 0 | 47 | 0 | @r0_46 | +| PointerCompare(int *, int *) -> void | 0 | 47 | 1 | @m0_4 | +| PointerCompare(int *, int *) -> void | 0 | 49 | 0 | @r0_48 | +| PointerCompare(int *, int *) -> void | 0 | 49 | 1 | @m0_7 | +| PointerCompare(int *, int *) -> void | 0 | 50 | 3 | @r0_47 | +| PointerCompare(int *, int *) -> void | 0 | 50 | 4 | @r0_49 | +| PointerCompare(int *, int *) -> void | 0 | 52 | 0 | @r0_51 | +| PointerCompare(int *, int *) -> void | 0 | 52 | 1 | @r0_50 | +| PointerCompare(int *, int *) -> void | 0 | 55 | 0 | @mu* | +| PointerCrement(int *) -> void | 0 | 4 | 0 | @r0_3 | +| PointerCrement(int *) -> void | 0 | 4 | 1 | @r0_2 | +| PointerCrement(int *) -> void | 0 | 7 | 0 | @r0_5 | +| PointerCrement(int *) -> void | 0 | 7 | 1 | @r0_6 | +| PointerCrement(int *) -> void | 0 | 9 | 0 | @r0_8 | +| PointerCrement(int *) -> void | 0 | 9 | 1 | @m0_4 | +| PointerCrement(int *) -> void | 0 | 11 | 3 | @r0_9 | +| PointerCrement(int *) -> void | 0 | 11 | 4 | @r0_10 | +| PointerCrement(int *) -> void | 0 | 12 | 0 | @r0_8 | +| PointerCrement(int *) -> void | 0 | 12 | 1 | @r0_11 | +| PointerCrement(int *) -> void | 0 | 14 | 0 | @r0_13 | +| PointerCrement(int *) -> void | 0 | 14 | 1 | @r0_11 | +| PointerCrement(int *) -> void | 0 | 16 | 0 | @r0_15 | +| PointerCrement(int *) -> void | 0 | 16 | 1 | @m0_12 | +| PointerCrement(int *) -> void | 0 | 18 | 3 | @r0_16 | +| PointerCrement(int *) -> void | 0 | 18 | 4 | @r0_17 | +| PointerCrement(int *) -> void | 0 | 19 | 0 | @r0_15 | +| PointerCrement(int *) -> void | 0 | 19 | 1 | @r0_18 | +| PointerCrement(int *) -> void | 0 | 21 | 0 | @r0_20 | +| PointerCrement(int *) -> void | 0 | 21 | 1 | @r0_18 | +| PointerCrement(int *) -> void | 0 | 23 | 0 | @r0_22 | +| PointerCrement(int *) -> void | 0 | 23 | 1 | @m0_19 | +| PointerCrement(int *) -> void | 0 | 25 | 3 | @r0_23 | +| PointerCrement(int *) -> void | 0 | 25 | 4 | @r0_24 | +| PointerCrement(int *) -> void | 0 | 26 | 0 | @r0_22 | +| PointerCrement(int *) -> void | 0 | 26 | 1 | @r0_25 | +| PointerCrement(int *) -> void | 0 | 28 | 0 | @r0_27 | +| PointerCrement(int *) -> void | 0 | 28 | 1 | @r0_23 | +| PointerCrement(int *) -> void | 0 | 30 | 0 | @r0_29 | +| PointerCrement(int *) -> void | 0 | 30 | 1 | @m0_26 | +| PointerCrement(int *) -> void | 0 | 32 | 3 | @r0_30 | +| PointerCrement(int *) -> void | 0 | 32 | 4 | @r0_31 | +| PointerCrement(int *) -> void | 0 | 33 | 0 | @r0_29 | +| PointerCrement(int *) -> void | 0 | 33 | 1 | @r0_32 | +| PointerCrement(int *) -> void | 0 | 35 | 0 | @r0_34 | +| PointerCrement(int *) -> void | 0 | 35 | 1 | @r0_30 | +| PointerCrement(int *) -> void | 0 | 38 | 0 | @mu* | +| PointerOps(int *, int) -> void | 0 | 4 | 0 | @r0_3 | +| PointerOps(int *, int) -> void | 0 | 4 | 1 | @r0_2 | +| PointerOps(int *, int) -> void | 0 | 7 | 0 | @r0_6 | +| PointerOps(int *, int) -> void | 0 | 7 | 1 | @r0_5 | +| PointerOps(int *, int) -> void | 0 | 10 | 0 | @r0_8 | +| PointerOps(int *, int) -> void | 0 | 10 | 1 | @r0_9 | +| PointerOps(int *, int) -> void | 0 | 13 | 0 | @r0_11 | +| PointerOps(int *, int) -> void | 0 | 13 | 1 | @r0_12 | +| PointerOps(int *, int) -> void | 0 | 15 | 0 | @r0_14 | +| PointerOps(int *, int) -> void | 0 | 15 | 1 | @m0_4 | +| PointerOps(int *, int) -> void | 0 | 17 | 0 | @r0_16 | +| PointerOps(int *, int) -> void | 0 | 17 | 1 | @m0_7 | +| PointerOps(int *, int) -> void | 0 | 18 | 3 | @r0_15 | +| PointerOps(int *, int) -> void | 0 | 18 | 4 | @r0_17 | +| PointerOps(int *, int) -> void | 0 | 20 | 0 | @r0_19 | +| PointerOps(int *, int) -> void | 0 | 20 | 1 | @r0_18 | +| PointerOps(int *, int) -> void | 0 | 22 | 0 | @r0_21 | +| PointerOps(int *, int) -> void | 0 | 22 | 1 | @m0_7 | +| PointerOps(int *, int) -> void | 0 | 24 | 0 | @r0_23 | +| PointerOps(int *, int) -> void | 0 | 24 | 1 | @m0_4 | +| PointerOps(int *, int) -> void | 0 | 25 | 3 | @r0_24 | +| PointerOps(int *, int) -> void | 0 | 25 | 4 | @r0_22 | +| PointerOps(int *, int) -> void | 0 | 27 | 0 | @r0_26 | +| PointerOps(int *, int) -> void | 0 | 27 | 1 | @r0_25 | +| PointerOps(int *, int) -> void | 0 | 29 | 0 | @r0_28 | +| PointerOps(int *, int) -> void | 0 | 29 | 1 | @m0_4 | +| PointerOps(int *, int) -> void | 0 | 31 | 0 | @r0_30 | +| PointerOps(int *, int) -> void | 0 | 31 | 1 | @m0_7 | +| PointerOps(int *, int) -> void | 0 | 32 | 3 | @r0_29 | +| PointerOps(int *, int) -> void | 0 | 32 | 4 | @r0_31 | +| PointerOps(int *, int) -> void | 0 | 34 | 0 | @r0_33 | +| PointerOps(int *, int) -> void | 0 | 34 | 1 | @r0_32 | +| PointerOps(int *, int) -> void | 0 | 36 | 0 | @r0_35 | +| PointerOps(int *, int) -> void | 0 | 36 | 1 | @m0_4 | +| PointerOps(int *, int) -> void | 0 | 38 | 0 | @r0_37 | +| PointerOps(int *, int) -> void | 0 | 38 | 1 | @m0_34 | +| PointerOps(int *, int) -> void | 0 | 39 | 3 | @r0_36 | +| PointerOps(int *, int) -> void | 0 | 39 | 4 | @r0_38 | +| PointerOps(int *, int) -> void | 0 | 40 | 2 | @r0_39 | +| PointerOps(int *, int) -> void | 0 | 42 | 0 | @r0_41 | +| PointerOps(int *, int) -> void | 0 | 42 | 1 | @r0_40 | +| PointerOps(int *, int) -> void | 0 | 44 | 0 | @r0_43 | +| PointerOps(int *, int) -> void | 0 | 44 | 1 | @m0_4 | +| PointerOps(int *, int) -> void | 0 | 46 | 0 | @r0_45 | +| PointerOps(int *, int) -> void | 0 | 46 | 1 | @r0_44 | +| PointerOps(int *, int) -> void | 0 | 48 | 0 | @r0_47 | +| PointerOps(int *, int) -> void | 0 | 48 | 1 | @m0_42 | +| PointerOps(int *, int) -> void | 0 | 50 | 0 | @r0_49 | +| PointerOps(int *, int) -> void | 0 | 50 | 1 | @m0_46 | +| PointerOps(int *, int) -> void | 0 | 51 | 3 | @r0_50 | +| PointerOps(int *, int) -> void | 0 | 51 | 4 | @r0_48 | +| PointerOps(int *, int) -> void | 0 | 52 | 0 | @r0_49 | +| PointerOps(int *, int) -> void | 0 | 52 | 1 | @r0_51 | +| PointerOps(int *, int) -> void | 0 | 54 | 0 | @r0_53 | +| PointerOps(int *, int) -> void | 0 | 54 | 1 | @m0_42 | +| PointerOps(int *, int) -> void | 0 | 56 | 0 | @r0_55 | +| PointerOps(int *, int) -> void | 0 | 56 | 1 | @m0_52 | +| PointerOps(int *, int) -> void | 0 | 57 | 3 | @r0_56 | +| PointerOps(int *, int) -> void | 0 | 57 | 4 | @r0_54 | +| PointerOps(int *, int) -> void | 0 | 58 | 0 | @r0_55 | +| PointerOps(int *, int) -> void | 0 | 58 | 1 | @r0_57 | +| PointerOps(int *, int) -> void | 0 | 60 | 0 | @r0_59 | +| PointerOps(int *, int) -> void | 0 | 60 | 1 | @m0_4 | +| PointerOps(int *, int) -> void | 0 | 62 | 3 | @r0_60 | +| PointerOps(int *, int) -> void | 0 | 62 | 4 | @r0_61 | +| PointerOps(int *, int) -> void | 0 | 64 | 0 | @r0_63 | +| PointerOps(int *, int) -> void | 0 | 64 | 1 | @r0_62 | +| PointerOps(int *, int) -> void | 0 | 66 | 0 | @r0_65 | +| PointerOps(int *, int) -> void | 0 | 66 | 1 | @m0_4 | +| PointerOps(int *, int) -> void | 0 | 68 | 3 | @r0_66 | +| PointerOps(int *, int) -> void | 0 | 68 | 4 | @r0_67 | +| PointerOps(int *, int) -> void | 0 | 69 | 2 | @r0_68 | +| PointerOps(int *, int) -> void | 0 | 71 | 0 | @r0_70 | +| PointerOps(int *, int) -> void | 0 | 71 | 1 | @r0_69 | +| PointerOps(int *, int) -> void | 0 | 74 | 0 | @mu* | +| PolymorphicBase::PolymorphicBase() -> void | 0 | 5 | 0 | @mu* | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 3 | 2 | @r0_2 | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 5 | 9 | @r0_4 | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 5 | 10 | this:@r0_3 | +| PolymorphicDerived::PolymorphicDerived() -> void | 0 | 8 | 0 | @mu* | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 4 | 2 | @r0_2 | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 6 | 9 | @r0_5 | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 6 | 10 | this:@r0_4 | +| PolymorphicDerived::~PolymorphicDerived() -> void | 0 | 8 | 0 | @mu* | +| ReturnStruct(Point) -> Point | 0 | 4 | 0 | @r0_3 | +| ReturnStruct(Point) -> Point | 0 | 4 | 1 | @r0_2 | +| ReturnStruct(Point) -> Point | 0 | 7 | 0 | @r0_6 | +| ReturnStruct(Point) -> Point | 0 | 7 | 1 | @m0_4 | +| ReturnStruct(Point) -> Point | 0 | 8 | 0 | @r0_5 | +| ReturnStruct(Point) -> Point | 0 | 8 | 1 | @r0_7 | +| ReturnStruct(Point) -> Point | 0 | 10 | 0 | @r0_9 | +| ReturnStruct(Point) -> Point | 0 | 10 | 5 | @m0_8 | +| ReturnStruct(Point) -> Point | 0 | 11 | 0 | @mu* | +| SetFuncPtr() -> void | 0 | 4 | 0 | @r0_2 | +| SetFuncPtr() -> void | 0 | 4 | 1 | @r0_3 | +| SetFuncPtr() -> void | 0 | 7 | 0 | @r0_6 | +| SetFuncPtr() -> void | 0 | 7 | 1 | @r0_5 | +| SetFuncPtr() -> void | 0 | 10 | 0 | @r0_9 | +| SetFuncPtr() -> void | 0 | 10 | 1 | @r0_8 | +| SetFuncPtr() -> void | 0 | 13 | 0 | @r0_12 | +| SetFuncPtr() -> void | 0 | 13 | 1 | @r0_11 | +| SetFuncPtr() -> void | 0 | 16 | 0 | @mu* | +| String::String() -> void | 0 | 5 | 2 | @r0_4 | +| String::String() -> void | 0 | 6 | 9 | @r0_3 | +| String::String() -> void | 0 | 6 | 10 | this:@r0_2 | +| String::String() -> void | 0 | 6 | 11 | @r0_5 | +| String::String() -> void | 0 | 9 | 0 | @mu* | +| StringLiteral(int) -> void | 0 | 4 | 0 | @r0_3 | +| StringLiteral(int) -> void | 0 | 4 | 1 | @r0_2 | +| StringLiteral(int) -> void | 0 | 7 | 2 | @r0_6 | +| StringLiteral(int) -> void | 0 | 9 | 0 | @r0_8 | +| StringLiteral(int) -> void | 0 | 9 | 1 | @m0_4 | +| StringLiteral(int) -> void | 0 | 10 | 3 | @r0_7 | +| StringLiteral(int) -> void | 0 | 10 | 4 | @r0_9 | +| StringLiteral(int) -> void | 0 | 11 | 0 | @r0_10 | +| StringLiteral(int) -> void | 0 | 11 | 1 | @mu0_1 | +| StringLiteral(int) -> void | 0 | 12 | 0 | @r0_5 | +| StringLiteral(int) -> void | 0 | 12 | 1 | @r0_11 | +| StringLiteral(int) -> void | 0 | 15 | 2 | @r0_14 | +| StringLiteral(int) -> void | 0 | 16 | 2 | @r0_15 | +| StringLiteral(int) -> void | 0 | 17 | 0 | @r0_13 | +| StringLiteral(int) -> void | 0 | 17 | 1 | @r0_16 | +| StringLiteral(int) -> void | 0 | 20 | 0 | @r0_19 | +| StringLiteral(int) -> void | 0 | 20 | 1 | @m0_17 | +| StringLiteral(int) -> void | 0 | 22 | 0 | @r0_21 | +| StringLiteral(int) -> void | 0 | 22 | 1 | @m0_4 | +| StringLiteral(int) -> void | 0 | 23 | 3 | @r0_20 | +| StringLiteral(int) -> void | 0 | 23 | 4 | @r0_22 | +| StringLiteral(int) -> void | 0 | 24 | 0 | @r0_23 | +| StringLiteral(int) -> void | 0 | 24 | 1 | @mu0_1 | +| StringLiteral(int) -> void | 0 | 25 | 0 | @r0_18 | +| StringLiteral(int) -> void | 0 | 25 | 1 | @r0_24 | +| StringLiteral(int) -> void | 0 | 28 | 0 | @mu* | +| Switch(int) -> void | 0 | 4 | 0 | @r0_3 | +| Switch(int) -> void | 0 | 4 | 1 | @r0_2 | +| Switch(int) -> void | 0 | 7 | 0 | @r0_5 | +| Switch(int) -> void | 0 | 7 | 1 | @r0_6 | +| Switch(int) -> void | 0 | 9 | 0 | @r0_8 | +| Switch(int) -> void | 0 | 9 | 1 | @m0_4 | +| Switch(int) -> void | 0 | 10 | 7 | @r0_9 | +| Switch(int) -> void | 1 | 2 | 0 | @r1_1 | +| Switch(int) -> void | 1 | 2 | 1 | @r1_0 | +| Switch(int) -> void | 2 | 3 | 0 | @r2_2 | +| Switch(int) -> void | 2 | 3 | 1 | @r2_1 | +| Switch(int) -> void | 4 | 3 | 0 | @r4_2 | +| Switch(int) -> void | 4 | 3 | 1 | @r4_1 | +| Switch(int) -> void | 5 | 3 | 0 | @r5_2 | +| Switch(int) -> void | 5 | 3 | 1 | @r5_1 | +| Switch(int) -> void | 6 | 3 | 0 | @r6_2 | +| Switch(int) -> void | 6 | 3 | 1 | @r6_1 | +| Switch(int) -> void | 7 | 3 | 0 | @r7_2 | +| Switch(int) -> void | 7 | 3 | 1 | @r7_1 | +| Switch(int) -> void | 8 | 2 | 0 | @r8_1 | +| Switch(int) -> void | 8 | 2 | 1 | @r8_0 | +| Switch(int) -> void | 9 | 3 | 0 | @mu* | +| TakeReference() -> int & | 0 | 4 | 0 | @r0_2 | +| TakeReference() -> int & | 0 | 4 | 1 | @r0_3 | +| TakeReference() -> int & | 0 | 6 | 0 | @r0_5 | +| TakeReference() -> int & | 0 | 6 | 5 | @m0_4 | +| TakeReference() -> int & | 0 | 7 | 0 | @mu* | +| TryCatch(bool) -> void | 0 | 4 | 0 | @r0_3 | +| TryCatch(bool) -> void | 0 | 4 | 1 | @r0_2 | +| TryCatch(bool) -> void | 0 | 7 | 0 | @r0_5 | +| TryCatch(bool) -> void | 0 | 7 | 1 | @r0_6 | +| TryCatch(bool) -> void | 0 | 9 | 0 | @r0_8 | +| TryCatch(bool) -> void | 0 | 9 | 1 | @m0_4 | +| TryCatch(bool) -> void | 0 | 10 | 7 | @r0_9 | +| TryCatch(bool) -> void | 1 | 0 | 0 | @mu* | +| TryCatch(bool) -> void | 3 | 2 | 2 | @r3_1 | +| TryCatch(bool) -> void | 3 | 3 | 0 | @r3_0 | +| TryCatch(bool) -> void | 3 | 3 | 1 | @r3_2 | +| TryCatch(bool) -> void | 3 | 4 | 0 | @r3_0 | +| TryCatch(bool) -> void | 3 | 4 | 6 | @m3_3 | +| TryCatch(bool) -> void | 4 | 1 | 0 | @r4_0 | +| TryCatch(bool) -> void | 4 | 1 | 1 | @m0_7 | +| TryCatch(bool) -> void | 4 | 3 | 3 | @r4_1 | +| TryCatch(bool) -> void | 4 | 3 | 4 | @r4_2 | +| TryCatch(bool) -> void | 4 | 4 | 7 | @r4_3 | +| TryCatch(bool) -> void | 5 | 1 | 0 | @r5_0 | +| TryCatch(bool) -> void | 5 | 1 | 1 | @m0_4 | +| TryCatch(bool) -> void | 5 | 2 | 7 | @r5_1 | +| TryCatch(bool) -> void | 6 | 2 | 0 | @r6_1 | +| TryCatch(bool) -> void | 6 | 2 | 1 | @r6_0 | +| TryCatch(bool) -> void | 6 | 4 | 0 | @r6_3 | +| TryCatch(bool) -> void | 6 | 4 | 1 | @m6_2 | +| TryCatch(bool) -> void | 6 | 6 | 0 | @r6_5 | +| TryCatch(bool) -> void | 6 | 6 | 1 | @r6_4 | +| TryCatch(bool) -> void | 7 | 3 | 2 | @r7_2 | +| TryCatch(bool) -> void | 7 | 4 | 9 | @r7_1 | +| TryCatch(bool) -> void | 7 | 4 | 10 | this:@r7_0 | +| TryCatch(bool) -> void | 7 | 4 | 11 | @r7_3 | +| TryCatch(bool) -> void | 7 | 5 | 0 | @r7_0 | +| TryCatch(bool) -> void | 7 | 5 | 6 | @mu0_1 | +| TryCatch(bool) -> void | 8 | 2 | 0 | @r8_1 | +| TryCatch(bool) -> void | 8 | 2 | 1 | @r8_0 | +| TryCatch(bool) -> void | 10 | 2 | 0 | @r10_1 | +| TryCatch(bool) -> void | 10 | 2 | 1 | @r10_0 | +| TryCatch(bool) -> void | 10 | 6 | 0 | @r10_5 | +| TryCatch(bool) -> void | 10 | 6 | 1 | @m10_2 | +| TryCatch(bool) -> void | 10 | 7 | 9 | @r10_4 | +| TryCatch(bool) -> void | 10 | 7 | 10 | this:@r10_3 | +| TryCatch(bool) -> void | 10 | 7 | 11 | @r10_6 | +| TryCatch(bool) -> void | 10 | 8 | 0 | @r10_3 | +| TryCatch(bool) -> void | 10 | 8 | 6 | @mu0_1 | +| TryCatch(bool) -> void | 12 | 2 | 0 | @r12_1 | +| TryCatch(bool) -> void | 12 | 2 | 1 | @r12_0 | +| UninitializedVariables() -> void | 0 | 4 | 0 | @r0_2 | +| UninitializedVariables() -> void | 0 | 4 | 1 | @r0_3 | +| UninitializedVariables() -> void | 0 | 7 | 0 | @r0_6 | +| UninitializedVariables() -> void | 0 | 7 | 1 | @m0_4 | +| UninitializedVariables() -> void | 0 | 8 | 0 | @r0_5 | +| UninitializedVariables() -> void | 0 | 8 | 1 | @r0_7 | +| UninitializedVariables() -> void | 0 | 11 | 0 | @mu* | +| UnionInit(int, float) -> void | 0 | 4 | 0 | @r0_3 | +| UnionInit(int, float) -> void | 0 | 4 | 1 | @r0_2 | +| UnionInit(int, float) -> void | 0 | 7 | 0 | @r0_6 | +| UnionInit(int, float) -> void | 0 | 7 | 1 | @r0_5 | +| UnionInit(int, float) -> void | 0 | 9 | 2 | @r0_8 | +| UnionInit(int, float) -> void | 0 | 11 | 0 | @r0_10 | +| UnionInit(int, float) -> void | 0 | 11 | 1 | @m0_7 | +| UnionInit(int, float) -> void | 0 | 12 | 2 | @r0_11 | +| UnionInit(int, float) -> void | 0 | 13 | 0 | @r0_9 | +| UnionInit(int, float) -> void | 0 | 13 | 1 | @r0_12 | +| UnionInit(int, float) -> void | 0 | 16 | 0 | @mu* | +| VarArgUsage(int) -> void | 0 | 4 | 0 | @r0_3 | +| VarArgUsage(int) -> void | 0 | 4 | 1 | @r0_2 | +| VarArgUsage(int) -> void | 0 | 7 | 0 | @r0_5 | +| VarArgUsage(int) -> void | 0 | 7 | 1 | @r0_6 | +| VarArgUsage(int) -> void | 0 | 9 | 2 | @r0_8 | +| VarArgUsage(int) -> void | 0 | 11 | 11 | @r0_9 | +| VarArgUsage(int) -> void | 0 | 11 | 12 | @r0_10 | +| VarArgUsage(int) -> void | 0 | 14 | 0 | @r0_12 | +| VarArgUsage(int) -> void | 0 | 14 | 1 | @r0_13 | +| VarArgUsage(int) -> void | 0 | 16 | 2 | @r0_15 | +| VarArgUsage(int) -> void | 0 | 18 | 2 | @r0_17 | +| VarArgUsage(int) -> void | 0 | 19 | 11 | @r0_16 | +| VarArgUsage(int) -> void | 0 | 19 | 12 | @r0_18 | +| VarArgUsage(int) -> void | 0 | 22 | 2 | @r0_21 | +| VarArgUsage(int) -> void | 0 | 23 | 11 | @r0_22 | +| VarArgUsage(int) -> void | 0 | 24 | 0 | @r0_23 | +| VarArgUsage(int) -> void | 0 | 24 | 1 | @mu0_1 | +| VarArgUsage(int) -> void | 0 | 25 | 0 | @r0_20 | +| VarArgUsage(int) -> void | 0 | 25 | 1 | @r0_24 | +| VarArgUsage(int) -> void | 0 | 28 | 2 | @r0_27 | +| VarArgUsage(int) -> void | 0 | 29 | 11 | @r0_28 | +| VarArgUsage(int) -> void | 0 | 30 | 0 | @r0_29 | +| VarArgUsage(int) -> void | 0 | 30 | 1 | @mu0_1 | +| VarArgUsage(int) -> void | 0 | 31 | 2 | @r0_30 | +| VarArgUsage(int) -> void | 0 | 32 | 0 | @r0_26 | +| VarArgUsage(int) -> void | 0 | 32 | 1 | @r0_31 | +| VarArgUsage(int) -> void | 0 | 34 | 2 | @r0_33 | +| VarArgUsage(int) -> void | 0 | 35 | 11 | @r0_34 | +| VarArgUsage(int) -> void | 0 | 37 | 2 | @r0_36 | +| VarArgUsage(int) -> void | 0 | 38 | 11 | @r0_37 | +| VarArgUsage(int) -> void | 0 | 41 | 0 | @mu* | +| VarArgs() -> void | 0 | 4 | 2 | @r0_3 | +| VarArgs() -> void | 0 | 7 | 2 | @r0_6 | +| VarArgs() -> void | 0 | 8 | 9 | @r0_2 | +| VarArgs() -> void | 0 | 8 | 11 | @r0_4 | +| VarArgs() -> void | 0 | 8 | 12 | @r0_5 | +| VarArgs() -> void | 0 | 8 | 13 | @r0_7 | +| VarArgs() -> void | 0 | 11 | 0 | @mu* | +| WhileStatements(int) -> void | 0 | 4 | 0 | @r0_3 | +| WhileStatements(int) -> void | 0 | 4 | 1 | @r0_2 | +| WhileStatements(int) -> void | 1 | 2 | 0 | @r1_1 | +| WhileStatements(int) -> void | 1 | 2 | 1 | @m3_0 | +| WhileStatements(int) -> void | 1 | 3 | 3 | @r1_2 | +| WhileStatements(int) -> void | 1 | 3 | 4 | @r1_0 | +| WhileStatements(int) -> void | 1 | 4 | 0 | @r1_1 | +| WhileStatements(int) -> void | 1 | 4 | 1 | @r1_3 | +| WhileStatements(int) -> void | 2 | 2 | 0 | @mu* | +| WhileStatements(int) -> void | 3 | 0 | 11 | from 0: @m0_4 | +| WhileStatements(int) -> void | 3 | 0 | 11 | from 1: @m1_4 | +| WhileStatements(int) -> void | 3 | 2 | 0 | @r3_1 | +| WhileStatements(int) -> void | 3 | 2 | 1 | @m3_0 | +| WhileStatements(int) -> void | 3 | 4 | 3 | @r3_2 | +| WhileStatements(int) -> void | 3 | 4 | 4 | @r3_3 | +| WhileStatements(int) -> void | 3 | 5 | 7 | @r3_4 | +| min(int, int) -> int | 0 | 4 | 0 | @r0_3 | +| min(int, int) -> int | 0 | 4 | 1 | @r0_2 | +| min(int, int) -> int | 0 | 7 | 0 | @r0_6 | +| min(int, int) -> int | 0 | 7 | 1 | @r0_5 | +| min(int, int) -> int | 0 | 10 | 0 | @r0_9 | +| min(int, int) -> int | 0 | 10 | 1 | @m0_4 | +| min(int, int) -> int | 0 | 12 | 0 | @r0_11 | +| min(int, int) -> int | 0 | 12 | 1 | @m0_7 | +| min(int, int) -> int | 0 | 13 | 3 | @r0_10 | +| min(int, int) -> int | 0 | 13 | 4 | @r0_12 | +| min(int, int) -> int | 0 | 14 | 7 | @r0_13 | +| min(int, int) -> int | 1 | 1 | 0 | @r1_0 | +| min(int, int) -> int | 1 | 1 | 1 | @m0_4 | +| min(int, int) -> int | 1 | 3 | 0 | @r1_2 | +| min(int, int) -> int | 1 | 3 | 1 | @r1_1 | +| min(int, int) -> int | 2 | 1 | 0 | @r2_0 | +| min(int, int) -> int | 2 | 1 | 1 | @m0_7 | +| min(int, int) -> int | 2 | 3 | 0 | @r2_2 | +| min(int, int) -> int | 2 | 3 | 1 | @r2_1 | +| min(int, int) -> int | 3 | 0 | 11 | from 1: @m1_3 | +| min(int, int) -> int | 3 | 0 | 11 | from 2: @m2_3 | +| min(int, int) -> int | 3 | 2 | 0 | @r3_1 | +| min(int, int) -> int | 3 | 2 | 1 | @m3_0 | +| min(int, int) -> int | 3 | 3 | 0 | @r0_8 | +| min(int, int) -> int | 3 | 3 | 1 | @r3_2 | +| min(int, int) -> int | 3 | 5 | 0 | @r3_4 | +| min(int, int) -> int | 3 | 5 | 5 | @m3_3 | +| min(int, int) -> int | 3 | 6 | 0 | @mu* | +#select diff --git a/cpp/ql/test/library-tests/ir/ir/ssa_ir.ql b/cpp/ql/test/library-tests/ir/ir/ssa_ir.ql new file mode 100644 index 000000000000..85c960074d60 --- /dev/null +++ b/cpp/ql/test/library-tests/ir/ir/ssa_ir.ql @@ -0,0 +1,6 @@ +import semmle.code.cpp.ssa.SSAIR +import semmle.code.cpp.ssa.PrintSSAIR + +from Instruction instr +where none() +select instr diff --git a/cpp/ql/test/library-tests/lambdas/calling_conv/calling_conv.expected b/cpp/ql/test/library-tests/lambdas/calling_conv/calling_conv.expected new file mode 100644 index 000000000000..5e513acbe8d2 --- /dev/null +++ b/cpp/ql/test/library-tests/lambdas/calling_conv/calling_conv.expected @@ -0,0 +1,34 @@ +| file://:0:0:0:0 | operator= | +| file://:0:0:0:0 | operator= | +| gnu.cpp:1:13:1:15 | gnu | +| gnu.cpp:2:28:2:28 | (constructor) | +| gnu.cpp:2:28:2:28 | (constructor) | +| gnu.cpp:2:28:2:28 | (constructor) | +| gnu.cpp:2:28:2:28 | operator= | +| gnu.cpp:2:30:2:30 | _FUN | +| gnu.cpp:2:30:2:30 | operator auto (*)(int &)->int | +| gnu.cpp:2:30:2:30 | operator() | +| ms32.cpp:1:13:1:16 | ms32 | +| ms32.cpp:2:28:2:28 | (constructor) | +| ms32.cpp:2:28:2:28 | (constructor) | +| ms32.cpp:2:28:2:28 | (constructor) | +| ms32.cpp:2:28:2:28 | operator= | +| ms32.cpp:2:30:2:30 | _FUN__cdecl | +| ms32.cpp:2:30:2:30 | _FUN__fastcall | +| ms32.cpp:2:30:2:30 | _FUN__stdcall | +| ms32.cpp:2:30:2:30 | _FUN__vectorcall | +| ms32.cpp:2:30:2:30 | operator auto (__cdecl *)(int &)->int | +| ms32.cpp:2:30:2:30 | operator auto (__fastcall *)(int &)->int | +| ms32.cpp:2:30:2:30 | operator auto (__stdcall *)(int &)->int | +| ms32.cpp:2:30:2:30 | operator auto (__vectorcall *)(int &)->int | +| ms32.cpp:2:30:2:30 | operator() | +| ms.cpp:1:13:1:14 | ms | +| ms.cpp:2:28:2:28 | (constructor) | +| ms.cpp:2:28:2:28 | (constructor) | +| ms.cpp:2:28:2:28 | (constructor) | +| ms.cpp:2:28:2:28 | operator= | +| ms.cpp:2:30:2:30 | _FUN | +| ms.cpp:2:30:2:30 | _FUN__vectorcall | +| ms.cpp:2:30:2:30 | operator auto (*)(int &)->int | +| ms.cpp:2:30:2:30 | operator auto (__vectorcall *)(int &)->int | +| ms.cpp:2:30:2:30 | operator() | diff --git a/cpp/ql/test/library-tests/lambdas/calling_conv/calling_conv.ql b/cpp/ql/test/library-tests/lambdas/calling_conv/calling_conv.ql new file mode 100644 index 000000000000..2e9dd6395dfc --- /dev/null +++ b/cpp/ql/test/library-tests/lambdas/calling_conv/calling_conv.ql @@ -0,0 +1,4 @@ +import cpp + +from Function f +select f \ No newline at end of file diff --git a/cpp/ql/test/library-tests/lambdas/calling_conv/gnu.cpp b/cpp/ql/test/library-tests/lambdas/calling_conv/gnu.cpp new file mode 100644 index 000000000000..4f3b4fa69f8a --- /dev/null +++ b/cpp/ql/test/library-tests/lambdas/calling_conv/gnu.cpp @@ -0,0 +1,3 @@ +static void gnu() { + auto NormalizeAssembly = [](int& str) -> int { return str; }; +} diff --git a/cpp/ql/test/library-tests/lambdas/calling_conv/ms.cpp b/cpp/ql/test/library-tests/lambdas/calling_conv/ms.cpp new file mode 100644 index 000000000000..48851a59aa2c --- /dev/null +++ b/cpp/ql/test/library-tests/lambdas/calling_conv/ms.cpp @@ -0,0 +1,4 @@ +static void ms() { + auto NormalizeAssembly = [](int& str) -> int { return str; }; +} +// semmle-extractor-options: --microsoft --microsoft_version 1900 diff --git a/cpp/ql/test/library-tests/lambdas/calling_conv/ms32.cpp b/cpp/ql/test/library-tests/lambdas/calling_conv/ms32.cpp new file mode 100644 index 000000000000..85e619fcdc98 --- /dev/null +++ b/cpp/ql/test/library-tests/lambdas/calling_conv/ms32.cpp @@ -0,0 +1,4 @@ +static void ms32() { + auto NormalizeAssembly = [](int& str) -> int { return str; }; +} +// semmle-extractor-options: --microsoft --microsoft_version 1900 --edg --target --edg win32 diff --git a/cpp/ql/test/library-tests/lambdas/captures/captures.cpp b/cpp/ql/test/library-tests/lambdas/captures/captures.cpp new file mode 100644 index 000000000000..31afb63fe66f --- /dev/null +++ b/cpp/ql/test/library-tests/lambdas/captures/captures.cpp @@ -0,0 +1,13 @@ +struct foo { + void a(int x) { + [x, this] { + a(x + 1); + }; + } + + void b(int x) { + [=] { + b(x + 1); + }; + } +}; diff --git a/cpp/ql/test/library-tests/lambdas/captures/captures.expected b/cpp/ql/test/library-tests/lambdas/captures/captures.expected new file mode 100644 index 000000000000..1e154d8827fa --- /dev/null +++ b/cpp/ql/test/library-tests/lambdas/captures/captures.expected @@ -0,0 +1,7 @@ +| captures.cpp:3:6:3:6 | x | explicit | +| captures.cpp:3:9:3:12 | (captured this) | explicit | +| captures.cpp:3:9:3:12 | x | explicit | +| captures.cpp:10:7:10:7 | (captured this) | implicit | +| captures.cpp:10:9:10:9 | (captured this) | implicit | +| captures.cpp:10:9:10:9 | x | implicit | +| end_pos.cpp:9:17:9:18 | ii | explicit | diff --git a/cpp/ql/test/library-tests/lambdas/captures/captures.ql b/cpp/ql/test/library-tests/lambdas/captures/captures.ql new file mode 100644 index 000000000000..b53c3f58b6fb --- /dev/null +++ b/cpp/ql/test/library-tests/lambdas/captures/captures.ql @@ -0,0 +1,5 @@ +import cpp + +from LambdaCapture lc, string mode +where if lc.isImplicit() then mode = "implicit" else mode = "explicit" +select lc, mode diff --git a/cpp/ql/test/library-tests/lambdas/captures/elements.expected b/cpp/ql/test/library-tests/lambdas/captures/elements.expected new file mode 100644 index 000000000000..0000487e2ff7 --- /dev/null +++ b/cpp/ql/test/library-tests/lambdas/captures/elements.expected @@ -0,0 +1,184 @@ +| captures.cpp:0:0:0:0 | captures.cpp | +| captures.cpp:1:8:1:8 | declaration of operator= | +| captures.cpp:1:8:1:8 | declaration of operator= | +| captures.cpp:1:8:1:8 | operator= | +| captures.cpp:1:8:1:8 | operator= | +| captures.cpp:1:8:1:10 | definition of foo | +| captures.cpp:1:8:1:10 | foo | +| captures.cpp:2:8:2:8 | a | +| captures.cpp:2:8:2:8 | definition of a | +| captures.cpp:2:14:2:14 | definition of x | +| captures.cpp:2:14:2:14 | x | +| captures.cpp:2:17:6:3 | { ... } | +| 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 | definition of operator= | +| captures.cpp:3:5:3:5 | operator= | +| captures.cpp:3:5:5:5 | [...](...){...} | +| captures.cpp:3:5:5:5 | {...} | +| captures.cpp:3:5:5:6 | ExprStmt | +| captures.cpp:3:6:3:6 | definition of x | +| captures.cpp:3:6:3:6 | x | +| captures.cpp:3:6:3:6 | x | +| captures.cpp:3:9:3:9 | (captured this) | +| captures.cpp:3:9:3:9 | definition of (captured this) | +| captures.cpp:3:9:3:12 | this | +| captures.cpp:3:15:3:15 | definition of operator() | +| captures.cpp:3:15:3:15 | operator() | +| captures.cpp:3:15:5:5 | { ... } | +| captures.cpp:4:7:4:7 | call to a | +| captures.cpp:4:7:4:15 | ExprStmt | +| captures.cpp:4:9:4:13 | ... + ... | +| captures.cpp:4:13:4:13 | 1 | +| captures.cpp:5:5:5:5 | return ... | +| captures.cpp:6:3:6:3 | return ... | +| captures.cpp:8:8:8:8 | b | +| captures.cpp:8:8:8:8 | definition of b | +| captures.cpp:8:14:8:14 | definition of x | +| captures.cpp:8:14:8:14 | x | +| captures.cpp:8:17:12:3 | { ... } | +| 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 | definition of operator= | +| captures.cpp:9:5:9:5 | operator= | +| captures.cpp:9:5:11:5 | [...](...){...} | +| captures.cpp:9:5:11:5 | {...} | +| captures.cpp:9:5:11:6 | ExprStmt | +| captures.cpp:9:9:9:9 | definition of operator() | +| captures.cpp:9:9:9:9 | operator() | +| captures.cpp:9:9:11:5 | { ... } | +| captures.cpp:10:7:10:7 | (captured this) | +| captures.cpp:10:7:10:7 | call to b | +| captures.cpp:10:7:10:7 | definition of (captured this) | +| captures.cpp:10:7:10:15 | ExprStmt | +| captures.cpp:10:9:10:9 | definition of x | +| captures.cpp:10:9:10:9 | x | +| captures.cpp:10:9:10:13 | ... + ... | +| captures.cpp:10:13:10:13 | 1 | +| captures.cpp:11:5:11:5 | return ... | +| captures.cpp:12:3:12:3 | return ... | +| end_pos.cpp:0:0:0:0 | end_pos.cpp | +| end_pos.cpp:2:1:2:14 | #define OPEN { | +| end_pos.cpp:3:6:3:10 | definition of igFun | +| end_pos.cpp:3:6:3:10 | igFun | +| end_pos.cpp:3:14:12:1 | { ... } | +| end_pos.cpp:4:5:4:8 | OPEN | +| end_pos.cpp:4:5:5:5 | { ... } | +| end_pos.cpp:6:5:6:15 | declaration | +| end_pos.cpp:6:9:6:10 | definition of ii | +| end_pos.cpp:6:9:6:10 | ii | +| end_pos.cpp:6:13:6:14 | 0 | +| end_pos.cpp:6:13:6:14 | initializer for ii | +| end_pos.cpp:7:5:7:69 | // EDG used to not give the initialization for this ii capture an | +| end_pos.cpp:8:5:8:20 | // end location: | +| end_pos.cpp:9:5:11:6 | declaration | +| end_pos.cpp:9:10:9:11 | definition of fp | +| end_pos.cpp:9:10:9:11 | fp | +| end_pos.cpp:9:14:11:5 | initializer for fp | +| end_pos.cpp:9:15:9:15 | (constructor) | +| end_pos.cpp:9:15:9:15 | (constructor) | +| end_pos.cpp:9:15:9:15 | (constructor) | +| 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 | definition of operator= | +| end_pos.cpp:9:15:9:15 | operator= | +| end_pos.cpp:9:15:11:5 | [...](...){...} | +| end_pos.cpp:9:15:11:5 | {...} | +| end_pos.cpp:9:17:9:17 | definition of ii | +| end_pos.cpp:9:17:9:17 | ii | +| end_pos.cpp:9:17:9:18 | (reference to) | +| end_pos.cpp:9:17:9:18 | ii | +| end_pos.cpp:9:20:9:20 | definition of operator() | +| end_pos.cpp:9:20:9:20 | operator() | +| end_pos.cpp:9:27:11:5 | { ... } | +| end_pos.cpp:10:9:10:17 | return ... | +| end_pos.cpp:10:16:10:16 | 1 | +| end_pos.cpp:12:1:12:1 | return ... | +| file://:0:0:0:0 | | +| file://:0:0:0:0 | (captured this) | +| file://:0:0:0:0 | (captured this) | +| file://:0:0:0:0 | (global namespace) | +| file://:0:0:0:0 | ..()(..) | +| file://:0:0:0:0 | ..(*)(..) | +| file://:0:0:0:0 | __va_list_tag | +| file://:0:0:0:0 | __va_list_tag & | +| file://:0:0:0:0 | auto | +| file://:0:0:0:0 | const foo | +| file://:0:0:0:0 | const foo & | +| file://:0:0:0:0 | const lambda [] type at line 3, col. 5 | +| file://:0:0:0:0 | const lambda [] type at line 3, col. 5 & | +| file://:0:0:0:0 | const lambda [] type at line 3, col. 5 * | +| file://:0:0:0:0 | const lambda [] type at line 3, col. 5 *const | +| 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. 5 & | +| 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. 5 *const | +| 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 | declaration of 1st parameter | +| file://:0:0:0:0 | declaration of 1st parameter | +| file://:0:0:0:0 | declaration of 1st parameter | +| file://:0:0:0:0 | declaration of 1st parameter | +| file://:0:0:0:0 | declaration of 1st parameter | +| file://:0:0:0:0 | declaration of 1st parameter | +| file://:0:0:0:0 | declaration of 1st parameter | +| file://:0:0:0:0 | declaration of 1st parameter | +| file://:0:0:0:0 | declaration of 1st parameter | +| file://:0:0:0:0 | declaration of 1st parameter | +| file://:0:0:0:0 | declaration of 1st parameter | +| file://:0:0:0:0 | decltype([...](...){...}) | +| file://:0:0:0:0 | decltype([...](...){...}) | +| file://:0:0:0:0 | decltype([...](...){...}) | +| file://:0:0:0:0 | definition of __va_list_tag | +| file://:0:0:0:0 | definition of fp_offset | +| file://:0:0:0:0 | definition of gp_offset | +| file://:0:0:0:0 | definition of overflow_arg_area | +| file://:0:0:0:0 | definition of reg_save_area | +| file://:0:0:0:0 | foo & | +| file://:0:0:0:0 | foo && | +| file://:0:0:0:0 | foo * | +| file://:0:0:0:0 | foo *const | +| file://:0:0:0:0 | fp_offset | +| file://:0:0:0:0 | gp_offset | +| file://:0:0:0:0 | int & | +| file://:0:0:0:0 | lambda [] type at line 3, col. 5 & | +| file://:0:0:0:0 | lambda [] type at line 3, col. 5 && | +| file://:0:0:0:0 | lambda [] type at line 3, col. 5 * | +| file://:0:0:0:0 | lambda [] type at line 9, col. 5 & | +| file://:0:0:0:0 | lambda [] type at line 9, col. 5 && | +| 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 | operator= | +| file://:0:0:0:0 | operator= | +| file://:0:0:0:0 | overflow_arg_area | +| 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 | reg_save_area | +| file://:0:0:0:0 | this | +| file://:0:0:0:0 | this | +| file://:0:0:0:0 | this | +| file://:0:0:0:0 | this | +| file://:0:0:0:0 | this | +| file://:0:0:0:0 | void * | +| file://:0:0:0:0 | x | +| file://:0:0:0:0 | x | +| file://:0:0:0:0 | x | diff --git a/cpp/ql/test/library-tests/lambdas/captures/elements.ql b/cpp/ql/test/library-tests/lambdas/captures/elements.ql new file mode 100644 index 000000000000..38b30a227221 --- /dev/null +++ b/cpp/ql/test/library-tests/lambdas/captures/elements.ql @@ -0,0 +1,7 @@ +import cpp + +from Element e +where not e instanceof BuiltInType + and not e instanceof Specifier + and not e instanceof Folder +select e diff --git a/cpp/ql/test/library-tests/lambdas/captures/end_pos.cpp b/cpp/ql/test/library-tests/lambdas/captures/end_pos.cpp new file mode 100644 index 000000000000..759d86bab093 --- /dev/null +++ b/cpp/ql/test/library-tests/lambdas/captures/end_pos.cpp @@ -0,0 +1,13 @@ + +#define OPEN { +void igFun() { + OPEN + } + int ii = 0; + // EDG used to not give the initialization for this ii capture an + // end location: + auto fp = [&ii](void) { + return 1; + }; +} + diff --git a/cpp/ql/test/library-tests/lambdas/cfg/cfg.expected b/cpp/ql/test/library-tests/lambdas/cfg/cfg.expected new file mode 100644 index 000000000000..da693902885b --- /dev/null +++ b/cpp/ql/test/library-tests/lambdas/cfg/cfg.expected @@ -0,0 +1,93 @@ +| | false | 160 | 160 | operator() | +| | false | 166 | 166 | declaration | +| | false | 168 | 168 | call to printf | +| | false | 172 | 172 | Running with %d and %d\n | +| | false | 173 | 173 | array to pointer conversion | +| | false | 175 | 175 | (char *)... | +| | false | 177 | 177 | x | +| | false | 179 | 179 | y | +| | false | 181 | 181 | ExprStmt | +| | false | 183 | 183 | z | +| | false | 185 | 185 | x | +| | false | 187 | 187 | y | +| | false | 189 | 189 | ... + ... | +| | false | 191 | 191 | ... = ... | +| | false | 193 | 193 | ExprStmt | +| | false | 195 | 195 | call to printf | +| | false | 199 | 199 | Returning %d %d\n | +| | false | 200 | 200 | array to pointer conversion | +| | false | 202 | 202 | (char *)... | +| | false | 204 | 204 | z | +| | false | 206 | 206 | z | +| | false | 208 | 208 | ExprStmt | +| | false | 210 | 210 | z | +| | false | 212 | 212 | return ... | +| | false | 214 | 214 | { ... } | +| | false | 216 | 216 | operator auto (*)(int, int)->int | +| | false | 219 | 219 | (constructor) | +| | false | 223 | 223 | (constructor) | +| | false | 225 | 225 | operator= | +| | false | 227 | 227 | (constructor) | +| | false | 228 | 228 | _FUN | +| | true | 166 | 181 | | +| | true | 168 | 193 | | +| | true | 172 | 177 | | +| | true | 177 | 179 | | +| | true | 179 | 168 | | +| | true | 181 | 172 | | +| | true | 183 | 191 | | +| | true | 185 | 187 | | +| | true | 187 | 189 | | +| | true | 189 | 183 | | +| | true | 191 | 208 | | +| | true | 193 | 185 | | +| | true | 195 | 212 | | +| | true | 199 | 204 | | +| | true | 204 | 206 | | +| | true | 206 | 195 | | +| | true | 208 | 199 | | +| | true | 210 | 160 | | +| | true | 212 | 210 | | +| | true | 214 | 166 | | +| __va_list_tag::operator= | false | 61 | 61 | operator= | +| __va_list_tag::operator= | false | 65 | 65 | operator= | +| main | false | 153 | 153 | main | +| main | false | 251 | 251 | [...](...){...} | +| main | false | 253 | 253 | initializer for myLambda | +| main | false | 257 | 257 | declaration | +| main | false | 259 | 259 | call to printf | +| main | false | 265 | 265 | Some results: %d %d\n | +| main | false | 266 | 266 | array to pointer conversion | +| main | false | 268 | 268 | (char *)... | +| main | false | 272 | 272 | call to operator() | +| main | false | 274 | 274 | myLambda | +| main | false | 276 | 276 | (const lambda [] type at line 12, col. 21)... | +| main | false | 280 | 280 | 5 | +| main | false | 283 | 283 | 6 | +| main | false | 284 | 284 | call to operator() | +| main | false | 286 | 286 | myLambda | +| main | false | 288 | 288 | (const lambda [] type at line 12, col. 21)... | +| main | false | 292 | 292 | 7 | +| main | false | 295 | 295 | 8 | +| main | false | 296 | 296 | ExprStmt | +| main | false | 300 | 300 | 0 | +| main | false | 301 | 301 | return ... | +| main | false | 303 | 303 | { ... } | +| main | true | 251 | 296 | | +| main | true | 253 | 251 | | +| main | true | 257 | 253 | | +| main | true | 259 | 301 | | +| main | true | 265 | 280 | | +| main | true | 272 | 292 | | +| main | true | 274 | 272 | | +| main | true | 280 | 283 | | +| main | true | 283 | 274 | | +| main | true | 284 | 259 | | +| main | true | 286 | 284 | | +| main | true | 292 | 295 | | +| main | true | 295 | 286 | | +| main | true | 296 | 265 | | +| main | true | 300 | 153 | | +| main | true | 301 | 300 | | +| main | true | 303 | 257 | | +| printf | false | 138 | 138 | printf | diff --git a/cpp/ql/test/library-tests/lambdas/cfg/cfg.ql b/cpp/ql/test/library-tests/lambdas/cfg/cfg.ql new file mode 100644 index 000000000000..4c3d73d46eea --- /dev/null +++ b/cpp/ql/test/library-tests/lambdas/cfg/cfg.ql @@ -0,0 +1,34 @@ +// query-type: graph +import cpp + +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() + } +} + +string scope(ControlFlowNode x) { + if exists(x.getControlFlowScope().getQualifiedName()) + then result = x.getControlFlowScope().getQualifiedName() + else result = "" +} + +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 x.getASuccessor() = y + and if x.getATrueSuccessor() = y then truelabel = "T" else truelabel = "" + and if x.getAFalseSuccessor() = y then falselabel = "F" else falselabel = "" + and label = truelabel + falselabel) +} + +from boolean isEdge, ControlFlowNode x, ControlFlowNode y, string label +where isNode(isEdge, x, y, label) or isSuccessor(isEdge, x, y, label) +select scope(x), isEdge, x, y, label + diff --git a/cpp/ql/test/library-tests/lambdas/cfg/lambda_cfg.cpp b/cpp/ql/test/library-tests/lambdas/cfg/lambda_cfg.cpp new file mode 100644 index 000000000000..1cb26db7dc37 --- /dev/null +++ b/cpp/ql/test/library-tests/lambdas/cfg/lambda_cfg.cpp @@ -0,0 +1,24 @@ +// Compilable with: +// g++ -DCOMPILABLE_TEST -std=c++0x lambda_cfg.cpp -o lambda_cfg +// clang -DCOMPILABLE_TEST -std=c++0x lambda_cfg.cpp -o lambda_cfg + +#ifdef COMPILABLE_TEST +#include +#else +void printf(char *str, int x, int y); +#endif + +int main(void) { + auto myLambda = [] (int x, int y) -> int { + int z; + printf("Running with %d and %d\n", x, y); + z = x + y; + printf("Returning %d %d\n", z, z); + return z; + }; + + printf("Some results: %d %d\n", myLambda(5, 6), myLambda(7, 8)); + + return 0; +} + diff --git a/cpp/ql/test/library-tests/lambdas/cfg/reachability.expected b/cpp/ql/test/library-tests/lambdas/cfg/reachability.expected new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/cpp/ql/test/library-tests/lambdas/cfg/reachability.ql b/cpp/ql/test/library-tests/lambdas/cfg/reachability.ql new file mode 100644 index 000000000000..7f5c8e8d9151 --- /dev/null +++ b/cpp/ql/test/library-tests/lambdas/cfg/reachability.ql @@ -0,0 +1,7 @@ +import cpp + +from ControlFlowNode n +where not reachable(n) +select n.getLocation().getStartLine(), + n + diff --git a/cpp/ql/test/library-tests/lambdas/cfg/stmts.expected b/cpp/ql/test/library-tests/lambdas/cfg/stmts.expected new file mode 100644 index 000000000000..94450f3c36d4 --- /dev/null +++ b/cpp/ql/test/library-tests/lambdas/cfg/stmts.expected @@ -0,0 +1,10 @@ +| lambda_cfg.cpp:11:16:23:1 | { ... } | lambda_cfg.cpp:11:5:11:8 | main | +| lambda_cfg.cpp:12:5:18:6 | declaration | lambda_cfg.cpp:11:5:11:8 | main | +| lambda_cfg.cpp:12:46:18:5 | { ... } | lambda_cfg.cpp:12:24:12:24 | operator() | +| lambda_cfg.cpp:13:9:13:14 | declaration | lambda_cfg.cpp:12:24:12:24 | operator() | +| lambda_cfg.cpp:14:9:14:49 | ExprStmt | lambda_cfg.cpp:12:24:12:24 | operator() | +| lambda_cfg.cpp:15:9:15:18 | ExprStmt | lambda_cfg.cpp:12:24:12:24 | operator() | +| lambda_cfg.cpp:16:9:16:42 | ExprStmt | lambda_cfg.cpp:12:24:12:24 | operator() | +| lambda_cfg.cpp:17:9:17:17 | return ... | lambda_cfg.cpp:12:24:12:24 | operator() | +| lambda_cfg.cpp:20:5:20:68 | ExprStmt | lambda_cfg.cpp:11:5:11:8 | main | +| lambda_cfg.cpp:22:5:22:13 | return ... | lambda_cfg.cpp:11:5:11:8 | main | diff --git a/cpp/ql/test/library-tests/lambdas/cfg/stmts.ql b/cpp/ql/test/library-tests/lambdas/cfg/stmts.ql new file mode 100644 index 000000000000..b451a3dee4ea --- /dev/null +++ b/cpp/ql/test/library-tests/lambdas/cfg/stmts.ql @@ -0,0 +1,4 @@ +import cpp + +from Stmt s +select s, s.getEnclosingFunction() diff --git a/cpp/ql/test/library-tests/languages/a.c b/cpp/ql/test/library-tests/languages/a.c new file mode 100644 index 000000000000..2a024df9ee90 --- /dev/null +++ b/cpp/ql/test/library-tests/languages/a.c @@ -0,0 +1,3 @@ + +#include "h.h" + diff --git a/cpp/ql/test/library-tests/languages/b.cpp b/cpp/ql/test/library-tests/languages/b.cpp new file mode 100644 index 000000000000..b0a9cd1482fd --- /dev/null +++ b/cpp/ql/test/library-tests/languages/b.cpp @@ -0,0 +1,3 @@ +// semmle-extractor-options: -std=gnu++0x --microsoft +#include "h.h" + diff --git a/cpp/ql/test/library-tests/languages/exprs.expected b/cpp/ql/test/library-tests/languages/exprs.expected new file mode 100644 index 000000000000..ae4dadd7d833 --- /dev/null +++ b/cpp/ql/test/library-tests/languages/exprs.expected @@ -0,0 +1,3 @@ +| h.h:2:10:2:10 | 3 | +| h.h:2:10:2:10 | 3 | +| h.h:2:10:2:10 | (some cast)... | diff --git a/cpp/ql/test/library-tests/languages/exprs.ql b/cpp/ql/test/library-tests/languages/exprs.ql new file mode 100644 index 000000000000..0ad7fdc61c93 --- /dev/null +++ b/cpp/ql/test/library-tests/languages/exprs.ql @@ -0,0 +1,11 @@ +import cpp + +class SomeCStyleCast extends CStyleCast { + override string toString() { + result = "(some cast)..." + } +} + +from Expr e +select e + diff --git a/cpp/ql/test/library-tests/languages/h.h b/cpp/ql/test/library-tests/languages/h.h new file mode 100644 index 000000000000..37a299e60c03 --- /dev/null +++ b/cpp/ql/test/library-tests/languages/h.h @@ -0,0 +1,3 @@ + +char *ns[3]; + diff --git a/cpp/ql/test/library-tests/large_expressions/exprs.expected b/cpp/ql/test/library-tests/large_expressions/exprs.expected new file mode 100644 index 000000000000..cf7a0d20d6dc --- /dev/null +++ b/cpp/ql/test/library-tests/large_expressions/exprs.expected @@ -0,0 +1,3 @@ +| test.c:0:0:0:0 | test.c | 51 | 8 | +| test.c:0:0:0:0 | test.c | 52 | 91732 | +| test.c:0:0:0:0 | test.c | 57 | 1 | diff --git a/cpp/ql/test/library-tests/large_expressions/exprs.ql b/cpp/ql/test/library-tests/large_expressions/exprs.ql new file mode 100644 index 000000000000..7f88d6862530 --- /dev/null +++ b/cpp/ql/test/library-tests/large_expressions/exprs.ql @@ -0,0 +1,4 @@ +import cpp + +from File f, int line +select f, line, strictcount(Expr e | e.getFile() = f and e.getLocation().getStartLine() = line) diff --git a/cpp/ql/test/library-tests/large_expressions/test.c b/cpp/ql/test/library-tests/large_expressions/test.c new file mode 100644 index 000000000000..453e3885053c --- /dev/null +++ b/cpp/ql/test/library-tests/large_expressions/test.c @@ -0,0 +1,58 @@ + +// this is a test of extracting a large expression (not a sensible way to find primes) + +#define check(v, n) ( ((v) == (n)) || ((v) % (n) != 0) ) + +#define check10(v, n) \ + (check(v, n) && \ + check(v, n + 1) && \ + check(v, n + 2) && \ + check(v, n + 3) && \ + check(v, n + 4) && \ + check(v, n + 5) && \ + check(v, n + 6) && \ + check(v, n + 7) && \ + check(v, n + 8) && \ + check(v, n + 9)) + +#define check100(v, n) \ + (check10(v, n) && \ + check10(v, n + 10) && \ + check10(v, n + 20) && \ + check10(v, n + 30) && \ + check10(v, n + 40) && \ + check10(v, n + 50) && \ + check10(v, n + 60) && \ + check10(v, n + 70) && \ + check10(v, n + 80) && \ + check10(v, n + 90)) + +#define check1000(v, n) \ + (check100(v, n) && \ + check100(v, n + 100) && \ + check100(v, n + 200) && \ + check100(v, n + 300) && \ + check100(v, n + 400) && \ + check100(v, n + 500) && \ + check100(v, n + 600) && \ + check100(v, n + 700) && \ + check100(v, n + 800) && \ + check100(v, n + 900)) + +#define check3000(v, n) \ + (check1000(v, n) && \ + check1000(v, n + 1000) && \ + check1000(v, n + 2000)) + +int main() +{ + int i; + + for (i = 2; i < 3000; i++) { + if check3000(i, 2) { + // ... i is prime ... + } + } + + return 0; +} diff --git a/cpp/ql/test/library-tests/literal_locations/duplicate_literal_locations.c b/cpp/ql/test/library-tests/literal_locations/duplicate_literal_locations.c new file mode 100644 index 000000000000..6a5348c54670 --- /dev/null +++ b/cpp/ql/test/library-tests/literal_locations/duplicate_literal_locations.c @@ -0,0 +1,7 @@ +void foo(void) { + int a, b, c; + a = 123; + b = 4; + c = 123; +} + diff --git a/cpp/ql/test/library-tests/literal_locations/duplicate_literal_locations.expected b/cpp/ql/test/library-tests/literal_locations/duplicate_literal_locations.expected new file mode 100644 index 000000000000..6573c908eec6 --- /dev/null +++ b/cpp/ql/test/library-tests/literal_locations/duplicate_literal_locations.expected @@ -0,0 +1,6 @@ +| duplicate_literal_locations.c:2:9:2:9 | a | duplicate_literal_locations.c:3:5:3:11 | ... = ... | duplicate_literal_locations.c:3:9:3:11 | 123 | +| duplicate_literal_locations.c:2:12:2:12 | b | duplicate_literal_locations.c:4:5:4:9 | ... = ... | duplicate_literal_locations.c:4:9:4:9 | 4 | +| duplicate_literal_locations.c:2:15:2:15 | c | duplicate_literal_locations.c:5:5:5:11 | ... = ... | duplicate_literal_locations.c:5:9:5:11 | 123 | +| literal_locations.c:11:9:11:9 | i | literal_locations.c:11:12:11:17 | 55555 | literal_locations.c:11:12:11:17 | 55555 | +| literal_locations.c:13:13:13:17 | pairs | literal_locations.c:13:22:16:5 | {...} | literal_locations.c:13:22:16:5 | {...} | +| literal_locations.c:18:9:18:9 | j | literal_locations.c:18:12:18:17 | 55555 | literal_locations.c:18:12:18:17 | 55555 | diff --git a/cpp/ql/test/library-tests/literal_locations/duplicate_literal_locations.ql b/cpp/ql/test/library-tests/literal_locations/duplicate_literal_locations.ql new file mode 100644 index 000000000000..4346da2c77ec --- /dev/null +++ b/cpp/ql/test/library-tests/literal_locations/duplicate_literal_locations.ql @@ -0,0 +1,6 @@ +import cpp + +from LocalScopeVariable v, ControlFlowNode def, Expr e +where exprDefinition(v, def, e) +select v, def, e + diff --git a/cpp/ql/test/library-tests/literal_locations/literal_locations.c b/cpp/ql/test/library-tests/literal_locations/literal_locations.c new file mode 100644 index 000000000000..289c233a3bcc --- /dev/null +++ b/cpp/ql/test/library-tests/literal_locations/literal_locations.c @@ -0,0 +1,29 @@ +// Compilable with: +// gcc -c literal_locations.c +// clang -c literal_locations.c + +typedef struct { + int i; + int j; +} intPair; + +void f(void) { + int i = 55555; + + intPair pairs[] = { + {11111, 22222}, + {33333, 44444} + }; + + int j = 55555; + + switch (i) { + case 5 ... 8: + break; + } + + if (i == -1) + { + } +} + diff --git a/cpp/ql/test/library-tests/literal_locations/literal_locations.expected b/cpp/ql/test/library-tests/literal_locations/literal_locations.expected new file mode 100644 index 000000000000..fc1d4a2f126c --- /dev/null +++ b/cpp/ql/test/library-tests/literal_locations/literal_locations.expected @@ -0,0 +1,12 @@ +| duplicate_literal_locations.c:3:9:3:11 | 123 | +| duplicate_literal_locations.c:4:9:4:9 | 4 | +| duplicate_literal_locations.c:5:9:5:11 | 123 | +| literal_locations.c:11:12:11:17 | 55555 | +| literal_locations.c:14:10:14:14 | 11111 | +| literal_locations.c:14:17:14:21 | 22222 | +| literal_locations.c:15:10:15:14 | 33333 | +| literal_locations.c:15:17:15:21 | 44444 | +| literal_locations.c:18:12:18:17 | 55555 | +| literal_locations.c:21:10:21:10 | 5 | +| literal_locations.c:21:16:21:16 | 8 | +| literal_locations.c:25:15:25:15 | 1 | diff --git a/cpp/ql/test/library-tests/literal_locations/literal_locations.ql b/cpp/ql/test/library-tests/literal_locations/literal_locations.ql new file mode 100644 index 000000000000..39a931141057 --- /dev/null +++ b/cpp/ql/test/library-tests/literal_locations/literal_locations.ql @@ -0,0 +1,5 @@ +import cpp + +from Literal l +select l + diff --git a/cpp/ql/test/library-tests/literals/aggregate_literals/aggregate_literals.c b/cpp/ql/test/library-tests/literals/aggregate_literals/aggregate_literals.c new file mode 100644 index 000000000000..aea1c4fb2f35 --- /dev/null +++ b/cpp/ql/test/library-tests/literals/aggregate_literals/aggregate_literals.c @@ -0,0 +1,39 @@ +struct someStruct { + int i; + int j; +}; + +struct someOtherStruct { + int a; + int b; +}; + +union someUnion { + int n; + double d; +}; + +void f(int x, int y) { + struct someStruct sInit1 = { + .i = x + x, + .j = y - y, + }; + + struct someStruct sInit2 = { x + x, y - y }; + + struct someStruct ss[] = {{x + x, y - y}, {x * x, y / y}}; + + struct someStruct sInit3 = { x }; + +// struct someStruct sInit4 = { .j = y }; Currently fails. Initializes 'i' with 'y' as well. + + int aInit1[2] = { x, y }; + + int aInit2[2] = { x }; + +// int aInit3[2] = { [1] = y }; Currently fails. Initializes [0] with 'y' as well. + + union someUnion uInit1 = { x }; +// union someUnion uInit2 = { .n = x }; Currently fails. Initializes .d with 'x' as well. +// union someUnion uInit3 = { .d = 5.0 }; Currently fails. Initializes .n with '5.0' as well. +} diff --git a/cpp/ql/test/library-tests/literals/aggregate_literals/aggregate_literals_cpp.cpp b/cpp/ql/test/library-tests/literals/aggregate_literals/aggregate_literals_cpp.cpp new file mode 100644 index 000000000000..13f83203c553 --- /dev/null +++ b/cpp/ql/test/library-tests/literals/aggregate_literals/aggregate_literals_cpp.cpp @@ -0,0 +1,28 @@ +struct StructWithBitfields { + void Method(); + + unsigned int a : 12; + + static int s; // Static member variables aren't initialized + + unsigned int b : 5; + unsigned int : 15; // Unnamed bitfields aren't initialized + unsigned int c : 7; +}; + +union UnionWithMethods { + void Method(); + + static int s; + + double d; + int x; +}; + +void Init(int x, int y, int z) { + StructWithBitfields s1 = { x, y, z }; + StructWithBitfields s2 = { x, y }; // s2.c is value initialized + + UnionWithMethods u1 = { x }; + UnionWithMethods u2 = {}; +} diff --git a/cpp/ql/test/library-tests/literals/aggregate_literals/arrays.expected b/cpp/ql/test/library-tests/literals/aggregate_literals/arrays.expected new file mode 100644 index 000000000000..c84cf5ed7470 --- /dev/null +++ b/cpp/ql/test/library-tests/literals/aggregate_literals/arrays.expected @@ -0,0 +1,5 @@ +| aggregate_literals.c:24:29:24:61 | {...} | file://:0:0:0:0 | someStruct[2] | 0 | aggregate_literals.c:24:31:24:44 | {...} | +| aggregate_literals.c:24:29:24:61 | {...} | file://:0:0:0:0 | someStruct[2] | 1 | aggregate_literals.c:24:47:24:60 | {...} | +| aggregate_literals.c:30:20:30:28 | {...} | file://:0:0:0:0 | int[2] | 0 | aggregate_literals.c:30:23:30:23 | x | +| aggregate_literals.c:30:20:30:28 | {...} | file://:0:0:0:0 | int[2] | 1 | aggregate_literals.c:30:26:30:26 | y | +| aggregate_literals.c:32:20:32:25 | {...} | file://:0:0:0:0 | int[2] | 0 | aggregate_literals.c:32:23:32:23 | x | diff --git a/cpp/ql/test/library-tests/literals/aggregate_literals/arrays.ql b/cpp/ql/test/library-tests/literals/aggregate_literals/arrays.ql new file mode 100644 index 000000000000..58c162b3f8d0 --- /dev/null +++ b/cpp/ql/test/library-tests/literals/aggregate_literals/arrays.ql @@ -0,0 +1,5 @@ +import cpp + +from ArrayType a, ArrayAggregateLiteral al, int i +where a = al.getType() +select al, a, i, al.getElementExpr(i) diff --git a/cpp/ql/test/library-tests/literals/aggregate_literals/arrays_value_init.expected b/cpp/ql/test/library-tests/literals/aggregate_literals/arrays_value_init.expected new file mode 100644 index 000000000000..d558e0321397 --- /dev/null +++ b/cpp/ql/test/library-tests/literals/aggregate_literals/arrays_value_init.expected @@ -0,0 +1 @@ +| aggregate_literals.c:32:20:32:25 | {...} | file://:0:0:0:0 | int[2] | 1 | diff --git a/cpp/ql/test/library-tests/literals/aggregate_literals/arrays_value_init.ql b/cpp/ql/test/library-tests/literals/aggregate_literals/arrays_value_init.ql new file mode 100644 index 000000000000..91efe6900015 --- /dev/null +++ b/cpp/ql/test/library-tests/literals/aggregate_literals/arrays_value_init.ql @@ -0,0 +1,6 @@ +import cpp + +from ArrayType a, ArrayAggregateLiteral al, int i +where a = al.getType() + and al.isValueInitialized(i) +select al, a, i diff --git a/cpp/ql/test/library-tests/literals/aggregate_literals/classes.expected b/cpp/ql/test/library-tests/literals/aggregate_literals/classes.expected new file mode 100644 index 000000000000..4d62f293bd21 --- /dev/null +++ b/cpp/ql/test/library-tests/literals/aggregate_literals/classes.expected @@ -0,0 +1,16 @@ +| aggregate_literals.c:17:31:20:5 | {...} | aggregate_literals.c:1:8:1:17 | someStruct | aggregate_literals.c:2:9:2:9 | i | aggregate_literals.c:18:14:18:18 | ... + ... | +| aggregate_literals.c:17:31:20:5 | {...} | aggregate_literals.c:1:8:1:17 | someStruct | aggregate_literals.c:3:9:3:9 | j | aggregate_literals.c:18:14:18:18 | ... + ... | +| aggregate_literals.c:22:31:22:47 | {...} | aggregate_literals.c:1:8:1:17 | someStruct | aggregate_literals.c:2:9:2:9 | i | aggregate_literals.c:22:34:22:38 | ... + ... | +| aggregate_literals.c:22:31:22:47 | {...} | aggregate_literals.c:1:8:1:17 | someStruct | aggregate_literals.c:3:9:3:9 | j | aggregate_literals.c:22:41:22:45 | ... - ... | +| aggregate_literals.c:24:31:24:44 | {...} | aggregate_literals.c:1:8:1:17 | someStruct | aggregate_literals.c:2:9:2:9 | i | aggregate_literals.c:24:32:24:36 | ... + ... | +| aggregate_literals.c:24:31:24:44 | {...} | aggregate_literals.c:1:8:1:17 | someStruct | aggregate_literals.c:3:9:3:9 | j | aggregate_literals.c:24:39:24:43 | ... - ... | +| aggregate_literals.c:24:47:24:60 | {...} | aggregate_literals.c:1:8:1:17 | someStruct | aggregate_literals.c:2:9:2:9 | i | aggregate_literals.c:24:48:24:52 | ... * ... | +| aggregate_literals.c:24:47:24:60 | {...} | aggregate_literals.c:1:8:1:17 | someStruct | aggregate_literals.c:3:9:3:9 | j | aggregate_literals.c:24:55:24:59 | ... / ... | +| aggregate_literals.c:26:31:26:36 | {...} | aggregate_literals.c:1:8:1:17 | someStruct | aggregate_literals.c:2:9:2:9 | i | aggregate_literals.c:26:34:26:34 | x | +| aggregate_literals.c:36:29:36:34 | {...} | aggregate_literals.c:11:7:11:15 | someUnion | aggregate_literals.c:12:9:12:9 | n | aggregate_literals.c:36:32:36:32 | x | +| aggregate_literals_cpp.cpp:23:29:23:40 | {...} | aggregate_literals_cpp.cpp:1:8:1:26 | StructWithBitfields | aggregate_literals_cpp.cpp:4:18:4:18 | a | aggregate_literals_cpp.cpp:23:32:23:32 | x | +| aggregate_literals_cpp.cpp:23:29:23:40 | {...} | aggregate_literals_cpp.cpp:1:8:1:26 | StructWithBitfields | aggregate_literals_cpp.cpp:8:18:8:18 | b | aggregate_literals_cpp.cpp:23:35:23:35 | y | +| aggregate_literals_cpp.cpp:23:29:23:40 | {...} | aggregate_literals_cpp.cpp:1:8:1:26 | StructWithBitfields | aggregate_literals_cpp.cpp:10:18:10:18 | c | aggregate_literals_cpp.cpp:23:38:23:38 | z | +| aggregate_literals_cpp.cpp:24:29:24:37 | {...} | aggregate_literals_cpp.cpp:1:8:1:26 | StructWithBitfields | aggregate_literals_cpp.cpp:4:18:4:18 | a | aggregate_literals_cpp.cpp:24:32:24:32 | x | +| aggregate_literals_cpp.cpp:24:29:24:37 | {...} | aggregate_literals_cpp.cpp:1:8:1:26 | StructWithBitfields | aggregate_literals_cpp.cpp:8:18:8:18 | b | aggregate_literals_cpp.cpp:24:35:24:35 | y | +| aggregate_literals_cpp.cpp:26:26:26:31 | {...} | aggregate_literals_cpp.cpp:13:7:13:22 | UnionWithMethods | aggregate_literals_cpp.cpp:18:12:18:12 | d | aggregate_literals_cpp.cpp:26:29:26:29 | x | diff --git a/cpp/ql/test/library-tests/literals/aggregate_literals/classes.ql b/cpp/ql/test/library-tests/literals/aggregate_literals/classes.ql new file mode 100644 index 000000000000..2203251a1878 --- /dev/null +++ b/cpp/ql/test/library-tests/literals/aggregate_literals/classes.ql @@ -0,0 +1,6 @@ +import cpp + +from Class c, ClassAggregateLiteral al, Field f +where c = al.getType() + and f = c.getAField() +select al, c, f, al.getFieldExpr(f) diff --git a/cpp/ql/test/library-tests/literals/aggregate_literals/classes_value_init.expected b/cpp/ql/test/library-tests/literals/aggregate_literals/classes_value_init.expected new file mode 100644 index 000000000000..56e7c12eb73e --- /dev/null +++ b/cpp/ql/test/library-tests/literals/aggregate_literals/classes_value_init.expected @@ -0,0 +1,3 @@ +| aggregate_literals.c:26:31:26:36 | {...} | aggregate_literals.c:1:8:1:17 | someStruct | aggregate_literals.c:3:9:3:9 | j | +| aggregate_literals_cpp.cpp:24:29:24:37 | {...} | aggregate_literals_cpp.cpp:1:8:1:26 | StructWithBitfields | aggregate_literals_cpp.cpp:10:18:10:18 | c | +| aggregate_literals_cpp.cpp:27:26:27:28 | {...} | aggregate_literals_cpp.cpp:13:7:13:22 | UnionWithMethods | aggregate_literals_cpp.cpp:18:12:18:12 | d | diff --git a/cpp/ql/test/library-tests/literals/aggregate_literals/classes_value_init.ql b/cpp/ql/test/library-tests/literals/aggregate_literals/classes_value_init.ql new file mode 100644 index 000000000000..01d0098ff5e5 --- /dev/null +++ b/cpp/ql/test/library-tests/literals/aggregate_literals/classes_value_init.ql @@ -0,0 +1,6 @@ +import cpp + +from Class c, ClassAggregateLiteral al, Field f +where c = al.getType() + and al.isValueInitialized(f) +select al, c, f diff --git a/cpp/ql/test/library-tests/literals/aggregates/aggregates.c b/cpp/ql/test/library-tests/literals/aggregates/aggregates.c new file mode 100644 index 000000000000..a6434869d554 --- /dev/null +++ b/cpp/ql/test/library-tests/literals/aggregates/aggregates.c @@ -0,0 +1,11 @@ + +#define F(X) X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X +#define X1 F("s") +#define X2 F(X1) +#define X3 F(X2) +#define X4 F(X3) + +const char *strings[] = { + X4 +}; + diff --git a/cpp/ql/test/library-tests/literals/aggregates/count.expected b/cpp/ql/test/library-tests/literals/aggregates/count.expected new file mode 100644 index 000000000000..7bee14cbcd4c --- /dev/null +++ b/cpp/ql/test/library-tests/literals/aggregates/count.expected @@ -0,0 +1 @@ +| 1594324 | diff --git a/cpp/ql/test/library-tests/literals/aggregates/count.ql b/cpp/ql/test/library-tests/literals/aggregates/count.ql new file mode 100644 index 000000000000..3e6f0e8f5f3a --- /dev/null +++ b/cpp/ql/test/library-tests/literals/aggregates/count.ql @@ -0,0 +1,3 @@ +import cpp + +select count(Expr e) diff --git a/cpp/ql/test/library-tests/literals/literals/literals.c b/cpp/ql/test/library-tests/literals/literals/literals.c new file mode 100644 index 000000000000..70a98a986083 --- /dev/null +++ b/cpp/ql/test/library-tests/literals/literals/literals.c @@ -0,0 +1,16 @@ + +double dd = 1.0d; +double dD = 1.0D; +double df = 1.0f; +double dF = 1.0F; +double di = 1.0i; +double dI = 1.0I; +double dj = 1.0j; +double dJ = 1.0J; +double dl = 1.0l; +double dL = 1.0L; +double dq = 1.0q; +double dQ = 1.0Q; +double dw = 1.0w; +double dW = 1.0W; + diff --git a/cpp/ql/test/library-tests/literals/literals/literals.expected b/cpp/ql/test/library-tests/literals/literals/literals.expected new file mode 100644 index 000000000000..4e640c9b2dcc --- /dev/null +++ b/cpp/ql/test/library-tests/literals/literals/literals.expected @@ -0,0 +1,14 @@ +| literals.c:2:13:2:16 | 1.0 | +| literals.c:3:13:3:16 | 1.0 | +| literals.c:4:13:4:16 | 1.0 | +| literals.c:5:13:5:16 | 1.0 | +| literals.c:6:13:6:16 | (1.0,1.0i) | +| literals.c:7:13:7:16 | (1.0,1.0i) | +| literals.c:8:13:8:16 | (1.0,1.0i) | +| literals.c:9:13:9:16 | (1.0,1.0i) | +| literals.c:10:13:10:16 | 1.0 | +| literals.c:11:13:11:16 | 1.0 | +| literals.c:12:13:12:16 | 1.0 | +| literals.c:13:13:13:16 | 1.0 | +| literals.c:14:13:14:16 | 1.0 | +| literals.c:15:13:15:16 | 1.0 | diff --git a/cpp/ql/test/library-tests/literals/literals/literals.ql b/cpp/ql/test/library-tests/literals/literals/literals.ql new file mode 100644 index 000000000000..39a931141057 --- /dev/null +++ b/cpp/ql/test/library-tests/literals/literals/literals.ql @@ -0,0 +1,5 @@ +import cpp + +from Literal l +select l + diff --git a/cpp/ql/test/library-tests/literals/uuidof/uuidof.cpp b/cpp/ql/test/library-tests/literals/uuidof/uuidof.cpp new file mode 100644 index 000000000000..afe3c0d0c95b --- /dev/null +++ b/cpp/ql/test/library-tests/literals/uuidof/uuidof.cpp @@ -0,0 +1,19 @@ +struct _GUID { + char a[16]; +}; + +struct __declspec(uuid("{01234567-89ab-cdef-0123-456789ABCDEF}")) S {}; +struct __declspec(uuid("FEDCBA98-7654-3210-fedc-ba9876543210")) T {}; +template struct Templ {}; +S ReturnS(); + +void GetUUID() { + _GUID uuid = __uuidof(S); + const _GUID& r = __uuidof(T); + const _GUID* p = &__uuidof(S); + uuid = __uuidof(ReturnS()); + uuid = __uuidof(Templ); + S s; + uuid = __uuidof(s); +} +// semmle-extractor-options: --microsoft diff --git a/cpp/ql/test/library-tests/literals/uuidof/uuidof.expected b/cpp/ql/test/library-tests/literals/uuidof/uuidof.expected new file mode 100644 index 000000000000..a0635a81d4f4 --- /dev/null +++ b/cpp/ql/test/library-tests/literals/uuidof/uuidof.expected @@ -0,0 +1,13 @@ +classUuids +| uuidof.cpp:1:8:1:12 | _GUID | | +| uuidof.cpp:5:67:5:67 | S | 01234567-89ab-cdef-0123-456789abcdef | +| uuidof.cpp:6:65:6:65 | T | fedcba98-7654-3210-fedc-ba9876543210 | +| uuidof.cpp:7:29:7:33 | Templ | | +| uuidof.cpp:7:29:7:33 | Templ | | +uuidofOperators +| uuidof.cpp:11:18:11:28 | __uuidof(S) | const _GUID | 01234567-89ab-cdef-0123-456789abcdef | +| uuidof.cpp:12:22:12:32 | __uuidof(T) | const _GUID | fedcba98-7654-3210-fedc-ba9876543210 | +| uuidof.cpp:13:23:13:33 | __uuidof(S) | const _GUID | 01234567-89ab-cdef-0123-456789abcdef | +| uuidof.cpp:14:12:14:30 | __uuidof(S) | const _GUID | 01234567-89ab-cdef-0123-456789abcdef | +| uuidof.cpp:15:12:15:29 | __uuidof(S) | const _GUID | 01234567-89ab-cdef-0123-456789abcdef | +| uuidof.cpp:17:12:17:22 | __uuidof(S) | const _GUID | 01234567-89ab-cdef-0123-456789abcdef | diff --git a/cpp/ql/test/library-tests/literals/uuidof/uuidof.ql b/cpp/ql/test/library-tests/literals/uuidof/uuidof.ql new file mode 100644 index 000000000000..90cf10c061bc --- /dev/null +++ b/cpp/ql/test/library-tests/literals/uuidof/uuidof.ql @@ -0,0 +1,13 @@ +import default + +query predicate classUuids(Class cls, string uuid) { + if exists(cls.getUuid()) then + uuid = cls.getUuid() + else + uuid = "" +} + +query predicate uuidofOperators(UuidofOperator op, string type, string uuid) { + uuid = op.getValue() and + type = op.getType().toString() +} diff --git a/cpp/ql/test/library-tests/locations/aggregate_literals/nested.cpp b/cpp/ql/test/library-tests/locations/aggregate_literals/nested.cpp new file mode 100644 index 000000000000..fb357b7ef765 --- /dev/null +++ b/cpp/ql/test/library-tests/locations/aggregate_literals/nested.cpp @@ -0,0 +1,7 @@ +struct foo_t { int x; float y; char z; }; + +static const foo_t glob[] = { + {1, 2, 3}, + {3, 4, 5}, + {5, 6, 7} +}; diff --git a/cpp/ql/test/library-tests/locations/aggregate_literals/nested.expected b/cpp/ql/test/library-tests/locations/aggregate_literals/nested.expected new file mode 100644 index 000000000000..2bdafeeda27a --- /dev/null +++ b/cpp/ql/test/library-tests/locations/aggregate_literals/nested.expected @@ -0,0 +1,4 @@ +| 3 | +| 4 | +| 5 | +| 6 | diff --git a/cpp/ql/test/library-tests/locations/aggregate_literals/nested.ql b/cpp/ql/test/library-tests/locations/aggregate_literals/nested.ql new file mode 100644 index 000000000000..68120ed186e8 --- /dev/null +++ b/cpp/ql/test/library-tests/locations/aggregate_literals/nested.ql @@ -0,0 +1,5 @@ +import cpp + +from AggregateLiteral al +where al.getFile().getShortName() = "nested" +select al.getLocation().getStartLine() diff --git a/cpp/ql/test/library-tests/locations/calls/calls.expected b/cpp/ql/test/library-tests/locations/calls/calls.expected new file mode 100644 index 000000000000..0d47ee9aed8e --- /dev/null +++ b/cpp/ql/test/library-tests/locations/calls/calls.expected @@ -0,0 +1,6 @@ +| holder.cpp:13:2:13:20 | call to Holder | holder.cpp:5:2:5:7 | Holder | +| holder.cpp:17:14:17:15 | call to Holder | holder.cpp:5:2:5:7 | Holder | +| holder.cpp:18:18:18:31 | call to Holder | holder.cpp:5:2:5:7 | Holder | +| holder.cpp:19:2:19:3 | call to Holder | holder.cpp:6:2:6:7 | Holder | +| holder.cpp:19:5:19:5 | call to operator+ | holder.cpp:12:11:12:19 | operator+ | +| holder.cpp:19:7:19:8 | call to Holder | holder.cpp:6:2:6:7 | Holder | diff --git a/cpp/ql/test/library-tests/locations/calls/calls.ql b/cpp/ql/test/library-tests/locations/calls/calls.ql new file mode 100644 index 000000000000..f52a0677281b --- /dev/null +++ b/cpp/ql/test/library-tests/locations/calls/calls.ql @@ -0,0 +1,4 @@ +import cpp + +from FunctionCall fc +select fc, fc.getTarget() diff --git a/cpp/ql/test/library-tests/locations/calls/holder.cpp b/cpp/ql/test/library-tests/locations/calls/holder.cpp new file mode 100644 index 000000000000..2258a2cabf65 --- /dev/null +++ b/cpp/ql/test/library-tests/locations/calls/holder.cpp @@ -0,0 +1,20 @@ +template +class Holder { + T myT; +public: + Holder() {} + Holder(const Holder &other) { + myT = other.myT; + } +}; + +template +Holder operator+(Holder h1, Holder h2) { + return Holder(); +} + +void test() { + Holder h1; + Holder h2 = Holder(); + h1 + h2; +} diff --git a/cpp/ql/test/library-tests/locations/constants/c.c b/cpp/ql/test/library-tests/locations/constants/c.c new file mode 100644 index 000000000000..b3906db11435 --- /dev/null +++ b/cpp/ql/test/library-tests/locations/constants/c.c @@ -0,0 +1,12 @@ + +enum MyCEnum { + MyCVal = 5, + MyNextVal = 6 +}; + +struct MyCStruct { + int es[2]; +}; + +static const struct MyCStruct s = {{ MyCVal, MyNextVal }}; + diff --git a/cpp/ql/test/library-tests/locations/constants/cpp.cpp b/cpp/ql/test/library-tests/locations/constants/cpp.cpp new file mode 100644 index 000000000000..26d9f3e70f69 --- /dev/null +++ b/cpp/ql/test/library-tests/locations/constants/cpp.cpp @@ -0,0 +1,12 @@ + +enum MyEnum { + MyVal = 5, + MyNextVal = 6 +}; + +struct MyStruct { + MyEnum es[2]; +}; + +static const MyStruct s = {{ MyVal, MyNextVal }}; + diff --git a/cpp/ql/test/library-tests/locations/constants/locations.expected b/cpp/ql/test/library-tests/locations/constants/locations.expected new file mode 100644 index 000000000000..6a2766c4984f --- /dev/null +++ b/cpp/ql/test/library-tests/locations/constants/locations.expected @@ -0,0 +1,13 @@ +| c.c:3:12:3:12 | 5 | 5 | 5 | +| c.c:4:15:4:15 | 6 | 6 | 6 | +| c.c:8:10:8:10 | 2 | 2 | 2 | +| c.c:11:38:11:43 | Conversion of MyCVal | 5 | MyCVal | +| c.c:11:38:11:43 | MyCVal | 5 | MyCVal | +| c.c:11:46:11:54 | Conversion of MyNextVal | 6 | MyNextVal | +| c.c:11:46:11:54 | MyNextVal | 6 | MyNextVal | +| cpp.cpp:3:11:3:11 | 5 | 5 | 5 | +| cpp.cpp:4:15:4:15 | 6 | 6 | 6 | +| cpp.cpp:8:13:8:13 | 2 | 2 | 2 | +| cpp.cpp:8:13:8:13 | Conversion of 2 | 2 | 2 | +| cpp.cpp:11:30:11:34 | MyVal | 5 | MyVal | +| cpp.cpp:11:37:11:45 | MyNextVal | 6 | MyNextVal | diff --git a/cpp/ql/test/library-tests/locations/constants/locations.ql b/cpp/ql/test/library-tests/locations/constants/locations.ql new file mode 100644 index 000000000000..701ddd867bac --- /dev/null +++ b/cpp/ql/test/library-tests/locations/constants/locations.ql @@ -0,0 +1,12 @@ +import cpp + +/* + * Plainer output for CStyleCast, that does not reference the (potentially platform + * dependent) type of the cast. + */ +class CStyleCastPlain extends CStyleCast { + override string toString() { result = "Conversion of " + getExpr().toString() } +} + +from Expr e +select e, e.getValue(), e.getValueText() diff --git a/cpp/ql/test/library-tests/locations/enum_initialisers/enum_initialisers.c b/cpp/ql/test/library-tests/locations/enum_initialisers/enum_initialisers.c new file mode 100644 index 000000000000..d2f6963e8865 --- /dev/null +++ b/cpp/ql/test/library-tests/locations/enum_initialisers/enum_initialisers.c @@ -0,0 +1,9 @@ +enum MisleadingAlphabet { + alpha, + bravo = alpha +}; + +int main() { + int result = bravo; + return result; +} diff --git a/cpp/ql/test/library-tests/locations/enum_initialisers/enum_initialisers.expected b/cpp/ql/test/library-tests/locations/enum_initialisers/enum_initialisers.expected new file mode 100644 index 000000000000..94b52eb23d76 --- /dev/null +++ b/cpp/ql/test/library-tests/locations/enum_initialisers/enum_initialisers.expected @@ -0,0 +1,2 @@ +| enum_initialisers.c:3:11:3:15 | alpha | +| enum_initialisers.c:7:16:7:20 | bravo | diff --git a/cpp/ql/test/library-tests/locations/enum_initialisers/enum_initialisers.ql b/cpp/ql/test/library-tests/locations/enum_initialisers/enum_initialisers.ql new file mode 100644 index 000000000000..78b76f2274bc --- /dev/null +++ b/cpp/ql/test/library-tests/locations/enum_initialisers/enum_initialisers.ql @@ -0,0 +1,4 @@ +import cpp + +from EnumConstantAccess eca +select eca diff --git a/cpp/ql/test/library-tests/locations/overloaded_operators/locations.expected b/cpp/ql/test/library-tests/locations/overloaded_operators/locations.expected new file mode 100644 index 000000000000..e512cb4794c1 --- /dev/null +++ b/cpp/ql/test/library-tests/locations/overloaded_operators/locations.expected @@ -0,0 +1,7 @@ +| test.cpp:9:12:9:35 | call to MyInt | +| test.cpp:26:18:26:23 | call to MyInt | +| test.cpp:42:15:42:15 | call to operator+ | +| test.cpp:43:5:43:5 | call to operator+= | +| test.cpp:44:3:44:3 | call to operator++ | +| test.cpp:45:4:45:4 | call to operator++ | +| test.cpp:46:5:46:5 | call to operator>>= | diff --git a/cpp/ql/test/library-tests/locations/overloaded_operators/locations.ql b/cpp/ql/test/library-tests/locations/overloaded_operators/locations.ql new file mode 100644 index 000000000000..c22d8d273141 --- /dev/null +++ b/cpp/ql/test/library-tests/locations/overloaded_operators/locations.ql @@ -0,0 +1,4 @@ +import cpp + +from FunctionCall fc +select fc diff --git a/cpp/ql/test/library-tests/locations/overloaded_operators/test.cpp b/cpp/ql/test/library-tests/locations/overloaded_operators/test.cpp new file mode 100644 index 000000000000..a00a6a9fc8e5 --- /dev/null +++ b/cpp/ql/test/library-tests/locations/overloaded_operators/test.cpp @@ -0,0 +1,47 @@ + +class MyInt +{ +public: + MyInt(int _value) : value(_value) {}; + + MyInt operator+(const MyInt &rhs) + { + return MyInt(value + rhs.value); + } + + MyInt &operator+=(const MyInt &rhs) + { + value += rhs.value; + return *this; + } + + MyInt &operator++() // ++x + { + value++; + return *this; + } + + MyInt operator++(int) // x++ + { + MyInt result(value); + value++; + return result; + } + + MyInt &operator>>=(int amount) + { + value >>= amount; + return *this; + } + + int value; +}; + +void test(MyInt a, MyInt b) +{ + MyInt c = a + b; + c += b; + ++c; + c++; + c >>= 4; +} diff --git a/cpp/ql/test/library-tests/locations/udt_implicit_conversions/four.cpp b/cpp/ql/test/library-tests/locations/udt_implicit_conversions/four.cpp new file mode 100644 index 000000000000..4abec82f678b --- /dev/null +++ b/cpp/ql/test/library-tests/locations/udt_implicit_conversions/four.cpp @@ -0,0 +1,10 @@ +struct Four { + operator int() { + return 4; + } +}; + +int main() { + Four four; + return four; // There is a call to operator int() here. [ITS LOCATION SHOULD BE THE ENTIRE WORD 'four'] +} diff --git a/cpp/ql/test/library-tests/locations/udt_implicit_conversions/udt_implicit_conversions.expected b/cpp/ql/test/library-tests/locations/udt_implicit_conversions/udt_implicit_conversions.expected new file mode 100644 index 000000000000..aaee3f360fee --- /dev/null +++ b/cpp/ql/test/library-tests/locations/udt_implicit_conversions/udt_implicit_conversions.expected @@ -0,0 +1 @@ +| four.cpp:9:10:9:10 | call to operator int | diff --git a/cpp/ql/test/library-tests/locations/udt_implicit_conversions/udt_implicit_conversions.ql b/cpp/ql/test/library-tests/locations/udt_implicit_conversions/udt_implicit_conversions.ql new file mode 100644 index 000000000000..c22d8d273141 --- /dev/null +++ b/cpp/ql/test/library-tests/locations/udt_implicit_conversions/udt_implicit_conversions.ql @@ -0,0 +1,4 @@ +import cpp + +from FunctionCall fc +select fc diff --git a/cpp/ql/test/library-tests/loops/loops.cpp b/cpp/ql/test/library-tests/loops/loops.cpp new file mode 100644 index 000000000000..e87c918225cd --- /dev/null +++ b/cpp/ql/test/library-tests/loops/loops.cpp @@ -0,0 +1,36 @@ +void update(bool b); +void init(int &i); +bool cond(int i); +void update(int &i); + +void test_while() +{ + bool b; + + b = true; + while (b) update(b); + + do + { + update(b); + } while (b); +} + +void test_for() +{ + int i, j; + + for (i = 0; i < 10; i++) { + } + + for (j = 10; j >= 0; ) j--; + + for (i = 0, j = 0; i < 10; i++, j++) { + } + + for (init(i); cond(i); update(i)) { + } + + for ((i = 100); (i >= 0); (i--)) { + } +} diff --git a/cpp/ql/test/library-tests/loops/loops.expected b/cpp/ql/test/library-tests/loops/loops.expected new file mode 100644 index 000000000000..5b1549e9e33f --- /dev/null +++ b/cpp/ql/test/library-tests/loops/loops.expected @@ -0,0 +1,34 @@ +| loops.cpp:11:2:11:21 | while (...) ... | getCondition() | loops.cpp:11:9:11:9 | b | +| loops.cpp:11:2:11:21 | while (...) ... | getStmt() | loops.cpp:11:12:11:21 | ExprStmt: call to update | +| loops.cpp:13:2:16:13 | do (...) ... | getCondition() | loops.cpp:16:11:16:11 | b | +| loops.cpp:13:2:16:13 | do (...) ... | getStmt() | loops.cpp:14:2:16:2 | { ... } | +| loops.cpp:23:2:24:2 | for(...;...;...) ... | (ForStmt).getAnIterationVariable() | loops.cpp:21:6:21:6 | i | +| loops.cpp:23:2:24:2 | for(...;...;...) ... | (ForStmt).getInitialization() | loops.cpp:23:7:23:12 | ExprStmt: ... = ... | +| loops.cpp:23:2:24:2 | for(...;...;...) ... | (ForStmt).getUpdate() | loops.cpp:23:22:23:24 | ... ++ | +| loops.cpp:23:2:24:2 | for(...;...;...) ... | getCondition() | loops.cpp:23:14:23:19 | ... < ... | +| loops.cpp:23:2:24:2 | for(...;...;...) ... | getStmt() | loops.cpp:23:27:24:2 | { ... } | +| loops.cpp:26:2:26:28 | for(...;...;...) ... | (ForStmt).getInitialization() | loops.cpp:26:7:26:13 | ExprStmt: ... = ... | +| loops.cpp:26:2:26:28 | for(...;...;...) ... | getCondition() | loops.cpp:26:15:26:20 | ... >= ... | +| loops.cpp:26:2:26:28 | for(...;...;...) ... | getStmt() | loops.cpp:26:25:26:28 | ExprStmt: ... -- | +| loops.cpp:28:2:29:2 | for(...;...;...) ... | (ForStmt).getAnIterationVariable() | loops.cpp:21:6:21:6 | i | +| loops.cpp:28:2:29:2 | for(...;...;...) ... | (ForStmt).getInitialization() | loops.cpp:28:7:28:19 | ExprStmt: ... , ... | +| loops.cpp:28:2:29:2 | for(...;...;...) ... | (ForStmt).getUpdate() | loops.cpp:28:29:28:36 | ... , ... | +| loops.cpp:28:2:29:2 | for(...;...;...) ... | getCondition() | loops.cpp:28:21:28:26 | ... < ... | +| loops.cpp:28:2:29:2 | for(...;...;...) ... | getStmt() | loops.cpp:28:39:29:2 | { ... } | +| loops.cpp:31:2:32:2 | for(...;...;...) ... | (ForStmt).getInitialization() | loops.cpp:31:7:31:14 | ExprStmt: call to init | +| loops.cpp:31:2:32:2 | for(...;...;...) ... | (ForStmt).getUpdate() | loops.cpp:31:25:31:30 | call to update | +| loops.cpp:31:2:32:2 | for(...;...;...) ... | getCondition() | loops.cpp:31:16:31:19 | call to cond | +| loops.cpp:31:2:32:2 | for(...;...;...) ... | getStmt() | loops.cpp:31:36:32:2 | { ... } | +| loops.cpp:34:2:35:2 | for(...;...;...) ... | (ForStmt).getAnIterationVariable() | loops.cpp:21:6:21:6 | i | +| loops.cpp:34:2:35:2 | for(...;...;...) ... | (ForStmt).getInitialization() | loops.cpp:34:7:34:16 | ExprStmt: ... = ... | +| loops.cpp:34:2:35:2 | for(...;...;...) ... | (ForStmt).getUpdate() | loops.cpp:34:29:34:31 | ... -- | +| loops.cpp:34:2:35:2 | for(...;...;...) ... | getCondition() | loops.cpp:34:19:34:24 | ... >= ... | +| loops.cpp:34:2:35:2 | for(...;...;...) ... | getStmt() | loops.cpp:34:35:35:2 | { ... } | +| rbfl.cpp:11:3:13:3 | for(...:...) ... | (RangeBasedForStmt).getEnclosingFunction().getATemplateArgument() | file://:0:0:0:0 | int | +| rbfl.cpp:11:3:13:3 | for(...:...) ... | (RangeBasedForStmt).getEnclosingFunction().getATemplateArgument() | rbfl.cpp:8:20:8:20 | T | +| rbfl.cpp:11:3:13:3 | for(...:...) ... | (RangeBasedForStmt).getUpdate() | file://:0:0:0:0 | ++ ... | +| rbfl.cpp:11:3:13:3 | for(...:...) ... | (RangeBasedForStmt).getVariable() | rbfl.cpp:11:10:11:12 | itr | +| rbfl.cpp:11:3:13:3 | for(...:...) ... | (RangeBasedForStmt).getVariable() | rbfl.cpp:11:10:11:12 | itr | +| rbfl.cpp:11:3:13:3 | for(...:...) ... | getCondition() | file://:0:0:0:0 | ... != ... | +| rbfl.cpp:11:3:13:3 | for(...:...) ... | getStmt() | rbfl.cpp:12:3:13:3 | { ... } | +| rbfl.cpp:11:3:13:3 | for(...:...) ... | getStmt() | rbfl.cpp:12:3:13:3 | { ... } | diff --git a/cpp/ql/test/library-tests/loops/loops.ql b/cpp/ql/test/library-tests/loops/loops.ql new file mode 100644 index 000000000000..55bc198159ca --- /dev/null +++ b/cpp/ql/test/library-tests/loops/loops.ql @@ -0,0 +1,36 @@ +import cpp + +class ExprStmt_ extends ExprStmt { + override string toString() { + result = "ExprStmt: " + getExpr().toString() + } +} + +from Loop l, string s, Element e +where + ( + s = "getCondition()" and + e = l.getCondition() + ) or ( + s = "getStmt()" and + e = l.getStmt() + ) or ( + s = "(ForStmt).getInitialization()" and + e = l.(ForStmt).getInitialization() + ) or ( + s = "(ForStmt).getUpdate()" and + e = l.(ForStmt).getUpdate() + ) or ( + s = "(ForStmt).getAnIterationVariable()" and + e = l.(ForStmt).getAnIterationVariable() + ) or ( + s = "(RangeBasedForStmt).getVariable()" and + e = l.(RangeBasedForStmt).getVariable() + ) or ( + s = "(RangeBasedForStmt).getUpdate()" and + e = l.(RangeBasedForStmt).getUpdate() + ) or ( + s = "(RangeBasedForStmt).getEnclosingFunction().getATemplateArgument()" and + e = l.(RangeBasedForStmt).getEnclosingFunction().getATemplateArgument() + ) +select l, s, e diff --git a/cpp/ql/test/library-tests/loops/rbfl.cpp b/cpp/ql/test/library-tests/loops/rbfl.cpp new file mode 100644 index 000000000000..a252d1d98ef8 --- /dev/null +++ b/cpp/ql/test/library-tests/loops/rbfl.cpp @@ -0,0 +1,21 @@ +template +struct vector +{ + T* begin() { return nullptr; } + T* end() { return nullptr; } +}; + +template +void stream_it(vector& t) +{ + for(T& itr : t) + { + } +} + +int main() +{ + vector xs; + stream_it(xs); + return 0; +} diff --git a/cpp/ql/test/library-tests/lossy_pointer_cast/lossy_pointer_cast.c b/cpp/ql/test/library-tests/lossy_pointer_cast/lossy_pointer_cast.c new file mode 100644 index 000000000000..8f76cdb42f20 --- /dev/null +++ b/cpp/ql/test/library-tests/lossy_pointer_cast/lossy_pointer_cast.c @@ -0,0 +1,16 @@ + +void f(void) { + void *p1, *p2, *p3, *p4; + + short int i; + long long int z; + + z = (long long int)p1; // OK: long long int is big enough + i = (short int)p2; // Bad: short is too small + i = (short int)(long long int)p3; // OK: we assume they know what + // they are doing if they go + // via a large-enough type + i = (short int)(void *)p4; // Bad: Going via a pointer type is + // not convincing +} + diff --git a/cpp/ql/test/library-tests/lossy_pointer_cast/lossy_pointer_cast.expected b/cpp/ql/test/library-tests/lossy_pointer_cast/lossy_pointer_cast.expected new file mode 100644 index 000000000000..c291cc8e5e0d --- /dev/null +++ b/cpp/ql/test/library-tests/lossy_pointer_cast/lossy_pointer_cast.expected @@ -0,0 +1,2 @@ +| lossy_pointer_cast.c:9:20:9:21 | p2 | Converted from void * to smaller type short | +| lossy_pointer_cast.c:13:20:13:29 | (void *)... | Converted from void * to smaller type short | diff --git a/cpp/ql/test/library-tests/lossy_pointer_cast/lossy_pointer_cast.qlref b/cpp/ql/test/library-tests/lossy_pointer_cast/lossy_pointer_cast.qlref new file mode 100644 index 000000000000..d202b53c6aa6 --- /dev/null +++ b/cpp/ql/test/library-tests/lossy_pointer_cast/lossy_pointer_cast.qlref @@ -0,0 +1 @@ +Likely Bugs/Conversion/LossyPointerCast.ql diff --git a/cpp/ql/test/library-tests/macros/affects/affects.expected b/cpp/ql/test/library-tests/macros/affects/affects.expected new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/cpp/ql/test/library-tests/macros/affects/affects.ql b/cpp/ql/test/library-tests/macros/affects/affects.ql new file mode 100644 index 000000000000..a7595855b9c2 --- /dev/null +++ b/cpp/ql/test/library-tests/macros/affects/affects.ql @@ -0,0 +1,6 @@ +import cpp + +from Element e, MacroAccess ma +where affectedbymacroexpansion(e, ma) +select e.getLocation(), e, ma.getLocation(), ma + diff --git a/cpp/ql/test/library-tests/macros/affects/test.c b/cpp/ql/test/library-tests/macros/affects/test.c new file mode 100644 index 000000000000..29d949a08d5c --- /dev/null +++ b/cpp/ql/test/library-tests/macros/affects/test.c @@ -0,0 +1,8 @@ + +#define FOO 1 + +#if defined(FOO) +#endif + +int i = 80; + diff --git a/cpp/ql/test/library-tests/macros/arguments/ascii_nul.c b/cpp/ql/test/library-tests/macros/arguments/ascii_nul.c new file mode 100644 index 000000000000..843f40747d5b Binary files /dev/null and b/cpp/ql/test/library-tests/macros/arguments/ascii_nul.c differ diff --git a/cpp/ql/test/library-tests/macros/arguments/macro_arguments.expected b/cpp/ql/test/library-tests/macros/arguments/macro_arguments.expected new file mode 100644 index 000000000000..0ddac7d27a6f --- /dev/null +++ b/cpp/ql/test/library-tests/macros/arguments/macro_arguments.expected @@ -0,0 +1,55 @@ +| :0:0:0:0 | :0:0:0:0 | top | 0 | 1 | 1 | +| :0:0:0:0 | top_and_nested.h:2:16:2:24 | top -> nested | 0 | 1 | | +| :0:0:0:0 | top_and_nested.h:3:16:3:30 | top -> nested | 0 | 2 + (1) | | +| ascii_nul.c:7:1:7:34 | ascii_nul.c:3:20:3:32 | IGNORE2 -> IGNORE | 0 | ID("escaped: \\0, raw: \u0000") | | +| ascii_nul.c:7:1:7:34 | ascii_nul.c:7:1:7:34 | IGNORE2 | 0 | ID("escaped: \\0, raw: \u0000") | "escaped: \\0, raw: \u0000" | +| ascii_nul.c:7:1:7:34 | ascii_nul.c:7:9:7:33 | IGNORE2 -> ID | 0 | "escaped: \\0, raw: \u0000" | "escaped: \\0, raw: \u0000" | +| test.c:4:1:4:6 | test.c:4:1:4:6 | top | 0 | 1 | 1 | +| test.c:4:1:4:6 | top_and_nested.h:2:16:2:24 | top -> nested | 0 | 1 | | +| test.c:4:1:4:6 | top_and_nested.h:3:16:3:30 | top -> nested | 0 | 2 + (1) | | +| test.c:6:1:8:1 | test.c:6:1:8:1 | top | 0 | "\\"Hello " "world\\"" | "\\"Hello " "world\\"" | +| test.c:6:1:8:1 | top_and_nested.h:2:16:2:24 | top -> nested | 0 | "\\"Hello " "world\\"" | | +| test.c:6:1:8:1 | top_and_nested.h:3:16:3:30 | top -> nested | 0 | 2 + ("\\"Hello " "world\\"") | | +| test.c:10:1:12:4 | test.c:10:1:12:4 | top | 0 | top(2) nested(4) | | +| test.c:10:1:12:4 | test.c:11:5:11:10 | top -> top | 0 | 2 | 2 | +| test.c:10:1:12:4 | test.c:11:38:11:46 | top -> nested | 0 | 4 | | +| test.c:10:1:12:4 | top_and_nested.h:2:16:2:24 | top -> nested | 0 | | | +| test.c:10:1:12:4 | top_and_nested.h:2:16:2:24 | top -> top -> nested | 0 | 2 | | +| test.c:10:1:12:4 | top_and_nested.h:3:16:3:30 | top -> nested | 0 | 2 + () | | +| test.c:10:1:12:4 | top_and_nested.h:3:16:3:30 | top -> top -> nested | 0 | 2 + (2) | | +| test.c:22:17:24:6 | test.c:22:17:24:6 | CONCAT | 0 | HELLO | "hello" | +| test.c:22:17:24:6 | test.c:22:17:24:6 | CONCAT | 1 | WORLD | "word" | +| test.c:27:17:33:1 | test.c:27:17:33:1 | CONCAT | 0 | "hello" | "hello" | +| test.c:27:17:33:1 | test.c:27:17:33:1 | CONCAT | 1 | "Semmle" | "Semmle" | +| test.c:42:1:42:13 | test.c:42:1:42:13 | APPLY | 0 | top | top | +| test.c:42:1:42:13 | test.c:42:1:42:13 | APPLY | 1 | 3 | 3 | +| test.c:42:1:42:13 | test.c:42:7:41:24 | APPLY -> top | 0 | 3 | 3 | +| test.c:42:1:42:13 | top_and_nested.h:2:16:2:24 | APPLY -> top -> nested | 0 | 3 | | +| test.c:42:1:42:13 | top_and_nested.h:3:16:3:30 | APPLY -> top -> nested | 0 | 2 + (3) | | +| test.c:47:1:47:23 | test.c:45:15:45:22 | DECLARE_STRING -> ID | 0 | string1 | string1 | +| test.c:47:1:47:23 | test.c:45:26:45:34 | DECLARE_STRING -> ID | 0 | "string1" | "string1" | +| test.c:47:1:47:23 | test.c:47:1:47:23 | DECLARE_STRING | 0 | string1 | string1 | +| test.c:48:1:48:27 | test.c:45:15:45:22 | DECLARE_STRING -> ID | 0 | string2 | string2 | +| test.c:48:1:48:27 | test.c:45:26:45:34 | DECLARE_STRING -> ID | 0 | "ID(string2)" | "ID(string2)" | +| test.c:48:1:48:27 | test.c:48:1:48:27 | DECLARE_STRING | 0 | ID(string2) | string2 | +| test.c:48:1:48:27 | test.c:48:16:48:26 | DECLARE_STRING -> ID | 0 | string2 | string2 | +| test.c:53:17:54:26 | test.c:53:17:54:26 | ID | 0 | ID (1) | 1 | +| test.c:53:17:54:26 | test.c:53:21:54:25 | ID -> ID | 0 | 1 | 1 | +| test.c:55:17:56:27 | test.c:50:21:50:25 | APPLY_ID -> ID | 0 | 2 | 2 | +| test.c:55:17:56:27 | test.c:55:17:56:27 | APPLY_ID | 0 | 2 | 2 | +| test.c:62:1:62:11 | test.c:62:1:62:11 | VARARG_ID | 0 | | | +| test.c:70:3:70:37 | test.c:70:3:70:37 | VARARG_SECOND | 0 | "Format string only" | "Format string only" | +| test.c:70:3:70:37 | test.c:70:3:70:37 | VARARG_SECOND | 1 | | | +| test.c:73:3:73:30 | test.c:73:3:73:30 | VARARG_SECOND | 0 | "%d %d" | "%d %d" | +| test.c:73:3:73:30 | test.c:73:3:73:30 | VARARG_SECOND | 1 | i, j | | +| test.c:79:1:79:45 | test.c:79:1:79:45 | ID | 0 | int myarray <: :> = VARARG_ID(<% 4, 5 %>) | int myarray <: :> = <% 4, 5 %> | +| test.c:79:1:79:45 | test.c:79:24:79:44 | ID -> VARARG_ID | 0 | <% 4, 5 %> | <% 4, 5 %> | +| test.c:81:30:81:37 | test.c:81:30:81:37 | APPLY | 0 | | | +| test.c:81:30:81:37 | test.c:81:30:81:37 | APPLY | 1 | | | +| test.c:82:1:82:4 | test.c:82:1:82:4 | ID | 0 | | | +| test.c:84:5:84:20 | test.c:84:5:84:20 | APPLY | 0 | ID | ID | +| test.c:84:5:84:20 | test.c:84:5:84:20 | APPLY | 1 | ID(1) | 1 | +| test.c:84:5:84:20 | test.c:84:11:41:24 | APPLY -> ID | 0 | 1 | 1 | +| test.c:84:5:84:20 | test.c:84:15:84:19 | APPLY -> ID | 0 | 1 | 1 | +| test.c:85:21:85:40 | test.c:85:21:85:40 | CMD_LINE_MACRO | 0 | 5 | 5 | +| test.c:85:21:85:40 | test.c:85:21:85:40 | CMD_LINE_MACRO | 1 | 6 | 6 | diff --git a/cpp/ql/test/library-tests/macros/arguments/macro_arguments.ql b/cpp/ql/test/library-tests/macros/arguments/macro_arguments.ql new file mode 100644 index 000000000000..047ee6a950ab --- /dev/null +++ b/cpp/ql/test/library-tests/macros/arguments/macro_arguments.ql @@ -0,0 +1,21 @@ +import cpp + +string prettyPrintMacroInvocation(MacroInvocation mi) { + not exists(mi.getParentInvocation()) and + result = mi.getMacro().getName() + or + result = prettyPrintMacroInvocation(mi.getParentInvocation()) + + " -> " + + mi.getMacro().getName() +} + +from MacroInvocation mi, int arg_index, string unexpanded, string expanded +where unexpanded = mi.getUnexpandedArgument(arg_index) + and expanded = mi.getExpandedArgument(arg_index) +select + mi.getLocation().toString().regexpCapture(".*/([^/]*)$", 1), + mi.getActualLocation().toString().regexpCapture(".*/([^/]*)$", 1), + prettyPrintMacroInvocation(mi), + arg_index, + unexpanded, + expanded diff --git a/cpp/ql/test/library-tests/macros/arguments/split1.h b/cpp/ql/test/library-tests/macros/arguments/split1.h new file mode 100644 index 000000000000..0ed8fcfb08b7 --- /dev/null +++ b/cpp/ql/test/library-tests/macros/arguments/split1.h @@ -0,0 +1 @@ +top( diff --git a/cpp/ql/test/library-tests/macros/arguments/split2.h b/cpp/ql/test/library-tests/macros/arguments/split2.h new file mode 100644 index 000000000000..5e36ce0121e4 --- /dev/null +++ b/cpp/ql/test/library-tests/macros/arguments/split2.h @@ -0,0 +1 @@ +1) diff --git a/cpp/ql/test/library-tests/macros/arguments/test.c b/cpp/ql/test/library-tests/macros/arguments/test.c new file mode 100644 index 000000000000..287b4517f88e --- /dev/null +++ b/cpp/ql/test/library-tests/macros/arguments/test.c @@ -0,0 +1,86 @@ +#include "top_and_nested.h" +#define ID(x) x +// semmle-extractor-options: -DCMD_LINE_MACRO(x,y)=x+y +top(1) + +top("\"Hello " + "world\"" +) + +top( + top(2) /* comment here top(3) */ nested(4) + ) + +// This code is rejected by any sensible compiler. It is included in this test +// to ensure that it does not crash the extractor. +#include "split1.h" +#include "split2.h" + +// macro defined (`#define`) + used inside a macro invocation argument list +#define CONCAT(x, y) x y +#define HELLO "hello" +const char *s = CONCAT(HELLO, +#define WORLD "word" +WORLD); + +// preprocessor condition (`#if`) inside a macro invocation argument list +const char *t = CONCAT("hello", +#if defined(NOTDEFINED) + "world" +#else + "Semmle" +#endif +); + + + +// Extracting text for object-like macros would just waste space +#define object_like_macro 123 +static const int number = object_like_macro; + +#define APPLY(M, X) M(X) +APPLY(top, 3) + +#define DECLARE_STRING(name) \ + const char *ID(name) = ID(#name); + +DECLARE_STRING(string1) +DECLARE_STRING(ID(string2)) + +#define APPLY_ID(x) ID(x) + +// These two invocations are from the documentation in Macro.qll +const int one = ID (ID + (1)); +const int two = APPLY_ID + (2); + +#define NOPARAMS() +NOPARAMS() + +#define VARARG_ID(...) __VA_ARGS__ +VARARG_ID() + +// The extractor runs in GCC dialect mode for our unit tests, so here we can +// make use of the non-standard extension to put ## in front of __VA_ARGS__. +#define VARARG_SECOND(fmt, ...) \ + printf(fmt "\n", ##__VA_ARGS__) + +void va1(void) { + VARARG_SECOND("Format string only"); +} +void va2(int i, int j) { + VARARG_SECOND("%d %d", i, j); +} + +// Digraphs are preserved in the database. Our default dialect options for unit +// tests do not appear to support trigraphs. The VARARG_ID macro here is used +// to protect the comma from being seen as beginning a second argument to ID. +ID(int myarray <: :> = VARARG_ID(<% 4, 5 %>)); + +int empty_parentheses_follow APPLY(,) ; +ID() + +#if APPLY(ID, ID(1)) +int from_cmd_line = CMD_LINE_MACRO(5, 6); +#endif diff --git a/cpp/ql/test/library-tests/macros/arguments/top_and_nested.h b/cpp/ql/test/library-tests/macros/arguments/top_and_nested.h new file mode 100644 index 000000000000..d6306d8d0629 --- /dev/null +++ b/cpp/ql/test/library-tests/macros/arguments/top_and_nested.h @@ -0,0 +1,3 @@ +#define nested(y) +#define top(x) nested(x) \ + nested(2 + (x)) diff --git a/cpp/ql/test/library-tests/macros/inmacroexpansion/inmacroexpansion.expected b/cpp/ql/test/library-tests/macros/inmacroexpansion/inmacroexpansion.expected new file mode 100644 index 000000000000..487f23c7baeb --- /dev/null +++ b/cpp/ql/test/library-tests/macros/inmacroexpansion/inmacroexpansion.expected @@ -0,0 +1,20 @@ +| file://:0:0:0:0 | operator= | false | +| file://:0:0:0:0 | operator= | 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 | declaration of operator= | false | +| test.cpp:4:1:4:1 | declaration of operator= | false | +| test.cpp:4:1:4:1 | operator= | false | +| test.cpp:4:1:4:1 | operator= | false | +| test.cpp:4:1:4:3 | FOO | false | +| test.cpp:4:1:4:3 | S | false | +| test.cpp:4:1:4:3 | declaration | true | +| test.cpp:4:1:4:3 | definition of S | true | +| test.cpp:4:1:4:3 | definition of f | true | +| test.cpp:4:1:4:3 | definition of i | true | +| test.cpp:4:1:4:3 | definition of j | true | +| test.cpp:4:1:4:3 | f | false | +| test.cpp:4:1:4:3 | i | false | +| test.cpp:4:1:4:3 | j | true | +| test.cpp:4:1:4:3 | return ... | true | +| test.cpp:4:1:4:3 | { ... } | true | diff --git a/cpp/ql/test/library-tests/macros/inmacroexpansion/inmacroexpansion.ql b/cpp/ql/test/library-tests/macros/inmacroexpansion/inmacroexpansion.ql new file mode 100644 index 000000000000..e58ea5c3b615 --- /dev/null +++ b/cpp/ql/test/library-tests/macros/inmacroexpansion/inmacroexpansion.ql @@ -0,0 +1,8 @@ +import cpp + +from Element e +where not e.getLocation() instanceof UnknownLocation + and not e instanceof Folder +select e, + any(boolean b | if e.isInMacroExpansion() then b = true else b = false) + diff --git a/cpp/ql/test/library-tests/macros/inmacroexpansion/test.cpp b/cpp/ql/test/library-tests/macros/inmacroexpansion/test.cpp new file mode 100644 index 000000000000..7cc1d581a0a9 --- /dev/null +++ b/cpp/ql/test/library-tests/macros/inmacroexpansion/test.cpp @@ -0,0 +1,5 @@ + +#define FOO class S{int i; void f(void) { int j; return; } }; + +FOO + diff --git a/cpp/ql/test/library-tests/macros/macros/affectedbymacroexpansion.expected b/cpp/ql/test/library-tests/macros/macros/affectedbymacroexpansion.expected new file mode 100644 index 000000000000..2f70165ffdfa --- /dev/null +++ b/cpp/ql/test/library-tests/macros/macros/affectedbymacroexpansion.expected @@ -0,0 +1,45 @@ +| test.c:45:15:71:1 | { ... } | test.c:47:9:47:12 | STMT | +| test.c:45:15:71:1 | { ... } | test.c:52:9:52:12 | STMT | +| test.c:45:15:71:1 | { ... } | test.c:56:5:56:9 | BLOCK | +| test.c:45:15:71:1 | { ... } | test.c:58:5:58:19 | STUFF_AND_BLOCK | +| test.c:45:15:71:1 | { ... } | test.c:62:5:62:23 | STUFF_AND_END_BLOCK | +| test.c:45:15:71:1 | { ... } | test.c:64:5:64:25 | STUFF_AND_START_BLOCK | +| test.c:45:15:71:1 | { ... } | test.c:68:5:68:15 | MEGA_MACRO1 | +| test.c:45:15:71:1 | { ... } | test.c:68:5:68:15 | STMT | +| test.c:45:15:71:1 | { ... } | test.c:68:5:68:15 | STMT | +| test.c:45:15:71:1 | { ... } | test.c:68:5:68:15 | STMT | +| test.c:45:15:71:1 | { ... } | test.c:68:5:68:15 | STMT1 | +| test.c:45:15:71:1 | { ... } | test.c:68:5:68:15 | STMT2 | +| test.c:45:15:71:1 | { ... } | test.c:68:5:68:15 | STMT3 | +| test.c:45:15:71:1 | { ... } | test.c:68:5:68:15 | STUFF_AND_END_BLOCK | +| test.c:45:15:71:1 | { ... } | test.c:68:5:68:15 | STUFF_AND_START_BLOCK | +| test.c:45:15:71:1 | { ... } | test.c:70:5:70:15 | MEGA_MACRO2 | +| test.c:45:15:71:1 | { ... } | test.c:70:5:70:15 | STMT | +| test.c:45:15:71:1 | { ... } | test.c:70:5:70:15 | STMT | +| test.c:45:15:71:1 | { ... } | test.c:70:5:70:15 | STMT | +| test.c:45:15:71:1 | { ... } | test.c:70:5:70:15 | STMT4 | +| test.c:45:15:71:1 | { ... } | test.c:70:5:70:15 | STMT5 | +| test.c:45:15:71:1 | { ... } | test.c:70:5:70:15 | STMT6 | +| test.c:45:15:71:1 | { ... } | test.c:70:5:70:15 | STUFF_AND_START_BLOCK | +| test.c:46:5:48:5 | { ... } | test.c:47:9:47:12 | STMT | +| test.c:50:5:54:5 | { ... } | test.c:52:9:52:12 | STMT | +| test.c:60:5:62:23 | { ... } | test.c:62:5:62:23 | STUFF_AND_END_BLOCK | +| test.c:64:5:66:5 | { ... } | test.c:64:5:64:25 | STUFF_AND_START_BLOCK | +| test.c:68:5:68:15 | { ... } | test.c:68:5:68:15 | STMT | +| test.c:68:5:68:15 | { ... } | test.c:68:5:68:15 | STMT2 | +| test.c:68:5:68:15 | { ... } | test.c:68:5:68:15 | STUFF_AND_END_BLOCK | +| test.c:68:5:68:15 | { ... } | test.c:68:5:68:15 | STUFF_AND_START_BLOCK | +| test.c:70:5:70:15 | { ... } | test.c:70:5:70:15 | STMT | +| test.c:70:5:70:15 | { ... } | test.c:70:5:70:15 | STMT5 | +| test.c:70:5:70:15 | { ... } | test.c:70:5:70:15 | STUFF_AND_START_BLOCK | +| test.c:77:15:79:1 | { ... } | test.c:78:5:78:6 | P0 | +| test.c:77:15:79:1 | { ... } | test.c:78:5:78:6 | P1 | +| test.c:77:15:79:1 | { ... } | test.c:78:5:78:6 | P2 | +| test.c:78:3:78:6 | { ... } | test.c:78:5:78:6 | P2 | +| test.c:78:5:78:6 | { ... } | test.c:78:5:78:6 | P0 | +| test.c:85:15:87:1 | { ... } | test.c:86:5:86:10 | P3 | +| test.c:85:15:87:1 | { ... } | test.c:86:5:86:10 | P4 | +| test.c:85:15:87:1 | { ... } | test.c:86:5:86:10 | P5(x) | +| test.c:86:3:86:10 | { ... } | test.c:86:5:86:10 | P3 | +| test.c:86:3:86:10 | { ... } | test.c:86:5:86:10 | P4 | +| test.c:86:3:86:10 | { ... } | test.c:86:5:86:10 | P5(x) | diff --git a/cpp/ql/test/library-tests/macros/macros/affectedbymacroexpansion.ql b/cpp/ql/test/library-tests/macros/macros/affectedbymacroexpansion.ql new file mode 100644 index 000000000000..45254648a6ad --- /dev/null +++ b/cpp/ql/test/library-tests/macros/macros/affectedbymacroexpansion.ql @@ -0,0 +1,6 @@ +import cpp + +from Block b, MacroAccess m +where affectedbymacroexpansion(b, m) +select b, m + diff --git a/cpp/ql/test/library-tests/macros/macros/inmacroexpansion.expected b/cpp/ql/test/library-tests/macros/macros/inmacroexpansion.expected new file mode 100644 index 000000000000..7e63ac9f7293 --- /dev/null +++ b/cpp/ql/test/library-tests/macros/macros/inmacroexpansion.expected @@ -0,0 +1,8 @@ +| test.c:56:5:56:9 | { ... } | test.c:56:5:56:9 | BLOCK | +| test.c:58:5:58:19 | { ... } | test.c:58:5:58:19 | STUFF_AND_BLOCK | +| test.c:68:5:68:15 | { ... } | test.c:68:5:68:15 | MEGA_MACRO1 | +| test.c:70:5:70:15 | { ... } | test.c:70:5:70:15 | MEGA_MACRO2 | +| test.c:78:5:78:6 | { ... } | test.c:78:5:78:6 | P1 | +| test.c:78:5:78:6 | { ... } | test.c:78:5:78:6 | P2 | +| test.c:86:5:86:10 | { ... } | test.c:86:5:86:10 | P4 | +| test.c:86:5:86:10 | { ... } | test.c:86:5:86:10 | P5(x) | diff --git a/cpp/ql/test/library-tests/macros/macros/inmacroexpansion.ql b/cpp/ql/test/library-tests/macros/macros/inmacroexpansion.ql new file mode 100644 index 000000000000..736cca37436c --- /dev/null +++ b/cpp/ql/test/library-tests/macros/macros/inmacroexpansion.ql @@ -0,0 +1,6 @@ +import cpp + +from Block b, MacroAccess m +where inmacroexpansion(b, m) +select b, m + diff --git a/cpp/ql/test/library-tests/macros/macros/test.c b/cpp/ql/test/library-tests/macros/macros/test.c new file mode 100644 index 000000000000..db319fa52bb7 --- /dev/null +++ b/cpp/ql/test/library-tests/macros/macros/test.c @@ -0,0 +1,88 @@ + +int x; + +#define STMT x = 1; + +#define BLOCK { \ + x = 1; \ + } + +#define STUFF_AND_BLOCK x = 1; \ + { \ + x = 1; \ + } \ + x = 1; + +#define STUFF_AND_START_BLOCK x = 1; \ + { \ + x = 1; + +#define STUFF_AND_END_BLOCK x = 1; \ + } \ + x = 1; + +#define STMT1 STMT +#define STMT2 STMT +#define STMT3 STMT + +#define MEGA_MACRO1 STMT1 \ + STUFF_AND_START_BLOCK \ + STMT2 \ + STUFF_AND_END_BLOCK \ + STMT3 + +#define STMT4 STMT +#define STMT5 STMT +#define STMT6 STMT + +#define MEGA_MACRO2 STMT4 \ + STUFF_AND_START_BLOCK \ + STMT5 \ + } \ + STMT6 + + +void f1(void) { + { + STMT + } + + { + x = 1; + STMT + x = 1; + } + + BLOCK + + STUFF_AND_BLOCK + + { + x = 1; + STUFF_AND_END_BLOCK + + STUFF_AND_START_BLOCK + x = 1; + } + + MEGA_MACRO1 + + MEGA_MACRO2 +} + +#define P2 } P1 +#define P1 { P0 } +#define P0 ; + +void f2(void) { + { P2 +} + +#define P5(x) } x +#define P4 { P3 } +#define P3 ; + +void f3(void) { + { P5(P4) +} + diff --git a/cpp/ql/test/library-tests/macros/redefines/ppds.expected b/cpp/ql/test/library-tests/macros/redefines/ppds.expected new file mode 100644 index 000000000000..1aaf8ed84016 --- /dev/null +++ b/cpp/ql/test/library-tests/macros/redefines/ppds.expected @@ -0,0 +1,6 @@ +| redefines.c:1:1:1:15 | #define X a b c | +| redefines.c:2:1:2:15 | #define Y d e f | +| redefines.c:3:1:3:15 | #define Z g h i | +| redefines.c:4:1:4:15 | #define X a b c | +| redefines.c:5:1:5:15 | #define Y d e f | +| redefines.c:6:1:6:15 | #define Z j k l | diff --git a/cpp/ql/test/library-tests/macros/redefines/ppds.ql b/cpp/ql/test/library-tests/macros/redefines/ppds.ql new file mode 100644 index 000000000000..1e7e9822b1cc --- /dev/null +++ b/cpp/ql/test/library-tests/macros/redefines/ppds.ql @@ -0,0 +1,4 @@ +import cpp + +from PreprocessorDirective ppd +select ppd diff --git a/cpp/ql/test/library-tests/macros/redefines/redefines.c b/cpp/ql/test/library-tests/macros/redefines/redefines.c new file mode 100644 index 000000000000..3087e999b187 --- /dev/null +++ b/cpp/ql/test/library-tests/macros/redefines/redefines.c @@ -0,0 +1,6 @@ +#define X a b c +#define Y d e f +#define Z g h i +#define X a b c +#define Y d e f +#define Z j k l diff --git a/cpp/ql/test/library-tests/members/getters/members.expected b/cpp/ql/test/library-tests/members/getters/members.expected new file mode 100644 index 000000000000..ce7b7f6e56b0 --- /dev/null +++ b/cpp/ql/test/library-tests/members/getters/members.expected @@ -0,0 +1,24 @@ +| file://:0:0:0:0 | | test.cpp:34:6:34:6 | g | g() | Orphan | +| test.cpp:2:7:2:7 | C | test.cpp:2:7:2:7 | operator= | C::operator=(C &&) | getAMember(), getAMember(5), getAMemberFunction(), getCanonicalMember(5), getDeclaringType() | +| test.cpp:2:7:2:7 | C | test.cpp:2:7:2:7 | operator= | C::operator=(const C &) | getAMember(), getAMember(4), getAMemberFunction(), getCanonicalMember(4), getDeclaringType() | +| test.cpp:2:7:2:7 | C | test.cpp:5:10:5:21 | f_template_C | C::f_template_C(T) | getAMember(), getAMember(0), getAMemberFunction(), getCanonicalMember(0), getDeclaringType() | +| test.cpp:2:7:2:7 | C | test.cpp:5:10:5:21 | f_template_C | C::f_template_C(double) | getAMember(), getAMember(0), getAMemberFunction(), getDeclaringType() | +| test.cpp:2:7:2:7 | C | test.cpp:5:10:5:21 | f_template_C | C::f_template_C(int) | getAMember(), getAMember(0), getAMemberFunction(), getDeclaringType() | +| 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:8:10:8:23 | f_template_C_D | C::f_template_C_D(int) | getAMember(), getAMember(1), getAMemberFunction(), 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 | 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:23 | f_template_C_D | D::f_template_C_D(T) | getAMember(), getAMember(0), getAMemberFunction(), getCanonicalMember(0), getDeclaringType() | +| test.cpp:14:7:14:7 | D | test.cpp:17:10:17:23 | f_template_C_D | D::f_template_C_D(double) | getAMember(), getAMember(0), getAMemberFunction(), getDeclaringType() | +| test.cpp:14:7:14:7 | D | test.cpp:20:10:20:21 | f_template_D | D::f_template_D(T) | getAMember(), getAMember(1), getAMemberFunction(), getCanonicalMember(1), getDeclaringType() | +| test.cpp:14:7:14:7 | D | test.cpp:20:10:20:21 | f_template_D | D::f_template_D(double) | getAMember(), getAMember(1), getAMemberFunction(), getDeclaringType() | +| test.cpp:14:7:14:7 | D | test.cpp:22:10:22:14 | f_C_D | D::f_C_D() | getAMember(), getAMember(2), getAMemberFunction(), getCanonicalMember(2), getDeclaringType() | +| test.cpp:14:7:14:7 | D | test.cpp:23:10:23:12 | f_D | D::f_D() | getAMember(), getAMember(3), getAMemberFunction(), getCanonicalMember(3), getDeclaringType() | +| test.cpp:27:7:27:7 | E | test.cpp:29:10:29:12 | f_E | E::f_E() | getAMember(), getAMember(0), getAMemberFunction(), getCanonicalMember(0), getDeclaringType() | +| test.cpp:27:7:27:7 | E | test.cpp:31:10:31:16 | f_E_arg | E::f_E_arg(E) | getAMember(), getAMember(1), getAMemberFunction(), getCanonicalMember(1), getDeclaringType() | +| test.cpp:27:7:27:7 | E | test.cpp:27:7:27:7 | operator= | E::operator=(E &&) | getAMember(), getAMember(3), getAMemberFunction(), getCanonicalMember(3), getDeclaringType() | +| test.cpp:27:7:27:7 | E | test.cpp:27:7:27:7 | operator= | E::operator=(const E &) | getAMember(), getAMember(2), getAMemberFunction(), getCanonicalMember(2), getDeclaringType() | +| test.cpp:27:7:27:7 | E | test.cpp:29:10:29:12 | f_E | E::f_E() | getAMember(), getAMember(0), getAMemberFunction(), getCanonicalMember(0), getDeclaringType() | +| test.cpp:27:7:27:7 | E | test.cpp:31:10:31:16 | f_E_arg | E::f_E_arg(E) | getAMember(), getAMember(1), getAMemberFunction(), getCanonicalMember(1), getDeclaringType() | diff --git a/cpp/ql/test/library-tests/members/getters/members.ql b/cpp/ql/test/library-tests/members/getters/members.ql new file mode 100644 index 000000000000..b36b56a4e4c1 --- /dev/null +++ b/cpp/ql/test/library-tests/members/getters/members.ql @@ -0,0 +1,72 @@ +import cpp + +newtype TMaybeClass = TClass(Class c) or TNoClass() + +class MaybeClass extends TMaybeClass { + abstract string toString(); + abstract Location getLocation(); + abstract string relation(Function f); +} + +string relation(Class c, Function f) { + exists(int i | f = c.getCanonicalMember(i) and result = "getCanonicalMember(" + i + ")") + or exists(int i | f = c.getAMember(i) and result = "getAMember(" + i + ")") + or (f = c.getAMember() and result = "getAMember()") + or (f = c.getAMemberFunction() and result = "getAMemberFunction()") + or (f.getDeclaringType() = c and result = "getDeclaringType()") +} + +class YesMaybeClass extends MaybeClass { + Class c; + + YesMaybeClass() { + this = TClass(c) + } + + override string toString() { + result = c.toString() + } + + override Location getLocation() { + result = c.getLocation() + } + + override string relation(Function f) { + result = relation(c, f) + } +} + +class NoMaybeClass extends MaybeClass { + NoMaybeClass() { + this = TNoClass() + } + + override string toString() { + result = "" + } + + override Location getLocation() { + result instanceof UnknownLocation + } + + override string relation(Function f) { + not exists(relation(_, f)) + and result = "Orphan" + } +} + +string functionName(Function f) { + exists(string name, string templateArgs, string args | + result = name + templateArgs + args + and name = f.getQualifiedName() + and if exists(f.getATemplateArgument()) + then templateArgs = "<" + concat(int i | | f.getTemplateArgument(i).toString(), "," order by i) + ">" + else templateArgs = "" + and args = "(" + concat(int i | | f.getParameter(i).getType().toString(), "," order by i) + ")") +} + +from MaybeClass m, Function f +where not f.getDeclaringType().getName() = "__va_list_tag" + and exists(m.relation(f)) +select m, f, functionName(f), concat(m.relation(f), ", ") + diff --git a/cpp/ql/test/library-tests/members/getters/test.cpp b/cpp/ql/test/library-tests/members/getters/test.cpp new file mode 100644 index 000000000000..a01215e2b013 --- /dev/null +++ b/cpp/ql/test/library-tests/members/getters/test.cpp @@ -0,0 +1,55 @@ + +class C { +public: + template + void f_template_C(T t) { } + + template + void f_template_C_D(T t) { } + + void f_C(void) { } + void f_C_D(void) { } +}; + +class D : public C { +public: + template + void f_template_C_D(T t) { } + + template + void f_template_D(T t) { } + + void f_C_D(void) { } + void f_D(void) { } +}; + +template +class E { +public: + void f_E(void) {} + + void f_E_arg(E e) {} +}; + +void g(void) { + int vi; + double vd; + + C c; + c.f_template_C(vi); + c.f_template_C_D(vi); + c.f_C(); + c.f_C_D(); + D d; + d.f_template_C(vd); + d.f_template_C_D(vd); + d.f_template_D(vd); + d.f_C(); + d.f_C_D(); + d.f_D(); + + E e; + e.f_E(); + e.f_E_arg(e); +} + diff --git a/cpp/ql/test/library-tests/members/members/diags.expected b/cpp/ql/test/library-tests/members/members/diags.expected new file mode 100644 index 000000000000..d9a833eb1f89 --- /dev/null +++ b/cpp/ql/test/library-tests/members/members/diags.expected @@ -0,0 +1,2 @@ +| file://:0:0:0:0 | There was an error during this compilation | 4 | Error occurred | There was an error during this compilation | +| non-existent.cpp:3:9:3:9 | declaration is incompatible with 'void S::f(int)' (declared at line 2) | 4 | not_compatible_with_previous_decl | "non-existent.cpp", line 3: error: declaration is incompatible with "void S::f(int)" (declared at line 2)\n void S::f(char) {}\n ^\n\n | diff --git a/cpp/ql/test/library-tests/members/members/diags.ql b/cpp/ql/test/library-tests/members/members/diags.ql new file mode 100644 index 000000000000..8c255348ea7a --- /dev/null +++ b/cpp/ql/test/library-tests/members/members/diags.ql @@ -0,0 +1,4 @@ +import cpp + +from Diagnostic d +select d, d.getSeverity(), d.getTag(), d.getFullMessage() diff --git a/cpp/ql/test/library-tests/members/members/functions.expected b/cpp/ql/test/library-tests/members/members/functions.expected new file mode 100644 index 000000000000..c11db3ba5bfc --- /dev/null +++ b/cpp/ql/test/library-tests/members/members/functions.expected @@ -0,0 +1,5 @@ +| file://:0:0:0:0 | operator= | +| file://:0:0:0:0 | operator= | +| non-existent.cpp:2:8:2:8 | operator= | +| non-existent.cpp:2:8:2:8 | operator= | +| non-existent.cpp:2:17:2:17 | f | diff --git a/cpp/ql/test/library-tests/members/members/functions.ql b/cpp/ql/test/library-tests/members/members/functions.ql new file mode 100644 index 000000000000..af1f94dc9b5a --- /dev/null +++ b/cpp/ql/test/library-tests/members/members/functions.ql @@ -0,0 +1,5 @@ +import cpp + +from Function f +select f + diff --git a/cpp/ql/test/library-tests/members/members/non-existent.cpp b/cpp/ql/test/library-tests/members/members/non-existent.cpp new file mode 100644 index 000000000000..6a4b518626fc --- /dev/null +++ b/cpp/ql/test/library-tests/members/members/non-existent.cpp @@ -0,0 +1,4 @@ +// semmle-extractor-options: --expect_errors +struct S { void f(int); }; +void S::f(char) {} + diff --git a/cpp/ql/test/library-tests/members/templates/c1.cpp b/cpp/ql/test/library-tests/members/templates/c1.cpp new file mode 100644 index 000000000000..d407b580f6e4 --- /dev/null +++ b/cpp/ql/test/library-tests/members/templates/c1.cpp @@ -0,0 +1,7 @@ + +#include "h.h" + +static void f(void) { + CA::CB x; + CA::CB y; +} diff --git a/cpp/ql/test/library-tests/members/templates/c2.cpp b/cpp/ql/test/library-tests/members/templates/c2.cpp new file mode 100644 index 000000000000..fd44a84ba77b --- /dev/null +++ b/cpp/ql/test/library-tests/members/templates/c2.cpp @@ -0,0 +1,7 @@ + +#include "h.h" + +static void f(void) { + CA::CB x; + CA::CB y; +} diff --git a/cpp/ql/test/library-tests/members/templates/h.h b/cpp/ql/test/library-tests/members/templates/h.h new file mode 100644 index 000000000000..0000cfd477db --- /dev/null +++ b/cpp/ql/test/library-tests/members/templates/h.h @@ -0,0 +1,9 @@ + +class CA { +public: + template + class CB { + public: + }; +}; + diff --git a/cpp/ql/test/library-tests/members/templates/members.expected b/cpp/ql/test/library-tests/members/templates/members.expected new file mode 100644 index 000000000000..770b85cc7b66 --- /dev/null +++ b/cpp/ql/test/library-tests/members/templates/members.expected @@ -0,0 +1,19 @@ +| file://:0:0:0:0 | __va_list_tag | 0 __va_list_tag::gp_offset | +| file://:0:0:0:0 | __va_list_tag | 1 __va_list_tag::fp_offset | +| file://:0:0:0:0 | __va_list_tag | 2 __va_list_tag::overflow_arg_area | +| file://:0:0:0:0 | __va_list_tag | 3 __va_list_tag::reg_save_area | +| file://:0:0:0:0 | __va_list_tag | 4 __va_list_tag::operator= | +| file://:0:0:0:0 | __va_list_tag | 5 __va_list_tag::operator= | +| h.h:2:7:2:8 | CA | 0 CA::CB | +| h.h:2:7:2:8 | CA | 0 CA::CB | +| h.h:2:7:2:8 | CA | 0 CA::CB | +| h.h:2:7:2:8 | CA | 0 CA::CB | +| h.h:2:7:2:8 | CA | 1 CA::operator= | +| h.h:2:7:2:8 | CA | 2 CA::operator= | +| h.h:5:11:5:12 | CB | | +| h.h:5:11:5:12 | CB | 0 CA::CB::operator= | +| h.h:5:11:5:12 | CB | 1 CA::CB::operator= | +| h.h:5:11:5:12 | CB | 0 CA::CB::operator= | +| h.h:5:11:5:12 | CB | 1 CA::CB::operator= | +| h.h:5:11:5:12 | CB | 0 CA::CB::operator= | +| h.h:5:11:5:12 | CB | 1 CA::CB::operator= | diff --git a/cpp/ql/test/library-tests/members/templates/members.ql b/cpp/ql/test/library-tests/members/templates/members.ql new file mode 100644 index 000000000000..2e9e53a06ebc --- /dev/null +++ b/cpp/ql/test/library-tests/members/templates/members.ql @@ -0,0 +1,11 @@ +import cpp + +string mem(Class c) { + if exists(c.getAMember()) + then exists(int i | result = i.toString() + " " + c.getAMember(i).getQualifiedName()) + else result = "" +} + +from Class c +select c, mem(c) + diff --git a/cpp/ql/test/library-tests/multiple_declarations/conditional_field/conditional_field.expected b/cpp/ql/test/library-tests/multiple_declarations/conditional_field/conditional_field.expected new file mode 100644 index 000000000000..17221bf8ec9e --- /dev/null +++ b/cpp/ql/test/library-tests/multiple_declarations/conditional_field/conditional_field.expected @@ -0,0 +1,7 @@ +| file://:0:0:0:0 | __va_list_tag | 0 | file://:0:0:0:0 | gp_offset | 1 | +| file://:0:0:0:0 | __va_list_tag | 1 | file://:0:0:0:0 | fp_offset | 1 | +| file://:0:0:0:0 | __va_list_tag | 2 | file://:0:0:0:0 | overflow_arg_area | 1 | +| file://:0:0:0:0 | __va_list_tag | 3 | file://:0:0:0:0 | reg_save_area | 1 | +| header.h:5:8:5:8 | S | 0 | header.h:7:9:7:9 | a | 1 | +| header.h:5:8:5:8 | S | 0 | header.h:9:9:9:9 | b | 1 | +| header.h:5:8:5:8 | S | 1 | header.h:9:9:9:9 | b | 1 | diff --git a/cpp/ql/test/library-tests/multiple_declarations/conditional_field/conditional_field.ql b/cpp/ql/test/library-tests/multiple_declarations/conditional_field/conditional_field.ql new file mode 100644 index 000000000000..fc7a19c4eefd --- /dev/null +++ b/cpp/ql/test/library-tests/multiple_declarations/conditional_field/conditional_field.ql @@ -0,0 +1,4 @@ +import cpp + +from Struct s, int i +select s, i, s.getAMember(i), count(s.getAMember(i)) diff --git a/cpp/ql/test/library-tests/multiple_declarations/conditional_field/has_a.c b/cpp/ql/test/library-tests/multiple_declarations/conditional_field/has_a.c new file mode 100644 index 000000000000..d6452fa6b89d --- /dev/null +++ b/cpp/ql/test/library-tests/multiple_declarations/conditional_field/has_a.c @@ -0,0 +1,2 @@ +#define S_HAS_A +#include "header.h" diff --git a/cpp/ql/test/library-tests/multiple_declarations/conditional_field/header.h b/cpp/ql/test/library-tests/multiple_declarations/conditional_field/header.h new file mode 100644 index 000000000000..cb6849765101 --- /dev/null +++ b/cpp/ql/test/library-tests/multiple_declarations/conditional_field/header.h @@ -0,0 +1,10 @@ +// When this header is included both with and without `S_HAS_A` defined, the +// struct ends up with three fields: one copy of `a` at position 0, a copy of +// `b` at position 0, and a copy of `b` at position 1. + +struct S { +#ifdef S_HAS_A + int a; +#endif + int b; +}; diff --git a/cpp/ql/test/library-tests/multiple_declarations/conditional_field/no_a.c b/cpp/ql/test/library-tests/multiple_declarations/conditional_field/no_a.c new file mode 100644 index 000000000000..cebd2cebd0b6 --- /dev/null +++ b/cpp/ql/test/library-tests/multiple_declarations/conditional_field/no_a.c @@ -0,0 +1,2 @@ +#undef S_HAS_A +#include "header.h" diff --git a/cpp/ql/test/library-tests/multiple_declarations/functions/arity2.c b/cpp/ql/test/library-tests/multiple_declarations/functions/arity2.c new file mode 100644 index 000000000000..6ecab5340713 --- /dev/null +++ b/cpp/ql/test/library-tests/multiple_declarations/functions/arity2.c @@ -0,0 +1 @@ +int f(int, int); diff --git a/cpp/ql/test/library-tests/multiple_declarations/functions/count_f.expected b/cpp/ql/test/library-tests/multiple_declarations/functions/count_f.expected new file mode 100644 index 000000000000..2a4f078a25fc --- /dev/null +++ b/cpp/ql/test/library-tests/multiple_declarations/functions/count_f.expected @@ -0,0 +1 @@ +| 1 | diff --git a/cpp/ql/test/library-tests/multiple_declarations/functions/count_f.ql b/cpp/ql/test/library-tests/multiple_declarations/functions/count_f.ql new file mode 100644 index 000000000000..76ae821ae152 --- /dev/null +++ b/cpp/ql/test/library-tests/multiple_declarations/functions/count_f.ql @@ -0,0 +1,3 @@ +import cpp + +select count(Function f | f.getName() = "f") diff --git a/cpp/ql/test/library-tests/multiple_declarations/functions/getParameter.expected b/cpp/ql/test/library-tests/multiple_declarations/functions/getParameter.expected new file mode 100644 index 000000000000..c6f3339fc8c0 --- /dev/null +++ b/cpp/ql/test/library-tests/multiple_declarations/functions/getParameter.expected @@ -0,0 +1,4 @@ +| 0 | arity2.c:1:7:1:9 | p#0 | 1 | 3 | +| 0 | incompatible_decl.c:2:9:2:13 | p#0 | 1 | 3 | +| 0 | main.c:2:9:2:13 | p#0 | 1 | 3 | +| 1 | arity2.c:1:12:1:14 | p#1 | 1 | 1 | diff --git a/cpp/ql/test/library-tests/multiple_declarations/functions/getParameter.ql b/cpp/ql/test/library-tests/multiple_declarations/functions/getParameter.ql new file mode 100644 index 000000000000..93ea17762541 --- /dev/null +++ b/cpp/ql/test/library-tests/multiple_declarations/functions/getParameter.ql @@ -0,0 +1,5 @@ +import cpp + +from Function f, int i +where f.getName() = "f" +select i, f.getParameter(i), count(f.getParameter(i)), count(f.getParameter(i).getType()) diff --git a/cpp/ql/test/library-tests/multiple_declarations/functions/getTarget.expected b/cpp/ql/test/library-tests/multiple_declarations/functions/getTarget.expected new file mode 100644 index 000000000000..20eb6e35d389 --- /dev/null +++ b/cpp/ql/test/library-tests/multiple_declarations/functions/getTarget.expected @@ -0,0 +1,3 @@ +| main.c:8:5:8:5 | call to f | arity2.c:1:5:1:5 | f | 1 | +| main.c:8:5:8:5 | call to f | incompatible_decl.c:2:7:2:7 | f | 1 | +| main.c:8:5:8:5 | call to f | main.c:2:7:2:7 | f | 1 | diff --git a/cpp/ql/test/library-tests/multiple_declarations/functions/getTarget.ql b/cpp/ql/test/library-tests/multiple_declarations/functions/getTarget.ql new file mode 100644 index 000000000000..1a1e877be8ca --- /dev/null +++ b/cpp/ql/test/library-tests/multiple_declarations/functions/getTarget.ql @@ -0,0 +1,4 @@ +import cpp + +from FunctionCall fc +select fc, fc.getTarget(), count(fc.getTarget()) diff --git a/cpp/ql/test/library-tests/multiple_declarations/functions/incompatible_decl.c b/cpp/ql/test/library-tests/multiple_declarations/functions/incompatible_decl.c new file mode 100644 index 000000000000..05edfadeb093 --- /dev/null +++ b/cpp/ql/test/library-tests/multiple_declarations/functions/incompatible_decl.c @@ -0,0 +1,2 @@ +typedef short MyInt; +MyInt f(MyInt); diff --git a/cpp/ql/test/library-tests/multiple_declarations/functions/main.c b/cpp/ql/test/library-tests/multiple_declarations/functions/main.c new file mode 100644 index 000000000000..a5d181722eb3 --- /dev/null +++ b/cpp/ql/test/library-tests/multiple_declarations/functions/main.c @@ -0,0 +1,10 @@ +typedef struct { int i; } MyInt; +MyInt f(MyInt); + +int main() { + MyInt myInt = { 0 }; + // This call dispatches to one `Function` that has three `Location`s and + // two `Parameters`. The first `Parameter` has three types. + f(myInt); + return 0; +} diff --git a/cpp/ql/test/library-tests/multiple_declarations/macroinvocations/a.c b/cpp/ql/test/library-tests/multiple_declarations/macroinvocations/a.c new file mode 100644 index 000000000000..80f6aef63df4 --- /dev/null +++ b/cpp/ql/test/library-tests/multiple_declarations/macroinvocations/a.c @@ -0,0 +1,3 @@ +#define UNRELATED_MACRO + +#include "header.h" diff --git a/cpp/ql/test/library-tests/multiple_declarations/macroinvocations/b.c b/cpp/ql/test/library-tests/multiple_declarations/macroinvocations/b.c new file mode 100644 index 000000000000..f5bec35f1cfe --- /dev/null +++ b/cpp/ql/test/library-tests/multiple_declarations/macroinvocations/b.c @@ -0,0 +1 @@ +#include "header.h" diff --git a/cpp/ql/test/library-tests/multiple_declarations/macroinvocations/header.h b/cpp/ql/test/library-tests/multiple_declarations/macroinvocations/header.h new file mode 100644 index 000000000000..240ef31fcfa7 --- /dev/null +++ b/cpp/ql/test/library-tests/multiple_declarations/macroinvocations/header.h @@ -0,0 +1,7 @@ +#define MY_MACRO() + +MY_MACRO() + +#ifdef UNRELATED_MACRO +#undef UNRELATED_MACRO +#endif diff --git a/cpp/ql/test/library-tests/multiple_declarations/macroinvocations/multiple_macro_invocations.expected b/cpp/ql/test/library-tests/multiple_declarations/macroinvocations/multiple_macro_invocations.expected new file mode 100644 index 000000000000..b05a9cc61be9 --- /dev/null +++ b/cpp/ql/test/library-tests/multiple_declarations/macroinvocations/multiple_macro_invocations.expected @@ -0,0 +1,2 @@ +| header.h:3:1:3:10 | MY_MACRO() | +| header.h:3:1:3:10 | MY_MACRO() | diff --git a/cpp/ql/test/library-tests/multiple_declarations/macroinvocations/multiple_macro_invocations.ql b/cpp/ql/test/library-tests/multiple_declarations/macroinvocations/multiple_macro_invocations.ql new file mode 100644 index 000000000000..6c1db3b8dfd7 --- /dev/null +++ b/cpp/ql/test/library-tests/multiple_declarations/macroinvocations/multiple_macro_invocations.ql @@ -0,0 +1,10 @@ +import cpp + +// This test case demonstrates that when a header file invokes a macro, we get +// a `MacroInvocation` in the database for each preprocessor context in which +// the header file is used. This can cause performance issues on large +// databases, where there may be hundreds of such contexts. + +from MacroInvocation mi +where mi.getMacroName() = "MY_MACRO" +select mi diff --git a/cpp/ql/test/library-tests/multiple_declarations/preprocbranch/preprocbranch.cpp b/cpp/ql/test/library-tests/multiple_declarations/preprocbranch/preprocbranch.cpp new file mode 100644 index 000000000000..ba1d73937582 --- /dev/null +++ b/cpp/ql/test/library-tests/multiple_declarations/preprocbranch/preprocbranch.cpp @@ -0,0 +1,19 @@ +#if defined(FOO) + #error "This shouldn't happen" +#elif !defined(BAR) + + #if 1 + // ... + #else + // ... + #endif + +#else + #warning "This shouldn't happen either" +#endif + +#if 0 + // ... +#else + // ... +#endif \ No newline at end of file diff --git a/cpp/ql/test/library-tests/multiple_declarations/preprocbranch/preprocbranch.expected b/cpp/ql/test/library-tests/multiple_declarations/preprocbranch/preprocbranch.expected new file mode 100644 index 000000000000..03e9539116f3 --- /dev/null +++ b/cpp/ql/test/library-tests/multiple_declarations/preprocbranch/preprocbranch.expected @@ -0,0 +1,10 @@ +| preprocbranch.cpp:1:1:1:16 | #if defined(FOO) | preprocbranch.cpp:1:1:1:16 | #if defined(FOO) | 3 | #elif !defined(BAR) | preprocbranch.cpp:13:1:13:6 | #endif | +| preprocbranch.cpp:3:1:3:19 | #elif !defined(BAR) | preprocbranch.cpp:1:1:1:16 | #if defined(FOO) | 11 | #else | preprocbranch.cpp:13:1:13:6 | #endif | +| preprocbranch.cpp:5:1:5:6 | #if 1 | preprocbranch.cpp:5:1:5:6 | #if 1 | 7 | #else | preprocbranch.cpp:9:1:9:7 | #endif | +| preprocbranch.cpp:7:1:7:6 | #else | preprocbranch.cpp:5:1:5:6 | #if 1 | 9 | #endif | preprocbranch.cpp:9:1:9:7 | #endif | +| preprocbranch.cpp:9:1:9:7 | #endif | preprocbranch.cpp:5:1:5:6 | #if 1 | N/A | N/A | preprocbranch.cpp:9:1:9:7 | #endif | +| preprocbranch.cpp:11:1:11:5 | #else | preprocbranch.cpp:1:1:1:16 | #if defined(FOO) | 13 | #endif | preprocbranch.cpp:13:1:13:6 | #endif | +| preprocbranch.cpp:13:1:13:6 | #endif | preprocbranch.cpp:1:1:1:16 | #if defined(FOO) | N/A | N/A | preprocbranch.cpp:13:1:13:6 | #endif | +| preprocbranch.cpp:15:1:15:5 | #if 0 | preprocbranch.cpp:15:1:15:5 | #if 0 | 17 | #else | preprocbranch.cpp:19:1:19:6 | #endif | +| preprocbranch.cpp:17:1:17:5 | #else | preprocbranch.cpp:15:1:15:5 | #if 0 | 19 | #endif | preprocbranch.cpp:19:1:19:6 | #endif | +| preprocbranch.cpp:19:1:19:6 | #endif | preprocbranch.cpp:15:1:15:5 | #if 0 | N/A | N/A | preprocbranch.cpp:19:1:19:6 | #endif | diff --git a/cpp/ql/test/library-tests/multiple_declarations/preprocbranch/preprocbranch.ql b/cpp/ql/test/library-tests/multiple_declarations/preprocbranch/preprocbranch.ql new file mode 100644 index 000000000000..1fd2a7a31d45 --- /dev/null +++ b/cpp/ql/test/library-tests/multiple_declarations/preprocbranch/preprocbranch.ql @@ -0,0 +1,17 @@ +import cpp + +from PreprocessorBranchDirective pbd_if, PreprocessorBranchDirective pbd, string pbd_next, string pbd_next_line, PreprocessorBranchDirective pbd_endif +where + pbd_if = pbd.getIf() and + if exists(pbd.getNext()) then ( + pbd_next = pbd.getNext().toString() and + pbd_next_line = pbd.getNext().getLocation().getStartLine().toString() + ) else ( + pbd_next = "N/A" and + pbd_next_line = "N/A" + ) and + pbd_endif = pbd.getEndIf() +select pbd, + pbd_if, + pbd_next_line, pbd_next, + pbd_endif diff --git a/cpp/ql/test/library-tests/naked_attribute/naked.expected b/cpp/ql/test/library-tests/naked_attribute/naked.expected new file mode 100644 index 000000000000..a968454c7fd0 --- /dev/null +++ b/cpp/ql/test/library-tests/naked_attribute/naked.expected @@ -0,0 +1,2 @@ +| naked.gnu.cpp:3:6:3:8 | gnu | +| naked.ms.cpp:1:24:1:32 | microsoft | diff --git a/cpp/ql/test/library-tests/naked_attribute/naked.gnu.cpp b/cpp/ql/test/library-tests/naked_attribute/naked.gnu.cpp new file mode 100644 index 000000000000..e1211809c91f --- /dev/null +++ b/cpp/ql/test/library-tests/naked_attribute/naked.gnu.cpp @@ -0,0 +1,9 @@ +void gnu() __attribute__((naked)); + +void gnu() +{ +} + +void gnu_not_naked() +{ +} diff --git a/cpp/ql/test/library-tests/naked_attribute/naked.ms.cpp b/cpp/ql/test/library-tests/naked_attribute/naked.ms.cpp new file mode 100644 index 000000000000..3401c4f9f6d7 --- /dev/null +++ b/cpp/ql/test/library-tests/naked_attribute/naked.ms.cpp @@ -0,0 +1,4 @@ +__declspec(naked) void microsoft() {} + +void microsoft_not_naked() {} +// semmle-extractor-options: --microsoft diff --git a/cpp/ql/test/library-tests/naked_attribute/naked.ql b/cpp/ql/test/library-tests/naked_attribute/naked.ql new file mode 100644 index 000000000000..8a35de7ee1f4 --- /dev/null +++ b/cpp/ql/test/library-tests/naked_attribute/naked.ql @@ -0,0 +1,5 @@ +import cpp + +from Function f +where f.isNaked() +select f diff --git a/cpp/ql/test/library-tests/name_qualifiers/NameQualifiers1.expected b/cpp/ql/test/library-tests/name_qualifiers/NameQualifiers1.expected new file mode 100644 index 000000000000..72d7d615c815 --- /dev/null +++ b/cpp/ql/test/library-tests/name_qualifiers/NameQualifiers1.expected @@ -0,0 +1,32 @@ +| name_qualifiers.cpp:29:7:29:8 | :: | name_qualifiers.cpp:29:7:29:9 | x | file://:0:0:0:0 | (global namespace) | +| name_qualifiers.cpp:31:7:31:10 | N1:: | name_qualifiers.cpp:31:7:31:12 | nx | name_qualifiers.cpp:4:11:4:12 | N1 | +| name_qualifiers.cpp:34:7:34:8 | :: | name_qualifiers.cpp:34:9:34:12 | N1:: | file://:0:0:0:0 | (global namespace) | +| name_qualifiers.cpp:34:9:34:12 | N1:: | name_qualifiers.cpp:34:7:34:14 | nx | name_qualifiers.cpp:4:11:4:12 | N1 | +| name_qualifiers.cpp:36:7:36:10 | N1:: | name_qualifiers.cpp:36:11:36:14 | N2:: | name_qualifiers.cpp:4:11:4:12 | N1 | +| name_qualifiers.cpp:36:11:36:14 | N2:: | name_qualifiers.cpp:36:7:36:16 | ny | name_qualifiers.cpp:9:13:9:14 | N1::N2 | +| name_qualifiers.cpp:38:7:38:10 | N2:: | name_qualifiers.cpp:38:7:38:12 | ny | name_qualifiers.cpp:9:13:9:14 | N1::N2 | +| name_qualifiers.cpp:41:7:41:8 | :: | name_qualifiers.cpp:41:9:41:12 | N1:: | file://:0:0:0:0 | (global namespace) | +| name_qualifiers.cpp:41:9:41:12 | N1:: | name_qualifiers.cpp:41:13:41:16 | N2:: | name_qualifiers.cpp:4:11:4:12 | N1 | +| name_qualifiers.cpp:41:13:41:16 | N2:: | name_qualifiers.cpp:41:7:41:18 | ny | name_qualifiers.cpp:9:13:9:14 | N1::N2 | +| name_qualifiers.cpp:43:3:43:8 | Base:: | name_qualifiers.cpp:43:3:43:10 | bx | name_qualifiers.cpp:16:8:16:11 | Base | +| name_qualifiers.cpp:44:7:44:12 | Base:: | name_qualifiers.cpp:44:7:44:14 | bx | name_qualifiers.cpp:16:8:16:11 | Base | +| name_qualifiers.cpp:45:7:45:15 | Derived:: | name_qualifiers.cpp:45:7:45:17 | bx | name_qualifiers.cpp:24:8:24:14 | Derived | +| name_qualifiers.cpp:46:7:46:8 | :: | name_qualifiers.cpp:46:9:46:14 | Base:: | file://:0:0:0:0 | (global namespace) | +| name_qualifiers.cpp:46:9:46:14 | Base:: | name_qualifiers.cpp:46:7:46:16 | bx | name_qualifiers.cpp:16:8:16:11 | Base | +| name_qualifiers.cpp:47:7:47:8 | :: | name_qualifiers.cpp:47:9:47:17 | Derived:: | file://:0:0:0:0 | (global namespace) | +| name_qualifiers.cpp:47:9:47:17 | Derived:: | name_qualifiers.cpp:47:7:47:19 | bx | name_qualifiers.cpp:24:8:24:14 | Derived | +| name_qualifiers.cpp:50:3:50:4 | :: | name_qualifiers.cpp:50:3:50:5 | call to f | file://:0:0:0:0 | (global namespace) | +| name_qualifiers.cpp:52:3:52:6 | N1:: | name_qualifiers.cpp:52:3:52:8 | call to nf | name_qualifiers.cpp:4:11:4:12 | N1 | +| name_qualifiers.cpp:55:3:55:4 | :: | name_qualifiers.cpp:55:5:55:8 | N1:: | file://:0:0:0:0 | (global namespace) | +| name_qualifiers.cpp:55:5:55:8 | N1:: | name_qualifiers.cpp:55:3:55:10 | call to nf | name_qualifiers.cpp:4:11:4:12 | N1 | +| name_qualifiers.cpp:57:3:57:6 | N1:: | name_qualifiers.cpp:57:7:57:10 | N2:: | name_qualifiers.cpp:4:11:4:12 | N1 | +| name_qualifiers.cpp:57:7:57:10 | N2:: | name_qualifiers.cpp:57:3:57:12 | call to ng | name_qualifiers.cpp:9:13:9:14 | N1::N2 | +| name_qualifiers.cpp:58:3:58:4 | :: | name_qualifiers.cpp:58:5:58:8 | N1:: | file://:0:0:0:0 | (global namespace) | +| name_qualifiers.cpp:58:5:58:8 | N1:: | name_qualifiers.cpp:58:9:58:12 | N2:: | name_qualifiers.cpp:4:11:4:12 | N1 | +| name_qualifiers.cpp:58:9:58:12 | N2:: | name_qualifiers.cpp:58:3:58:14 | call to ng | name_qualifiers.cpp:9:13:9:14 | N1::N2 | +| name_qualifiers.cpp:60:3:60:8 | Base:: | name_qualifiers.cpp:60:3:60:10 | call to bf | name_qualifiers.cpp:16:8:16:11 | Base | +| name_qualifiers.cpp:61:3:61:11 | Derived:: | name_qualifiers.cpp:61:3:61:13 | call to bf | name_qualifiers.cpp:24:8:24:14 | Derived | +| name_qualifiers.cpp:62:3:62:4 | :: | name_qualifiers.cpp:62:5:62:10 | Base:: | file://:0:0:0:0 | (global namespace) | +| name_qualifiers.cpp:62:5:62:10 | Base:: | name_qualifiers.cpp:62:3:62:12 | call to bf | name_qualifiers.cpp:16:8:16:11 | Base | +| name_qualifiers.cpp:63:3:63:4 | :: | name_qualifiers.cpp:63:5:63:13 | Derived:: | file://:0:0:0:0 | (global namespace) | +| name_qualifiers.cpp:63:5:63:13 | Derived:: | name_qualifiers.cpp:63:3:63:15 | call to bf | name_qualifiers.cpp:24:8:24:14 | Derived | diff --git a/cpp/ql/test/library-tests/name_qualifiers/NameQualifiers1.ql b/cpp/ql/test/library-tests/name_qualifiers/NameQualifiers1.ql new file mode 100644 index 000000000000..aae7e8509a91 --- /dev/null +++ b/cpp/ql/test/library-tests/name_qualifiers/NameQualifiers1.ql @@ -0,0 +1,6 @@ +import cpp + +from NameQualifier nq, Location l +where l = nq.getQualifiedElement().getLocation() + and l.getFile().getShortName() = "name_qualifiers" +select nq, nq.getQualifiedElement(), nq.getQualifyingElement() diff --git a/cpp/ql/test/library-tests/name_qualifiers/NameQualifiers2.expected b/cpp/ql/test/library-tests/name_qualifiers/NameQualifiers2.expected new file mode 100644 index 000000000000..cb60ab1bb2bb --- /dev/null +++ b/cpp/ql/test/library-tests/name_qualifiers/NameQualifiers2.expected @@ -0,0 +1,10 @@ +| name_qualifiers.cpp:29:7:29:9 | x | +| name_qualifiers.cpp:34:7:34:14 | nx | +| name_qualifiers.cpp:41:7:41:18 | ny | +| name_qualifiers.cpp:46:7:46:16 | bx | +| name_qualifiers.cpp:47:7:47:19 | bx | +| name_qualifiers.cpp:50:3:50:5 | call to f | +| name_qualifiers.cpp:55:3:55:10 | call to nf | +| name_qualifiers.cpp:58:3:58:14 | call to ng | +| name_qualifiers.cpp:62:3:62:12 | call to bf | +| name_qualifiers.cpp:63:3:63:15 | call to bf | diff --git a/cpp/ql/test/library-tests/name_qualifiers/NameQualifiers2.ql b/cpp/ql/test/library-tests/name_qualifiers/NameQualifiers2.ql new file mode 100644 index 000000000000..64ab4f4dcfc0 --- /dev/null +++ b/cpp/ql/test/library-tests/name_qualifiers/NameQualifiers2.ql @@ -0,0 +1,7 @@ +import cpp + +from NameQualifiableElement e +where + e.hasGlobalQualifiedName() + and e instanceof Expr +select e diff --git a/cpp/ql/test/library-tests/name_qualifiers/inconsistency.cpp b/cpp/ql/test/library-tests/name_qualifiers/inconsistency.cpp new file mode 100644 index 000000000000..caa5a6817c1f --- /dev/null +++ b/cpp/ql/test/library-tests/name_qualifiers/inconsistency.cpp @@ -0,0 +1,8 @@ +// This file is present to test whether name-qualifying an enum constant leads to a database inconsistency. +// As such, there is no QL part of the test. + +struct S { enum E { A }; }; + +static int f() { + switch(0) { case S::A: break; } +} diff --git a/cpp/ql/test/library-tests/name_qualifiers/name_qualifiers.cpp b/cpp/ql/test/library-tests/name_qualifiers/name_qualifiers.cpp new file mode 100644 index 000000000000..aadd94e65409 --- /dev/null +++ b/cpp/ql/test/library-tests/name_qualifiers/name_qualifiers.cpp @@ -0,0 +1,66 @@ +int x = 23; +void f() {} + +namespace N1 +{ + void nf() {} + int nx = 9; + + namespace N2 + { + void ng() {} + int ny = 84; + } +} + +struct Base +{ + static void bf() {} + static int bx; +}; + +int Base::bx; + +struct Derived : Base {}; + +int main() +{ + int i = x; + i = ::x; + + i = N1::nx; + using N1::nx; + i = nx; + i = ::N1::nx; + + i = N1::N2::ny; + using namespace N1; + i = N2::ny; + using namespace N2; + i = ny; + i = ::N1::N2::ny; + + Base::bx = 24; + i = Base::bx; + i = Derived::bx; + i = ::Base::bx; + i = ::Derived::bx; + + f(); + ::f(); + + N1::nf(); + using N1::nf; + nf(); + ::N1::nf(); + + N1::N2::ng(); + ::N1::N2::ng(); + + Base::bf(); + Derived::bf(); + ::Base::bf(); + ::Derived::bf(); + + return 0; +} diff --git a/cpp/ql/test/library-tests/namespaces/namespaces/decls.expected b/cpp/ql/test/library-tests/namespaces/namespaces/decls.expected new file mode 100644 index 000000000000..e051d828cdf4 --- /dev/null +++ b/cpp/ql/test/library-tests/namespaces/namespaces/decls.expected @@ -0,0 +1,18 @@ +| file://:0:0:0:0 | (global namespace) | file://:0:0:0:0 | __va_list_tag | +| file://:0:0:0:0 | (global namespace) | file://:0:0:0:0 | fp_offset | +| file://:0:0:0:0 | (global namespace) | file://:0:0:0:0 | gp_offset | +| file://:0:0:0:0 | (global namespace) | file://:0:0:0:0 | operator= | +| file://:0:0:0:0 | (global namespace) | file://:0:0:0:0 | operator= | +| file://:0:0:0:0 | (global namespace) | file://:0:0:0:0 | overflow_arg_area | +| file://:0:0:0:0 | (global namespace) | file://:0:0:0:0 | reg_save_area | +| file://:0:0:0:0 | B | namespaces.cpp:32:7:32:7 | x | +| namespaces.cpp:11:13:11:13 | C::D | file://:0:0:0:0 | p#0 | +| namespaces.cpp:11:13:11:13 | C::D | file://:0:0:0:0 | p#0 | +| namespaces.cpp:11:13:11:13 | C::D | namespaces.cpp:13:17:13:17 | f | +| namespaces.cpp:11:13:11:13 | C::D | namespaces.cpp:15:12:15:12 | E | +| namespaces.cpp:11:13:11:13 | C::D | namespaces.cpp:15:12:15:12 | operator= | +| namespaces.cpp:11:13:11:13 | C::D | namespaces.cpp:15:12:15:12 | operator= | +| namespaces.cpp:11:13:11:13 | C::D | namespaces.cpp:17:12:17:12 | g | +| namespaces.cpp:11:13:11:13 | C::D | namespaces.cpp:17:18:17:18 | p | +| namespaces.cpp:11:13:11:13 | C::D | namespaces.cpp:18:12:18:12 | a | +| namespaces.cpp:11:13:11:13 | C::D | namespaces.cpp:20:13:20:13 | b | diff --git a/cpp/ql/test/library-tests/namespaces/namespaces/decls.ql b/cpp/ql/test/library-tests/namespaces/namespaces/decls.ql new file mode 100644 index 000000000000..c7d60f5aff13 --- /dev/null +++ b/cpp/ql/test/library-tests/namespaces/namespaces/decls.ql @@ -0,0 +1,6 @@ +import cpp + +from Namespace n, Declaration d +where n = d.getNamespace() +select n, d + diff --git a/cpp/ql/test/library-tests/namespaces/namespaces/namespaces.cpp b/cpp/ql/test/library-tests/namespaces/namespaces/namespaces.cpp new file mode 100644 index 000000000000..943271ab218b --- /dev/null +++ b/cpp/ql/test/library-tests/namespaces/namespaces/namespaces.cpp @@ -0,0 +1,38 @@ +namespace A { + +} + +namespace B { + +} + +namespace C { + + namespace D { + + inline int f() { return 0; } + + class E { + + void g(int p) { + int a; + { + int b; + } + } + + }; + + } + +} + +namespace B { + + int x = C::D::f(); + +} + +namespace std { + +} diff --git a/cpp/ql/test/library-tests/namespaces/namespaces/namespaces.expected b/cpp/ql/test/library-tests/namespaces/namespaces/namespaces.expected new file mode 100644 index 000000000000..4837fc272c5f --- /dev/null +++ b/cpp/ql/test/library-tests/namespaces/namespaces/namespaces.expected @@ -0,0 +1,6 @@ +| file://:0:0:0:0 | (global namespace) | | | Yes | | +| file://:0:0:0:0 | B | B | B | Yes | (global namespace) | +| namespaces.cpp:1:11:1:11 | A | A | A | Yes | (global namespace) | +| namespaces.cpp:9:11:9:11 | C | C | C | Yes | (global namespace) | +| namespaces.cpp:11:13:11:13 | C::D | D | C::D | Yes | C | +| namespaces.cpp:36:11:36:13 | std | std | std | Yes | (global namespace) | diff --git a/cpp/ql/test/library-tests/namespaces/namespaces/namespaces.ql b/cpp/ql/test/library-tests/namespaces/namespaces/namespaces.ql new file mode 100644 index 000000000000..2d156f4fb3d9 --- /dev/null +++ b/cpp/ql/test/library-tests/namespaces/namespaces/namespaces.ql @@ -0,0 +1,8 @@ +import cpp + +from Namespace n, string sane, string parent +where if n.hasName(n.getName()) then sane = "Yes" else sane = "No" + and if exists(n.getParentNamespace()) + then parent = n.getParentNamespace().toString() + else parent = "" +select n, n.getName(), n.getQualifiedName(), sane, parent diff --git a/cpp/ql/test/library-tests/namespaces/same_name/decls.expected b/cpp/ql/test/library-tests/namespaces/same_name/decls.expected new file mode 100644 index 000000000000..3d72020f4c4b --- /dev/null +++ b/cpp/ql/test/library-tests/namespaces/same_name/decls.expected @@ -0,0 +1,9 @@ +| file://:0:0:0:0 | (global namespace) | file://:0:0:0:0 | __va_list_tag | +| file://:0:0:0:0 | (global namespace) | file://:0:0:0:0 | fp_offset | +| file://:0:0:0:0 | (global namespace) | file://:0:0:0:0 | gp_offset | +| file://:0:0:0:0 | (global namespace) | file://:0:0:0:0 | operator= | +| file://:0:0:0:0 | (global namespace) | file://:0:0:0:0 | operator= | +| file://:0:0:0:0 | (global namespace) | file://:0:0:0:0 | overflow_arg_area | +| file://:0:0:0:0 | (global namespace) | file://:0:0:0:0 | reg_save_area | +| same_name.cpp:4:11:4:21 | namespace_a | same_name.cpp:2:11:2:11 | c | +| same_name.cpp:4:11:4:21 | namespace_a | same_name.cpp:6:12:6:12 | c | diff --git a/cpp/ql/test/library-tests/namespaces/same_name/decls.ql b/cpp/ql/test/library-tests/namespaces/same_name/decls.ql new file mode 100644 index 000000000000..c7d60f5aff13 --- /dev/null +++ b/cpp/ql/test/library-tests/namespaces/same_name/decls.ql @@ -0,0 +1,6 @@ +import cpp + +from Namespace n, Declaration d +where n = d.getNamespace() +select n, d + diff --git a/cpp/ql/test/library-tests/namespaces/same_name/same_name.cpp b/cpp/ql/test/library-tests/namespaces/same_name/same_name.cpp new file mode 100644 index 000000000000..6e7d7204e73f --- /dev/null +++ b/cpp/ql/test/library-tests/namespaces/same_name/same_name.cpp @@ -0,0 +1,16 @@ + +const int c = 1; + +namespace namespace_a +{ + const int c = 1; +} + +namespace namespace_b +{ + //const int c = 1; + // + // this example is causing a DBCheck failure along the lines of: + // + // [INVALID_KEY] Relation namespacembrs((@namespace parentid, unique @namespacembr memberid)): Value 132 of key field memberid occurs in several tuples. Two such tuples are: (134,132) and (144,132) +} diff --git a/cpp/ql/test/library-tests/nested_functions/nested_functions/namespaces.expected b/cpp/ql/test/library-tests/nested_functions/nested_functions/namespaces.expected new file mode 100644 index 000000000000..2c112db442c6 --- /dev/null +++ b/cpp/ql/test/library-tests/nested_functions/nested_functions/namespaces.expected @@ -0,0 +1,24 @@ +| nested_functions.c:6:5:6:8 | main | (global namespace) | (global namespace) | +| nested_functions.c:7:9:7:9 | i | NONE | (global namespace) | +| nested_functions.c:7:12:7:12 | j | NONE | (global namespace) | +| nested_functions.c:9:9:9:9 | g | (global namespace) | (global namespace) | +| nested_functions.c:9:15:9:15 | x | NONE | (global namespace) | +| nested_functions.c:22:6:22:6 | h | (global namespace) | (global namespace) | +| nested_functions.c:23:11:23:11 | f | NONE | (global namespace) | +| nested_functions.c:25:11:25:11 | g | (global namespace) | (global namespace) | +| nested_functions.c:25:19:25:19 | x | NONE | (global namespace) | +| nested_functions.c:32:6:32:8 | bad | (global namespace) | (global namespace) | +| nested_functions.c:33:9:33:9 | i | NONE | (global namespace) | +| nested_functions.c:35:9:35:9 | g | (global namespace) | (global namespace) | +| nested_functions.c:39:6:39:8 | f_a | (global namespace) | (global namespace) | +| nested_functions.c:44:8:44:10 | f_c | (global namespace) | (global namespace) | +| nested_functions.c:52:6:52:8 | f_b | (global namespace) | (global namespace) | +| nested_functions.cpp:2:7:2:7 | operator= | NONE | (global namespace) | +| nested_functions.cpp:2:7:2:7 | operator= | NONE | (global namespace) | +| nested_functions.cpp:2:7:2:13 | myClass | (global namespace) | (global namespace) | +| nested_functions.cpp:5:7:5:14 | myMethod | NONE | (global namespace) | +| nested_functions.cpp:6:8:6:23 | myNestedFunction | (global namespace) | (global namespace) | +| nested_functions.cpp:23:7:23:9 | f_a | ns | ns | +| nested_functions.cpp:28:9:28:11 | f_c | ns | ns | +| nested_functions.cpp:30:9:30:11 | f_d | ns | ns | +| nested_functions.cpp:36:7:36:9 | f_b | ns | ns | diff --git a/cpp/ql/test/library-tests/nested_functions/nested_functions/namespaces.ql b/cpp/ql/test/library-tests/nested_functions/nested_functions/namespaces.ql new file mode 100644 index 000000000000..d2d2c4637a72 --- /dev/null +++ b/cpp/ql/test/library-tests/nested_functions/nested_functions/namespaces.ql @@ -0,0 +1,24 @@ +import cpp + +Namespace topLevelNamespace(Declaration d) +{ + result.getADeclaration() = d +} + +from Declaration d, string tl, string ns +where + d.getFile().toString() != "" and + ( + if exists(topLevelNamespace(d)) then ( + tl = topLevelNamespace(d).toString() + ) else ( + tl = "NONE" + ) + ) and ( + if exists(d.getNamespace()) then ( + ns = d.getNamespace().toString() + ) else ( + ns = "NONE" + ) + ) +select d, tl, ns diff --git a/cpp/ql/test/library-tests/nested_functions/nested_functions/nested_functions.c b/cpp/ql/test/library-tests/nested_functions/nested_functions/nested_functions.c new file mode 100644 index 000000000000..b0082b9e7abd --- /dev/null +++ b/cpp/ql/test/library-tests/nested_functions/nested_functions/nested_functions.c @@ -0,0 +1,54 @@ + +#ifdef COMPILABLE_TEST +#include +#endif + +int main(void) { + int i, j = 3; + + int g(int x) { + return x + j + 1; + } + + i = g(8); + +#ifdef COMPILABLE_TEST + printf("Got %d\n", i); +#endif + + return 0; +} + +void h(void) { + float f; + + float g(float x) { + return 3.0; + } + + f = g(8); +} + +void bad(void) { + int i; +#ifndef COMPILABLE_TEST + i = g(8); +#endif +} + +void f_a() +{ + void f_b(); // not a nested function, but declared inside a function. + + { + void f_c(); // not a nested function, but declared inside a function. + + /*void f_d() // nested function --- extractor error + { + }*/ + } +} + +void f_b() +{ +} diff --git a/cpp/ql/test/library-tests/nested_functions/nested_functions/nested_functions.cpp b/cpp/ql/test/library-tests/nested_functions/nested_functions/nested_functions.cpp new file mode 100644 index 000000000000..86de533762f7 --- /dev/null +++ b/cpp/ql/test/library-tests/nested_functions/nested_functions/nested_functions.cpp @@ -0,0 +1,39 @@ + +class myClass +{ +public: + void myMethod() { + void myNestedFunction() { // nested function + } + } +}; + +/* +Nested functions are a GCC language extension. The nested function is defined inside the parent, +can't be accessed elsewhere, and has access to the parent's parameters. + +However in standard C/C++ you can put a function declaration inside another function. This is +not a nested function, the only effect it has is to limit the visibility of that single declaration. + +These tests examine both cases. +*/ + +namespace ns +{ + void f_a() + { + void f_b(); // not a nested function, but declared inside a function. + + { + void f_c(); // not a nested function, but declared inside a function. + + void f_d() // nested function + { + } + } + } + + void f_b() + { + } +} diff --git a/cpp/ql/test/library-tests/nested_functions/nested_functions/nested_functions.expected b/cpp/ql/test/library-tests/nested_functions/nested_functions/nested_functions.expected new file mode 100644 index 000000000000..719d4cc6ba4a --- /dev/null +++ b/cpp/ql/test/library-tests/nested_functions/nested_functions/nested_functions.expected @@ -0,0 +1,19 @@ +| file://:0:0:0:0 | operator= | file://:0:0:0:0 | __va_list_tag & | | +| file://:0:0:0:0 | operator= | file://:0:0:0:0 | __va_list_tag & | | +| nested_functions.c:6:5:6:8 | main | file://:0:0:0:0 | int | | +| nested_functions.c:9:9:9:9 | g | file://:0:0:0:0 | int | nested_functions.c:13:9:13:9 call to g | +| nested_functions.c:22:6:22:6 | h | file://:0:0:0:0 | void | | +| nested_functions.c:25:11:25:11 | g | file://:0:0:0:0 | float | nested_functions.c:29:9:29:9 call to g | +| nested_functions.c:32:6:32:8 | bad | file://:0:0:0:0 | void | | +| nested_functions.c:35:9:35:9 | g | file://:0:0:0:0 | int | nested_functions.c:35:9:35:9 call to g | +| nested_functions.c:39:6:39:8 | f_a | file://:0:0:0:0 | void | | +| nested_functions.c:44:8:44:10 | f_c | file://:0:0:0:0 | void | | +| nested_functions.c:52:6:52:8 | f_b | file://:0:0:0:0 | void | | +| nested_functions.cpp:2:7:2:7 | operator= | file://:0:0:0:0 | myClass & | | +| nested_functions.cpp:2:7:2:7 | operator= | file://:0:0:0:0 | myClass & | | +| nested_functions.cpp:5:7:5:14 | myMethod | file://:0:0:0:0 | void | | +| nested_functions.cpp:6:8:6:23 | myNestedFunction | file://:0:0:0:0 | void | | +| nested_functions.cpp:23:7:23:9 | f_a | file://:0:0:0:0 | void | | +| nested_functions.cpp:28:9:28:11 | f_c | file://:0:0:0:0 | void | | +| nested_functions.cpp:30:9:30:11 | f_d | file://:0:0:0:0 | void | | +| nested_functions.cpp:36:7:36:9 | f_b | file://:0:0:0:0 | void | | diff --git a/cpp/ql/test/library-tests/nested_functions/nested_functions/nested_functions.ql b/cpp/ql/test/library-tests/nested_functions/nested_functions/nested_functions.ql new file mode 100644 index 000000000000..f9eeff6efb07 --- /dev/null +++ b/cpp/ql/test/library-tests/nested_functions/nested_functions/nested_functions.ql @@ -0,0 +1,11 @@ +import cpp + +from Function f, string call +where if exists(f.getACallToThisFunction()) + then call = f.getACallToThisFunction().getLocation().toString() + " " + + f.getACallToThisFunction().toString() + else call = "" +select f, + f.getType(), + call + diff --git a/cpp/ql/test/library-tests/nested_functions/nested_functions/var_uses.expected b/cpp/ql/test/library-tests/nested_functions/nested_functions/var_uses.expected new file mode 100644 index 000000000000..3f0b79cccdb6 --- /dev/null +++ b/cpp/ql/test/library-tests/nested_functions/nested_functions/var_uses.expected @@ -0,0 +1,5 @@ +| nested_functions.c:7:9:7:9 | i | nested_functions.c:13:5:13:5 | nested_functions.c:6:5:6:8 | main | +| nested_functions.c:7:12:7:12 | j | nested_functions.c:10:20:10:20 | nested_functions.c:9:9:9:9 | g | +| nested_functions.c:9:15:9:15 | x | nested_functions.c:10:16:10:16 | nested_functions.c:9:9:9:9 | g | +| nested_functions.c:23:11:23:11 | f | nested_functions.c:29:5:29:5 | nested_functions.c:22:6:22:6 | h | +| nested_functions.c:33:9:33:9 | i | nested_functions.c:35:5:35:5 | nested_functions.c:32:6:32:8 | bad | diff --git a/cpp/ql/test/library-tests/nested_functions/nested_functions/var_uses.ql b/cpp/ql/test/library-tests/nested_functions/nested_functions/var_uses.ql new file mode 100644 index 000000000000..f7dfc19d540a --- /dev/null +++ b/cpp/ql/test/library-tests/nested_functions/nested_functions/var_uses.ql @@ -0,0 +1,8 @@ +import cpp + +from Variable v, VariableAccess a +where a = v.getAnAccess() +select v, + a.getLocation().toString(), + a.getEnclosingFunction() + diff --git a/cpp/ql/test/library-tests/noexcept/copy_from_prototype/copy_from_prototype.cpp b/cpp/ql/test/library-tests/noexcept/copy_from_prototype/copy_from_prototype.cpp new file mode 100644 index 000000000000..2fc18f7475a8 --- /dev/null +++ b/cpp/ql/test/library-tests/noexcept/copy_from_prototype/copy_from_prototype.cpp @@ -0,0 +1,27 @@ +// An example that causes copy_from_prototype to be TRUE. +template +class a { + template a() noexcept(123); +}; + +class b : a { +}; + +// A similar example that would be an error if processing of exception +// specifications weren't delayed until they're actually needed. +template +class c { + template c() noexcept(T::X); +}; + +class d : c { +}; + +// Example where copy_from_prototype is FALSE. +template +struct e { + template e() noexcept(456); +}; + +template<> template e::e() + noexcept(789); 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 new file mode 100644 index 000000000000..91a180f53fd2 --- /dev/null +++ b/cpp/ql/test/library-tests/noexcept/copy_from_prototype/copy_from_prototype.expected @@ -0,0 +1,32 @@ +| copy_from_prototype.cpp:3:7:3:7 | a | a::a(a &&) | copy_from_prototype.cpp:3:7:3:7 | a | | +| copy_from_prototype.cpp:3:7:3:7 | a | a::a(const a &) | copy_from_prototype.cpp:3:7:3:7 | a | | +| copy_from_prototype.cpp:3:7:3:7 | operator= | a::operator=(a &&) | copy_from_prototype.cpp:3:7:3:7 | a | | +| copy_from_prototype.cpp:3:7:3:7 | operator= | a::operator=(const a &) | copy_from_prototype.cpp:3:7:3:7 | a | | +| copy_from_prototype.cpp:4:26:4:26 | a | a<>::a<(unnamed)>() | copy_from_prototype.cpp:3:7:3:7 | a<> | 123 | +| copy_from_prototype.cpp:4:26:4:26 | a | a::a() | copy_from_prototype.cpp:3:7:3:7 | a | | +| copy_from_prototype.cpp:4:26:4:26 | a | a::a<(unnamed)>() | copy_from_prototype.cpp:3:7:3:7 | a | | +| copy_from_prototype.cpp:7:7:7:7 | b | b::b() | copy_from_prototype.cpp:7:7:7:7 | b | | +| copy_from_prototype.cpp:7:7:7:7 | b | b::b(b &&) | copy_from_prototype.cpp:7:7:7:7 | b | | +| copy_from_prototype.cpp:7:7:7:7 | b | b::b(const b &) | copy_from_prototype.cpp:7:7:7:7 | b | | +| copy_from_prototype.cpp:7:7:7:7 | operator= | b::operator=(b &&) | copy_from_prototype.cpp:7:7:7:7 | b | | +| copy_from_prototype.cpp:7:7:7:7 | operator= | b::operator=(const b &) | copy_from_prototype.cpp:7:7:7:7 | b | | +| copy_from_prototype.cpp:13:7:13:7 | c | c::c(c &&) | copy_from_prototype.cpp:13:7:13:7 | c | | +| copy_from_prototype.cpp:13:7:13:7 | c | c::c(const c &) | copy_from_prototype.cpp:13:7:13:7 | c | | +| copy_from_prototype.cpp:13:7:13:7 | operator= | c::operator=(c &&) | copy_from_prototype.cpp:13:7:13:7 | c | | +| copy_from_prototype.cpp:13:7:13:7 | operator= | c::operator=(const c &) | copy_from_prototype.cpp:13:7:13:7 | c | | +| copy_from_prototype.cpp:14:26:14:26 | c | c::c<(unnamed)>() | copy_from_prototype.cpp:13:7:13:7 | c | Unknown literal | +| copy_from_prototype.cpp:14:26:14:26 | c | c::c() | copy_from_prototype.cpp:13:7:13:7 | c | | +| copy_from_prototype.cpp:14:26:14:26 | c | c::c<(unnamed)>() | copy_from_prototype.cpp:13:7:13:7 | c | | +| copy_from_prototype.cpp:17:7:17:7 | d | d::d() | copy_from_prototype.cpp:17:7:17:7 | d | | +| copy_from_prototype.cpp:17:7:17:7 | d | d::d(const d &) | copy_from_prototype.cpp:17:7:17:7 | d | | +| copy_from_prototype.cpp:17:7:17:7 | d | d::d(d &&) | copy_from_prototype.cpp:17:7:17:7 | d | | +| copy_from_prototype.cpp:17:7:17:7 | operator= | d::operator=(const d &) | copy_from_prototype.cpp:17:7:17:7 | d | | +| copy_from_prototype.cpp:17:7:17:7 | operator= | d::operator=(d &&) | copy_from_prototype.cpp:17:7:17:7 | d | | +| copy_from_prototype.cpp:22:8:22:8 | e | e::e(const e &) | copy_from_prototype.cpp:22:8:22:8 | e | | +| copy_from_prototype.cpp:22:8:22:8 | e | e::e(e &&) | copy_from_prototype.cpp:22:8:22:8 | e | | +| copy_from_prototype.cpp:22:8:22:8 | operator= | e::operator=(const e &) | copy_from_prototype.cpp:22:8:22:8 | e | | +| copy_from_prototype.cpp:22:8:22:8 | operator= | e::operator=(e &&) | copy_from_prototype.cpp:22:8:22:8 | e | | +| copy_from_prototype.cpp:23:26:23:26 | e | e::e<(unnamed)>() | copy_from_prototype.cpp:22:8:22:8 | e | 456 | +| copy_from_prototype.cpp:26:35:26:43 | e | e::e<(unnamed)>() | copy_from_prototype.cpp:22:8:22:8 | e | 456 | +| file://:0:0:0:0 | operator= | __va_list_tag::operator=() | file://:0:0:0:0 | __va_list_tag | | +| file://:0:0:0:0 | operator= | __va_list_tag::operator=() | file://:0:0:0:0 | __va_list_tag | | diff --git a/cpp/ql/test/library-tests/noexcept/copy_from_prototype/copy_from_prototype.ql b/cpp/ql/test/library-tests/noexcept/copy_from_prototype/copy_from_prototype.ql new file mode 100644 index 000000000000..90921275f1d7 --- /dev/null +++ b/cpp/ql/test/library-tests/noexcept/copy_from_prototype/copy_from_prototype.ql @@ -0,0 +1,19 @@ +import cpp + +string functionName(Function f) { + exists(string name, string templateArgs, string args | + result = name + templateArgs + args + and name = f.getQualifiedName() + and if exists(f.getATemplateArgument()) + then templateArgs = "<" + concat(int i | exists(f.getTemplateArgument(i)) | f.getTemplateArgument(i).toString(), "," order by i) + ">" + else templateArgs = "" + and args = "(" + concat(int i | exists(f.getParameter(i)) | f.getParameter(i).getType().toString(), "," order by i) + ")") +} + +from Function f, string e +where if f.hasExceptionSpecification() + then if exists(f.getADeclarationEntry().getNoExceptExpr()) + then e = f.getADeclarationEntry().getNoExceptExpr().toString() + else e = "" + else e = "" +select f, functionName(f), f.getDeclaringType(), e diff --git a/cpp/ql/test/library-tests/noexcept/noexcept/box.cpp b/cpp/ql/test/library-tests/noexcept/noexcept/box.cpp new file mode 100644 index 000000000000..9f39db601adc --- /dev/null +++ b/cpp/ql/test/library-tests/noexcept/noexcept/box.cpp @@ -0,0 +1,7 @@ +#include "box.h" + +void h() { + box(120); + box(134.f); + box("bee"); +} diff --git a/cpp/ql/test/library-tests/noexcept/noexcept/box.h b/cpp/ql/test/library-tests/noexcept/noexcept/box.h new file mode 100644 index 000000000000..9806f9e2992e --- /dev/null +++ b/cpp/ql/test/library-tests/noexcept/noexcept/box.h @@ -0,0 +1,11 @@ +template +struct Box { + Box(T&& apple) noexcept(__has_nothrow_copy(T)) { + T banana = apple; + } +}; + +template +inline Box box(T&& carrot) { + return Box((T&&)carrot); +} diff --git a/cpp/ql/test/library-tests/noexcept/noexcept/noexcept.cpp b/cpp/ql/test/library-tests/noexcept/noexcept/noexcept.cpp new file mode 100644 index 000000000000..cd4633701df5 --- /dev/null +++ b/cpp/ql/test/library-tests/noexcept/noexcept/noexcept.cpp @@ -0,0 +1,19 @@ + +class T { +}; + +void swap1(T &lhs, T &rhs); +void swap2(T &lhs, T &rhs) noexcept; +void swap3(T &lhs, T &rhs) noexcept(true); + +int f(void) { + return noexcept(1+2); +} + +#include "box.h" + +void g() { + box(12); + box(13.4f); + box("hello"); +} diff --git a/cpp/ql/test/library-tests/noexcept/noexcept/noexcept_expr.expected b/cpp/ql/test/library-tests/noexcept/noexcept/noexcept_expr.expected new file mode 100644 index 000000000000..aa0e4dfe6fe1 --- /dev/null +++ b/cpp/ql/test/library-tests/noexcept/noexcept/noexcept_expr.expected @@ -0,0 +1 @@ +| noexcept.cpp:10:12:10:24 | noexcept(...) | noexcept.cpp:10:21:10:23 | ... + ... | diff --git a/cpp/ql/test/library-tests/noexcept/noexcept/noexcept_expr.ql b/cpp/ql/test/library-tests/noexcept/noexcept/noexcept_expr.ql new file mode 100644 index 000000000000..535c0d98390c --- /dev/null +++ b/cpp/ql/test/library-tests/noexcept/noexcept/noexcept_expr.ql @@ -0,0 +1,6 @@ +import cpp + +from NoExceptExpr e +select e, + e.getExpr() + diff --git a/cpp/ql/test/library-tests/noexcept/noexcept/noexcept_specifier.expected b/cpp/ql/test/library-tests/noexcept/noexcept/noexcept_specifier.expected new file mode 100644 index 000000000000..7d367b1674c6 --- /dev/null +++ b/cpp/ql/test/library-tests/noexcept/noexcept/noexcept_specifier.expected @@ -0,0 +1,26 @@ +| box.cpp:3:6:3:6 | definition of h | -------- | --- | +| box.h:2:8:2:8 | declaration of Box | -------- | --- | +| box.h:2:8:2:8 | declaration of Box | -------- | --- | +| box.h:2:8:2:8 | declaration of Box | -------- | --- | +| box.h:2:8:2:8 | declaration of Box | -------- | --- | +| box.h:2:8:2:8 | declaration of operator= | -------- | --- | +| box.h:2:8:2:8 | declaration of operator= | -------- | --- | +| box.h:2:8:2:8 | declaration of operator= | -------- | --- | +| box.h:2:8:2:8 | declaration of operator= | -------- | --- | +| box.h:2:8:2:8 | declaration of operator= | -------- | --- | +| box.h:2:8:2:8 | declaration of operator= | -------- | --- | +| box.h:2:8:2:8 | declaration of operator= | -------- | --- | +| box.h:2:8:2:8 | declaration of operator= | -------- | --- | +| box.h:2:8:2:8 | definition of Box | no except | --- | +| box.h:2:8:2:8 | definition of Box | no except | --- | +| box.h:2:8:2:8 | definition of Box | no except | --- | +| box.h:2:8:2:8 | definition of Box | no except | --- | +| box.h:3:3:3:5 | definition of Box | -------- | __has_nothrow_copy | +| box.h:9:15:9:17 | definition of box | -------- | --- | +| noexcept.cpp:2:7:2:7 | declaration of operator= | -------- | --- | +| noexcept.cpp:2:7:2:7 | declaration of operator= | -------- | --- | +| noexcept.cpp:5:6:5:10 | declaration of swap1 | -------- | --- | +| noexcept.cpp:6:6:6:10 | declaration of swap2 | no except | --- | +| noexcept.cpp:7:6:7:10 | declaration of swap3 | -------- | 1 | +| noexcept.cpp:9:5:9:5 | definition of f | -------- | --- | +| noexcept.cpp:15:6:15:6 | definition of g | -------- | --- | diff --git a/cpp/ql/test/library-tests/noexcept/noexcept/noexcept_specifier.ql b/cpp/ql/test/library-tests/noexcept/noexcept/noexcept_specifier.ql new file mode 100644 index 000000000000..f0b4229c5fdb --- /dev/null +++ b/cpp/ql/test/library-tests/noexcept/noexcept/noexcept_specifier.ql @@ -0,0 +1,12 @@ +import cpp + +from FunctionDeclarationEntry f, string noExcept, string noExceptExpr +where if f.isNoExcept() then noExcept = "no except" + else noExcept = "--------" + and if exists(f.getNoExceptExpr()) + then noExceptExpr = f.getNoExceptExpr().toString() + else noExceptExpr = "---" +select f, + noExcept, + noExceptExpr + diff --git a/cpp/ql/test/library-tests/nulltermination/mayAddNullTerminator.expected b/cpp/ql/test/library-tests/nulltermination/mayAddNullTerminator.expected new file mode 100644 index 000000000000..f16205389246 --- /dev/null +++ b/cpp/ql/test/library-tests/nulltermination/mayAddNullTerminator.expected @@ -0,0 +1,23 @@ +| test.c:15:3:15:18 | ... = ... | test.c:15:3:15:8 | buffer | +| test.c:22:11:22:34 | ... = ... | test.c:22:24:22:27 | args | +| test.c:23:5:23:15 | ... = ... | test.c:23:6:23:8 | ptr | +| test.c:36:3:36:12 | call to addNullAsm | test.c:36:14:36:19 | buffer | +| test.c:41:3:41:13 | call to expression | test.c:41:7:41:12 | buffer | +| test.c:48:2:48:10 | ... = ... | test.c:48:3:48:6 | data | +| test.c:49:2:49:15 | ... = ... | test.c:49:3:49:6 | data | +| test.c:52:2:52:12 | ... = ... | test.c:52:2:52:5 | data | +| test.c:55:16:55:19 | data | test.c:55:16:55:19 | data | +| test.c:56:2:56:13 | ... = ... | test.c:56:2:56:6 | data2 | +| test.c:59:2:59:14 | ... = ... | test.c:59:11:59:14 | data | +| test.c:60:2:60:18 | ... = ... | test.c:60:11:60:14 | data | +| test.c:63:2:63:7 | call to strcpy | test.c:63:9:63:12 | data | +| test.c:64:2:64:8 | call to addNull | test.c:64:10:64:13 | data | +| test.c:65:2:65:15 | call to addNullVarargs | test.c:65:20:65:23 | data | +| test.c:66:2:66:11 | call to addNullAsm | test.c:66:13:66:16 | data | +| test.c:67:2:67:15 | call to addNullWrapper | test.c:67:17:67:20 | data | +| test.c:68:2:68:23 | call to addNullFunctionPointer | test.c:68:25:68:28 | data | +| test.c:86:2:86:13 | ... = ... | test.c:86:2:86:6 | data2 | +| test.c:100:2:100:7 | call to strcpy | test.c:100:9:100:14 | buffer | +| test.c:108:2:108:21 | ... = ... | test.c:108:2:108:5 | data | +| test.c:114:2:114:18 | call to strlenWrapperSafe | test.c:114:20:114:23 | data | +| test.c:115:2:115:7 | call to strcpy | test.c:115:9:115:12 | data | diff --git a/cpp/ql/test/library-tests/nulltermination/mayAddNullTerminator.ql b/cpp/ql/test/library-tests/nulltermination/mayAddNullTerminator.ql new file mode 100644 index 000000000000..602fbd5b5303 --- /dev/null +++ b/cpp/ql/test/library-tests/nulltermination/mayAddNullTerminator.ql @@ -0,0 +1,6 @@ +import cpp +import semmle.code.cpp.commons.NullTermination + +from Expr e, VariableAccess va +where mayAddNullTerminator(e, va) +select e, va \ No newline at end of file diff --git a/cpp/ql/test/library-tests/nulltermination/test.c b/cpp/ql/test/library-tests/nulltermination/test.c new file mode 100644 index 000000000000..f16558de957a --- /dev/null +++ b/cpp/ql/test/library-tests/nulltermination/test.c @@ -0,0 +1,116 @@ +#define va_list void* +#define va_start(x, y) x = NULL; +#define va_arg(x, y) ((y)x) +#define va_end(x) +#define NULL 0 + +char* strcat(char* destination, const char* source); +char* strcpy(char* destination, const char* source); +int strlen(const char* str); +char* strcpy(char* destination, const char* source); + +char* global = " "; + +void addNull(char* buffer) { + buffer[0] = '\0'; +} + +void addNullVarargs(int val, ...) { + char* ptr; + va_list args; + va_start(args, val); + while ((ptr = va_arg(args, int*)) != NULL) { + *ptr = '\0'; + } +} + +void addNullAsm(char* buffer) { + int src = 0; + asm ("mov %1, %0\n\t" + "add $1, %0" + : "=r" (buffer) + : "r" (src)); +} + +void addNullWrapper(char* buffer) { + addNullAsm(buffer); +} + +void addNullFunctionPointer(char* buffer) { + int (*ptr)(char*) = addNullWrapper; + ptr(buffer); +} + +void mayAdd() { + char* data = malloc(10*sizeof(char)); + + // Assignment (dereferencing) + *data = 0; + *data++ = '\0'; + + // Assignment (array access) + data[9] = 0; + + // Assignment to another stack variable + char* data2 = data; + data2[9] = 0; + + // Assignment to global variable + global = data; + global = data + 1; + + // Function call + strcpy(data, "string"); + addNull(data); + addNullVarargs(0, data); + addNullAsm(data); + addNullWrapper(data); + addNullFunctionPointer(data); +} + +void dummy(char* data) { } + +void notMayAdd() { + char* data = malloc(10*sizeof(char)); + + // Assignment (dereferencing) + *data = 2; + *data++ = 1; + + // Assignment (array access) + data[0] = 1; + data[9] = 'a'; + + // Assignment to another stack variable + char* data2 = malloc(10*sizeof(char)); + data2[0] = 0; + data2 = data; + + // Function call + dummy(data); +} + +int strlenWrapper(char* data) { + return strlen(data); +} + +void mustBe(char* data) { + char* buffer; + strlen(data); + strcpy(buffer, data); + strcmp(buffer, data); + strchr(data, 0); + strstr(data, buffer); + strlenWrapper(data); +} + +int strlenWrapperSafe(char* data, int max) { + data[max - 1] = '\0'; + return strlen(data); +} + +void notMustBe(char* data) { + char buffer[10] = ""; + strlenWrapperSafe(data, 10); + strcpy(data, "string"); +} \ No newline at end of file diff --git a/cpp/ql/test/library-tests/nulltermination/variableMustBeNullTerminated.expected b/cpp/ql/test/library-tests/nulltermination/variableMustBeNullTerminated.expected new file mode 100644 index 000000000000..8a486975eb2f --- /dev/null +++ b/cpp/ql/test/library-tests/nulltermination/variableMustBeNullTerminated.expected @@ -0,0 +1,10 @@ +| test.c:94:16:94:19 | data | +| test.c:99:9:99:12 | data | +| test.c:100:17:100:20 | data | +| test.c:101:9:101:14 | buffer | +| test.c:101:17:101:20 | data | +| test.c:102:9:102:12 | data | +| test.c:103:9:103:12 | data | +| test.c:103:15:103:20 | buffer | +| test.c:104:16:104:19 | data | +| test.c:109:16:109:19 | data | diff --git a/cpp/ql/test/library-tests/nulltermination/variableMustBeNullTerminated.ql b/cpp/ql/test/library-tests/nulltermination/variableMustBeNullTerminated.ql new file mode 100644 index 000000000000..e69a2da6924a --- /dev/null +++ b/cpp/ql/test/library-tests/nulltermination/variableMustBeNullTerminated.ql @@ -0,0 +1,6 @@ +import cpp +import semmle.code.cpp.commons.NullTermination + +from VariableAccess va +where variableMustBeNullTerminated(va) +select va \ No newline at end of file diff --git a/cpp/ql/test/library-tests/numlines/numlines.cpp b/cpp/ql/test/library-tests/numlines/numlines.cpp new file mode 100644 index 000000000000..b91453f14870 --- /dev/null +++ b/cpp/ql/test/library-tests/numlines/numlines.cpp @@ -0,0 +1,50 @@ +int conventional() { + int x = 3; + return x; // Return x +} + +const char* long_string() { + return "Hello world\ + from a \"company\ + called //Semmle\\"; +} + +void misleading_comment() { + // This is \ + a nice long\\ + comment with\\\ + lots of\\\\ + line continuation +} + +long long long_char() { + return 'xx//'; +} + +template +void unusedTemplateFunction(T x) { + x = 1; + x = 2; + x = 3; +} + +template +void onceUsedTemplateFunction(T x) { + x = 1; + x = 2; + x = 3; +} + +template +void twiceUsedTemplateFunction(T x) { + x = 1; + x = 2; + x = 3; +} + +void templateFunctionUser(signed int x, unsigned int y) { + onceUsedTemplateFunction(x); + twiceUsedTemplateFunction(x); + twiceUsedTemplateFunction(y); +} + diff --git a/cpp/ql/test/library-tests/numlines/numlines.expected b/cpp/ql/test/library-tests/numlines/numlines.expected new file mode 100644 index 000000000000..a04cd4c34f9e --- /dev/null +++ b/cpp/ql/test/library-tests/numlines/numlines.expected @@ -0,0 +1,8 @@ +| conventional() | 4 | 4 | 1 | +| long_char() | 3 | 3 | 0 | +| long_string() | 5 | 5 | 0 | +| misleading_comment() | 7 | 2 | 5 | +| numlines | 50 | 37 | 6 | +| onceUsedTemplateFunction(T) | 6 | 6 | 0 | +| templateFunctionUser(signed int,unsigned int) | 5 | 5 | 0 | +| twiceUsedTemplateFunction(T) | 6 | 6 | 0 | diff --git a/cpp/ql/test/library-tests/numlines/numlines.ql b/cpp/ql/test/library-tests/numlines/numlines.ql new file mode 100644 index 000000000000..d6b9655ee3e6 --- /dev/null +++ b/cpp/ql/test/library-tests/numlines/numlines.ql @@ -0,0 +1,16 @@ +import cpp + +string functionName(Function f) { + exists(string name, string templateArgs, string args | + result = name + templateArgs + args + and name = f.getQualifiedName() + and if exists(f.getATemplateArgument()) + then templateArgs = "<" + concat(int i | exists(f.getTemplateArgument(i)) | f.getTemplateArgument(i).toString(), "," order by i) + ">" + else templateArgs = "" + and args = "(" + concat(int i | exists(f.getParameter(i)) | f.getParameter(i).getType().toString(), "," order by i) + ")") +} + +from string resource, int ntotal, int ncode, int ncomment +where exists(File f | f.getShortName() = resource and numlines(f, ntotal, ncode, ncomment)) + or exists(Function f | functionName(f) = resource and numlines(f, ntotal, ncode, ncomment)) +select resource, ntotal, ncode, ncomment diff --git a/cpp/ql/test/library-tests/odasa2646/cfi.expected b/cpp/ql/test/library-tests/odasa2646/cfi.expected new file mode 100644 index 000000000000..c540a03eb300 --- /dev/null +++ b/cpp/ql/test/library-tests/odasa2646/cfi.expected @@ -0,0 +1,3 @@ +| odasa2646.cpp:7:7:7:12 | constructor init of field ints | odasa2646.cpp:7:7:7:12 | {...} | ArrayAggregateLiteral | +| odasa2646.cpp:8:7:8:13 | constructor init of field chars | odasa2646.cpp:8:7:8:13 | {...} | ArrayAggregateLiteral | +| odasa2646.cpp:9:7:9:15 | constructor init of field doubles | odasa2646.cpp:9:7:9:15 | {...} | ArrayAggregateLiteral | diff --git a/cpp/ql/test/library-tests/odasa2646/cfi.ql b/cpp/ql/test/library-tests/odasa2646/cfi.ql new file mode 100644 index 000000000000..5d5caceb9d6f --- /dev/null +++ b/cpp/ql/test/library-tests/odasa2646/cfi.ql @@ -0,0 +1,7 @@ +import cpp + +from ConstructorFieldInit cfi, Expr e, string clazz +where e = cfi.getExpr() + and clazz = e.getAQlClass() + and clazz.matches("%Literal") +select cfi, e, clazz diff --git a/cpp/ql/test/library-tests/odasa2646/odasa2646.cpp b/cpp/ql/test/library-tests/odasa2646/odasa2646.cpp new file mode 100644 index 000000000000..f967b0bafd5d --- /dev/null +++ b/cpp/ql/test/library-tests/odasa2646/odasa2646.cpp @@ -0,0 +1,12 @@ +struct foo { + int ints[10]; + char chars[10]; + double doubles[10]; + + foo() + : ints() + , chars() + , doubles() + { + } +}; diff --git a/cpp/ql/test/library-tests/operators/operators.cpp b/cpp/ql/test/library-tests/operators/operators.cpp new file mode 100644 index 000000000000..790342977257 --- /dev/null +++ b/cpp/ql/test/library-tests/operators/operators.cpp @@ -0,0 +1,27 @@ +class Name { + const char* s; +}; + +class Table { + Name* p; + int sz; +public: + // See JIRA 521. Including a non-default copy constructor for now. + Table(const Table& other) { } + Table(int s=15) { p = new Name[sz=s]; } // constructor + ~Table() { delete[] p; } + Name* lookup (const char*); + bool insert(Name*); +}; + +void foo() { + Table t1; // call to user-defined constructor + Table t2(t1); // call to default copy constructor + t2.insert(0); +} + +// This used to have TRAP import errors +struct A { A() {} A(A&) {} } a; +A operator+(int, A) { return a; } +int f_A() { 0 + a; } + diff --git a/cpp/ql/test/library-tests/operators/operators.expected b/cpp/ql/test/library-tests/operators/operators.expected new file mode 100644 index 000000000000..5c221dda4a00 --- /dev/null +++ b/cpp/ql/test/library-tests/operators/operators.expected @@ -0,0 +1,9 @@ +| operators.cpp:18:9:18:10 | call to Table | +| operators.cpp:19:12:19:14 | call to Table | +| operators.cpp:20:6:20:11 | call to insert | +| operators.cpp:21:1:21:1 | call to ~Table | +| operators.cpp:21:1:21:1 | call to ~Table | +| operators.cpp:24:30:24:30 | call to A | +| operators.cpp:25:23:25:31 | call to A | +| operators.cpp:26:15:26:15 | call to operator+ | +| operators.cpp:26:17:26:17 | call to A | diff --git a/cpp/ql/test/library-tests/operators/operators.ql b/cpp/ql/test/library-tests/operators/operators.ql new file mode 100644 index 000000000000..0e072dc827e7 --- /dev/null +++ b/cpp/ql/test/library-tests/operators/operators.ql @@ -0,0 +1,3 @@ +import cpp + +from Call c select c diff --git a/cpp/ql/test/library-tests/opts/main.cpp b/cpp/ql/test/library-tests/opts/main.cpp new file mode 100644 index 000000000000..2f78971c3fca --- /dev/null +++ b/cpp/ql/test/library-tests/opts/main.cpp @@ -0,0 +1,33 @@ + +char *Xstrdup(const char *string); +void abort(); +struct FILE; +char *fgets(char *str, int num, FILE *stream); +int ignore_return_value(); +#define IGNORE_RETURN_VALUE() ignore_return_value() +void myIgnoreReturnValue(); + +int main(int argc, char *argv[]) +{ + char *s1 = Xstrdup("Hello, world!"); + char *s2 = Xstrdup(0); + + if (argc == 0) + { + abort(); + } else if (argc == 1) { + __assume(0); + } + + { + char buffer[256]; + char *result; + FILE *s; + + result = fgets(buffer, 256, s); + } + + IGNORE_RETURN_VALUE(); + + myIgnoreReturnValue(); +} diff --git a/cpp/ql/test/library-tests/opts/options b/cpp/ql/test/library-tests/opts/options new file mode 100644 index 000000000000..91adeb70204c --- /dev/null +++ b/cpp/ql/test/library-tests/opts/options @@ -0,0 +1 @@ +extractor_flags: --microsoft diff --git a/cpp/ql/test/library-tests/opts/options.expected b/cpp/ql/test/library-tests/opts/options.expected new file mode 100644 index 000000000000..53b93a432a95 --- /dev/null +++ b/cpp/ql/test/library-tests/opts/options.expected @@ -0,0 +1,8 @@ +| main.cpp:3:6:3:10 | abort | exits | +| main.cpp:5:7:5:11 | fgets | alwaysCheckReturnValue | +| main.cpp:12:13:12:19 | call to Xstrdup | overrideReturnsNull | +| main.cpp:13:13:13:19 | call to Xstrdup | overrideReturnsNull | +| main.cpp:13:13:13:19 | call to Xstrdup | returnsNull | +| main.cpp:19:3:19:13 | __assume(...) | exprExits | +| main.cpp:30:2:30:22 | call to ignore_return_value | okToIgnoreReturnValue | +| main.cpp:32:2:32:20 | call to myIgnoreReturnValue | okToIgnoreReturnValue | diff --git a/cpp/ql/test/library-tests/opts/options.ql b/cpp/ql/test/library-tests/opts/options.ql new file mode 100644 index 000000000000..dd75d9f07a36 --- /dev/null +++ b/cpp/ql/test/library-tests/opts/options.ql @@ -0,0 +1,32 @@ +import cpp + +class CustomTestOptions extends Options +{ + override predicate okToIgnoreReturnValue(FunctionCall fc) { + Options.super.okToIgnoreReturnValue(fc) or + fc.getTarget().getName() = "myIgnoreReturnValue" + } +} + +from Options opts, Element e, string why +where + ( + opts.overrideReturnsNull(e) and + why = "overrideReturnsNull" + ) or ( + opts.returnsNull(e) and + why = "returnsNull" + ) or ( + opts.exits(e) and + why = "exits" + ) or ( + opts.exprExits(e) and + why = "exprExits" + ) or ( + opts.alwaysCheckReturnValue(e) and + why = "alwaysCheckReturnValue" + ) or ( + opts.okToIgnoreReturnValue(e) and + why = "okToIgnoreReturnValue" + ) +select e, why diff --git a/cpp/ql/test/library-tests/padding/bitfields.c b/cpp/ql/test/library-tests/padding/bitfields.c new file mode 100644 index 000000000000..c42c701edcbc --- /dev/null +++ b/cpp/ql/test/library-tests/padding/bitfields.c @@ -0,0 +1,214 @@ +#include "size_asserts.h" + +// Bitfield packing: +// (1) A struct containing a bitfield with declared type T (e.g. T bf : 7) will be aligned as if it +// contained an actual member variable of type T. Thus, a struct containing a bitfield 'unsigned int bf : 8' +// will have an alignment of at least alignof(unsigned int), even though the bitfield was only 8 bits. +// (2) If a bitfield with declared type T would straddle a sizeof(T) boundary, padding is inserted +// before the bitfield to align it on an alignof(T) boundary. Note the subtle distinction between alignof +// and sizeof. This matters for 32-bit Linux, where sizeof(long long) == 8, but alignof(long long) == 4. +// (3) [MSVC only!] If a bitfield with declared type T immediately follows another bitfield with declared type P, +// and sizeof(P) != sizeof(T), padding will be inserted to align the new bitfield to a boundary of +// max(alignof(P), alignof(T)). + +struct Bitfield_Straddle { +// 0 + unsigned int bf0 : 24; +// 3 +// Pad 1 +// 4 + unsigned int bf1 : 24; +// 7 +// Pad 1 +// 8 + unsigned int bf2 : 16; +// 10 +// Pad 2 +// 12 +}; +CASSERT(sizeof(struct Bitfield_Straddle) == 12); + +struct Bitfield_longlong_25 { +// 0 + char c; +// 1 +// Pad 7(MSVC); 0(GCC) +// 8(MSVC); 1(GCC) + unsigned long long bf : 25; +// 11.1(MSVC); 4.1(GCC) +// Pad 4.7(MSVC); 3.7(GCC) +// 16(MSVC); 8(GCC) +}; +CASSERT_MSVC(sizeof(struct Bitfield_longlong_25) == 16); +CASSERT_GCC(sizeof(struct Bitfield_longlong_25) == 8); + +struct Bitfield_longlong_57 { +// 0 + char c; +// 1 +// Pad 7(MSVC, GCC64); 3(GCC32) +// 8(MSVC, GCC64); 4(GCC32) + unsigned long long bf : 57; +// 15.1(MSVC, GCC64); 11.1(GCC32) +// Padd 0.7 +// 16(MSVC, GCC64); 12(GCC32) +}; +CASSERT_MSVC(sizeof(struct Bitfield_longlong_57) == 16); +CASSERT_GCC64(sizeof(struct Bitfield_longlong_57) == 16); +CASSERT_GCC32(sizeof(struct Bitfield_longlong_57) == 12); + +struct Bitfield_Int7 { +// 0 + char c; +// 1 +// Pad 3(MSVC); 0(GCC) +// 4(MSVC); 1(GCC) + unsigned int bf : 7; +// 4.7(MSVC); 1.7(GCC) +// Pad 3.1(MSVC); 2.1(GCC) +// 8(MSVC); 4(GCC) +}; +CASSERT_MSVC(sizeof(struct Bitfield_Int7) == 8); +CASSERT_GCC(sizeof(struct Bitfield_Int7) == 4); + +struct Bitfield_Int8 { +// 0 + char c; +// 1 +// Pad 3(MSVC); 0(GCC) +// 4(MSVC); 1(GCC) + unsigned int bf : 8; +// 5(MSVC); 2(GCC) +// Pad 3(MSVC); 2(GCC) +// 8(MSVC); 4(GCC) +}; +CASSERT_MSVC(sizeof(struct Bitfield_Int8) == 8); +CASSERT_GCC(sizeof(struct Bitfield_Int8) == 4); + +struct Bitfield_Int9 { +// 0 +char c; +// 1 +// Pad 3(MSVC); 0(GCC) +// 4(MSVC); 1(GCC) + unsigned int bf : 9; +// 5.1(MSVC); 2.1(GCC) +// Pad 2.7(MSVC); 1.7(GCC) +// 8(MSVC); 4(GCC) +}; +CASSERT_MSVC(sizeof(struct Bitfield_Int9) == 8); +CASSERT_GCC(sizeof(struct Bitfield_Int9) == 4); + +struct Bitfield_Short7 { +// 0 + char c; +// 1 +// Pad 1(MSVC); 0(GCC) +// 2(MSVC); 1(GCC) + unsigned short bf : 7; +// 2.7(MSVC); 1.7(GCC) +// Pad 1.1(MSVC); 0.1(GCC) +// 4(MSVC); 2(GCC) +}; +CASSERT_MSVC(sizeof(struct Bitfield_Short7) == 4); +CASSERT_GCC(sizeof(struct Bitfield_Short7) == 2); + +struct Bitfield_Short8 { +// 0 + char c; +// 1 +// Pad 1(MSVC); 0(GCC) +// 2(MSVC); 1(GCC) + unsigned short bf : 8; +// 3(MSVC); 2(GCC) +// Pad 1(MSVC); 0(GCC) +// 4(MSVC); 2(GCC) +}; +CASSERT_MSVC(sizeof(struct Bitfield_Short8) == 4); +CASSERT_GCC(sizeof(struct Bitfield_Short8) == 2); + +struct Bitfield_Short9 { +// 0 + char c; +// 1 +// Pad 1 +// 2 + unsigned short bf : 9; +// 3.1 +// Pad 0.7(MSVC) +// 4 +}; +CASSERT_MSVC(sizeof(struct Bitfield_Short9) == 4); +CASSERT_GCC(sizeof(struct Bitfield_Short9) == 4); + +struct Bitfield_Mixed { +// 0 + unsigned char bfc : 2; +// 0.2 +// Pad 3.6(MSVC); 0(GCC) +// 4(MSVC); 0.2(GCC) + unsigned int bfi : 7; +// 4.7(MSVC); 1.1(GCC) +// Pad 3.1(MSVC); 0(GCC) +// 8(MSVC); 1.1(GCC) + unsigned short bfs : 7; +// 8.7(MSVC); 2(GCC) +// Pad 3.1(MSVC); 0(GCC) +}; +CASSERT_MSVC(sizeof(struct Bitfield_Mixed) == 12); +CASSERT_GCC(sizeof(struct Bitfield_Mixed) == 4); + +typedef char CHAR; +typedef short SHORT; + +struct Bitfield_TypeEquality_char { + char x : 2; + const char y : 2; +}; +CASSERT(sizeof(struct Bitfield_TypeEquality_char) == 1); + +struct Bitfield_TypeEquality_char_uchar { + char x : 2; + unsigned char y : 2; +}; +CASSERT(sizeof(struct Bitfield_TypeEquality_char_uchar) == 1); + +struct Bitfield_TypeEquality_char_schar { + char x : 2; + signed char y : 2; +}; +CASSERT(sizeof(struct Bitfield_TypeEquality_char_schar) == 1); + +struct Bitfield_TypeEquality_char_CHAR { + char x : 2; + CHAR y : 2; +}; +CASSERT(sizeof(struct Bitfield_TypeEquality_char_CHAR) == 1); + +struct Bitfield_TypeEquality_short { + short x : 2; + short y : 2; +}; +CASSERT(sizeof(struct Bitfield_TypeEquality_short) == 2); + +struct Bitfield_TypeEquality_short_ushort { + short x : 2; + unsigned short y : 2; +}; +CASSERT(sizeof(struct Bitfield_TypeEquality_short_ushort) == 2); + +struct Bitfield_TypeEquality_int_long { + int x : 2; + long y : 2; +}; +CASSERT_MSVC(sizeof(struct Bitfield_TypeEquality_int_long) == 4); +CASSERT_GCC32(sizeof(struct Bitfield_TypeEquality_int_long) == 4); +CASSERT_GCC64(sizeof(struct Bitfield_TypeEquality_int_long) == 8); + +struct Bitfield_TypeEquality_int_ulong { + int x : 2; + unsigned long y : 2; +}; +CASSERT_MSVC(sizeof(struct Bitfield_TypeEquality_int_long) == 4); +CASSERT_GCC32(sizeof(struct Bitfield_TypeEquality_int_long) == 4); +CASSERT_GCC64(sizeof(struct Bitfield_TypeEquality_int_long) == 8); diff --git a/cpp/ql/test/library-tests/padding/cpp.cpp b/cpp/ql/test/library-tests/padding/cpp.cpp new file mode 100644 index 000000000000..9dcc2cef5036 --- /dev/null +++ b/cpp/ql/test/library-tests/padding/cpp.cpp @@ -0,0 +1,278 @@ +#include "size_asserts.h" + +CASSERT_MSVC(sizeof(wchar_t) == 2); +CASSERT_GCC(sizeof(wchar_t) == 4); + +struct ReferencesAndPointers { +// 0 + char c1; +// 1 +// Pad 7(GCC64, MSVC64); 3(GCC32, MSVC32) +// 8(GCC64, MSVC64); 4(GCC32, MSVC32) + short& r2; +// 16(GCC64, MSVC64); 8(GCC32, MSVC32) + wchar_t wc3; +// 18(GCC64, MSVC64); 10(GCC32, MSVC32) +// Pad 6(GCC64, MSVC64); 2(GCC32, MSVC32) +// 24(GCC64, MSVC64); 12(GCC32, MSVC32) + long long* p4; +// 32(GCC64, MSVC64); 16(GCC32, MSVC32) +}; +CASSERT_MSVC64(sizeof(struct ReferencesAndPointers) == 32); +CASSERT_MSVC32(sizeof(struct ReferencesAndPointers) == 16); +CASSERT_GCC64(sizeof(struct ReferencesAndPointers) == 32); +CASSERT_GCC32(sizeof(struct ReferencesAndPointers) == 16); + +enum E { + E_None +}; +CASSERT(sizeof(E) == sizeof(int)); + +enum E_char : char { + E_char_None +}; +CASSERT(sizeof(E_char) == sizeof(char)); + +enum E_short : short { + E_short_None +}; +CASSERT(sizeof(E_short) == sizeof(short)); + +enum E_int : int { + E_int_None +}; +CASSERT(sizeof(E_int) == sizeof(int)); + +enum E_long : long { + E_long_None +}; +CASSERT(sizeof(E_long) == sizeof(long)); + +enum E_longlong : long long { + E_longlong_None +}; +CASSERT(sizeof(E_longlong) == sizeof(long long)); + +enum class EC { + None +}; +CASSERT(sizeof(EC) == sizeof(int)); + +enum class EC_char : char { + None +}; +CASSERT(sizeof(EC_char) == sizeof(char)); + +enum class EC_short : short { + None +}; +CASSERT(sizeof(EC_short) == sizeof(short)); + +enum class EC_int : int { + None +}; +CASSERT(sizeof(EC_int) == sizeof(int)); + +enum class EC_long : long { + None +}; +CASSERT(sizeof(EC_long) == sizeof(long)); + +enum class EC_longlong : long long { + None +}; +CASSERT(sizeof(EC_longlong) == sizeof(long long)); + +struct Enums_Packed { +// 0 + E_longlong ell; +// 8 + E_long el; +// 16(GCC64); 12(GCC32, MSVC) + E_int ei; +// 20(GCC64); 16(GCC32, MSVC) + E e; +// 24(GCC64); 20(GCC32, MSVC) + E_short es; +// 26(GCC64); 22(GCC32, MSVC) + E_char ec; +// 27(GCC64); 23(GCC32, MSVC) +// Pad 5(GCC64); 1(GCC32, MSVC) +// 32(GCC64); 24(GCC32, MSVC) +}; +CASSERT_GCC64(sizeof(Enums_Packed) == 32); +CASSERT_GCC32(sizeof(Enums_Packed) == 24); +CASSERT_MSVC(sizeof(Enums_Packed) == 24); + +struct Enums_MixedBitfields { +// 0 + E_short ei1 : 8; +// 1 + short s2 : 8; +// 2 +}; +CASSERT_GCC(sizeof(Enums_MixedBitfields) == 2); +CASSERT_MSVC(sizeof(Enums_MixedBitfields) == 2); + +struct Enums_MixedBitfields_Padded { +// 0 + E_short ei1 : 8; +// 1 +// Pad 1(MSVC); 0(GCC) +// 2(MSVC); 1(GCC) + unsigned char uc2 : 8; +// 3(MSVC); 2(GCC) +// Pad 1(MSVC); 0(GCC) +// 4(MSVC); 2(GCC) +}; +CASSERT_GCC(sizeof(Enums_MixedBitfields_Padded) == 2); +CASSERT_MSVC(sizeof(Enums_MixedBitfields_Padded) == 4); + +struct Base_Empty { +// 0 +// Pad 1 +// 1 +}; +CASSERT(sizeof(Base_Empty) == 1); + +struct Derived_EmptyBase : Base_Empty { +// 0 +// Base class Base_Empty (Empty Base Optimization) +// 0 + int n1; +// 4 +}; +CASSERT(sizeof(Derived_EmptyBase) == 4); + +struct Base_NoPadding { +// 0 + int n1; +// 4 +}; +CASSERT(sizeof(Base_NoPadding) == 4); + +struct Derived_NoBasePadding : Base_NoPadding { +// 0 +// Base class Base_NoPadding +// 4 + int n2; +// 8 +}; +CASSERT(sizeof(Derived_NoBasePadding) == 8); + +struct Base_Padding { +// 0 + float f1; +// 4 + short s1; +// 6 +// Pad 2 +// 8 +}; +CASSERT(sizeof(Base_Padding) == 8); + +struct Derived_BasePadding : Base_Padding { +// 0 +// Base class Base_Padding +// 8 + short s2; +// 10 +// Pad 2 +// 12 +}; +CASSERT(sizeof(Derived_BasePadding) == 12); + +struct Intermediate_NoFields : Base_NoPadding { +// 0 +// Base class Base_NoPadding +// 4 +}; +CASSERT(sizeof(Intermediate_NoFields) == 4); + +struct Derived_NonEmptyBaseWithNoFields : Intermediate_NoFields { +// 0 +// Base class Intermediate_NoFields +// 4 + int n2; +// 8 +}; +CASSERT(sizeof(Derived_NonEmptyBaseWithNoFields) == 8); + +struct MultipleInheritance : Base_NoPadding, Base_Padding { + int n3; +}; +CASSERT(sizeof(MultipleInheritance) == 16); + +struct StaticMemberVariables { +// 0 + static int s_n1; +// 0 + float f1; +// 4 + static int s_n2; +// 4 +}; +CASSERT(sizeof(StaticMemberVariables) == 4); + +struct VirtualInheritance : virtual Base_NoPadding { + int n3; +}; + +struct Derived_EmptyBase_Conflict1 : Base_Empty { +// 0 +// Base class Base_Empty (GCC Only: no Empty Base Optimization due to conflict with m1) +// 1(GCC); 0(MSVC) + Base_Empty m1; +// 2(GCC); 1(MSVC) +}; +CASSERT_GCC(sizeof(Derived_EmptyBase_Conflict1) == 2); +CASSERT_MSVC(sizeof(Derived_EmptyBase_Conflict1) == 1); + +struct Derived_EmptyBase_Conflict2 : Base_Empty { +// 0 +// Base class Base_Empty (GCC Only: no Empty Base Optimization due to conflict with m1) +// 1(GCC); 0(MSVC) +// Pad 3(GCC); 0(MSVC) +// 4(GCC); 0(MSVC) + Derived_EmptyBase m1; +// 8(GCC); 4(MSVC) +}; +CASSERT_GCC(sizeof(Derived_EmptyBase_Conflict2) == 8); +CASSERT_MSVC(sizeof(Derived_EmptyBase_Conflict2) == 4); + +struct Base_Empty2 : Base_Empty { +// 0 +// Base class Base_Empty (Empty Base Optimization) +// 0 +// Pad 1 +// 1 +}; +CASSERT(sizeof(Base_Empty2) == 1); + +struct Derived_EmptyBase_Conflict3 : Base_Empty2 { +// 0 +// Base class Base_Empty2 (GCC Only: no Empty Base Optimization due to conflict with m1) +// 1(GCC); 0(MSVC) + Base_Empty m1; +// 2(GCC); 1(MSVC) +}; +CASSERT_GCC(sizeof(Derived_EmptyBase_Conflict3) == 2); +CASSERT_MSVC(sizeof(Derived_EmptyBase_Conflict3) == 1); + +struct Base_NoPaddingButSmallAlignment { +// 0 + short s1; +// 2 +}; +CASSERT(sizeof(Base_NoPaddingButSmallAlignment) == 2); + +struct Derived_NoBasePaddingButNeedsAlignmentPadding : Base_NoPaddingButSmallAlignment { +// 0 +// Base class Base_NoPaddingButSmallAlignment +// 2 +// Pad 2 +// 4 + int n1; +// 8 +}; +CASSERT(sizeof(Derived_NoBasePaddingButNeedsAlignmentPadding) == 8); diff --git a/cpp/ql/test/library-tests/padding/padding.expected b/cpp/ql/test/library-tests/padding/padding.expected new file mode 100644 index 000000000000..bce1c51a54b2 --- /dev/null +++ b/cpp/ql/test/library-tests/padding/padding.expected @@ -0,0 +1,176 @@ +| ILP32 | bitfields.c:14:8:14:24 | Bitfield_Straddle | 80 | 64 | 96 | +| ILP32 | bitfields.c:31:8:31:27 | Bitfield_longlong_25 | 33 | 33 | 64 | +| ILP32 | bitfields.c:45:8:45:27 | Bitfield_longlong_57 | 89 | 65 | 96 | +| ILP32 | bitfields.c:60:8:60:20 | Bitfield_Int7 | 15 | 15 | 32 | +| ILP32 | bitfields.c:74:8:74:20 | Bitfield_Int8 | 16 | 16 | 32 | +| ILP32 | bitfields.c:88:8:88:20 | Bitfield_Int9 | 17 | 17 | 32 | +| ILP32 | bitfields.c:102:8:102:22 | Bitfield_Short7 | 15 | 15 | 16 | +| ILP32 | bitfields.c:116:8:116:22 | Bitfield_Short8 | 16 | 16 | 16 | +| ILP32 | bitfields.c:130:8:130:22 | Bitfield_Short9 | 25 | 17 | 32 | +| ILP32 | bitfields.c:144:8:144:21 | Bitfield_Mixed | 16 | 16 | 32 | +| ILP32 | bitfields.c:164:8:164:33 | Bitfield_TypeEquality_char | 4 | 4 | 8 | +| ILP32 | bitfields.c:170:8:170:39 | Bitfield_TypeEquality_char_uchar | 4 | 4 | 8 | +| ILP32 | bitfields.c:176:8:176:39 | Bitfield_TypeEquality_char_schar | 4 | 4 | 8 | +| ILP32 | bitfields.c:182:8:182:38 | Bitfield_TypeEquality_char_CHAR | 4 | 4 | 8 | +| ILP32 | bitfields.c:188:8:188:34 | Bitfield_TypeEquality_short | 4 | 4 | 16 | +| ILP32 | bitfields.c:194:8:194:41 | Bitfield_TypeEquality_short_ushort | 4 | 4 | 16 | +| ILP32 | bitfields.c:200:8:200:37 | Bitfield_TypeEquality_int_long | 4 | 4 | 32 | +| ILP32 | bitfields.c:208:8:208:38 | Bitfield_TypeEquality_int_ulong | 4 | 4 | 32 | +| ILP32 | cpp.cpp:6:8:6:28 | ReferencesAndPointers | 128 | 104 | 128 | +| ILP32 | cpp.cpp:86:8:86:19 | Enums_Packed | 184 | 184 | 192 | +| ILP32 | cpp.cpp:107:8:107:27 | Enums_MixedBitfields | 16 | 16 | 16 | +| ILP32 | cpp.cpp:117:8:117:34 | Enums_MixedBitfields_Padded | 16 | 16 | 16 | +| ILP32 | cpp.cpp:131:8:131:17 | Base_Empty | 0 | 0 | 8 | +| ILP32 | cpp.cpp:138:8:138:24 | Derived_EmptyBase | 32 | 32 | 32 | +| ILP32 | cpp.cpp:147:8:147:21 | Base_NoPadding | 32 | 32 | 32 | +| ILP32 | cpp.cpp:154:8:154:28 | Derived_NoBasePadding | 64 | 64 | 64 | +| ILP32 | cpp.cpp:163:8:163:19 | Base_Padding | 48 | 48 | 64 | +| ILP32 | cpp.cpp:174:8:174:26 | Derived_BasePadding | 80 | 64 | 96 | +| ILP32 | cpp.cpp:185:8:185:28 | Intermediate_NoFields | 32 | 32 | 32 | +| ILP32 | cpp.cpp:192:8:192:39 | Derived_NonEmptyBaseWithNoFields | 64 | 64 | 64 | +| ILP32 | cpp.cpp:206:8:206:28 | StaticMemberVariables | 32 | 32 | 32 | +| ILP32 | cpp.cpp:221:8:221:34 | Derived_EmptyBase_Conflict1 | 16 | 8 | 16 | +| ILP32 | cpp.cpp:231:8:231:34 | Derived_EmptyBase_Conflict2 | 64 | 32 | 64 | +| ILP32 | cpp.cpp:243:8:243:18 | Base_Empty2 | 0 | 0 | 8 | +| ILP32 | cpp.cpp:252:8:252:34 | Derived_EmptyBase_Conflict3 | 16 | 8 | 16 | +| ILP32 | cpp.cpp:262:8:262:38 | Base_NoPaddingButSmallAlignment | 16 | 16 | 16 | +| ILP32 | cpp.cpp:269:8:269:52 | Derived_NoBasePaddingButNeedsAlignmentPadding | 64 | 48 | 64 | +| ILP32 | file://:0:0:0:0 | __va_list_tag | 128 | 128 | 128 | +| ILP32 | test.c:6:8:6:22 | Integers_Packed | 312 | 312 | 320 | +| ILP32 | test.c:38:8:38:24 | Integers_Assorted | 272 | 192 | 288 | +| ILP32 | test.c:75:8:75:20 | Floats_Packed | 192 | 192 | 192 | +| ILP32 | test.c:92:8:92:20 | Arrays_Packed | 96 | 96 | 96 | +| ILP32 | test.c:100:8:100:19 | Arrays_Mixed | 176 | 176 | 192 | +| ILP32 | test.c:120:8:120:21 | Pointers_Mixed | 64 | 64 | 64 | +| ILP32_MS | bitfields.c:14:8:14:24 | Bitfield_Straddle | 80 | 64 | 96 | +| ILP32_MS | bitfields.c:31:8:31:27 | Bitfield_longlong_25 | 89 | 33 | 128 | +| ILP32_MS | bitfields.c:45:8:45:27 | Bitfield_longlong_57 | 121 | 65 | 128 | +| ILP32_MS | bitfields.c:60:8:60:20 | Bitfield_Int7 | 39 | 15 | 64 | +| ILP32_MS | bitfields.c:74:8:74:20 | Bitfield_Int8 | 40 | 16 | 64 | +| ILP32_MS | bitfields.c:88:8:88:20 | Bitfield_Int9 | 41 | 17 | 64 | +| ILP32_MS | bitfields.c:102:8:102:22 | Bitfield_Short7 | 23 | 15 | 32 | +| ILP32_MS | bitfields.c:116:8:116:22 | Bitfield_Short8 | 24 | 16 | 32 | +| ILP32_MS | bitfields.c:130:8:130:22 | Bitfield_Short9 | 25 | 17 | 32 | +| ILP32_MS | bitfields.c:144:8:144:21 | Bitfield_Mixed | 71 | 16 | 96 | +| ILP32_MS | bitfields.c:164:8:164:33 | Bitfield_TypeEquality_char | 4 | 4 | 8 | +| ILP32_MS | bitfields.c:170:8:170:39 | Bitfield_TypeEquality_char_uchar | 4 | 4 | 8 | +| ILP32_MS | bitfields.c:176:8:176:39 | Bitfield_TypeEquality_char_schar | 4 | 4 | 8 | +| ILP32_MS | bitfields.c:182:8:182:38 | Bitfield_TypeEquality_char_CHAR | 4 | 4 | 8 | +| ILP32_MS | bitfields.c:188:8:188:34 | Bitfield_TypeEquality_short | 4 | 4 | 16 | +| ILP32_MS | bitfields.c:194:8:194:41 | Bitfield_TypeEquality_short_ushort | 4 | 4 | 16 | +| ILP32_MS | bitfields.c:200:8:200:37 | Bitfield_TypeEquality_int_long | 4 | 4 | 32 | +| ILP32_MS | bitfields.c:208:8:208:38 | Bitfield_TypeEquality_int_ulong | 4 | 4 | 32 | +| ILP32_MS | cpp.cpp:6:8:6:28 | ReferencesAndPointers | 128 | 88 | 128 | +| ILP32_MS | cpp.cpp:86:8:86:19 | Enums_Packed | 184 | 184 | 192 | +| ILP32_MS | cpp.cpp:107:8:107:27 | Enums_MixedBitfields | 16 | 16 | 16 | +| ILP32_MS | cpp.cpp:117:8:117:34 | Enums_MixedBitfields_Padded | 24 | 16 | 32 | +| ILP32_MS | cpp.cpp:131:8:131:17 | Base_Empty | 0 | 0 | 8 | +| ILP32_MS | cpp.cpp:138:8:138:24 | Derived_EmptyBase | 32 | 32 | 32 | +| ILP32_MS | cpp.cpp:147:8:147:21 | Base_NoPadding | 32 | 32 | 32 | +| ILP32_MS | cpp.cpp:154:8:154:28 | Derived_NoBasePadding | 64 | 64 | 64 | +| ILP32_MS | cpp.cpp:163:8:163:19 | Base_Padding | 48 | 48 | 64 | +| ILP32_MS | cpp.cpp:174:8:174:26 | Derived_BasePadding | 80 | 64 | 96 | +| ILP32_MS | cpp.cpp:185:8:185:28 | Intermediate_NoFields | 32 | 32 | 32 | +| ILP32_MS | cpp.cpp:192:8:192:39 | Derived_NonEmptyBaseWithNoFields | 64 | 64 | 64 | +| ILP32_MS | cpp.cpp:206:8:206:28 | StaticMemberVariables | 32 | 32 | 32 | +| ILP32_MS | cpp.cpp:221:8:221:34 | Derived_EmptyBase_Conflict1 | 8 | 8 | 8 | +| ILP32_MS | cpp.cpp:231:8:231:34 | Derived_EmptyBase_Conflict2 | 32 | 32 | 32 | +| ILP32_MS | cpp.cpp:243:8:243:18 | Base_Empty2 | 0 | 0 | 8 | +| ILP32_MS | cpp.cpp:252:8:252:34 | Derived_EmptyBase_Conflict3 | 8 | 8 | 8 | +| ILP32_MS | cpp.cpp:262:8:262:38 | Base_NoPaddingButSmallAlignment | 16 | 16 | 16 | +| ILP32_MS | cpp.cpp:269:8:269:52 | Derived_NoBasePaddingButNeedsAlignmentPadding | 64 | 48 | 64 | +| ILP32_MS | file://:0:0:0:0 | __va_list_tag | 128 | 128 | 128 | +| ILP32_MS | test.c:6:8:6:22 | Integers_Packed | 312 | 312 | 320 | +| ILP32_MS | test.c:38:8:38:24 | Integers_Assorted | 304 | 192 | 320 | +| ILP32_MS | test.c:75:8:75:20 | Floats_Packed | 160 | 160 | 192 | +| ILP32_MS | test.c:92:8:92:20 | Arrays_Packed | 96 | 96 | 96 | +| ILP32_MS | test.c:100:8:100:19 | Arrays_Mixed | 208 | 176 | 256 | +| ILP32_MS | test.c:120:8:120:21 | Pointers_Mixed | 64 | 64 | 64 | +| LLP64_MS | bitfields.c:14:8:14:24 | Bitfield_Straddle | 80 | 64 | 96 | +| LLP64_MS | bitfields.c:31:8:31:27 | Bitfield_longlong_25 | 89 | 33 | 128 | +| LLP64_MS | bitfields.c:45:8:45:27 | Bitfield_longlong_57 | 121 | 65 | 128 | +| LLP64_MS | bitfields.c:60:8:60:20 | Bitfield_Int7 | 39 | 15 | 64 | +| LLP64_MS | bitfields.c:74:8:74:20 | Bitfield_Int8 | 40 | 16 | 64 | +| LLP64_MS | bitfields.c:88:8:88:20 | Bitfield_Int9 | 41 | 17 | 64 | +| LLP64_MS | bitfields.c:102:8:102:22 | Bitfield_Short7 | 23 | 15 | 32 | +| LLP64_MS | bitfields.c:116:8:116:22 | Bitfield_Short8 | 24 | 16 | 32 | +| LLP64_MS | bitfields.c:130:8:130:22 | Bitfield_Short9 | 25 | 17 | 32 | +| LLP64_MS | bitfields.c:144:8:144:21 | Bitfield_Mixed | 71 | 16 | 96 | +| LLP64_MS | bitfields.c:164:8:164:33 | Bitfield_TypeEquality_char | 4 | 4 | 8 | +| LLP64_MS | bitfields.c:170:8:170:39 | Bitfield_TypeEquality_char_uchar | 4 | 4 | 8 | +| LLP64_MS | bitfields.c:176:8:176:39 | Bitfield_TypeEquality_char_schar | 4 | 4 | 8 | +| LLP64_MS | bitfields.c:182:8:182:38 | Bitfield_TypeEquality_char_CHAR | 4 | 4 | 8 | +| LLP64_MS | bitfields.c:188:8:188:34 | Bitfield_TypeEquality_short | 4 | 4 | 16 | +| LLP64_MS | bitfields.c:194:8:194:41 | Bitfield_TypeEquality_short_ushort | 4 | 4 | 16 | +| LLP64_MS | bitfields.c:200:8:200:37 | Bitfield_TypeEquality_int_long | 4 | 4 | 32 | +| LLP64_MS | bitfields.c:208:8:208:38 | Bitfield_TypeEquality_int_ulong | 4 | 4 | 32 | +| LLP64_MS | cpp.cpp:6:8:6:28 | ReferencesAndPointers | 256 | 152 | 256 | +| LLP64_MS | cpp.cpp:86:8:86:19 | Enums_Packed | 184 | 184 | 192 | +| LLP64_MS | cpp.cpp:107:8:107:27 | Enums_MixedBitfields | 16 | 16 | 16 | +| LLP64_MS | cpp.cpp:117:8:117:34 | Enums_MixedBitfields_Padded | 24 | 16 | 32 | +| LLP64_MS | cpp.cpp:131:8:131:17 | Base_Empty | 0 | 0 | 8 | +| LLP64_MS | cpp.cpp:138:8:138:24 | Derived_EmptyBase | 32 | 32 | 32 | +| LLP64_MS | cpp.cpp:147:8:147:21 | Base_NoPadding | 32 | 32 | 32 | +| LLP64_MS | cpp.cpp:154:8:154:28 | Derived_NoBasePadding | 64 | 64 | 64 | +| LLP64_MS | cpp.cpp:163:8:163:19 | Base_Padding | 48 | 48 | 64 | +| LLP64_MS | cpp.cpp:174:8:174:26 | Derived_BasePadding | 80 | 64 | 96 | +| LLP64_MS | cpp.cpp:185:8:185:28 | Intermediate_NoFields | 32 | 32 | 32 | +| LLP64_MS | cpp.cpp:192:8:192:39 | Derived_NonEmptyBaseWithNoFields | 64 | 64 | 64 | +| LLP64_MS | cpp.cpp:206:8:206:28 | StaticMemberVariables | 32 | 32 | 32 | +| LLP64_MS | cpp.cpp:221:8:221:34 | Derived_EmptyBase_Conflict1 | 8 | 8 | 8 | +| LLP64_MS | cpp.cpp:231:8:231:34 | Derived_EmptyBase_Conflict2 | 32 | 32 | 32 | +| LLP64_MS | cpp.cpp:243:8:243:18 | Base_Empty2 | 0 | 0 | 8 | +| LLP64_MS | cpp.cpp:252:8:252:34 | Derived_EmptyBase_Conflict3 | 8 | 8 | 8 | +| LLP64_MS | cpp.cpp:262:8:262:38 | Base_NoPaddingButSmallAlignment | 16 | 16 | 16 | +| LLP64_MS | cpp.cpp:269:8:269:52 | Derived_NoBasePaddingButNeedsAlignmentPadding | 64 | 48 | 64 | +| LLP64_MS | file://:0:0:0:0 | __va_list_tag | 192 | 192 | 192 | +| LLP64_MS | test.c:6:8:6:22 | Integers_Packed | 312 | 312 | 320 | +| LLP64_MS | test.c:38:8:38:24 | Integers_Assorted | 304 | 192 | 320 | +| LLP64_MS | test.c:75:8:75:20 | Floats_Packed | 160 | 160 | 192 | +| LLP64_MS | test.c:92:8:92:20 | Arrays_Packed | 96 | 96 | 96 | +| LLP64_MS | test.c:100:8:100:19 | Arrays_Mixed | 208 | 176 | 256 | +| LLP64_MS | test.c:120:8:120:21 | Pointers_Mixed | 128 | 96 | 128 | +| LP64 | bitfields.c:14:8:14:24 | Bitfield_Straddle | 80 | 64 | 96 | +| LP64 | bitfields.c:31:8:31:27 | Bitfield_longlong_25 | 33 | 33 | 64 | +| LP64 | bitfields.c:45:8:45:27 | Bitfield_longlong_57 | 121 | 65 | 128 | +| LP64 | bitfields.c:60:8:60:20 | Bitfield_Int7 | 15 | 15 | 32 | +| LP64 | bitfields.c:74:8:74:20 | Bitfield_Int8 | 16 | 16 | 32 | +| LP64 | bitfields.c:88:8:88:20 | Bitfield_Int9 | 17 | 17 | 32 | +| LP64 | bitfields.c:102:8:102:22 | Bitfield_Short7 | 15 | 15 | 16 | +| LP64 | bitfields.c:116:8:116:22 | Bitfield_Short8 | 16 | 16 | 16 | +| LP64 | bitfields.c:130:8:130:22 | Bitfield_Short9 | 25 | 17 | 32 | +| LP64 | bitfields.c:144:8:144:21 | Bitfield_Mixed | 16 | 16 | 32 | +| LP64 | bitfields.c:164:8:164:33 | Bitfield_TypeEquality_char | 4 | 4 | 8 | +| LP64 | bitfields.c:170:8:170:39 | Bitfield_TypeEquality_char_uchar | 4 | 4 | 8 | +| LP64 | bitfields.c:176:8:176:39 | Bitfield_TypeEquality_char_schar | 4 | 4 | 8 | +| LP64 | bitfields.c:182:8:182:38 | Bitfield_TypeEquality_char_CHAR | 4 | 4 | 8 | +| LP64 | bitfields.c:188:8:188:34 | Bitfield_TypeEquality_short | 4 | 4 | 16 | +| LP64 | bitfields.c:194:8:194:41 | Bitfield_TypeEquality_short_ushort | 4 | 4 | 16 | +| LP64 | bitfields.c:200:8:200:37 | Bitfield_TypeEquality_int_long | 4 | 4 | 64 | +| LP64 | bitfields.c:208:8:208:38 | Bitfield_TypeEquality_int_ulong | 4 | 4 | 64 | +| LP64 | cpp.cpp:6:8:6:28 | ReferencesAndPointers | 256 | 168 | 256 | +| LP64 | cpp.cpp:86:8:86:19 | Enums_Packed | 216 | 216 | 256 | +| LP64 | cpp.cpp:107:8:107:27 | Enums_MixedBitfields | 16 | 16 | 16 | +| LP64 | cpp.cpp:117:8:117:34 | Enums_MixedBitfields_Padded | 16 | 16 | 16 | +| LP64 | cpp.cpp:131:8:131:17 | Base_Empty | 0 | 0 | 8 | +| LP64 | cpp.cpp:138:8:138:24 | Derived_EmptyBase | 32 | 32 | 32 | +| LP64 | cpp.cpp:147:8:147:21 | Base_NoPadding | 32 | 32 | 32 | +| LP64 | cpp.cpp:154:8:154:28 | Derived_NoBasePadding | 64 | 64 | 64 | +| LP64 | cpp.cpp:163:8:163:19 | Base_Padding | 48 | 48 | 64 | +| LP64 | cpp.cpp:174:8:174:26 | Derived_BasePadding | 80 | 64 | 96 | +| LP64 | cpp.cpp:185:8:185:28 | Intermediate_NoFields | 32 | 32 | 32 | +| LP64 | cpp.cpp:192:8:192:39 | Derived_NonEmptyBaseWithNoFields | 64 | 64 | 64 | +| LP64 | cpp.cpp:206:8:206:28 | StaticMemberVariables | 32 | 32 | 32 | +| LP64 | cpp.cpp:221:8:221:34 | Derived_EmptyBase_Conflict1 | 16 | 8 | 16 | +| LP64 | cpp.cpp:231:8:231:34 | Derived_EmptyBase_Conflict2 | 64 | 32 | 64 | +| LP64 | cpp.cpp:243:8:243:18 | Base_Empty2 | 0 | 0 | 8 | +| LP64 | cpp.cpp:252:8:252:34 | Derived_EmptyBase_Conflict3 | 16 | 8 | 16 | +| LP64 | cpp.cpp:262:8:262:38 | Base_NoPaddingButSmallAlignment | 16 | 16 | 16 | +| LP64 | cpp.cpp:269:8:269:52 | Derived_NoBasePaddingButNeedsAlignmentPadding | 64 | 48 | 64 | +| LP64 | file://:0:0:0:0 | __va_list_tag | 192 | 192 | 192 | +| LP64 | test.c:6:8:6:22 | Integers_Packed | 376 | 376 | 384 | +| LP64 | test.c:38:8:38:24 | Integers_Assorted | 368 | 224 | 384 | +| LP64 | test.c:75:8:75:20 | Floats_Packed | 224 | 224 | 256 | +| LP64 | test.c:92:8:92:20 | Arrays_Packed | 96 | 96 | 96 | +| LP64 | test.c:100:8:100:19 | Arrays_Mixed | 208 | 176 | 256 | +| LP64 | test.c:120:8:120:21 | Pointers_Mixed | 128 | 96 | 128 | diff --git a/cpp/ql/test/library-tests/padding/padding.ql b/cpp/ql/test/library-tests/padding/padding.ql new file mode 100644 index 000000000000..ce2b27193997 --- /dev/null +++ b/cpp/ql/test/library-tests/padding/padding.ql @@ -0,0 +1,4 @@ +import semmle.code.cpp.padding.Padding + +from Architecture a, PaddedType t +select a, t, t.typeBitSize(a), t.dataSize(a), t.paddedSize(a) diff --git a/cpp/ql/test/library-tests/padding/size_asserts.h b/cpp/ql/test/library-tests/padding/size_asserts.h new file mode 100644 index 000000000000..674257cba41a --- /dev/null +++ b/cpp/ql/test/library-tests/padding/size_asserts.h @@ -0,0 +1,46 @@ +// Static assert macro +#define CASSERT(condition) typedef char cassertTypedef[((condition) != 0) ? 1 : -1] + +#if defined(_MSC_VER) +#define CASSERT_MSVC(condition) CASSERT(condition) +#define CASSERT_GCC(condition) +#if defined(_WIN64) +#define TARGET_BIT_SIZE 64 +#else +#define TARGET_BIT_SIZE 32 +#endif +#elif defined(__GNUC__) +#define CASSERT_MSVC(condition) +#define CASSERT_GCC(condition) CASSERT(condition) +#if defined(__x86_64) +#define TARGET_BIT_SIZE 64 +#else +#define TARGET_BIT_SIZE 32 +#endif +#else +CASSERT(0); +#endif + +#if defined(_MSC_VER) && (TARGET_BIT_SIZE == 32) +#define CASSERT_MSVC32(condition) CASSERT(condition) +#else +#define CASSERT_MSVC32(condition) +#endif + +#if defined(_MSC_VER) && (TARGET_BIT_SIZE == 64) +#define CASSERT_MSVC64(condition) CASSERT(condition) +#else +#define CASSERT_MSVC64(condition) +#endif + +#if defined(__GNUC__) && (TARGET_BIT_SIZE == 32) +#define CASSERT_GCC32(condition) CASSERT(condition) +#else +#define CASSERT_GCC32(condition) +#endif + +#if defined(__GNUC__) && (TARGET_BIT_SIZE == 64) +#define CASSERT_GCC64(condition) CASSERT(condition) +#else +#define CASSERT_GCC64(condition) +#endif diff --git a/cpp/ql/test/library-tests/padding/test.c b/cpp/ql/test/library-tests/padding/test.c new file mode 100644 index 000000000000..41f19fcd5d60 --- /dev/null +++ b/cpp/ql/test/library-tests/padding/test.c @@ -0,0 +1,132 @@ +#include "size_asserts.h" + +CASSERT_MSVC(sizeof(long) == 4); +CASSERT_GCC(sizeof(long) == sizeof(void*)); + +struct Integers_Packed +{ +// 0 + unsigned long long ull; +// 8 + long long ll; +// 16 + unsigned long ul; +// 24(GCC64); 20(GCC32, MSVC) + long l; +// 32(GCC64); 24(GCC32, MSVC) + unsigned int ui; +// 36(GCC64); 28(GCC32, MSVC) + int i; +// 40(GCC64); 32(GCC32, MSVC) + unsigned short us; +// 42(GCC64); 34(GCC32, MSVC) + short s; +// 44(GCC64); 36(GCC32, MSVC) + unsigned char uc; +// 45(GCC64); 37(GCC32, MSVC) + signed char sc; +// 46(GCC64); 38(GCC32, MSVC) + char c; +// 47(GCC64); 39(GCC32, MSVC) +// Pad 1 +// 48(GCC64); 40(GCC32, MSVC) +}; +CASSERT_MSVC(sizeof(struct Integers_Packed) == 40); +CASSERT_GCC32(sizeof(struct Integers_Packed) == 40); +CASSERT_GCC64(sizeof(struct Integers_Packed) == 48); + +struct Integers_Assorted +{ +// 0 + char c1; +// Pad 7(GCC64, MSVC); 3(GCC32) +// 8(GCC64, MSVC); 4(GCC32) + long long ll1; +// 16(GCC64, MSVC); 12(GCC32) + char c2; +// 17(GCC64, MSVC); 13(GCC32) +// Pad 7(GCC64); 3(GCC32, MSVC) +// 24(GCC64); 20(MSVC); 16(GCC32) + long l2; +// 32(GCC64); 24(MSVC); 20(GCC32) + char c3; +// 33(GCC64); 25(MSVC); 21(GCC32) +// Pad 3 +// 36(GCC64); 28(MSVC); 24(GCC32) + int i3; +// 40(GCC64); 32( MSVC); 28(GCC32) + char c4; +// 41(GCC64); 33(MSVC); 29(GCC32) +// Pad 1 +// 42(GCC64); 34(MSVC); 30(GCC32) + short s4; +// 44(GCC64); 36(MSVC); 32(GCC32) + char c5; +// 45(GCC64); 37(MSVC); 33(GCC32) + unsigned char uc5; +// 46(GCC64); 38(MSVC); 34(GCC32) +// Pad 2 +// 48(GCC64); 40(MSVC); 36(GCC32) +}; +CASSERT_MSVC(sizeof(struct Integers_Assorted) == 40); +CASSERT_GCC32(sizeof(struct Integers_Assorted) == 36); +CASSERT_GCC64(sizeof(struct Integers_Assorted) == 48); + +struct Floats_Packed +{ +// 0 + long double ld; +// 16(GCC64); 12(GCC32); 8(MSVC) + double d; +// Pad 4(GCC32); 0(GCC64, MSVC) +// 24(GCC64); 16(GCC32, MSVC) + float f; +// 28(GCC); 20(GCC32, MSVC) +// Pad 4 +// 32(GCC64); 24(GCC32, MSVC) +}; +CASSERT_MSVC(sizeof(struct Floats_Packed) == 24); +CASSERT_GCC32(sizeof(struct Floats_Packed) == 24); +CASSERT_GCC64(sizeof(struct Floats_Packed) == 32); + +struct Arrays_Packed +{ +// 0 + char ac[12]; +// 12 +}; +CASSERT(sizeof(struct Arrays_Packed) == 12); + +struct Arrays_Mixed +{ +// 0 + int a; +// 4 +// Pad 4(GCC64, MSVC); 0(GCC32) +// 8 (GCC64, MSVC); 4(GCC32) + double b; +// 16 (GCC64, MSVC); 12(GCC32) + int c; +// 20 (GCC64, MSVC); 16(GCC32) + char d[6]; +// 26 (GCC64, MSVC); 22(GCC32) +// Pad 6(GCC64, MSVC); 2(GCC32) +// 32 (GCC64, MSVC); 24(GCC32) +}; +CASSERT_MSVC(sizeof(struct Arrays_Mixed) == 32); +CASSERT_GCC64(sizeof(struct Arrays_Mixed) == 32); +CASSERT_GCC32(sizeof(struct Arrays_Mixed) == 24); + +struct Pointers_Mixed { +// 0 + int a; +// 4 +// Pad 4(GCC64, MSVC64); 0(GCC32, MSVC32) +// 8 (GCC64, MSVC64); 4(GCC32, MSVC32) + float* p; +// 16 (GCC64, MSVC64); 8(GCC32, MSVC32) +}; +CASSERT_MSVC64(sizeof(struct Pointers_Mixed) == 16); +CASSERT_MSVC32(sizeof(struct Pointers_Mixed) == 8); +CASSERT_GCC64(sizeof(struct Pointers_Mixed) == 16); +CASSERT_GCC32(sizeof(struct Pointers_Mixed) == 8); diff --git a/cpp/ql/test/library-tests/parameters/catch/catch.cpp b/cpp/ql/test/library-tests/parameters/catch/catch.cpp new file mode 100644 index 000000000000..6b3ea698c827 --- /dev/null +++ b/cpp/ql/test/library-tests/parameters/catch/catch.cpp @@ -0,0 +1,23 @@ +class Foo {}; + +void named() { + try { + } + catch(Foo x) { + } + catch(const char* y) { + } + catch(int z) { + } +} + +void unnamed() { + try { + } + catch(Foo) { + } + catch(const char*) { + } + catch(int) { + } +} diff --git a/cpp/ql/test/library-tests/parameters/catch/parameters.expected b/cpp/ql/test/library-tests/parameters/catch/parameters.expected new file mode 100644 index 000000000000..2e751af800b4 --- /dev/null +++ b/cpp/ql/test/library-tests/parameters/catch/parameters.expected @@ -0,0 +1,8 @@ +| 0 | Foo && | p#0 | +| 0 | const Foo & | p#0 | +| 6 | Foo | x | +| 8 | const char * | y | +| 10 | int | z | +| 17 | Foo | p#0 | +| 19 | const char * | p#0 | +| 21 | int | p#0 | diff --git a/cpp/ql/test/library-tests/parameters/catch/parameters.ql b/cpp/ql/test/library-tests/parameters/catch/parameters.ql new file mode 100644 index 000000000000..e93eddce2d0b --- /dev/null +++ b/cpp/ql/test/library-tests/parameters/catch/parameters.ql @@ -0,0 +1,4 @@ +import cpp + +from Parameter p +select p.getLocation().getStartLine(), p.getType().toString(), p.getName() diff --git a/cpp/ql/test/library-tests/parameters/parameters/Parameters1.expected b/cpp/ql/test/library-tests/parameters/parameters/Parameters1.expected new file mode 100644 index 000000000000..bb7a28a21b7d --- /dev/null +++ b/cpp/ql/test/library-tests/parameters/parameters/Parameters1.expected @@ -0,0 +1,6 @@ +| file://:0:0:0:0 | operator= | 0 | +| file://:0:0:0:0 | operator= | 0 | +| parameters.cpp:3:5:3:14 | Callback_1 | 2 | +| parameters.cpp:8:5:8:14 | Callback_2 | 2 | +| parameters.cpp:13:5:13:14 | Callback_3 | 2 | +| parameters.cpp:18:6:18:13 | Dispatch | 5 | diff --git a/cpp/ql/test/library-tests/parameters/parameters/Parameters1.ql b/cpp/ql/test/library-tests/parameters/parameters/Parameters1.ql new file mode 100644 index 000000000000..db6b6087e7e3 --- /dev/null +++ b/cpp/ql/test/library-tests/parameters/parameters/Parameters1.ql @@ -0,0 +1,4 @@ +import cpp + +from Function f +select f, count(f.getAParameter()) diff --git a/cpp/ql/test/library-tests/parameters/parameters/Parameters2.expected b/cpp/ql/test/library-tests/parameters/parameters/Parameters2.expected new file mode 100644 index 000000000000..f082a67fcf66 --- /dev/null +++ b/cpp/ql/test/library-tests/parameters/parameters/Parameters2.expected @@ -0,0 +1 @@ +| 0 | diff --git a/cpp/ql/test/library-tests/parameters/parameters/Parameters2.ql b/cpp/ql/test/library-tests/parameters/parameters/Parameters2.ql new file mode 100644 index 000000000000..124a63868487 --- /dev/null +++ b/cpp/ql/test/library-tests/parameters/parameters/Parameters2.ql @@ -0,0 +1,5 @@ +import cpp + +select count(Function f, Function g | f.getAParameter() = g.getAParameter() + and f != g) + diff --git a/cpp/ql/test/library-tests/parameters/parameters/Parameters3.expected b/cpp/ql/test/library-tests/parameters/parameters/Parameters3.expected new file mode 100644 index 000000000000..db2f7ad81de1 --- /dev/null +++ b/cpp/ql/test/library-tests/parameters/parameters/Parameters3.expected @@ -0,0 +1,5 @@ +| parameters.cpp:18:6:18:13 | Dispatch | 0 | n | true | +| parameters.cpp:18:6:18:13 | Dispatch | 1 | a | true | +| parameters.cpp:18:6:18:13 | Dispatch | 2 | b | true | +| parameters.cpp:18:6:18:13 | Dispatch | 3 | c | true | +| parameters.cpp:18:6:18:13 | Dispatch | 4 | p#4 | false | diff --git a/cpp/ql/test/library-tests/parameters/parameters/Parameters3.ql b/cpp/ql/test/library-tests/parameters/parameters/Parameters3.ql new file mode 100644 index 000000000000..b88808a132ec --- /dev/null +++ b/cpp/ql/test/library-tests/parameters/parameters/Parameters3.ql @@ -0,0 +1,14 @@ +/** + * @name Parameters3 + * @kind table + */ +import cpp + +from Function f, int i, Parameter p, string pname, boolean named +where f.hasName("Dispatch") +and f.getParameter(i) = p +and p.getName() = pname +and ( + p.isNamed() and named = true + or not p.isNamed() and named = false) +select f, i, pname, named diff --git a/cpp/ql/test/library-tests/parameters/parameters/parameters.cpp b/cpp/ql/test/library-tests/parameters/parameters/parameters.cpp new file mode 100644 index 000000000000..1c85d65409bf --- /dev/null +++ b/cpp/ql/test/library-tests/parameters/parameters/parameters.cpp @@ -0,0 +1,28 @@ +typedef int (*CallbackFn)(int a, int b); + +int Callback_1(int a, int b) +{ + return a + b; +} + +int Callback_2(int a, int b) +{ + return a; +} + +int Callback_3(int, int b) +{ + return b; +} + +void Dispatch(int n, int a, int b, int c, int) +{ + CallbackFn pFn; + switch(n) + { + case 0: pFn = &Callback_1; break; + case 1: pFn = &Callback_2; break; + default: pFn = &Callback_3; break; + } + (*pFn)(a,b); +} diff --git a/cpp/ql/test/library-tests/parameters/toStrings/params.expected b/cpp/ql/test/library-tests/parameters/toStrings/params.expected new file mode 100644 index 000000000000..908089b31e68 --- /dev/null +++ b/cpp/ql/test/library-tests/parameters/toStrings/params.expected @@ -0,0 +1,24 @@ +| test.c:2:8:2:10 | declaration of 1st parameter | +| test.c:2:13:2:15 | declaration of 2nd parameter | +| test.c:2:18:2:20 | declaration of 3rd parameter | +| test.c:2:23:2:25 | declaration of 4th parameter | +| test.c:3:8:3:10 | declaration of y1 as anonymous 1st parameter | +| test.c:3:13:3:15 | declaration of y2 as anonymous 2nd parameter | +| test.c:3:18:3:20 | declaration of y3 as anonymous 3rd parameter | +| test.c:3:23:3:25 | declaration of y4 as anonymous 4th parameter | +| test.c:4:12:4:13 | declaration of x1 | +| test.c:4:20:4:21 | declaration of x2 | +| test.c:4:28:4:29 | declaration of x3 | +| test.c:4:36:4:37 | declaration of x4 | +| test.c:5:12:5:13 | declaration of y1 as x1 | +| test.c:5:20:5:21 | declaration of y2 as x2 | +| test.c:5:28:5:29 | declaration of y3 as x3 | +| test.c:5:36:5:37 | declaration of y4 as x4 | +| test.c:7:12:7:13 | definition of y1 | +| test.c:7:20:7:21 | definition of y2 | +| test.c:7:28:7:29 | definition of y3 | +| test.c:7:36:7:37 | definition of y4 | +| test.c:8:12:8:13 | definition of y1 | +| test.c:8:20:8:21 | definition of y2 | +| test.c:8:28:8:29 | definition of y3 | +| test.c:8:36:8:37 | definition of y4 | diff --git a/cpp/ql/test/library-tests/parameters/toStrings/params.ql b/cpp/ql/test/library-tests/parameters/toStrings/params.ql new file mode 100644 index 000000000000..3b3d3ee2a6f1 --- /dev/null +++ b/cpp/ql/test/library-tests/parameters/toStrings/params.ql @@ -0,0 +1,6 @@ +import cpp + +from ParameterDeclarationEntry pde, string str +where if exists(pde.toString()) then str = pde.toString() else str = "" +select pde.getLocation().toString(), str + diff --git a/cpp/ql/test/library-tests/parameters/toStrings/test.c b/cpp/ql/test/library-tests/parameters/toStrings/test.c new file mode 100644 index 000000000000..8462fd3a2420 --- /dev/null +++ b/cpp/ql/test/library-tests/parameters/toStrings/test.c @@ -0,0 +1,9 @@ + +int f1(int, int, int, int); +int f2(int, int, int, int); +int f3(int x1, int x2, int x3, int x4); +int f4(int x1, int x2, int x3, int x4); + +int f2(int y1, int y2, int y3, int y4) {} +int f4(int y1, int y2, int y3, int y4) {} + diff --git a/cpp/ql/test/library-tests/permissive/accesses.expected b/cpp/ql/test/library-tests/permissive/accesses.expected new file mode 100644 index 000000000000..f3e66dcedaf7 --- /dev/null +++ b/cpp/ql/test/library-tests/permissive/accesses.expected @@ -0,0 +1 @@ +| permissive.cpp:6:5:6:7 | str | diff --git a/cpp/ql/test/library-tests/permissive/accesses.ql b/cpp/ql/test/library-tests/permissive/accesses.ql new file mode 100644 index 000000000000..92511c9a75cb --- /dev/null +++ b/cpp/ql/test/library-tests/permissive/accesses.ql @@ -0,0 +1,5 @@ +import cpp + +from Access a +select a + diff --git a/cpp/ql/test/library-tests/permissive/calls.expected b/cpp/ql/test/library-tests/permissive/calls.expected new file mode 100644 index 000000000000..35e31c1b6911 --- /dev/null +++ b/cpp/ql/test/library-tests/permissive/calls.expected @@ -0,0 +1,2 @@ +| non_permissive.cpp:6:3:6:3 | call to f | non_permissive.cpp:2:13:2:13 | f | +| permissive.cpp:6:3:6:3 | call to f | permissive.cpp:2:13:2:13 | f | diff --git a/cpp/ql/test/library-tests/permissive/calls.ql b/cpp/ql/test/library-tests/permissive/calls.ql new file mode 100644 index 000000000000..cb4368e31a12 --- /dev/null +++ b/cpp/ql/test/library-tests/permissive/calls.ql @@ -0,0 +1,5 @@ +import cpp + +from FunctionCall fc +select fc, fc.getTarget() + diff --git a/cpp/ql/test/library-tests/permissive/diags.expected b/cpp/ql/test/library-tests/permissive/diags.expected new file mode 100644 index 000000000000..fc93c87ced0d --- /dev/null +++ b/cpp/ql/test/library-tests/permissive/diags.expected @@ -0,0 +1,2 @@ +| file://:0:0:0:0 | file://:0:0:0:0 | 4 | Error occurred | There was an error during this compilation | There was an error during this compilation | +| non_permissive.cpp:6:5:6:5 | non_permissive.cpp:6:5:6:5 | 4 | incompatible_param | argument of type 'const char *' is incompatible with parameter of type 'char *' | "non_permissive.cpp", line 6: error: argument of type "const char *" is incompatible with parameter of type "char *"\n f(str);\n ^\n\n | diff --git a/cpp/ql/test/library-tests/permissive/diags.ql b/cpp/ql/test/library-tests/permissive/diags.ql new file mode 100644 index 000000000000..965b3498444b --- /dev/null +++ b/cpp/ql/test/library-tests/permissive/diags.ql @@ -0,0 +1,5 @@ +import cpp + +from Diagnostic d +select d.getLocation(), d.getSeverity(), d.getTag(), d.getMessage(), d.getFullMessage() + diff --git a/cpp/ql/test/library-tests/permissive/non_permissive.cpp b/cpp/ql/test/library-tests/permissive/non_permissive.cpp new file mode 100644 index 000000000000..0de71d3a042a --- /dev/null +++ b/cpp/ql/test/library-tests/permissive/non_permissive.cpp @@ -0,0 +1,8 @@ +// semmle-extractor-options: --expect_errors +static void f(char* foo) {} + +static void g(void) { + const char* str = "foo"; + f(str); +} + diff --git a/cpp/ql/test/library-tests/permissive/permissive.cpp b/cpp/ql/test/library-tests/permissive/permissive.cpp new file mode 100644 index 000000000000..15f5b94ef61c --- /dev/null +++ b/cpp/ql/test/library-tests/permissive/permissive.cpp @@ -0,0 +1,8 @@ +// semmle-extractor-options: --edg --permissive +static void f(char* foo) {} + +static void g(void) { + const char* str = "foo"; + f(str); +} + diff --git a/cpp/ql/test/library-tests/pod/isPOD.expected b/cpp/ql/test/library-tests/pod/isPOD.expected new file mode 100644 index 000000000000..f85ac7f8a774 --- /dev/null +++ b/cpp/ql/test/library-tests/pod/isPOD.expected @@ -0,0 +1,57 @@ +| file://:0:0:0:0 | __va_list_tag | true | +| test1.cpp:5:8:5:8 | S | true | +| test2.cpp:6:8:6:10 | Foo | false | +| test3.cpp:2:7:2:14 | MyClass1 | true | +| test3.cpp:17:7:17:14 | MyClass2 | true | +| test3.cpp:23:7:23:14 | MyClass3 | true | +| test3.cpp:29:8:29:15 | MyClass4 | true | +| test3.cpp:34:7:34:14 | MyClass5 | true | +| test3.cpp:41:7:41:15 | MyClass10 | true | +| test3.cpp:54:7:54:15 | MyClass11 | false | +| test3.cpp:60:7:60:15 | MyClass12 | false | +| test3.cpp:68:7:68:15 | MyClass20 | false | +| test3.cpp:74:7:74:15 | MyClass21 | false | +| test3.cpp:80:7:80:15 | MyClass22 | false | +| test3.cpp:86:7:86:15 | MyClass23 | false | +| test3.cpp:92:7:92:15 | MyClass24 | false | +| test3.cpp:100:7:100:15 | MyClass30 | true | +| test3.cpp:104:7:104:15 | MyClass31 | true | +| test3.cpp:116:7:116:15 | MyClass32 | true | +| test3.cpp:122:7:122:15 | MyClass33 | false | +| test3.cpp:128:7:128:15 | MyClass34 | true | +| test4.cpp:2:8:2:16 | MyStruct1 | false | +| test4.cpp:4:7:4:14 | MyClass1 | true | +| test4.cpp:9:7:9:14 | MyClass2 | false | +| test4.cpp:16:7:16:14 | MyClass3 | false | +| test4.cpp:24:7:24:15 | MyClass4a | true | +| test4.cpp:27:7:27:15 | MyClass4b | false | +| test4.cpp:32:7:32:14 | MyClass5 | false | +| test4.cpp:35:7:35:14 | MyClass6 | true | +| test4.cpp:40:7:40:14 | MyClass7 | false | +| test4.cpp:45:7:45:14 | MyClass8 | true | +| test4.cpp:50:25:50:32 | MyClass9 | false | +| test4.cpp:50:25:50:32 | MyClass9 | false | +| test4.cpp:50:25:50:32 | MyClass9 | true | +| test4.cpp:56:25:56:33 | MyClass10 | true | +| test4.cpp:56:25:56:33 | MyClass10 | false | +| test4.cpp:56:25:56:33 | MyClass10 | true | +| test5.c:2:8:2:16 | MyStruct2 | false | +| test5.c:4:8:4:16 | MyStruct3 | true | +| test6.cpp:2:7:2:13 | Test6_1 | true | +| test6.cpp:8:7:8:13 | Test6_2 | true | +| test6.cpp:14:7:14:13 | Test6_3 | false | +| test6.cpp:21:7:21:13 | Test6_4 | false | +| test6.cpp:28:7:28:14 | Test6_20 | true | +| test6.cpp:32:7:32:14 | Test6_21 | false | +| test6.cpp:36:7:36:14 | Test6_22 | false | +| test6.cpp:39:7:39:14 | Test6_23 | false | +| test6.cpp:42:7:42:14 | Test6_24 | false | +| test6.cpp:47:7:47:14 | Test6_25 | true | +| test6.cpp:52:7:52:14 | Test6_26 | false | +| test6.cpp:57:7:57:14 | Test6_27 | false | +| test6.cpp:64:7:64:14 | Test6_30 | true | +| test6.cpp:67:7:67:14 | Test6_31 | false | +| test6.cpp:72:7:72:14 | Test6_32 | false | +| test6.cpp:82:7:82:14 | Test6_40 | false | +| test6.cpp:87:7:87:14 | Test6_41 | false | +| test6.cpp:90:7:90:14 | Test6_42 | false | diff --git a/cpp/ql/test/library-tests/pod/isPOD.ql b/cpp/ql/test/library-tests/pod/isPOD.ql new file mode 100644 index 000000000000..cf0b0052e6cd --- /dev/null +++ b/cpp/ql/test/library-tests/pod/isPOD.ql @@ -0,0 +1,8 @@ +import cpp + +from Class c, boolean ispod +where + if c.isPOD() + then ispod = true + else ispod = false +select c, ispod diff --git a/cpp/ql/test/library-tests/pod/isPOD03.expected b/cpp/ql/test/library-tests/pod/isPOD03.expected new file mode 100644 index 000000000000..b39b5878de33 --- /dev/null +++ b/cpp/ql/test/library-tests/pod/isPOD03.expected @@ -0,0 +1,57 @@ +| file://:0:0:0:0 | __va_list_tag | true | +| test1.cpp:5:8:5:8 | S | false | +| test2.cpp:6:8:6:10 | Foo | true | +| test3.cpp:2:7:2:14 | MyClass1 | true | +| test3.cpp:17:7:17:14 | MyClass2 | false | +| test3.cpp:23:7:23:14 | MyClass3 | false | +| test3.cpp:29:8:29:15 | MyClass4 | true | +| test3.cpp:34:7:34:14 | MyClass5 | false | +| test3.cpp:41:7:41:15 | MyClass10 | true | +| test3.cpp:54:7:54:15 | MyClass11 | false | +| test3.cpp:60:7:60:15 | MyClass12 | false | +| test3.cpp:68:7:68:15 | MyClass20 | false | +| test3.cpp:74:7:74:15 | MyClass21 | false | +| test3.cpp:80:7:80:15 | MyClass22 | false | +| test3.cpp:86:7:86:15 | MyClass23 | false | +| test3.cpp:92:7:92:15 | MyClass24 | false | +| test3.cpp:100:7:100:15 | MyClass30 | false | +| test3.cpp:104:7:104:15 | MyClass31 | true | +| test3.cpp:116:7:116:15 | MyClass32 | false | +| test3.cpp:122:7:122:15 | MyClass33 | false | +| test3.cpp:128:7:128:15 | MyClass34 | false | +| test4.cpp:2:8:2:16 | MyStruct1 | true | +| test4.cpp:4:7:4:14 | MyClass1 | true | +| test4.cpp:9:7:9:14 | MyClass2 | false | +| test4.cpp:16:7:16:14 | MyClass3 | false | +| test4.cpp:24:7:24:15 | MyClass4a | false | +| test4.cpp:27:7:27:15 | MyClass4b | false | +| test4.cpp:32:7:32:14 | MyClass5 | false | +| test4.cpp:35:7:35:14 | MyClass6 | true | +| test4.cpp:40:7:40:14 | MyClass7 | false | +| test4.cpp:45:7:45:14 | MyClass8 | true | +| test4.cpp:50:25:50:32 | MyClass9 | false | +| test4.cpp:50:25:50:32 | MyClass9 | false | +| test4.cpp:50:25:50:32 | MyClass9 | false | +| test4.cpp:56:25:56:33 | MyClass10 | false | +| test4.cpp:56:25:56:33 | MyClass10 | false | +| test4.cpp:56:25:56:33 | MyClass10 | false | +| test5.c:2:8:2:16 | MyStruct2 | true | +| test5.c:4:8:4:16 | MyStruct3 | true | +| test6.cpp:2:7:2:13 | Test6_1 | true | +| test6.cpp:8:7:8:13 | Test6_2 | false | +| test6.cpp:14:7:14:13 | Test6_3 | false | +| test6.cpp:21:7:21:13 | Test6_4 | false | +| test6.cpp:28:7:28:14 | Test6_20 | false | +| test6.cpp:32:7:32:14 | Test6_21 | false | +| test6.cpp:36:7:36:14 | Test6_22 | false | +| test6.cpp:39:7:39:14 | Test6_23 | false | +| test6.cpp:42:7:42:14 | Test6_24 | false | +| test6.cpp:47:7:47:14 | Test6_25 | true | +| test6.cpp:52:7:52:14 | Test6_26 | false | +| test6.cpp:57:7:57:14 | Test6_27 | false | +| test6.cpp:64:7:64:14 | Test6_30 | true | +| test6.cpp:67:7:67:14 | Test6_31 | false | +| test6.cpp:72:7:72:14 | Test6_32 | false | +| test6.cpp:82:7:82:14 | Test6_40 | false | +| test6.cpp:87:7:87:14 | Test6_41 | false | +| test6.cpp:90:7:90:14 | Test6_42 | false | diff --git a/cpp/ql/test/library-tests/pod/isPOD03.ql b/cpp/ql/test/library-tests/pod/isPOD03.ql new file mode 100644 index 000000000000..8653461a0c22 --- /dev/null +++ b/cpp/ql/test/library-tests/pod/isPOD03.ql @@ -0,0 +1,9 @@ +import semmle.code.cpp.PODType03 + + +from Class c, boolean ispod +where + if isPODClass03(c) + then ispod = true + else ispod = false +select c, ispod diff --git a/cpp/ql/test/library-tests/pod/test1.cpp b/cpp/ql/test/library-tests/pod/test1.cpp new file mode 100644 index 000000000000..d10f67f28d4f --- /dev/null +++ b/cpp/ql/test/library-tests/pod/test1.cpp @@ -0,0 +1,10 @@ + + + +// This struct is POD in C++11, but not in C++03. +struct S +{ + int a; + S(int b) : a(b) {} + S() = default; +}; diff --git a/cpp/ql/test/library-tests/pod/test2.cpp b/cpp/ql/test/library-tests/pod/test2.cpp new file mode 100644 index 000000000000..8b060da9eceb --- /dev/null +++ b/cpp/ql/test/library-tests/pod/test2.cpp @@ -0,0 +1,11 @@ + + +enum Cheese { cheddar, brie, camembert, gouda }; + +// This struct is POD in C++03, but not in C++11. +struct Foo +{ + int a = 0; + int b = 0; + Cheese cheese = cheddar; +}; diff --git a/cpp/ql/test/library-tests/pod/test3.cpp b/cpp/ql/test/library-tests/pod/test3.cpp new file mode 100644 index 000000000000..3d928c798f29 --- /dev/null +++ b/cpp/ql/test/library-tests/pod/test3.cpp @@ -0,0 +1,132 @@ + +class MyClass1 +{ + // public / static data members +public: + int public_val; + char public_arr[100]; + static int public_static_val; + +protected: + static int protected_static_val; + +private: + static int private_static_val; +}; + +class MyClass2 +{ +protected: + int protected_val; // has protected non-static variable -> not aggregate / POD +}; + +class MyClass3 +{ +private: + char private_arr[100]; // has private non-static variable -> not aggregate / POD +}; + +struct MyClass4 +{ + int public_val; // (public by default) +}; + +class MyClass5 +{ + int private_val; // (private by default) has private non-static variable -> not aggregate / POD +}; + +// --- + +class MyClass10 +{ + // non-virtual functions +public: + void public_fun(); + +protected: + void protected_fun(); + +private: + void private_fun(); +}; + +class MyClass11 +{ +public: + virtual void fun(); // has virtual function -> not aggregate / POD +}; + +class MyClass12 +{ +public: + virtual ~MyClass12(); // has virtual function -> not aggregate / POD +}; + +// --- + +class MyClass20 +{ +public: + MyClass20 &operator=(const MyClass20 &); // // copy-assignment -> not POD +}; + +class MyClass21 +{ +public: + ~MyClass21(); // destructor -> not POD +}; + +class MyClass22 +{ +public: + MyClass22(); // has user-declared constructor -> not aggregate / POD +}; + +class MyClass23 +{ +public: + MyClass23(int fromInt); // has user-declared constructor -> not aggregate / POD +}; + +class MyClass24 +{ +private: + MyClass24(const MyClass24 &); // has user-declared copy-constructor -> not aggregate / POD +}; + +// --- + +class MyClass30 : public MyClass1 // has a base class -> not aggregate / POD +{ +}; + +class MyClass31 +{ +public: + static MyClass30 public_static_mc30; + +protected: + static MyClass30 protected_static_mc30; + +private: + static MyClass30 private_static_mc30; +}; + +class MyClass32 +{ +public: + MyClass30 public_mc30; // has non-static, non-POD variable -> not POD +}; + +class MyClass33 +{ +protected: + MyClass30 &public_mc30; // has non-static, non-POD variable reference -> not POD +}; + +class MyClass34 +{ +protected: + MyClass30 public_mc30[10]; // has array of non-static, non-POD type -> not POD +}; diff --git a/cpp/ql/test/library-tests/pod/test4.cpp b/cpp/ql/test/library-tests/pod/test4.cpp new file mode 100644 index 000000000000..680f25de97f7 --- /dev/null +++ b/cpp/ql/test/library-tests/pod/test4.cpp @@ -0,0 +1,60 @@ + +struct MyStruct1; + +class MyClass1 { // POD +public: + int a, b, c; +}; + +class MyClass2 { // non-POD due to user-defined constructor +public: + MyClass2(); + + int a, b, c; +}; + +class MyClass3 { // non-POD due to user-defined constructor +public: + MyClass3(int i); + +private: + int a, b, c; +}; + +class MyClass4a : public MyClass1 { // POD because it does not add extra fields +}; + +class MyClass4b : public MyClass1 { // non-POD because it adds extra fields +public: + int x, y, z; +}; + +class MyClass5 : public MyClass2 { // non-POD due to inherited user-defined constructor +}; + +class MyClass6 { // POD +public: + MyClass1 mc1; +}; + +class MyClass7 { // non-POD due to contained user-defined constructor +public: + MyClass2 mc2; +}; + +class MyClass8 { // POD +public: + MyClass2 *mc2; +}; + +template class MyClass9 { // non-POD, because T is not POD + T t; +}; +MyClass9 v1; // POD +MyClass9 v2; // non-POD + +template class MyClass10 { // POD (T is not used) + int i; +}; +MyClass10 v3; // POD +MyClass10 v4; // POD diff --git a/cpp/ql/test/library-tests/pod/test5.c b/cpp/ql/test/library-tests/pod/test5.c new file mode 100644 index 000000000000..e765d81aa0b0 --- /dev/null +++ b/cpp/ql/test/library-tests/pod/test5.c @@ -0,0 +1,7 @@ + +struct MyStruct2; + +struct MyStruct3 // POD +{ + int a, b, c; +}; diff --git a/cpp/ql/test/library-tests/pod/test6.cpp b/cpp/ql/test/library-tests/pod/test6.cpp new file mode 100644 index 000000000000..532ed5b80e36 --- /dev/null +++ b/cpp/ql/test/library-tests/pod/test6.cpp @@ -0,0 +1,93 @@ + +class Test6_1 { +public: + int x, y; + void fun(); +}; + +class Test6_2 { +private: + int x, y; + void fun(); +}; + +class Test6_3 { +public: + int x; +private: + int y; // different access control -> not standard layout +}; + +class Test6_4 { +public: + virtual void fun(); // virtual function -> not standard layout, not trivial +}; + +// --- + +class Test6_20 : public Test6_1 { + static int z; +}; + +class Test6_21 : public Test6_1 { + int z; // non-static variable in more than one part of in heritance graph -> not standard layout +}; + +class Test6_22 : public Test6_3 { // non-standard layout base class -> not standard layout +}; + +class Test6_23 : public virtual Test6_1 { // virtual inheritance -> not standard layout, not trivial +}; + +class Test6_24 { +public: + Test6_3 obj; // non-standard layout, non-static variable -> not standard layout +}; + +class Test6_25 { +public: + static Test6_3 obj; +}; + +class Test6_26 { +public: + Test6_3 &obj_ref; // non-standard layout, non-static variable -> not standard layout +}; + +class Test6_27 { +public: + Test6_3 obj_arr[10]; // non-standard layout, non-static variable -> not standard layout +}; + +// --- + +class Test6_30 { +}; + +class Test6_31 : public Test6_30 { +public: + Test6_30 obj; // base class has same type as first non-static variable -> not standard layout +}; + +class Test6_32 : public Test6_30 { +public: + int x; + Test6_30 obj; + Test6_30 &obj_ref; + Test6_30 obj_arr[10]; +}; + +// --- + +class Test6_40 { +public: + Test6_40() {}; // user provided constructor -> not trivial +}; + +class Test6_41 : public Test6_40 { // base class non-trivial -> not trivial +}; + +class Test6_42 { +public: + Test6_40 obj; // non-trivial variable -> not trivial +}; diff --git a/cpp/ql/test/library-tests/pointsto/address-of-assign/PointsToExpr.expected b/cpp/ql/test/library-tests/pointsto/address-of-assign/PointsToExpr.expected new file mode 100644 index 000000000000..96b9c9936c89 --- /dev/null +++ b/cpp/ql/test/library-tests/pointsto/address-of-assign/PointsToExpr.expected @@ -0,0 +1,2 @@ +| test.cpp:8:9:8:9 | b | test.cpp:1:6:1:6 | a | +| test.cpp:8:12:8:12 | c | test.cpp:1:11:1:11 | b | diff --git a/cpp/ql/test/library-tests/pointsto/address-of-assign/PointsToExpr.ql b/cpp/ql/test/library-tests/pointsto/address-of-assign/PointsToExpr.ql new file mode 100644 index 000000000000..90955bea9a88 --- /dev/null +++ b/cpp/ql/test/library-tests/pointsto/address-of-assign/PointsToExpr.ql @@ -0,0 +1,17 @@ +import cpp +import semmle.code.cpp.pointsto.PointsTo + +class AccessPT extends PointsToExpr +{ + override predicate interesting() { + this instanceof VariableAccess and + exists(FunctionCall use | + use = this.getParent() and + use.getTarget().hasName("use") + ) + } +} + +from AccessPT access, Element loc +where access.pointsTo() = loc +select access, loc diff --git a/cpp/ql/test/library-tests/pointsto/address-of-assign/test.cpp b/cpp/ql/test/library-tests/pointsto/address-of-assign/test.cpp new file mode 100644 index 000000000000..32387d0da359 --- /dev/null +++ b/cpp/ql/test/library-tests/pointsto/address-of-assign/test.cpp @@ -0,0 +1,9 @@ +int *a, **b, ***c; + +extern void use(int *x, ...); + +void f() +{ + c = &(b = &a); + use(a, b, c); +} diff --git a/cpp/ql/test/library-tests/pointsto/arguments/Report.expected b/cpp/ql/test/library-tests/pointsto/arguments/Report.expected new file mode 100644 index 000000000000..f318f0d2635f --- /dev/null +++ b/cpp/ql/test/library-tests/pointsto/arguments/Report.expected @@ -0,0 +1,16 @@ +| calls.c:6:9:6:10 | * ... | calls.c:23:6:23:6 | a | +| calls.c:6:9:6:10 | * ... | calls.c:32:6:32:6 | c | +| calls.c:12:9:12:13 | * ... | calls.c:23:6:23:6 | a | +| calls.c:12:9:12:13 | * ... | calls.c:32:6:32:6 | c | +| calls.c:12:9:12:13 | * ... | calls.c:42:6:42:6 | e | +| calls.c:13:9:13:10 | xv | calls.c:23:6:23:6 | a | +| calls.c:13:9:13:10 | xv | calls.c:32:6:32:6 | c | +| calls.c:13:9:13:10 | xv | calls.c:42:6:42:6 | e | +| calls.c:18:9:18:9 | v | calls.c:23:6:23:6 | a | +| calls.c:18:9:18:9 | v | calls.c:32:6:32:6 | c | +| calls.c:18:9:18:9 | v | calls.c:42:6:42:6 | e | +| calls.c:27:9:27:9 | b | calls.c:23:6:23:6 | a | +| calls.c:27:9:27:9 | b | calls.c:32:6:32:6 | c | +| calls.c:37:9:37:9 | d | calls.c:23:6:23:6 | a | +| calls.c:37:9:37:9 | d | calls.c:32:6:32:6 | c | +| calls.c:46:9:46:9 | f | calls.c:42:6:42:6 | e | diff --git a/cpp/ql/test/library-tests/pointsto/arguments/Report.ql b/cpp/ql/test/library-tests/pointsto/arguments/Report.ql new file mode 100644 index 000000000000..54a5c1cff7f3 --- /dev/null +++ b/cpp/ql/test/library-tests/pointsto/arguments/Report.ql @@ -0,0 +1,16 @@ +import cpp +import semmle.code.cpp.pointsto.PointsTo + +class ReportPT extends PointsToExpr +{ + override predicate interesting() { + exists(FunctionCall report | + report.getTarget().hasName("report") and + report.getAnArgument() = this + ) + } +} + +from ReportPT report, Element loc +where report.pointsTo() = loc +select report, loc diff --git a/cpp/ql/test/library-tests/pointsto/arguments/calls.c b/cpp/ql/test/library-tests/pointsto/arguments/calls.c new file mode 100644 index 000000000000..2ca8e2819435 --- /dev/null +++ b/cpp/ql/test/library-tests/pointsto/arguments/calls.c @@ -0,0 +1,55 @@ +void report(void *p) { } + +void use_parameter_address(int *x) +{ + int **y = &x; + report(*y); +} + +void use_parameter_address_and_value(int *xv) +{ + int **addr = &xv; + report(*addr); + report(xv); +} + +void use_parameter_value(int *v) +{ + report(v); +} + +void use1(void) +{ + int a; + int *b = &a; + use_parameter_address(b); + use_parameter_value(b); + report(b); +} + +void use2(void) +{ + int c; + int *d = &c; + use_parameter_address(d); + use_parameter_address_and_value(d); + use_parameter_value(d); + report(d); +} + +void use3(void) +{ + int e; + int *f = &e; + use_parameter_address_and_value(f); + use_parameter_value(f); + report(f); +} + +int main(int argc, char **argv) +{ + use1(); + use2(); + use3(); + return 0; +} diff --git a/cpp/ql/test/library-tests/pointsto/basic/PointsToExpr.expected b/cpp/ql/test/library-tests/pointsto/basic/PointsToExpr.expected new file mode 100644 index 000000000000..16d8a21992dc --- /dev/null +++ b/cpp/ql/test/library-tests/pointsto/basic/PointsToExpr.expected @@ -0,0 +1,8 @@ +| test.cpp:27:18:27:19 | p1 | test.cpp:6:10:6:10 | a | +| test.cpp:27:18:27:19 | p1 | test.cpp:6:13:6:13 | b | +| test.cpp:27:22:27:23 | p2 | test.cpp:6:10:6:10 | a | +| test.cpp:27:22:27:23 | p2 | test.cpp:6:13:6:13 | b | +| test.cpp:27:26:27:27 | p3 | test.cpp:6:16:6:16 | c | +| test.cpp:27:26:27:27 | p3 | test.cpp:6:19:6:19 | d | +| test.cpp:27:30:27:32 | pp1 | test.cpp:7:21:7:22 | p3 | +| test.cpp:27:35:27:37 | pp2 | test.cpp:7:21:7:22 | p3 | diff --git a/cpp/ql/test/library-tests/pointsto/basic/PointsToExpr.ql b/cpp/ql/test/library-tests/pointsto/basic/PointsToExpr.ql new file mode 100644 index 000000000000..90955bea9a88 --- /dev/null +++ b/cpp/ql/test/library-tests/pointsto/basic/PointsToExpr.ql @@ -0,0 +1,17 @@ +import cpp +import semmle.code.cpp.pointsto.PointsTo + +class AccessPT extends PointsToExpr +{ + override predicate interesting() { + this instanceof VariableAccess and + exists(FunctionCall use | + use = this.getParent() and + use.getTarget().hasName("use") + ) + } +} + +from AccessPT access, Element loc +where access.pointsTo() = loc +select access, loc diff --git a/cpp/ql/test/library-tests/pointsto/basic/anythingPointsToExpr.expected b/cpp/ql/test/library-tests/pointsto/basic/anythingPointsToExpr.expected new file mode 100644 index 000000000000..20ec54327ade --- /dev/null +++ b/cpp/ql/test/library-tests/pointsto/basic/anythingPointsToExpr.expected @@ -0,0 +1,5 @@ +| test.cpp:6:10:6:10 | a | +| test.cpp:6:13:6:13 | b | +| test.cpp:6:16:6:16 | c | +| test.cpp:6:19:6:19 | d | +| test.cpp:7:21:7:22 | p3 | diff --git a/cpp/ql/test/library-tests/pointsto/basic/anythingPointsToExpr.ql b/cpp/ql/test/library-tests/pointsto/basic/anythingPointsToExpr.ql new file mode 100644 index 000000000000..8e0c24b14a71 --- /dev/null +++ b/cpp/ql/test/library-tests/pointsto/basic/anythingPointsToExpr.ql @@ -0,0 +1,17 @@ +import cpp +import semmle.code.cpp.pointsto.PointsTo + +class AccessPT extends PointsToExpr +{ + override predicate interesting() { + this instanceof VariableAccess and + exists(FunctionCall use | + use = this.getParent() and + use.getTarget().hasName("use") + ) + } +} + +from Element loc +where anythingPointsTo(loc) +select loc diff --git a/cpp/ql/test/library-tests/pointsto/basic/compoundEdge.expected b/cpp/ql/test/library-tests/pointsto/basic/compoundEdge.expected new file mode 100644 index 000000000000..93db95738cf6 --- /dev/null +++ b/cpp/ql/test/library-tests/pointsto/basic/compoundEdge.expected @@ -0,0 +1 @@ +| 12 | test.cpp:12:6:12:9 | test | 12 | test.cpp:12:6:12:9 | test | +arg0 | 14 | test.cpp:14:6:14:9 | cond | 0 | diff --git a/cpp/ql/test/library-tests/pointsto/basic/compoundEdge.ql b/cpp/ql/test/library-tests/pointsto/basic/compoundEdge.ql new file mode 100644 index 000000000000..609293d0f06e --- /dev/null +++ b/cpp/ql/test/library-tests/pointsto/basic/compoundEdge.ql @@ -0,0 +1,5 @@ +import semmle.code.cpp.pointsto.PointsTo + +from Element parent, Element element, string label, Element other, int kind +where compoundEdge(parent, element, label, other, kind) +select parent.getLocation().getStartLine(), parent, element.getLocation().getStartLine(), element, label, other.getLocation().getStartLine(), other, kind diff --git a/cpp/ql/test/library-tests/pointsto/basic/flow.expected b/cpp/ql/test/library-tests/pointsto/basic/flow.expected new file mode 100644 index 000000000000..98988a0b7686 --- /dev/null +++ b/cpp/ql/test/library-tests/pointsto/basic/flow.expected @@ -0,0 +1,26 @@ +| test.cpp:3:11:3:12 | 10 | test.cpp:3:11:3:12 | (unsigned long)... | +| test.cpp:6:10:6:10 | a | test.cpp:2:16:2:23 | MyStruct | +| test.cpp:6:10:6:10 | a | test.cpp:16:9:16:9 | a | +| test.cpp:6:13:6:13 | b | test.cpp:2:16:2:23 | MyStruct | +| test.cpp:6:13:6:13 | b | test.cpp:18:9:18:9 | b | +| test.cpp:6:16:6:16 | c | test.cpp:2:16:2:23 | MyStruct | +| test.cpp:6:16:6:16 | c | test.cpp:22:8:22:8 | c | +| test.cpp:6:19:6:19 | d | test.cpp:2:16:2:23 | MyStruct | +| test.cpp:6:19:6:19 | d | test.cpp:24:8:24:8 | d | +| test.cpp:7:11:7:12 | p1 | test.cpp:16:3:16:4 | p1 | +| test.cpp:7:11:7:12 | p1 | test.cpp:18:3:18:4 | p1 | +| test.cpp:7:16:7:17 | p2 | test.cpp:20:2:20:3 | p2 | +| test.cpp:7:21:7:22 | p3 | test.cpp:22:2:22:3 | p3 | +| test.cpp:7:21:7:22 | p3 | test.cpp:23:9:23:10 | p3 | +| test.cpp:7:21:7:22 | p3 | test.cpp:24:2:24:3 | p3 | +| test.cpp:7:21:7:22 | p3 | test.cpp:25:9:25:10 | p3 | +| test.cpp:8:12:8:14 | pp1 | test.cpp:23:2:23:4 | pp1 | +| test.cpp:8:19:8:21 | pp2 | test.cpp:25:2:25:4 | pp2 | +| test.cpp:10:19:10:19 | v | test.cpp:2:16:2:23 | MyStruct | +| test.cpp:14:6:14:9 | cond | test.cpp:14:6:14:9 | (bool)... | +| test.cpp:16:9:16:9 | a | test.cpp:16:8:16:9 | & ... | +| test.cpp:18:9:18:9 | b | test.cpp:18:8:18:9 | & ... | +| test.cpp:22:8:22:8 | c | test.cpp:22:7:22:8 | & ... | +| test.cpp:23:9:23:10 | p3 | test.cpp:23:8:23:10 | & ... | +| test.cpp:24:8:24:8 | d | test.cpp:24:7:24:8 | & ... | +| test.cpp:25:9:25:10 | p3 | test.cpp:25:8:25:10 | & ... | diff --git a/cpp/ql/test/library-tests/pointsto/basic/flow.ql b/cpp/ql/test/library-tests/pointsto/basic/flow.ql new file mode 100644 index 000000000000..b8120b6fe0ab --- /dev/null +++ b/cpp/ql/test/library-tests/pointsto/basic/flow.ql @@ -0,0 +1,5 @@ +import semmle.code.cpp.pointsto.PointsTo + +from Element src, Element dest +where flow(src, dest) +select src, dest diff --git a/cpp/ql/test/library-tests/pointsto/basic/lvalue.expected b/cpp/ql/test/library-tests/pointsto/basic/lvalue.expected new file mode 100644 index 000000000000..fec02a0db0d3 --- /dev/null +++ b/cpp/ql/test/library-tests/pointsto/basic/lvalue.expected @@ -0,0 +1,13 @@ +| test.cpp:16:3:16:4 | p1 | +| test.cpp:16:9:16:9 | a | +| test.cpp:18:3:18:4 | p1 | +| test.cpp:18:9:18:9 | b | +| test.cpp:20:2:20:3 | p2 | +| test.cpp:22:2:22:3 | p3 | +| test.cpp:22:8:22:8 | c | +| test.cpp:23:2:23:4 | pp1 | +| test.cpp:23:9:23:10 | p3 | +| test.cpp:24:2:24:3 | p3 | +| test.cpp:24:8:24:8 | d | +| test.cpp:25:2:25:4 | pp2 | +| test.cpp:25:9:25:10 | p3 | diff --git a/cpp/ql/test/library-tests/pointsto/basic/lvalue.ql b/cpp/ql/test/library-tests/pointsto/basic/lvalue.ql new file mode 100644 index 000000000000..8e3321e08ae3 --- /dev/null +++ b/cpp/ql/test/library-tests/pointsto/basic/lvalue.ql @@ -0,0 +1,5 @@ +import semmle.code.cpp.pointsto.PointsTo + +from Element e +where lvalue(e) +select e diff --git a/cpp/ql/test/library-tests/pointsto/basic/pointer.expected b/cpp/ql/test/library-tests/pointsto/basic/pointer.expected new file mode 100644 index 000000000000..89b2e5b68643 --- /dev/null +++ b/cpp/ql/test/library-tests/pointsto/basic/pointer.expected @@ -0,0 +1,25 @@ +| test.cpp:6:10:6:10 | a | test.cpp:27:6:27:6 | a | +| test.cpp:6:13:6:13 | b | test.cpp:27:9:27:9 | b | +| test.cpp:6:16:6:16 | c | test.cpp:27:12:27:12 | c | +| test.cpp:6:19:6:19 | d | test.cpp:27:15:27:15 | d | +| test.cpp:7:11:7:12 | p1 | test.cpp:20:7:20:8 | p1 | +| test.cpp:7:11:7:12 | p1 | test.cpp:27:18:27:19 | p1 | +| test.cpp:7:16:7:17 | p2 | test.cpp:27:22:27:23 | p2 | +| test.cpp:7:21:7:22 | p3 | test.cpp:27:26:27:27 | p3 | +| test.cpp:8:12:8:14 | pp1 | test.cpp:27:30:27:32 | pp1 | +| test.cpp:8:19:8:21 | pp2 | test.cpp:27:35:27:37 | pp2 | +| test.cpp:12:15:12:18 | cond | test.cpp:14:6:14:9 | cond | +| test.cpp:16:3:16:4 | p1 | test.cpp:16:3:16:9 | ... = ... | +| test.cpp:16:3:16:4 | p1 | test.cpp:16:8:16:9 | & ... | +| test.cpp:18:3:18:4 | p1 | test.cpp:18:3:18:9 | ... = ... | +| test.cpp:18:3:18:4 | p1 | test.cpp:18:8:18:9 | & ... | +| test.cpp:20:2:20:3 | p2 | test.cpp:20:2:20:8 | ... = ... | +| test.cpp:20:2:20:3 | p2 | test.cpp:20:7:20:8 | p1 | +| test.cpp:22:2:22:3 | p3 | test.cpp:22:2:22:8 | ... = ... | +| test.cpp:22:2:22:3 | p3 | test.cpp:22:7:22:8 | & ... | +| test.cpp:23:2:23:4 | pp1 | test.cpp:23:2:23:10 | ... = ... | +| test.cpp:23:2:23:4 | pp1 | test.cpp:23:8:23:10 | & ... | +| test.cpp:24:2:24:3 | p3 | test.cpp:24:2:24:8 | ... = ... | +| test.cpp:24:2:24:3 | p3 | test.cpp:24:7:24:8 | & ... | +| test.cpp:25:2:25:4 | pp2 | test.cpp:25:2:25:10 | ... = ... | +| test.cpp:25:2:25:4 | pp2 | test.cpp:25:8:25:10 | & ... | diff --git a/cpp/ql/test/library-tests/pointsto/basic/pointer.ql b/cpp/ql/test/library-tests/pointsto/basic/pointer.ql new file mode 100644 index 000000000000..e830eb1d9d10 --- /dev/null +++ b/cpp/ql/test/library-tests/pointsto/basic/pointer.ql @@ -0,0 +1,5 @@ +import semmle.code.cpp.pointsto.PointsTo + +from Element src, Element dest +where pointer(src, dest) +select src, dest diff --git a/cpp/ql/test/library-tests/pointsto/basic/sets.expected b/cpp/ql/test/library-tests/pointsto/basic/sets.expected new file mode 100644 index 000000000000..e8f6b7169dad --- /dev/null +++ b/cpp/ql/test/library-tests/pointsto/basic/sets.expected @@ -0,0 +1,169 @@ +| pointsto | false | 0 | 0 | set: {gp_offset} | +| pointsto | false | 1 | 1 | set: {fp_offset} | +| pointsto | false | 2 | 2 | set: {overflow_arg_area} | +| pointsto | false | 3 | 3 | set: {reg_save_area} | +| pointsto | false | 4 | 4 | set: {operator=} | +| pointsto | false | 5 | 5 | set: {operator=} | +| pointsto | false | 6 | 6 | set: {MyStruct} | +| pointsto | false | 7 | 7 | set: {data} | +| pointsto | false | 8 | 8 | set: {operator=} | +| pointsto | false | 9 | 9 | set: {p#0} | +| pointsto | false | 10 | 10 | set: {operator=} | +| pointsto | false | 11 | 11 | set: {p#0} | +| pointsto | false | 12 | 12 | set: {(unsigned long)..., 10} | +| pointsto | false | 13 | 13 | set: {use} | +| pointsto | false | 14 | 14 | set: {v} | +| pointsto | false | 15 | 15 | set: {test} | +| pointsto | false | 16 | 16 | set: {cond} | +| pointsto | false | 17 | 17 | set: {(bool)..., cond} | +| pointsto | false | 18 | 18 | set: {p1, p1, p1} | +| pointsto | false | 19 | 19 | set: {a, a} | +| pointsto | false | 20 | 20 | set: {b, b} | +| pointsto | false | 21 | 21 | set: {p2, p2} | +| pointsto | false | 22 | 22 | set: {& ..., & ..., ... = ..., ... = ..., ... = ..., p1, p1, p2} | +| pointsto | false | 23 | 23 | set: {c, c} | +| pointsto | false | 24 | 24 | set: {pp1, pp1} | +| pointsto | false | 25 | 25 | set: {d, d} | +| pointsto | false | 26 | 26 | set: {pp2, pp2} | +| pointsto | false | 27 | 27 | set: {a, b, c, d} | +| pointsto | false | 28 | 28 | set: {& ..., & ..., ... = ..., ... = ..., p3} | +| pointsto | false | 29 | 29 | set: {& ..., & ..., ... = ..., ... = ..., p3, p3, p3, p3, p3, pp1, pp2} | +| pointsto | false | 79 | 79 | file://:0:0:0:0\ngp_offset | +| pointsto | false | 80 | 80 | file://:0:0:0:0\nfp_offset | +| pointsto | false | 81 | 81 | file://:0:0:0:0\noverflow_arg_area | +| pointsto | false | 82 | 82 | file://:0:0:0:0\nreg_save_area | +| pointsto | false | 83 | 83 | \noperator= | +| pointsto | false | 85 | 85 | \noperator= | +| pointsto | false | 88 | 88 | test.cpp:2:16:2:23\nMyStruct | +| pointsto | false | 89 | 89 | test.cpp:3:6:3:9\ndata | +| pointsto | false | 91 | 91 | test.cpp:2:16:2:16\noperator= | +| pointsto | false | 92 | 92 | file://:0:0:0:0\np#0 | +| pointsto | false | 95 | 95 | test.cpp:2:16:2:16\noperator= | +| pointsto | false | 96 | 96 | file://:0:0:0:0\np#0 | +| pointsto | false | 99 | 99 | test.cpp:3:11:3:12\n10 | +| pointsto | false | 100 | 100 | test.cpp:3:11:3:12\n(unsigned long)... | +| pointsto | false | 104 | 104 | test.cpp:6:10:6:10\na | +| pointsto | false | 106 | 106 | test.cpp:6:13:6:13\nb | +| pointsto | false | 108 | 108 | test.cpp:6:16:6:16\nc | +| pointsto | false | 110 | 110 | test.cpp:6:19:6:19\nd | +| pointsto | false | 112 | 112 | test.cpp:7:11:7:12\np1 | +| pointsto | false | 116 | 116 | test.cpp:7:16:7:17\np2 | +| pointsto | false | 118 | 118 | test.cpp:7:21:7:22\np3 | +| pointsto | false | 120 | 120 | test.cpp:8:12:8:14\npp1 | +| pointsto | false | 124 | 124 | test.cpp:8:19:8:21\npp2 | +| pointsto | false | 126 | 126 | test.cpp:10:6:10:8\nuse | +| pointsto | false | 127 | 127 | test.cpp:10:19:10:19\nv | +| pointsto | false | 130 | 130 | test.cpp:12:6:12:9\ntest | +| pointsto | false | 131 | 131 | test.cpp:12:15:12:18\ncond | +| pointsto | false | 132 | 132 | test.cpp:14:6:14:9\ncond | +| pointsto | false | 133 | 133 | test.cpp:14:6:14:9\n(bool)... | +| pointsto | false | 134 | 134 | test.cpp:16:3:16:4\np1 | +| pointsto | false | 135 | 135 | test.cpp:16:9:16:9\na | +| pointsto | false | 136 | 136 | test.cpp:16:8:16:9\n& ... | +| pointsto | false | 137 | 137 | test.cpp:16:3:16:9\n... = ... | +| pointsto | false | 140 | 140 | test.cpp:18:3:18:4\np1 | +| pointsto | false | 141 | 141 | test.cpp:18:9:18:9\nb | +| pointsto | false | 142 | 142 | test.cpp:18:8:18:9\n& ... | +| pointsto | false | 143 | 143 | test.cpp:18:3:18:9\n... = ... | +| pointsto | false | 148 | 148 | test.cpp:20:2:20:3\np2 | +| pointsto | false | 149 | 149 | test.cpp:20:7:20:8\np1 | +| pointsto | false | 150 | 150 | test.cpp:20:2:20:8\n... = ... | +| pointsto | false | 152 | 152 | test.cpp:22:2:22:3\np3 | +| pointsto | false | 153 | 153 | test.cpp:22:8:22:8\nc | +| pointsto | false | 154 | 154 | test.cpp:22:7:22:8\n& ... | +| pointsto | false | 155 | 155 | test.cpp:22:2:22:8\n... = ... | +| pointsto | false | 157 | 157 | test.cpp:23:2:23:4\npp1 | +| pointsto | false | 158 | 158 | test.cpp:23:9:23:10\np3 | +| pointsto | false | 159 | 159 | test.cpp:23:8:23:10\n& ... | +| pointsto | false | 160 | 160 | test.cpp:23:2:23:10\n... = ... | +| pointsto | false | 162 | 162 | test.cpp:24:2:24:3\np3 | +| pointsto | false | 163 | 163 | test.cpp:24:8:24:8\nd | +| pointsto | false | 164 | 164 | test.cpp:24:7:24:8\n& ... | +| pointsto | false | 165 | 165 | test.cpp:24:2:24:8\n... = ... | +| pointsto | false | 168 | 168 | test.cpp:25:2:25:4\npp2 | +| pointsto | false | 169 | 169 | test.cpp:25:9:25:10\np3 | +| pointsto | false | 170 | 170 | test.cpp:25:8:25:10\n& ... | +| pointsto | false | 171 | 171 | test.cpp:25:2:25:10\n... = ... | +| pointsto | false | 176 | 176 | test.cpp:27:6:27:6\na | +| pointsto | false | 177 | 177 | test.cpp:27:9:27:9\nb | +| pointsto | false | 178 | 178 | test.cpp:27:12:27:12\nc | +| pointsto | false | 179 | 179 | test.cpp:27:15:27:15\nd | +| pointsto | false | 180 | 180 | test.cpp:27:18:27:19\np1 | +| pointsto | false | 181 | 181 | test.cpp:27:22:27:23\np2 | +| pointsto | false | 182 | 182 | test.cpp:27:26:27:27\np3 | +| pointsto | false | 183 | 183 | test.cpp:27:30:27:32\npp1 | +| pointsto | false | 184 | 184 | test.cpp:27:35:27:37\npp2 | +| pointsto | true | 0 | 79 | pt: {gp_offset} -> gp_offset | +| pointsto | true | 1 | 80 | pt: {fp_offset} -> fp_offset | +| pointsto | true | 2 | 81 | pt: {overflow_arg_area} -> overflow_arg_area | +| pointsto | true | 3 | 82 | pt: {reg_save_area} -> reg_save_area | +| pointsto | true | 4 | 83 | pt: {operator=} -> operator= | +| pointsto | true | 5 | 85 | pt: {operator=} -> operator= | +| pointsto | true | 6 | 88 | pt: {MyStruct} -> MyStruct | +| pointsto | true | 7 | 89 | pt: {data} -> data | +| pointsto | true | 8 | 91 | pt: {operator=} -> operator= | +| pointsto | true | 9 | 92 | pt: {p#0} -> p#0 | +| pointsto | true | 10 | 95 | pt: {operator=} -> operator= | +| pointsto | true | 11 | 96 | pt: {p#0} -> p#0 | +| pointsto | true | 12 | 99 | pt: {(unsigned long)..., 10} -> 10 | +| pointsto | true | 12 | 100 | pt: {(unsigned long)..., 10} -> (unsigned long)... | +| pointsto | true | 13 | 126 | pt: {use} -> use | +| pointsto | true | 14 | 6 | sf | +| pointsto | true | 14 | 127 | pt: {v} -> v | +| pointsto | true | 15 | 130 | pt: {test} -> test | +| pointsto | true | 16 | 131 | pt: {cond} -> cond | +| pointsto | true | 17 | 132 | pt: {(bool)..., cond} -> cond | +| pointsto | true | 17 | 133 | pt: {(bool)..., cond} -> (bool)... | +| pointsto | true | 18 | 112 | pt: {p1, p1, p1} -> p1 | +| pointsto | true | 18 | 134 | pt: {p1, p1, p1} -> p1 | +| pointsto | true | 18 | 140 | pt: {p1, p1, p1} -> p1 | +| pointsto | true | 19 | 6 | sf | +| pointsto | true | 19 | 22 | sf | +| pointsto | true | 19 | 104 | pt: {a, a} -> a | +| pointsto | true | 19 | 135 | pt: {a, a} -> a | +| pointsto | true | 20 | 6 | sf | +| pointsto | true | 20 | 22 | sf | +| pointsto | true | 20 | 106 | pt: {b, b} -> b | +| pointsto | true | 20 | 141 | pt: {b, b} -> b | +| pointsto | true | 21 | 116 | pt: {p2, p2} -> p2 | +| pointsto | true | 21 | 148 | pt: {p2, p2} -> p2 | +| pointsto | true | 22 | 136 | pt: {& ..., & ..., ... = ..., ... = ..., ... = ..., p1, p1, p2} -> & ... | +| pointsto | true | 22 | 137 | pt: {& ..., & ..., ... = ..., ... = ..., ... = ..., p1, p1, p2} -> ... = ... | +| pointsto | true | 22 | 142 | pt: {& ..., & ..., ... = ..., ... = ..., ... = ..., p1, p1, p2} -> & ... | +| pointsto | true | 22 | 143 | pt: {& ..., & ..., ... = ..., ... = ..., ... = ..., p1, p1, p2} -> ... = ... | +| pointsto | true | 22 | 149 | pt: {& ..., & ..., ... = ..., ... = ..., ... = ..., p1, p1, p2} -> p1 | +| pointsto | true | 22 | 150 | pt: {& ..., & ..., ... = ..., ... = ..., ... = ..., p1, p1, p2} -> ... = ... | +| pointsto | true | 22 | 180 | pt: {& ..., & ..., ... = ..., ... = ..., ... = ..., p1, p1, p2} -> p1 | +| pointsto | true | 22 | 181 | pt: {& ..., & ..., ... = ..., ... = ..., ... = ..., p1, p1, p2} -> p2 | +| pointsto | true | 23 | 6 | sf | +| pointsto | true | 23 | 28 | sf | +| pointsto | true | 23 | 108 | pt: {c, c} -> c | +| pointsto | true | 23 | 153 | pt: {c, c} -> c | +| pointsto | true | 24 | 120 | pt: {pp1, pp1} -> pp1 | +| pointsto | true | 24 | 157 | pt: {pp1, pp1} -> pp1 | +| pointsto | true | 25 | 6 | sf | +| pointsto | true | 25 | 28 | sf | +| pointsto | true | 25 | 110 | pt: {d, d} -> d | +| pointsto | true | 25 | 163 | pt: {d, d} -> d | +| pointsto | true | 26 | 124 | pt: {pp2, pp2} -> pp2 | +| pointsto | true | 26 | 168 | pt: {pp2, pp2} -> pp2 | +| pointsto | true | 27 | 176 | pt: {a, b, c, d} -> a | +| pointsto | true | 27 | 177 | pt: {a, b, c, d} -> b | +| pointsto | true | 27 | 178 | pt: {a, b, c, d} -> c | +| pointsto | true | 27 | 179 | pt: {a, b, c, d} -> d | +| pointsto | true | 28 | 154 | pt: {& ..., & ..., ... = ..., ... = ..., p3} -> & ... | +| pointsto | true | 28 | 155 | pt: {& ..., & ..., ... = ..., ... = ..., p3} -> ... = ... | +| pointsto | true | 28 | 164 | pt: {& ..., & ..., ... = ..., ... = ..., p3} -> & ... | +| pointsto | true | 28 | 165 | pt: {& ..., & ..., ... = ..., ... = ..., p3} -> ... = ... | +| pointsto | true | 28 | 182 | pt: {& ..., & ..., ... = ..., ... = ..., p3} -> p3 | +| pointsto | true | 29 | 118 | pt: {& ..., & ..., ... = ..., ... = ..., p3, p3, p3, p3, p3, pp1, pp2} -> p3 | +| pointsto | true | 29 | 152 | pt: {& ..., & ..., ... = ..., ... = ..., p3, p3, p3, p3, p3, pp1, pp2} -> p3 | +| pointsto | true | 29 | 158 | pt: {& ..., & ..., ... = ..., ... = ..., p3, p3, p3, p3, p3, pp1, pp2} -> p3 | +| pointsto | true | 29 | 159 | pt: {& ..., & ..., ... = ..., ... = ..., p3, p3, p3, p3, p3, pp1, pp2} -> & ... | +| pointsto | true | 29 | 160 | pt: {& ..., & ..., ... = ..., ... = ..., p3, p3, p3, p3, p3, pp1, pp2} -> ... = ... | +| pointsto | true | 29 | 162 | pt: {& ..., & ..., ... = ..., ... = ..., p3, p3, p3, p3, p3, pp1, pp2} -> p3 | +| pointsto | true | 29 | 169 | pt: {& ..., & ..., ... = ..., ... = ..., p3, p3, p3, p3, p3, pp1, pp2} -> p3 | +| pointsto | true | 29 | 170 | pt: {& ..., & ..., ... = ..., ... = ..., p3, p3, p3, p3, p3, pp1, pp2} -> & ... | +| pointsto | true | 29 | 171 | pt: {& ..., & ..., ... = ..., ... = ..., p3, p3, p3, p3, p3, pp1, pp2} -> ... = ... | +| pointsto | true | 29 | 183 | pt: {& ..., & ..., ... = ..., ... = ..., p3, p3, p3, p3, p3, pp1, pp2} -> pp1 | +| pointsto | true | 29 | 184 | pt: {& ..., & ..., ... = ..., ... = ..., p3, p3, p3, p3, p3, pp1, pp2} -> pp2 | diff --git a/cpp/ql/test/library-tests/pointsto/basic/sets.ql b/cpp/ql/test/library-tests/pointsto/basic/sets.ql new file mode 100644 index 000000000000..6942cc9c3123 --- /dev/null +++ b/cpp/ql/test/library-tests/pointsto/basic/sets.ql @@ -0,0 +1,71 @@ +// query-type: graph +import cpp +import semmle.code.cpp.pointsto.PointsTo + +newtype TElementOrSet = MkElement(Element e) or MkSet(int i) { pointstosets(i, _) } + +class ElementOrSet extends TElementOrSet { + int asSet() { + this = MkSet(result) + } + + Element asElement() { + this = MkElement(result) + } + + string toString() { + result = any(Element e | this = MkElement(e)).toString() or + result = any(int set | this = MkSet(set)).toString() + } +} + +predicate isSetFlowEnd(boolean isEdge, int x, int y, string label) { + (setflow(x, _) or setflow(_, x)) + and isEdge = false + and x = y + and label = "set: {" + concat(Element e | pointstosets(x, e) | e.toString(), ", ") + "}" +} + +predicate isSetFlow(boolean isEdge, int x, int y, string label) { + isEdge = true + and setflow(x, y) + and label = "sf" +} + +predicate isPointsToSetSrc(boolean isEdge, int x, int y, string label) { + pointstosets(x, _) + and isEdge = false + and x = y + and label = "set: {" + concat(Element e | pointstosets(x, e) | e.toString(), ", ") + "}" +} + +predicate isPointsToSetDest(boolean isEdge, Element x, Element y, string label) { + exists(string loc, string name | + pointstosets(_, x) + and isEdge = false + and x = y + and (if exists(x.getLocation().toString()) + then loc = x.getLocation().toString() + else loc = "") + and (if exists(x.toString()) + then name = x.toString() + else name = "") + and label = loc + "\n" + name) +} + +predicate isPointsToSets(boolean isEdge, int x, Element y, string label) { + isEdge = true + and pointstosets(x, y) + and label = "pt: {" + + concat(Element e | pointstosets(x, e) | e.toString(), ", ") + + "} -> " + y.toString() +} + +from boolean isEdge, ElementOrSet x, ElementOrSet y, string label +where isSetFlowEnd(isEdge, x.asSet(), y.asSet(), label) + or isSetFlow(isEdge, x.asSet(), y.asSet(), label) + or isPointsToSetSrc(isEdge, x.asSet(), y.asSet(), label) + or isPointsToSetDest(isEdge, x.asElement(), y.asElement(), label) + or isPointsToSets(isEdge, x.asSet(), y.asElement(), label) +select "pointsto", isEdge, x, y, label + diff --git a/cpp/ql/test/library-tests/pointsto/basic/test.cpp b/cpp/ql/test/library-tests/pointsto/basic/test.cpp new file mode 100644 index 000000000000..f1b6f1dc35bf --- /dev/null +++ b/cpp/ql/test/library-tests/pointsto/basic/test.cpp @@ -0,0 +1,28 @@ + +typedef struct { + int data[10]; +} MyStruct; + +MyStruct a, b, c, d; +MyStruct *p1, *p2, *p3; +MyStruct **pp1, **pp2; + +void use(MyStruct v, ...); + +void test(int cond) +{ + if (cond) + { + p1 = &a; + } else { + p1 = &b; + } + p2 = p1; // p1, p2 could point to a or b + + p3 = &c; + pp1 = &p3; + p3 = &d; + pp2 = &p3; // pp1, pp2 point to p3; p3 could point to c or d (at different times) + + use(a, b, c, d, p1, p2, p3, pp1, pp2); +} diff --git a/cpp/ql/test/library-tests/pointsto/more/PointsToExpr.expected b/cpp/ql/test/library-tests/pointsto/more/PointsToExpr.expected new file mode 100644 index 000000000000..4d536f7697ca --- /dev/null +++ b/cpp/ql/test/library-tests/pointsto/more/PointsToExpr.expected @@ -0,0 +1,8 @@ +| aggregate_init.c:14:11:14:13 | ptr | aggregate_init.c:10:6:10:6 | x | +| aggregate_init.c:14:11:14:13 | ptr | aggregate_init.c:10:9:10:9 | y | +| classes.cpp:20:10:20:13 | ptr1 | classes.cpp:34:6:34:6 | x | +| classes.cpp:20:10:20:13 | ptr1 | classes.cpp:34:12:34:12 | z | +| classes.cpp:25:10:25:13 | ptr2 | classes.cpp:34:9:34:9 | y | +| classes.cpp:66:15:66:17 | ptr | classes.cpp:61:6:61:6 | x | +| structs.c:18:11:18:13 | ptr | structs.c:10:6:10:6 | x | +| structs.c:18:11:18:13 | ptr | structs.c:10:9:10:9 | y | diff --git a/cpp/ql/test/library-tests/pointsto/more/PointsToExpr.ql b/cpp/ql/test/library-tests/pointsto/more/PointsToExpr.ql new file mode 100644 index 000000000000..48d7c95a0549 --- /dev/null +++ b/cpp/ql/test/library-tests/pointsto/more/PointsToExpr.ql @@ -0,0 +1,13 @@ +import cpp +import semmle.code.cpp.pointsto.PointsTo + +class ReturnPT extends PointsToExpr +{ + override predicate interesting() { + this instanceof VariableAccess and + this.getParent() instanceof ReturnStmt + } +} + +from ReturnPT return +select return, return.pointsTo() diff --git a/cpp/ql/test/library-tests/pointsto/more/aggregate_init.c b/cpp/ql/test/library-tests/pointsto/more/aggregate_init.c new file mode 100644 index 000000000000..51acc1132905 --- /dev/null +++ b/cpp/ql/test/library-tests/pointsto/more/aggregate_init.c @@ -0,0 +1,15 @@ +// pointsto on aggregates + +typedef struct +{ + int *ptr; +} myStruct1; + +int *myFunction1() +{ + int x, y; + myStruct1 a = { .ptr = &x }; + myStruct1 b = { .ptr = &y }; + + return a.ptr; // &x [FALSE POSITIVE: y] +} diff --git a/cpp/ql/test/library-tests/pointsto/more/classes.cpp b/cpp/ql/test/library-tests/pointsto/more/classes.cpp new file mode 100644 index 000000000000..95c6616ac83e --- /dev/null +++ b/cpp/ql/test/library-tests/pointsto/more/classes.cpp @@ -0,0 +1,67 @@ +// pointsto on classes + +class myClass +{ +public: + myClass() : ptr1(0), ptr2(0) {}; + + void set1(int *ptr) + { + ptr1 = ptr; + } + + void set2(int *ptr) + { + ptr2 = ptr; + } + + int *get1() + { + return ptr1; // &x, &z + } + + int *get2() + { + return ptr2; // &y + } + +private: + int *ptr1, *ptr2; +}; + +int *myFunction() +{ + int x, y, z; + myClass a, b; + + a.set1(&x); + a.set2(&y); + b.set1(&z); + + return a.get1(); // [EXPECTED: x] +} + +// --- + +struct myStruct2 +{ + int *ptr; +}; + +class myClass2 +{ +public: + myClass2(myStruct2 _s) : s(_s) {}; + + myStruct2 s; +}; + +int *myFunction2() +{ + int x; + myStruct2 s2; + s2.ptr = &x; + myClass2 mc2(s2); + + return mc2.s.ptr; // &x +} diff --git a/cpp/ql/test/library-tests/pointsto/more/structs.c b/cpp/ql/test/library-tests/pointsto/more/structs.c new file mode 100644 index 000000000000..129f5d5f19c8 --- /dev/null +++ b/cpp/ql/test/library-tests/pointsto/more/structs.c @@ -0,0 +1,19 @@ +// pointsto on structs + +typedef struct +{ + int *ptr; +} myStruct; + +int *myFunction() +{ + int x, y, z; + myStruct a, b; + int *zp; + + a.ptr = &x; + b.ptr = &y; + zp = &z; + + return a.ptr; // &x [FALSE POSITIVE: y] +} diff --git a/cpp/ql/test/library-tests/pointsto/new-virtual/PointsToNew.expected b/cpp/ql/test/library-tests/pointsto/new-virtual/PointsToNew.expected new file mode 100644 index 000000000000..c7d5a5021be7 --- /dev/null +++ b/cpp/ql/test/library-tests/pointsto/new-virtual/PointsToNew.expected @@ -0,0 +1,13 @@ +| test.cpp:7:3:7:6 | * ... | test.cpp:25:6:25:6 | i | +| test.cpp:7:4:7:6 | ptr | test.cpp:25:6:25:6 | i | +| test.cpp:17:3:17:6 | * ... | test.cpp:26:6:26:6 | j | +| test.cpp:17:4:17:6 | ptr | test.cpp:26:6:26:6 | j | +| test.cpp:23:22:23:38 | new | test.cpp:23:22:23:38 | new | +| test.cpp:24:22:24:34 | (MyClassBase *)... | test.cpp:24:22:24:34 | new | +| test.cpp:24:22:24:34 | new | test.cpp:24:22:24:34 | new | +| test.cpp:28:2:28:5 | ptr1 | test.cpp:23:22:23:38 | new | +| test.cpp:28:15:28:16 | & ... | test.cpp:25:6:25:6 | i | +| test.cpp:28:16:28:16 | i | test.cpp:25:6:25:6 | i | +| test.cpp:29:2:29:5 | ptr2 | test.cpp:24:22:24:34 | new | +| test.cpp:29:15:29:16 | & ... | test.cpp:26:6:26:6 | j | +| test.cpp:29:16:29:16 | j | test.cpp:26:6:26:6 | j | diff --git a/cpp/ql/test/library-tests/pointsto/new-virtual/PointsToNew.ql b/cpp/ql/test/library-tests/pointsto/new-virtual/PointsToNew.ql new file mode 100644 index 000000000000..fda36b8d34b8 --- /dev/null +++ b/cpp/ql/test/library-tests/pointsto/new-virtual/PointsToNew.ql @@ -0,0 +1,13 @@ +import cpp +import semmle.code.cpp.pointsto.PointsTo + +class MyPointsTo extends PointsToExpr +{ + override predicate interesting() { + any() + } +} + +from MyPointsTo new, Element loc +where new.pointsTo() = loc +select new, loc diff --git a/cpp/ql/test/library-tests/pointsto/new-virtual/test.cpp b/cpp/ql/test/library-tests/pointsto/new-virtual/test.cpp new file mode 100644 index 000000000000..28c43ae263a3 --- /dev/null +++ b/cpp/ql/test/library-tests/pointsto/new-virtual/test.cpp @@ -0,0 +1,30 @@ + +class MyClassBase +{ +public: + virtual void method(int *ptr) + { + *ptr = 1; + } + + int a; +}; + +class MyClass : public MyClassBase +{ + virtual void method(int *ptr) + { + *ptr = 2; + } +}; + +void myFunction() +{ + MyClassBase *ptr1 = new MyClassBase(); + MyClassBase *ptr2 = new MyClass(); + int i; + int j; + + ptr1->method(&i); + ptr2->method(&j); +} diff --git a/cpp/ql/test/library-tests/predefines/expr.expected b/cpp/ql/test/library-tests/predefines/expr.expected new file mode 100644 index 000000000000..5e255af3f927 --- /dev/null +++ b/cpp/ql/test/library-tests/predefines/expr.expected @@ -0,0 +1,3 @@ +| predefines.c:3:23:3:41 | __PRETTY_FUNCTION__ | +| predefines.c:3:23:3:41 | array to pointer conversion | +| predefines.c:3:23:3:41 | fun | diff --git a/cpp/ql/test/library-tests/predefines/expr.ql b/cpp/ql/test/library-tests/predefines/expr.ql new file mode 100644 index 000000000000..3c2a3269e72e --- /dev/null +++ b/cpp/ql/test/library-tests/predefines/expr.ql @@ -0,0 +1,5 @@ +import cpp + +from Expr e +select e + diff --git a/cpp/ql/test/library-tests/predefines/localVariables.expected b/cpp/ql/test/library-tests/predefines/localVariables.expected new file mode 100644 index 000000000000..cc5ed256a629 --- /dev/null +++ b/cpp/ql/test/library-tests/predefines/localVariables.expected @@ -0,0 +1,2 @@ +| predefines.c:3:17:3:19 | str | +| predefines.c:3:23:3:41 | __PRETTY_FUNCTION__ | diff --git a/cpp/ql/test/library-tests/predefines/localVariables.ql b/cpp/ql/test/library-tests/predefines/localVariables.ql new file mode 100644 index 000000000000..a38e27f2e531 --- /dev/null +++ b/cpp/ql/test/library-tests/predefines/localVariables.ql @@ -0,0 +1,5 @@ +import cpp + +from LocalVariable lv +select lv + diff --git a/cpp/ql/test/library-tests/predefines/predefines.c b/cpp/ql/test/library-tests/predefines/predefines.c new file mode 100644 index 000000000000..3fa7e5746c7b --- /dev/null +++ b/cpp/ql/test/library-tests/predefines/predefines.c @@ -0,0 +1,5 @@ + +void fun(void) { + const char *str = __PRETTY_FUNCTION__; +} + diff --git a/cpp/ql/test/library-tests/preprocessor/dependent_defs/a.c b/cpp/ql/test/library-tests/preprocessor/dependent_defs/a.c new file mode 100644 index 000000000000..cf79084c2d28 --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/dependent_defs/a.c @@ -0,0 +1,3 @@ +#define MYTYPE char + +#include "h.h" diff --git a/cpp/ql/test/library-tests/preprocessor/dependent_defs/b.c b/cpp/ql/test/library-tests/preprocessor/dependent_defs/b.c new file mode 100644 index 000000000000..ba3ddf7a0d57 --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/dependent_defs/b.c @@ -0,0 +1,3 @@ +#define MYTYPE int + +#include "h.h" diff --git a/cpp/ql/test/library-tests/preprocessor/dependent_defs/block.expected b/cpp/ql/test/library-tests/preprocessor/dependent_defs/block.expected new file mode 100644 index 000000000000..9cb34403c746 --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/dependent_defs/block.expected @@ -0,0 +1,4 @@ +| h.h:5:15:5:19 | myfun | h.h:5:23:5:35 | { ... } | 2 | +| h.h:5:15:5:19 | myfun | h.h:5:23:5:35 | { ... } | 2 | +| h.h:7:12:7:17 | intfun | h.h:7:21:10:1 | { ... } | 2 | +| h.h:7:12:7:17 | intfun | h.h:7:21:10:1 | { ... } | 2 | diff --git a/cpp/ql/test/library-tests/preprocessor/dependent_defs/block.ql b/cpp/ql/test/library-tests/preprocessor/dependent_defs/block.ql new file mode 100644 index 000000000000..574d0c02e38b --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/dependent_defs/block.ql @@ -0,0 +1,9 @@ +import cpp + +// A function might have more than one block if it's used in more than one +// preprocessor context -- even if the preprocessor context does not affect the +// blocks. + +from Function f +select f, f.getBlock(), + count(f.getBlock()) diff --git a/cpp/ql/test/library-tests/preprocessor/dependent_defs/getType.expected b/cpp/ql/test/library-tests/preprocessor/dependent_defs/getType.expected new file mode 100644 index 000000000000..17eb7291e950 --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/dependent_defs/getType.expected @@ -0,0 +1,10 @@ +| h.h:3:16:3:23 | mytype_t | file://:0:0:0:0 | char | 1 | +| h.h:3:16:3:23 | mytype_t | file://:0:0:0:0 | int | 1 | +| h.h:5:15:5:19 | myfun | file://:0:0:0:0 | char | 2 | +| h.h:5:15:5:19 | myfun | file://:0:0:0:0 | int | 2 | +| h.h:7:12:7:17 | intfun | file://:0:0:0:0 | int | 1 | +| h.h:8:10:8:17 | localVar | file://:0:0:0:0 | char | 1 | +| h.h:8:10:8:17 | localVar | file://:0:0:0:0 | int | 1 | +| h.h:12:15:12:19 | myvar | file://:0:0:0:0 | char | 1 | +| h.h:12:15:12:19 | myvar | file://:0:0:0:0 | int | 1 | +| h.h:16:12:16:17 | intvar | file://:0:0:0:0 | int | 1 | diff --git a/cpp/ql/test/library-tests/preprocessor/dependent_defs/getType.ql b/cpp/ql/test/library-tests/preprocessor/dependent_defs/getType.ql new file mode 100644 index 000000000000..d840fab0cf41 --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/dependent_defs/getType.ql @@ -0,0 +1,15 @@ +import cpp + +Type getType(Element e) { + // A function may get more than one return type if it's declared in different + // preprocessor contexts. + result = e.(Function).getType() + or + result = e.(Variable).getType() + or + result = e.(TypedefType).getBaseType() +} + +from Element e +where e.getFile().fromSource() +select e, getType(e), count(getType(e)) diff --git a/cpp/ql/test/library-tests/preprocessor/dependent_defs/h.h b/cpp/ql/test/library-tests/preprocessor/dependent_defs/h.h new file mode 100644 index 000000000000..903410835066 --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/dependent_defs/h.h @@ -0,0 +1,16 @@ +// define the preprocessor macro `MYTYPE` before including this file. + +typedef MYTYPE mytype_t; + +inline MYTYPE myfun() { return 0; } + +inline int intfun() { + MYTYPE localVar = sizeof(MYTYPE); + return localVar; +} + +extern MYTYPE myvar; + +// This creates a separate variable named `intvar` for each file that includes +// this header. That's definitely bad practice. +static int intvar = sizeof(MYTYPE); diff --git a/cpp/ql/test/library-tests/preprocessor/dependent_defs/init.expected b/cpp/ql/test/library-tests/preprocessor/dependent_defs/init.expected new file mode 100644 index 000000000000..8d097c8f8075 --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/dependent_defs/init.expected @@ -0,0 +1,4 @@ +| h.h:8:10:8:17 | localVar | 1 | 1 | +| h.h:8:10:8:17 | localVar | 4 | 1 | +| h.h:16:12:16:17 | intvar | 1 | 2 | +| h.h:16:12:16:17 | intvar | 4 | 2 | diff --git a/cpp/ql/test/library-tests/preprocessor/dependent_defs/init.ql b/cpp/ql/test/library-tests/preprocessor/dependent_defs/init.ql new file mode 100644 index 000000000000..7966a402f9be --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/dependent_defs/init.ql @@ -0,0 +1,9 @@ +import cpp + +// A variable may have more than one initializer because variables are merged +// when they have the same name and appear in the same source file, but their +// initializers are not merged. + +from Variable v +select v, v.getInitializer().getExpr().getValue(), + count(v.getInitializer().getExpr().getValue()) diff --git a/cpp/ql/test/library-tests/preprocessor/hashing/hashing/a.c b/cpp/ql/test/library-tests/preprocessor/hashing/hashing/a.c new file mode 100644 index 000000000000..6080a4ef945e --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/hashing/hashing/a.c @@ -0,0 +1,5 @@ + +#define A + +#include "h.h" + diff --git a/cpp/ql/test/library-tests/preprocessor/hashing/hashing/b.c b/cpp/ql/test/library-tests/preprocessor/hashing/hashing/b.c new file mode 100644 index 000000000000..f99fc4ca7b06 --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/hashing/hashing/b.c @@ -0,0 +1,5 @@ + +#define B + +#include "h.h" + diff --git a/cpp/ql/test/library-tests/preprocessor/hashing/hashing/h.h b/cpp/ql/test/library-tests/preprocessor/hashing/hashing/h.h new file mode 100644 index 000000000000..05b950efa1d4 --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/hashing/hashing/h.h @@ -0,0 +1,5 @@ + +#if defined A || defined B +extern int i; +#endif + diff --git a/cpp/ql/test/library-tests/preprocessor/hashing/hashing/loca.c b/cpp/ql/test/library-tests/preprocessor/hashing/hashing/loca.c new file mode 100644 index 000000000000..67ed74138a5c --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/hashing/hashing/loca.c @@ -0,0 +1,5 @@ + +typedef int off_t; +#define __off_t_defined + +#include "loch.h" diff --git a/cpp/ql/test/library-tests/preprocessor/hashing/hashing/locb.c b/cpp/ql/test/library-tests/preprocessor/hashing/hashing/locb.c new file mode 100644 index 000000000000..0013d29020d3 --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/hashing/hashing/locb.c @@ -0,0 +1,3 @@ + +#include "loch.h" + diff --git a/cpp/ql/test/library-tests/preprocessor/hashing/hashing/loch.h b/cpp/ql/test/library-tests/preprocessor/hashing/hashing/loch.h new file mode 100644 index 000000000000..c02d875bb7c9 --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/hashing/hashing/loch.h @@ -0,0 +1,7 @@ + +#include "lochx.h" + +#ifndef __off_t_defined + +#endif + diff --git a/cpp/ql/test/library-tests/preprocessor/hashing/hashing/lochx.h b/cpp/ql/test/library-tests/preprocessor/hashing/hashing/lochx.h new file mode 100644 index 000000000000..11c8f5cafa27 --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/hashing/hashing/lochx.h @@ -0,0 +1,6 @@ + +#ifndef __off_t_defined +typedef int off_t; +#define __off_t_defined +#endif + diff --git a/cpp/ql/test/library-tests/preprocessor/hashing/hashing/macroAccess.expected b/cpp/ql/test/library-tests/preprocessor/hashing/hashing/macroAccess.expected new file mode 100644 index 000000000000..7583378bcc99 --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/hashing/hashing/macroAccess.expected @@ -0,0 +1,5 @@ +| h.h:2:13:2:13 | A | a.c:2:1:2:9 | #define A | +| h.h:2:26:2:26 | B | b.c:2:1:2:9 | #define B | +| loch.h:4:9:4:23 | __off_t_defined | loca.c:3:1:3:23 | #define __off_t_defined | +| loch.h:4:9:4:23 | __off_t_defined | lochx.h:4:1:4:23 | #define __off_t_defined | +| lochx.h:2:9:2:23 | __off_t_defined | loca.c:3:1:3:23 | #define __off_t_defined | diff --git a/cpp/ql/test/library-tests/preprocessor/hashing/hashing/macroAccess.ql b/cpp/ql/test/library-tests/preprocessor/hashing/hashing/macroAccess.ql new file mode 100644 index 000000000000..92d7deb40941 --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/hashing/hashing/macroAccess.ql @@ -0,0 +1,6 @@ +import cpp + +from MacroAccess ma, Macro m +where m = ma.getMacro() +select ma, m + diff --git a/cpp/ql/test/library-tests/preprocessor/hashing/undef/a.c b/cpp/ql/test/library-tests/preprocessor/hashing/undef/a.c new file mode 100644 index 000000000000..f0e92f270008 --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/hashing/undef/a.c @@ -0,0 +1,4 @@ + +#define A +#include "h.h" + diff --git a/cpp/ql/test/library-tests/preprocessor/hashing/undef/b.c b/cpp/ql/test/library-tests/preprocessor/hashing/undef/b.c new file mode 100644 index 000000000000..197abf3c402e --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/hashing/undef/b.c @@ -0,0 +1,4 @@ + +#define B +#include "h.h" + diff --git a/cpp/ql/test/library-tests/preprocessor/hashing/undef/h.h b/cpp/ql/test/library-tests/preprocessor/hashing/undef/h.h new file mode 100644 index 000000000000..2691f638249c --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/hashing/undef/h.h @@ -0,0 +1,4 @@ + +#undef A +#undef B + diff --git a/cpp/ql/test/library-tests/preprocessor/hashing/undef/preproc.expected b/cpp/ql/test/library-tests/preprocessor/hashing/undef/preproc.expected new file mode 100644 index 000000000000..af1b3dcd8526 --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/hashing/undef/preproc.expected @@ -0,0 +1,6 @@ +| a.c:2:1:2:9 | #define A | +| a.c:3:1:3:14 | #include "h.h" | +| b.c:2:1:2:9 | #define B | +| b.c:3:1:3:14 | #include "h.h" | +| h.h:2:1:2:8 | #undef A | +| h.h:3:1:3:8 | #undef B | diff --git a/cpp/ql/test/library-tests/preprocessor/hashing/undef/preproc.ql b/cpp/ql/test/library-tests/preprocessor/hashing/undef/preproc.ql new file mode 100644 index 000000000000..3fa4c9f6dbb6 --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/hashing/undef/preproc.ql @@ -0,0 +1,5 @@ +import cpp + +from PreprocessorDirective pd +select pd + diff --git a/cpp/ql/test/library-tests/preprocessor/include/guarded.h b/cpp/ql/test/library-tests/preprocessor/include/guarded.h new file mode 100644 index 000000000000..96fb298d5c96 --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/include/guarded.h @@ -0,0 +1,8 @@ +// guarded.h + +#ifndef GUARDED_H +#define GUARDED_H + + // ... content ... + +#endif // GUARDED_H \ No newline at end of file diff --git a/cpp/ql/test/library-tests/preprocessor/include/include.expected b/cpp/ql/test/library-tests/preprocessor/include/include.expected new file mode 100644 index 000000000000..e7ff2a37de2c --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/include/include.expected @@ -0,0 +1,4 @@ +| main.cpp:3:1:3:20 | #include "guarded.h" | guarded.h:0:0:0:0 | guarded.h | +| main.cpp:4:1:4:20 | #include "guarded.h" | guarded.h:0:0:0:0 | guarded.h | +| main.cpp:5:1:5:22 | #include "unguarded.h" | unguarded.h:0:0:0:0 | unguarded.h | +| main.cpp:6:1:6:22 | #include "unguarded.h" | unguarded.h:0:0:0:0 | unguarded.h | diff --git a/cpp/ql/test/library-tests/preprocessor/include/include.ql b/cpp/ql/test/library-tests/preprocessor/include/include.ql new file mode 100644 index 000000000000..03c9663428f4 --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/include/include.ql @@ -0,0 +1,4 @@ +import cpp + +from Include i +select i, i.getIncludedFile() diff --git a/cpp/ql/test/library-tests/preprocessor/include/main.cpp b/cpp/ql/test/library-tests/preprocessor/include/main.cpp new file mode 100644 index 000000000000..7dad80063c25 --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/include/main.cpp @@ -0,0 +1,6 @@ +// main.cpp + +#include "guarded.h" +#include "guarded.h" // a suppressed include +#include "unguarded.h" +#include "unguarded.h" \ No newline at end of file diff --git a/cpp/ql/test/library-tests/preprocessor/include/unguarded.h b/cpp/ql/test/library-tests/preprocessor/include/unguarded.h new file mode 100644 index 000000000000..14f174c8729f --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/include/unguarded.h @@ -0,0 +1,3 @@ +// unguarded.h + +// ... content ... diff --git a/cpp/ql/test/library-tests/preprocessor/linedirective/header.h b/cpp/ql/test/library-tests/preprocessor/linedirective/header.h new file mode 100644 index 000000000000..51fb5f568d65 --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/linedirective/header.h @@ -0,0 +1,3 @@ +// header.h + +// ... content ... diff --git a/cpp/ql/test/library-tests/preprocessor/linedirective/line.h b/cpp/ql/test/library-tests/preprocessor/linedirective/line.h new file mode 100644 index 000000000000..5ec8cc56e1f0 --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/linedirective/line.h @@ -0,0 +1,14 @@ +// line.h + +#line 100 "line.h" + +#include "header.h" + +void myLineFunction(); + +#define MYLINEDEFINE (1) + +struct myLineStruct +{ + int v; +}; diff --git a/cpp/ql/test/library-tests/preprocessor/linedirective/linedirective.expected b/cpp/ql/test/library-tests/preprocessor/linedirective/linedirective.expected new file mode 100644 index 000000000000..ddd32a32c176 --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/linedirective/linedirective.expected @@ -0,0 +1,19 @@ +| line.h:5:1:5:19 | #include "header.h" | header.h | +| line.h:7:6:7:19 | declaration of myLineFunction | | +| line.h:9:1:9:24 | #define MYLINEDEFINE (1) | | +| line.h:11:8:11:8 | declaration of operator= | | +| line.h:11:8:11:8 | declaration of operator= | | +| line.h:11:8:11:19 | definition of myLineStruct | | +| line.h:13:6:13:6 | definition of v | | +| main.cpp:3:1:3:17 | #include "line.h" | line.h | +| main.cpp:4:1:4:19 | #include "system.h" | system.h | +| main.cpp:6:1:6:19 | #include "header.h" | header.h | +| main.cpp:10:1:10:19 | #include "header.h" | header.h | +| main.cpp:14:1:14:19 | #include "header.h" | header.h | +| system.h:5:1:5:19 | #include "header.h" | header.h | +| system.h:7:6:7:21 | declaration of mySystemFunction | | +| system.h:9:1:9:26 | #define MYSYSTEMDEFINE (2) | | +| system.h:11:8:11:8 | declaration of operator= | | +| system.h:11:8:11:8 | declaration of operator= | | +| system.h:11:8:11:21 | definition of mySystemStruct | | +| system.h:13:6:13:6 | definition of v | | diff --git a/cpp/ql/test/library-tests/preprocessor/linedirective/linedirective.ql b/cpp/ql/test/library-tests/preprocessor/linedirective/linedirective.ql new file mode 100644 index 000000000000..2a774e787ef8 --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/linedirective/linedirective.ql @@ -0,0 +1,7 @@ +import cpp + +from Element e, string comment +where (e instanceof Include or e instanceof DeclarationEntry or e instanceof Macro) +and (e.getFile().toString() != "") +and if exists(((Include)e).getIncludedFile()) then comment = ((Include)e).getIncludedFile().toString() else comment = "" +select e, comment diff --git a/cpp/ql/test/library-tests/preprocessor/linedirective/main.cpp b/cpp/ql/test/library-tests/preprocessor/linedirective/main.cpp new file mode 100644 index 000000000000..fb4661033c1d --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/linedirective/main.cpp @@ -0,0 +1,14 @@ +// main.cpp + +#include "line.h" +#include "system.h" + +#include "header.h" + +#line 100 "main.cpp" + +#include "header.h" + +#line 200 "main.cpp" + +#include "header.h" diff --git a/cpp/ql/test/library-tests/preprocessor/linedirective/system.h b/cpp/ql/test/library-tests/preprocessor/linedirective/system.h new file mode 100644 index 000000000000..eec178192a5f --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/linedirective/system.h @@ -0,0 +1,14 @@ +// system.h + +#pragma GCC system_header + +#include "header.h" + +void mySystemFunction(); + +#define MYSYSTEMDEFINE (2) + +struct mySystemStruct +{ + int v; +}; diff --git a/cpp/ql/test/library-tests/preprocessor/macroinvocations/getanaffectedelement.expected b/cpp/ql/test/library-tests/preprocessor/macroinvocations/getanaffectedelement.expected new file mode 100644 index 000000000000..5bbb82d243d1 --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/macroinvocations/getanaffectedelement.expected @@ -0,0 +1,64 @@ +| header2.h:5:1:5:31 | CALL_WITH_A_NAME(macro) | header2.h:5:1:5:31 | definition of var10 | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() | +| header2.h:5:1:5:31 | CALL_WITH_A_NAME(macro) | header2.h:5:1:5:31 | var10 | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() | +| header2.h:5:1:5:31 | DECLARE_VAR10(name) | header2.h:5:1:5:31 | definition of var10 | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() | +| header2.h:5:1:5:31 | DECLARE_VAR10(name) | header2.h:5:1:5:31 | var10 | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() | +| in_class.h:2:1:2:11 | CONTEXTTYPE | in_class.h:2:13:2:18 | definition of myVar2 | getAnAffectedElement() | +| in_class.h:2:1:2:11 | CONTEXTTYPE | in_class.h:2:13:2:18 | definition of myVar2 | getAnAffectedElement() | +| in_class.h:2:1:2:11 | CONTEXTTYPE | in_class.h:2:13:2:18 | definition of myVar2 | getAnAffectedElement() | +| in_class.h:2:1:2:11 | CONTEXTTYPE | in_class.h:2:13:2:18 | definition of myVar2 | getAnAffectedElement() | +| in_class.h:2:1:2:11 | CONTEXTTYPE | in_class.h:2:13:2:18 | myVar2 | getAnAffectedElement() | +| in_class.h:2:1:2:11 | CONTEXTTYPE | in_class.h:2:13:2:18 | myVar2 | getAnAffectedElement() | +| in_class.h:2:1:2:11 | CONTEXTTYPE | in_class.h:2:13:2:18 | myVar2 | getAnAffectedElement() | +| in_class.h:2:1:2:11 | CONTEXTTYPE | in_class.h:2:13:2:18 | myVar2 | getAnAffectedElement() | +| in_function.h:2:1:2:11 | CONTEXTTYPE | in_function.h:2:13:2:18 | definition of myVar1 | getAnAffectedElement() | +| in_function.h:2:1:2:11 | CONTEXTTYPE | in_function.h:2:13:2:18 | myVar1 | getAnAffectedElement() | +| macroinvocations.cpp:14:1:14:4 | VAR1 | macroinvocations.cpp:14:1:14:4 | definition of var1 | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() | +| macroinvocations.cpp:14:1:14:4 | VAR1 | macroinvocations.cpp:14:1:14:4 | var1 | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() | +| macroinvocations.cpp:15:1:15:6 | VAR2() | macroinvocations.cpp:15:1:15:6 | definition of var2 | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() | +| macroinvocations.cpp:15:1:15:6 | VAR2() | macroinvocations.cpp:15:1:15:6 | var2 | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() | +| macroinvocations.cpp:21:1:21:17 | VAR3 | macroinvocations.cpp:21:1:21:17 | definition of var3 | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() | +| macroinvocations.cpp:21:1:21:17 | VAR3 | macroinvocations.cpp:21:1:21:17 | var3 | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() | +| macroinvocations.cpp:21:1:21:17 | WRAPOBJLIKE(x) | macroinvocations.cpp:21:1:21:17 | definition of var3 | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() | +| macroinvocations.cpp:21:1:21:17 | WRAPOBJLIKE(x) | macroinvocations.cpp:21:1:21:17 | var3 | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() | +| macroinvocations.cpp:22:1:22:17 | VAR4() | macroinvocations.cpp:22:1:22:17 | definition of var4 | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() | +| macroinvocations.cpp:22:1:22:17 | VAR4() | macroinvocations.cpp:22:1:22:17 | var4 | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() | +| macroinvocations.cpp:22:1:22:17 | WRAPFUNLIKE(x) | macroinvocations.cpp:22:1:22:17 | definition of var4 | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() | +| macroinvocations.cpp:22:1:22:17 | WRAPFUNLIKE(x) | macroinvocations.cpp:22:1:22:17 | var4 | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() | +| macroinvocations.cpp:28:1:28:19 | IGNOREOBJLIKE(x) | macroinvocations.cpp:28:1:28:19 | definition of var5 | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() | +| macroinvocations.cpp:28:1:28:19 | IGNOREOBJLIKE(x) | macroinvocations.cpp:28:1:28:19 | var5 | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() | +| macroinvocations.cpp:28:1:28:19 | VAR5 | macroinvocations.cpp:28:1:28:19 | definition of var5 | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() | +| macroinvocations.cpp:28:1:28:19 | VAR5 | macroinvocations.cpp:28:1:28:19 | var5 | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() | +| macroinvocations.cpp:29:1:29:19 | IGNOREFUNLIKE(x) | macroinvocations.cpp:29:1:29:19 | definition of var6 | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() | +| macroinvocations.cpp:29:1:29:19 | IGNOREFUNLIKE(x) | macroinvocations.cpp:29:1:29:19 | var6 | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() | +| macroinvocations.cpp:29:1:29:19 | VAR6() | macroinvocations.cpp:29:1:29:19 | definition of var6 | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() | +| macroinvocations.cpp:29:1:29:19 | VAR6() | macroinvocations.cpp:29:1:29:19 | var6 | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() | +| macroinvocations.cpp:34:1:34:18 | SECOND(x,y,z) | macroinvocations.cpp:34:1:34:18 | definition of var7 | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() | +| macroinvocations.cpp:34:1:34:18 | SECOND(x,y,z) | macroinvocations.cpp:34:1:34:18 | var7 | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() | +| macroinvocations.cpp:34:1:34:18 | VAR7 | macroinvocations.cpp:34:1:34:18 | definition of var7 | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() | +| macroinvocations.cpp:34:1:34:18 | VAR7 | macroinvocations.cpp:34:1:34:18 | var7 | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() | +| macroinvocations.cpp:39:1:39:9 | FIXEDJOIN | macroinvocations.cpp:39:1:39:9 | definition of var8 | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() | +| macroinvocations.cpp:39:1:39:9 | FIXEDJOIN | macroinvocations.cpp:39:1:39:9 | var8 | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() | +| macroinvocations.cpp:39:1:39:9 | VAR8() | macroinvocations.cpp:39:1:39:9 | definition of var8 | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() | +| macroinvocations.cpp:39:1:39:9 | VAR8() | macroinvocations.cpp:39:1:39:9 | var8 | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() | +| macroinvocations.cpp:42:1:43:7 | JOIN(x,y) | macroinvocations.cpp:42:1:43:7 | definition of var9 | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() | +| macroinvocations.cpp:42:1:43:7 | JOIN(x,y) | macroinvocations.cpp:42:1:43:7 | var9 | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() | +| macroinvocations.cpp:42:1:43:7 | VAR9 | macroinvocations.cpp:42:1:43:7 | definition of var9 | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() | +| macroinvocations.cpp:42:1:43:7 | VAR9 | macroinvocations.cpp:42:1:43:7 | var9 | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() | +| macroinvocations.cpp:83:1:83:5 | MYINT | macroinvocations.cpp:83:7:83:22 | definition of myGlobalVariable | getAnAffectedElement() | +| macroinvocations.cpp:83:1:83:5 | MYINT | macroinvocations.cpp:83:7:83:22 | myGlobalVariable | getAnAffectedElement() | +| macroinvocations.cpp:85:1:85:6 | MYVOID | macroinvocations.cpp:85:8:85:17 | definition of myFunction | getAnAffectedElement() | +| macroinvocations.cpp:85:1:85:6 | MYVOID | macroinvocations.cpp:85:8:85:17 | myFunction | getAnAffectedElement() | +| macroinvocations.cpp:85:19:85:23 | MYINT | macroinvocations.cpp:85:25:85:31 | definition of myParam | getAnAffectedElement() | +| macroinvocations.cpp:85:19:85:23 | MYINT | macroinvocations.cpp:85:25:85:31 | myParam | getAnAffectedElement() | +| macroinvocations.cpp:86:2:86:6 | MYINT | macroinvocations.cpp:86:8:86:22 | definition of myLocalVariable | getAnAffectedElement() | +| macroinvocations.cpp:86:2:86:6 | MYINT | macroinvocations.cpp:86:8:86:22 | myLocalVariable | getAnAffectedElement() | +| macroinvocations.cpp:92:2:92:6 | MYINT | macroinvocations.cpp:92:8:92:15 | definition of myMember | getAnAffectedElement() | +| macroinvocations.cpp:92:2:92:6 | MYINT | macroinvocations.cpp:92:8:92:15 | myMember | getAnAffectedElement() | +| macroinvocations.cpp:123:10:123:19 | MACRO_EXPR | macroinvocations.cpp:119:6:119:15 | myFunction | getEnclosingFunction() | +| macroinvocations.cpp:123:10:123:19 | MACRO_EXPR | macroinvocations.cpp:123:10:123:19 | 2 | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() | +| macroinvocations.cpp:123:10:123:19 | MACRO_EXPR | macroinvocations.cpp:123:10:123:19 | 3 | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() | +| macroinvocations.cpp:123:10:123:19 | MACRO_EXPR | macroinvocations.cpp:123:10:123:19 | 4 | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() | +| macroinvocations.cpp:123:10:123:19 | MACRO_EXPR | macroinvocations.cpp:123:10:123:19 | (...) | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() getExpr() | +| macroinvocations.cpp:123:10:123:19 | MACRO_EXPR | macroinvocations.cpp:123:10:123:19 | (...) | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() getExpr() | +| macroinvocations.cpp:123:10:123:19 | MACRO_EXPR | macroinvocations.cpp:123:10:123:19 | ... * ... | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() getExpr() | +| macroinvocations.cpp:123:10:123:19 | MACRO_EXPR | macroinvocations.cpp:123:10:123:19 | ... + ... | getAGeneratedElement() getAnAffectedElement() getAnExpandedElement() | diff --git a/cpp/ql/test/library-tests/preprocessor/macroinvocations/getanaffectedelement.ql b/cpp/ql/test/library-tests/preprocessor/macroinvocations/getanaffectedelement.ql new file mode 100644 index 000000000000..c361681075c0 --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/macroinvocations/getanaffectedelement.ql @@ -0,0 +1,29 @@ +import cpp + +predicate reason(MacroInvocation mi, Element e, string reason) { + (mi.getAnAffectedElement() = e and reason = "getAnAffectedElement()") or + (mi.getAnExpandedElement() = e and reason = "getAnExpandedElement()") or + (mi.getAGeneratedElement() = e and reason = "getAGeneratedElement()") or + (mi.getExpr() = e and reason = "getExpr()") or + (mi.getStmt() = e and reason = "getStmt()") or + (mi.getEnclosingFunction() = e and reason = "getEnclosingFunction()") +} + +string reasonspart(MacroInvocation mi, Element e) { + reason(mi, e, result) or + exists(string a, string b | + a = reasonspart(mi, e) and + reason(mi, e, b) and + not a.splitAt(" ") >= b and + result = a + " " + b + ) +} + +string reasons(MacroInvocation mi, Element e) { + result = reasonspart(mi, e) and + not reasonspart(mi, e).length() > result.length() +} + +from MacroInvocation mi, Element e +where reason(mi, e, _) +select mi, e, reasons(mi, e) diff --git a/cpp/ql/test/library-tests/preprocessor/macroinvocations/header1.h b/cpp/ql/test/library-tests/preprocessor/macroinvocations/header1.h new file mode 100644 index 000000000000..8d9ed65ac6ac --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/macroinvocations/header1.h @@ -0,0 +1,3 @@ +// see also header2.h + +#define CALL_WITH_A_NAME(macro) macro(banana) \ No newline at end of file diff --git a/cpp/ql/test/library-tests/preprocessor/macroinvocations/header2.h b/cpp/ql/test/library-tests/preprocessor/macroinvocations/header2.h new file mode 100644 index 000000000000..4555ac5f6e11 --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/macroinvocations/header2.h @@ -0,0 +1,5 @@ +// see also header1.h + +#define DECLARE_VAR10(name) int var10; + +CALL_WITH_A_NAME(DECLARE_VAR10) \ No newline at end of file diff --git a/cpp/ql/test/library-tests/preprocessor/macroinvocations/in_class.h b/cpp/ql/test/library-tests/preprocessor/macroinvocations/in_class.h new file mode 100644 index 000000000000..50a973033fa1 --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/macroinvocations/in_class.h @@ -0,0 +1,2 @@ + +CONTEXTTYPE myVar2; diff --git a/cpp/ql/test/library-tests/preprocessor/macroinvocations/in_function.h b/cpp/ql/test/library-tests/preprocessor/macroinvocations/in_function.h new file mode 100644 index 000000000000..ca3aebe61222 --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/macroinvocations/in_function.h @@ -0,0 +1,4 @@ + +CONTEXTTYPE myVar1; + +#include "nested.h" diff --git a/cpp/ql/test/library-tests/preprocessor/macroinvocations/macroinvocations.cpp b/cpp/ql/test/library-tests/preprocessor/macroinvocations/macroinvocations.cpp new file mode 100644 index 000000000000..a2a8eb6b9d81 --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/macroinvocations/macroinvocations.cpp @@ -0,0 +1,124 @@ + +#define VAR1 int var1; +#define VAR2() int var2; +#define VAR3 int var3; +#define VAR4() int var4; +#define VAR5 int var5; +#define VAR6() int var6; +#define VAR7 int var7; +#define VAR8() int var8; +#define VAR9 int var9; + +// --- simple ---- + +VAR1 +VAR2() + +// --- nested using parameter --- + +#define WRAPOBJLIKE(x) x +#define WRAPFUNLIKE(x) x() +WRAPOBJLIKE(VAR3) +WRAPFUNLIKE(VAR4) + +// --- nested ignoring parameter --- + +#define IGNOREOBJLIKE(x) VAR5 +#define IGNOREFUNLIKE(x) VAR6() +IGNOREOBJLIKE(VAR5) +IGNOREFUNLIKE(VAR6) + +// --- multiple parameters --- + +#define SECOND(x, y, z) y +SECOND(X, VAR7, Z) + +// --- token pasting --- + +#define FIXEDJOIN VAR8##() +FIXEDJOIN + +#define JOIN(x,y) x##y +JOIN(VAR, + 9) + +// --- example spanning header files --- + +#include "header1.h" +#include "header2.h" + +// --- check variables were created --- + +void use_vars() +{ + var1 = var2 = var3 = var4 = var5 = var6 = var7 = var8 = var9 = var10 = 1; +} + +// --- macro accesses --- + +#define FOO 1 +#define BAR FOO + +#if BAR +#endif + +#ifdef BAR +#endif + +#ifndef BAR +#endif + +#if (defined FOO || defined BAR || defined NA) +#endif + +#define DEF_BAR defined BAR +#if DEF_BAR +#endif + +// --- type define --- + +#define MYVOID void +#define MYINT int + +MYINT myGlobalVariable; + +MYVOID myFunction(MYINT myParam) { + MYINT myLocalVariable; +} + +class myClass +{ +public: + MYINT myMember; +}; + +// --- macro invocations in files included in context --- + +void function_context() +{ + #define CONTEXTTYPE int + #include "in_function.h" + #undef CONTEXTTYPE +} + +template +class template_class_context +{ +public: + #define CONTEXTTYPE short + #include "in_class.h" + #undef CONTEXTTYPE +}; + +template_class_context tcci; + +// --- macro expr -- + +#define MACRO_EXPR ((2 + 3) * 4) + +void myFunction() +{ + int x; + + x = 1 - MACRO_EXPR / 5; +} diff --git a/cpp/ql/test/library-tests/preprocessor/macroinvocations/macroinvocations.expected b/cpp/ql/test/library-tests/preprocessor/macroinvocations/macroinvocations.expected new file mode 100644 index 000000000000..9eb9fd7e5fda --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/macroinvocations/macroinvocations.expected @@ -0,0 +1,35 @@ +| header2.h:5:1:5:31 | CALL_WITH_A_NAME(macro) | MacroInvocation | none | +| header2.h:5:1:5:31 | DECLARE_VAR10(name) | MacroInvocation | CALL_WITH_A_NAME(macro) | +| in_class.h:2:1:2:11 | CONTEXTTYPE | MacroInvocation | none | +| in_class.h:2:1:2:11 | CONTEXTTYPE | MacroInvocation | none | +| in_function.h:2:1:2:11 | CONTEXTTYPE | MacroInvocation | none | +| macroinvocations.cpp:14:1:14:4 | VAR1 | MacroInvocation | none | +| macroinvocations.cpp:15:1:15:6 | VAR2() | MacroInvocation | none | +| macroinvocations.cpp:21:1:21:17 | VAR3 | MacroInvocation | WRAPOBJLIKE(x) | +| macroinvocations.cpp:21:1:21:17 | WRAPOBJLIKE(x) | MacroInvocation | none | +| macroinvocations.cpp:22:1:22:17 | VAR4() | MacroInvocation | WRAPFUNLIKE(x) | +| macroinvocations.cpp:22:1:22:17 | WRAPFUNLIKE(x) | MacroInvocation | none | +| macroinvocations.cpp:28:1:28:19 | IGNOREOBJLIKE(x) | MacroInvocation | none | +| macroinvocations.cpp:28:1:28:19 | VAR5 | MacroInvocation | IGNOREOBJLIKE(x) | +| macroinvocations.cpp:29:1:29:19 | IGNOREFUNLIKE(x) | MacroInvocation | none | +| macroinvocations.cpp:29:1:29:19 | VAR6() | MacroInvocation | IGNOREFUNLIKE(x) | +| macroinvocations.cpp:34:1:34:18 | SECOND(x,y,z) | MacroInvocation | none | +| macroinvocations.cpp:34:1:34:18 | VAR7 | MacroInvocation | SECOND(x,y,z) | +| macroinvocations.cpp:39:1:39:9 | FIXEDJOIN | MacroInvocation | none | +| macroinvocations.cpp:39:1:39:9 | VAR8() | MacroInvocation | FIXEDJOIN | +| macroinvocations.cpp:42:1:43:7 | JOIN(x,y) | MacroInvocation | none | +| macroinvocations.cpp:42:1:43:7 | VAR9 | MacroInvocation | JOIN(x,y) | +| macroinvocations.cpp:62:5:62:7 | BAR | MacroInvocation | none | +| macroinvocations.cpp:62:5:62:7 | FOO | MacroInvocation | BAR | +| macroinvocations.cpp:65:8:65:10 | BAR | MacroAccess | none | +| macroinvocations.cpp:68:9:68:11 | BAR | MacroAccess | none | +| macroinvocations.cpp:71:14:71:16 | FOO | MacroAccess | none | +| macroinvocations.cpp:71:29:71:31 | BAR | MacroAccess | none | +| macroinvocations.cpp:75:5:75:11 | BAR | MacroAccess | DEF_BAR | +| macroinvocations.cpp:75:5:75:11 | DEF_BAR | MacroInvocation | none | +| macroinvocations.cpp:83:1:83:5 | MYINT | MacroInvocation | none | +| macroinvocations.cpp:85:1:85:6 | MYVOID | MacroInvocation | none | +| macroinvocations.cpp:85:19:85:23 | MYINT | MacroInvocation | none | +| macroinvocations.cpp:86:2:86:6 | MYINT | MacroInvocation | none | +| macroinvocations.cpp:92:2:92:6 | MYINT | MacroInvocation | none | +| macroinvocations.cpp:123:10:123:19 | MACRO_EXPR | MacroInvocation | none | diff --git a/cpp/ql/test/library-tests/preprocessor/macroinvocations/macroinvocations.ql b/cpp/ql/test/library-tests/preprocessor/macroinvocations/macroinvocations.ql new file mode 100644 index 000000000000..14bebd2ad731 --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/macroinvocations/macroinvocations.ql @@ -0,0 +1,11 @@ +import cpp + +from MacroAccess mi, string type, string parent +where if mi instanceof MacroInvocation + then type = "MacroInvocation" + else type = "MacroAccess" +and if exists(mi.getParentInvocation()) + then parent = mi.getParentInvocation().toString() + else parent = "none" + +select mi, type, parent diff --git a/cpp/ql/test/library-tests/preprocessor/macroinvocations/nested.h b/cpp/ql/test/library-tests/preprocessor/macroinvocations/nested.h new file mode 100644 index 000000000000..07a921dee22d --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/macroinvocations/nested.h @@ -0,0 +1,2 @@ + +// ... diff --git a/cpp/ql/test/library-tests/preprocessor/pragma/pragma.expected b/cpp/ql/test/library-tests/preprocessor/pragma/pragma.expected new file mode 100644 index 000000000000..1e983c2247fb --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/pragma/pragma.expected @@ -0,0 +1,4 @@ +| test.c:1:1:1:12 | #pragma once | +| test.c:2:1:2:32 | #pragma GCC warning "My warning" | +| test.c:4:1:4:43 | #pragma My strange pragma. ("foo" : bar !!) | +| test.c:7:1:7:47 | #pragma prefast(suppress: 26014, "qwerty uiop") | diff --git a/cpp/ql/test/library-tests/preprocessor/pragma/pragma.ql b/cpp/ql/test/library-tests/preprocessor/pragma/pragma.ql new file mode 100644 index 000000000000..5530e1711c31 --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/pragma/pragma.ql @@ -0,0 +1,4 @@ +import cpp + +from PreprocessorPragma pragma +select pragma diff --git a/cpp/ql/test/library-tests/preprocessor/pragma/test.c b/cpp/ql/test/library-tests/preprocessor/pragma/test.c new file mode 100644 index 000000000000..33f08b97ed2c --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/pragma/test.c @@ -0,0 +1,7 @@ +#pragma once +#pragma GCC warning "My warning" + +#pragma My strange pragma. ("foo" : bar !!) + + +#pragma prefast(suppress: 26014, "qwerty uiop") diff --git a/cpp/ql/test/library-tests/preprocessor/preprocessor/a.h b/cpp/ql/test/library-tests/preprocessor/preprocessor/a.h new file mode 100644 index 000000000000..3f54c799b6ab --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/preprocessor/a.h @@ -0,0 +1 @@ +#include_next "a.h" diff --git a/cpp/ql/test/library-tests/preprocessor/preprocessor/more_headers/a.h b/cpp/ql/test/library-tests/preprocessor/preprocessor/more_headers/a.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/cpp/ql/test/library-tests/preprocessor/preprocessor/pp.cpp b/cpp/ql/test/library-tests/preprocessor/preprocessor/pp.cpp new file mode 100644 index 000000000000..20beabf8c932 --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/preprocessor/pp.cpp @@ -0,0 +1,70 @@ +#if defined(FOO) +#error "This shouldn't happen" +#elif !defined(BAR) +#define BAR // I'm not a stool +#define BAR_val 1 /* I'm not a function */ +#define BAR_fn() BAR_val +#else +#warning "This shouldn't happen either" +#endif +// semmle-extractor-options: -I${testdir}/more_headers/ "-U SOME_SYM" +#undef BAR +//#define SCARY(a,aa,aaah) /* we ignore a */ (aa /* but we take aa */) /* and we ignore aaa */ +#define LOG(fmt, ...) printf("Warning: %s", fmt, __VA__ARGS__) +#include "pp.h" + +#if 0 +#else +#endif + +#if 1 +#else +#endif + +#import "a.h" + +// --- contexts and templates --- + +void functionContext() +{ + #define MACRO_FUNCTIONCONTEXT 1 +} + +class classContext +{ +public: + #define MACRO_CLASSCONTEXT 2 +}; + +template +void templateFunctionContext() +{ + #define MACRO_TEMPLATEFUNCTIONCONTEXT 3 +} + +template +class templateClassContext +{ +public: + #define MACRO_TEMPLATECLASSCONTEXT 4 + #define MACRO_TEMPLATECLASSCONTEXT_REFERENCED 5 + + template + void templateMethodContext() { + #define MACRO_TEMPLATEMETHODCONTEXT 6 + } + + #ifdef INSTANTIATION + #define IN_INSTANTIATION + #else + #define IN_TEMPLATE + #endif + + static int val; +}; + +template +int templateClassContext :: val = MACRO_TEMPLATECLASSCONTEXT_REFERENCED; + +#define INSTANTIATION +templateClassContext tcci; diff --git a/cpp/ql/test/library-tests/preprocessor/preprocessor/pp.h b/cpp/ql/test/library-tests/preprocessor/preprocessor/pp.h new file mode 100644 index 000000000000..288442716b52 --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/preprocessor/pp.h @@ -0,0 +1,18 @@ +#pragma once +//#warning "This should happen" +#line 33 "emerald_city.h" // Magic! +//#pragma byte_order(big_endian) +#warning "Not in Kansas any more" + +//#define MULTILINE \ + /* Hello */ \ + world \ + /* from */ \ + a long \ + /* macro */ +//#undef \ + MULTILINE + +//#include \ + \ + \ diff --git a/cpp/ql/test/library-tests/preprocessor/preprocessor/preproc.expected b/cpp/ql/test/library-tests/preprocessor/preprocessor/preproc.expected new file mode 100644 index 000000000000..cff0dd489eaa --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/preprocessor/preproc.expected @@ -0,0 +1,32 @@ +| a.h:0:0:0:0 | a.h | 1 | 1 | 1 | 19 | IncludeNext | "a.h" | N/A | +| pp.cpp:0:0:0:0 | pp.cpp | 1 | 1 | 1 | 16 | PreprocessorIf | defined(FOO) | N/A | +| pp.cpp:0:0:0:0 | pp.cpp | 3 | 1 | 3 | 19 | PreprocessorElif | !defined(BAR) | N/A | +| pp.cpp:0:0:0:0 | pp.cpp | 4 | 1 | 4 | 11 | Macro | BAR | | +| pp.cpp:0:0:0:0 | pp.cpp | 5 | 1 | 5 | 17 | Macro | BAR_val | 1 | +| pp.cpp:0:0:0:0 | pp.cpp | 6 | 1 | 6 | 24 | Macro | BAR_fn() | BAR_val | +| pp.cpp:0:0:0:0 | pp.cpp | 7 | 1 | 7 | 5 | PreprocessorElse | N/A | N/A | +| pp.cpp:0:0:0:0 | pp.cpp | 9 | 1 | 9 | 6 | PreprocessorEndif | N/A | N/A | +| pp.cpp:0:0:0:0 | pp.cpp | 11 | 1 | 11 | 10 | PreprocessorUndef | BAR | N/A | +| pp.cpp:0:0:0:0 | pp.cpp | 13 | 1 | 13 | 63 | Macro | LOG(fmt,__VA_ARGS__...) | printf("Warning: %s", fmt, __VA__ARGS__) | +| pp.cpp:0:0:0:0 | pp.cpp | 14 | 1 | 14 | 15 | Include | "pp.h" | N/A | +| pp.cpp:0:0:0:0 | pp.cpp | 16 | 1 | 16 | 5 | PreprocessorIf | 0 | N/A | +| pp.cpp:0:0:0:0 | pp.cpp | 17 | 1 | 17 | 5 | PreprocessorElse | N/A | N/A | +| pp.cpp:0:0:0:0 | pp.cpp | 18 | 1 | 18 | 6 | PreprocessorEndif | N/A | N/A | +| pp.cpp:0:0:0:0 | pp.cpp | 20 | 1 | 20 | 5 | PreprocessorIf | 1 | N/A | +| pp.cpp:0:0:0:0 | pp.cpp | 21 | 1 | 21 | 5 | PreprocessorElse | N/A | N/A | +| pp.cpp:0:0:0:0 | pp.cpp | 22 | 1 | 22 | 6 | PreprocessorEndif | N/A | N/A | +| pp.cpp:0:0:0:0 | pp.cpp | 24 | 1 | 24 | 13 | Import | "a.h" | N/A | +| pp.cpp:0:0:0:0 | pp.cpp | 30 | 2 | 30 | 32 | Macro | MACRO_FUNCTIONCONTEXT | 1 | +| pp.cpp:0:0:0:0 | pp.cpp | 36 | 2 | 36 | 29 | Macro | MACRO_CLASSCONTEXT | 2 | +| pp.cpp:0:0:0:0 | pp.cpp | 42 | 2 | 42 | 40 | Macro | MACRO_TEMPLATEFUNCTIONCONTEXT | 3 | +| pp.cpp:0:0:0:0 | pp.cpp | 49 | 2 | 49 | 37 | Macro | MACRO_TEMPLATECLASSCONTEXT | 4 | +| pp.cpp:0:0:0:0 | pp.cpp | 50 | 2 | 50 | 48 | Macro | MACRO_TEMPLATECLASSCONTEXT_REFERENCED | 5 | +| pp.cpp:0:0:0:0 | pp.cpp | 54 | 3 | 54 | 39 | Macro | MACRO_TEMPLATEMETHODCONTEXT | 6 | +| pp.cpp:0:0:0:0 | pp.cpp | 57 | 1 | 57 | 21 | PreprocessorIfdef | INSTANTIATION | N/A | +| pp.cpp:0:0:0:0 | pp.cpp | 59 | 1 | 59 | 6 | PreprocessorElse | N/A | N/A | +| pp.cpp:0:0:0:0 | pp.cpp | 60 | 3 | 60 | 21 | Macro | IN_TEMPLATE | | +| pp.cpp:0:0:0:0 | pp.cpp | 61 | 1 | 61 | 7 | PreprocessorEndif | N/A | N/A | +| pp.cpp:0:0:0:0 | pp.cpp | 69 | 1 | 69 | 21 | Macro | INSTANTIATION | | +| pp.h:0:0:0:0 | pp.h | 1 | 1 | 1 | 12 | PreprocessorPragma | once | N/A | +| pp.h:0:0:0:0 | pp.h | 3 | 1 | 3 | 27 | PreprocessorLine | 33 "emerald_city.h" | N/A | +| pp.h:0:0:0:0 | pp.h | 5 | 1 | 5 | 33 | PreprocessorWarning | "Not in Kansas any more" | N/A | diff --git a/cpp/ql/test/library-tests/preprocessor/preprocessor/preproc.ql b/cpp/ql/test/library-tests/preprocessor/preprocessor/preproc.ql new file mode 100644 index 000000000000..2ec13f889364 --- /dev/null +++ b/cpp/ql/test/library-tests/preprocessor/preprocessor/preproc.ql @@ -0,0 +1,7 @@ +import cpp + +from Location loc, PreprocessorDirective pd, string head, string body +where loc = pd.getLocation() +and if exists(pd.getHead()) then head = pd.getHead() else head = "N/A" +and if pd instanceof Macro then body = ((Macro)pd).getBody() else body = "N/A" +select loc.getFile(), loc.getStartLine(), loc.getStartColumn(), loc.getEndLine(), loc.getEndColumn(), concat(pd.getAQlClass(), ", "), head, body diff --git a/cpp/ql/test/library-tests/proxy_class/is_proxy_class_for.expected b/cpp/ql/test/library-tests/proxy_class/is_proxy_class_for.expected new file mode 100644 index 000000000000..77b7cbefc28c --- /dev/null +++ b/cpp/ql/test/library-tests/proxy_class/is_proxy_class_for.expected @@ -0,0 +1 @@ +| proxy_class.cpp:3:20:3:20 | T | proxy_class.cpp:3:20:3:20 | T | diff --git a/cpp/ql/test/library-tests/proxy_class/is_proxy_class_for.ql b/cpp/ql/test/library-tests/proxy_class/is_proxy_class_for.ql new file mode 100644 index 000000000000..e5a4ed3f83b9 --- /dev/null +++ b/cpp/ql/test/library-tests/proxy_class/is_proxy_class_for.ql @@ -0,0 +1,4 @@ +import cpp + +from ProxyClass pc +select pc, pc.getTemplateParameter() diff --git a/cpp/ql/test/library-tests/proxy_class/locations.expected b/cpp/ql/test/library-tests/proxy_class/locations.expected new file mode 100644 index 000000000000..6c0eb498cbbe --- /dev/null +++ b/cpp/ql/test/library-tests/proxy_class/locations.expected @@ -0,0 +1,4 @@ +| proxy_class.cpp:1:8:1:11 | Base | Struct | +| proxy_class.cpp:3:20:3:20 | T | ProxyClass | +| proxy_class.cpp:3:20:3:20 | T | TemplateParameter | +| proxy_class.cpp:4:8:4:14 | Derived | Struct | diff --git a/cpp/ql/test/library-tests/proxy_class/locations.ql b/cpp/ql/test/library-tests/proxy_class/locations.ql new file mode 100644 index 000000000000..5d6e087f61e9 --- /dev/null +++ b/cpp/ql/test/library-tests/proxy_class/locations.ql @@ -0,0 +1,11 @@ +import cpp + +string clazz(Declaration d) { + (d instanceof ProxyClass and result = "ProxyClass") or + (d instanceof TemplateParameter and result = "TemplateParameter") or + (d instanceof Struct and result = "Struct") +} + +from Declaration d +where d.getLocation().getStartLine() > 0 +select d, clazz(d) diff --git a/cpp/ql/test/library-tests/proxy_class/proxy_class.cpp b/cpp/ql/test/library-tests/proxy_class/proxy_class.cpp new file mode 100644 index 000000000000..09d45d7a3d07 --- /dev/null +++ b/cpp/ql/test/library-tests/proxy_class/proxy_class.cpp @@ -0,0 +1,7 @@ +struct Base {}; + +template +struct Derived {friend class T;}; + +int main() { return sizeof(Derived); } +// semmle-extractor-options: --microsoft diff --git a/cpp/ql/test/library-tests/ptr_to_member/segfault/exprs.expected b/cpp/ql/test/library-tests/ptr_to_member/segfault/exprs.expected new file mode 100644 index 000000000000..7af2f7c8c172 --- /dev/null +++ b/cpp/ql/test/library-tests/ptr_to_member/segfault/exprs.expected @@ -0,0 +1,28 @@ +| segfault.cpp:25:46:25:65 | call to S | file://:0:0:0:0 | void | +| segfault.cpp:25:46:25:65 | call to S | file://:0:0:0:0 | void | +| segfault.cpp:25:48:25:55 | __second | segfault.cpp:15:7:15:11 | tuple | +| segfault.cpp:25:48:25:55 | __second | segfault.cpp:15:7:15:11 | tuple | +| segfault.cpp:25:48:25:55 | __second | segfault.cpp:15:7:15:11 | tuple | +| segfault.cpp:25:58:25:64 | 0 | segfault.cpp:12:8:12:12 | index | +| segfault.cpp:25:58:25:64 | 0 | segfault.cpp:12:8:12:12 | index | +| segfault.cpp:25:58:25:64 | 0 | segfault.cpp:12:8:12:12 | index | +| segfault.cpp:29:23:29:44 | 0 | file://:0:0:0:0 | ..:: * | +| segfault.cpp:29:23:29:44 | 0 | file://:0:0:0:0 | ..:: * | +| segfault.cpp:29:23:29:44 | constructor init of field second | file://:0:0:0:0 | ..:: * | +| segfault.cpp:29:23:29:44 | constructor init of field second | file://:0:0:0:0 | ..:: * | +| segfault.cpp:29:23:29:44 | constructor init of field second | segfault.cpp:21:16:21:16 | T | +| segfault.cpp:29:30:29:37 | Unknown literal | file://:0:0:0:0 | unknown | +| segfault.cpp:29:30:29:40 | call to expression | file://:0:0:0:0 | unknown | +| segfault.cpp:29:39:29:39 | t | segfault.cpp:15:7:15:11 | tuple | +| segfault.cpp:35:5:35:5 | x | file://:0:0:0:0 | S<..(*)(..)> * | +| segfault.cpp:35:5:35:56 | ... = ... | file://:0:0:0:0 | S<..(*)(..)> * | +| segfault.cpp:35:9:35:56 | call to S | file://:0:0:0:0 | void | +| segfault.cpp:35:9:35:56 | new | file://:0:0:0:0 | S<..(*)(..)> * | +| segfault.cpp:35:24:35:46 | 0 | segfault.cpp:9:8:9:28 | piecewise_construct_t | +| segfault.cpp:35:49:35:55 | 0 | segfault.cpp:15:7:15:11 | tuple | +| segfault.cpp:40:5:40:5 | x | file://:0:0:0:0 | S * | +| segfault.cpp:40:5:40:54 | ... = ... | file://:0:0:0:0 | S * | +| segfault.cpp:40:9:40:54 | call to S | file://:0:0:0:0 | void | +| segfault.cpp:40:9:40:54 | new | file://:0:0:0:0 | S * | +| segfault.cpp:40:22:40:44 | 0 | segfault.cpp:9:8:9:28 | piecewise_construct_t | +| segfault.cpp:40:47:40:53 | 0 | segfault.cpp:15:7:15:11 | tuple | diff --git a/cpp/ql/test/library-tests/ptr_to_member/segfault/exprs.ql b/cpp/ql/test/library-tests/ptr_to_member/segfault/exprs.ql new file mode 100644 index 000000000000..e2ff0353b038 --- /dev/null +++ b/cpp/ql/test/library-tests/ptr_to_member/segfault/exprs.ql @@ -0,0 +1,5 @@ +import cpp + +from Expr e +select e, e.getType() + diff --git a/cpp/ql/test/library-tests/ptr_to_member/segfault/segfault.cpp b/cpp/ql/test/library-tests/ptr_to_member/segfault/segfault.cpp new file mode 100644 index 000000000000..cebffae85506 --- /dev/null +++ b/cpp/ql/test/library-tests/ptr_to_member/segfault/segfault.cpp @@ -0,0 +1,42 @@ + +class F { + public: +}; + +typedef int (F::* FMethod) (int prefix); +typedef int F::* FData; + +struct piecewise_construct_t { +}; + +struct index { +}; + +class tuple { +}; + +template + void get(tuple __t); + +template +struct S { + T second; + + S(piecewise_construct_t, tuple __second) : S(__second, index()) { + } + + template + S(tuple t, index) : second(get(t)...) { + } +}; + +void f() { + S* x; + x = new S(piecewise_construct_t(), tuple()); +} + +void g() { + S* x; + x = new S(piecewise_construct_t(), tuple()); +} + diff --git a/cpp/ql/test/library-tests/qualifiers/class-enum/expr.expected b/cpp/ql/test/library-tests/qualifiers/class-enum/expr.expected new file mode 100644 index 000000000000..655f9ae50e81 --- /dev/null +++ b/cpp/ql/test/library-tests/qualifiers/class-enum/expr.expected @@ -0,0 +1,8 @@ +| file://:0:0:0:0 | 0 | +| file://:0:0:0:0 | 1 | +| test.cpp:10:38:10:51 | A | +| test.cpp:18:19:18:54 | call to MyClass2 | +| test.cpp:18:29:18:32 | 1 | +| test.cpp:18:29:18:53 | ... ? ... : ... | +| test.cpp:18:36:18:49 | A | +| test.cpp:18:53:18:53 | v | diff --git a/cpp/ql/test/library-tests/qualifiers/class-enum/expr.ql b/cpp/ql/test/library-tests/qualifiers/class-enum/expr.ql new file mode 100644 index 000000000000..3c2a3269e72e --- /dev/null +++ b/cpp/ql/test/library-tests/qualifiers/class-enum/expr.ql @@ -0,0 +1,5 @@ +import cpp + +from Expr e +select e + diff --git a/cpp/ql/test/library-tests/qualifiers/class-enum/test.cpp b/cpp/ql/test/library-tests/qualifiers/class-enum/test.cpp new file mode 100644 index 000000000000..33f7a564993b --- /dev/null +++ b/cpp/ql/test/library-tests/qualifiers/class-enum/test.cpp @@ -0,0 +1,19 @@ + +class MyEnumClass { +public: + enum MyEnum { + A, + B + }; +}; + +static const MyEnumClass::MyEnum v = MyEnumClass::A; + +class MyClass2 { +public: + inline MyClass2(MyEnumClass::MyEnum p) { } +}; + +void f() { + MyClass2 mc2 = MyClass2(true ? MyEnumClass::A : v); +} diff --git a/cpp/ql/test/library-tests/queries/bitwise_sign_check/bsc.cpp b/cpp/ql/test/library-tests/queries/bitwise_sign_check/bsc.cpp new file mode 100644 index 000000000000..e8d829de9bbb --- /dev/null +++ b/cpp/ql/test/library-tests/queries/bitwise_sign_check/bsc.cpp @@ -0,0 +1,15 @@ +bool is_bit_set_v1(int x, int bitnum) { + return (x & (1 << bitnum)) > 0; +} + +bool is_bit_set_v2(int x, int bitnum) { + return ((1 << bitnum) & x) > 0; +} + +bool plain_wrong(int x, int bitnum) { + return (x & (1 << bitnum)) >= 0; +} + +bool is_bit24_set(int x) { + return (x & (1 << 24)) > 0; +} diff --git a/cpp/ql/test/library-tests/queries/bitwise_sign_check/bsc.expected b/cpp/ql/test/library-tests/queries/bitwise_sign_check/bsc.expected new file mode 100644 index 000000000000..a51eab28751b --- /dev/null +++ b/cpp/ql/test/library-tests/queries/bitwise_sign_check/bsc.expected @@ -0,0 +1,3 @@ +| bsc.cpp:2:10:2:32 | ... > ... | Potential unsafe sign check of a bitwise operation. | +| bsc.cpp:6:10:6:32 | ... > ... | Potential unsafe sign check of a bitwise operation. | +| bsc.cpp:10:10:10:33 | ... >= ... | Potential unsafe sign check of a bitwise operation. | diff --git a/cpp/ql/test/library-tests/queries/bitwise_sign_check/bsc.qlref b/cpp/ql/test/library-tests/queries/bitwise_sign_check/bsc.qlref new file mode 100644 index 000000000000..27d5a87962e4 --- /dev/null +++ b/cpp/ql/test/library-tests/queries/bitwise_sign_check/bsc.qlref @@ -0,0 +1 @@ +Likely Bugs/Arithmetic/BitwiseSignCheck.ql \ No newline at end of file diff --git a/cpp/ql/test/library-tests/queries/expr_has_no_effect/calls.cpp b/cpp/ql/test/library-tests/queries/expr_has_no_effect/calls.cpp new file mode 100644 index 000000000000..d9a71fa7bafa --- /dev/null +++ b/cpp/ql/test/library-tests/queries/expr_has_no_effect/calls.cpp @@ -0,0 +1,30 @@ +namespace Calls { + +int external(); + +class Base { +public: + virtual int thingy() { + 1; + } + + int our_thingy() { + Base::thingy(); + return 2; + } +}; + +class Derived : public Base { +public: + virtual int thingy() { + external(); + return 3; + } +}; + +void internal() { + Base* ptr = new Derived(); + ptr->thingy(); +} + +} \ No newline at end of file diff --git a/cpp/ql/test/library-tests/queries/expr_has_no_effect/expr_has_no_effect.expected b/cpp/ql/test/library-tests/queries/expr_has_no_effect/expr_has_no_effect.expected new file mode 100644 index 000000000000..af2bacade1a2 --- /dev/null +++ b/cpp/ql/test/library-tests/queries/expr_has_no_effect/expr_has_no_effect.expected @@ -0,0 +1,8 @@ +| calls.cpp:8:5:8:5 | 1 | This expression has no effect. | calls.cpp:8:5:8:5 | 1 | | +| calls.cpp:12:5:12:16 | call to thingy | This expression has no effect (because $@ has no external side effects). | calls.cpp:7:15:7:20 | thingy | thingy | +| templatey.cpp:4:3:4:8 | ... << ... | This expression has no effect. | templatey.cpp:4:3:4:8 | ... << ... | | +| templatey.cpp:39:3:39:23 | call to pointless_add_numbers | This expression has no effect (because $@ has no external side effects). | templatey.cpp:29:5:29:25 | pointless_add_numbers | pointless_add_numbers | +| volatile.c:9:5:9:5 | c | This expression has no effect. | volatile.c:9:5:9:5 | c | | +| volatile.c:12:5:12:9 | access to array | This expression has no effect. | volatile.c:12:5:12:9 | access to array | | +| volatile.c:16:5:16:7 | * ... | This expression has no effect. | volatile.c:16:5:16:7 | * ... | | +| volatile.c:20:5:20:13 | * ... | This expression has no effect. | volatile.c:20:5:20:13 | * ... | | diff --git a/cpp/ql/test/library-tests/queries/expr_has_no_effect/expr_has_no_effect.qlref b/cpp/ql/test/library-tests/queries/expr_has_no_effect/expr_has_no_effect.qlref new file mode 100644 index 000000000000..6d8e82140d68 --- /dev/null +++ b/cpp/ql/test/library-tests/queries/expr_has_no_effect/expr_has_no_effect.qlref @@ -0,0 +1 @@ +Likely Bugs/Likely Typos/ExprHasNoEffect.ql \ No newline at end of file diff --git a/cpp/ql/test/library-tests/queries/expr_has_no_effect/templatey.cpp b/cpp/ql/test/library-tests/queries/expr_has_no_effect/templatey.cpp new file mode 100644 index 000000000000..114e2052a530 --- /dev/null +++ b/cpp/ql/test/library-tests/queries/expr_has_no_effect/templatey.cpp @@ -0,0 +1,40 @@ +template +void foo(T x, T y) +{ + x << y; +}; + +struct streamable +{ +}; + +void operator<<(streamable& lhs, streamable& rhs) +{ +} + +int main() +{ + int x = 3; + foo(x, x); + streamable y; + foo(y, y); + return 0; +} + +int add_numbers(int& lhs, int rhs) +{ + return lhs += rhs; +} + +int pointless_add_numbers(int lhs, int rhs) +{ + return lhs += rhs; +} + +void call_add_numbers() +{ + int accum = 0; + add_numbers(accum, 4); + add_numbers(accum, 10); + pointless_add_numbers(accum, 20); +} diff --git a/cpp/ql/test/library-tests/queries/expr_has_no_effect/volatile.c b/cpp/ql/test/library-tests/queries/expr_has_no_effect/volatile.c new file mode 100644 index 000000000000..27ee532c99c2 --- /dev/null +++ b/cpp/ql/test/library-tests/queries/expr_has_no_effect/volatile.c @@ -0,0 +1,25 @@ + +char c; +volatile char v; + +char *pc; +volatile char *pv; + +void f(void) { + c; + v; + + pc[5]; + pv[5]; + ((volatile char *)pc)[5]; + + *pc; + *pv; + *((volatile char *)pc); + + *(pc + 5); + *(pv + 5); + *((volatile char *)(pc + 5)); + *(((volatile char *)pc) + 5); +} + diff --git a/cpp/ql/test/library-tests/queries/jsf97/jsf97.cpp b/cpp/ql/test/library-tests/queries/jsf97/jsf97.cpp new file mode 100644 index 000000000000..df00f154480a --- /dev/null +++ b/cpp/ql/test/library-tests/queries/jsf97/jsf97.cpp @@ -0,0 +1,62 @@ +typedef char chars[10]; +typedef int jmp_buf[16]; + +class C { public: + +static int bad1(char xs[10]) +{ + return sizeof(xs); +} + +static int bad2(char xs[]) +{ + return sizeof(xs); +} + +static int bad3(chars xs) +{ + return sizeof(xs); +} + +static int bad4(chars const xs) +{ + return sizeof(xs); +} + +static int good1(char (&xs)[10]) +{ + return sizeof(xs); +} + +static int good2(chars& xs) +{ + return sizeof(xs); +} + +static void good_longjmp(jmp_buf j) +{ +} + +static void bad_longjmp(int j[16]) +{ +} + +template +static unsigned array_size(T(&)[N]) +{ + return N; +} + +template +static void no_op(T&& arg) +{ +} + +}; + +int main() +{ + chars c; + C::no_op(c); + return C::array_size(c); +} diff --git a/cpp/ql/test/library-tests/queries/jsf97/jsf97.expected b/cpp/ql/test/library-tests/queries/jsf97/jsf97.expected new file mode 100644 index 000000000000..84205c2cebbf --- /dev/null +++ b/cpp/ql/test/library-tests/queries/jsf97/jsf97.expected @@ -0,0 +1,5 @@ +| jsf97.cpp:6:12:6:15 | bad1 | Raw arrays should not be used in interfaces. A container class should be used instead. | +| jsf97.cpp:11:12:11:15 | bad2 | Raw arrays should not be used in interfaces. A container class should be used instead. | +| jsf97.cpp:16:12:16:15 | bad3 | Raw arrays should not be used in interfaces. A container class should be used instead. | +| jsf97.cpp:21:12:21:15 | bad4 | Raw arrays should not be used in interfaces. A container class should be used instead. | +| jsf97.cpp:40:13:40:23 | bad_longjmp | Raw arrays should not be used in interfaces. A container class should be used instead. | diff --git a/cpp/ql/test/library-tests/queries/jsf97/jsf97.qlref b/cpp/ql/test/library-tests/queries/jsf97/jsf97.qlref new file mode 100644 index 000000000000..c08b4c966198 --- /dev/null +++ b/cpp/ql/test/library-tests/queries/jsf97/jsf97.qlref @@ -0,0 +1 @@ +jsf/4.10 Classes/AV Rule 97.ql \ No newline at end of file diff --git a/cpp/ql/test/library-tests/queries/todo_fixme/.gitattributes b/cpp/ql/test/library-tests/queries/todo_fixme/.gitattributes new file mode 100644 index 000000000000..309468b7488a --- /dev/null +++ b/cpp/ql/test/library-tests/queries/todo_fixme/.gitattributes @@ -0,0 +1 @@ +todo_fixme.cpp text eol=lf diff --git a/cpp/ql/test/library-tests/queries/todo_fixme/fixme.expected b/cpp/ql/test/library-tests/queries/todo_fixme/fixme.expected new file mode 100644 index 000000000000..65721142ad4b --- /dev/null +++ b/cpp/ql/test/library-tests/queries/todo_fixme/fixme.expected @@ -0,0 +1,3 @@ +| todo_fixme.cpp:8:1:8:16 | // FIXME: Bug 1. | FIXME comment: Bug 1. | +| todo_fixme.cpp:9:1:9:19 | /* FIXME: Bug 2. */ | FIXME comment: Bug 2. | +| todo_fixme.cpp:10:1:12:3 | /**\n * FIXME: Bug 3.\n */ | FIXME comment: Bug 3. | diff --git a/cpp/ql/test/library-tests/queries/todo_fixme/fixme.qlref b/cpp/ql/test/library-tests/queries/todo_fixme/fixme.qlref new file mode 100644 index 000000000000..8392f493657e --- /dev/null +++ b/cpp/ql/test/library-tests/queries/todo_fixme/fixme.qlref @@ -0,0 +1 @@ +Documentation/FixmeComments.ql diff --git a/cpp/ql/test/library-tests/queries/todo_fixme/todo.expected b/cpp/ql/test/library-tests/queries/todo_fixme/todo.expected new file mode 100644 index 000000000000..f7dd64f95404 --- /dev/null +++ b/cpp/ql/test/library-tests/queries/todo_fixme/todo.expected @@ -0,0 +1,3 @@ +| todo_fixme.cpp:1:1:1:17 | // TODO: Thing 1. | TODO comment: Thing 1. | +| todo_fixme.cpp:2:1:2:20 | /* TODO: Thing 2. */ | TODO comment: Thing 2. | +| todo_fixme.cpp:3:1:5:3 | /**\n * TODO: Thing 3.\n */ | TODO comment: Thing 3. | diff --git a/cpp/ql/test/library-tests/queries/todo_fixme/todo.qlref b/cpp/ql/test/library-tests/queries/todo_fixme/todo.qlref new file mode 100644 index 000000000000..bb57dbe50cee --- /dev/null +++ b/cpp/ql/test/library-tests/queries/todo_fixme/todo.qlref @@ -0,0 +1 @@ +Documentation/TodoComments.ql diff --git a/cpp/ql/test/library-tests/queries/todo_fixme/todo_fixme.cpp b/cpp/ql/test/library-tests/queries/todo_fixme/todo_fixme.cpp new file mode 100644 index 000000000000..5162d68f39ab --- /dev/null +++ b/cpp/ql/test/library-tests/queries/todo_fixme/todo_fixme.cpp @@ -0,0 +1,13 @@ +// TODO: Thing 1. +/* TODO: Thing 2. */ +/** + * TODO: Thing 3. + */ +// For more things, read the /usr/local/doc/TODO file. + +// FIXME: Bug 1. +/* FIXME: Bug 2. */ +/** + * FIXME: Bug 3. + */ +// For more bugs, read the /usr/local/doc/FIXME file. diff --git a/cpp/ql/test/library-tests/question_mark_colon/cfg.expected b/cpp/ql/test/library-tests/question_mark_colon/cfg.expected new file mode 100644 index 000000000000..82cda5c6f84c --- /dev/null +++ b/cpp/ql/test/library-tests/question_mark_colon/cfg.expected @@ -0,0 +1,23 @@ +| 2 | 1 | question_mark_colon.c:2:7:2:7 | f | None | +| 4 | 1 | question_mark_colon.c:4:14:8:1 | { ... } | declaration | +| 4 | 17 | question_mark_colon.c:4:6:4:6 | g | None | +| 5 | 2 | question_mark_colon.c:5:5:5:12 | declaration | ExprStmt | +| 6 | 1 | question_mark_colon.c:6:17:6:21 | array to pointer conversion | None | +| 6 | 3 | question_mark_colon.c:6:5:6:22 | ExprStmt | ... ? ... : ... | +| 6 | 4 | question_mark_colon.c:6:9:6:21 | ... ? ... : ... | call to f | +| 6 | 5 | question_mark_colon.c:6:9:6:9 | call to f | s | +| 6 | 5 | question_mark_colon.c:6:9:6:9 | call to f | str | +| 6 | 6 | question_mark_colon.c:6:17:6:21 | str | s | +| 6 | 7 | question_mark_colon.c:6:5:6:5 | s | ... = ... | +| 6 | 8 | question_mark_colon.c:6:5:6:21 | ... = ... | ExprStmt | +| 7 | 1 | question_mark_colon.c:7:15:7:17 | array to pointer conversion | None | +| 7 | 1 | question_mark_colon.c:7:21:7:23 | array to pointer conversion | None | +| 7 | 9 | question_mark_colon.c:7:5:7:24 | ExprStmt | ... ? ... : ... | +| 7 | 10 | question_mark_colon.c:7:9:7:23 | ... ? ... : ... | call to f | +| 7 | 11 | question_mark_colon.c:7:9:7:9 | call to f | a | +| 7 | 11 | question_mark_colon.c:7:9:7:9 | call to f | b | +| 7 | 12 | question_mark_colon.c:7:15:7:17 | a | s | +| 7 | 12 | question_mark_colon.c:7:21:7:23 | b | s | +| 7 | 14 | question_mark_colon.c:7:5:7:5 | s | ... = ... | +| 7 | 15 | question_mark_colon.c:7:5:7:23 | ... = ... | return ... | +| 8 | 16 | question_mark_colon.c:8:1:8:1 | return ... | g | diff --git a/cpp/ql/test/library-tests/question_mark_colon/cfg.ql b/cpp/ql/test/library-tests/question_mark_colon/cfg.ql new file mode 100644 index 000000000000..65ea01a698f4 --- /dev/null +++ b/cpp/ql/test/library-tests/question_mark_colon/cfg.ql @@ -0,0 +1,14 @@ +import cpp + +string getASuccessorOrNone(ControlFlowNode n) { + if exists(n.getASuccessor()) + then result = n.getASuccessor().toString() + else result = "None" +} + +from ControlFlowNode n +select n.getLocation().getStartLine(), + count(n.getAPredecessor*()), // This helps order things sensibly + n, + getASuccessorOrNone(n) + diff --git a/cpp/ql/test/library-tests/question_mark_colon/question_mark_colon.c b/cpp/ql/test/library-tests/question_mark_colon/question_mark_colon.c new file mode 100644 index 000000000000..686394142c2b --- /dev/null +++ b/cpp/ql/test/library-tests/question_mark_colon/question_mark_colon.c @@ -0,0 +1,8 @@ + +char *f(void); + +void g(void) { + char *s; + s = f() ? : "str"; // the 'then' part is implictly the value of the condition (GNU C extension) + s = f() ? "a" : "b"; +} diff --git a/cpp/ql/test/library-tests/question_mark_colon/ternary.expected b/cpp/ql/test/library-tests/question_mark_colon/ternary.expected new file mode 100644 index 000000000000..e45f2eeda1a8 --- /dev/null +++ b/cpp/ql/test/library-tests/question_mark_colon/ternary.expected @@ -0,0 +1,2 @@ +| question_mark_colon.c:6:9:6:21 | ... ? ... : ... | question_mark_colon.c:6:9:6:9 | call to f | question_mark_colon.c:6:9:6:9 | call to f | question_mark_colon.c:6:17:6:21 | str | +| question_mark_colon.c:7:9:7:23 | ... ? ... : ... | question_mark_colon.c:7:9:7:9 | call to f | question_mark_colon.c:7:15:7:17 | a | question_mark_colon.c:7:21:7:23 | b | diff --git a/cpp/ql/test/library-tests/question_mark_colon/ternary.ql b/cpp/ql/test/library-tests/question_mark_colon/ternary.ql new file mode 100644 index 000000000000..5c212480003b --- /dev/null +++ b/cpp/ql/test/library-tests/question_mark_colon/ternary.ql @@ -0,0 +1,4 @@ +import cpp + +from ConditionalExpr ce +select ce, ce.getCondition(), ce.getThen(), ce.getElse() diff --git a/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaCompleteness.expected b/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaCompleteness.expected new file mode 100644 index 000000000000..f082a67fcf66 --- /dev/null +++ b/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaCompleteness.expected @@ -0,0 +1 @@ +| 0 | diff --git a/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaCompleteness.ql b/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaCompleteness.ql new file mode 100644 index 000000000000..97f70b54d01b --- /dev/null +++ b/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaCompleteness.ql @@ -0,0 +1,22 @@ +/** + * @name RangeSsa completeness test + * @description RangeSsa completeness test. If there is a + * RangeSsaDefinition for a variable that reaches a use of that + * variable then there must be a RangeSsaDefinition for that use + * @kind test + */ + +import cpp +import semmle.code.cpp.rangeanalysis.RangeSSA + +/* Count of number of uses of a LocalScopeVariable where no corresponding SSA definition exists, + but at least one SSA definition for that variable can reach that use. + Should always be zero *regardless* of the input */ +select +count(LocalScopeVariable v, Expr use | + exists(RangeSsaDefinition def, BasicBlock db, BasicBlock ub | + def.getAUse(v) = use and db.contains(def.getDefinition()) and ub.contains(use) | + db.getASuccessor+() = ub + ) + and not exists(RangeSsaDefinition def | def.getAUse(v) = use) +) \ No newline at end of file diff --git a/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaCorrespondence.expected b/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaCorrespondence.expected new file mode 100644 index 000000000000..f082a67fcf66 --- /dev/null +++ b/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaCorrespondence.expected @@ -0,0 +1 @@ +| 0 | diff --git a/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaCorrespondence.ql b/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaCorrespondence.ql new file mode 100644 index 000000000000..cdbe4297fe0f --- /dev/null +++ b/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaCorrespondence.ql @@ -0,0 +1,22 @@ +/** + * @name RangeSsa correspondence test + * @description RangeSsa correspondence test. For each reachable definition or parameter there should be a corresponding SSA definition. + * @kind test + */ + +import cpp +import semmle.code.cpp.rangeanalysis.RangeSSA + +/* Count of number of reachable definitions or parameters where the no corresponding SSA definition exists. + Should always be zero *regardless* of the input */ + +select +count(Variable v, ControlFlowNode def | var_definition(v, def) and not unreachable(def) and + not exists(RangeSsaDefinition d | d.getAVariable() = v and d.getDefinition() = def) +) ++ +count(Parameter p | + exists(p.getAnAccess()) + and + not exists(RangeSsaDefinition d | d.getAVariable() = p and d.getDefinition() = p.getFunction().getEntryPoint()) +) diff --git a/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaDefUsePairs.expected b/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaDefUsePairs.expected new file mode 100644 index 000000000000..90a131b70d02 --- /dev/null +++ b/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaDefUsePairs.expected @@ -0,0 +1,38 @@ +| test.c:2:31:72:1 | SSA definition | SSA def(x) | test.c:7:9:7:9 | x | +| test.c:14:5:14:13 | SSA definition | SSA def(z) | test.c:20:16:20:16 | z | +| test.c:14:5:14:14 | SSA definition | SSA phi(x) | test.c:14:9:14:9 | x | +| test.c:14:5:14:14 | SSA definition | SSA phi(x) | test.c:17:8:17:8 | x | +| test.c:14:5:14:14 | SSA definition | SSA phi(y) | test.c:14:13:14:13 | y | +| test.c:18:9:18:15 | SSA definition | SSA phi(x) | test.c:26:9:26:9 | x | +| test.c:31:5:31:10 | SSA definition | SSA def(z) | test.c:39:5:39:5 | z | +| test.c:31:5:31:11 | SSA definition | SSA phi(x) | test.c:31:10:31:10 | x | +| test.c:31:5:31:11 | SSA definition | SSA phi(z) | test.c:31:5:31:5 | z | +| test.c:34:11:34:11 | SSA definition | SSA phi(x) | test.c:34:11:34:11 | x | +| test.c:34:11:34:11 | SSA definition | SSA phi(y) | test.c:39:10:39:10 | y | +| test.c:34:18:37:5 | SSA definition | SSA phi(x) | test.c:36:9:36:9 | x | +| test.c:39:5:39:10 | SSA definition | SSA def(z) | test.c:47:5:47:5 | z | +| test.c:42:16:42:16 | SSA definition | SSA phi(j) | test.c:42:16:42:16 | j | +| test.c:42:16:42:16 | SSA definition | SSA phi(w) | test.c:47:10:47:10 | w | +| test.c:42:29:45:5 | SSA definition | SSA phi(j) | test.c:42:24:42:24 | j | +| test.c:50:16:50:16 | SSA definition | SSA phi(j) | test.c:50:16:50:16 | j | +| test.c:50:16:50:16 | SSA definition | SSA phi(x) | test.c:66:10:66:10 | x | +| test.c:50:16:50:16 | SSA definition | SSA phi(z) | test.c:52:12:52:12 | z | +| test.c:50:29:64:5 | SSA definition | SSA phi(j) | test.c:50:24:50:24 | j | +| test.c:51:9:51:14 | SSA definition | SSA def(y) | test.c:53:16:53:16 | y | +| test.c:64:5:64:5 | SSA definition | SSA phi(w) | test.c:66:18:66:18 | w | +| test.c:64:5:64:5 | SSA definition | SSA phi(y) | test.c:66:14:66:14 | y | +| test.c:64:5:64:5 | SSA definition | SSA phi(z) | test.c:66:5:66:5 | z | +| test.c:70:5:70:10 | SSA definition | SSA def(w) | test.c:71:12:71:12 | w | +| test.c:77:14:87:5 | SSA definition | SSA phi(a) | test.c:79:13:79:13 | a | +| test.c:80:13:80:18 | SSA definition | SSA def(c) | test.c:81:17:81:17 | c | +| test.c:83:9:84:18 | SSA definition | SSA phi(a) | test.c:83:13:83:13 | a | +| test.c:83:9:84:18 | SSA definition | SSA phi(b) | test.c:88:12:88:12 | b | +| test.c:83:9:84:18 | SSA definition | SSA phi(c) | test.c:86:20:86:20 | c | +| test.c:85:9:86:21 | SSA definition | SSA phi(a) | test.c:85:13:85:13 | a | +| test.c:91:33:97:1 | SSA definition | SSA def(cond) | test.c:93:9:93:12 | cond | +| test.c:96:5:96:11 | SSA definition | SSA phi(x) | test.c:96:9:96:9 | x | +| test.cpp:2:29:13:1 | SSA definition | SSA def(a) | test.cpp:4:14:4:14 | a | +| test.cpp:4:14:4:14 | SSA definition | SSA def(x) | test.cpp:5:17:5:17 | x | +| test.cpp:4:14:4:14 | SSA definition | SSA def(x) | test.cpp:6:9:6:9 | x | +| test.cpp:4:14:4:14 | SSA definition | SSA def(x) | test.cpp:7:9:7:9 | x | +| test.cpp:4:14:4:14 | SSA definition | SSA def(x) | test.cpp:8:13:8:13 | x | diff --git a/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaDefUsePairs.ql b/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaDefUsePairs.ql new file mode 100644 index 000000000000..762fcfa8a3b5 --- /dev/null +++ b/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaDefUsePairs.ql @@ -0,0 +1,12 @@ +/** + * @name RangeSsa def-use pairs test + * @description List all the uses for each RangeSsaDefinition + * @kind test + */ + +import cpp +import semmle.code.cpp.rangeanalysis.RangeSSA + +from RangeSsaDefinition def, LocalScopeVariable var, Expr use +where def.getAUse(var) = use +select def, def.toString(var), use diff --git a/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaDefinedByParameter.expected b/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaDefinedByParameter.expected new file mode 100644 index 000000000000..f806c34240f8 --- /dev/null +++ b/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaDefinedByParameter.expected @@ -0,0 +1,6 @@ +| test.c:2:31:72:1 | SSA definition | test.c:2:14:2:14 | x | +| test.c:2:31:72:1 | SSA definition | test.c:2:21:2:21 | w | +| test.c:2:31:72:1 | SSA definition | test.c:2:28:2:28 | z | +| test.c:74:19:89:1 | SSA definition | test.c:74:16:74:16 | a | +| test.c:91:33:97:1 | SSA definition | test.c:91:27:91:30 | cond | +| test.cpp:2:29:13:1 | SSA definition | test.cpp:2:26:2:26 | a | diff --git a/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaDefinedByParameter.ql b/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaDefinedByParameter.ql new file mode 100644 index 000000000000..eb6d07609b22 --- /dev/null +++ b/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaDefinedByParameter.ql @@ -0,0 +1,12 @@ +/** + * @name RangeSsa definedByParameter test + * @description check the results of the definedByParameter method + * @kind test + */ + +import cpp +import semmle.code.cpp.rangeanalysis.RangeSSA + +from RangeSsaDefinition def, Parameter p +where def.definedByParameter(p) +select def, p diff --git a/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaDominance.expected b/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaDominance.expected new file mode 100644 index 000000000000..f082a67fcf66 --- /dev/null +++ b/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaDominance.expected @@ -0,0 +1 @@ +| 0 | diff --git a/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaDominance.ql b/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaDominance.ql new file mode 100644 index 000000000000..c227af18dd8d --- /dev/null +++ b/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaDominance.ql @@ -0,0 +1,21 @@ +/** + * @name SSA dominance property test + * @description SSA dominance property test. SSA definitions *must* dominate all uses + * @kind test + */ + +import cpp +import semmle.code.cpp.rangeanalysis.RangeSSA + +/* Count of number of SSA def-use pairs where the defn does not dominate the use. + Should always be zero *regardless* of the input */ + +select +count(RangeSsaDefinition d, LocalScopeVariable v, Expr u | + d.getAUse(v) = u and + not exists(BasicBlock bd, BasicBlock bu | bd.contains((ControlFlowNode)d) and bu.contains(u) | + bbStrictlyDominates(bd, bu) + or + exists(int i, int j | bd = bu and bd.getNode(i) = d and bu.getNode(j) = u and i <= j) + ) +) \ No newline at end of file diff --git a/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaPhiInputs.expected b/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaPhiInputs.expected new file mode 100644 index 000000000000..77c46b679fe3 --- /dev/null +++ b/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaPhiInputs.expected @@ -0,0 +1,48 @@ +| 14 | SSA phi(x) | 7 | SSA phi(x) | +| 14 | SSA phi(x) | 10 | SSA phi(x) | +| 14 | SSA phi(y) | 8 | SSA def(y) | +| 14 | SSA phi(y) | 11 | SSA def(y) | +| 31 | SSA phi(x) | 26 | SSA phi(x) | +| 31 | SSA phi(y) | 18 | SSA def(y) | +| 31 | SSA phi(y) | 27 | SSA def(y) | +| 31 | SSA phi(z) | 23 | SSA def(z) | +| 31 | SSA phi(z) | 28 | SSA def(z) | +| 34 | SSA phi(x) | 31 | SSA phi(x) | +| 34 | SSA phi(x) | 36 | SSA def(x) | +| 34 | SSA phi(y) | 31 | SSA phi(y) | +| 34 | SSA phi(y) | 35 | SSA def(y) | +| 42 | SSA phi(j) | 42 | SSA def(j) | +| 42 | SSA phi(w) | 2 | SSA def(w) | +| 42 | SSA phi(w) | 44 | SSA def(w) | +| 42 | SSA phi(y) | 34 | SSA phi(y) | +| 42 | SSA phi(y) | 43 | SSA def(y) | +| 50 | SSA phi(j) | 50 | SSA def(j) | +| 50 | SSA phi(w) | 42 | SSA phi(w) | +| 50 | SSA phi(w) | 50 | SSA phi(w) | +| 50 | SSA phi(w) | 57 | SSA def(w) | +| 50 | SSA phi(w) | 60 | SSA def(w) | +| 50 | SSA phi(x) | 39 | SSA phi(x) | +| 50 | SSA phi(x) | 50 | SSA phi(x) | +| 50 | SSA phi(x) | 63 | SSA def(x) | +| 50 | SSA phi(y) | 42 | SSA phi(y) | +| 50 | SSA phi(y) | 50 | SSA phi(y) | +| 50 | SSA phi(y) | 51 | SSA def(y) | +| 50 | SSA phi(y) | 56 | SSA phi(y) | +| 50 | SSA phi(z) | 47 | SSA def(z) | +| 50 | SSA phi(z) | 50 | SSA phi(z) | +| 50 | SSA phi(z) | 53 | SSA phi(z) | +| 50 | SSA phi(z) | 59 | SSA phi(z) | +| 64 | SSA phi(w) | 50 | SSA phi(w) | +| 64 | SSA phi(w) | 54 | SSA def(w) | +| 64 | SSA phi(y) | 50 | SSA phi(y) | +| 64 | SSA phi(y) | 53 | SSA phi(y) | +| 64 | SSA phi(z) | 50 | SSA phi(z) | +| 64 | SSA phi(z) | 53 | SSA phi(z) | +| 77 | SSA phi(a) | 74 | SSA def(a) | +| 77 | SSA phi(c) | 83 | SSA phi(c) | +| 83 | SSA phi(a) | 79 | SSA phi(a) | +| 83 | SSA phi(b) | 78 | SSA def(b) | +| 83 | SSA phi(b) | 81 | SSA def(b) | +| 83 | SSA phi(c) | 77 | SSA phi(c) | +| 83 | SSA phi(c) | 80 | SSA def(c) | +| 96 | SSA phi(x) | 94 | SSA def(x) | diff --git a/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaPhiInputs.ql b/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaPhiInputs.ql new file mode 100644 index 000000000000..e460ef557bc8 --- /dev/null +++ b/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaPhiInputs.ql @@ -0,0 +1,14 @@ +/** + * @name RangeSsa phi-node inputs test + * @description List all the inputs for each SSA phi-node + * @kind test + */ + +import cpp +import semmle.code.cpp.rangeanalysis.RangeSSA + +from RangeSsaDefinition phi, LocalScopeVariable var, RangeSsaDefinition input, int philine, int inputline +where phi.getAPhiInput(var) = input and +philine = phi.getLocation().getStartLine() and +inputline = input.getLocation().getStartLine() +select philine, phi.toString(var), inputline, input.toString(var) diff --git a/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaUniqueness.expected b/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaUniqueness.expected new file mode 100644 index 000000000000..f082a67fcf66 --- /dev/null +++ b/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaUniqueness.expected @@ -0,0 +1 @@ +| 0 | diff --git a/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaUniqueness.ql b/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaUniqueness.ql new file mode 100644 index 000000000000..acdafeaf67b5 --- /dev/null +++ b/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/RangeSsaUniqueness.ql @@ -0,0 +1,17 @@ +/** + * @name SSA unique definition test + * @description SSA unique definition test. For each use there must be zero or one SSA definitions. + * @kind test + */ + +import cpp +import semmle.code.cpp.rangeanalysis.RangeSSA + +/* Count of number of uses where the number of SSA definitions exceeds one. + Should always be zero *regardless* of the input */ + +select +count(RangeSsaDefinition d1, RangeSsaDefinition d2, Expr u, LocalScopeVariable v | + d1.getAUse(v) = u and + d2.getAUse(v) = u and not d1 = d2 +) diff --git a/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/test.c b/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/test.c new file mode 100644 index 000000000000..2205a8f5b3b5 --- /dev/null +++ b/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/test.c @@ -0,0 +1,98 @@ + +int test(int x, int w, int z) { + int j; + long y = 50; + + // if-else, multiple statements in block + if (x > 0) { + y = 20; + z = 10; + } else { + y = 30; + } + + z = x + y; + + // if-else with return in one branch + if(x < 0) + y = 40; + else + return z; + + // this is not the start of a BB due to the return + z = 10; + + // single-branch if-else + if (x == 0) { + y = 60; + z = 10; + } + + z += x; + + // while loop + while(x > 0) { + y = 10; + x--; + } + + z += y; + + // for loop + for(j = 0; j < 10; j++) { + y = 0; + w = 10; + } + + z += w; + + // nested control flow + for(j = 0; j < 10; j++) { + y = 30; + if(z > 0) + if(y > 0) { + w = 0; + break; + } else { + w = 20; + } + else { + w = 10; + continue; + } + x = 0; + } + + z += x + y + w; + + // nested control-flow + + w = 40; + return w; +} + +void test2(int a) { + /* Some more complex flow control */ + int b, c; + for (;;) { + b = 10; + if (a > 100) { + c = 10; + b = c; + } + if (a == 10) + break; + if (a == 20) + return c; + } + return b; +} + +void partly_undefined(int cond) { + int x; + if (cond) { + x = 1; + } + use(x); +} + diff --git a/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/test.cpp b/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/test.cpp new file mode 100644 index 000000000000..43b6d2161ee5 --- /dev/null +++ b/cpp/ql/test/library-tests/rangeanalysis/RangeSSA/test.cpp @@ -0,0 +1,13 @@ + +bool test_references(int a) { + // x is a reference, so it only has one definition. + int &x = a; + int *ptr = &x; + if (x > 10) { + x--; + if (x < 5) { + return 1; + } + } + return 0; +} diff --git a/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/inline_assembly.c b/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/inline_assembly.c new file mode 100644 index 000000000000..430838d77363 --- /dev/null +++ b/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/inline_assembly.c @@ -0,0 +1,24 @@ +// The ASM statements are +// causing problems, because our SSA analysis does not notice that they +// might change the value of `x`. This was a latent bug that came out +// of the woodwork when we added support for statement expressions. + +int printf(const char *format, ...); + +int main() { + unsigned int x = 0, y; + y = 1; + + printf("x = %i y = %i\n", x, y); // 0, 1 + + // exchange x and y + asm volatile ( "xchg %0, %1\n" + : "+r" (x), "+a" (y) // outputs (x and y) + : + : + ); + + printf("x = %i y = %i\n", x, y); // 1, 0 (but without analysing the ASM: unknown, unknown) + + return 0; +} diff --git a/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/lowerBound.expected b/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/lowerBound.expected new file mode 100644 index 000000000000..ec0a7995410a --- /dev/null +++ b/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/lowerBound.expected @@ -0,0 +1,470 @@ +| 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: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: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: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: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: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: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 | 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 | 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 | 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.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 | diff --git a/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/lowerBound.ql b/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/lowerBound.ql new file mode 100644 index 000000000000..67da63679723 --- /dev/null +++ b/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/lowerBound.ql @@ -0,0 +1,4 @@ +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis + +from VariableAccess expr +select expr, lowerBound(expr) diff --git a/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/minmax.c b/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/minmax.c new file mode 100644 index 000000000000..460167ccb4c6 --- /dev/null +++ b/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/minmax.c @@ -0,0 +1,27 @@ +// semmle-extractor-options: --gnu_version 40400 +// Note: this file uses statement expressions, which are a GNU extension, +// so it has an options file to specify the compiler version. The statement +// expression extension is described here: +// https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html + +int printf(const char *format, ...); + +// The & operator is +// causing problems, because it disables SSA. Also, range analysis did not +// have support for the statement expression language feature that is used +// here. + +void minmax() +{ + int x = 1, y = 2, z = 3; + + printf("x = %i, y = %i, z = %i\n", x, y, z); // 1, 2, 3 + + z = ({ + int t = 0; + if (&x != &y) {t = x;} // t = 1 + t; + }); + + printf("x = %i, y = %i, z = %i\n", x, y, z); // 1, 2, 1 +} diff --git a/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/ternaryLower.expected b/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/ternaryLower.expected new file mode 100644 index 000000000000..7f0469925834 --- /dev/null +++ b/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/ternaryLower.expected @@ -0,0 +1,15 @@ +| test.c:154:10:154:40 | ... ? ... : ... | -1.0 | 1.0 | -1.0 | +| test.c:357:8:357:23 | ... ? ... : ... | 0.0 | 0.0 | 10.0 | +| test.c:358:8:358:24 | ... ? ... : ... | 0.0 | 10.0 | 0.0 | +| test.c:366:10:366:15 | ... ? ... : ... | 0.0 | 0.0 | 5.0 | +| test.c:367:10:367:17 | ... ? ... : ... | 0.0 | 0.0 | 500.0 | +| test.c:368:10:368:21 | ... ? ... : ... | 1.0 | 1.0 | 500.0 | +| test.c:369:10:369:36 | ... ? ... : ... | 0.0 | 1.0 | 5.0 | +| test.c:370:10:370:38 | ... ? ... : ... | 0.0 | 1.0 | 500.0 | +| test.c:371:10:371:39 | ... ? ... : ... | 1.0 | 1.0 | 500.0 | +| test.c:379:8:379:24 | ... ? ... : ... | 101.0 | 101.0 | 110.0 | +| test.c:380:8:380:25 | ... ? ... : ... | 101.0 | 110.0 | 101.0 | +| test.c:385:10:385:21 | ... ? ... : ... | 0.0 | 0.0 | 5.0 | +| 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 | diff --git a/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/ternaryLower.ql b/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/ternaryLower.ql new file mode 100644 index 000000000000..f4065881bcc9 --- /dev/null +++ b/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/ternaryLower.ql @@ -0,0 +1,5 @@ +import cpp +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis + +from ConditionalExpr ce +select ce, lowerBound(ce), lowerBound(ce.getThen()), lowerBound(ce.getElse()) diff --git a/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/ternaryUpper.expected b/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/ternaryUpper.expected new file mode 100644 index 000000000000..e2c21e309ae5 --- /dev/null +++ b/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/ternaryUpper.expected @@ -0,0 +1,15 @@ +| test.c:154:10:154:40 | ... ? ... : ... | 2.147483647E9 | 2.147483647E9 | -1.0 | +| test.c:357:8:357:23 | ... ? ... : ... | 99.0 | 99.0 | 10.0 | +| test.c:358:8:358:24 | ... ? ... : ... | 99.0 | 10.0 | 99.0 | +| test.c:366:10:366:15 | ... ? ... : ... | 299.0 | 299.0 | 5.0 | +| test.c:367:10:367:17 | ... ? ... : ... | 500.0 | 299.0 | 500.0 | +| test.c:368:10:368:21 | ... ? ... : ... | 300.0 | 300.0 | 500.0 | +| test.c:369:10:369:36 | ... ? ... : ... | 255.0 | 300.0 | 5.0 | +| test.c:370:10:370:38 | ... ? ... : ... | 500.0 | 300.0 | 500.0 | +| test.c:371:10:371:39 | ... ? ... : ... | 300.0 | 300.0 | 500.0 | +| test.c:379:8:379:24 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 110.0 | +| test.c:380:8:380:25 | ... ? ... : ... | 4.294967295E9 | 110.0 | 4.294967295E9 | +| test.c:385:10:385:21 | ... ? ... : ... | 4.294967295E9 | 4.294967295E9 | 5.0 | +| 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 | diff --git a/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/ternaryUpper.ql b/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/ternaryUpper.ql new file mode 100644 index 000000000000..409735829361 --- /dev/null +++ b/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/ternaryUpper.ql @@ -0,0 +1,5 @@ +import cpp +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis + +from ConditionalExpr ce +select ce, upperBound(ce), upperBound(ce.getThen()), upperBound(ce.getElse()) diff --git a/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/test.c b/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/test.c new file mode 100644 index 000000000000..fa294a678230 --- /dev/null +++ b/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/test.c @@ -0,0 +1,400 @@ +struct List { + struct List* next; +}; + +int test1(struct List* p) { + int count = 0; + for (; p; p = p->next) { + count = count+1; + } + return count; +} + +int test2(struct List* p) { + int count = 0; + for (; p; p = p->next) { + count = (count+1) % 10; + } + return count; +} + +int test3(struct List* p) { + int count = 0; + for (; p; p = p->next) { + count++; + count = count % 10; + } + return count; +} + +int test4() { + int i = 0; + int total = 0; + for (i = 0; i < 2; i = i+1) { + total += i; + } + return total + i; +} + +int test5() { + int i = 0; + int total = 0; + for (i = 0; i < 2; i++) { + total += i; + } + return total + i; +} + +int test6() { + int i = 0; + int total = 0; + for (i = 0; i+2 < 4; i = i+1) { + total += i; + } + return total + i; +} + +int test7(int i) { + if (i < 4) { + if (i < 5) { + return i; + } + } + return 1; +} + +int test8(int x, int y) { + if (-1000 < y && y < 10) { + if (x < y-2) { + return x; + } + } + return y; +} + +int test9(int x, int y) { + if (y == 0) { + if (x < 4) { + return 0; + } + } else { + if (x < 4) { + return 1; + } + } + return x; +} + +int test10(int x, int y) { + if (y > 7) { + if (x < y) { + return 0; + } + return x; + } + return 1; +} + +int test11(char *p) { + char c; + c = *p; + if (c != '\0') + *p++ = '\0'; + + if (c == ':') { + c = *p; + if (c != '\0') + *p++ = '\0'; + + if (c != ',') + return 1; + } + return 0; +} + +typedef unsigned long long size_type; + +size_type test12_helper() { + static size_type n = 0; + return n++; +} + +int test12() { + size_type Start = 0; + while (Start <= test12_helper()-1) + { + const size_type Length = test12_helper(); + Start += Length + 1; + } + + return 1; +} + +// Tests for overflow conditions. +int test13(char c, int i) { + unsigned char uc = c; + unsigned int x = 0; + unsigned int y = x-1; + int z = i+1; + return (double)(c + i + uc + x + y + z); +} + +// Regression test for ODASA-6013. +int test14(int x) { + int x0 = (int)(char)x; + int x1 = (int)(unsigned char)x; + int x2 = (int)(unsigned short)x; + int x3 = (int)(unsigned int)x; + char c0 = x; + unsigned short s0 = x; + return x0 + x1 + x2 + x3 + c0 + s0; +} + +long long test15(long long x) { + return (x > 0 && x == (int)x) ? x : -1; +} + +// Tests for unary operators. +int test_unary(int a) { + int total = 0; + + if (3 <= a && a <= 11) { + int b = +a; + int c = -a; + total += b+c; + } + if (0 <= a && a <= 11) { + int b = +a; + int c = -a; + total += b+c; + } + if (-7 <= a && a <= 11) { + int b = +a; + int c = -a; + total += b+c; + } + if (-7 <= a && a <= 1) { + int b = +a; + int c = -a; + total += b+c; + } + if (-7 <= a && a <= 0) { + int b = +a; + int c = -a; + total += b+c; + } + if (-7 <= a && a <= -2) { + int b = +a; + int c = -a; + total += b+c; + } + + return total; +} + + +// Tests for multiplication. +int test_mult01(int a, int 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; // -143 .. 253 + total += r; + } + if (3 <= a && a <= 11 && -13 <= b && b <= 0) { + int r = a*b; // -143 .. 0 + total += r; + } + if (3 <= a && a <= 11 && -13 <= b && b <= -7) { + int r = a*b; // -143 .. -21 + total += r; + } + + return total; +} + +// Tests for multiplication. +int test_mult02(int a, int b) { + int total = 0; + + if (0 <= a && a <= 11 && 5 <= b && b <= 23) { + int r = a*b; // 0 .. 253 + total += r; + } + if (0 <= a && a <= 11 && 0 <= b && b <= 23) { + int r = a*b; // 0 .. 253 + total += r; + } + if (0 <= a && a <= 11 && -13 <= b && b <= 23) { + int r = a*b; // -143 .. 253 + total += r; + } + if (0 <= a && a <= 11 && -13 <= b && b <= 0) { + int r = a*b; // -143 .. 0 + total += r; + } + if (0 <= a && a <= 11 && -13 <= b && b <= -7) { + int r = a*b; // -143 .. 0 + total += r; + } + + return total; +} + +// Tests for multiplication. +int test_mult03(int a, int b) { + int total = 0; + + if (-17 <= a && a <= 11 && 5 <= b && b <= 23) { + int r = a*b; // -391 .. 253 + total += r; + } + if (-17 <= a && a <= 11 && 0 <= b && b <= 23) { + int r = a*b; // -391 .. 253 + total += r; + } + if (-17 <= a && a <= 11 && -13 <= b && b <= 23) { + int r = a*b; // -391 .. 253 + total += r; + } + if (-17 <= a && a <= 11 && -13 <= b && b <= 0) { + int r = a*b; // -143 .. 221 + total += r; + } + if (-17 <= a && a <= 11 && -13 <= b && b <= -7) { + int r = a*b; // -143 .. 221 + total += r; + } + + return total; +} + +// Tests for multiplication. +int test_mult04(int a, int b) { + int total = 0; + + if (-17 <= a && a <= 0 && 5 <= b && b <= 23) { + int r = a*b; // -391 .. 0 + total += r; + } + if (-17 <= a && a <= 0 && 0 <= b && b <= 23) { + int r = a*b; // -391 .. 0 + total += r; + } + if (-17 <= a && a <= 0 && -13 <= b && b <= 23) { + int r = a*b; // -391 .. 221 + total += r; + } + if (-17 <= a && a <= 0 && -13 <= b && b <= 0) { + int r = a*b; // 0 .. 221 + total += r; + } + if (-17 <= a && a <= 0 && -13 <= b && b <= -7) { + int r = a*b; // 0 .. 221 + total += r; + } + + return total; +} + +// Tests for multiplication. +int test_mult05(int a, int b) { + int total = 0; + + if (-17 <= a && a <= -2 && 5 <= b && b <= 23) { + int r = a*b; // -391 .. -10 + total += r; + } + if (-17 <= a && a <= -2 && 0 <= b && b <= 23) { + int r = a*b; // -391 .. 0 + total += r; + } + if (-17 <= a && a <= -2 && -13 <= b && b <= 23) { + int r = a*b; // -391 .. 221 + total += r; + } + if (-17 <= a && a <= -2 && -13 <= b && b <= 0) { + int r = a*b; // 0 .. 221 + total += r; + } + if (-17 <= a && a <= -2 && -13 <= b && b <= -7) { + int r = a*b; // 14 .. 221 + total += r; + } + + return total; +} + +int test16(int x) { + int d, i = 0; + if (x < 0) { + return -1; + } + + while (i < 3) { + i++; + } + d = i; + if (x < 0) { // Comparison is always false. + if (d > -x) { // Unreachable code. + return 1; + } + } + return 0; +} + +// Test ternary expression upper bounds. +unsigned int test_ternary01(unsigned int x) { + unsigned int y1, y2, y3, y4, y5, y6, y7, y8; + y1 = x < 100 ? x : 10; // y1 < 100 + y2 = x >= 100 ? 10 : x; // y2 < 100 + y3 = 0; + y4 = 0; + y5 = 0; + y6 = 0; + y7 = 0; + y8 = 0; + if (x < 300) { + y3 = x ?: 5; // y3 < 300 + y4 = x ?: 500; // y4 <= 500 + y5 = (x+1) ?: 500; // y5 <= 300 + y6 = ((unsigned char)(x+1)) ?: 5; // y6 < 256 + y7 = ((unsigned char)(x+1)) ?: 500; // y7 <= 500 + y8 = ((unsigned short)(x+1)) ?: 500; // y8 <= 300 + } + return y1 + y2 + y3 + y4 + y5 + y6 + y7 + y8; +} + +// Test ternary expression lower bounds. +unsigned int test_ternary02(unsigned int x) { + unsigned int y1, y2, y3, y4, y5; + y1 = x > 100 ? x : 110; // y1 > 100 + y2 = x <= 100 ? 110 : x; // y2 > 100 + y3 = 1000; + y4 = 1000; + y5 = 1000; + if (x >= 300) { + y3 = (x-300) ?: 5; // y3 >= 0 + y4 = (x-200) ?: 5; // y4 >= 100 + y5 = ((unsigned char)(x-200)) ?: 5; // y6 >= 0 + } + return y1 + y2 + y3 + y4 + y5; +} + +// Test the comma expression. +unsigned int test_comma01(unsigned int x) { + unsigned int y = x < 100 ? x : 100; + unsigned int y1; + unsigned int y2; + y1 = (++y, y); + y2 = (y++, y += 3, y); + return y1 + y2; +} diff --git a/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/test.cpp b/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/test.cpp new file mode 100644 index 000000000000..9a930772b8d7 --- /dev/null +++ b/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/test.cpp @@ -0,0 +1,70 @@ +template +class vector { +public: + T& operator[](int); + const T& operator[](int) const; +}; + +int test1(vector vec, int b) { + int x = -1; + if (b) { + x = vec[3]; + } + return x; +} + +// Regression test for ODASA-6013. +int test2(int x) { + int x0 = static_cast(x); + return x0; +} + +// Tests for conversion to bool +bool test3(bool b, int x, int y) { + // The purpose the assignments to `x` below is to generate a lot of + // potential upper and lower bounds for `x`, so that the logic in + // boolConversionLowerBound and boolConversionUpperBound gets exercized. + if (y == 0) { + x = 0; + } + if (y == -1) { + x = -1; + } + if (y == 1) { + x = 1; + } + if (y == -128) { + x = -128; + } + if (y == 128) { + x = 128; + } + if (y == -1024) { + x = -1024; + } + if (y == 1024) { + x = 1024; + } + + int t = 0; + + if (x == 0) { + bool xb = (bool)x; // (bool)x == false + t += (int)xb; + } + + if (x > 0) { + bool xb = (bool)x; // (bool)x == true + t += (int)xb; + } + + if (x < 0) { + bool xb = (bool)x; // (bool)x == true + t += (int)xb; + } + + bool xb = (bool)x; // Value of (bool)x is unknown. + t += (int)xb; + + return b || (bool)t; +} diff --git a/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/upperBound.expected b/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/upperBound.expected new file mode 100644 index 000000000000..c61da12bacbe --- /dev/null +++ b/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/upperBound.expected @@ -0,0 +1,470 @@ +| 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 | 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 | 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 | 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 | 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 | 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 | 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: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: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: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.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 | diff --git a/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/upperBound.ql b/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/upperBound.ql new file mode 100644 index 000000000000..341e5784aa36 --- /dev/null +++ b/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/upperBound.ql @@ -0,0 +1,4 @@ +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis + +from VariableAccess expr +select expr, upperBound(expr) diff --git a/cpp/ql/test/library-tests/reachability/reachability.c b/cpp/ql/test/library-tests/reachability/reachability.c new file mode 100644 index 000000000000..aa512271069b --- /dev/null +++ b/cpp/ql/test/library-tests/reachability/reachability.c @@ -0,0 +1,86 @@ + +int x; + +void f1(void) { + x = 1; + return; + x = 2; +} + +void f2(void) { + if (1) { + x = 1; + } else { + x = 2; + } + + if (0) { + x = 1; + } else { + x = 2; + } +} + +void f3(void) { + x = 1; + + while (1) { + x = 2; + } + + x = 3; +} + +void f4(void) { + x = 1; + + while (1) { + x = 2; + break; + } + + x = 3; +} + +void f5(void) { + x = 1; + + for (;;) { + x = 2; + } + + x = 3; +} + +void f6(void) { + x = 1; + + for (;;) { + x = 2; + goto out; + } + +out: + x = 3; +} + +void f7(void) { + x = 1; + +start: + switch (0) { + case 0: + goto start; + default: + return; + } +} + +void f8(void) { + x = 1; + +start: + goto start; + return; +} + diff --git a/cpp/ql/test/library-tests/reachability/reachability.expected b/cpp/ql/test/library-tests/reachability/reachability.expected new file mode 100644 index 000000000000..9b75e614d1d6 --- /dev/null +++ b/cpp/ql/test/library-tests/reachability/reachability.expected @@ -0,0 +1,29 @@ +| reachability.c:7:5:7:5 | x | +| reachability.c:7:5:7:9 | ... = ... | +| reachability.c:7:5:7:10 | ExprStmt | +| reachability.c:7:9:7:9 | 2 | +| reachability.c:13:12:15:5 | { ... } | +| reachability.c:14:9:14:9 | x | +| reachability.c:14:9:14:13 | ... = ... | +| reachability.c:14:9:14:14 | ExprStmt | +| reachability.c:14:13:14:13 | 2 | +| reachability.c:17:12:19:5 | { ... } | +| reachability.c:18:9:18:9 | x | +| reachability.c:18:9:18:13 | ... = ... | +| reachability.c:18:9:18:14 | ExprStmt | +| reachability.c:18:13:18:13 | 1 | +| reachability.c:24:6:24:7 | f3 | +| reachability.c:31:5:31:5 | x | +| reachability.c:31:5:31:9 | ... = ... | +| reachability.c:31:5:31:10 | ExprStmt | +| reachability.c:31:9:31:9 | 3 | +| reachability.c:45:6:45:7 | f5 | +| reachability.c:52:5:52:5 | x | +| reachability.c:52:5:52:9 | ... = ... | +| reachability.c:52:5:52:10 | ExprStmt | +| reachability.c:52:9:52:9 | 3 | +| reachability.c:67:6:67:7 | f7 | +| reachability.c:74:5:74:12 | default: | +| reachability.c:75:9:75:15 | return ... | +| reachability.c:79:6:79:7 | f8 | +| reachability.c:84:5:84:11 | return ... | diff --git a/cpp/ql/test/library-tests/reachability/reachability.ql b/cpp/ql/test/library-tests/reachability/reachability.ql new file mode 100644 index 000000000000..f7984b3f9f70 --- /dev/null +++ b/cpp/ql/test/library-tests/reachability/reachability.ql @@ -0,0 +1,6 @@ +import cpp + +from ControlFlowNode n +where not reachable(n) +select n + diff --git a/cpp/ql/test/library-tests/rvalueCast/default.cpp b/cpp/ql/test/library-tests/rvalueCast/default.cpp new file mode 100644 index 000000000000..f23be835a4c1 --- /dev/null +++ b/cpp/ql/test/library-tests/rvalueCast/default.cpp @@ -0,0 +1,7 @@ +void def(void) { + int a = 1; + int *p = &a; + p = &(int)a; +} + +// semmle-extractor-options: --microsoft diff --git a/cpp/ql/test/library-tests/rvalueCast/diag.expected b/cpp/ql/test/library-tests/rvalueCast/diag.expected new file mode 100644 index 000000000000..5522f5a600c4 --- /dev/null +++ b/cpp/ql/test/library-tests/rvalueCast/diag.expected @@ -0,0 +1,2 @@ +| enabled.cpp:4:9:4:9 | expression must be an lvalue or a function designator | 4 | expr_not_an_lvalue_or_function_designator | +| file://:0:0:0:0 | There was an error during this compilation | 4 | Error occurred | diff --git a/cpp/ql/test/library-tests/rvalueCast/diag.ql b/cpp/ql/test/library-tests/rvalueCast/diag.ql new file mode 100644 index 000000000000..53e8ea5de9da --- /dev/null +++ b/cpp/ql/test/library-tests/rvalueCast/diag.ql @@ -0,0 +1,4 @@ +import cpp + +from Diagnostic d +select d, d.getSeverity(), d.getTag() diff --git a/cpp/ql/test/library-tests/rvalueCast/disabled.cpp b/cpp/ql/test/library-tests/rvalueCast/disabled.cpp new file mode 100644 index 000000000000..49955ef9a70d --- /dev/null +++ b/cpp/ql/test/library-tests/rvalueCast/disabled.cpp @@ -0,0 +1,7 @@ +void disabled(void) { + int a = 1; + int *p = &a; + p = &(int)a; +} + +// semmle-extractor-options: --microsoft /Zc:rvalueCast- diff --git a/cpp/ql/test/library-tests/rvalueCast/enabled.cpp b/cpp/ql/test/library-tests/rvalueCast/enabled.cpp new file mode 100644 index 000000000000..d27225327767 --- /dev/null +++ b/cpp/ql/test/library-tests/rvalueCast/enabled.cpp @@ -0,0 +1,7 @@ +void enabled(void) { + int a = 1; + int *p = &a; + p = &(int)a; +} + +// semmle-extractor-options: --microsoft /Zc:rvalueCast --expect_errors diff --git a/cpp/ql/test/library-tests/rvalueCast/rvalueCast.expected b/cpp/ql/test/library-tests/rvalueCast/rvalueCast.expected new file mode 100644 index 000000000000..c6c09b54afa3 --- /dev/null +++ b/cpp/ql/test/library-tests/rvalueCast/rvalueCast.expected @@ -0,0 +1,3 @@ +| default.cpp:1:6:1:8 | def | 8 | 0 | +| disabled.cpp:1:6:1:13 | disabled | 8 | 0 | +| enabled.cpp:1:6:1:12 | enabled | 6 | 1 | diff --git a/cpp/ql/test/library-tests/rvalueCast/rvalueCast.ql b/cpp/ql/test/library-tests/rvalueCast/rvalueCast.ql new file mode 100644 index 000000000000..9225a2ce8709 --- /dev/null +++ b/cpp/ql/test/library-tests/rvalueCast/rvalueCast.ql @@ -0,0 +1,7 @@ +import cpp + +from Function f +select f, + count(Expr e | f = e.getEnclosingFunction()), + count(ErrorExpr e | f = e.getEnclosingFunction()) + diff --git a/cpp/ql/test/library-tests/rvaluerefs/rvaluerefs.cpp b/cpp/ql/test/library-tests/rvaluerefs/rvaluerefs.cpp new file mode 100644 index 000000000000..0fc70a39b828 --- /dev/null +++ b/cpp/ql/test/library-tests/rvaluerefs/rvaluerefs.cpp @@ -0,0 +1,2 @@ +void x(int&& rvalue) {} +void x(int& lvalue) {} diff --git a/cpp/ql/test/library-tests/rvaluerefs/xargs.expected b/cpp/ql/test/library-tests/rvaluerefs/xargs.expected new file mode 100644 index 000000000000..828a02b0d8c1 --- /dev/null +++ b/cpp/ql/test/library-tests/rvaluerefs/xargs.expected @@ -0,0 +1,2 @@ +| 1 | int && | +| 2 | int & | diff --git a/cpp/ql/test/library-tests/rvaluerefs/xargs.ql b/cpp/ql/test/library-tests/rvaluerefs/xargs.ql new file mode 100644 index 000000000000..7d505884ad95 --- /dev/null +++ b/cpp/ql/test/library-tests/rvaluerefs/xargs.ql @@ -0,0 +1,5 @@ +import cpp + +from ParameterDeclarationEntry pde +where pde.getFunctionDeclarationEntry().getFunction().hasName("x") +select pde.getLocation().getStartLine(), pde.getType().toString() diff --git a/cpp/ql/test/library-tests/sal/sal.expected b/cpp/ql/test/library-tests/sal/sal.expected new file mode 100644 index 000000000000..4792adc3d7e6 --- /dev/null +++ b/cpp/ql/test/library-tests/sal/sal.expected @@ -0,0 +1,15 @@ +| test.cpp:6:3:6:19 | __in_ecount(x) | test.cpp:6:33:6:36 | str1 | +| test.cpp:7:3:7:6 | __in | test.cpp:7:12:7:15 | len1 | +| test.cpp:8:3:8:19 | __in_ecount(x) | test.cpp:8:32:8:35 | str2 | +| test.cpp:9:3:9:6 | __in | test.cpp:9:12:9:15 | len2 | +| test.cpp:10:3:10:10 | __in_opt | test.cpp:10:21:10:29 | opt_param | +| test.cpp:11:3:11:12 | __reserved | test.cpp:11:23:11:30 | reserved | +| test.cpp:13:1:13:13 | _Ret_notnull_ | test.cpp:16:7:16:8 | f1 | +| test.cpp:14:1:14:21 | _Must_inspect_result_ | test.cpp:16:7:16:8 | f1 | +| test.cpp:15:1:15:14 | _Check_return_ | test.cpp:16:7:16:8 | f1 | +| test.cpp:16:10:16:14 | _Out_ | test.cpp:16:44:16:44 | x | +| test.cpp:16:16:16:37 | _Result_zeroonfailure_ | test.cpp:16:44:16:44 | x | +| test.cpp:18:1:18:13 | _Ret_notnull_ | test.cpp:22:7:22:8 | f2 | +| test.cpp:21:1:21:14 | _Check_return_ | test.cpp:22:7:22:8 | f2 | +| test.cpp:23:5:23:9 | _Out_ | test.cpp:25:6:25:6 | x | +| test.cpp:29:20:29:23 | _In_ | test.cpp:29:38:29:38 | a | diff --git a/cpp/ql/test/library-tests/sal/sal.h b/cpp/ql/test/library-tests/sal/sal.h new file mode 100644 index 000000000000..2da0afa19516 --- /dev/null +++ b/cpp/ql/test/library-tests/sal/sal.h @@ -0,0 +1,12 @@ + +// sufficient macros for the test to compile +#define __in +#define __in_ecount(x) +#define __in_opt +#define __reserved +#define _Ret_notnull_ +#define _Must_inspect_result_ +#define _Check_return_ +#define _Out_ +#define _Result_zeroonfailure_ +#define _In_ diff --git a/cpp/ql/test/library-tests/sal/sal.ql b/cpp/ql/test/library-tests/sal/sal.ql new file mode 100644 index 000000000000..eb4908bae9ea --- /dev/null +++ b/cpp/ql/test/library-tests/sal/sal.ql @@ -0,0 +1,4 @@ +import Microsoft.SAL + +from SALAnnotation a +select a, a.getDeclaration() diff --git a/cpp/ql/test/library-tests/sal/test.cpp b/cpp/ql/test/library-tests/sal/test.cpp new file mode 100644 index 000000000000..f98bb4572aee --- /dev/null +++ b/cpp/ql/test/library-tests/sal/test.cpp @@ -0,0 +1,29 @@ +#include "sal.h" + +#define VOID_PTR void * + +int sal_strncmp( + __in_ecount(len1) const char *str1, + __in int len1, + __in_ecount(len2) const char str2, + __in int len2, + __in_opt VOID_PTR opt_param, + __reserved VOID_PTR reserved); + +_Ret_notnull_ +_Must_inspect_result_ +_Check_return_ +char *f1(_Out_ _Result_zeroonfailure_ int *x); + +_Ret_notnull_ +// Next line intentionally left blank + +_Check_return_ +char *f2( + _Out_ + int + *x); + +// [KNOWN BUG] Because of ODASA-5806, we can't see that the annotation belongs +// on `x`. +void f3(void (*fp)(_In_ int *x), int a); diff --git a/cpp/ql/test/library-tests/scanf/ms.cpp b/cpp/ql/test/library-tests/scanf/ms.cpp new file mode 100644 index 000000000000..6e648f6acfca --- /dev/null +++ b/cpp/ql/test/library-tests/scanf/ms.cpp @@ -0,0 +1,20 @@ +// semmle-extractor-options: --microsoft --edg --target --edg win64 +/** standard library functions */ +typedef __int32 int32_t; +typedef __int64 int64_t; + +int sscanf(const char *s, const char *format, ...); +int printf(const char *format, ...); + +/** test program */ + +void test() +{ + // I64 is a Microsoft specific size prefix + { + __int64 i64; + + sscanf("9999999999", "%I64i", &i64); // [MISSING FROM SCANFFORMATLITERAL] + printf("= %I64i\n", i64); + } +} diff --git a/cpp/ql/test/library-tests/scanf/scanfFormatLiteral.expected b/cpp/ql/test/library-tests/scanf/scanfFormatLiteral.expected new file mode 100644 index 000000000000..263ea54b8cd3 --- /dev/null +++ b/cpp/ql/test/library-tests/scanf/scanfFormatLiteral.expected @@ -0,0 +1,5 @@ +| test.c:18:2:18:6 | call to scanf | 0 | s | 0 | 0 | +| test.c:19:2:19:7 | call to fscanf | 0 | s | 10 | 10 | +| test.c:19:2:19:7 | call to fscanf | 1 | i | 0 | 0 | +| test.c:20:2:20:7 | call to sscanf | 0 | s | 0 | 0 | +| test.c:21:2:21:8 | call to swscanf | 0 | s | 10 | 10 | diff --git a/cpp/ql/test/library-tests/scanf/scanfFormatLiteral.ql b/cpp/ql/test/library-tests/scanf/scanfFormatLiteral.ql new file mode 100644 index 000000000000..a77182d5adec --- /dev/null +++ b/cpp/ql/test/library-tests/scanf/scanfFormatLiteral.ql @@ -0,0 +1,4 @@ +import semmle.code.cpp.commons.Scanf + +from ScanfFormatLiteral sfl, int n +select sfl.getUse(), n, sfl.getConversionChar(n), sum(sfl.getMaxWidth(n)), sum(sfl.getMaxConvertedLength(n)) diff --git a/cpp/ql/test/library-tests/scanf/scanfFunctionCall.expected b/cpp/ql/test/library-tests/scanf/scanfFunctionCall.expected new file mode 100644 index 000000000000..b0dce385b7bb --- /dev/null +++ b/cpp/ql/test/library-tests/scanf/scanfFunctionCall.expected @@ -0,0 +1,5 @@ +| ms.cpp:17:3:17:8 | call to sscanf | 0 | 1 | ms.cpp:17:24:17:30 | %I64i | non-wide | +| test.c:18:2:18:6 | call to scanf | 0 | 0 | test.c:18:8:18:11 | %s | non-wide | +| test.c:19:2:19:7 | call to fscanf | 0 | 1 | test.c:19:15:19:23 | %10s %i | non-wide | +| test.c:20:2:20:7 | call to sscanf | 0 | 1 | test.c:20:19:20:28 | %*i%s%*s | non-wide | +| test.c:21:2:21:8 | call to swscanf | 0 | 1 | test.c:21:21:21:26 | %10s | wide | diff --git a/cpp/ql/test/library-tests/scanf/scanfFunctionCall.ql b/cpp/ql/test/library-tests/scanf/scanfFunctionCall.ql new file mode 100644 index 000000000000..4ead3b006e45 --- /dev/null +++ b/cpp/ql/test/library-tests/scanf/scanfFunctionCall.ql @@ -0,0 +1,5 @@ +import semmle.code.cpp.commons.Scanf + +from ScanfFunctionCall sfc, string wide +where if sfc.isWideCharDefault() then wide = "wide" else wide = "non-wide" +select sfc, sum(sfc.getInputParameterIndex()), sfc.getFormatParameterIndex(), sfc.getFormat(), wide diff --git a/cpp/ql/test/library-tests/scanf/test.c b/cpp/ql/test/library-tests/scanf/test.c new file mode 100644 index 000000000000..c378eec72220 --- /dev/null +++ b/cpp/ql/test/library-tests/scanf/test.c @@ -0,0 +1,24 @@ +/** standard printf functions */ + +#define FILE void +typedef unsigned int wchar_t; + +int scanf(const char *format, ...); +int fscanf(FILE *stream, const char *format, ...); +int sscanf(const char *s, const char *format, ...); +int swscanf(const wchar_t* ws, const wchar_t* format, ...); + +int main(int argc, char *argv[]) +{ + char buffer[256]; + wchar_t wbuffer[256]; + FILE *file; + int i; + + scanf("%s", buffer); + fscanf(file, "%10s %i", buffer, i); + sscanf("Hello.", "%*i%s%*s", buffer); + swscanf(L"Hello.", "%10s", wbuffer); + + return 0; +} \ No newline at end of file diff --git a/cpp/ql/test/library-tests/scopes/includes/a.cpp b/cpp/ql/test/library-tests/scopes/includes/a.cpp new file mode 100644 index 000000000000..fd89b4b7e70e --- /dev/null +++ b/cpp/ql/test/library-tests/scopes/includes/a.cpp @@ -0,0 +1,10 @@ +#include "h.h" + +extern "C" { +#include "h2.h" +} + +extern "C" { +extern "C++" { +#include "h3.h" + diff --git a/cpp/ql/test/library-tests/scopes/includes/b.cpp b/cpp/ql/test/library-tests/scopes/includes/b.cpp new file mode 100644 index 000000000000..490d14a1de3a --- /dev/null +++ b/cpp/ql/test/library-tests/scopes/includes/b.cpp @@ -0,0 +1,13 @@ + +namespace { + +#include "h.h" + +} + +#include "h2.h" + +extern "C" { +extern "C" { +#include "h3.h" + diff --git a/cpp/ql/test/library-tests/scopes/includes/function_info.expected b/cpp/ql/test/library-tests/scopes/includes/function_info.expected new file mode 100644 index 000000000000..05bf1624279e --- /dev/null +++ b/cpp/ql/test/library-tests/scopes/includes/function_info.expected @@ -0,0 +1,11 @@ +| file://:0:0:0:0 | operator= | file://:0:0:0:0 | (global namespace) | false | +| file://:0:0:0:0 | operator= | file://:0:0:0:0 | (global namespace) | false | +| h2.h:2:12:2:15 | h2_1 | file://:0:0:0:0 | (global namespace) | true | +| h2.h:3:5:3:8 | h2_2 | file://:0:0:0:0 | (global namespace) | false | +| h2.h:3:5:3:8 | h2_2 | file://:0:0:0:0 | (global namespace) | true | +| h3.h:2:5:2:8 | h3_1 | file://:0:0:0:0 | (global namespace) | false | +| h3.h:2:5:2:8 | h3_1 | file://:0:0:0:0 | (global namespace) | true | +| h3.h:4:5:4:8 | h3_2 | file://:0:0:0:0 | (global namespace) | true | +| h3.h:6:5:6:8 | h3_3 | file://:0:0:0:0 | (global namespace) | false | +| h.h:2:14:2:15 | h1 | b.cpp:2:1:2:9 | (unnamed namespace) | false | +| h.h:2:14:2:15 | h1 | file://:0:0:0:0 | (global namespace) | false | diff --git a/cpp/ql/test/library-tests/scopes/includes/function_info.ql b/cpp/ql/test/library-tests/scopes/includes/function_info.ql new file mode 100644 index 000000000000..1ff5d3f64258 --- /dev/null +++ b/cpp/ql/test/library-tests/scopes/includes/function_info.ql @@ -0,0 +1,7 @@ +import cpp + +from Function f, boolean cLinkage +where if f.hasCLinkage() + then cLinkage = true + else cLinkage = false +select f, f.getNamespace(), cLinkage diff --git a/cpp/ql/test/library-tests/scopes/includes/h.h b/cpp/ql/test/library-tests/scopes/includes/h.h new file mode 100644 index 000000000000..7364a2e96d35 --- /dev/null +++ b/cpp/ql/test/library-tests/scopes/includes/h.h @@ -0,0 +1,3 @@ + +extern void *h1 (void); + diff --git a/cpp/ql/test/library-tests/scopes/includes/h2.h b/cpp/ql/test/library-tests/scopes/includes/h2.h new file mode 100644 index 000000000000..6978a86e9042 --- /dev/null +++ b/cpp/ql/test/library-tests/scopes/includes/h2.h @@ -0,0 +1,4 @@ + +static int h2_1 (void); +int h2_2 (void); + diff --git a/cpp/ql/test/library-tests/scopes/includes/h3.h b/cpp/ql/test/library-tests/scopes/includes/h3.h new file mode 100644 index 000000000000..c951a4e7f954 --- /dev/null +++ b/cpp/ql/test/library-tests/scopes/includes/h3.h @@ -0,0 +1,7 @@ + +int h3_1(void); +} +int h3_2(void); +} +int h3_3(void); + diff --git a/cpp/ql/test/library-tests/scopes/parents/parents.cpp b/cpp/ql/test/library-tests/scopes/parents/parents.cpp new file mode 100644 index 000000000000..3e0f66aa52c5 --- /dev/null +++ b/cpp/ql/test/library-tests/scopes/parents/parents.cpp @@ -0,0 +1,19 @@ + +namespace foo { + namespace bar { + void f(int i) { + int j; + try { + for (i = 0; i < 3; i++) { + int k; + } + } + catch (int e) { + } + } + } +} + + + + diff --git a/cpp/ql/test/library-tests/scopes/parents/parents.expected b/cpp/ql/test/library-tests/scopes/parents/parents.expected new file mode 100644 index 000000000000..523d0884393b --- /dev/null +++ b/cpp/ql/test/library-tests/scopes/parents/parents.expected @@ -0,0 +1,18 @@ +| 0 | file://:0:0:0:0 | (global namespace) | file://:0:0:0:0 | __va_list_tag | +| 0 | file://:0:0:0:0 | (global namespace) | parents.cpp:2:11:2:13 | foo | +| 1 | file://:0:0:0:0 | __va_list_tag | file://:0:0:0:0 | fp_offset | +| 1 | file://:0:0:0:0 | __va_list_tag | file://:0:0:0:0 | gp_offset | +| 1 | file://:0:0:0:0 | __va_list_tag | file://:0:0:0:0 | operator= | +| 1 | file://:0:0:0:0 | __va_list_tag | file://:0:0:0:0 | operator= | +| 1 | file://:0:0:0:0 | __va_list_tag | file://:0:0:0:0 | overflow_arg_area | +| 1 | file://:0:0:0:0 | __va_list_tag | file://:0:0:0:0 | reg_save_area | +| 1 | parents.cpp:2:11:2:13 | foo | parents.cpp:3:13:3:15 | foo::bar | +| 1 | parents.cpp:3:13:3:15 | foo::bar | parents.cpp:4:10:4:10 | f | +| 1 | parents.cpp:4:10:4:10 | f | parents.cpp:4:16:4:16 | i | +| 1 | parents.cpp:4:10:4:10 | f | parents.cpp:4:19:13:5 | { ... } | +| 1 | parents.cpp:4:19:13:5 | { ... } | parents.cpp:5:11:5:11 | j | +| 1 | parents.cpp:4:19:13:5 | { ... } | parents.cpp:6:11:10:7 | { ... } | +| 1 | parents.cpp:4:19:13:5 | { ... } | parents.cpp:11:21:12:7 | { ... } | +| 1 | parents.cpp:6:11:10:7 | { ... } | parents.cpp:7:9:9:9 | for(...;...;...) ... | +| 1 | parents.cpp:6:11:10:7 | { ... } | parents.cpp:7:33:9:9 | { ... } | +| 1 | parents.cpp:7:33:9:9 | { ... } | parents.cpp:8:15:8:15 | k | diff --git a/cpp/ql/test/library-tests/scopes/parents/parents.ql b/cpp/ql/test/library-tests/scopes/parents/parents.ql new file mode 100644 index 000000000000..a917d89e38e6 --- /dev/null +++ b/cpp/ql/test/library-tests/scopes/parents/parents.ql @@ -0,0 +1,8 @@ +import cpp + +from Element parent, Element child +where parent = child.getParentScope() +select count(parent.getParentScope()), // For sensible output ordering + parent, + child + diff --git a/cpp/ql/test/library-tests/scopes/scopes/Scopes1.expected b/cpp/ql/test/library-tests/scopes/scopes/Scopes1.expected new file mode 100644 index 000000000000..01915a9253ed --- /dev/null +++ b/cpp/ql/test/library-tests/scopes/scopes/Scopes1.expected @@ -0,0 +1,71 @@ +| file://:0:0:0:0 | (global namespace) | GlobalNamespace | file://:0:0:0:0 | operator delete[] | | +| file://:0:0:0:0 | (global namespace) | GlobalNamespace | file://:0:0:0:0 | operator new[] | | +| file://:0:0:0:0 | (global namespace) | GlobalNamespace | scopes.cpp:2:7:2:7 | A | Class | +| file://:0:0:0:0 | (global namespace) | GlobalNamespace | scopes.cpp:7:7:7:7 | B | Class | +| file://:0:0:0:0 | (global namespace) | GlobalNamespace | scopes.cpp:11:7:11:7 | C | Class | +| file://:0:0:0:0 | (global namespace) | GlobalNamespace | scopes.cpp:15:7:15:7 | D | Class | +| file://:0:0:0:0 | (global namespace) | GlobalNamespace | scopes.cpp:35:6:35:6 | f | | +| file://:0:0:0:0 | (global namespace) | GlobalNamespace | scopes.cpp:46:8:46:8 | S | Class | +| file://:0:0:0:0 | (global namespace) | GlobalNamespace | scopes.cpp:53:6:53:6 | g | | +| file://:0:0:0:0 | (global namespace) | GlobalNamespace | scopes.cpp:58:7:58:10 | Name | Class | +| file://:0:0:0:0 | (global namespace) | GlobalNamespace | scopes.cpp:62:7:62:11 | Table | Class | +| file://:0:0:0:0 | (global namespace) | GlobalNamespace | scopes.cpp:72:16:72:21 | strlen | | +| file://:0:0:0:0 | (global namespace) | GlobalNamespace | scopes.cpp:74:11:74:13 | One | Namespace | +| file://:0:0:0:0 | __va_list_tag | Class | file://:0:0:0:0 | fp_offset | | +| file://:0:0:0:0 | __va_list_tag | Class | file://:0:0:0:0 | gp_offset | | +| file://:0:0:0:0 | __va_list_tag | Class | file://:0:0:0:0 | operator= | | +| file://:0:0:0:0 | __va_list_tag | Class | file://:0:0:0:0 | operator= | | +| file://:0:0:0:0 | __va_list_tag | Class | file://:0:0:0:0 | overflow_arg_area | | +| file://:0:0:0:0 | __va_list_tag | Class | file://:0:0:0:0 | reg_save_area | | +| scopes.cpp:2:7:2:7 | A | Class | scopes.cpp:2:7:2:7 | operator= | | +| 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 | 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 | 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 | +| scopes.cpp:21:7:21:10 | E | NestedClass | scopes.cpp:21:10:21:10 | operator= | | +| scopes.cpp:21:7:21:10 | E | NestedClass | scopes.cpp:21:10:21:10 | operator= | | +| scopes.cpp:21:7:21:10 | E | NestedClass | scopes.cpp:25:8:25:8 | G | NestedClass | +| 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 | 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 | | +| 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: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 | 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 | | +| scopes.cpp:62:7:62:11 | Table | Class | scopes.cpp:62:7:62:7 | Table | | +| scopes.cpp:62:7:62:11 | Table | Class | scopes.cpp:62:7:62:7 | operator= | | +| scopes.cpp:62:7:62:11 | Table | Class | scopes.cpp:63:9:63:9 | p | | +| scopes.cpp:62:7:62:11 | Table | Class | scopes.cpp:64:7:64:8 | sz | | +| scopes.cpp:62:7:62:11 | Table | Class | scopes.cpp:66:3:66:7 | Table | | +| scopes.cpp:62:7:62:11 | Table | Class | scopes.cpp:67:3:67:8 | ~Table | | +| scopes.cpp:62:7:62:11 | Table | Class | scopes.cpp:68:9:68:14 | lookup | | +| scopes.cpp:62:7:62:11 | Table | Class | scopes.cpp:69:8:69:13 | insert | | +| scopes.cpp:74:11:74:13 | One | Namespace | scopes.cpp:77:8:77:8 | H | Class | +| scopes.cpp:74:11:74:13 | One | Namespace | scopes.cpp:77:8:77:8 | H | Class | +| scopes.cpp:74:11:74:13 | One | Namespace | scopes.cpp:77:8:77:8 | H | Class | +| scopes.cpp:74:11:74:13 | One | Namespace | scopes.cpp:82:11:82:17 | h_short | | +| scopes.cpp:74:11:74:13 | One | Namespace | scopes.cpp:83:10:83:15 | h_long | | +| scopes.cpp:74:11:74:13 | One | Namespace | scopes.cpp:85:12:85:14 | One::Two | Namespace | +| 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 | 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 | 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:85:12:85:14 | One::Two | Namespace | scopes.cpp:87:8:87:13 | myEnum | | +| scopes.cpp:95:8:95:8 | I | Class | scopes.cpp:95:8:95:8 | operator= | | +| scopes.cpp:95:8:95:8 | I | Class | scopes.cpp:95:8:95:8 | operator= | | +| scopes.cpp:95:8:95:8 | I | Class | scopes.cpp:97:8:97:14 | myEnum2 | | diff --git a/cpp/ql/test/library-tests/scopes/scopes/Scopes1.ql b/cpp/ql/test/library-tests/scopes/scopes/Scopes1.ql new file mode 100644 index 000000000000..b939fd12f54a --- /dev/null +++ b/cpp/ql/test/library-tests/scopes/scopes/Scopes1.ql @@ -0,0 +1,31 @@ +/** + * @name Scopes1 + * @kind table + */ +import cpp + +string describe(Element e) { + if e instanceof GlobalNamespace then ( + result = "GlobalNamespace" + ) else if e instanceof Namespace then ( + result = "Namespace" + ) else if e instanceof NestedClass then ( + result = "NestedClass" + ) else if e instanceof Class then ( + result = "Class" + ) else ( + result = "" + ) +} + +from Element parent, Element child +where + ( + parent instanceof Namespace or + parent instanceof Class or + child instanceof Namespace or + child instanceof Class + ) and + parent = child.getParentScope() and + child.toString() != "__va_list_tag" // platform dependent +select parent, describe(parent), child, describe(child) diff --git a/cpp/ql/test/library-tests/scopes/scopes/Scopes2.expected b/cpp/ql/test/library-tests/scopes/scopes/Scopes2.expected new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/cpp/ql/test/library-tests/scopes/scopes/Scopes2.ql b/cpp/ql/test/library-tests/scopes/scopes/Scopes2.ql new file mode 100644 index 000000000000..abb9c5d572e7 --- /dev/null +++ b/cpp/ql/test/library-tests/scopes/scopes/Scopes2.ql @@ -0,0 +1,21 @@ +/** + * @name Scopes2 + * @kind table + */ +import cpp + +// This test finds places where Element.getParentScope() isn't tree-like. There +// should be no results. +from Element e, int i +where + i = count(e.getParentScope()) and + ( + i > 1 or // multiple parents + e.getParentScope+() = e or // cyclic parent relation + ( + // not GlobalNamespace ancestor + i > 0 and + not e.getParentScope+() instanceof GlobalNamespace + ) + ) +select e, i diff --git a/cpp/ql/test/library-tests/scopes/scopes/Scopes3.expected b/cpp/ql/test/library-tests/scopes/scopes/Scopes3.expected new file mode 100644 index 000000000000..af73b99dff8d --- /dev/null +++ b/cpp/ql/test/library-tests/scopes/scopes/Scopes3.expected @@ -0,0 +1,35 @@ +| file://:0:0:0:0 | operator= | | file://:0:0:0:0 | __va_list_tag | 6 | +| file://:0:0:0:0 | operator= | | file://:0:0:0:0 | __va_list_tag | 6 | +| scopes.cpp:2:7:2:7 | operator= | | scopes.cpp:2:7:2:7 | A | 2 | +| 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: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: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: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: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/Scopes3.ql b/cpp/ql/test/library-tests/scopes/scopes/Scopes3.ql new file mode 100644 index 000000000000..6d5f42417674 --- /dev/null +++ b/cpp/ql/test/library-tests/scopes/scopes/Scopes3.ql @@ -0,0 +1,12 @@ +/** + * @name Scopes3 + * @kind table + */ +import cpp + +from MemberFunction f, string constructor, Class declType, int countAtScope +where + if f instanceof Constructor then constructor = "Constructor" else constructor = "" and + declType = f.getDeclaringType() and + countAtScope = count(Element other | other.getParentScope() = declType) +select f, constructor, declType, countAtScope diff --git a/cpp/ql/test/library-tests/scopes/scopes/Scopes4.expected b/cpp/ql/test/library-tests/scopes/scopes/Scopes4.expected new file mode 100644 index 000000000000..5a4c8bcf4af7 --- /dev/null +++ b/cpp/ql/test/library-tests/scopes/scopes/Scopes4.expected @@ -0,0 +1,40 @@ +| file://:0:0:0:0 | operator delete[] | isTopLevel() | 1 | 1 | +| file://:0:0:0:0 | operator new[] | isTopLevel() | 1 | 1 | +| file://:0:0:0:0 | operator= | | 1 | 1 | +| file://:0:0:0:0 | operator= | | 1 | 1 | +| scopes.cpp:2:7:2:7 | operator= | | 1 | 1 | +| 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 | operator= | | 1 | 1 | +| scopes.cpp:11:7:11:7 | operator= | | 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 | operator= | | 1 | 1 | +| scopes.cpp:31:13:31:13 | operator= | | 1 | 1 | +| scopes.cpp:32:8:32:27 | doubleNestedFunction | | 1 | 1 | +| scopes.cpp:35:6:35:6 | f | isTopLevel() | 1 | 1 | +| scopes.cpp:46:8:46:8 | operator= | | 1 | 1 | +| scopes.cpp:46:8:46:8 | operator= | | 1 | 1 | +| 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 | operator= | | 1 | 1 | +| scopes.cpp:58:7:58:7 | operator= | | 1 | 1 | +| scopes.cpp:62:7:62:7 | Table | | 1 | 1 | +| scopes.cpp:62:7:62:7 | operator= | | 1 | 1 | +| scopes.cpp:66:3:66:7 | Table | | 1 | 1 | +| scopes.cpp:67:3:67:8 | ~Table | | 1 | 1 | +| 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 | operator= | | 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 | +| scopes.cpp:95:8:95:8 | operator= | | 1 | 0 | +| scopes.cpp:95:8:95:8 | operator= | | 1 | 0 | diff --git a/cpp/ql/test/library-tests/scopes/scopes/Scopes4.ql b/cpp/ql/test/library-tests/scopes/scopes/Scopes4.ql new file mode 100644 index 000000000000..2dbaf8856940 --- /dev/null +++ b/cpp/ql/test/library-tests/scopes/scopes/Scopes4.ql @@ -0,0 +1,9 @@ +/** + * @name Scopes4 + * @kind table + */ +import cpp + +from Function f, string top +where if f.isTopLevel() then top = "isTopLevel()" else top = "" +select f, top, count(f.getNamespace()), count(GlobalNamespace gn | f.getNamespace() = gn) diff --git a/cpp/ql/test/library-tests/scopes/scopes/Scopes5.expected b/cpp/ql/test/library-tests/scopes/scopes/Scopes5.expected new file mode 100644 index 000000000000..b31e7ece7d74 --- /dev/null +++ b/cpp/ql/test/library-tests/scopes/scopes/Scopes5.expected @@ -0,0 +1 @@ +| file://:0:0:0:0 | (global namespace) | 0 | diff --git a/cpp/ql/test/library-tests/scopes/scopes/Scopes5.ql b/cpp/ql/test/library-tests/scopes/scopes/Scopes5.ql new file mode 100644 index 000000000000..ea99a8bbf85b --- /dev/null +++ b/cpp/ql/test/library-tests/scopes/scopes/Scopes5.ql @@ -0,0 +1,8 @@ +/** + * @name Scopes5 + * @kind table + */ +import cpp + +from GlobalNamespace gn +select gn, count(gn.getParentScope()) diff --git a/cpp/ql/test/library-tests/scopes/scopes/scopes.cpp b/cpp/ql/test/library-tests/scopes/scopes/scopes.cpp new file mode 100644 index 000000000000..3e1353e1ff73 --- /dev/null +++ b/cpp/ql/test/library-tests/scopes/scopes/scopes.cpp @@ -0,0 +1,104 @@ + +class A { + +}; + + +class B { + +}; + +class C: A, B { + +}; + +class D: C { + + class E; + +}; + +class D::E { + + class F; + + class G { + + }; + +}; + +class D::E::F: D::E::G { + void doubleNestedFunction() {} +}; + +void f(int a, int b) { + bool c = a==b; + + switch (a) + { + default: + } +} + +void g(); + +struct S { + void af() { + + } + void ag(); +}; + +void g() { + S s; + s.ag(); +} + +class Name { + const char* s; +}; + +class Table { + Name* p; + int sz; +public: + Table(int s=15) { p = new Name[sz=s]; } // constructor + ~Table() { delete[] p; } + Name* lookup (const char*); + bool insert(Name*); +}; + +extern "C" int strlen(const char*); + +namespace One +{ + template + class H + { + T t; + }; + + H h_short; + H h_long; + + namespace Two + { + enum myEnum + { + myOne, + myTwo, + myThree + }; + }; + + class I + { + enum myEnum2 + { + Alpha, + Beta, + Gamma + }; + }; +}; \ No newline at end of file diff --git a/cpp/ql/test/library-tests/sideEffects/exprs/exprs.c b/cpp/ql/test/library-tests/sideEffects/exprs/exprs.c new file mode 100644 index 000000000000..e560a88929f0 --- /dev/null +++ b/cpp/ql/test/library-tests/sideEffects/exprs/exprs.c @@ -0,0 +1,13 @@ + +void f1(int p) { + int i; + + for ( + i = 0; + i < 10; + ++i, + ++p) { + } + + return p; +} diff --git a/cpp/ql/test/library-tests/sideEffects/exprs/exprs.cpp b/cpp/ql/test/library-tests/sideEffects/exprs/exprs.cpp new file mode 100644 index 000000000000..d28e8513e841 --- /dev/null +++ b/cpp/ql/test/library-tests/sideEffects/exprs/exprs.cpp @@ -0,0 +1,40 @@ + +class MyIterator + { +public: + MyIterator &operator++() + { + return (*this); + } + + MyIterator &operator--() + { + v--; + return (*this); + } + +private: + int v; + }; + +void f2() { + MyIterator mi; + + ++mi; + --mi; +} + +template void myTemplateFunction() { + static MyIterator mi; + static int i; + static T t; + + ++mi; // pure + ++i; // impure + ++t; // varies +} + +void f3() { + myTemplateFunction(); + myTemplateFunction(); +} diff --git a/cpp/ql/test/library-tests/sideEffects/exprs/exprs.expected b/cpp/ql/test/library-tests/sideEffects/exprs/exprs.expected new file mode 100644 index 000000000000..0d48d60e2270 --- /dev/null +++ b/cpp/ql/test/library-tests/sideEffects/exprs/exprs.expected @@ -0,0 +1,53 @@ +| exprs.c:6:3:6:3 | i | isPure | | | +| exprs.c:6:3:6:7 | ... = ... | | mayBeImpure | | +| exprs.c:6:7:6:7 | 0 | isPure | | | +| exprs.c:7:3:7:3 | i | isPure | | | +| exprs.c:7:3:7:8 | ... < ... | isPure | | | +| exprs.c:7:7:7:8 | 10 | isPure | | | +| exprs.c:8:3:8:5 | ++ ... | | mayBeImpure | | +| exprs.c:8:3:9:5 | ... , ... | | mayBeImpure | | +| exprs.c:8:5:8:5 | i | isPure | | | +| exprs.c:9:3:9:5 | ++ ... | | mayBeImpure | | +| exprs.c:9:5:9:5 | p | isPure | | | +| exprs.c:12:12:12:12 | p | isPure | | | +| exprs.cpp:7:10:7:16 | (...) | isPure | | | +| exprs.cpp:7:10:7:16 | (reference to) | isPure | | | +| exprs.cpp:7:11:7:15 | * ... | isPure | | | +| exprs.cpp:7:12:7:15 | this | isPure | | | +| exprs.cpp:12:3:12:3 | v | isPure | | | +| exprs.cpp:12:3:12:5 | ... -- | | mayBeImpure | mayBeGloballyImpure | +| exprs.cpp:13:10:13:16 | (...) | isPure | | | +| exprs.cpp:13:10:13:16 | (reference to) | isPure | | | +| exprs.cpp:13:11:13:15 | * ... | isPure | | | +| exprs.cpp:13:12:13:15 | this | isPure | | | +| exprs.cpp:23:2:23:2 | call to operator++ | isPure | | | +| exprs.cpp:23:2:23:6 | (reference dereference) | isPure | | | +| exprs.cpp:23:4:23:5 | mi | isPure | | | +| exprs.cpp:24:2:24:2 | call to operator-- | | mayBeImpure | mayBeGloballyImpure | +| exprs.cpp:24:2:24:6 | (reference dereference) | | mayBeImpure | mayBeGloballyImpure | +| exprs.cpp:24:4:24:5 | mi | isPure | | | +| exprs.cpp:32:2:32:2 | call to operator++ | isPure | | | +| exprs.cpp:32:2:32:2 | call to operator++ | isPure | | | +| exprs.cpp:32:2:32:2 | call to operator++ | isPure | | | +| exprs.cpp:32:2:32:6 | (reference dereference) | isPure | | | +| exprs.cpp:32:2:32:6 | (reference dereference) | isPure | | | +| exprs.cpp:32:2:32:6 | (reference dereference) | isPure | | | +| exprs.cpp:32:4:32:5 | mi | isPure | | | +| exprs.cpp:32:4:32:5 | mi | isPure | | | +| exprs.cpp:32:4:32:5 | mi | isPure | | | +| exprs.cpp:33:2:33:4 | ++ ... | | mayBeImpure | mayBeGloballyImpure | +| exprs.cpp:33:2:33:4 | ++ ... | | mayBeImpure | mayBeGloballyImpure | +| exprs.cpp:33:2:33:4 | ++ ... | | mayBeImpure | mayBeGloballyImpure | +| exprs.cpp:33:4:33:4 | i | isPure | | | +| exprs.cpp:33:4:33:4 | i | isPure | | | +| exprs.cpp:33:4:33:4 | i | isPure | | | +| exprs.cpp:34:2:34:2 | call to operator++ | isPure | | | +| exprs.cpp:34:2:34:4 | ++ ... | | mayBeImpure | mayBeGloballyImpure | +| exprs.cpp:34:2:34:4 | ++ ... | | mayBeImpure | mayBeGloballyImpure | +| exprs.cpp:34:2:34:5 | (reference dereference) | isPure | | | +| exprs.cpp:34:4:34:4 | t | isPure | | | +| exprs.cpp:34:4:34:4 | t | isPure | | | +| exprs.cpp:34:4:34:4 | t | isPure | | | +| exprs.cpp:38:2:38:31 | call to myTemplateFunction | | mayBeImpure | mayBeGloballyImpure | +| exprs.cpp:39:2:39:24 | call to myTemplateFunction | | mayBeImpure | mayBeGloballyImpure | +| file://:0:0:0:0 | this | isPure | | | diff --git a/cpp/ql/test/library-tests/sideEffects/exprs/exprs.ql b/cpp/ql/test/library-tests/sideEffects/exprs/exprs.ql new file mode 100644 index 000000000000..9d339d5ffc63 --- /dev/null +++ b/cpp/ql/test/library-tests/sideEffects/exprs/exprs.ql @@ -0,0 +1,8 @@ +import cpp + +from Expr e, string pure, string impure, string globalimpure +where + if e.isPure() then pure = "isPure" else pure = "" and + if e.mayBeImpure() then impure = "mayBeImpure" else impure = "" and + if e.mayBeGloballyImpure() then globalimpure = "mayBeGloballyImpure" else globalimpure = "" +select e, pure, impure, globalimpure diff --git a/cpp/ql/test/library-tests/sideEffects/functions/cpp.cpp b/cpp/ql/test/library-tests/sideEffects/functions/cpp.cpp new file mode 100644 index 000000000000..4768e53ee219 --- /dev/null +++ b/cpp/ql/test/library-tests/sideEffects/functions/cpp.cpp @@ -0,0 +1,101 @@ + +int topLevel = 0; + +class SimpleFoo { + public: + SimpleFoo() { + } +}; + +class SimpleBar : SimpleFoo { + public: + SimpleBar() { + } +}; + +class Foo { + public: + int fooInt; + Foo(int i) : fooInt(8) { } + Foo(int i, int j) : fooInt(i++) { } + Foo(int i, int j, int k) : fooInt(topLevel++) { } +}; + +class Bar : Foo { + public: + int barInt; + Bar(int i) : Foo(7), barInt(9) { } + Bar(int i, int j) : Foo(7, 8), barInt(9) { } + Bar(int i, int j, int k) : Foo(7, 8, 9), barInt(9) { } + Bar(int i, int j, int k, int l) : Foo(7), barInt(i++) { } + Bar(int i, int j, int k, int l, int m) : Foo(7), barInt(topLevel++) { } + Bar(int i, int j, int k, int l, int m, int n) : Foo(topLevel++), barInt(9) { } +}; + +class PurelyDestructible { + public: + ~PurelyDestructible() { } +}; + +class ImpurelyDestructible { + public: + ~ImpurelyDestructible() { topLevel++; } +}; + +class SuperPurelyDestructible : PurelyDestructible { + public: + ~SuperPurelyDestructible() { } +}; + +class SuperImpurelyDestructible : ImpurelyDestructible { + public: + ~SuperImpurelyDestructible() { } +}; + +class someClass { + public: + int operator*() const { + return 5; + } +}; + +void startSomeClass(someClass c) { + int i; + + i = *c; +} + +int global = 0; + +void functionAccessesGlobal1() { + global++; +} + +int functionAccessesGlobal2() { // not pure but side-effect free [considered pure] + return global; +} + +int functionAccessesGlobal3() { // not pure but side-effect free [considered pure] + if (global == 5) + { + return 5; + } else { + return 0; + } +} + +int functionAccessesStatic() { + static int ctr = 0; + + return ctr++; +} + +void increment(int &ref) { + ref++; +} + +void doIncrement() { // pure [currently wrong, but hard to detect] + int i; + + increment(i); +} diff --git a/cpp/ql/test/library-tests/sideEffects/functions/sideEffects.c b/cpp/ql/test/library-tests/sideEffects/functions/sideEffects.c new file mode 100644 index 000000000000..fbe6f76e0276 --- /dev/null +++ b/cpp/ql/test/library-tests/sideEffects/functions/sideEffects.c @@ -0,0 +1,105 @@ + +int topLevel = 0; + +int f1(void) { + int i; + + i = 2; + + return i; +} + +int f2(void) { + int i = 0; + + i = 2; + + return i; +} + +int f3(void) { + int i = 0; + + i = 2; + i++; + + return i; +} + +int f4(void) { + static int i = 0; + + i++; + + return i; +} + +void f5(void) { + int x; + int y; + x = 7; + y = x++; + x = 8; +} + +void f6(void) { + int x; + int y; + x = 7; + y = topLevel++; + x = 8; +} + +int f7(int x) { + switch(x) { + case 1: + return 3; + case 2: + break; + default: + return 4; + } + return 5; +} + +void f8(void) { + int x; + for (x = 3; x < 10; x++) { } + for (x = 3; x < 10; ) { } + for (x = 3; ; x++) { } + for (x = 3; ; ) { } + for ( ; x < 10; x++) { } + for ( ; x < 10; ) { } + for ( ; ; x++) { } + for ( ; ; ) { } +} + +void (*funPointer)(void); + +void f9(void) { + funPointer(); +} + +int fib(int x) { + if (x < 2) { + return x; + } else { + return fib(x - 2) + fib(x - 1); + } +} + +void mut1(int x); +void mut2(int x); + +void mut1(int x) { + if (x != 0) { + mut2(x - 1); + } +} + +void mut2(int x) { + if (x != 0) { + mut1(x - 1); + } +} + diff --git a/cpp/ql/test/library-tests/sideEffects/functions/sideEffects.expected b/cpp/ql/test/library-tests/sideEffects/functions/sideEffects.expected new file mode 100644 index 000000000000..ae4c8d3fb3e1 --- /dev/null +++ b/cpp/ql/test/library-tests/sideEffects/functions/sideEffects.expected @@ -0,0 +1,63 @@ +| cpp.cpp:4:7:4:7 | SimpleFoo | SimpleFoo && -> void | false | +| cpp.cpp:4:7:4:7 | SimpleFoo | const SimpleFoo & -> void | false | +| cpp.cpp:4:7:4:7 | operator= | SimpleFoo && -> SimpleFoo & | false | +| cpp.cpp:4:7:4:7 | operator= | const SimpleFoo & -> SimpleFoo & | false | +| cpp.cpp:6:5:6:13 | SimpleFoo | void | true | +| cpp.cpp:10:7:10:7 | SimpleBar | SimpleBar && -> void | false | +| cpp.cpp:10:7:10:7 | SimpleBar | const SimpleBar & -> void | false | +| cpp.cpp:10:7:10:7 | operator= | SimpleBar && -> SimpleBar & | false | +| cpp.cpp:10:7:10:7 | operator= | const SimpleBar & -> SimpleBar & | false | +| cpp.cpp:12:5:12:13 | SimpleBar | void | true | +| cpp.cpp:16:7:16:7 | Foo | Foo && -> void | false | +| cpp.cpp:16:7:16:7 | Foo | const Foo & -> void | false | +| cpp.cpp:16:7:16:7 | operator= | Foo && -> Foo & | false | +| cpp.cpp:16:7:16:7 | operator= | const Foo & -> Foo & | false | +| cpp.cpp:19:5:19:7 | Foo | int -> void | true | +| cpp.cpp:20:5:20:7 | Foo | int -> int -> void | true | +| cpp.cpp:21:5:21:7 | Foo | int -> int -> int -> void | false | +| cpp.cpp:24:7:24:7 | Bar | Bar && -> void | false | +| cpp.cpp:24:7:24:7 | Bar | const Bar & -> void | false | +| cpp.cpp:24:7:24:7 | operator= | Bar && -> Bar & | false | +| cpp.cpp:24:7:24:7 | operator= | const Bar & -> Bar & | false | +| cpp.cpp:27:5:27:7 | Bar | int -> void | true | +| cpp.cpp:28:5:28:7 | Bar | int -> int -> void | true | +| cpp.cpp:29:5:29:7 | Bar | int -> int -> int -> void | false | +| 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 | operator= | const PurelyDestructible & -> PurelyDestructible & | false | +| cpp.cpp:37:5:37:23 | ~PurelyDestructible | void | true | +| 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 | operator= | const SuperPurelyDestructible & -> SuperPurelyDestructible & | false | +| cpp.cpp:47:5:47:28 | ~SuperPurelyDestructible | void | true | +| 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 | +| cpp.cpp:55:7:55:7 | operator= | someClass && -> someClass & | false | +| cpp.cpp:57:9:57:17 | operator* | int | true | +| cpp.cpp:62:6:62:19 | startSomeClass | someClass -> void | true | +| cpp.cpp:70:6:70:28 | functionAccessesGlobal1 | void | false | +| cpp.cpp:74:5:74:27 | functionAccessesGlobal2 | int | true | +| cpp.cpp:78:5:78:27 | functionAccessesGlobal3 | int | true | +| cpp.cpp:87:5:87:26 | functionAccessesStatic | int | false | +| cpp.cpp:93:6:93:14 | increment | int & -> void | false | +| cpp.cpp:97:6:97:16 | doIncrement | void | false | +| file://:0:0:0:0 | operator= | __va_list_tag & | false | +| file://:0:0:0:0 | operator= | __va_list_tag & | false | +| sideEffects.c:4:5:4:6 | f1 | int | true | +| sideEffects.c:12:5:12:6 | f2 | int | true | +| sideEffects.c:20:5:20:6 | f3 | int | true | +| sideEffects.c:29:5:29:6 | f4 | int | false | +| sideEffects.c:37:6:37:7 | f5 | void | true | +| sideEffects.c:45:6:45:7 | f6 | void | false | +| sideEffects.c:53:5:53:6 | f7 | int -> int | true | +| sideEffects.c:65:6:65:7 | f8 | void | true | +| sideEffects.c:79:6:79:7 | f9 | void | false | +| sideEffects.c:83:5:83:7 | fib | int -> int | true | +| sideEffects.c:94:6:94:9 | mut1 | int -> void | true | +| sideEffects.c:100:6:100:9 | mut2 | int -> void | true | +| templates.cpp:3:5:3:5 | f | T -> int | true | +| templates.cpp:3:5:3:5 | f | int -> int | true | +| templates.cpp:8:6:8:6 | g | T -> void | false | +| templates.cpp:16:6:16:6 | h | int -> void | true | diff --git a/cpp/ql/test/library-tests/sideEffects/functions/sideEffects.ql b/cpp/ql/test/library-tests/sideEffects/functions/sideEffects.ql new file mode 100644 index 000000000000..9ced4033c4e5 --- /dev/null +++ b/cpp/ql/test/library-tests/sideEffects/functions/sideEffects.ql @@ -0,0 +1,12 @@ +import cpp + +string functionType(Function f, int i) { + if exists(f.getParameter(i)) + then result = f.getParameter(i).getType().toString() + " -> " + functionType(f, i + 1) + else (i = f.getNumberOfParameters() and result = f.getType().toString()) +} + +from Function f, boolean b +where if f.isSideEffectFree() then b = true else b = false +select f, functionType(f, 0), b + diff --git a/cpp/ql/test/library-tests/sideEffects/functions/templates.cpp b/cpp/ql/test/library-tests/sideEffects/functions/templates.cpp new file mode 100644 index 000000000000..fd000dd4d3de --- /dev/null +++ b/cpp/ql/test/library-tests/sideEffects/functions/templates.cpp @@ -0,0 +1,19 @@ + +template +int f(T a) { + return 5; +} + +template +void g(T t) { + long l = 1; + // This is a call to f(long), but as g is never called, we + // never instantiate it. So f(long) has a definition, but not + // a body. + f(l); +} + +void h(int x) { + f(x); +} + diff --git a/cpp/ql/test/library-tests/sideEffects/stmts/stmts.c b/cpp/ql/test/library-tests/sideEffects/stmts/stmts.c new file mode 100644 index 000000000000..4120c2de68d6 --- /dev/null +++ b/cpp/ql/test/library-tests/sideEffects/stmts/stmts.c @@ -0,0 +1,34 @@ + +void f(int x) { + int y; + int z = x + 3; + int j = y++; + + switch(x) { + case 3: + return; + case 4: + break; + default: + return; + } + return; +} + +int x = 0; + +struct myStruct { + int myInt; +} *p; + +struct myStruct *getMyStruct(void) { + x = 1; + + return p; +} + +void f2(void) { + int i; + getMyStruct()->myInt; +} + diff --git a/cpp/ql/test/library-tests/sideEffects/stmts/stmts.cpp b/cpp/ql/test/library-tests/sideEffects/stmts/stmts.cpp new file mode 100644 index 000000000000..811b26dd88b9 --- /dev/null +++ b/cpp/ql/test/library-tests/sideEffects/stmts/stmts.cpp @@ -0,0 +1,47 @@ + +int topLevel = 100; + +class MyPureClass { + public: + MyPureClass operator+(const MyPureClass &other) { + } + MyPureClass operator=(const MyPureClass &other) { + } + MyPureClass &operator++() { + } + MyPureClass operator++(int x) { + } +}; + +class MyImpureClass { + public: + MyImpureClass operator+(const MyImpureClass &other) { + topLevel += 1; + } + MyImpureClass operator=(const MyImpureClass &other) { + topLevel += 2; + } + MyImpureClass &operator++() { + topLevel += 4; + } + MyImpureClass operator++(int x) { + topLevel += 8; + } +}; + +void cpp_operator_overloading() { + int x = 1; + MyPureClass p = MyPureClass(); + MyImpureClass i = MyImpureClass(); + + x = x + x; + p = p + p; + i = i + i; + ++x; + ++p; + ++i; + x++; + p++; + i++; +} + diff --git a/cpp/ql/test/library-tests/sideEffects/stmts/stmts.expected b/cpp/ql/test/library-tests/sideEffects/stmts/stmts.expected new file mode 100644 index 000000000000..be1a1a7d973a --- /dev/null +++ b/cpp/ql/test/library-tests/sideEffects/stmts/stmts.expected @@ -0,0 +1,55 @@ +| stmts.c:2:15:16:1 | { ... } | false | true | +| stmts.c:3:5:3:10 | declaration | true | true | +| stmts.c:4:5:4:18 | declaration | true | true | +| stmts.c:5:5:5:16 | declaration | false | true | +| stmts.c:7:5:14:5 | switch (...) ... | true | true | +| stmts.c:7:15:14:5 | { ... } | true | true | +| stmts.c:8:9:8:15 | case ...: | true | true | +| stmts.c:9:13:9:19 | return ... | true | true | +| stmts.c:10:9:10:15 | case ...: | true | true | +| stmts.c:11:13:11:18 | break; | true | true | +| stmts.c:12:9:12:16 | default: | true | true | +| stmts.c:13:13:13:19 | return ... | true | true | +| stmts.c:14:5:14:5 | label ...: | true | true | +| stmts.c:15:5:15:11 | return ... | true | true | +| stmts.c:24:36:28:1 | { ... } | false | false | +| stmts.c:25:5:25:10 | ExprStmt | false | false | +| stmts.c:27:5:27:13 | return ... | true | true | +| stmts.c:30:15:33:1 | { ... } | false | false | +| stmts.c:31:5:31:10 | declaration | true | true | +| stmts.c:32:5:32:25 | ExprStmt | false | false | +| stmts.c:33:1:33:1 | return ... | true | true | +| stmts.cpp:6:53:7:5 | { ... } | true | true | +| stmts.cpp:7:5:7:5 | return ... | true | true | +| stmts.cpp:8:53:9:5 | { ... } | true | true | +| stmts.cpp:9:5:9:5 | return ... | true | true | +| stmts.cpp:10:31:11:5 | { ... } | true | true | +| stmts.cpp:11:5:11:5 | return ... | true | true | +| stmts.cpp:12:35:13:5 | { ... } | true | true | +| stmts.cpp:13:5:13:5 | return ... | true | true | +| stmts.cpp:18:57:20:5 | { ... } | false | false | +| stmts.cpp:19:9:19:22 | ExprStmt | false | false | +| stmts.cpp:20:5:20:5 | return ... | true | true | +| stmts.cpp:21:57:23:5 | { ... } | false | false | +| stmts.cpp:22:9:22:22 | ExprStmt | false | false | +| stmts.cpp:23:5:23:5 | return ... | true | true | +| stmts.cpp:24:33:26:5 | { ... } | false | false | +| stmts.cpp:25:9:25:22 | ExprStmt | false | false | +| stmts.cpp:26:5:26:5 | return ... | true | true | +| stmts.cpp:27:37:29:5 | { ... } | false | false | +| stmts.cpp:28:9:28:22 | ExprStmt | false | false | +| stmts.cpp:29:5:29:5 | return ... | true | true | +| stmts.cpp:32:33:46:1 | { ... } | false | false | +| stmts.cpp:33:5:33:14 | declaration | true | true | +| stmts.cpp:34:5:34:34 | declaration | true | true | +| stmts.cpp:35:5:35:38 | declaration | true | true | +| stmts.cpp:37:5:37:14 | ExprStmt | false | true | +| stmts.cpp:38:5:38:14 | ExprStmt | true | true | +| stmts.cpp:39:5:39:14 | ExprStmt | false | false | +| stmts.cpp:40:5:40:8 | ExprStmt | false | true | +| stmts.cpp:41:5:41:8 | ExprStmt | true | true | +| stmts.cpp:42:5:42:8 | ExprStmt | false | false | +| stmts.cpp:43:5:43:8 | ExprStmt | false | true | +| stmts.cpp:44:5:44:8 | ExprStmt | true | true | +| stmts.cpp:45:5:45:8 | ExprStmt | false | false | +| stmts.cpp:46:1:46:1 | return ... | true | true | diff --git a/cpp/ql/test/library-tests/sideEffects/stmts/stmts.ql b/cpp/ql/test/library-tests/sideEffects/stmts/stmts.ql new file mode 100644 index 000000000000..a5b6d0ff4153 --- /dev/null +++ b/cpp/ql/test/library-tests/sideEffects/stmts/stmts.ql @@ -0,0 +1,11 @@ +import cpp + +from Stmt s, boolean pure, boolean pureExceptLocals +where if s.isPure() + then pure = true + else pure = false + and if s.mayBeGloballyImpure() + then pureExceptLocals = false + else pureExceptLocals = true +select s, pure, pureExceptLocals + diff --git a/cpp/ql/test/library-tests/special_members/detect/detect.cpp b/cpp/ql/test/library-tests/special_members/detect/detect.cpp new file mode 100644 index 000000000000..db89f55ebf19 --- /dev/null +++ b/cpp/ql/test/library-tests/special_members/detect/detect.cpp @@ -0,0 +1,63 @@ +class C; + +typedef C Ctop; +typedef Ctop&& Ctop_rref1; +typedef Ctop_rref1 Ctop_rref; + +// These templates provide the syntax needed to denote references to +// references. There are no actual references to references in C++, but C++11 +// will perform "reference collapsing", where attempts to declare rvalue refs to +// rvalue refs collapse to rvalue refs while all other combinations collapse to +// lvalue refs. +template using lref = T&; +template using rref = T&&; + +struct C { + typedef C Cinside1; + typedef Cinside1 Cinside; + typedef Cinside& Cinside_lref1; + typedef Cinside_lref1 Cinside_lref; + + // Constructors + C(); + // C(C c); // doesn't compile + C(C* c); + C(const C* c); + C(C c, int i); // My compiler requires the int parameter + C(const C& c1, const C& c2); // has extra parameter + C(int i, const C& c); // has leading parameter + template C(const C& c_templated); + template C(C&& c_templated, double *p = nullptr); + + // Copy constructors + C(C& c, ...); // This is apparently a copy constructor + C(const Ctop& c, int i = 0, int j = 2); + C(volatile C& c); + C(const volatile Cinside_lref c, double x = 0.0); + C(const rref> c, int* p = nullptr); + C(const lref> c, int** p = nullptr); + + // Move constructors + C(C&& c, int i = 0); + C(Ctop_rref c); + C(const volatile C&& c); + C(volatile Cinside&& c); + C(const rref>> c, int* p = nullptr); + + // Copy assignment + C& operator=(Cinside_lref c); + C& operator=(Cinside nonref); + C& operator=(const volatile C& c); + C& operator=(rref>> c_copy); + + // Move assignment + C& operator=(Ctop_rref c); + C& operator=(const volatile C&& c); + C& operator=(rref>> c_move); + + // Non-assignment member functions + C& operator=(int i); + template C& operator=(volatile C& c_templated); + template C& operator=(volatile C&& c_templated); + bool operator==(const C& c); +}; diff --git a/cpp/ql/test/library-tests/special_members/detect/detect.expected b/cpp/ql/test/library-tests/special_members/detect/detect.expected new file mode 100644 index 000000000000..1d4f114a4990 --- /dev/null +++ b/cpp/ql/test/library-tests/special_members/detect/detect.expected @@ -0,0 +1,31 @@ +| 1 constructor | C::C() | +| 1 constructor | C::C(C && c_templated, double * p) | +| 1 constructor | C::C(C * c) | +| 1 constructor | C::C(C c, int i) | +| 1 constructor | C::C(const C & c1, const C & c2) | +| 1 constructor | C::C(const C & c_templated) | +| 1 constructor | C::C(const C * c) | +| 1 constructor | C::C(int i, const C & c) | +| 2 copy constructor | C::C(C & c) | +| 2 copy constructor | C::C(Cinside_lref c, double x) | +| 2 copy constructor | C::C(const Ctop & c, int i, int j) | +| 2 copy constructor | C::C(lref c, int ** p) | +| 2 copy constructor | C::C(rref c, int * p) | +| 2 copy constructor | C::C(volatile C & c) | +| 3 move constructor | C::C(C && c, int i) | +| 3 move constructor | C::C(Ctop_rref c) | +| 3 move constructor | C::C(const volatile C && c) | +| 3 move constructor | C::C(rref c, int * p) | +| 3 move constructor | C::C(volatile Cinside && c) | +| 4 copy assignment | C::operator=(Cinside nonref) | +| 4 copy assignment | C::operator=(Cinside_lref c) | +| 4 copy assignment | C::operator=(const volatile C & c) | +| 4 copy assignment | C::operator=(rref c_copy) | +| 5 move assignment | C::operator=(Ctop_rref c) | +| 5 move assignment | C::operator=(const volatile C && c) | +| 5 move assignment | C::operator=(rref c_move) | +| 6 none of the above | C::operator=(int i) | +| 6 none of the above | C::operator=(volatile C & c_templated) | +| 6 none of the above | C::operator=(volatile C && c_templated) | +| 6 none of the above | C::operator==(const C & c) | +| 6 none of the above | __va_list_tag::operator=() | diff --git a/cpp/ql/test/library-tests/special_members/detect/detect.ql b/cpp/ql/test/library-tests/special_members/detect/detect.ql new file mode 100644 index 000000000000..31f272b865c1 --- /dev/null +++ b/cpp/ql/test/library-tests/special_members/detect/detect.ql @@ -0,0 +1,23 @@ +import cpp + +string functionName(Function f) { + result = f.getQualifiedName() +"("+ f.getParameterString() +")" or + not exists(f.getQualifiedName()) and + result = f.getName() +"("+ f.getParameterString() +")" +} + +from MemberFunction f, string description +where + description = "1 constructor" + and f instanceof Constructor + and not f instanceof CopyConstructor + and not f instanceof MoveConstructor +or description = "2 copy constructor" and f instanceof CopyConstructor +or description = "3 move constructor" and f instanceof MoveConstructor +or description = "4 copy assignment" and f instanceof CopyAssignmentOperator +or description = "5 move assignment" and f instanceof MoveAssignmentOperator +or description = "6 none of the above" + and not f instanceof Constructor + and not f instanceof CopyAssignmentOperator + and not f instanceof MoveAssignmentOperator +select description, functionName(f) diff --git a/cpp/ql/test/library-tests/special_members/generated_copy/assign.expected b/cpp/ql/test/library-tests/special_members/generated_copy/assign.expected new file mode 100644 index 000000000000..eec36e7e39c3 --- /dev/null +++ b/cpp/ql/test/library-tests/special_members/generated_copy/assign.expected @@ -0,0 +1,28 @@ +| container::Copyable | can | does | have implicit copy assignment | +| container::CopyableComposition | can | does | have implicit copy assignment | +| container::CopyableInheritance | can | does | have implicit copy assignment | +| container::NotCopyable | can NOT | does NOT | have implicit copy assignment | +| container::NotCopyableComposition | can NOT | does NOT | have implicit copy assignment | +| container::NotCopyableInheritance | can NOT | does NOT | have implicit copy assignment | +| container::Wrapper | can | does | have implicit copy assignment | +| container::Wrapper | can NOT | does NOT | have implicit copy assignment | +| deleted_cc::C | can | does NOT | have implicit copy assignment | +| deleted_cc::Sub | can NOT | does NOT | have implicit copy assignment | +| difference::Base | can | does NOT | have implicit copy assignment | +| difference::OnlyAssign | can | does | have implicit copy assignment | +| difference::OnlyCtor | can NOT | does NOT | have implicit copy assignment | +| moves::MoveAssign | can NOT | does NOT | have implicit copy assignment | +| moves::MoveCtor | can NOT | does NOT | have implicit copy assignment | +| private_cc::C | can | does NOT | have implicit copy assignment | +| private_cc::HasArray | can NOT | does NOT | have implicit copy assignment | +| private_cc::HasArray2D | can NOT | does NOT | have implicit copy assignment | +| private_cc::HasPointer | can | does | have implicit copy assignment | +| private_cc::Sub | can NOT | does NOT | have implicit copy assignment | +| protected_cc::C | can | does NOT | have implicit copy assignment | +| protected_cc::HasMember | can NOT | does NOT | have implicit copy assignment | +| protected_cc::Sub1 | can | does | have implicit copy assignment | +| protected_cc::Sub2 | can | does | have implicit copy assignment | +| typedefs::A | can | does | have implicit copy assignment | +| typedefs::A::B | can | does | have implicit copy assignment | +| typedefs::C | can | does | have implicit copy assignment | +| typedefs::Derived | can | does | have implicit copy assignment | diff --git a/cpp/ql/test/library-tests/special_members/generated_copy/assign.ql b/cpp/ql/test/library-tests/special_members/generated_copy/assign.ql new file mode 100644 index 000000000000..a264dd0e2601 --- /dev/null +++ b/cpp/ql/test/library-tests/special_members/generated_copy/assign.ql @@ -0,0 +1,12 @@ +import cpp + +from Class c +where not c.getName().charAt(0) = "_" + and not c instanceof TemplateClass +select + c.getQualifiedName(), + any(string s | + if c.implicitCopyAssignmentOperatorDeleted() then s="can NOT" else s="can"), + any(string s | + if c.hasImplicitCopyAssignmentOperator() then s="does" else s="does NOT"), + "have implicit copy assignment" diff --git a/cpp/ql/test/library-tests/special_members/generated_copy/copy.cpp b/cpp/ql/test/library-tests/special_members/generated_copy/copy.cpp new file mode 100644 index 000000000000..8d68cfdc09e5 --- /dev/null +++ b/cpp/ql/test/library-tests/special_members/generated_copy/copy.cpp @@ -0,0 +1,133 @@ +namespace protected_cc { + class C { + int i; + protected: + C(const C&) = default; + C& operator=(const C&) = default; + }; + + class Sub1 : private C { + // should have copy members + }; + + class Sub2: public Sub1 { + // should have copy members + }; + + class HasMember { + C c; + // should NOT have copy members + }; +} + +namespace deleted_cc { + class C { + C(const C&) = delete; + C& operator=(const C&) = delete; + }; + + class Sub : public C { + // should NOT have copy members + }; +} + +namespace private_cc { + class C { + private: + C(C&) = default; + C& operator=(const C&) = default; + }; + + class Sub : private C { + // should NOT have copy members + public: + // In the terminology of clang, this constructor is explicitly defaulted + // but implicitly deleted + Sub(Sub&) = default; + }; + + class HasPointer { + C *c; + // should have copy members since pointers are copyable + }; + + class HasArray { + C c[2]; + // should NOT have copy members + }; + + class HasArray2D { + C c[2][2]; + // should NOT have copy members + }; +} + +namespace container { + template + class Wrapper { + T t; + }; + + class Copyable {}; + class NotCopyable { + int&& i; + }; + + class CopyableComposition { + Wrapper w; + }; + + class NotCopyableComposition { + Wrapper w; + }; + + class CopyableInheritance : Wrapper {}; + + class NotCopyableInheritance : Wrapper {}; +} + +namespace typedefs { + class A { + class B { }; + friend class C; + }; + + class C { + public: + typedef A::B D; + }; + + class Derived : C::D { + // should have copy members + }; +} + +namespace moves { + class MoveCtor { + public: + MoveCtor(MoveCtor&& that) {} + }; + + class MoveAssign { + public: + MoveAssign& operator=(MoveAssign&& that) { + return *this; + } + }; +} + +namespace difference { + class OnlyCtor { + const int i; + }; + + class Base { + public: + Base& operator=(const Base&); + private: + Base(const Base&); + }; + + class OnlyAssign : Base { + }; +} diff --git a/cpp/ql/test/library-tests/special_members/generated_copy/ctor.expected b/cpp/ql/test/library-tests/special_members/generated_copy/ctor.expected new file mode 100644 index 000000000000..22745978bf89 --- /dev/null +++ b/cpp/ql/test/library-tests/special_members/generated_copy/ctor.expected @@ -0,0 +1,28 @@ +| container::Copyable | can | does | have implicit copy constructor | +| container::CopyableComposition | can | does | have implicit copy constructor | +| container::CopyableInheritance | can | does | have implicit copy constructor | +| container::NotCopyable | can NOT | does NOT | have implicit copy constructor | +| container::NotCopyableComposition | can NOT | does NOT | have implicit copy constructor | +| container::NotCopyableInheritance | can NOT | does NOT | have implicit copy constructor | +| container::Wrapper | can | does | have implicit copy constructor | +| container::Wrapper | can NOT | does NOT | have implicit copy constructor | +| deleted_cc::C | can | does NOT | have implicit copy constructor | +| deleted_cc::Sub | can NOT | does NOT | have implicit copy constructor | +| difference::Base | can | does NOT | have implicit copy constructor | +| difference::OnlyAssign | can NOT | does NOT | have implicit copy constructor | +| difference::OnlyCtor | can | does | have implicit copy constructor | +| moves::MoveAssign | can NOT | does NOT | have implicit copy constructor | +| moves::MoveCtor | can NOT | does NOT | have implicit copy constructor | +| private_cc::C | can | does NOT | have implicit copy constructor | +| private_cc::HasArray | can NOT | does NOT | have implicit copy constructor | +| private_cc::HasArray2D | can NOT | does NOT | have implicit copy constructor | +| private_cc::HasPointer | can | does | have implicit copy constructor | +| private_cc::Sub | can NOT | does NOT | have implicit copy constructor | +| protected_cc::C | can | does NOT | have implicit copy constructor | +| protected_cc::HasMember | can NOT | does NOT | have implicit copy constructor | +| protected_cc::Sub1 | can | does | have implicit copy constructor | +| protected_cc::Sub2 | can | does | have implicit copy constructor | +| typedefs::A | can | does | have implicit copy constructor | +| typedefs::A::B | can | does | have implicit copy constructor | +| typedefs::C | can | does | have implicit copy constructor | +| typedefs::Derived | can | does | have implicit copy constructor | diff --git a/cpp/ql/test/library-tests/special_members/generated_copy/ctor.ql b/cpp/ql/test/library-tests/special_members/generated_copy/ctor.ql new file mode 100644 index 000000000000..e2e8375da68b --- /dev/null +++ b/cpp/ql/test/library-tests/special_members/generated_copy/ctor.ql @@ -0,0 +1,12 @@ +import cpp + +from Class c +where not c.getName().charAt(0) = "_" + and not c instanceof TemplateClass +select + c.getQualifiedName(), + any(string s | + if c.implicitCopyConstructorDeleted() then s="can NOT" else s="can"), + any(string s | + if c.hasImplicitCopyConstructor() then s="does" else s="does NOT"), + "have implicit copy constructor" 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 new file mode 100644 index 000000000000..bb28af216711 --- /dev/null +++ b/cpp/ql/test/library-tests/special_members/generated_copy/functions.expected @@ -0,0 +1,86 @@ +| copy.cpp:5:5:5:5 | C | | defaulted | +| copy.cpp:6:8:6:16 | operator= | | defaulted | +| copy.cpp:9:9:9:9 | Sub1 | | | +| copy.cpp:9:9:9:9 | Sub1 | | | +| copy.cpp:9:9:9:9 | Sub1 | deleted | | +| copy.cpp:9:9:9:9 | operator= | | | +| copy.cpp:9:9:9:9 | operator= | | | +| copy.cpp:13:9:13:9 | Sub2 | | | +| copy.cpp:13:9:13:9 | Sub2 | | | +| copy.cpp:13:9:13:9 | Sub2 | deleted | | +| copy.cpp:13:9:13:9 | operator= | | | +| copy.cpp:13:9:13:9 | operator= | | | +| copy.cpp:17:9:17:9 | HasMember | | | +| copy.cpp:17:9:17:9 | HasMember | | | +| copy.cpp:17:9:17:9 | HasMember | deleted | | +| copy.cpp:17:9:17:9 | operator= | | | +| copy.cpp:17:9:17:9 | operator= | | | +| copy.cpp:25:5:25:5 | C | deleted | | +| copy.cpp:26:8:26:16 | operator= | deleted | | +| copy.cpp:29:9:29:9 | Sub | deleted | | +| copy.cpp:29:9:29:9 | Sub | deleted | | +| copy.cpp:29:9:29:9 | operator= | deleted | | +| copy.cpp:37:5:37:5 | C | | defaulted | +| 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 | operator= | | | +| copy.cpp:49:9:49:9 | operator= | | | +| copy.cpp:54:9:54:9 | HasArray | deleted | | +| copy.cpp:54:9:54:9 | HasArray | deleted | | +| copy.cpp:54:9:54:9 | operator= | | | +| copy.cpp:54:9:54:9 | operator= | | | +| copy.cpp:59:9:59:9 | HasArray2D | deleted | | +| copy.cpp:59:9:59:9 | HasArray2D | deleted | | +| 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 | deleted | | +| copy.cpp:67:9:67:9 | Wrapper | deleted | | +| copy.cpp:67:9:67:9 | operator= | | | +| copy.cpp:67:9:67:9 | operator= | | | +| copy.cpp:67:9:67:9 | operator= | deleted | | +| copy.cpp:71:9:71:9 | operator= | | | +| copy.cpp:71:9:71:9 | operator= | | | +| copy.cpp:72:9:72:9 | NotCopyable | | | +| 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 | 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 | operator= | | | +| copy.cpp:84:9:84:9 | operator= | | | +| copy.cpp:86:9:86:9 | NotCopyableInheritance | | | +| copy.cpp:86:9:86:9 | NotCopyableInheritance | deleted | | +| copy.cpp:86:9:86:9 | NotCopyableInheritance | deleted | | +| copy.cpp:86:9:86:9 | operator= | deleted | | +| copy.cpp:90:9:90:9 | operator= | | | +| copy.cpp:90:9:90:9 | operator= | | | +| copy.cpp:91:11:91:11 | operator= | | | +| 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 | 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 | 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= | | | +| copy.cpp:128:5:128:8 | Base | | | +| copy.cpp:131:9:131:9 | OnlyAssign | deleted | | +| copy.cpp:131:9:131:9 | OnlyAssign | deleted | | +| copy.cpp:131:9:131:9 | operator= | | | +| copy.cpp:131:9:131:9 | operator= | | | +| file://:0:0:0:0 | operator= | | | +| file://:0:0:0:0 | operator= | | | diff --git a/cpp/ql/test/library-tests/special_members/generated_copy/functions.ql b/cpp/ql/test/library-tests/special_members/generated_copy/functions.ql new file mode 100644 index 000000000000..d7a59a882f6c --- /dev/null +++ b/cpp/ql/test/library-tests/special_members/generated_copy/functions.ql @@ -0,0 +1,7 @@ +import cpp + +from Function f, string deleted, string defaulted +where if f.isDeleted() then deleted = "deleted" else deleted = "" + and if f.isDefaulted() then defaulted = "defaulted" else defaulted = "" +select f, deleted, defaulted + diff --git a/cpp/ql/test/library-tests/specifiers/Specifiers1.expected b/cpp/ql/test/library-tests/specifiers/Specifiers1.expected new file mode 100644 index 000000000000..ee8284666887 --- /dev/null +++ b/cpp/ql/test/library-tests/specifiers/Specifiers1.expected @@ -0,0 +1,4 @@ +| 5 | specifiers.cpp:5:11:5:11 | c | +| 6 | specifiers.cpp:6:14:6:15 | pi | +| 13 | specifiers.cpp:13:19:13:30 | int_pointer1 | +| 15 | specifiers.cpp:15:19:15:30 | int_pointer3 | diff --git a/cpp/ql/test/library-tests/specifiers/Specifiers1.ql b/cpp/ql/test/library-tests/specifiers/Specifiers1.ql new file mode 100644 index 000000000000..8d58e624a707 --- /dev/null +++ b/cpp/ql/test/library-tests/specifiers/Specifiers1.ql @@ -0,0 +1,10 @@ +/** + * @name Specifiers1 + */ +import cpp + +from Variable v +where v.isConst() +select v.getLocation().getStartLine(), + v + diff --git a/cpp/ql/test/library-tests/specifiers/Specifiers2.expected b/cpp/ql/test/library-tests/specifiers/Specifiers2.expected new file mode 100644 index 000000000000..88259402cbf7 --- /dev/null +++ b/cpp/ql/test/library-tests/specifiers/Specifiers2.expected @@ -0,0 +1,3 @@ +| 1 | i | +| 2 | i | +| 3 | i | diff --git a/cpp/ql/test/library-tests/specifiers/Specifiers2.ql b/cpp/ql/test/library-tests/specifiers/Specifiers2.ql new file mode 100644 index 000000000000..5174adba0bd2 --- /dev/null +++ b/cpp/ql/test/library-tests/specifiers/Specifiers2.ql @@ -0,0 +1,10 @@ +/** + * @name Specifiers2 + */ +import cpp + +from Variable v +where v.hasSpecifier("extern") +select v.getLocation().getStartLine(), + v.getName() + diff --git a/cpp/ql/test/library-tests/specifiers/Specifiers3.expected b/cpp/ql/test/library-tests/specifiers/Specifiers3.expected new file mode 100644 index 000000000000..ee8284666887 --- /dev/null +++ b/cpp/ql/test/library-tests/specifiers/Specifiers3.expected @@ -0,0 +1,4 @@ +| 5 | specifiers.cpp:5:11:5:11 | c | +| 6 | specifiers.cpp:6:14:6:15 | pi | +| 13 | specifiers.cpp:13:19:13:30 | int_pointer1 | +| 15 | specifiers.cpp:15:19:15:30 | int_pointer3 | diff --git a/cpp/ql/test/library-tests/specifiers/Specifiers3.ql b/cpp/ql/test/library-tests/specifiers/Specifiers3.ql new file mode 100644 index 000000000000..8f2717b3e4af --- /dev/null +++ b/cpp/ql/test/library-tests/specifiers/Specifiers3.ql @@ -0,0 +1,11 @@ +/** + * @name Specifiers3 + */ +import cpp + +from Variable v, Type t +where v.getType() = t + and t.isConst() +select v.getLocation().getStartLine(), + v + diff --git a/cpp/ql/test/library-tests/specifiers/specifiers.cpp b/cpp/ql/test/library-tests/specifiers/specifiers.cpp new file mode 100644 index 000000000000..db40595b09cb --- /dev/null +++ b/cpp/ql/test/library-tests/specifiers/specifiers.cpp @@ -0,0 +1,22 @@ +extern int i; +extern int i; +extern int i; + +const int c = 7; +const double pi = 3.1415926535897932385; + +unsigned a; + +unsigned int b; +int b2; + + int * const int_pointer1 = &b2; +const int * int_pointer2 = &c; // Note: int_pointer2 is not const itself +const int * const int_pointer3 = &c; + +typedef char* Pchar; + +const char* kings[] = { "Antigonus", "Seleucus", "Ptolemy" }; + +int* p, q; +int v[10], *pv; diff --git a/cpp/ql/test/library-tests/specifiers2/specifiers2.c b/cpp/ql/test/library-tests/specifiers2/specifiers2.c new file mode 100644 index 000000000000..2f5e38495755 --- /dev/null +++ b/cpp/ql/test/library-tests/specifiers2/specifiers2.c @@ -0,0 +1,28 @@ +// Compilable with: +// gcc -c specifiers2.c + +int i; +extern int i; +extern int i; + +extern int j; +extern int j; + +void f(void); +extern void f(void); +extern void f(void); + +extern void g(void); +extern void g(void); + +static volatile int svi; +const int ci; + +void somefun(char * __restrict__ p); + +__thread int ti; + +inline int add(int x, int y) { return x + y; } + +typedef const int constInt; + diff --git a/cpp/ql/test/library-tests/specifiers2/specifiers2.expected b/cpp/ql/test/library-tests/specifiers2/specifiers2.expected new file mode 100644 index 000000000000..c67a5e69d262 --- /dev/null +++ b/cpp/ql/test/library-tests/specifiers2/specifiers2.expected @@ -0,0 +1,140 @@ +| Class | specifiers2pp.cpp:8:7:8:13 | MyClass | MyClass | abstract | +| Class | specifiers2pp.cpp:24:7:24:14 | MyClass2 | MyClass2 | abstract | +| 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 | +| Function | specifiers2.c:15:13:15:13 | g | g | extern | +| Function | specifiers2.c:16:13:16:13 | g | g | extern | +| Function | specifiers2.c:21:6:21:12 | somefun | somefun | extern | +| Function | specifiers2.c:25:12:25:14 | add | add | inline | +| Function | specifiers2pp.cpp:8:7:8:7 | MyClass | MyClass | extern | +| Function | specifiers2pp.cpp:8:7:8:7 | MyClass | MyClass | extern | +| Function | specifiers2pp.cpp:8:7:8:7 | MyClass | MyClass | inline | +| Function | specifiers2pp.cpp:8:7:8:7 | MyClass | MyClass | inline | +| Function | specifiers2pp.cpp:8:7:8:7 | MyClass | MyClass | public | +| Function | specifiers2pp.cpp:8:7:8:7 | MyClass | MyClass | public | +| Function | specifiers2pp.cpp:8:7:8:7 | operator= | operator= | extern | +| Function | specifiers2pp.cpp:8:7:8:7 | operator= | operator= | extern | +| Function | specifiers2pp.cpp:8:7:8:7 | operator= | operator= | inline | +| Function | specifiers2pp.cpp:8:7:8:7 | operator= | operator= | inline | +| Function | specifiers2pp.cpp:8:7:8:7 | operator= | operator= | public | +| Function | specifiers2pp.cpp:8:7:8:7 | operator= | operator= | public | +| Function | specifiers2pp.cpp:10:18:10:24 | MyClass | MyClass | explicit | +| Function | specifiers2pp.cpp:10:18:10:24 | MyClass | MyClass | extern | +| Function | specifiers2pp.cpp:10:18:10:24 | MyClass | MyClass | public | +| Function | specifiers2pp.cpp:12:14:12:22 | publicFun | publicFun | inline | +| Function | specifiers2pp.cpp:12:14:12:22 | publicFun | publicFun | public | +| Function | specifiers2pp.cpp:13:21:13:26 | getInt | getInt | extern | +| Function | specifiers2pp.cpp:13:21:13:26 | getInt | getInt | public | +| Function | specifiers2pp.cpp:13:21:13:26 | getInt | getInt | pure | +| Function | specifiers2pp.cpp:13:21:13:26 | getInt | getInt | virtual | +| Function | specifiers2pp.cpp:14:21:14:21 | f | f | extern | +| Function | specifiers2pp.cpp:14:21:14:21 | f | f | public | +| Function | specifiers2pp.cpp:14:21:14:21 | f | f | virtual | +| Function | specifiers2pp.cpp:18:14:18:23 | privateFun | privateFun | inline | +| Function | specifiers2pp.cpp:18:14:18:23 | privateFun | privateFun | private | +| Function | specifiers2pp.cpp:21:14:21:25 | protectedFun | protectedFun | inline | +| Function | specifiers2pp.cpp:21:14:21:25 | protectedFun | protectedFun | protected | +| Function | specifiers2pp.cpp:24:7:24:7 | MyClass2 | MyClass2 | extern | +| Function | specifiers2pp.cpp:24:7:24:7 | MyClass2 | MyClass2 | extern | +| Function | specifiers2pp.cpp:24:7:24:7 | MyClass2 | MyClass2 | extern | +| Function | specifiers2pp.cpp:24:7:24:7 | MyClass2 | MyClass2 | inline | +| Function | specifiers2pp.cpp:24:7:24:7 | MyClass2 | MyClass2 | inline | +| Function | specifiers2pp.cpp:24:7:24:7 | MyClass2 | MyClass2 | inline | +| Function | specifiers2pp.cpp:24:7:24:7 | MyClass2 | MyClass2 | public | +| Function | specifiers2pp.cpp:24:7:24:7 | MyClass2 | MyClass2 | public | +| Function | specifiers2pp.cpp:24:7:24:7 | MyClass2 | MyClass2 | public | +| Function | specifiers2pp.cpp:24:7:24:7 | operator= | operator= | extern | +| Function | specifiers2pp.cpp:24:7:24:7 | operator= | operator= | extern | +| Function | specifiers2pp.cpp:24:7:24:7 | operator= | operator= | inline | +| Function | specifiers2pp.cpp:24:7:24:7 | operator= | operator= | inline | +| Function | specifiers2pp.cpp:24:7:24:7 | operator= | operator= | public | +| Function | specifiers2pp.cpp:24:7:24:7 | operator= | operator= | public | +| Function | specifiers2pp.cpp:26:21:26:21 | f | f | extern | +| Function | specifiers2pp.cpp:26:21:26:21 | f | f | override | +| Function | specifiers2pp.cpp:26:21:26:21 | f | f | public | +| Function | specifiers2pp.cpp:26:21:26:21 | f | f | virtual | +| Function | specifiers2pp.cpp:29:7:29:7 | operator= | operator= | extern | +| 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= | public | +| Function | specifiers2pp.cpp:29:7:29:7 | operator= | operator= | public | +| Function | specifiers2pp.cpp:33:5:33:18 | fun | fun | public | +| Function | specifiers2pp.cpp:35:7:35:7 | operator= | operator= | extern | +| 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= | 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 | +| 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 | +| FunctionDeclarationEntry | specifiers2.c:12:13:12:13 | declaration of f | f | extern | +| FunctionDeclarationEntry | specifiers2.c:12:13:12:13 | declaration of f | f | void_param_list | +| FunctionDeclarationEntry | specifiers2.c:13:13:13:13 | declaration of f | f | c_linkage | +| FunctionDeclarationEntry | specifiers2.c:13:13:13:13 | declaration of f | f | extern | +| FunctionDeclarationEntry | specifiers2.c:13:13:13:13 | declaration of f | f | void_param_list | +| FunctionDeclarationEntry | specifiers2.c:15:13:15:13 | declaration of g | g | c_linkage | +| FunctionDeclarationEntry | specifiers2.c:15:13:15:13 | declaration of g | g | extern | +| FunctionDeclarationEntry | specifiers2.c:15:13:15:13 | declaration of g | g | void_param_list | +| FunctionDeclarationEntry | specifiers2.c:16:13:16:13 | declaration of g | g | c_linkage | +| FunctionDeclarationEntry | specifiers2.c:16:13:16:13 | declaration of g | g | extern | +| FunctionDeclarationEntry | specifiers2.c:16:13:16:13 | declaration of g | g | void_param_list | +| FunctionDeclarationEntry | specifiers2.c:21:6:21:12 | declaration of somefun | somefun | c_linkage | +| FunctionDeclarationEntry | specifiers2.c:25:12:25:14 | definition of add | add | c_linkage | +| FunctionDeclarationEntry | specifiers2pp.cpp:2:6:2:9 | definition of afun | afun | void_param_list | +| FunctionDeclarationEntry | specifiers2pp.cpp:10:18:10:24 | declaration of MyClass | MyClass | explicit | +| FunctionDeclarationEntry | specifiers2pp.cpp:12:14:12:22 | definition of publicFun | publicFun | void_param_list | +| FunctionDeclarationEntry | specifiers2pp.cpp:13:21:13:26 | declaration of getInt | getInt | virtual | +| FunctionDeclarationEntry | specifiers2pp.cpp:14:21:14:21 | declaration of f | f | virtual | +| FunctionDeclarationEntry | specifiers2pp.cpp:18:14:18:23 | definition of privateFun | privateFun | void_param_list | +| FunctionDeclarationEntry | specifiers2pp.cpp:21:14:21:25 | definition of protectedFun | protectedFun | void_param_list | +| FunctionDeclarationEntry | specifiers2pp.cpp:26:21:26:21 | declaration of f | f | virtual | +| FunctionDeclarationEntry | specifiers2pp.cpp:31:13:31:15 | declaration of fun | fun | void_param_list | +| FunctionDeclarationEntry | specifiers2pp.cpp:33:5:33:18 | definition of fun | fun | void_param_list | +| FunctionDeclarationEntry | specifiers2pp.cpp:40:12:40:18 | declaration of someFun | someFun | extern | +| FunctionDeclarationEntry | specifiers2pp.cpp:41:16:41:23 | declaration of someFun2 | someFun2 | c_linkage | +| FunctionDeclarationEntry | specifiers2pp.cpp:41:16:41:23 | declaration of someFun2 | someFun2 | extern | +| FunctionDeclarationEntry | specifiers2pp.cpp:43:9:43:16 | declaration of someFun3 | someFun3 | c_linkage | +| FunctionDeclarationEntry | specifiers2pp.cpp:43:9:43:16 | declaration of someFun3 | someFun3 | extern | +| FunctionDeclarationEntry | specifiers2pp.cpp:44:16:44:23 | declaration of someFun4 | someFun4 | c_linkage | +| FunctionDeclarationEntry | specifiers2pp.cpp:44:16:44:23 | declaration of someFun4 | someFun4 | static | +| TypedefType | file://:0:0:0:0 | Const | Const | const | +| TypedefType | specifiers2.c:27:19:27:26 | constInt | constInt | const | +| TypedefType | specifiers2pp.cpp:47:20:47:32 | const_pointer | const_pointer | const | +| TypedefType | specifiers2pp.cpp:49:32:49:53 | volatile_const_pointer | volatile_const_pointer | const | +| TypedefType | specifiers2pp.cpp:49:32:49:53 | volatile_const_pointer | volatile_const_pointer | volatile | +| TypedefType | specifiers2pp.cpp:50:32:50:54 | volatile_const_pointer2 | volatile_const_pointer2 | const | +| TypedefType | specifiers2pp.cpp:50:32:50:54 | volatile_const_pointer2 | volatile_const_pointer2 | volatile | +| TypedefType | specifiers2pp.cpp:53:23:53:45 | volatile_const_pointer3 | volatile_const_pointer3 | const | +| TypedefType | specifiers2pp.cpp:53:23:53:45 | volatile_const_pointer3 | volatile_const_pointer3 | volatile | +| TypedefType | specifiers2pp.cpp:54:44:54:74 | volatile_const_restrict_pointer | volatile_const_restrict_pointer | const | +| TypedefType | specifiers2pp.cpp:54:44:54:74 | volatile_const_restrict_pointer | volatile_const_restrict_pointer | restrict | +| TypedefType | specifiers2pp.cpp:54:44:54:74 | volatile_const_restrict_pointer | volatile_const_restrict_pointer | volatile | +| TypedefType | specifiers2pp.cpp:56:28:56:32 | Const | Const | const | +| TypedefType | specifiers2pp.cpp:58:7:58:15 | Const_int | Const_int | const | +| TypedefType | specifiers2pp.cpp:60:28:60:45 | volatile_Const_int | volatile_Const_int | const | +| TypedefType | specifiers2pp.cpp:60:28:60:45 | volatile_Const_int | volatile_Const_int | volatile | +| Variable | specifiers2.c:8:12:8:12 | j | j | extern | +| Variable | specifiers2.c:9:12:9:12 | j | j | extern | +| Variable | specifiers2.c:18:21:18:23 | svi | svi | static | +| Variable | specifiers2pp.cpp:3:9:3:9 | x | x | auto | +| Variable | specifiers2pp.cpp:4:10:4:11 | x2 | x2 | auto | +| Variable | specifiers2pp.cpp:5:18:5:18 | y | y | register | +| Variable | specifiers2pp.cpp:11:13:11:21 | publicInt | publicInt | public | +| Variable | specifiers2pp.cpp:16:13:16:22 | privateInt | privateInt | private | +| Variable | specifiers2pp.cpp:17:21:17:30 | mutableInt | mutableInt | private | +| Variable | specifiers2pp.cpp:20:13:20:24 | protectedInt | protectedInt | protected | +| Variable | specifiers2pp.cpp:52:25:52:27 | vci | vci | static | +| VariableDeclarationEntry | specifiers2.c:5:12:5:12 | declaration of i | i | extern | +| VariableDeclarationEntry | specifiers2.c:6:12:6:12 | declaration of i | i | extern | +| VariableDeclarationEntry | specifiers2.c:8:12:8:12 | declaration of j | j | extern | +| VariableDeclarationEntry | specifiers2.c:9:12:9:12 | declaration of j | j | extern | +| VariableDeclarationEntry | specifiers2.c:18:21:18:23 | definition of svi | svi | static | +| VariableDeclarationEntry | specifiers2pp.cpp:5:18:5:18 | definition of y | y | register | +| VariableDeclarationEntry | specifiers2pp.cpp:17:21:17:30 | definition of mutableInt | mutableInt | mutable | diff --git a/cpp/ql/test/library-tests/specifiers2/specifiers2.ql b/cpp/ql/test/library-tests/specifiers2/specifiers2.ql new file mode 100644 index 000000000000..294b0616ed82 --- /dev/null +++ b/cpp/ql/test/library-tests/specifiers2/specifiers2.ql @@ -0,0 +1,23 @@ +import cpp + +string interesting(Element e) { + (e instanceof Class and result = "Class") or + (e instanceof Function and result = "Function") or + (e instanceof FunctionDeclarationEntry and result = "FunctionDeclarationEntry") or + (e instanceof TypedefType and result = "TypedefType") or + (e instanceof Variable and result = "Variable") or + (e instanceof VariableDeclarationEntry and result = "VariableDeclarationEntry") +} + +from Element e, string name, string specifier +where + ( + name = e.(Declaration).getName() or + name = e.(DeclarationEntry).getName() or + name = e.(Type).toString() + ) and ( + specifier = e.(Declaration).getASpecifier().toString() or + specifier = e.(DeclarationEntry).getASpecifier() or + specifier = e.(TypedefType).getBaseType().getASpecifier().toString() + ) +select interesting(e), e, name, specifier diff --git a/cpp/ql/test/library-tests/specifiers2/specifiers2d.expected b/cpp/ql/test/library-tests/specifiers2/specifiers2d.expected new file mode 100644 index 000000000000..27a1059c3252 --- /dev/null +++ b/cpp/ql/test/library-tests/specifiers2/specifiers2d.expected @@ -0,0 +1,5 @@ +| specifiers2.c:18:21:18:23 | svi | file://:0:0:0:0 | volatile int | file://:0:0:0:0 | volatile | +| specifiers2.c:19:11:19:12 | ci | file://:0:0:0:0 | const int | file://:0:0:0:0 | const | +| specifiers2.c:21:34:21:34 | p | file://:0:0:0:0 | char *__restrict__ | file://:0:0:0:0 | restrict | +| specifiers2pp.cpp:52:25:52:27 | vci | specifiers2pp.cpp:50:32:50:54 | volatile_const_pointer2 | file://:0:0:0:0 | const | +| specifiers2pp.cpp:52:25:52:27 | vci | specifiers2pp.cpp:50:32:50:54 | volatile_const_pointer2 | file://:0:0:0:0 | volatile | diff --git a/cpp/ql/test/library-tests/specifiers2/specifiers2d.ql b/cpp/ql/test/library-tests/specifiers2/specifiers2d.ql new file mode 100644 index 000000000000..a0a8138a062c --- /dev/null +++ b/cpp/ql/test/library-tests/specifiers2/specifiers2d.ql @@ -0,0 +1,8 @@ +import cpp + +from Variable v, Type t +where t = v.getType() +select v, + t, + t.getASpecifier() + diff --git a/cpp/ql/test/library-tests/specifiers2/specifiers2pp.cpp b/cpp/ql/test/library-tests/specifiers2/specifiers2pp.cpp new file mode 100644 index 000000000000..7bceb782ecaf --- /dev/null +++ b/cpp/ql/test/library-tests/specifiers2/specifiers2pp.cpp @@ -0,0 +1,60 @@ + +void afun(void) { + int x = 5; + auto x2 = x; + register int y; +} + +class MyClass { + public: + explicit MyClass (int n); + int publicInt; + void publicFun(void) {} + virtual int getInt() = 0; + virtual int f(); + private: + int privateInt; + mutable int mutableInt; + void privateFun(void) {} + protected: + int protectedInt; + void protectedFun(void) {} +}; + +class MyClass2: public MyClass { + public: + virtual int f() override; +}; + +class SomeClass { + public: + int fun(void); +}; +int SomeClass::fun(void) {} + +class AnotherClass { + friend class MyClass; + friend int SomeClass::fun(); +}; + +extern int someFun(int x); +extern "C" int someFun2(int x); +extern "C" { + int someFun3(int x); + static int someFun4(int x); +} + +typedef int *const const_pointer; + +typedef volatile const_pointer volatile_const_pointer; +typedef volatile_const_pointer volatile_const_pointer2; + +volatile_const_pointer2 vci = 0; +typedef decltype(vci) volatile_const_pointer3; +typedef __restrict volatile_const_pointer3 volatile_const_restrict_pointer; + +template using Const = const T; + +using Const_int = Const; + +typedef volatile Const_int volatile_Const_int; diff --git a/cpp/ql/test/library-tests/specifiers3/structs.c b/cpp/ql/test/library-tests/specifiers3/structs.c new file mode 100644 index 000000000000..76a88d4fb6c5 --- /dev/null +++ b/cpp/ql/test/library-tests/specifiers3/structs.c @@ -0,0 +1,23 @@ +struct pair { + int a; + int b; +}; + +typedef struct pair pair; + +struct rect { + pair* tl; + pair* br; +}; + +struct pair2 { + int a; + int b; +}; + +typedef struct pair2 pair2; + +struct square { + struct pair2* tl; + pair2* br; +}; \ No newline at end of file diff --git a/cpp/ql/test/library-tests/specifiers3/unspecified_member_var_types.expected b/cpp/ql/test/library-tests/specifiers3/unspecified_member_var_types.expected new file mode 100644 index 000000000000..492965392bc8 --- /dev/null +++ b/cpp/ql/test/library-tests/specifiers3/unspecified_member_var_types.expected @@ -0,0 +1,12 @@ +| file://:0:0:0:0 | __va_list_tag | file://:0:0:0:0 | fp_offset | file://:0:0:0:0 | unsigned int | file://:0:0:0:0 | unsigned int | +| file://:0:0:0:0 | __va_list_tag | file://:0:0:0:0 | gp_offset | file://:0:0:0:0 | unsigned int | file://:0:0:0:0 | unsigned int | +| file://:0:0:0:0 | __va_list_tag | file://:0:0:0:0 | overflow_arg_area | file://:0:0:0:0 | void * | file://:0:0:0:0 | void * | +| file://:0:0:0:0 | __va_list_tag | file://:0:0:0:0 | reg_save_area | file://:0:0:0:0 | void * | file://:0:0:0:0 | void * | +| structs.c:1:8:1:11 | pair | structs.c:2:7:2:7 | a | file://:0:0:0:0 | int | file://:0:0:0:0 | int | +| structs.c:1:8:1:11 | pair | structs.c:3:7:3:7 | b | file://:0:0:0:0 | int | file://:0:0:0:0 | int | +| structs.c:8:8:8:11 | rect | structs.c:9:9:9:10 | tl | file://:0:0:0:0 | pair * | file://:0:0:0:0 | pair * | +| structs.c:8:8:8:11 | rect | structs.c:10:9:10:10 | br | file://:0:0:0:0 | pair * | file://:0:0:0:0 | pair * | +| structs.c:13:8:13:12 | pair2 | structs.c:14:7:14:7 | a | file://:0:0:0:0 | int | file://:0:0:0:0 | int | +| structs.c:13:8:13:12 | pair2 | structs.c:15:7:15:7 | b | file://:0:0:0:0 | int | file://:0:0:0:0 | int | +| structs.c:20:8:20:13 | square | structs.c:21:17:21:18 | tl | file://:0:0:0:0 | pair2 * | file://:0:0:0:0 | pair2 * | +| structs.c:20:8:20:13 | square | structs.c:22:10:22:11 | br | file://:0:0:0:0 | pair2 * | file://:0:0:0:0 | pair2 * | diff --git a/cpp/ql/test/library-tests/specifiers3/unspecified_member_var_types.ql b/cpp/ql/test/library-tests/specifiers3/unspecified_member_var_types.ql new file mode 100644 index 000000000000..9b4c8645f5bf --- /dev/null +++ b/cpp/ql/test/library-tests/specifiers3/unspecified_member_var_types.ql @@ -0,0 +1,4 @@ +import cpp + +from Struct s +select s, s.getAMemberVariable() as v, v.getType() as t, t.getUnspecifiedType() as u \ No newline at end of file diff --git a/cpp/ql/test/library-tests/static_assert/expr.expected b/cpp/ql/test/library-tests/static_assert/expr.expected new file mode 100644 index 000000000000..954ce9c6d5d6 --- /dev/null +++ b/cpp/ql/test/library-tests/static_assert/expr.expected @@ -0,0 +1,19 @@ +| static_assert.cpp:2:15:2:15 | 3 | static_assert.cpp:2:15:2:19 | ... + ... | +| static_assert.cpp:2:15:2:19 | ... + ... | static_assert.cpp:2:15:2:24 | ... == ... | +| static_assert.cpp:2:15:2:24 | ... == ... | static_assert.cpp:2:1:2:46 | static_assert(..., "Addition is sane") | +| static_assert.cpp:2:19:2:19 | 4 | static_assert.cpp:2:15:2:19 | ... + ... | +| static_assert.cpp:2:24:2:24 | 7 | static_assert.cpp:2:15:2:24 | ... == ... | +| static_assert.cpp:3:15:3:15 | 3 | static_assert.cpp:3:15:3:19 | ... / ... | +| static_assert.cpp:3:15:3:19 | ... / ... | static_assert.cpp:3:15:3:24 | ... == ... | +| static_assert.cpp:3:15:3:24 | ... == ... | static_assert.cpp:3:1:3:50 | static_assert(..., "Division rounds down") | +| static_assert.cpp:3:19:3:19 | 4 | static_assert.cpp:3:15:3:19 | ... / ... | +| static_assert.cpp:3:24:3:24 | 0 | static_assert.cpp:3:15:3:24 | ... == ... | +| static_assert.cpp:7:17:7:25 | sizeof() | static_assert.cpp:7:17:7:38 | ... == ... | +| static_assert.cpp:7:17:7:38 | ... == ... | static_assert.cpp:7:3:7:63 | static_assert(..., "Type sizes are sane") | +| static_assert.cpp:7:24:7:24 | x | static_assert.cpp:7:17:7:25 | sizeof() | +| static_assert.cpp:7:30:7:38 | sizeof() | static_assert.cpp:7:17:7:38 | ... == ... | +| static_assert.cpp:7:37:7:37 | y | static_assert.cpp:7:30:7:38 | sizeof() | +| static_assert.cpp:8:10:8:10 | x | static_assert.cpp:8:10:8:14 | ... + ... | +| static_assert.cpp:8:10:8:14 | ... + ... | static_assert.cpp:8:3:8:15 | return ... | +| static_assert.cpp:8:14:8:14 | y | static_assert.cpp:8:10:8:14 | ... + ... | +| static_assert.cpp:11:15:11:15 | 1 | static_assert.cpp:11:1:11:72 | static_assert(..., "Look at this wide-char string containing a \u0000 byte") | diff --git a/cpp/ql/test/library-tests/static_assert/expr.ql b/cpp/ql/test/library-tests/static_assert/expr.ql new file mode 100644 index 000000000000..86aecf213f24 --- /dev/null +++ b/cpp/ql/test/library-tests/static_assert/expr.ql @@ -0,0 +1,4 @@ +import cpp + +from Expr e +select e, e.getParent() diff --git a/cpp/ql/test/library-tests/static_assert/options b/cpp/ql/test/library-tests/static_assert/options new file mode 100644 index 000000000000..61b5b18daf3c --- /dev/null +++ b/cpp/ql/test/library-tests/static_assert/options @@ -0,0 +1 @@ +extractor_flags: --edg --target --edg win64 diff --git a/cpp/ql/test/library-tests/static_assert/static_assert.cpp b/cpp/ql/test/library-tests/static_assert/static_assert.cpp new file mode 100644 index 000000000000..8da8e1a9c0f8 --- /dev/null +++ b/cpp/ql/test/library-tests/static_assert/static_assert.cpp @@ -0,0 +1,11 @@ +// Let's just check a few things... +static_assert(3 + 4 == 7, "Addition is sane"); +static_assert(3 / 4 == 0, "Division rounds down"); + +int add(int x, int y) +{ + static_assert(sizeof(x) == sizeof(y), "Type sizes are sane"); + return x + y; +} + +static_assert(1, L"Look at this wide-char string containing a \0 byte"); diff --git a/cpp/ql/test/library-tests/static_assert/static_assert.expected b/cpp/ql/test/library-tests/static_assert/static_assert.expected new file mode 100644 index 000000000000..a30793c95fd9 --- /dev/null +++ b/cpp/ql/test/library-tests/static_assert/static_assert.expected @@ -0,0 +1,4 @@ +| static_assert.cpp:2:1:2:46 | static_assert(..., "Addition is sane") | static_assert.cpp:2:15:2:24 | ... == ... | 1 | +| static_assert.cpp:3:1:3:50 | static_assert(..., "Division rounds down") | static_assert.cpp:3:15:3:24 | ... == ... | 0 | +| static_assert.cpp:7:3:7:63 | static_assert(..., "Type sizes are sane") | static_assert.cpp:7:17:7:38 | ... == ... | 0 | +| static_assert.cpp:11:1:11:72 | static_assert(..., "Look at this wide-char string containing a \u0000 byte") | static_assert.cpp:11:15:11:15 | 1 | 0 | diff --git a/cpp/ql/test/library-tests/static_assert/static_assert.ql b/cpp/ql/test/library-tests/static_assert/static_assert.ql new file mode 100644 index 000000000000..1332f1212868 --- /dev/null +++ b/cpp/ql/test/library-tests/static_assert/static_assert.ql @@ -0,0 +1,4 @@ +import cpp + +from StaticAssert sa +select sa, sa.getCondition(), count(Comment c | c.getCommentedElement() = sa) diff --git a/cpp/ql/test/library-tests/static_cast/expr.expected b/cpp/ql/test/library-tests/static_cast/expr.expected new file mode 100644 index 000000000000..7dc4465d718f --- /dev/null +++ b/cpp/ql/test/library-tests/static_cast/expr.expected @@ -0,0 +1,6 @@ +| file://:0:0:0:0 | this | +| ms.cpp:3:10:3:12 | 0 | +| ms.cpp:3:10:3:12 | constructor init of field x | +| ms.cpp:3:16:3:40 | static_cast... | +| ms.cpp:3:39:3:39 | x | +| ms.cpp:5:3:5:3 | call to S | diff --git a/cpp/ql/test/library-tests/static_cast/expr.ql b/cpp/ql/test/library-tests/static_cast/expr.ql new file mode 100644 index 000000000000..3c2a3269e72e --- /dev/null +++ b/cpp/ql/test/library-tests/static_cast/expr.ql @@ -0,0 +1,5 @@ +import cpp + +from Expr e +select e + diff --git a/cpp/ql/test/library-tests/static_cast/ms.cpp b/cpp/ql/test/library-tests/static_cast/ms.cpp new file mode 100644 index 000000000000..56f75d8f8e3a --- /dev/null +++ b/cpp/ql/test/library-tests/static_cast/ms.cpp @@ -0,0 +1,6 @@ + +struct S { + S() : x() { static_cast(x); } + int x; +} s; + diff --git a/cpp/ql/test/library-tests/static_cast/options b/cpp/ql/test/library-tests/static_cast/options new file mode 100644 index 000000000000..de6ee356c9a0 --- /dev/null +++ b/cpp/ql/test/library-tests/static_cast/options @@ -0,0 +1 @@ +extractor_flags: --microsoft --edg --no_preserve_lvalues_with_same_type_casts diff --git a/cpp/ql/test/library-tests/stmt/break/break.expected b/cpp/ql/test/library-tests/stmt/break/break.expected new file mode 100644 index 000000000000..cf8f351496bb --- /dev/null +++ b/cpp/ql/test/library-tests/stmt/break/break.expected @@ -0,0 +1,5 @@ +| test.c:7:4:7:9 | break; | test.c:6:3:8:3 | while (...) ... | +| test.c:11:4:11:9 | break; | test.c:10:3:12:14 | do (...) ... | +| test.c:16:4:16:9 | break; | test.c:14:3:23:3 | switch (...) ... | +| test.c:19:5:19:10 | break; | test.c:18:4:20:4 | for(...;...;...) ... | +| test.c:27:3:27:8 | break; | test.c:5:2:30:2 | for(...;...;...) ... | diff --git a/cpp/ql/test/library-tests/stmt/break/break.ql b/cpp/ql/test/library-tests/stmt/break/break.ql new file mode 100644 index 000000000000..d33941a0c099 --- /dev/null +++ b/cpp/ql/test/library-tests/stmt/break/break.ql @@ -0,0 +1,5 @@ +import cpp + +from BreakStmt b, Stmt s +where b.getBreakable() = s +select b, s diff --git a/cpp/ql/test/library-tests/stmt/break/continue.expected b/cpp/ql/test/library-tests/stmt/break/continue.expected new file mode 100644 index 000000000000..164a775b34c4 --- /dev/null +++ b/cpp/ql/test/library-tests/stmt/break/continue.expected @@ -0,0 +1,3 @@ +| test.c:22:4:22:12 | continue; | test.c:5:2:30:2 | for(...;...;...) ... | +| test.c:25:4:25:12 | continue; | test.c:24:3:26:3 | for(...;...;...) ... | +| test.c:29:5:29:13 | continue; | test.c:5:2:30:2 | for(...;...;...) ... | diff --git a/cpp/ql/test/library-tests/stmt/break/continue.ql b/cpp/ql/test/library-tests/stmt/break/continue.ql new file mode 100644 index 000000000000..d7d48732986d --- /dev/null +++ b/cpp/ql/test/library-tests/stmt/break/continue.ql @@ -0,0 +1,5 @@ +import cpp + +from ContinueStmt c, Stmt s +where c.getContinuable() = s +select c, s diff --git a/cpp/ql/test/library-tests/stmt/break/test.c b/cpp/ql/test/library-tests/stmt/break/test.c new file mode 100644 index 000000000000..93e994347048 --- /dev/null +++ b/cpp/ql/test/library-tests/stmt/break/test.c @@ -0,0 +1,31 @@ +int test() { + int i; + int j; + int x; + for (i = 0; i < 10; i++) { + while(1) { + break; + } + + do { + break; + } while (1); + + switch(i) { + case 1: + break; + case 2: + for(j=0; j < 10; j++) { + break; + } + default: + continue; + } + for(j = 0; j < 10; j++) { + continue; + } + break; + + ({continue;}); + } +} diff --git a/cpp/ql/test/library-tests/stmt/computed_goto/computedgoto.c b/cpp/ql/test/library-tests/stmt/computed_goto/computedgoto.c new file mode 100644 index 000000000000..bd2ab2ad5c62 --- /dev/null +++ b/cpp/ql/test/library-tests/stmt/computed_goto/computedgoto.c @@ -0,0 +1,16 @@ + +void computed_goto_test() +{ + void *ptr; + int i = 0; + +myLabel1: + if (++i == 1) + { + ptr = &&myLabel1; + } else { + ptr = &&myLabel2; + } + goto *ptr; +myLabel2: +} diff --git a/cpp/ql/test/library-tests/stmt/computed_goto/computedgoto.expected b/cpp/ql/test/library-tests/stmt/computed_goto/computedgoto.expected new file mode 100644 index 000000000000..70e07fd025c7 --- /dev/null +++ b/cpp/ql/test/library-tests/stmt/computed_goto/computedgoto.expected @@ -0,0 +1 @@ +| computedgoto.c:14:5:14:14 | computed goto ... | computedgoto.c:14:11:14:13 | ptr | diff --git a/cpp/ql/test/library-tests/stmt/computed_goto/computedgoto.ql b/cpp/ql/test/library-tests/stmt/computed_goto/computedgoto.ql new file mode 100644 index 000000000000..c1444aad55c3 --- /dev/null +++ b/cpp/ql/test/library-tests/stmt/computed_goto/computedgoto.ql @@ -0,0 +1,4 @@ +import cpp + +from ComputedGotoStmt cgs +select cgs, cgs.getExpr() diff --git a/cpp/ql/test/library-tests/stmt/computed_goto/labelliteral.expected b/cpp/ql/test/library-tests/stmt/computed_goto/labelliteral.expected new file mode 100644 index 000000000000..f0210e0d649e --- /dev/null +++ b/cpp/ql/test/library-tests/stmt/computed_goto/labelliteral.expected @@ -0,0 +1,2 @@ +| computedgoto.c:10:9:10:18 | &&myLabel1 | computedgoto.c:7:1:7:9 | label ...: | +| computedgoto.c:12:9:12:18 | &&myLabel2 | computedgoto.c:15:1:15:9 | label ...: | diff --git a/cpp/ql/test/library-tests/stmt/computed_goto/labelliteral.ql b/cpp/ql/test/library-tests/stmt/computed_goto/labelliteral.ql new file mode 100644 index 000000000000..656bf8015fe0 --- /dev/null +++ b/cpp/ql/test/library-tests/stmt/computed_goto/labelliteral.ql @@ -0,0 +1,4 @@ +import cpp + +from LabelLiteral ll +select ll, ll.getLabel() diff --git a/cpp/ql/test/library-tests/stmt/computed_goto/labelstmt.expected b/cpp/ql/test/library-tests/stmt/computed_goto/labelstmt.expected new file mode 100644 index 000000000000..3137b8ec5f14 --- /dev/null +++ b/cpp/ql/test/library-tests/stmt/computed_goto/labelstmt.expected @@ -0,0 +1,2 @@ +| computedgoto.c:7:1:7:9 | label ...: | myLabel1 | +| computedgoto.c:15:1:15:9 | label ...: | myLabel2 | diff --git a/cpp/ql/test/library-tests/stmt/computed_goto/labelstmt.ql b/cpp/ql/test/library-tests/stmt/computed_goto/labelstmt.ql new file mode 100644 index 000000000000..f76d57ea1185 --- /dev/null +++ b/cpp/ql/test/library-tests/stmt/computed_goto/labelstmt.ql @@ -0,0 +1,4 @@ +import cpp + +from LabelStmt ls +select ls, ls.getName() diff --git a/cpp/ql/test/library-tests/stmt/generated_blocks/options b/cpp/ql/test/library-tests/stmt/generated_blocks/options new file mode 100644 index 000000000000..03328e8715c1 --- /dev/null +++ b/cpp/ql/test/library-tests/stmt/generated_blocks/options @@ -0,0 +1 @@ +extractor_flags: -std=gnu99 diff --git a/cpp/ql/test/library-tests/stmt/generated_blocks/stmt.expected b/cpp/ql/test/library-tests/stmt/generated_blocks/stmt.expected new file mode 100644 index 000000000000..c470734d5665 --- /dev/null +++ b/cpp/ql/test/library-tests/stmt/generated_blocks/stmt.expected @@ -0,0 +1,8 @@ +| test.c:2:15:7:1 | { ... } | file://:0:0:0:0 | | +| test.c:3:5:3:5 | { ... } | test.c:2:15:7:1 | { ... } | +| test.c:3:5:6:5 | switch (...) ... | test.c:3:5:3:5 | { ... } | +| test.c:3:16:6:5 | { ... } | test.c:3:5:6:5 | switch (...) ... | +| test.c:4:7:4:13 | case ...: | test.c:3:16:6:5 | { ... } | +| test.c:5:9:5:14 | break; | test.c:3:16:6:5 | { ... } | +| test.c:6:5:6:5 | label ...: | test.c:3:5:3:5 | { ... } | +| test.c:7:1:7:1 | return ... | test.c:2:15:7:1 | { ... } | diff --git a/cpp/ql/test/library-tests/stmt/generated_blocks/stmt.ql b/cpp/ql/test/library-tests/stmt/generated_blocks/stmt.ql new file mode 100644 index 000000000000..834687f6c9a7 --- /dev/null +++ b/cpp/ql/test/library-tests/stmt/generated_blocks/stmt.ql @@ -0,0 +1,52 @@ +import cpp + +newtype TMaybeStmtParent = TStmtParent(StmtParent p) or TNoStmtParent() + +class MaybeStmtParent extends TMaybeStmtParent { + abstract string toString(); + abstract Location getLocation(); +} + +class YesMaybeStmtParent extends MaybeStmtParent { + StmtParent p; + + YesMaybeStmtParent() { + this = TStmtParent(p) + } + + override string toString() { + result = p.toString() + } + + override Location getLocation() { + result = p.getLocation() + } + + StmtParent getStmtParent() { + result = p + } +} + +class NoMaybeStmtParent extends MaybeStmtParent { + NoMaybeStmtParent() { + this = TNoStmtParent() + } + + override string toString() { + result = "" + } + + override Location getLocation() { + result instanceof UnknownLocation + } +} + +MaybeStmtParent parent(Stmt s) { + if exists(s.getParent()) + then result.(YesMaybeStmtParent).getStmtParent() = s.getParent() + else result instanceof NoMaybeStmtParent +} + +from Stmt s +select s, parent(s) + diff --git a/cpp/ql/test/library-tests/stmt/generated_blocks/test.c b/cpp/ql/test/library-tests/stmt/generated_blocks/test.c new file mode 100644 index 000000000000..58c84e7e9205 --- /dev/null +++ b/cpp/ql/test/library-tests/stmt/generated_blocks/test.c @@ -0,0 +1,8 @@ + +void f(int i) { + switch (i) { + case 1: + break; + } +} + diff --git a/cpp/ql/test/library-tests/stmt/stmt/stmt.c b/cpp/ql/test/library-tests/stmt/stmt/stmt.c new file mode 100644 index 000000000000..f6181880f2d9 --- /dev/null +++ b/cpp/ql/test/library-tests/stmt/stmt/stmt.c @@ -0,0 +1,43 @@ + +int main() +{ + asm volatile("int 21h"); + ; +} + +void if_test(int c1, int c2, int c3, int c4, int c5) +{ + if (c1) + { + if (c2) + { + ; + } else { + ; + } + } else { + if (c3) + { + ; + } else { + ; + } + } + + if (c4) ; + if (c5) ; else ; +} + +void for_test() +{ + int i, j; + + for (; ;) ; + + for (i = 0; i < 10; i++) + { + for (j = 0; j < 10; j++) + { + } + } +} diff --git a/cpp/ql/test/library-tests/stmt/stmt/stmt.expected b/cpp/ql/test/library-tests/stmt/stmt/stmt.expected new file mode 100644 index 000000000000..e79944c34366 --- /dev/null +++ b/cpp/ql/test/library-tests/stmt/stmt/stmt.expected @@ -0,0 +1,35 @@ +| stmt.c:3:1:6:1 | { ... } | | +| stmt.c:4:2:4:25 | asm statement | AsmStmt | +| stmt.c:5:2:5:2 | ; | EmptyStmt | +| stmt.c:6:1:6:1 | return ... | | +| stmt.c:9:1:29:1 | { ... } | | +| stmt.c:10:2:25:2 | if (...) ... | | +| stmt.c:11:2:18:2 | { ... } | | +| stmt.c:12:3:17:3 | if (...) ... | | +| stmt.c:13:3:15:3 | { ... } | | +| stmt.c:14:4:14:4 | ; | EmptyStmt | +| stmt.c:15:10:17:3 | { ... } | | +| stmt.c:16:4:16:4 | ; | EmptyStmt | +| stmt.c:18:9:25:2 | { ... } | | +| stmt.c:19:3:24:3 | if (...) ... | | +| stmt.c:20:3:22:3 | { ... } | | +| stmt.c:21:4:21:4 | ; | EmptyStmt | +| stmt.c:22:10:24:3 | { ... } | | +| stmt.c:23:4:23:4 | ; | EmptyStmt | +| stmt.c:27:2:27:10 | if (...) ... | | +| stmt.c:27:10:27:10 | ; | EmptyStmt | +| stmt.c:28:2:28:17 | if (...) ... | | +| stmt.c:28:10:28:10 | ; | EmptyStmt | +| stmt.c:28:17:28:17 | ; | EmptyStmt | +| stmt.c:29:1:29:1 | return ... | | +| stmt.c:32:1:43:1 | { ... } | | +| stmt.c:33:2:33:10 | declaration | | +| stmt.c:35:2:35:12 | for(...;...;...) ... | | +| stmt.c:35:12:35:12 | ; | EmptyStmt | +| stmt.c:37:2:42:2 | for(...;...;...) ... | | +| stmt.c:37:7:37:12 | ExprStmt | | +| stmt.c:38:2:42:2 | { ... } | | +| stmt.c:39:3:41:3 | for(...;...;...) ... | | +| stmt.c:39:8:39:13 | ExprStmt | | +| stmt.c:40:3:41:3 | { ... } | | +| stmt.c:43:1:43:1 | return ... | | diff --git a/cpp/ql/test/library-tests/stmt/stmt/stmt.ql b/cpp/ql/test/library-tests/stmt/stmt/stmt.ql new file mode 100644 index 000000000000..b574c337860b --- /dev/null +++ b/cpp/ql/test/library-tests/stmt/stmt/stmt.ql @@ -0,0 +1,6 @@ +import cpp + +from Stmt s, string empty, string asm +where if s instanceof EmptyStmt then empty = "EmptyStmt" else empty = "" +and if s instanceof AsmStmt then asm = "AsmStmt" else asm = "" +select s, empty + asm diff --git a/cpp/ql/test/library-tests/stmt_expr/cfg.expected b/cpp/ql/test/library-tests/stmt_expr/cfg.expected new file mode 100644 index 000000000000..c39020724fd1 --- /dev/null +++ b/cpp/ql/test/library-tests/stmt_expr/cfg.expected @@ -0,0 +1,42 @@ +| 4 | 1 | { ... } | maxint3 | 5 | return ... | +| 5 | 2 | return ... | ----- | 5 | { ... } | +| 5 | 3 | { ... } | ----- | 5 | declaration | +| 5 | 4 | declaration | ----- | 5 | initializer for _a | +| 5 | 5 | initializer for _a | ----- | 5 | { ... } | +| 5 | 6 | { ... } | ----- | 5 | declaration | +| 5 | 7 | declaration | ----- | 5 | initializer for _a | +| 5 | 8 | initializer for _a | ----- | 5 | a | +| 5 | 9 | a | ----- | 5 | initializer for _b | +| 5 | 10 | initializer for _b | ----- | 5 | b | +| 5 | 11 | b | ----- | 5 | ExprStmt | +| 5 | 12 | ExprStmt | ----- | 5 | ... ? ... : ... | +| 5 | 13 | ... ? ... : ... | ----- | 5 | _a | +| 5 | 14 | _a | ----- | 5 | _b | +| 5 | 15 | _b | ----- | 5 | ... > ... | +| 5 | 16 | ... > ... | ----- | 5 | _a | +| 5 | 16 | ... > ... | ----- | 5 | _b | +| 5 | 17 | _a | ----- | 5 | (statement expression) | +| 5 | 17 | _b | ----- | 5 | (statement expression) | +| 5 | 19 | (statement expression) | ----- | 5 | initializer for _b | +| 5 | 20 | initializer for _b | ----- | 5 | c | +| 5 | 21 | c | ----- | 5 | ExprStmt | +| 5 | 22 | ExprStmt | ----- | 5 | ... ? ... : ... | +| 5 | 23 | ... ? ... : ... | ----- | 5 | _a | +| 5 | 24 | _a | ----- | 5 | _b | +| 5 | 25 | _b | ----- | 5 | ... > ... | +| 5 | 26 | ... > ... | ----- | 5 | _a | +| 5 | 26 | ... > ... | ----- | 5 | _b | +| 5 | 27 | _a | ----- | 5 | (statement expression) | +| 5 | 27 | _b | ----- | 5 | (statement expression) | +| 5 | 29 | (statement expression) | ----- | 4 | maxint3 | +| 10 | 1 | { ... } | g1 | 11 | return ... | +| 11 | 2 | return ... | ----- | 11 | { ... } | +| 11 | 3 | { ... } | ----- | 11 | ExprStmt | +| 11 | 4 | ExprStmt | ----- | 11 | d | +| 11 | 5 | d | ----- | 11 | call to D | +| 11 | 6 | call to D | ----- | 11 | (statement expression) | +| 11 | 7 | (statement expression) | ----- | 10 | g1 | +| 14 | 1 | { ... } | g2 | 15 | return ... | +| 15 | 2 | return ... | ----- | 15 | d | +| 15 | 3 | d | ----- | 15 | call to D | +| 15 | 4 | call to D | ----- | 14 | g2 | diff --git a/cpp/ql/test/library-tests/stmt_expr/cfg.ql b/cpp/ql/test/library-tests/stmt_expr/cfg.ql new file mode 100644 index 000000000000..6d8f598ab6de --- /dev/null +++ b/cpp/ql/test/library-tests/stmt_expr/cfg.ql @@ -0,0 +1,15 @@ +import cpp +import semmle.code.cpp.exprs.ObjectiveC + +from ControlFlowNode x, ControlFlowNode y, string entryPoint +where y = x.getASuccessor() + and if exists(Function f | f.getEntryPoint() = x) + then forex(Function f | f.getEntryPoint() = x | entryPoint = f.toString()) + else entryPoint = "-----" +select x.getLocation().getStartLine(), + count(x.getAPredecessor*()), // This helps order things sensibly + x.toString(), + entryPoint, + y.getLocation().getStartLine(), + y.toString() + diff --git a/cpp/ql/test/library-tests/stmt_expr/stmt_expr.cpp b/cpp/ql/test/library-tests/stmt_expr/stmt_expr.cpp new file mode 100644 index 000000000000..7c46a147285a --- /dev/null +++ b/cpp/ql/test/library-tests/stmt_expr/stmt_expr.cpp @@ -0,0 +1,16 @@ +#define maxint(a,b) \ + ({int _a = (a), _b = (b); _a > _b ? _a : _b; }) + +int maxint3(int a, int b, int c) { + return maxint(maxint(a, b), c); +} + +struct D { D(D const&); }; + +D g1(D d) { + return ({ d; }); +} + +D g2(D d) { + return d; +} diff --git a/cpp/ql/test/library-tests/store_pointer_to_member/ast.expected b/cpp/ql/test/library-tests/store_pointer_to_member/ast.expected new file mode 100644 index 000000000000..502f3a2c62ae --- /dev/null +++ b/cpp/ql/test/library-tests/store_pointer_to_member/ast.expected @@ -0,0 +1,2 @@ +| main.cpp:14:42:14:43 | pa | main.cpp:14:39:14:39 | f | main.cpp:14:22:14:44 | initializer for f_pa | +| main.cpp:15:45:15:46 | pa | main.cpp:15:40:15:41 | pf | main.cpp:15:23:15:47 | initializer for pf_pa | diff --git a/cpp/ql/test/library-tests/store_pointer_to_member/ast.ql b/cpp/ql/test/library-tests/store_pointer_to_member/ast.ql new file mode 100644 index 000000000000..e5e426326d2d --- /dev/null +++ b/cpp/ql/test/library-tests/store_pointer_to_member/ast.ql @@ -0,0 +1,4 @@ +import cpp + +from VariableAccess va +select va, va.getQualifier(), va.getParent() diff --git a/cpp/ql/test/library-tests/store_pointer_to_member/main.cpp b/cpp/ql/test/library-tests/store_pointer_to_member/main.cpp new file mode 100644 index 000000000000..0a5242ce5b86 --- /dev/null +++ b/cpp/ql/test/library-tests/store_pointer_to_member/main.cpp @@ -0,0 +1,19 @@ +struct foo { + void a() { + } +}; + +int main() { + foo f; + foo* pf = &f; + void (foo::* pa)() = &foo::a; + + // NB: The C++ standard states that "if the result of .* or ->* is a function, then that + // result can be used only as the operand for the function call operator ()", but g++ 4.4 + // and later accept the following anyway. + void(*f_pa)(foo&) = (void(*)(foo&))(f.*pa); + void(*pf_pa)(foo&) = (void(*)(foo&))(pf->*pa); + + f_pa(f); + pf_pa(f); +} diff --git a/cpp/ql/test/library-tests/store_pointer_to_member/options b/cpp/ql/test/library-tests/store_pointer_to_member/options new file mode 100644 index 000000000000..c94b11fb4451 --- /dev/null +++ b/cpp/ql/test/library-tests/store_pointer_to_member/options @@ -0,0 +1 @@ +extractor_flags: --gnu_version 40400 diff --git a/cpp/ql/test/library-tests/strcat/strcat.c b/cpp/ql/test/library-tests/strcat/strcat.c new file mode 100644 index 000000000000..ea723e1e0f55 --- /dev/null +++ b/cpp/ql/test/library-tests/strcat/strcat.c @@ -0,0 +1,25 @@ + +extern void fiddle_with_string(const char **pstr); + +void f(void) { + const char *str1 = "my string"; + const char *str2 = "my other string"; + const char *str3 = "yet another string"; + char output1[100]; + char output2[100]; + char output3[100]; + char output4[100]; + + str2 = "yet another string"; + fiddle_with_string(&str3); + + output1[0] = '\0'; + output2[0] = '\0'; + output3[0] = '\0'; + output4[0] = '\0'; + strcat(output1, str1); + strcat(output2, str1); + strcat(output3, str2); // Bad, as str2 gets reassigned + strcat(output4, str3); // Bad, as str3 gets fiddled with +} + diff --git a/cpp/ql/test/library-tests/strcat/strcat.expected b/cpp/ql/test/library-tests/strcat/strcat.expected new file mode 100644 index 000000000000..d414ad1995d7 --- /dev/null +++ b/cpp/ql/test/library-tests/strcat/strcat.expected @@ -0,0 +1,2 @@ +| strcat.c:22:5:22:10 | call to strcat | Always check the size of the source buffer when using strcat. | +| strcat.c:23:5:23:10 | call to strcat | Always check the size of the source buffer when using strcat. | diff --git a/cpp/ql/test/library-tests/strcat/strcat.qlref b/cpp/ql/test/library-tests/strcat/strcat.qlref new file mode 100644 index 000000000000..9790cddebab5 --- /dev/null +++ b/cpp/ql/test/library-tests/strcat/strcat.qlref @@ -0,0 +1 @@ +Likely Bugs/Memory Management/UnsafeUseOfStrcat.ql diff --git a/cpp/ql/test/library-tests/string_analysis/StringAnalysis.expected b/cpp/ql/test/library-tests/string_analysis/StringAnalysis.expected new file mode 100644 index 000000000000..80c6946afcb9 --- /dev/null +++ b/cpp/ql/test/library-tests/string_analysis/StringAnalysis.expected @@ -0,0 +1,22 @@ +| string_analysis.cpp:6:2:6:11 | ... = ... | 2 | +| string_analysis.cpp:6:9:6:11 | 1 | 2 | +| string_analysis.cpp:7:2:7:12 | ... = ... | 3 | +| string_analysis.cpp:7:9:7:12 | 22 | 3 | +| string_analysis.cpp:8:2:8:13 | ... = ... | 4 | +| string_analysis.cpp:8:9:8:13 | 333 | 4 | +| string_analysis.cpp:10:2:10:14 | ... = ... | 2 | +| string_analysis.cpp:10:11:10:14 | str1 | 2 | +| string_analysis.cpp:13:3:13:32 | ... = ... | 4 | +| string_analysis.cpp:13:12:13:32 | (...) | 4 | +| string_analysis.cpp:13:13:13:31 | ... ? ... : ... | 4 | +| string_analysis.cpp:13:21:13:24 | str2 | 3 | +| string_analysis.cpp:13:28:13:31 | str3 | 4 | +| string_analysis.cpp:15:2:15:33 | ... = ... | 4 | +| string_analysis.cpp:15:11:15:33 | (...) | 4 | +| string_analysis.cpp:15:12:15:32 | ... ? ... : ... | 4 | +| string_analysis.cpp:15:20:15:23 | str1 | 2 | +| string_analysis.cpp:15:27:15:32 | result | 4 | +| string_analysis.cpp:16:20:16:23 | str1 | 2 | +| string_analysis.cpp:40:19:40:25 | [?] | 5 | +| string_analysis.cpp:41:19:41:23 | wstr1 | 5 | +| string_analysis.cpp:42:21:42:28 | 666666 | 7 | diff --git a/cpp/ql/test/library-tests/string_analysis/StringAnalysis.ql b/cpp/ql/test/library-tests/string_analysis/StringAnalysis.ql new file mode 100644 index 000000000000..730d3ceb2308 --- /dev/null +++ b/cpp/ql/test/library-tests/string_analysis/StringAnalysis.ql @@ -0,0 +1,14 @@ +/** + * @name StringAnalysis + * @kind table + */ +import cpp + +from AnalysedString s, string str +where + if s.(StringLiteral).getType().getUnspecifiedType().(DerivedType).getBaseType() instanceof Wchar_t then ( + str = "[?]" + ) else ( + str = s.toString() + ) +select s.getLocation().toString(), str, s.getMaxLength() diff --git a/cpp/ql/test/library-tests/string_analysis/string_analysis.cpp b/cpp/ql/test/library-tests/string_analysis/string_analysis.cpp new file mode 100644 index 000000000000..283baabd2ac2 --- /dev/null +++ b/cpp/ql/test/library-tests/string_analysis/string_analysis.cpp @@ -0,0 +1,44 @@ +int main(int argc, char *argv[]) +{ + char *str1, *str2, *str3, *result; + int cond1, cond2, cond3, cond4; + + str1 = "1"; + str2 = "22"; + str3 = "333"; + + result = str1; // max length 1 + if (cond1) + { + result = (cond2 ? str2 : str3); // max length 3 + } + result = (cond3 ? str1 : result); // max length 3 + result = (cond4 ? str1 : argv[0]); // max unknown + + return 0; +} + +namespace std +{ + class string + { + public: + string(char *_str) : str(_str) {}; + ~string() {}; + + string &operator=(string &other) { + str = other.str; + }; + + private: + char *str; + }; +} + +void more_cases() +{ + wchar_t *wstr1 = L"4444"; + wchar_t *wstr2 = wstr1; + std::string str1 = "666666"; + std::string str2 = str1; +} diff --git a/cpp/ql/test/library-tests/string_literals/string_literals.c b/cpp/ql/test/library-tests/string_literals/string_literals.c new file mode 100644 index 000000000000..9719ffa74792 --- /dev/null +++ b/cpp/ql/test/library-tests/string_literals/string_literals.c @@ -0,0 +1,5 @@ + +char *s1 = "test"; +char *s2 = "test\n"; +char *s3 = "a\tb\nc\\d"; + diff --git a/cpp/ql/test/library-tests/string_literals/string_literals.expected b/cpp/ql/test/library-tests/string_literals/string_literals.expected new file mode 100644 index 000000000000..a4d0feb43d17 --- /dev/null +++ b/cpp/ql/test/library-tests/string_literals/string_literals.expected @@ -0,0 +1,3 @@ +| string_literals.c:2:12:2:17 | test | +| string_literals.c:3:12:3:19 | test\n | +| string_literals.c:4:12:4:23 | a\tb\nc\\d | diff --git a/cpp/ql/test/library-tests/string_literals/string_literals.ql b/cpp/ql/test/library-tests/string_literals/string_literals.ql new file mode 100644 index 000000000000..7c901174e3a7 --- /dev/null +++ b/cpp/ql/test/library-tests/string_literals/string_literals.ql @@ -0,0 +1,5 @@ +import cpp + +from StringLiteral l +select l + diff --git a/cpp/ql/test/library-tests/string_literals_nul/string_literals_nul.c b/cpp/ql/test/library-tests/string_literals_nul/string_literals_nul.c new file mode 100644 index 000000000000..32fada4eee16 Binary files /dev/null and b/cpp/ql/test/library-tests/string_literals_nul/string_literals_nul.c differ diff --git a/cpp/ql/test/library-tests/string_literals_nul/string_literals_nul.expected b/cpp/ql/test/library-tests/string_literals_nul/string_literals_nul.expected new file mode 100644 index 000000000000..82432acf6743 --- /dev/null +++ b/cpp/ql/test/library-tests/string_literals_nul/string_literals_nul.expected @@ -0,0 +1,2 @@ +| 4 | string_literals_nul.c:4:12:4:31 | literal \u0000 with NUL | "literal \u0000 with NUL" | +| 5 | string_literals_nul.c:5:12:5:51 | literal \u0000 with NUL " and double quote | "literal \u0000 with NUL \\" and double quote" | diff --git a/cpp/ql/test/library-tests/string_literals_nul/string_literals_nul.ql b/cpp/ql/test/library-tests/string_literals_nul/string_literals_nul.ql new file mode 100644 index 000000000000..6349271e42b3 --- /dev/null +++ b/cpp/ql/test/library-tests/string_literals_nul/string_literals_nul.ql @@ -0,0 +1,7 @@ +import cpp + +from StringLiteral s +select s.getLocation().getStartLine(), + s, + s.getValueText() + diff --git a/cpp/ql/test/library-tests/structs/compatible_c/a1.c b/cpp/ql/test/library-tests/structs/compatible_c/a1.c new file mode 100644 index 000000000000..14ac29e2267a --- /dev/null +++ b/cpp/ql/test/library-tests/structs/compatible_c/a1.c @@ -0,0 +1,40 @@ +// Note: files `a1.c` and `a2.c` are completely identical. + +// The structs behind the two `anon_empty_t` types in `a1.c` and `a2.c` are +// extracted as anonymous structs. We should still identify them as the same +// struct, since they are considered compatible types according to the language. +typedef struct { } anon_empty_t; + +// This is another anonymous struct - we should identify all instances of +// `anon_nonempty_t` as the same struct, but distinct from `anon_empty_t`. +typedef struct { + int x; +} anon_nonempty_t; + +// We'd like to identify all instances of `another_anon_t` as the same, but +// distinct from `anon_nonempty_t`. The only difference between them is the +// typedef'd name. +typedef struct { + int x; +} another_anon_t; + +// If we incorrectly identify the `anon_empty_t` definitions in `a1.c` and `a2.c` +// as different structs (or for `anon_nonempty_t`), then our analysis will +// overcount the number of fields in `Foo`. +struct Foo { + anon_empty_t *empty; + anon_nonempty_t *nonempty; + int i; +}; + +struct Empty { }; + +struct NonEmpty { + int x; +}; + +struct Bar { + struct Empty *empty; + struct NonEmpty *nonempty; + int i; +}; diff --git a/cpp/ql/test/library-tests/structs/compatible_c/a2.c b/cpp/ql/test/library-tests/structs/compatible_c/a2.c new file mode 100644 index 000000000000..14ac29e2267a --- /dev/null +++ b/cpp/ql/test/library-tests/structs/compatible_c/a2.c @@ -0,0 +1,40 @@ +// Note: files `a1.c` and `a2.c` are completely identical. + +// The structs behind the two `anon_empty_t` types in `a1.c` and `a2.c` are +// extracted as anonymous structs. We should still identify them as the same +// struct, since they are considered compatible types according to the language. +typedef struct { } anon_empty_t; + +// This is another anonymous struct - we should identify all instances of +// `anon_nonempty_t` as the same struct, but distinct from `anon_empty_t`. +typedef struct { + int x; +} anon_nonempty_t; + +// We'd like to identify all instances of `another_anon_t` as the same, but +// distinct from `anon_nonempty_t`. The only difference between them is the +// typedef'd name. +typedef struct { + int x; +} another_anon_t; + +// If we incorrectly identify the `anon_empty_t` definitions in `a1.c` and `a2.c` +// as different structs (or for `anon_nonempty_t`), then our analysis will +// overcount the number of fields in `Foo`. +struct Foo { + anon_empty_t *empty; + anon_nonempty_t *nonempty; + int i; +}; + +struct Empty { }; + +struct NonEmpty { + int x; +}; + +struct Bar { + struct Empty *empty; + struct NonEmpty *nonempty; + int i; +}; diff --git a/cpp/ql/test/library-tests/structs/compatible_c/b1.c b/cpp/ql/test/library-tests/structs/compatible_c/b1.c new file mode 100644 index 000000000000..b154ff7eb9f0 --- /dev/null +++ b/cpp/ql/test/library-tests/structs/compatible_c/b1.c @@ -0,0 +1,84 @@ +// semmle-extractor-options: --microsoft --edg --c99 +struct AppleCompatible { // Definitions of Apple are exactly the same in b1.c and b2.c + int apple_x; +}; + +// Definitions of Banana are compatible but this copy uses int for the field type +struct BananaCompatible { + int banana_x; +}; + +// Definitions of Cherry are not compatible - the field types differ +struct Cherry { + int cherry_x; +}; + +// Definitions of Damson are not compatible - the field names differ +struct Damson { + int damson_x; +}; + +// Definitions of Elderberry are exactly the same and use a custom typedef +typedef int AnInt; +struct ElderberryCompatible { + AnInt elderberry_x; +}; + +// Definitions of Fig are not compatible - one uses float, the other double +struct Fig { + float fig_x; +}; + +// Definitions of Grape are not compatible - one uses _Imaginary float, the other _Imaginary double +struct Grape { + _Imaginary float grape_x; +}; + +// Definitions of Huckleberry are not compatible - one uses _Complex float, the other _Complex +// double +struct Huckleberry { + _Complex float huckleberry_x; +}; + +// Definitions of IndonesianLime are not compatible - they have different array sizes +struct IndonesianLime { + int indonesian_lime_x[7]; +}; + +// Definitions of Jujube are not compatible - the arrays have different base types +struct Jujube { + signed int jujube_x[4]; +}; + +// see c1.c and c2.c for Kiwi and Lemon + +// Definitions of Mango are not compatible - the enums differ in number of members +// N.B. you'll see two locations for the enum types - we don't currently add a +// compatibility hash to the trap labels for enums like we do for structs, so +// they get merged. +enum MangoEnum { MANGO_ENUM_A, MANGO_ENUM_B }; +struct Mango { + enum MangoEnum mango_x; +}; + +// Definitions of Nectarine are not compatible - the enum members have different values +enum NectarineEnum { NECTARINE_ENUM_A = 7, NECTARINE_ENUM_B }; +struct Nectarine { + enum NectarineEnum nectarine_x; +}; + +// Definitions of Orange are not compatible - the enum members have different names +enum OrangeEnum { ORANGE_ENUM_A, ORANGE_ENUM_B }; +struct Orange { + enum OrangeEnum orange_x; +}; + +// Definitions of Papaya are not compatible - they have pointers pointing to different types +struct Papaya { + int *papaya_x; +}; + +// Definitions of Quince are not compatible - the function pointers have different signatures +struct Quince { + int (*quince_fp)(int,int); +}; diff --git a/cpp/ql/test/library-tests/structs/compatible_c/b2.c b/cpp/ql/test/library-tests/structs/compatible_c/b2.c new file mode 100644 index 000000000000..51664a250d87 --- /dev/null +++ b/cpp/ql/test/library-tests/structs/compatible_c/b2.c @@ -0,0 +1,83 @@ +// semmle-extractor-options: --microsoft --edg --c99 +struct AppleCompatible { // Definitions of Apple are exactly the same in b1.c and b2.c + int apple_x; +}; + +// Definitions of Banana are compatible but this copy uses a typedef +typedef int MyInt; +typedef MyInt IntType; +struct BananaCompatible { + IntType banana_x; +}; + +// Definitions of Cherry are not compatible - the field types differ +struct Cherry { + short cherry_x; +}; + +// Definitions of Damson are not compatible - the field names differ +struct Damson { + int damson_y; +}; + +// Definitions of Elderberry are exactly the same and use a custom typedef +typedef int AnInt; +struct ElderberryCompatible { + AnInt elderberry_x; +}; + +// Definitions of Fig are not compatible - one uses float, the other double +struct Fig { + double fig_x; +}; + +// Definitions of Grape are not compatible - one uses _Imaginary float, the other _Imaginary double +struct Grape { + _Imaginary double grape_x; +}; + +// Definitions of Huckleberry are not compatible - one uses _Complex float, the other _Complex +// double +struct Huckleberry { + _Complex double huckleberry_x; +}; + +// Definitions of IndonesianLime are not compatible - they have different array sizes +struct IndonesianLime { + int indonesian_lime_x[9]; +}; + +// Definitions of Jujube are not compatible - the arrays have different base types +struct Jujube { + unsigned int jujube_x[4]; +}; + +// see c1.c and c2.c for Kiwi and Lemon + +// Definitions of Mango are not compatible - the enums differ in number of members +enum MangoEnum { MANGO_ENUM_A, MANGO_ENUM_B, MANGO_ENUM_C }; +struct Mango { + enum MangoEnum mango_x; +}; + +// Definitions of Nectarine are not compatible - the enum members have different values +enum NectarineEnum { NECTARINE_ENUM_A = 2, NECTARINE_ENUM_B }; +struct Nectarine { + enum NectarineEnum nectarine_x; +}; + +// Definitions of Orange are not compatible - the enum members have different names +enum OrangeEnum { ORANGE_ENUM_A, ORANGE_ENUM_FOO }; +struct Orange { + enum OrangeEnum orange_x; +}; + +// Definitions of Papaya are not compatible - they have pointers pointing to different types +struct Papaya { + long *papaya_x; +}; + +// Definitions of Quince are not compatible - the function pointers have different signatures +struct Quince { + int (*quince_fp)(long,int); +}; diff --git a/cpp/ql/test/library-tests/structs/compatible_c/c1_gnu.c b/cpp/ql/test/library-tests/structs/compatible_c/c1_gnu.c new file mode 100644 index 000000000000..cc00bc3561bb --- /dev/null +++ b/cpp/ql/test/library-tests/structs/compatible_c/c1_gnu.c @@ -0,0 +1,10 @@ +// Definitions of Kiwi are not compatible - they have different vector sizes +struct Kiwi { + int __attribute__ ((vector_size (16))) kiwi_x; +}; + +// Definitions of Lemon are not compatible - the vectors have different base types +struct Lemon { + unsigned int __attribute__ ((vector_size (16))) lemon_x; +}; +// semmle-extractor-options: --edg --c99 --edg --clang --edg --clang_vector_types --gnu_version 40700 diff --git a/cpp/ql/test/library-tests/structs/compatible_c/c2_gnu.c b/cpp/ql/test/library-tests/structs/compatible_c/c2_gnu.c new file mode 100644 index 000000000000..c1e8edafcc9a --- /dev/null +++ b/cpp/ql/test/library-tests/structs/compatible_c/c2_gnu.c @@ -0,0 +1,10 @@ +// Definitions of Kiwi are not compatible - they have different vector sizes +struct Kiwi { + int __attribute__ ((vector_size (8))) kiwi_x; +}; + +// Definitions of Lemon are not compatible - the vectors have different base types +struct Lemon { + signed int __attribute__ ((vector_size (16))) lemon_x; +}; +// semmle-extractor-options: --edg --c99 --edg --clang --edg --clang_vector_types --gnu_version 40700 diff --git a/cpp/ql/test/library-tests/structs/compatible_c/compatible.expected b/cpp/ql/test/library-tests/structs/compatible_c/compatible.expected new file mode 100644 index 000000000000..eb9e0d0529f7 --- /dev/null +++ b/cpp/ql/test/library-tests/structs/compatible_c/compatible.expected @@ -0,0 +1,60 @@ +| a1.c:6:16:6:16 | struct | 0 members | 2 locations | -1 | | +| a1.c:10:16:10:16 | struct | 1 members | 2 locations | 0 | x | +| a1.c:17:16:17:16 | struct | 1 members | 2 locations | 0 | x | +| a1.c:24:8:24:10 | Foo | 3 members | 2 locations | 0 | empty | +| a1.c:24:8:24:10 | Foo | 3 members | 2 locations | 1 | nonempty | +| a1.c:24:8:24:10 | Foo | 3 members | 2 locations | 2 | i | +| a1.c:30:8:30:12 | Empty | 0 members | 2 locations | -1 | | +| a1.c:32:8:32:15 | NonEmpty | 1 members | 2 locations | 0 | x | +| a1.c:36:8:36:10 | Bar | 3 members | 2 locations | 0 | empty | +| a1.c:36:8:36:10 | Bar | 3 members | 2 locations | 1 | nonempty | +| a1.c:36:8:36:10 | Bar | 3 members | 2 locations | 2 | i | +| a2.c:6:16:6:16 | struct | 0 members | 2 locations | -1 | | +| a2.c:10:16:10:16 | struct | 1 members | 2 locations | 0 | x | +| a2.c:17:16:17:16 | struct | 1 members | 2 locations | 0 | x | +| a2.c:24:8:24:10 | Foo | 3 members | 2 locations | 0 | empty | +| a2.c:24:8:24:10 | Foo | 3 members | 2 locations | 1 | nonempty | +| a2.c:24:8:24:10 | Foo | 3 members | 2 locations | 2 | i | +| a2.c:30:8:30:12 | Empty | 0 members | 2 locations | -1 | | +| a2.c:32:8:32:15 | NonEmpty | 1 members | 2 locations | 0 | x | +| a2.c:36:8:36:10 | Bar | 3 members | 2 locations | 0 | empty | +| a2.c:36:8:36:10 | Bar | 3 members | 2 locations | 1 | nonempty | +| a2.c:36:8:36:10 | Bar | 3 members | 2 locations | 2 | i | +| b1.c:2:8:2:22 | AppleCompatible | 1 members | 2 locations | 0 | apple_x | +| b1.c:7:8:7:23 | BananaCompatible | 1 members | 2 locations | 0 | banana_x | +| b1.c:12:8:12:13 | Cherry | 1 members | 1 locations | 0 | cherry_x | +| b1.c:17:8:17:13 | Damson | 1 members | 1 locations | 0 | damson_x | +| b1.c:23:8:23:27 | ElderberryCompatible | 1 members | 2 locations | 0 | elderberry_x | +| b1.c:28:8:28:10 | Fig | 1 members | 1 locations | 0 | fig_x | +| b1.c:33:8:33:12 | Grape | 1 members | 1 locations | 0 | grape_x | +| b1.c:39:8:39:18 | Huckleberry | 1 members | 1 locations | 0 | huckleberry_x | +| b1.c:44:8:44:21 | IndonesianLime | 1 members | 1 locations | 0 | indonesian_lime_x | +| b1.c:49:8:49:13 | Jujube | 1 members | 1 locations | 0 | jujube_x | +| b1.c:60:8:60:12 | Mango | 1 members | 1 locations | 0 | mango_x | +| b1.c:66:8:66:16 | Nectarine | 1 members | 1 locations | 0 | nectarine_x | +| b1.c:72:8:72:13 | Orange | 1 members | 1 locations | 0 | orange_x | +| b1.c:77:8:77:13 | Papaya | 1 members | 1 locations | 0 | papaya_x | +| b1.c:82:8:82:13 | Quince | 1 members | 1 locations | 0 | quince_fp | +| b2.c:2:8:2:22 | AppleCompatible | 1 members | 2 locations | 0 | apple_x | +| b2.c:9:8:9:23 | BananaCompatible | 1 members | 2 locations | 0 | banana_x | +| b2.c:14:8:14:13 | Cherry | 1 members | 1 locations | 0 | cherry_x | +| b2.c:19:8:19:13 | Damson | 1 members | 1 locations | 0 | damson_y | +| b2.c:25:8:25:27 | ElderberryCompatible | 1 members | 2 locations | 0 | elderberry_x | +| b2.c:30:8:30:10 | Fig | 1 members | 1 locations | 0 | fig_x | +| b2.c:35:8:35:12 | Grape | 1 members | 1 locations | 0 | grape_x | +| b2.c:41:8:41:18 | Huckleberry | 1 members | 1 locations | 0 | huckleberry_x | +| b2.c:46:8:46:21 | IndonesianLime | 1 members | 1 locations | 0 | indonesian_lime_x | +| b2.c:51:8:51:13 | Jujube | 1 members | 1 locations | 0 | jujube_x | +| b2.c:59:8:59:12 | Mango | 1 members | 1 locations | 0 | mango_x | +| b2.c:65:8:65:16 | Nectarine | 1 members | 1 locations | 0 | nectarine_x | +| b2.c:71:8:71:13 | Orange | 1 members | 1 locations | 0 | orange_x | +| b2.c:76:8:76:13 | Papaya | 1 members | 1 locations | 0 | papaya_x | +| b2.c:81:8:81:13 | Quince | 1 members | 1 locations | 0 | quince_fp | +| c1_gnu.c:2:8:2:11 | Kiwi | 1 members | 1 locations | 0 | kiwi_x | +| c1_gnu.c:7:8:7:12 | Lemon | 1 members | 1 locations | 0 | lemon_x | +| c2_gnu.c:2:8:2:11 | Kiwi | 1 members | 1 locations | 0 | kiwi_x | +| c2_gnu.c:7:8:7:12 | Lemon | 1 members | 1 locations | 0 | lemon_x | +| file://:0:0:0:0 | __va_list_tag | 4 members | 1 locations | 0 | gp_offset | +| file://:0:0:0:0 | __va_list_tag | 4 members | 1 locations | 1 | fp_offset | +| file://:0:0:0:0 | __va_list_tag | 4 members | 1 locations | 2 | overflow_arg_area | +| file://:0:0:0:0 | __va_list_tag | 4 members | 1 locations | 3 | reg_save_area | diff --git a/cpp/ql/test/library-tests/structs/compatible_c/compatible.ql b/cpp/ql/test/library-tests/structs/compatible_c/compatible.ql new file mode 100644 index 000000000000..c41a2fe45bc5 --- /dev/null +++ b/cpp/ql/test/library-tests/structs/compatible_c/compatible.ql @@ -0,0 +1,10 @@ +import cpp + +from Struct s, int i, string m, string members, string locations +where + if exists(s.getAMember()) + then m = s.getAMember(i).toString() + else (m = "" and i = -1) + and members = count(s.getAMember()) + " members" + and locations = count(s.getLocation()) + " locations" +select s, members, locations, i, m diff --git a/cpp/ql/test/library-tests/structs/compatible_c/compatible_types.expected b/cpp/ql/test/library-tests/structs/compatible_c/compatible_types.expected new file mode 100644 index 000000000000..1151179e58d6 --- /dev/null +++ b/cpp/ql/test/library-tests/structs/compatible_c/compatible_types.expected @@ -0,0 +1,64 @@ +| a1.c:10:16:10:16 | struct | 0 | file://:0:0:0:0 | int | 1 types | +| a1.c:17:16:17:16 | struct | 0 | file://:0:0:0:0 | int | 1 types | +| a1.c:24:8:24:10 | Foo | 0 | file://:0:0:0:0 | anon_empty_t * | 1 types | +| a1.c:24:8:24:10 | Foo | 1 | file://:0:0:0:0 | anon_nonempty_t * | 1 types | +| a1.c:24:8:24:10 | Foo | 2 | file://:0:0:0:0 | int | 1 types | +| a1.c:32:8:32:15 | NonEmpty | 0 | file://:0:0:0:0 | int | 1 types | +| a1.c:36:8:36:10 | Bar | 0 | file://:0:0:0:0 | Empty * | 1 types | +| a1.c:36:8:36:10 | Bar | 1 | file://:0:0:0:0 | NonEmpty * | 1 types | +| a1.c:36:8:36:10 | Bar | 2 | file://:0:0:0:0 | int | 1 types | +| a2.c:10:16:10:16 | struct | 0 | file://:0:0:0:0 | int | 1 types | +| a2.c:17:16:17:16 | struct | 0 | file://:0:0:0:0 | int | 1 types | +| a2.c:24:8:24:10 | Foo | 0 | file://:0:0:0:0 | anon_empty_t * | 1 types | +| a2.c:24:8:24:10 | Foo | 1 | file://:0:0:0:0 | anon_nonempty_t * | 1 types | +| a2.c:24:8:24:10 | Foo | 2 | file://:0:0:0:0 | int | 1 types | +| a2.c:32:8:32:15 | NonEmpty | 0 | file://:0:0:0:0 | int | 1 types | +| a2.c:36:8:36:10 | Bar | 0 | file://:0:0:0:0 | Empty * | 1 types | +| a2.c:36:8:36:10 | Bar | 1 | file://:0:0:0:0 | NonEmpty * | 1 types | +| a2.c:36:8:36:10 | Bar | 2 | file://:0:0:0:0 | int | 1 types | +| b1.c:2:8:2:22 | AppleCompatible | 0 | file://:0:0:0:0 | int | 1 types | +| b1.c:7:8:7:23 | BananaCompatible | 0 | file://:0:0:0:0 | int | 1 types | +| b1.c:12:8:12:13 | Cherry | 0 | file://:0:0:0:0 | int | 1 types | +| b1.c:17:8:17:13 | Damson | 0 | file://:0:0:0:0 | int | 1 types | +| b1.c:23:8:23:27 | ElderberryCompatible | 0 | b1.c:22:13:22:17 | AnInt | 1 types | +| b1.c:23:8:23:27 | ElderberryCompatible | 0 | b2.c:24:13:24:17 | AnInt | 1 types | +| b1.c:28:8:28:10 | Fig | 0 | file://:0:0:0:0 | float | 1 types | +| b1.c:33:8:33:12 | Grape | 0 | file://:0:0:0:0 | _Imaginary float | 1 types | +| b1.c:39:8:39:18 | Huckleberry | 0 | file://:0:0:0:0 | _Complex float | 1 types | +| b1.c:44:8:44:21 | IndonesianLime | 0 | file://:0:0:0:0 | int[7] | 1 types | +| b1.c:49:8:49:13 | Jujube | 0 | file://:0:0:0:0 | signed int[4] | 1 types | +| b1.c:60:8:60:12 | Mango | 0 | b1.c:59:6:59:14 | MangoEnum | 1 types | +| b1.c:60:8:60:12 | Mango | 0 | b2.c:58:6:58:14 | MangoEnum | 1 types | +| b1.c:66:8:66:16 | Nectarine | 0 | b1.c:65:6:65:18 | NectarineEnum | 1 types | +| b1.c:66:8:66:16 | Nectarine | 0 | b2.c:64:6:64:18 | NectarineEnum | 1 types | +| b1.c:72:8:72:13 | Orange | 0 | b1.c:71:6:71:15 | OrangeEnum | 1 types | +| b1.c:72:8:72:13 | Orange | 0 | b2.c:70:6:70:15 | OrangeEnum | 1 types | +| b1.c:77:8:77:13 | Papaya | 0 | file://:0:0:0:0 | int * | 1 types | +| b1.c:82:8:82:13 | Quince | 0 | file://:0:0:0:0 | ..(*)(..) | 1 types | +| b2.c:2:8:2:22 | AppleCompatible | 0 | file://:0:0:0:0 | int | 1 types | +| b2.c:9:8:9:23 | BananaCompatible | 0 | file://:0:0:0:0 | int | 1 types | +| b2.c:14:8:14:13 | Cherry | 0 | file://:0:0:0:0 | short | 1 types | +| b2.c:19:8:19:13 | Damson | 0 | file://:0:0:0:0 | int | 1 types | +| b2.c:25:8:25:27 | ElderberryCompatible | 0 | b1.c:22:13:22:17 | AnInt | 1 types | +| b2.c:25:8:25:27 | ElderberryCompatible | 0 | b2.c:24:13:24:17 | AnInt | 1 types | +| b2.c:30:8:30:10 | Fig | 0 | file://:0:0:0:0 | double | 1 types | +| b2.c:35:8:35:12 | Grape | 0 | file://:0:0:0:0 | _Imaginary double | 1 types | +| b2.c:41:8:41:18 | Huckleberry | 0 | file://:0:0:0:0 | _Complex double | 1 types | +| b2.c:46:8:46:21 | IndonesianLime | 0 | file://:0:0:0:0 | int[9] | 1 types | +| b2.c:51:8:51:13 | Jujube | 0 | file://:0:0:0:0 | unsigned int[4] | 1 types | +| b2.c:59:8:59:12 | Mango | 0 | b1.c:59:6:59:14 | MangoEnum | 1 types | +| b2.c:59:8:59:12 | Mango | 0 | b2.c:58:6:58:14 | MangoEnum | 1 types | +| b2.c:65:8:65:16 | Nectarine | 0 | b1.c:65:6:65:18 | NectarineEnum | 1 types | +| b2.c:65:8:65:16 | Nectarine | 0 | b2.c:64:6:64:18 | NectarineEnum | 1 types | +| b2.c:71:8:71:13 | Orange | 0 | b1.c:71:6:71:15 | OrangeEnum | 1 types | +| b2.c:71:8:71:13 | Orange | 0 | b2.c:70:6:70:15 | OrangeEnum | 1 types | +| b2.c:76:8:76:13 | Papaya | 0 | file://:0:0:0:0 | long * | 1 types | +| b2.c:81:8:81:13 | Quince | 0 | file://:0:0:0:0 | ..(*)(..) | 1 types | +| c1_gnu.c:2:8:2:11 | Kiwi | 0 | file://:0:0:0:0 | __attribute((vector_size(16))) int | 1 types | +| c1_gnu.c:7:8:7:12 | Lemon | 0 | file://:0:0:0:0 | __attribute((vector_size(16))) unsigned int | 1 types | +| c2_gnu.c:2:8:2:11 | Kiwi | 0 | file://:0:0:0:0 | __attribute((vector_size(8))) int | 1 types | +| c2_gnu.c:7:8:7:12 | Lemon | 0 | file://:0:0:0:0 | __attribute((vector_size(16))) signed int | 1 types | +| file://:0:0:0:0 | __va_list_tag | 0 | file://:0:0:0:0 | unsigned int | 1 types | +| file://:0:0:0:0 | __va_list_tag | 1 | file://:0:0:0:0 | unsigned int | 1 types | +| file://:0:0:0:0 | __va_list_tag | 2 | file://:0:0:0:0 | void * | 1 types | +| file://:0:0:0:0 | __va_list_tag | 3 | file://:0:0:0:0 | void * | 1 types | diff --git a/cpp/ql/test/library-tests/structs/compatible_c/compatible_types.ql b/cpp/ql/test/library-tests/structs/compatible_c/compatible_types.ql new file mode 100644 index 000000000000..45905ccae60c --- /dev/null +++ b/cpp/ql/test/library-tests/structs/compatible_c/compatible_types.ql @@ -0,0 +1,12 @@ +import cpp + +// Some structs have multiple definitions, but those definitions use different +// (but compatible) field types. With this query we can check getType() always +// returns a single result. + +from Struct s, MemberVariable mv, int i, Type t, string types +where + mv = s.getAMember(i) and + t = mv.getType() and + types = count(mv.getType()) + " types" +select s, i, t, types diff --git a/cpp/ql/test/library-tests/structs/compatible_cpp/a1.cpp b/cpp/ql/test/library-tests/structs/compatible_cpp/a1.cpp new file mode 100644 index 000000000000..c40ecc1cbbb5 --- /dev/null +++ b/cpp/ql/test/library-tests/structs/compatible_cpp/a1.cpp @@ -0,0 +1,14 @@ +// This is a small C++ addition to the compatible_c test. +// Note: files `a1.cpp` and `a2.cpp` are completely identical. + +struct Empty { }; + +struct NonEmpty { + int x; +}; + +struct Bar { + struct Empty *empty; + struct NonEmpty *nonempty; + int i; +}; diff --git a/cpp/ql/test/library-tests/structs/compatible_cpp/a2.cpp b/cpp/ql/test/library-tests/structs/compatible_cpp/a2.cpp new file mode 100644 index 000000000000..c40ecc1cbbb5 --- /dev/null +++ b/cpp/ql/test/library-tests/structs/compatible_cpp/a2.cpp @@ -0,0 +1,14 @@ +// This is a small C++ addition to the compatible_c test. +// Note: files `a1.cpp` and `a2.cpp` are completely identical. + +struct Empty { }; + +struct NonEmpty { + int x; +}; + +struct Bar { + struct Empty *empty; + struct NonEmpty *nonempty; + int i; +}; diff --git a/cpp/ql/test/library-tests/structs/compatible_cpp/b1.cpp b/cpp/ql/test/library-tests/structs/compatible_cpp/b1.cpp new file mode 100644 index 000000000000..c7d839b4874d --- /dev/null +++ b/cpp/ql/test/library-tests/structs/compatible_cpp/b1.cpp @@ -0,0 +1,26 @@ +// This is a small C++ addition to the compatible_c test. +// Note that we decided to follow the C compatibility rules for merging types, +// not the C++ ODR rules. + +// Definitions of Apple are exactly the same in b1.c and b2.c +class AppleCompatible { + int apple_x; +}; + +// Definitions of Banana are compatible but this copy uses int for the field type +class BananaCompatible { + int banana_x; +}; + +// Definitions of Cherry are not compatible - the field types differ +class Cherry { + int cherry_x; +}; + +// This shows that we currently only consider member variables, and ignore +// functions when deciding on class compatibility. In this file there is a +// member function called `foo`, in b2.cpp there is one called `bar`. +class Damson { + int damson_x; + void foo(); +}; diff --git a/cpp/ql/test/library-tests/structs/compatible_cpp/b2.cpp b/cpp/ql/test/library-tests/structs/compatible_cpp/b2.cpp new file mode 100644 index 000000000000..e38da72a32a1 --- /dev/null +++ b/cpp/ql/test/library-tests/structs/compatible_cpp/b2.cpp @@ -0,0 +1,24 @@ +// Definitions of Apple are exactly the same in b1.c and b2.c +class AppleCompatible { + int apple_x; +}; + +// Definitions of Banana are compatible but this copy uses a typedef +typedef int MyInt; +typedef MyInt IntType; +class BananaCompatible { + IntType banana_x; +}; + +// Definitions of Cherry are not compatible - the field types differ +class Cherry { + short cherry_x; +}; + +// This shows that we currently only consider member variables, and ignore +// functions when deciding on class compatibility. In this file there is a +// member function called `bar`, in b1.cpp there is one called `foo`. +class Damson { + int damson_x; + void bar(); +}; diff --git a/cpp/ql/test/library-tests/structs/compatible_cpp/compatible.expected b/cpp/ql/test/library-tests/structs/compatible_cpp/compatible.expected new file mode 100644 index 000000000000..4b631dde98b4 --- /dev/null +++ b/cpp/ql/test/library-tests/structs/compatible_cpp/compatible.expected @@ -0,0 +1,54 @@ +| a1.cpp:4:8:4:12 | Empty | 2 members | 2 locations | 0 | operator= | +| a1.cpp:4:8:4:12 | Empty | 2 members | 2 locations | 1 | operator= | +| a1.cpp:6:8:6:15 | NonEmpty | 3 members | 2 locations | 0 | x | +| a1.cpp:6:8:6:15 | NonEmpty | 3 members | 2 locations | 1 | operator= | +| a1.cpp:6:8:6:15 | NonEmpty | 3 members | 2 locations | 2 | operator= | +| a1.cpp:10:8:10:10 | Bar | 5 members | 2 locations | 0 | empty | +| a1.cpp:10:8:10:10 | Bar | 5 members | 2 locations | 1 | nonempty | +| a1.cpp:10:8:10:10 | Bar | 5 members | 2 locations | 2 | i | +| a1.cpp:10:8:10:10 | Bar | 5 members | 2 locations | 3 | operator= | +| a1.cpp:10:8:10:10 | Bar | 5 members | 2 locations | 4 | operator= | +| a2.cpp:4:8:4:12 | Empty | 2 members | 2 locations | 0 | operator= | +| a2.cpp:4:8:4:12 | Empty | 2 members | 2 locations | 1 | operator= | +| a2.cpp:6:8:6:15 | NonEmpty | 3 members | 2 locations | 0 | x | +| a2.cpp:6:8:6:15 | NonEmpty | 3 members | 2 locations | 1 | operator= | +| a2.cpp:6:8:6:15 | NonEmpty | 3 members | 2 locations | 2 | operator= | +| a2.cpp:10:8:10:10 | Bar | 5 members | 2 locations | 0 | empty | +| a2.cpp:10:8:10:10 | Bar | 5 members | 2 locations | 1 | nonempty | +| 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= | +| 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= | +| file://:0:0:0:0 | __va_list_tag | 6 members | 1 locations | 0 | gp_offset | +| file://:0:0:0:0 | __va_list_tag | 6 members | 1 locations | 1 | fp_offset | +| file://:0:0:0:0 | __va_list_tag | 6 members | 1 locations | 2 | overflow_arg_area | +| file://:0:0:0:0 | __va_list_tag | 6 members | 1 locations | 3 | reg_save_area | +| file://:0:0:0:0 | __va_list_tag | 6 members | 1 locations | 4 | operator= | +| file://:0:0:0:0 | __va_list_tag | 6 members | 1 locations | 5 | operator= | diff --git a/cpp/ql/test/library-tests/structs/compatible_cpp/compatible.ql b/cpp/ql/test/library-tests/structs/compatible_cpp/compatible.ql new file mode 100644 index 000000000000..dea9634a98b1 --- /dev/null +++ b/cpp/ql/test/library-tests/structs/compatible_cpp/compatible.ql @@ -0,0 +1,10 @@ +import cpp + +from Class c, int i, string m, string members, string locations +where + if exists(c.getAMember()) + then m = c.getAMember(i).toString() + else (m = "" and i = -1) + and members = count(c.getAMember()) + " members" + and locations = count(c.getLocation()) + " locations" +select c, members, locations, i, m diff --git a/cpp/ql/test/library-tests/structs/compatible_cpp/compatible_types.expected b/cpp/ql/test/library-tests/structs/compatible_cpp/compatible_types.expected new file mode 100644 index 000000000000..df2a178f6674 --- /dev/null +++ b/cpp/ql/test/library-tests/structs/compatible_cpp/compatible_types.expected @@ -0,0 +1,20 @@ +| a1.cpp:6:8:6:15 | NonEmpty | 0 | file://:0:0:0:0 | int | 1 types | +| a1.cpp:10:8:10:10 | Bar | 0 | file://:0:0:0:0 | Empty * | 1 types | +| a1.cpp:10:8:10:10 | Bar | 1 | file://:0:0:0:0 | NonEmpty * | 1 types | +| a1.cpp:10:8:10:10 | Bar | 2 | file://:0:0:0:0 | int | 1 types | +| a2.cpp:6:8:6:15 | NonEmpty | 0 | file://:0:0:0:0 | int | 1 types | +| a2.cpp:10:8:10:10 | Bar | 0 | file://:0:0:0:0 | Empty * | 1 types | +| a2.cpp:10:8:10:10 | Bar | 1 | file://:0:0:0:0 | NonEmpty * | 1 types | +| a2.cpp:10:8:10:10 | Bar | 2 | file://:0:0:0:0 | int | 1 types | +| b1.cpp:6:7:6:21 | AppleCompatible | 0 | file://:0:0:0:0 | int | 1 types | +| b1.cpp:11:7:11:22 | BananaCompatible | 0 | file://:0:0:0:0 | int | 1 types | +| b1.cpp:16:7:16:12 | Cherry | 0 | file://:0:0:0:0 | int | 1 types | +| b1.cpp:23:7:23:12 | Damson | 0 | file://:0:0:0:0 | int | 1 types | +| b2.cpp:2:7:2:21 | AppleCompatible | 0 | file://:0:0:0:0 | int | 1 types | +| b2.cpp:9:7:9:22 | BananaCompatible | 0 | file://:0:0:0:0 | int | 1 types | +| b2.cpp:14:7:14:12 | Cherry | 0 | file://:0:0:0:0 | short | 1 types | +| b2.cpp:21:7:21:12 | Damson | 0 | file://:0:0:0:0 | int | 1 types | +| file://:0:0:0:0 | __va_list_tag | 0 | file://:0:0:0:0 | unsigned int | 1 types | +| file://:0:0:0:0 | __va_list_tag | 1 | file://:0:0:0:0 | unsigned int | 1 types | +| file://:0:0:0:0 | __va_list_tag | 2 | file://:0:0:0:0 | void * | 1 types | +| file://:0:0:0:0 | __va_list_tag | 3 | file://:0:0:0:0 | void * | 1 types | diff --git a/cpp/ql/test/library-tests/structs/compatible_cpp/compatible_types.ql b/cpp/ql/test/library-tests/structs/compatible_cpp/compatible_types.ql new file mode 100644 index 000000000000..31058e84568a --- /dev/null +++ b/cpp/ql/test/library-tests/structs/compatible_cpp/compatible_types.ql @@ -0,0 +1,12 @@ +import cpp + +// Some structs have multiple definitions, but those definitions use different +// (but compatible) field types. With this query we can check getType() always +// returns a single result. + +from Class c, MemberVariable mv, int i, Type t, string types +where + mv = c.getAMember(i) and + t = mv.getType() and + types = count(mv.getType()) + " types" +select c, i, t, types diff --git a/cpp/ql/test/library-tests/structs/compatible_routines/a.cpp b/cpp/ql/test/library-tests/structs/compatible_routines/a.cpp new file mode 100644 index 000000000000..bac5e4e6fe21 --- /dev/null +++ b/cpp/ql/test/library-tests/structs/compatible_routines/a.cpp @@ -0,0 +1,6 @@ +#include "a.h" +#include "s.h" + +void Foo::bar(Thing *thing) {} + +void baz(Thing *thing) {} diff --git a/cpp/ql/test/library-tests/structs/compatible_routines/a.h b/cpp/ql/test/library-tests/structs/compatible_routines/a.h new file mode 100644 index 000000000000..d6929bb1ab49 --- /dev/null +++ b/cpp/ql/test/library-tests/structs/compatible_routines/a.h @@ -0,0 +1,8 @@ +struct Thing; + +class Foo { + public: + void bar(Thing *thing); +}; + +void baz(Thing *thing); diff --git a/cpp/ql/test/library-tests/structs/compatible_routines/b.cpp b/cpp/ql/test/library-tests/structs/compatible_routines/b.cpp new file mode 100644 index 000000000000..fde281e24a09 --- /dev/null +++ b/cpp/ql/test/library-tests/structs/compatible_routines/b.cpp @@ -0,0 +1,7 @@ +#include "a.h" + +void f(Thing *thing) { + Foo f; + f.bar(thing); + baz(thing); +} diff --git a/cpp/ql/test/library-tests/structs/compatible_routines/c.cpp b/cpp/ql/test/library-tests/structs/compatible_routines/c.cpp new file mode 100644 index 000000000000..ce19c928c562 --- /dev/null +++ b/cpp/ql/test/library-tests/structs/compatible_routines/c.cpp @@ -0,0 +1,8 @@ +#include "a.h" +#include "s.h" // include a complete definition of Thing + +void g(Thing *thing) { + Foo f; + f.bar(thing); + baz(thing); +} diff --git a/cpp/ql/test/library-tests/structs/compatible_routines/compatible.expected b/cpp/ql/test/library-tests/structs/compatible_routines/compatible.expected new file mode 100644 index 000000000000..b7c8ac999345 --- /dev/null +++ b/cpp/ql/test/library-tests/structs/compatible_routines/compatible.expected @@ -0,0 +1,4 @@ +| b.cpp:5:5:5:7 | call to bar | a.cpp:4:6:4:13 | bar | +| b.cpp:6:3:6:5 | call to baz | a.cpp:6:6:6:8 | baz | +| c.cpp:6:5:6:7 | call to bar | a.cpp:4:6:4:13 | bar | +| c.cpp:7:3:7:5 | call to baz | a.cpp:6:6:6:8 | baz | diff --git a/cpp/ql/test/library-tests/structs/compatible_routines/compatible.ql b/cpp/ql/test/library-tests/structs/compatible_routines/compatible.ql new file mode 100644 index 000000000000..4a8952b25e86 --- /dev/null +++ b/cpp/ql/test/library-tests/structs/compatible_routines/compatible.ql @@ -0,0 +1,5 @@ +import cpp + +from FunctionCall fc, Function f +where f = fc.getTarget() +select fc, f diff --git a/cpp/ql/test/library-tests/structs/compatible_routines/s.h b/cpp/ql/test/library-tests/structs/compatible_routines/s.h new file mode 100644 index 000000000000..6e0785d5ff18 --- /dev/null +++ b/cpp/ql/test/library-tests/structs/compatible_routines/s.h @@ -0,0 +1,3 @@ +struct Thing { + int x; +}; diff --git a/cpp/ql/test/library-tests/structs/compatible_variables/a.c b/cpp/ql/test/library-tests/structs/compatible_variables/a.c new file mode 100644 index 000000000000..bac709efb954 --- /dev/null +++ b/cpp/ql/test/library-tests/structs/compatible_variables/a.c @@ -0,0 +1,14 @@ +#include "h.h" + +// Provide a complete definition of Foo. +struct Foo { + int foo_x; +}; + +// This definition is incompatible with the one in b.c, so... +struct Bar { + int bar_y; +}; + +// ...we'd expect this declaration to create a separate variable in the db +extern struct Bar bar; diff --git a/cpp/ql/test/library-tests/structs/compatible_variables/b.c b/cpp/ql/test/library-tests/structs/compatible_variables/b.c new file mode 100644 index 000000000000..5037af9500dc --- /dev/null +++ b/cpp/ql/test/library-tests/structs/compatible_variables/b.c @@ -0,0 +1,17 @@ +// The extractor will not see a complete definition of Foo for this file. + +#include "h.h" + +// We want to check that these two variables don't get duplicated in the +// database. +void (*some_func_ptr)(struct Foo *foo); +struct Foo* foo_ptr1; +FooPtr foo_ptr2; + +// This definition is incompatible with the one in a.c, so... +struct Bar { + unsigned long bar_x; +}; + +// ...we'd expect this declaration to create a separate variable in the db +extern struct Bar bar; diff --git a/cpp/ql/test/library-tests/structs/compatible_variables/h.h b/cpp/ql/test/library-tests/structs/compatible_variables/h.h new file mode 100644 index 000000000000..95cf6d428608 --- /dev/null +++ b/cpp/ql/test/library-tests/structs/compatible_variables/h.h @@ -0,0 +1,10 @@ +// Provide an incomplete definition of Foo. +struct Foo; +typedef struct Foo* FooPtr; + +// When this file is included from a.c, the extractor will see a complete +// definition of Foo, but not when it's included b.c. We want to check that we +// don't see these variables duplicated in the database because of it. +extern void (*some_func_ptr)(struct Foo *foo); +extern struct Foo* foo_ptr1; +extern FooPtr foo_ptr2; diff --git a/cpp/ql/test/library-tests/structs/compatible_variables/test.expected b/cpp/ql/test/library-tests/structs/compatible_variables/test.expected new file mode 100644 index 000000000000..d1a043da6705 --- /dev/null +++ b/cpp/ql/test/library-tests/structs/compatible_variables/test.expected @@ -0,0 +1,12 @@ +| a.c:5:7:5:11 | foo_x | +| a.c:10:7:10:11 | bar_y | +| a.c:14:19:14:21 | bar | +| b.c:7:8:7:20 | some_func_ptr | +| b.c:8:13:8:20 | foo_ptr1 | +| b.c:9:8:9:15 | foo_ptr2 | +| b.c:13:17:13:21 | bar_x | +| b.c:17:19:17:21 | bar | +| file://:0:0:0:0 | fp_offset | +| file://:0:0:0:0 | gp_offset | +| file://:0:0:0:0 | overflow_arg_area | +| file://:0:0:0:0 | reg_save_area | diff --git a/cpp/ql/test/library-tests/structs/compatible_variables/test.ql b/cpp/ql/test/library-tests/structs/compatible_variables/test.ql new file mode 100644 index 000000000000..763deead9a49 --- /dev/null +++ b/cpp/ql/test/library-tests/structs/compatible_variables/test.ql @@ -0,0 +1,4 @@ +import cpp + +from Variable v +select v diff --git a/cpp/ql/test/library-tests/structs/incomplete_definition/a.h b/cpp/ql/test/library-tests/structs/incomplete_definition/a.h new file mode 100644 index 000000000000..d09cac85aa56 --- /dev/null +++ b/cpp/ql/test/library-tests/structs/incomplete_definition/a.h @@ -0,0 +1,6 @@ +// Incomplete definition of Foo +struct Foo; + +struct Bar { + Foo *cheese; +}; diff --git a/cpp/ql/test/library-tests/structs/incomplete_definition/pointerbasetype.expected b/cpp/ql/test/library-tests/structs/incomplete_definition/pointerbasetype.expected new file mode 100644 index 000000000000..98a672ad4353 --- /dev/null +++ b/cpp/ql/test/library-tests/structs/incomplete_definition/pointerbasetype.expected @@ -0,0 +1,2 @@ +| a.h:5:8:5:13 | cheese | y.cpp:4:8:4:10 | Foo | 3 | +| x.cpp:3:6:3:10 | bar_x | a.h:4:8:4:10 | Bar | 3 | diff --git a/cpp/ql/test/library-tests/structs/incomplete_definition/pointerbasetype.ql b/cpp/ql/test/library-tests/structs/incomplete_definition/pointerbasetype.ql new file mode 100644 index 000000000000..2fd5403f2157 --- /dev/null +++ b/cpp/ql/test/library-tests/structs/incomplete_definition/pointerbasetype.ql @@ -0,0 +1,5 @@ +import cpp + +from Variable v, Class t +where t = v.getType().(PointerType).getBaseType() +select v, t, count(t.getAMember()) diff --git a/cpp/ql/test/library-tests/structs/incomplete_definition/x.cpp b/cpp/ql/test/library-tests/structs/incomplete_definition/x.cpp new file mode 100644 index 000000000000..5b2a352e3dd7 --- /dev/null +++ b/cpp/ql/test/library-tests/structs/incomplete_definition/x.cpp @@ -0,0 +1,3 @@ +#include "a.h" + +Bar *bar_x; diff --git a/cpp/ql/test/library-tests/structs/incomplete_definition/y.cpp b/cpp/ql/test/library-tests/structs/incomplete_definition/y.cpp new file mode 100644 index 000000000000..6059af317665 --- /dev/null +++ b/cpp/ql/test/library-tests/structs/incomplete_definition/y.cpp @@ -0,0 +1,8 @@ +#include "a.h" + +// Completes definition of Foo +struct Foo { + int val; +}; + +Bar bar_y; diff --git a/cpp/ql/test/library-tests/structs/mutual_recursion/a.c b/cpp/ql/test/library-tests/structs/mutual_recursion/a.c new file mode 100644 index 000000000000..6fd3d62ba247 --- /dev/null +++ b/cpp/ql/test/library-tests/structs/mutual_recursion/a.c @@ -0,0 +1,61 @@ +struct Node { + struct Node *next; +}; + +struct CompatibleB; +struct CompatibleC; + +struct CompatibleA { + struct CompatibleB *b; +}; + +struct CompatibleB { + struct CompatibleC *c; +}; + +// The 2 definitions of CompatibleC use different but compatible types for x +struct CompatibleC { + struct CompatibleA *a; + int x; +}; + +// The initial handling of recursion didn't catch this case - if you start from +// D, you'll never revisit it, but you will revisit A/B/C. +struct CompatibleD { + struct CompatibleA *a; +}; + +// Ideally, we'd detect that the definitions of Incompatible{A,B} are incompatible, but since their +// fields are pointers, we can't deeply inspect them (it would be possible to have pointers to +// incomplete types that we *can't* deeply inspect). +struct IncompatibleB; +struct IncompatibleC; + +struct IncompatibleA { + struct IncompatibleB *b; +}; + +struct IncompatibleB { + struct IncompatibleC *c; +}; + +// The 2 definitions of IncompatibleC use different and incompatible types for x +struct IncompatibleC { + struct IncompatibleA *a; + short x; +}; + +struct IncompatibleD { + struct IncompatibleA *a; +}; + +// We should be able to detect that the definitions of NonRecursiveA and NonRecursiveB are +// incompatible - unlike the above cases, there's no mutual recursion and no pointer type, so we can +// deeply inspect NonRecursiveA when it's used from NonRecursiveB. +struct NonRecursiveA { + int val; +}; + +struct NonRecursiveB { + struct NonRecursiveA a; +}; diff --git a/cpp/ql/test/library-tests/structs/mutual_recursion/b.c b/cpp/ql/test/library-tests/structs/mutual_recursion/b.c new file mode 100644 index 000000000000..234e1f84e49d --- /dev/null +++ b/cpp/ql/test/library-tests/structs/mutual_recursion/b.c @@ -0,0 +1,57 @@ +struct Node { + struct Node *next; +}; + +struct CompatibleB; +struct CompatibleC; + +struct CompatibleA { + struct CompatibleB *b; +}; + +struct CompatibleB { + struct CompatibleC *c; +}; + +// The 2 definitions of CompatibleC use different but compatible types for x +typedef int AnInt; +struct CompatibleC { + struct CompatibleA *a; + AnInt x; +}; + +struct CompatibleD { + struct CompatibleA *a; +}; + +struct IncompatibleB; +struct IncompatibleC; + +struct IncompatibleA { + struct IncompatibleB *b; +}; + +struct IncompatibleB { + struct IncompatibleC *c; +}; + +// The 2 definitions of IncompatibleC use different and incompatible types for x +struct IncompatibleC { + struct IncompatibleA *a; + int x; +}; + +struct IncompatibleD { + struct IncompatibleA *a; +}; + +// We should be able to detect that the definitions of NonRecursiveA and NonRecursiveB are +// incompatible - unlike the above cases, there's no mutual recursion and no pointer type, so we can +// deeply inspect NonRecursiveA when it's used from NonRecursiveB. +struct NonRecursiveA { + short val; +}; + +struct NonRecursiveB { + struct NonRecursiveA a; +}; diff --git a/cpp/ql/test/library-tests/structs/mutual_recursion/compatible_members.expected b/cpp/ql/test/library-tests/structs/mutual_recursion/compatible_members.expected new file mode 100644 index 000000000000..92b8a908f755 --- /dev/null +++ b/cpp/ql/test/library-tests/structs/mutual_recursion/compatible_members.expected @@ -0,0 +1,30 @@ +| a.c:1:8:1:11 | Node | 1 members | 2 locations | 0 | next | +| a.c:8:8:8:18 | CompatibleA | 1 members | 2 locations | 0 | b | +| a.c:12:8:12:18 | CompatibleB | 1 members | 2 locations | 0 | c | +| a.c:17:8:17:18 | CompatibleC | 2 members | 2 locations | 0 | a | +| a.c:17:8:17:18 | CompatibleC | 2 members | 2 locations | 1 | x | +| a.c:24:8:24:18 | CompatibleD | 1 members | 2 locations | 0 | a | +| a.c:34:8:34:20 | IncompatibleA | 1 members | 2 locations | 0 | b | +| a.c:38:8:38:20 | IncompatibleB | 1 members | 2 locations | 0 | c | +| a.c:43:8:43:20 | IncompatibleC | 2 members | 1 locations | 0 | a | +| a.c:43:8:43:20 | IncompatibleC | 2 members | 1 locations | 1 | x | +| a.c:48:8:48:20 | IncompatibleD | 1 members | 2 locations | 0 | a | +| a.c:55:8:55:20 | NonRecursiveA | 1 members | 1 locations | 0 | val | +| a.c:59:8:59:20 | NonRecursiveB | 1 members | 1 locations | 0 | a | +| b.c:1:8:1:11 | Node | 1 members | 2 locations | 0 | next | +| b.c:8:8:8:18 | CompatibleA | 1 members | 2 locations | 0 | b | +| b.c:12:8:12:18 | CompatibleB | 1 members | 2 locations | 0 | c | +| b.c:18:8:18:18 | CompatibleC | 2 members | 2 locations | 0 | a | +| b.c:18:8:18:18 | CompatibleC | 2 members | 2 locations | 1 | x | +| b.c:23:8:23:18 | CompatibleD | 1 members | 2 locations | 0 | a | +| b.c:30:8:30:20 | IncompatibleA | 1 members | 2 locations | 0 | b | +| b.c:34:8:34:20 | IncompatibleB | 1 members | 2 locations | 0 | c | +| b.c:39:8:39:20 | IncompatibleC | 2 members | 1 locations | 0 | a | +| b.c:39:8:39:20 | IncompatibleC | 2 members | 1 locations | 1 | x | +| b.c:44:8:44:20 | IncompatibleD | 1 members | 2 locations | 0 | a | +| b.c:51:8:51:20 | NonRecursiveA | 1 members | 1 locations | 0 | val | +| b.c:55:8:55:20 | NonRecursiveB | 1 members | 1 locations | 0 | a | +| file://:0:0:0:0 | __va_list_tag | 4 members | 1 locations | 0 | gp_offset | +| file://:0:0:0:0 | __va_list_tag | 4 members | 1 locations | 1 | fp_offset | +| file://:0:0:0:0 | __va_list_tag | 4 members | 1 locations | 2 | overflow_arg_area | +| file://:0:0:0:0 | __va_list_tag | 4 members | 1 locations | 3 | reg_save_area | diff --git a/cpp/ql/test/library-tests/structs/mutual_recursion/compatible_members.ql b/cpp/ql/test/library-tests/structs/mutual_recursion/compatible_members.ql new file mode 100644 index 000000000000..c41a2fe45bc5 --- /dev/null +++ b/cpp/ql/test/library-tests/structs/mutual_recursion/compatible_members.ql @@ -0,0 +1,10 @@ +import cpp + +from Struct s, int i, string m, string members, string locations +where + if exists(s.getAMember()) + then m = s.getAMember(i).toString() + else (m = "" and i = -1) + and members = count(s.getAMember()) + " members" + and locations = count(s.getLocation()) + " locations" +select s, members, locations, i, m diff --git a/cpp/ql/test/library-tests/structs/mutual_recursion/compatible_structs.expected b/cpp/ql/test/library-tests/structs/mutual_recursion/compatible_structs.expected new file mode 100644 index 000000000000..e9240bfdc9d3 --- /dev/null +++ b/cpp/ql/test/library-tests/structs/mutual_recursion/compatible_structs.expected @@ -0,0 +1,23 @@ +| a.c:1:8:1:11 | Node | 1 different struct(s) called Node | +| a.c:8:8:8:18 | CompatibleA | 1 different struct(s) called CompatibleA | +| a.c:12:8:12:18 | CompatibleB | 1 different struct(s) called CompatibleB | +| a.c:17:8:17:18 | CompatibleC | 1 different struct(s) called CompatibleC | +| a.c:24:8:24:18 | CompatibleD | 1 different struct(s) called CompatibleD | +| a.c:34:8:34:20 | IncompatibleA | 1 different struct(s) called IncompatibleA | +| a.c:38:8:38:20 | IncompatibleB | 1 different struct(s) called IncompatibleB | +| a.c:43:8:43:20 | IncompatibleC | 2 different struct(s) called IncompatibleC | +| a.c:48:8:48:20 | IncompatibleD | 1 different struct(s) called IncompatibleD | +| a.c:55:8:55:20 | NonRecursiveA | 2 different struct(s) called NonRecursiveA | +| a.c:59:8:59:20 | NonRecursiveB | 2 different struct(s) called NonRecursiveB | +| b.c:1:8:1:11 | Node | 1 different struct(s) called Node | +| b.c:8:8:8:18 | CompatibleA | 1 different struct(s) called CompatibleA | +| b.c:12:8:12:18 | CompatibleB | 1 different struct(s) called CompatibleB | +| b.c:18:8:18:18 | CompatibleC | 1 different struct(s) called CompatibleC | +| b.c:23:8:23:18 | CompatibleD | 1 different struct(s) called CompatibleD | +| b.c:30:8:30:20 | IncompatibleA | 1 different struct(s) called IncompatibleA | +| b.c:34:8:34:20 | IncompatibleB | 1 different struct(s) called IncompatibleB | +| b.c:39:8:39:20 | IncompatibleC | 2 different struct(s) called IncompatibleC | +| b.c:44:8:44:20 | IncompatibleD | 1 different struct(s) called IncompatibleD | +| b.c:51:8:51:20 | NonRecursiveA | 2 different struct(s) called NonRecursiveA | +| b.c:55:8:55:20 | NonRecursiveB | 2 different struct(s) called NonRecursiveB | +| file://:0:0:0:0 | __va_list_tag | 1 different struct(s) called __va_list_tag | diff --git a/cpp/ql/test/library-tests/structs/mutual_recursion/compatible_structs.ql b/cpp/ql/test/library-tests/structs/mutual_recursion/compatible_structs.ql new file mode 100644 index 000000000000..9fb7cc296ca5 --- /dev/null +++ b/cpp/ql/test/library-tests/structs/mutual_recursion/compatible_structs.ql @@ -0,0 +1,8 @@ +import cpp + +from Struct s, string distinct +where + distinct = count(Struct x | x.getName() = s.getName()) + + " different struct(s) called " + + s.getName() +select s, distinct diff --git a/cpp/ql/test/library-tests/structs/structs/structs.c b/cpp/ql/test/library-tests/structs/structs/structs.c new file mode 100644 index 000000000000..a7b2518cc190 --- /dev/null +++ b/cpp/ql/test/library-tests/structs/structs/structs.c @@ -0,0 +1,12 @@ + +struct foo { + int i; +}; + +void f(void) { + int l; + struct foo s; + + l = s.i; +} + diff --git a/cpp/ql/test/library-tests/structs/structs/structs.expected b/cpp/ql/test/library-tests/structs/structs/structs.expected new file mode 100644 index 000000000000..00baa22ee602 --- /dev/null +++ b/cpp/ql/test/library-tests/structs/structs/structs.expected @@ -0,0 +1 @@ +| structs.c:10:5:10:11 | ... = ... | structs.c:10:5:10:5 | l | int | structs.c:10:11:10:11 | i | int | diff --git a/cpp/ql/test/library-tests/structs/structs/structs.ql b/cpp/ql/test/library-tests/structs/structs/structs.ql new file mode 100644 index 000000000000..c49673fddea4 --- /dev/null +++ b/cpp/ql/test/library-tests/structs/structs/structs.ql @@ -0,0 +1,9 @@ +import cpp + +from Assignment a +select a, + a.getLValue() as l, + l.getType().explain(), + a.getRValue() as r, + r.getType().explain() + diff --git a/cpp/ql/test/library-tests/sub_basic_blocks/cut.expected b/cpp/ql/test/library-tests/sub_basic_blocks/cut.expected new file mode 100644 index 000000000000..bfa299392220 --- /dev/null +++ b/cpp/ql/test/library-tests/sub_basic_blocks/cut.expected @@ -0,0 +1,18 @@ +| f | false | 154 | 154 | call to cut [line 8-9, 4 nodes, pos 2, 1 predecessors, 1 successors] | +| f | false | 166 | 166 | i [line 5-5, 3 nodes, pos 0 (first in BB), 2 predecessors, 1 successors] | +| f | false | 170 | 170 | call to cut [line 5-5, 4 nodes, pos 1 (last in BB), 1 predecessors, 2 successors] | +| f | false | 183 | 183 | call to cut [line 7-8, 5 nodes, pos 1, 1 predecessors, 1 successors] | +| f | false | 192 | 192 | call to cut [line 9-9, 1 nodes, pos 3 (last in BB), 1 predecessors, 1 successors] | +| f | false | 198 | 198 | { ... } [line 5-7, 8 nodes, pos 0 (first in BB), 1 predecessors, 1 successors] | +| f | false | 205 | 205 | call to cut [line 11-3, 3 nodes, pos 1 (last in BB), 1 predecessors, 0 successors] | +| f | false | 211 | 211 | return ... [line 11-11, 3 nodes, pos 0 (first in BB), 1 predecessors, 1 successors] | +| f | false | 213 | 213 | { ... } [line 3-10, 5 nodes, pos 0 (first in BB) (last in BB), 0 predecessors, 1 successors] | +| f | true | 154 | 192 | | +| f | true | 166 | 170 | | +| f | true | 170 | 198 | T | +| f | true | 170 | 211 | F | +| f | true | 183 | 154 | | +| f | true | 192 | 166 | | +| f | true | 198 | 183 | | +| f | true | 211 | 205 | | +| f | true | 213 | 166 | | diff --git a/cpp/ql/test/library-tests/sub_basic_blocks/cut.ql b/cpp/ql/test/library-tests/sub_basic_blocks/cut.ql new file mode 100644 index 000000000000..1c4d4d623afa --- /dev/null +++ b/cpp/ql/test/library-tests/sub_basic_blocks/cut.ql @@ -0,0 +1,13 @@ +// query-type: graph +import sbb_test + +class CutCall extends SubBasicBlockCutNode { + CutCall() { + this.(FunctionCall).getTarget().getName() = "cut" + } +} + +from boolean isEdge, SubBasicBlock x, SubBasicBlock y, string label +where isNode(isEdge, x, y, label) or isSuccessor(isEdge, x, y, label) +select x.getEnclosingFunction().toString(), isEdge, x, y, label + diff --git a/cpp/ql/test/library-tests/sub_basic_blocks/getNode.expected b/cpp/ql/test/library-tests/sub_basic_blocks/getNode.expected new file mode 100644 index 000000000000..5a8e3f01c79f --- /dev/null +++ b/cpp/ql/test/library-tests/sub_basic_blocks/getNode.expected @@ -0,0 +1,36 @@ +| call to cut [line 5-5, 4 nodes, pos 1 (last in BB), 1 predecessors, 2 successors] | 0 | test.c:5:18:5:20 | call to cut | +| call to cut [line 5-5, 4 nodes, pos 1 (last in BB), 1 predecessors, 2 successors] | 1 | test.c:5:27:5:27 | 1 | +| call to cut [line 5-5, 4 nodes, pos 1 (last in BB), 1 predecessors, 2 successors] | 2 | test.c:5:18:5:27 | ... + ... | +| call to cut [line 5-5, 4 nodes, pos 1 (last in BB), 1 predecessors, 2 successors] | 3 | test.c:5:12:5:27 | ... < ... | +| call to cut [line 7-8, 5 nodes, pos 1, 1 predecessors, 1 successors] | 0 | test.c:7:6:7:8 | call to cut | +| call to cut [line 7-8, 5 nodes, pos 1, 1 predecessors, 1 successors] | 1 | test.c:8:6:8:24 | declaration | +| call to cut [line 7-8, 5 nodes, pos 1, 1 predecessors, 1 successors] | 2 | test.c:8:13:8:23 | initializer for k | +| call to cut [line 7-8, 5 nodes, pos 1, 1 predecessors, 1 successors] | 3 | test.c:8:14:8:14 | j | +| call to cut [line 7-8, 5 nodes, pos 1, 1 predecessors, 1 successors] | 4 | test.c:8:22:8:22 | j | +| call to cut [line 8-9, 4 nodes, pos 2, 1 predecessors, 1 successors] | 0 | test.c:8:18:8:20 | call to cut | +| call to cut [line 8-9, 4 nodes, pos 2, 1 predecessors, 1 successors] | 1 | test.c:8:14:8:23 | ... + ... | +| call to cut [line 8-9, 4 nodes, pos 2, 1 predecessors, 1 successors] | 2 | test.c:9:6:9:12 | ExprStmt | +| call to cut [line 8-9, 4 nodes, pos 2, 1 predecessors, 1 successors] | 3 | test.c:9:10:9:10 | k | +| call to cut [line 9-9, 1 nodes, pos 3 (last in BB), 1 predecessors, 1 successors] | 0 | test.c:9:6:9:8 | call to cut | +| call to cut [line 11-3, 3 nodes, pos 1 (last in BB), 1 predecessors, 0 successors] | 0 | test.c:11:16:11:18 | call to cut | +| call to cut [line 11-3, 3 nodes, pos 1 (last in BB), 1 predecessors, 0 successors] | 1 | test.c:11:12:11:21 | ... + ... | +| call to cut [line 11-3, 3 nodes, pos 1 (last in BB), 1 predecessors, 0 successors] | 2 | test.c:3:5:3:5 | f | +| i [line 5-5, 3 nodes, pos 0 (first in BB), 2 predecessors, 1 successors] | 0 | test.c:5:14:5:14 | i | +| i [line 5-5, 3 nodes, pos 0 (first in BB), 2 predecessors, 1 successors] | 1 | test.c:5:12:5:14 | ++ ... | +| i [line 5-5, 3 nodes, pos 0 (first in BB), 2 predecessors, 1 successors] | 2 | test.c:5:22:5:22 | n | +| return ... [line 11-11, 3 nodes, pos 0 (first in BB), 1 predecessors, 1 successors] | 0 | test.c:11:5:11:22 | return ... | +| return ... [line 11-11, 3 nodes, pos 0 (first in BB), 1 predecessors, 1 successors] | 1 | test.c:11:12:11:12 | 1 | +| return ... [line 11-11, 3 nodes, pos 0 (first in BB), 1 predecessors, 1 successors] | 2 | test.c:11:20:11:20 | i | +| { ... } [line 3-10, 5 nodes, pos 0 (first in BB) (last in BB), 0 predecessors, 1 successors] | 0 | test.c:3:14:12:1 | { ... } | +| { ... } [line 3-10, 5 nodes, pos 0 (first in BB) (last in BB), 0 predecessors, 1 successors] | 1 | test.c:4:5:4:14 | declaration | +| { ... } [line 3-10, 5 nodes, pos 0 (first in BB) (last in BB), 0 predecessors, 1 successors] | 2 | test.c:4:12:4:13 | initializer for i | +| { ... } [line 3-10, 5 nodes, pos 0 (first in BB) (last in BB), 0 predecessors, 1 successors] | 3 | test.c:4:12:4:13 | 0 | +| { ... } [line 3-10, 5 nodes, pos 0 (first in BB) (last in BB), 0 predecessors, 1 successors] | 4 | test.c:5:5:10:5 | while (...) ... | +| { ... } [line 5-7, 8 nodes, pos 0 (first in BB), 1 predecessors, 1 successors] | 0 | test.c:5:30:10:5 | { ... } | +| { ... } [line 5-7, 8 nodes, pos 0 (first in BB), 1 predecessors, 1 successors] | 1 | test.c:6:6:6:19 | declaration | +| { ... } [line 5-7, 8 nodes, pos 0 (first in BB), 1 predecessors, 1 successors] | 2 | test.c:6:13:6:18 | initializer for j | +| { ... } [line 5-7, 8 nodes, pos 0 (first in BB), 1 predecessors, 1 successors] | 3 | test.c:6:14:6:14 | i | +| { ... } [line 5-7, 8 nodes, pos 0 (first in BB), 1 predecessors, 1 successors] | 4 | test.c:6:18:6:18 | 1 | +| { ... } [line 5-7, 8 nodes, pos 0 (first in BB), 1 predecessors, 1 successors] | 5 | test.c:6:14:6:18 | ... + ... | +| { ... } [line 5-7, 8 nodes, pos 0 (first in BB), 1 predecessors, 1 successors] | 6 | test.c:7:6:7:12 | ExprStmt | +| { ... } [line 5-7, 8 nodes, pos 0 (first in BB), 1 predecessors, 1 successors] | 7 | test.c:7:10:7:10 | 0 | diff --git a/cpp/ql/test/library-tests/sub_basic_blocks/getNode.ql b/cpp/ql/test/library-tests/sub_basic_blocks/getNode.ql new file mode 100644 index 000000000000..c5d09bb7c7b9 --- /dev/null +++ b/cpp/ql/test/library-tests/sub_basic_blocks/getNode.ql @@ -0,0 +1,10 @@ +import sbb_test + +class CutCall extends SubBasicBlockCutNode { + CutCall() { + this.(FunctionCall).getTarget().getName() = "cut" + } +} + +from SubBasicBlock sbb, int i +select subBasicBlockDebugInfo(sbb), i, sbb.getNode(i) diff --git a/cpp/ql/test/library-tests/sub_basic_blocks/no_cut.expected b/cpp/ql/test/library-tests/sub_basic_blocks/no_cut.expected new file mode 100644 index 000000000000..39e1a4bad842 --- /dev/null +++ b/cpp/ql/test/library-tests/sub_basic_blocks/no_cut.expected @@ -0,0 +1,8 @@ +| f | false | 166 | 166 | i [line 5-5, 7 nodes, pos 0 (first in BB) (last in BB), 2 predecessors, 2 successors] | +| f | false | 198 | 198 | { ... } [line 5-9, 18 nodes, pos 0 (first in BB) (last in BB), 1 predecessors, 1 successors] | +| f | false | 211 | 211 | return ... [line 11-3, 6 nodes, pos 0 (first in BB) (last in BB), 1 predecessors, 0 successors] | +| f | false | 213 | 213 | { ... } [line 3-10, 5 nodes, pos 0 (first in BB) (last in BB), 0 predecessors, 1 successors] | +| f | true | 166 | 198 | T | +| f | true | 166 | 211 | F | +| f | true | 198 | 166 | | +| f | true | 213 | 166 | | diff --git a/cpp/ql/test/library-tests/sub_basic_blocks/no_cut.ql b/cpp/ql/test/library-tests/sub_basic_blocks/no_cut.ql new file mode 100644 index 000000000000..f4e194c07938 --- /dev/null +++ b/cpp/ql/test/library-tests/sub_basic_blocks/no_cut.ql @@ -0,0 +1,9 @@ +// query-type: graph +import sbb_test + +// Note: no instance of `SubBasicBlockCutNode` + +from boolean isEdge, SubBasicBlock x, SubBasicBlock y, string label +where isNode(isEdge, x, y, label) or isSuccessor(isEdge, x, y, label) +select x.getEnclosingFunction().toString(), isEdge, x, y, label + diff --git a/cpp/ql/test/library-tests/sub_basic_blocks/sbb_test.qll b/cpp/ql/test/library-tests/sub_basic_blocks/sbb_test.qll new file mode 100644 index 000000000000..185e4635853f --- /dev/null +++ b/cpp/ql/test/library-tests/sub_basic_blocks/sbb_test.qll @@ -0,0 +1,31 @@ +import cpp +import semmle.code.cpp.controlflow.SubBasicBlocks + +string subBasicBlockDebugInfo(SubBasicBlock sbb) { + result = sbb.getStart().toString() + + " [line " + sbb.getStart().getLocation().getStartLine() + "-" + + sbb.getEnd().getLocation().getEndLine() + ", " + + sbb.getNumberOfNodes() + " nodes, " + + "pos " + sbb.getPosInBasicBlock(_) + + any(string s | if sbb.firstInBB() then s = " (first in BB)" else s = "") + + any(string s | if sbb.lastInBB() then s = " (last in BB)" else s = "") + + ", " + + count(sbb.getAPredecessor()) + " predecessors, " + + count(sbb.getASuccessor()) + " successors" + + "]" +} + +predicate isNode(boolean isEdge, SubBasicBlock x, SubBasicBlock y, string label) { + isEdge = false and + x = y and + label = subBasicBlockDebugInfo(x) +} + +predicate isSuccessor(boolean isEdge, SubBasicBlock x, SubBasicBlock y, string label) { + exists(string truelabel, string falselabel | + isEdge = true + and x.getASuccessor() = y + and if x.getATrueSuccessor() = y then truelabel = "T" else truelabel = "" + and if x.getAFalseSuccessor() = y then falselabel = "F" else falselabel = "" + and label = truelabel + falselabel) +} diff --git a/cpp/ql/test/library-tests/sub_basic_blocks/test.c b/cpp/ql/test/library-tests/sub_basic_blocks/test.c new file mode 100644 index 000000000000..362d2b76a619 --- /dev/null +++ b/cpp/ql/test/library-tests/sub_basic_blocks/test.c @@ -0,0 +1,12 @@ +int cut(int); + +int f(int n) { + int i = 0; + while (++i < cut(n) + 1) { + int j = i + 1; + cut(0); + int k = j + cut(j); + cut(k); + } + return 1 + cut(i); +} diff --git a/cpp/ql/test/library-tests/switch/blocks.expected b/cpp/ql/test/library-tests/switch/blocks.expected new file mode 100644 index 000000000000..9e08264d0f7e --- /dev/null +++ b/cpp/ql/test/library-tests/switch/blocks.expected @@ -0,0 +1,14 @@ +| switch.c:2:5:2:5 | f | switch.c:2:14:12:1 | { ... } | switch.c:3:5:10:5 | switch (...) ... | +| switch.c:2:5:2:5 | f | switch.c:2:14:12:1 | { ... } | switch.c:10:5:10:5 | label ...: | +| switch.c:2:5:2:5 | f | switch.c:2:14:12:1 | { ... } | switch.c:11:5:11:13 | return ... | +| switch.c:14:6:14:6 | g | switch.c:14:15:52:1 | { ... } | switch.c:15:2:15:7 | declaration | +| switch.c:14:6:14:6 | g | switch.c:14:15:52:1 | { ... } | switch.c:17:5:26:5 | switch (...) ... | +| switch.c:14:6:14:6 | g | switch.c:14:15:52:1 | { ... } | switch.c:26:5:26:5 | label ...: | +| switch.c:14:6:14:6 | g | switch.c:14:15:52:1 | { ... } | switch.c:28:5:29:5 | switch (...) ... | +| switch.c:14:6:14:6 | g | switch.c:14:15:52:1 | { ... } | switch.c:31:5:44:2 | switch (...) ... | +| switch.c:14:6:14:6 | g | switch.c:14:15:52:1 | { ... } | switch.c:44:2:44:2 | label ...: | +| switch.c:14:6:14:6 | g | switch.c:14:15:52:1 | { ... } | switch.c:46:2:51:2 | switch (...) ... | +| switch.c:14:6:14:6 | g | switch.c:14:15:52:1 | { ... } | switch.c:52:1:52:1 | return ... | +| switch.cpp:4:5:4:5 | f | switch.cpp:4:14:9:1 | { ... } | switch.cpp:5:5:8:5 | switch (...) ... | +| switch_gnu99.c:2:5:2:11 | f_gnu99 | switch_gnu99.c:2:20:14:1 | { ... } | switch_gnu99.c:3:5:3:5 | { ... } | +| switch_gnu99.c:2:5:2:11 | f_gnu99 | switch_gnu99.c:2:20:14:1 | { ... } | switch_gnu99.c:13:5:13:13 | return ... | diff --git a/cpp/ql/test/library-tests/switch/blocks.ql b/cpp/ql/test/library-tests/switch/blocks.ql new file mode 100644 index 000000000000..e24e5670bd7b --- /dev/null +++ b/cpp/ql/test/library-tests/switch/blocks.ql @@ -0,0 +1,6 @@ +import cpp + +from Function f, Block b +where b = f.getEntryPoint() +select f, b, b.getAStmt() + diff --git a/cpp/ql/test/library-tests/switch/switch.c b/cpp/ql/test/library-tests/switch/switch.c new file mode 100644 index 000000000000..a7044cec8cd4 --- /dev/null +++ b/cpp/ql/test/library-tests/switch/switch.c @@ -0,0 +1,52 @@ + +int f(int x) { + switch(x) { + case 1: + return 2; + case 2: + break; + default: + return 3; + } + return 4; +} + +void g(int y) { + int i; + + switch (y) { + case 1: + break; + case 2: + { + } break; + case 3: + i++; + break; + } + + switch (y) { + } + + switch (y) { + case 1: + break; + case 2: + { + break; + } + case 3: + { + { + break; + } + } + } + + switch (y) { + case 1: + case 2: + return; + case 3: + } +} diff --git a/cpp/ql/test/library-tests/switch/switch.cpp b/cpp/ql/test/library-tests/switch/switch.cpp new file mode 100644 index 000000000000..4fa8fd215775 --- /dev/null +++ b/cpp/ql/test/library-tests/switch/switch.cpp @@ -0,0 +1,9 @@ + +void fn(); + +int f(int x) { + switch(x) { + default: + throw "error"; + } +} diff --git a/cpp/ql/test/library-tests/switch/switch_gnu99.c b/cpp/ql/test/library-tests/switch/switch_gnu99.c new file mode 100644 index 000000000000..56647d88d3fe --- /dev/null +++ b/cpp/ql/test/library-tests/switch/switch_gnu99.c @@ -0,0 +1,15 @@ +// semmle-extractor-options: -std=gnu99 +int f_gnu99(int x) { + switch(x) { + case 1: + return 2; + case 2: + break; + case 3 ... 6: + break; + default: + return 3; + } + return 4; +} + diff --git a/cpp/ql/test/library-tests/switch/switchcase.expected b/cpp/ql/test/library-tests/switch/switchcase.expected new file mode 100644 index 000000000000..b8f83f528365 --- /dev/null +++ b/cpp/ql/test/library-tests/switch/switchcase.expected @@ -0,0 +1,84 @@ +| switch.c:4:5:4:11 | case ...: | switch.c:3:5:10:5 | switch (...) ... | getSwitchStmt | +| switch.c:4:5:4:11 | case ...: | switch.c:4:5:4:11 | case ...: | terminatesInReturnStmt() | +| switch.c:4:5:4:11 | case ...: | switch.c:4:10:4:10 | 1 | getExpr | +| switch.c:4:5:4:11 | case ...: | switch.c:5:9:5:17 | return ... | getLastStmt | +| switch.c:4:5:4:11 | case ...: | switch.c:6:5:6:11 | case ...: | getNextSwitchCase | +| switch.c:6:5:6:11 | case ...: | switch.c:3:5:10:5 | switch (...) ... | getSwitchStmt | +| switch.c:6:5:6:11 | case ...: | switch.c:4:5:4:11 | case ...: | getPreviousSwitchCase | +| switch.c:6:5:6:11 | case ...: | switch.c:6:5:6:11 | case ...: | terminatesInBreakStmt() | +| switch.c:6:5:6:11 | case ...: | switch.c:6:10:6:10 | 2 | getExpr | +| switch.c:6:5:6:11 | case ...: | switch.c:7:9:7:14 | break; | getLastStmt | +| switch.c:6:5:6:11 | case ...: | switch.c:8:5:8:12 | default: | getNextSwitchCase | +| switch.c:8:5:8:12 | default: | switch.c:3:5:10:5 | switch (...) ... | getSwitchStmt | +| switch.c:8:5:8:12 | default: | switch.c:6:5:6:11 | case ...: | getPreviousSwitchCase | +| switch.c:8:5:8:12 | default: | switch.c:8:5:8:12 | default: | terminatesInReturnStmt() | +| switch.c:8:5:8:12 | default: | switch.c:9:9:9:17 | return ... | getLastStmt | +| switch.c:18:5:18:11 | case ...: | switch.c:17:5:26:5 | switch (...) ... | getSwitchStmt | +| switch.c:18:5:18:11 | case ...: | switch.c:18:5:18:11 | case ...: | terminatesInBreakStmt() | +| switch.c:18:5:18:11 | case ...: | switch.c:18:10:18:10 | 1 | getExpr | +| switch.c:18:5:18:11 | case ...: | switch.c:19:3:19:8 | break; | getLastStmt | +| switch.c:18:5:18:11 | case ...: | switch.c:20:5:20:11 | case ...: | getNextSwitchCase | +| switch.c:20:5:20:11 | case ...: | switch.c:17:5:26:5 | switch (...) ... | getSwitchStmt | +| switch.c:20:5:20:11 | case ...: | switch.c:18:5:18:11 | case ...: | getPreviousSwitchCase | +| switch.c:20:5:20:11 | case ...: | switch.c:20:5:20:11 | case ...: | terminatesInBreakStmt() | +| switch.c:20:5:20:11 | case ...: | switch.c:20:10:20:10 | 2 | getExpr | +| switch.c:20:5:20:11 | case ...: | switch.c:22:5:22:10 | break; | getLastStmt | +| switch.c:20:5:20:11 | case ...: | switch.c:23:5:23:11 | case ...: | getNextSwitchCase | +| switch.c:23:5:23:11 | case ...: | switch.c:17:5:26:5 | switch (...) ... | getSwitchStmt | +| switch.c:23:5:23:11 | case ...: | switch.c:20:5:20:11 | case ...: | getPreviousSwitchCase | +| switch.c:23:5:23:11 | case ...: | switch.c:23:5:23:11 | case ...: | terminatesInBreakStmt() | +| switch.c:23:5:23:11 | case ...: | switch.c:23:10:23:10 | 3 | getExpr | +| switch.c:23:5:23:11 | case ...: | switch.c:25:3:25:8 | break; | getLastStmt | +| switch.c:32:2:32:8 | case ...: | switch.c:31:5:44:2 | switch (...) ... | getSwitchStmt | +| switch.c:32:2:32:8 | case ...: | switch.c:32:2:32:8 | case ...: | terminatesInBreakStmt() | +| switch.c:32:2:32:8 | case ...: | switch.c:32:7:32:7 | 1 | getExpr | +| switch.c:32:2:32:8 | case ...: | switch.c:33:3:33:8 | break; | getLastStmt | +| switch.c:32:2:32:8 | case ...: | switch.c:34:2:34:8 | case ...: | getNextSwitchCase | +| switch.c:34:2:34:8 | case ...: | switch.c:31:5:44:2 | switch (...) ... | getSwitchStmt | +| switch.c:34:2:34:8 | case ...: | switch.c:32:2:32:8 | case ...: | getPreviousSwitchCase | +| switch.c:34:2:34:8 | case ...: | switch.c:34:2:34:8 | case ...: | terminatesInBreakStmt() | +| switch.c:34:2:34:8 | case ...: | switch.c:34:7:34:7 | 2 | getExpr | +| switch.c:34:2:34:8 | case ...: | switch.c:36:4:36:9 | break; | getLastStmt | +| switch.c:34:2:34:8 | case ...: | switch.c:38:2:38:8 | case ...: | getNextSwitchCase | +| switch.c:38:2:38:8 | case ...: | switch.c:31:5:44:2 | switch (...) ... | getSwitchStmt | +| switch.c:38:2:38:8 | case ...: | switch.c:34:2:34:8 | case ...: | getPreviousSwitchCase | +| switch.c:38:2:38:8 | case ...: | switch.c:38:2:38:8 | case ...: | terminatesInBreakStmt() | +| switch.c:38:2:38:8 | case ...: | switch.c:38:7:38:7 | 3 | getExpr | +| switch.c:38:2:38:8 | case ...: | switch.c:41:5:41:10 | break; | getLastStmt | +| switch.c:47:2:47:8 | case ...: | switch.c:46:2:51:2 | switch (...) ... | getSwitchStmt | +| switch.c:47:2:47:8 | case ...: | switch.c:47:7:47:7 | 1 | getExpr | +| switch.c:47:2:47:8 | case ...: | switch.c:48:2:48:8 | case ...: | getNextSwitchCase | +| switch.c:48:2:48:8 | case ...: | switch.c:46:2:51:2 | switch (...) ... | getSwitchStmt | +| switch.c:48:2:48:8 | case ...: | switch.c:47:2:47:8 | case ...: | getPreviousSwitchCase | +| switch.c:48:2:48:8 | case ...: | switch.c:48:2:48:8 | case ...: | terminatesInReturnStmt() | +| switch.c:48:2:48:8 | case ...: | switch.c:48:7:48:7 | 2 | getExpr | +| switch.c:48:2:48:8 | case ...: | switch.c:49:3:49:9 | return ... | getLastStmt | +| switch.c:48:2:48:8 | case ...: | switch.c:50:2:50:8 | case ...: | getNextSwitchCase | +| switch.c:50:2:50:8 | case ...: | switch.c:46:2:51:2 | switch (...) ... | getSwitchStmt | +| switch.c:50:2:50:8 | case ...: | switch.c:48:2:48:8 | case ...: | getPreviousSwitchCase | +| switch.c:50:2:50:8 | case ...: | switch.c:50:7:50:7 | 3 | getExpr | +| switch.cpp:6:5:6:12 | default: | switch.cpp:5:5:8:5 | switch (...) ... | getSwitchStmt | +| switch.cpp:6:5:6:12 | default: | switch.cpp:6:5:6:12 | default: | terminatesInThrowStmt() | +| switch.cpp:6:5:6:12 | default: | switch.cpp:7:9:7:22 | ExprStmt | getLastStmt | +| switch_gnu99.c:4:5:4:11 | case ...: | switch_gnu99.c:3:5:12:5 | switch (...) ... | getSwitchStmt | +| switch_gnu99.c:4:5:4:11 | case ...: | switch_gnu99.c:4:5:4:11 | case ...: | terminatesInReturnStmt() | +| switch_gnu99.c:4:5:4:11 | case ...: | switch_gnu99.c:4:10:4:10 | 1 | getExpr | +| switch_gnu99.c:4:5:4:11 | case ...: | switch_gnu99.c:5:9:5:17 | return ... | getLastStmt | +| switch_gnu99.c:4:5:4:11 | case ...: | switch_gnu99.c:6:5:6:11 | case ...: | getNextSwitchCase | +| switch_gnu99.c:6:5:6:11 | case ...: | switch_gnu99.c:3:5:12:5 | switch (...) ... | getSwitchStmt | +| switch_gnu99.c:6:5:6:11 | case ...: | switch_gnu99.c:4:5:4:11 | case ...: | getPreviousSwitchCase | +| switch_gnu99.c:6:5:6:11 | case ...: | switch_gnu99.c:6:5:6:11 | case ...: | terminatesInBreakStmt() | +| switch_gnu99.c:6:5:6:11 | case ...: | switch_gnu99.c:6:10:6:10 | 2 | getExpr | +| switch_gnu99.c:6:5:6:11 | case ...: | switch_gnu99.c:7:9:7:14 | break; | getLastStmt | +| switch_gnu99.c:6:5:6:11 | case ...: | switch_gnu99.c:8:5:8:17 | case ...: | getNextSwitchCase | +| switch_gnu99.c:8:5:8:17 | case ...: | switch_gnu99.c:3:5:12:5 | switch (...) ... | getSwitchStmt | +| switch_gnu99.c:8:5:8:17 | case ...: | switch_gnu99.c:6:5:6:11 | case ...: | getPreviousSwitchCase | +| switch_gnu99.c:8:5:8:17 | case ...: | switch_gnu99.c:8:5:8:17 | case ...: | terminatesInBreakStmt() | +| switch_gnu99.c:8:5:8:17 | case ...: | switch_gnu99.c:8:10:8:10 | 3 | getExpr | +| switch_gnu99.c:8:5:8:17 | case ...: | switch_gnu99.c:8:16:8:16 | 6 | getEndExpr | +| switch_gnu99.c:8:5:8:17 | case ...: | switch_gnu99.c:9:9:9:14 | break; | getLastStmt | +| switch_gnu99.c:8:5:8:17 | case ...: | switch_gnu99.c:10:5:10:12 | default: | getNextSwitchCase | +| switch_gnu99.c:10:5:10:12 | default: | switch_gnu99.c:3:5:12:5 | switch (...) ... | getSwitchStmt | +| switch_gnu99.c:10:5:10:12 | default: | switch_gnu99.c:8:5:8:17 | case ...: | getPreviousSwitchCase | +| switch_gnu99.c:10:5:10:12 | default: | switch_gnu99.c:10:5:10:12 | default: | terminatesInReturnStmt() | +| switch_gnu99.c:10:5:10:12 | default: | switch_gnu99.c:11:9:11:17 | return ... | getLastStmt | diff --git a/cpp/ql/test/library-tests/switch/switchcase.ql b/cpp/ql/test/library-tests/switch/switchcase.ql new file mode 100644 index 000000000000..31cf19d5fe9a --- /dev/null +++ b/cpp/ql/test/library-tests/switch/switchcase.ql @@ -0,0 +1,13 @@ +import cpp + +from SwitchCase sc, Element e, string reason +where (e = sc.getExpr() and reason = "getExpr") + or (e = sc.getEndExpr() and reason = "getEndExpr") + or (e = sc.getSwitchStmt() and reason = "getSwitchStmt") + or (e = sc.getNextSwitchCase() and reason = "getNextSwitchCase") + or (e = sc.getPreviousSwitchCase() and reason = "getPreviousSwitchCase") + or (e = sc.getLastStmt() and reason = "getLastStmt") + or (e = sc and sc.terminatesInBreakStmt() and reason = "terminatesInBreakStmt()") + or (e = sc and sc.terminatesInReturnStmt() and reason = "terminatesInReturnStmt()") + or (e = sc and sc.terminatesInThrowStmt() and reason = "terminatesInThrowStmt()") +select sc, e, reason diff --git a/cpp/ql/test/library-tests/switch/switchstmt.expected b/cpp/ql/test/library-tests/switch/switchstmt.expected new file mode 100644 index 000000000000..24520422f70f --- /dev/null +++ b/cpp/ql/test/library-tests/switch/switchstmt.expected @@ -0,0 +1,34 @@ +| switch.c:3:5:10:5 | switch (...) ... | 3 | switch.c:3:12:3:12 | x | getExpr | +| switch.c:3:5:10:5 | switch (...) ... | 3 | switch.c:3:15:10:5 | { ... } | getStmt | +| switch.c:3:5:10:5 | switch (...) ... | 4 | switch.c:4:5:4:11 | case ...: | getASwitchCase | +| switch.c:3:5:10:5 | switch (...) ... | 6 | switch.c:6:5:6:11 | case ...: | getASwitchCase | +| switch.c:3:5:10:5 | switch (...) ... | 8 | switch.c:8:5:8:12 | default: | getASwitchCase | +| switch.c:3:5:10:5 | switch (...) ... | 8 | switch.c:8:5:8:12 | default: | getDefaultCase | +| switch.c:17:5:26:5 | switch (...) ... | 17 | switch.c:17:13:17:13 | y | getExpr | +| switch.c:17:5:26:5 | switch (...) ... | 17 | switch.c:17:16:26:5 | { ... } | getStmt | +| switch.c:17:5:26:5 | switch (...) ... | 18 | switch.c:18:5:18:11 | case ...: | getASwitchCase | +| switch.c:17:5:26:5 | switch (...) ... | 20 | switch.c:20:5:20:11 | case ...: | getASwitchCase | +| switch.c:17:5:26:5 | switch (...) ... | 23 | switch.c:23:5:23:11 | case ...: | getASwitchCase | +| switch.c:28:5:29:5 | switch (...) ... | 28 | switch.c:28:13:28:13 | y | getExpr | +| switch.c:28:5:29:5 | switch (...) ... | 28 | switch.c:28:16:29:5 | { ... } | getStmt | +| switch.c:31:5:44:2 | switch (...) ... | 31 | switch.c:31:13:31:13 | y | getExpr | +| switch.c:31:5:44:2 | switch (...) ... | 31 | switch.c:31:16:44:2 | { ... } | getStmt | +| switch.c:31:5:44:2 | switch (...) ... | 32 | switch.c:32:2:32:8 | case ...: | getASwitchCase | +| switch.c:31:5:44:2 | switch (...) ... | 34 | switch.c:34:2:34:8 | case ...: | getASwitchCase | +| switch.c:31:5:44:2 | switch (...) ... | 38 | switch.c:38:2:38:8 | case ...: | getASwitchCase | +| switch.c:46:2:51:2 | switch (...) ... | 46 | switch.c:46:10:46:10 | y | getExpr | +| switch.c:46:2:51:2 | switch (...) ... | 46 | switch.c:46:13:51:2 | { ... } | getStmt | +| switch.c:46:2:51:2 | switch (...) ... | 47 | switch.c:47:2:47:8 | case ...: | getASwitchCase | +| switch.c:46:2:51:2 | switch (...) ... | 48 | switch.c:48:2:48:8 | case ...: | getASwitchCase | +| switch.c:46:2:51:2 | switch (...) ... | 50 | switch.c:50:2:50:8 | case ...: | getASwitchCase | +| switch.cpp:5:5:8:5 | switch (...) ... | 5 | switch.cpp:5:12:5:12 | x | getExpr | +| switch.cpp:5:5:8:5 | switch (...) ... | 5 | switch.cpp:5:15:8:5 | { ... } | getStmt | +| switch.cpp:5:5:8:5 | switch (...) ... | 6 | switch.cpp:6:5:6:12 | default: | getASwitchCase | +| switch.cpp:5:5:8:5 | switch (...) ... | 6 | switch.cpp:6:5:6:12 | default: | getDefaultCase | +| switch_gnu99.c:3:5:12:5 | switch (...) ... | 3 | switch_gnu99.c:3:12:3:12 | x | getExpr | +| switch_gnu99.c:3:5:12:5 | switch (...) ... | 3 | switch_gnu99.c:3:15:12:5 | { ... } | getStmt | +| switch_gnu99.c:3:5:12:5 | switch (...) ... | 4 | switch_gnu99.c:4:5:4:11 | case ...: | getASwitchCase | +| switch_gnu99.c:3:5:12:5 | switch (...) ... | 6 | switch_gnu99.c:6:5:6:11 | case ...: | getASwitchCase | +| switch_gnu99.c:3:5:12:5 | switch (...) ... | 8 | switch_gnu99.c:8:5:8:17 | case ...: | getASwitchCase | +| switch_gnu99.c:3:5:12:5 | switch (...) ... | 10 | switch_gnu99.c:10:5:10:12 | default: | getASwitchCase | +| switch_gnu99.c:3:5:12:5 | switch (...) ... | 10 | switch_gnu99.c:10:5:10:12 | default: | getDefaultCase | diff --git a/cpp/ql/test/library-tests/switch/switchstmt.ql b/cpp/ql/test/library-tests/switch/switchstmt.ql new file mode 100644 index 000000000000..fd47c0620f15 --- /dev/null +++ b/cpp/ql/test/library-tests/switch/switchstmt.ql @@ -0,0 +1,8 @@ +import cpp + +from SwitchStmt ss, Element e, string reason +where (e = ss.getExpr() and reason = "getExpr") + or (e = ss.getStmt() and reason = "getStmt") + or (e = ss.getASwitchCase() and reason = "getASwitchCase") + or (e = ss.getDefaultCase() and reason = "getDefaultCase") +select ss, e.getLocation().getStartLine(), e, reason diff --git a/cpp/ql/test/library-tests/switch_cfg/cfg.expected b/cpp/ql/test/library-tests/switch_cfg/cfg.expected new file mode 100644 index 000000000000..9406983a0ce6 --- /dev/null +++ b/cpp/ql/test/library-tests/switch_cfg/cfg.expected @@ -0,0 +1,87 @@ +| 10 | 1 | { ... } | 12 switch (...) ... | +| 10 | 43 | f | | +| 12 | 2 | switch (...) ... | 12 cond | +| 12 | 3 | cond | 12 { ... } | +| 12 | 4 | { ... } | 13 case ...: | +| 12 | 4 | { ... } | 16 case ...: | +| 12 | 4 | { ... } | 18 case ...: | +| 12 | 4 | { ... } | 21 case ...: | +| 12 | 4 | { ... } | 22 case ...: | +| 12 | 4 | { ... } | 24 case ...: | +| 12 | 4 | { ... } | 25 default: | +| 12 | 4 | { ... } | 26 case ...: | +| 12 | 4 | { ... } | 28 case ...: | +| 13 | 1 | 1 | | +| 13 | 5 | case ...: | 14 ExprStmt | +| 14 | 6 | ExprStmt | 14 111 | +| 14 | 7 | 111 | 14 x | +| 14 | 8 | x | 14 ... = ... | +| 14 | 9 | ... = ... | 15 break; | +| 15 | 10 | break; | 31 label ...: | +| 16 | 1 | 1 | | +| 16 | 1 | 2 | | +| 16 | 1 | ... + ... | | +| 16 | 5 | case ...: | 17 ExprStmt | +| 17 | 6 | ExprStmt | 17 333 | +| 17 | 7 | 333 | 17 x | +| 17 | 8 | x | 17 ... = ... | +| 17 | 9 | ... = ... | 18 case ...: | +| 18 | 1 | 4 | | +| 18 | 10 | case ...: | 19 ExprStmt | +| 19 | 11 | ExprStmt | 19 444 | +| 19 | 12 | 444 | 19 x | +| 19 | 13 | x | 19 ... = ... | +| 19 | 14 | ... = ... | 20 break; | +| 20 | 15 | break; | 31 label ...: | +| 21 | 1 | 5 | | +| 21 | 5 | case ...: | 22 case ...: | +| 22 | 1 | 6 | | +| 22 | 6 | case ...: | 23 ExprStmt | +| 23 | 7 | ExprStmt | 23 777 | +| 23 | 8 | 777 | 23 x | +| 23 | 9 | x | 23 ... = ... | +| 23 | 10 | ... = ... | 24 case ...: | +| 24 | 1 | 7 | | +| 24 | 11 | case ...: | 25 default: | +| 25 | 12 | default: | 26 case ...: | +| 26 | 1 | 8 | | +| 26 | 13 | case ...: | 27 ExprStmt | +| 27 | 14 | ExprStmt | 27 888 | +| 27 | 15 | 888 | 27 x | +| 27 | 16 | x | 27 ... = ... | +| 27 | 17 | ... = ... | 28 case ...: | +| 28 | 1 | 9 | | +| 28 | 18 | case ...: | 29 ExprStmt | +| 29 | 19 | ExprStmt | 29 999 | +| 29 | 20 | 999 | 29 x | +| 29 | 21 | x | 29 ... = ... | +| 29 | 22 | ... = ... | 30 break; | +| 30 | 23 | break; | 31 label ...: | +| 31 | 41 | label ...: | 33 return ... | +| 33 | 42 | return ... | 10 f | +| 36 | 1 | { ... } | 38 switch (...) ... | +| 36 | 22 | g | | +| 38 | 2 | switch (...) ... | 38 cond | +| 38 | 3 | cond | 38 { ... } | +| 38 | 4 | { ... } | 39 case ...: | +| 38 | 4 | { ... } | 42 case ...: | +| 38 | 4 | { ... } | 45 label ...: | +| 39 | 1 | 1 | | +| 39 | 5 | case ...: | 40 ExprStmt | +| 40 | 6 | ExprStmt | 40 111 | +| 40 | 7 | 111 | 40 x | +| 40 | 8 | x | 40 ... = ... | +| 40 | 9 | ... = ... | 41 break; | +| 41 | 10 | break; | 45 label ...: | +| 42 | 1 | 2 | | +| 42 | 5 | case ...: | 43 ExprStmt | +| 43 | 6 | ExprStmt | 43 222 | +| 43 | 7 | 222 | 43 x | +| 43 | 8 | x | 43 ... = ... | +| 43 | 9 | ... = ... | 45 label ...: | +| 45 | 16 | label ...: | 46 ExprStmt | +| 46 | 17 | ExprStmt | 46 999 | +| 46 | 18 | 999 | 46 x | +| 46 | 19 | x | 46 ... = ... | +| 46 | 20 | ... = ... | 48 return ... | +| 48 | 21 | return ... | 36 g | diff --git a/cpp/ql/test/library-tests/switch_cfg/cfg.ql b/cpp/ql/test/library-tests/switch_cfg/cfg.ql new file mode 100644 index 000000000000..2c1503bc78f5 --- /dev/null +++ b/cpp/ql/test/library-tests/switch_cfg/cfg.ql @@ -0,0 +1,17 @@ +import cpp + +string successorInfo(ControlFlowNode x) { + if exists(x.getASuccessor()) + then exists(ControlFlowNode y | + y = x.getASuccessor() | + result = y.getLocation().getStartLine().toString() + " " + + y.toString()) + else result = "" +} + +from ControlFlowNode x +select x.getLocation().getStartLine(), + count(x.getAPredecessor*()), // This helps order things sensibly + x.toString(), + successorInfo(x) + diff --git a/cpp/ql/test/library-tests/switch_cfg/switch.c b/cpp/ql/test/library-tests/switch_cfg/switch.c new file mode 100644 index 000000000000..1fc5ee037e1e --- /dev/null +++ b/cpp/ql/test/library-tests/switch_cfg/switch.c @@ -0,0 +1,50 @@ + +/* +Some padding to make the line numbers >= 10. +At the time of writing, this helps the results +get ordered in a sensible way. +*/ + +int x; + +void f(int cond) { + + switch (cond) { + case 1: + x = 111; + break; + case 1+2: + x = 333; + case 4: + x = 444; + break; + case 5: + case 6: + x = 777; + case 7: + default: + case 8: + x = 888; + case 9: + x = 999; + break; + } + + return; +} + +void g(int cond) { + + switch (cond) { + case 1: + x = 111; + break; + case 2: + x = 222; + /* no default case */ + } + x = 999; + + return; +} + diff --git a/cpp/ql/test/library-tests/synchronization/mutextype.expected b/cpp/ql/test/library-tests/synchronization/mutextype.expected new file mode 100644 index 000000000000..791b6e4b15bd --- /dev/null +++ b/cpp/ql/test/library-tests/synchronization/mutextype.expected @@ -0,0 +1,38 @@ +| test.cpp:2:7:2:12 | Mutex1 | getLockAccess() | test.cpp:14:6:14:9 | call to lock | +| test.cpp:2:7:2:12 | Mutex1 | getLockAccess() | test.cpp:17:9:17:12 | call to lock | +| test.cpp:2:7:2:12 | Mutex1 | getMustlockAccess() | test.cpp:14:6:14:9 | call to lock | +| test.cpp:2:7:2:12 | Mutex1 | getMustlockAccess() | test.cpp:17:9:17:12 | call to lock | +| test.cpp:2:7:2:12 | Mutex1 | getUnlockAccess() | test.cpp:15:6:15:11 | call to unlock | +| test.cpp:2:7:2:12 | Mutex1 | getUnlockAccess() | test.cpp:18:9:18:14 | call to unlock | +| test.cpp:2:7:2:12 | Mutex1 | is MutexType | test.cpp:2:7:2:12 | Mutex1 | +| test.cpp:21:7:21:12 | Mutex2 | getLockAccess() | test.cpp:31:10:31:17 | call to try_lock | +| test.cpp:21:7:21:12 | Mutex2 | getLockAccess() | test.cpp:43:11:43:18 | call to try_lock | +| test.cpp:21:7:21:12 | Mutex2 | getTrylockAccess() | test.cpp:31:10:31:17 | call to try_lock | +| test.cpp:21:7:21:12 | Mutex2 | getTrylockAccess() | test.cpp:43:11:43:18 | call to try_lock | +| test.cpp:21:7:21:12 | Mutex2 | getUnlockAccess() | test.cpp:33:7:33:12 | call to unlock | +| test.cpp:21:7:21:12 | Mutex2 | getUnlockAccess() | test.cpp:45:8:45:13 | call to unlock | +| test.cpp:21:7:21:12 | Mutex2 | is MutexType | test.cpp:21:7:21:12 | Mutex2 | +| test.cpp:49:7:49:12 | Mutex4 | getLockAccess() | test.cpp:64:2:64:18 | call to mutex4_mutex_lock | +| test.cpp:49:7:49:12 | Mutex4 | getLockAccess() | test.cpp:67:2:67:18 | call to mutex4_mutex_lock | +| test.cpp:49:7:49:12 | Mutex4 | getMustlockAccess() | test.cpp:64:2:64:18 | call to mutex4_mutex_lock | +| test.cpp:49:7:49:12 | Mutex4 | getMustlockAccess() | test.cpp:67:2:67:18 | call to mutex4_mutex_lock | +| test.cpp:49:7:49:12 | Mutex4 | getUnlockAccess() | test.cpp:65:2:65:20 | call to mutex4_mutex_unlock | +| test.cpp:49:7:49:12 | Mutex4 | getUnlockAccess() | test.cpp:68:2:68:20 | call to mutex4_mutex_unlock | +| test.cpp:49:7:49:12 | Mutex4 | is MutexType | test.cpp:49:7:49:12 | Mutex4 | +| test.cpp:97:7:97:12 | Mutex7 | getLockAccess() | test.cpp:108:6:108:13 | call to custom_l | +| test.cpp:97:7:97:12 | Mutex7 | getMustlockAccess() | test.cpp:108:6:108:13 | call to custom_l | +| test.cpp:97:7:97:12 | Mutex7 | getUnlockAccess() | test.cpp:109:6:109:14 | call to custom_ul | +| test.cpp:97:7:97:12 | Mutex7 | is MutexType | test.cpp:97:7:97:12 | Mutex7 | +| test.cpp:112:7:112:12 | Mutex8 | getLockAccess() | test.cpp:119:9:119:12 | call to lock | +| test.cpp:112:7:112:12 | Mutex8 | getLockAccess() | test.cpp:122:3:122:6 | call to lock | +| test.cpp:112:7:112:12 | Mutex8 | getLockAccess() | test.cpp:133:9:133:12 | call to lock | +| test.cpp:112:7:112:12 | Mutex8 | getLockAccess() | test.cpp:136:10:136:13 | call to lock | +| test.cpp:112:7:112:12 | Mutex8 | getMustlockAccess() | test.cpp:119:9:119:12 | call to lock | +| test.cpp:112:7:112:12 | Mutex8 | getMustlockAccess() | test.cpp:122:3:122:6 | call to lock | +| test.cpp:112:7:112:12 | Mutex8 | getMustlockAccess() | test.cpp:133:9:133:12 | call to lock | +| test.cpp:112:7:112:12 | Mutex8 | getMustlockAccess() | test.cpp:136:10:136:13 | call to lock | +| test.cpp:112:7:112:12 | Mutex8 | getUnlockAccess() | test.cpp:120:9:120:14 | call to unlock | +| test.cpp:112:7:112:12 | Mutex8 | getUnlockAccess() | test.cpp:123:3:123:8 | call to unlock | +| test.cpp:112:7:112:12 | Mutex8 | getUnlockAccess() | test.cpp:134:9:134:14 | call to unlock | +| test.cpp:112:7:112:12 | Mutex8 | getUnlockAccess() | test.cpp:137:10:137:15 | call to unlock | +| test.cpp:112:7:112:12 | Mutex8 | is MutexType | test.cpp:112:7:112:12 | Mutex8 | diff --git a/cpp/ql/test/library-tests/synchronization/mutextype.ql b/cpp/ql/test/library-tests/synchronization/mutextype.ql new file mode 100644 index 000000000000..728a181a3a68 --- /dev/null +++ b/cpp/ql/test/library-tests/synchronization/mutextype.ql @@ -0,0 +1,22 @@ +import cpp +import testoptions + +from MutexType mt, string s, Element e +where + ( + s = "is MutexType" and + e = mt + ) or ( + s = "getLockAccess()" and + e = mt.getLockAccess() + ) or ( + s = "getMustlockAccess()" and + e = mt.getMustlockAccess() + ) or ( + s = "getTrylockAccess()" and + e = mt.getTrylockAccess() + ) or ( + s = "getUnlockAccess()" and + e = mt.getUnlockAccess() + ) +select mt, s, e diff --git a/cpp/ql/test/library-tests/synchronization/synchronization.expected b/cpp/ql/test/library-tests/synchronization/synchronization.expected new file mode 100644 index 000000000000..323fd851cf47 --- /dev/null +++ b/cpp/ql/test/library-tests/synchronization/synchronization.expected @@ -0,0 +1,27 @@ +| test.cpp:14:6:14:9 | call to lock | lockCall | test.cpp:14:2:14:4 | mt1 | +| test.cpp:14:6:14:9 | call to lock | mustlockCall | test.cpp:14:2:14:4 | mt1 | +| test.cpp:15:6:15:11 | call to unlock | unlockCall | test.cpp:15:2:15:4 | mt1 | +| test.cpp:17:9:17:12 | call to lock | lockCall | test.cpp:17:2:17:6 | mt1_p | +| test.cpp:17:9:17:12 | call to lock | mustlockCall | test.cpp:17:2:17:6 | mt1_p | +| test.cpp:18:9:18:14 | call to unlock | unlockCall | test.cpp:18:2:18:6 | mt1_p | +| test.cpp:31:10:31:17 | call to try_lock | lockCall | test.cpp:31:6:31:8 | mt2 | +| test.cpp:31:10:31:17 | call to try_lock | trylockCall | test.cpp:31:6:31:8 | mt2 | +| test.cpp:33:7:33:12 | call to unlock | unlockCall | test.cpp:33:3:33:5 | mt2 | +| test.cpp:64:2:64:18 | call to mutex4_mutex_lock | lockCall | test.cpp:64:20:64:22 | mt4 | +| test.cpp:64:2:64:18 | call to mutex4_mutex_lock | mustlockCall | test.cpp:64:20:64:22 | mt4 | +| test.cpp:65:2:65:20 | call to mutex4_mutex_unlock | unlockCall | test.cpp:65:22:65:24 | mt4 | +| test.cpp:67:2:67:18 | call to mutex4_mutex_lock | lockCall | test.cpp:67:20:67:22 | mt4 | +| test.cpp:67:2:67:18 | call to mutex4_mutex_lock | mustlockCall | test.cpp:67:20:67:22 | mt4 | +| test.cpp:68:2:68:20 | call to mutex4_mutex_unlock | unlockCall | test.cpp:68:22:68:24 | mt4 | +| test.cpp:108:6:108:13 | call to custom_l | lockCall | test.cpp:108:2:108:4 | mt7 | +| test.cpp:108:6:108:13 | call to custom_l | mustlockCall | test.cpp:108:2:108:4 | mt7 | +| test.cpp:109:6:109:14 | call to custom_ul | unlockCall | test.cpp:109:2:109:4 | mt7 | +| test.cpp:119:9:119:12 | call to lock | lockCall | test.cpp:119:3:119:6 | this | +| test.cpp:119:9:119:12 | call to lock | mustlockCall | test.cpp:119:3:119:6 | this | +| test.cpp:120:9:120:14 | call to unlock | unlockCall | test.cpp:120:3:120:6 | this | +| test.cpp:122:3:122:6 | call to lock | lockCall | file://:0:0:0:0 | this | +| test.cpp:122:3:122:6 | call to lock | mustlockCall | file://:0:0:0:0 | this | +| test.cpp:123:3:123:8 | call to unlock | unlockCall | file://:0:0:0:0 | this | +| test.cpp:136:10:136:13 | call to lock | lockCall | test.cpp:136:3:136:7 | this8 | +| test.cpp:136:10:136:13 | call to lock | mustlockCall | test.cpp:136:3:136:7 | this8 | +| test.cpp:137:10:137:15 | call to unlock | unlockCall | test.cpp:137:3:137:7 | this8 | diff --git a/cpp/ql/test/library-tests/synchronization/synchronization.ql b/cpp/ql/test/library-tests/synchronization/synchronization.ql new file mode 100644 index 000000000000..962d2616e0bb --- /dev/null +++ b/cpp/ql/test/library-tests/synchronization/synchronization.ql @@ -0,0 +1,19 @@ +import cpp +import testoptions + +from FunctionCall call, string s, Expr arg +where + ( + s = "lockCall" and + lockCall(arg, call) + ) or ( + s = "mustlockCall" and + mustlockCall(arg, call) + ) or ( + s = "trylockCall" and + trylockCall(arg, call) + ) or ( + s = "unlockCall" and + unlockCall(arg, call) + ) +select call, s, arg diff --git a/cpp/ql/test/library-tests/synchronization/test.cpp b/cpp/ql/test/library-tests/synchronization/test.cpp new file mode 100644 index 000000000000..71693bf95d0c --- /dev/null +++ b/cpp/ql/test/library-tests/synchronization/test.cpp @@ -0,0 +1,139 @@ + +class Mutex1 +{ +public: + void lock(); + void unlock(); +}; + +void test1() +{ + Mutex1 mt1; + Mutex1 *mt1_p = &mt1; + + mt1.lock(); + mt1.unlock(); + + mt1_p->lock(); + mt1_p->unlock(); +} + +class Mutex2 +{ +public: + void lock(); // (this is not recognized as a MutexType.getLockFunction() because it is not used) + bool try_lock(); + void unlock(); +}; + +void test2(Mutex2 &mt2) +{ + if (mt2.try_lock()) + { + mt2.unlock(); + } +} + +class Mutex3 : public Mutex2 +{ +}; + +void test3(Mutex3 *mt3) +{ + if (mt3->try_lock()) // (these are partially recognized as a use of Mutex2) + { + mt3->unlock(); + } +} + +class Mutex4 +{ +public: + int state; +}; + +void mutex4_mutex_lock(Mutex4 &mutex); +void mutex4_mutex_unlock(Mutex4 &mutex); +void mutex4_mutex_lock(Mutex4 &mutex, int times); +void mutex4_mutex_unlock(Mutex4 &mutex, int times); + +Mutex4 mt4; + +void test4() +{ + mutex4_mutex_lock(mt4); + mutex4_mutex_unlock(mt4); + + mutex4_mutex_lock(mt4, 1); + mutex4_mutex_unlock(mt4, 1); +} + +typedef volatile unsigned int Mutex5; // NOT RECONGIZED (not a Class) + +void lock(Mutex5 &mutex); +void unlock(Mutex5 &mutex); + +void test5(Mutex5 *mt5) +{ + lock(*mt5); + unlock(*mt5); +} + +class NotAMutex6 +{ +public: + void lock(); + bool try_lock(); +}; + +void test6() +{ + NotAMutex6 mt6; + + mt6.lock(); + mt6.try_lock(); +} + +class Mutex7 +{ +public: + void custom_l(); + void custom_ul(); +}; + +void test7() +{ + Mutex7 mt7; + + mt7.custom_l(); + mt7.custom_ul(); +} + +class Mutex8 +{ +public: + void lock(); + void unlock(); + + void myMethod() { + this->lock(); + this->unlock(); + + lock(); + unlock(); + }; +}; + +class Mutex9 : public Mutex8 +{ +public: + void myMethod2() { + Mutex8 *this8 = this; + + this->lock(); // (partially recognized as a use of Mutex8) + this->unlock(); // (partially recognized as a use of Mutex8) + + this8->lock(); + this8->unlock(); + } +}; diff --git a/cpp/ql/test/library-tests/synchronization/testoptions.qll b/cpp/ql/test/library-tests/synchronization/testoptions.qll new file mode 100644 index 000000000000..669b6cb63ce9 --- /dev/null +++ b/cpp/ql/test/library-tests/synchronization/testoptions.qll @@ -0,0 +1,29 @@ +import cpp + +class MutexTypeForTest extends MutexType { + MutexTypeForTest() { + this.(Class).getName() = "Mutex7" + } + + override predicate mustlockAccess(FunctionCall fc, Expr arg) { + exists(Function f | + f = fc.getTarget() and + f.getName() = "custom_l" and + f.getDeclaringType() = this and + arg = fc.getQualifier() + ) + } + + override predicate trylockAccess(FunctionCall fc, Expr arg) { + none() + } + + override predicate unlockAccess(FunctionCall fc, Expr arg) { + exists(Function f | + f = fc.getTarget() and + f.getName() = "custom_ul" and + f.getDeclaringType() = this and + arg = fc.getQualifier() + ) + } +} 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 new file mode 100644 index 000000000000..0f20692d443f --- /dev/null +++ b/cpp/ql/test/library-tests/templates/CPP-202/template_args.expected @@ -0,0 +1,7 @@ +| file://:0:0:0:0 | __va_list_tag | | +| test.cpp:3:8:3:9 | s1<> | | +| test.cpp:3:8:3:9 | s1<> | | +| 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> | T | diff --git a/cpp/ql/test/library-tests/templates/CPP-202/template_args.ql b/cpp/ql/test/library-tests/templates/CPP-202/template_args.ql new file mode 100644 index 000000000000..3666df9cf59f --- /dev/null +++ b/cpp/ql/test/library-tests/templates/CPP-202/template_args.ql @@ -0,0 +1,8 @@ +import cpp + +from Class c, string arg +where if exists(c.getATemplateArgument()) + then arg = c.getATemplateArgument().toString() + else arg = "" +select c, arg + diff --git a/cpp/ql/test/library-tests/templates/CPP-202/test.cpp b/cpp/ql/test/library-tests/templates/CPP-202/test.cpp new file mode 100644 index 000000000000..2465c0799cdb --- /dev/null +++ b/cpp/ql/test/library-tests/templates/CPP-202/test.cpp @@ -0,0 +1,11 @@ + +template +struct s1 { typedef int type; }; +template +struct s2 { constexpr operator bool() const noexcept { return true; } }; +template {}>::type> +struct s3 {}; + +// previously failed compiling with: +// "test.cpp", line 6: error: expression must have a constant value +// template {}>::type> diff --git a/cpp/ql/test/library-tests/templates/CPP-203/decls.expected b/cpp/ql/test/library-tests/templates/CPP-203/decls.expected new file mode 100644 index 000000000000..c6f3122fe84f --- /dev/null +++ b/cpp/ql/test/library-tests/templates/CPP-203/decls.expected @@ -0,0 +1,19 @@ +| file://:0:0:0:0 | __va_list_tag | +| file://:0:0:0:0 | auto | +| file://:0:0:0:0 | fp_offset | +| file://:0:0:0:0 | gp_offset | +| file://:0:0:0:0 | operator= | +| file://:0:0:0:0 | operator= | +| file://:0:0:0:0 | overflow_arg_area | +| file://:0:0:0:0 | p#0 | +| file://:0:0:0:0 | p#0 | +| file://:0:0:0:0 | reg_save_area | +| test.cpp:2:16:2:16 | T | +| test.cpp:3:8:3:8 | operator= | +| test.cpp:3:8:3:8 | operator= | +| test.cpp:3:8:3:10 | Str | +| test.cpp:3:8:3:10 | Str | +| test.cpp:8:11:8:21 | val | +| test.cpp:8:19:8:19 | val | +| test.cpp:10:6:10:6 | f | +| test.cpp:11:15:11:15 | x | diff --git a/cpp/ql/test/library-tests/templates/CPP-203/decls.ql b/cpp/ql/test/library-tests/templates/CPP-203/decls.ql new file mode 100644 index 000000000000..6df6c775d591 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/CPP-203/decls.ql @@ -0,0 +1,5 @@ +import cpp + +from Declaration d +select d + diff --git a/cpp/ql/test/library-tests/templates/CPP-203/test.cpp b/cpp/ql/test/library-tests/templates/CPP-203/test.cpp new file mode 100644 index 000000000000..869728effac9 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/CPP-203/test.cpp @@ -0,0 +1,14 @@ + +template +struct Str { + static int const val; +}; + +template +int const Str::val = 3; + +void f(void) { + int const x = Str::val; +} + +// previously compiling this caused an assert in the extractor. diff --git a/cpp/ql/test/library-tests/templates/CPP-204/element.expected b/cpp/ql/test/library-tests/templates/CPP-204/element.expected new file mode 100644 index 000000000000..6242ab059370 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/CPP-204/element.expected @@ -0,0 +1,52 @@ +| file://:0:0:0:0 | | +| file://:0:0:0:0 | 0 | +| file://:0:0:0:0 | (global namespace) | +| file://:0:0:0:0 | __va_list_tag | +| file://:0:0:0:0 | __va_list_tag & | +| file://:0:0:0:0 | auto | +| file://:0:0:0:0 | const EC | +| file://:0:0:0:0 | const bool | +| file://:0:0:0:0 | definition of __va_list_tag | +| file://:0:0:0:0 | definition of fp_offset | +| file://:0:0:0:0 | definition of gp_offset | +| file://:0:0:0:0 | definition of overflow_arg_area | +| file://:0:0:0:0 | definition of reg_save_area | +| file://:0:0:0:0 | derivation | +| file://:0:0:0:0 | fp_offset | +| file://:0:0:0:0 | gp_offset | +| file://:0:0:0:0 | initializer for V | +| file://:0:0:0:0 | operator= | +| file://:0:0:0:0 | operator= | +| file://:0:0:0:0 | overflow_arg_area | +| file://:0:0:0:0 | reg_save_area | +| file://:0:0:0:0 | void * | +| test.cpp:0:0:0:0 | test.cpp | +| test.cpp:2:12:2:13 | EC | +| test.cpp:2:12:2:13 | definition of EC | +| test.cpp:3:5:3:5 | V | +| test.cpp:6:10:6:11 | mention of EC | +| test.cpp:7:8:7:10 | IsX | +| test.cpp:7:8:7:10 | IsX | +| test.cpp:7:8:7:10 | definition of IsX | +| test.cpp:8:23:8:27 | Value | +| test.cpp:8:23:8:27 | declaration of Value | +| test.cpp:8:31:8:35 | 0 | +| test.cpp:8:31:8:35 | initializer for Value | +| test.cpp:11:10:11:11 | mention of EC | +| test.cpp:11:25:11:27 | mention of IsX | +| test.cpp:11:25:11:27 | mention of IsX | +| test.cpp:12:8:12:9 | DX | +| test.cpp:12:8:12:9 | definition of DX | +| test.cpp:13:17:13:20 | Type | +| test.cpp:13:17:13:20 | declaration of Type | +| test.cpp:16:10:16:11 | mention of EC | +| test.cpp:17:8:17:9 | IX | +| test.cpp:17:8:17:9 | definition of IX | +| test.cpp:18:21:18:25 | Value | +| test.cpp:18:21:18:25 | declaration of Value | +| test.cpp:18:29:18:32 | EC:: | +| test.cpp:18:29:18:33 | V | +| test.cpp:18:29:18:33 | initializer for Value | +| test.cpp:21:10:21:11 | mention of EC | +| test.cpp:22:6:22:8 | definition of run | +| test.cpp:22:6:22:8 | run | diff --git a/cpp/ql/test/library-tests/templates/CPP-204/element.ql b/cpp/ql/test/library-tests/templates/CPP-204/element.ql new file mode 100644 index 000000000000..38b30a227221 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/CPP-204/element.ql @@ -0,0 +1,7 @@ +import cpp + +from Element e +where not e instanceof BuiltInType + and not e instanceof Specifier + and not e instanceof Folder +select e diff --git a/cpp/ql/test/library-tests/templates/CPP-204/test.cpp b/cpp/ql/test/library-tests/templates/CPP-204/test.cpp new file mode 100644 index 000000000000..717a9a1a6a4f --- /dev/null +++ b/cpp/ql/test/library-tests/templates/CPP-204/test.cpp @@ -0,0 +1,26 @@ + +enum class EC : int { + V +}; + +template +struct IsX { + static const bool Value = false; +}; + +template::Value> +struct DX { + typedef int Type; +}; + +template +struct IX { + static const EC Value = EC::V; +}; + +template +void run() { + const EC y = IX::Value; + typedef typename DX::Type T; +} + diff --git a/cpp/ql/test/library-tests/templates/CPP-223/decls.expected b/cpp/ql/test/library-tests/templates/CPP-223/decls.expected new file mode 100644 index 000000000000..c3ab0388d525 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/CPP-223/decls.expected @@ -0,0 +1,18 @@ +| file://:0:0:0:0 | __va_list_tag | +| file://:0:0:0:0 | auto | +| file://:0:0:0:0 | d | +| file://:0:0:0:0 | fp_offset | +| file://:0:0:0:0 | gp_offset | +| file://:0:0:0:0 | operator= | +| file://:0:0:0:0 | operator= | +| file://:0:0:0:0 | overflow_arg_area | +| file://:0:0:0:0 | reg_save_area | +| test.cpp:3:24:3:24 | b | +| test.cpp:4:26:4:26 | c<> | +| test.cpp:4:26:4:26 | c<> | +| test.cpp:5:29:5:29 | e | +| test.cpp:6:26:6:26 | p#0 | +| test.cpp:6:29:6:31 | p#1 | +| test.cpp:7:20:7:26 | f | +| test.cpp:7:28:7:28 | p#0 | +| test.cpp:7:31:7:33 | p#1 | diff --git a/cpp/ql/test/library-tests/templates/CPP-223/decls.ql b/cpp/ql/test/library-tests/templates/CPP-223/decls.ql new file mode 100644 index 000000000000..6df6c775d591 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/CPP-223/decls.ql @@ -0,0 +1,5 @@ +import cpp + +from Declaration d +select d + diff --git a/cpp/ql/test/library-tests/templates/CPP-223/test.cpp b/cpp/ql/test/library-tests/templates/CPP-223/test.cpp new file mode 100644 index 000000000000..620571d2f117 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/CPP-223/test.cpp @@ -0,0 +1,8 @@ +// semmle-extractor-options: --edg --clang --edg --ms_extensions + +template class b { + template struct c; + typedef typename c::d e; + template void f(e, int); + template <> void f(e, int); +}; diff --git a/cpp/ql/test/library-tests/templates/ambiguous_cctor/ambiguous_cctor.cpp b/cpp/ql/test/library-tests/templates/ambiguous_cctor/ambiguous_cctor.cpp new file mode 100644 index 000000000000..3d47777bd9ba --- /dev/null +++ b/cpp/ql/test/library-tests/templates/ambiguous_cctor/ambiguous_cctor.cpp @@ -0,0 +1,19 @@ +struct copyable +{ + copyable(const copyable&); + template copyable(const T&); +}; + +template +struct templated +{ + copyable method() { + return T(); + } +}; + +int main() { + struct templated s; + // s.method(); TODO: This causes DBCheck failures + return 0; +} diff --git a/cpp/ql/test/library-tests/templates/ambiguous_cctor/ambiguous_cctor.expected b/cpp/ql/test/library-tests/templates/ambiguous_cctor/ambiguous_cctor.expected new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/cpp/ql/test/library-tests/templates/ambiguous_cctor/ambiguous_cctor.ql b/cpp/ql/test/library-tests/templates/ambiguous_cctor/ambiguous_cctor.ql new file mode 100644 index 000000000000..c7c3e657290b --- /dev/null +++ b/cpp/ql/test/library-tests/templates/ambiguous_cctor/ambiguous_cctor.ql @@ -0,0 +1,5 @@ +import cpp + +from FunctionCall fc, string returnParent +where if fc.getParent() instanceof ReturnStmt then returnParent = "ReturnStmt" else returnParent = "" +select fc, returnParent, fc.getEnclosingFunction(), count(fc.getTarget()) diff --git a/cpp/ql/test/library-tests/templates/bug/ODASA-6115.cpp b/cpp/ql/test/library-tests/templates/bug/ODASA-6115.cpp new file mode 100644 index 000000000000..55845099c68e --- /dev/null +++ b/cpp/ql/test/library-tests/templates/bug/ODASA-6115.cpp @@ -0,0 +1,15 @@ +template +struct __is_integral_helper +{}; + +template<> +struct __is_integral_helper +{}; + +template<> +struct __is_integral_helper +{}; + +int main() { + return 0; +} diff --git a/cpp/ql/test/library-tests/templates/bug/class.expected b/cpp/ql/test/library-tests/templates/bug/class.expected new file mode 100644 index 000000000000..d63a75390831 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/bug/class.expected @@ -0,0 +1,9 @@ +| ODASA-6115.cpp:2:8:2:27 | __is_integral_helper | +| ODASA-6115.cpp:6:8:6:37 | __is_integral_helper | +| ODASA-6115.cpp:10:8:10:43 | __is_integral_helper | +| file://:0:0:0:0 | __va_list_tag | +| test.cpp:2:8:2:8 | X | +| test.cpp:4:12:4:12 | Y | +| test.cpp:4:12:4:12 | Y | +| test.cpp:7:8:7:8 | Z | +| test.cpp:9:12:9:12 | S | diff --git a/cpp/ql/test/library-tests/templates/bug/class.ql b/cpp/ql/test/library-tests/templates/bug/class.ql new file mode 100644 index 000000000000..5307f90b4e67 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/bug/class.ql @@ -0,0 +1,5 @@ +import cpp + +from Class c +select c + diff --git a/cpp/ql/test/library-tests/templates/bug/test.cpp b/cpp/ql/test/library-tests/templates/bug/test.cpp new file mode 100644 index 000000000000..f30c1c6dc5f9 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/bug/test.cpp @@ -0,0 +1,11 @@ + +struct X { + template + struct Y { }; +}; + +struct Z : X { + template + struct S : Y { }; +}; + diff --git a/cpp/ql/test/library-tests/templates/decls/decls.cpp b/cpp/ql/test/library-tests/templates/decls/decls.cpp new file mode 100644 index 000000000000..99a29ac72b66 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/decls/decls.cpp @@ -0,0 +1,9 @@ + +template class C; + +template long f(C*); + +template<> long f(C* i); + +template long g(); + diff --git a/cpp/ql/test/library-tests/templates/decls/decls.expected b/cpp/ql/test/library-tests/templates/decls/decls.expected new file mode 100644 index 000000000000..3789b785252b --- /dev/null +++ b/cpp/ql/test/library-tests/templates/decls/decls.expected @@ -0,0 +1,19 @@ +| decls.cpp:2:19:2:20 | T1 | +| decls.cpp:2:29:2:29 | C | +| decls.cpp:2:29:2:29 | C | +| decls.cpp:2:29:2:29 | C | +| decls.cpp:4:19:4:20 | T2 | +| decls.cpp:4:28:4:28 | f | +| 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:25:8:25 | g | +| file://:0:0:0:0 | __va_list_tag | +| file://:0:0:0:0 | auto | +| file://:0:0:0:0 | fp_offset | +| file://:0:0:0:0 | gp_offset | +| file://:0:0:0:0 | operator= | +| file://:0:0:0:0 | operator= | +| file://:0:0:0:0 | overflow_arg_area | +| file://:0:0:0:0 | reg_save_area | diff --git a/cpp/ql/test/library-tests/templates/decls/decls.ql b/cpp/ql/test/library-tests/templates/decls/decls.ql new file mode 100644 index 000000000000..6df6c775d591 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/decls/decls.ql @@ -0,0 +1,5 @@ +import cpp + +from Declaration d +select d + diff --git a/cpp/ql/test/library-tests/templates/destructors/destructors.cpp b/cpp/ql/test/library-tests/templates/destructors/destructors.cpp new file mode 100644 index 000000000000..31ef9da56df6 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/destructors/destructors.cpp @@ -0,0 +1,10 @@ +template +struct Parameterized { + ~Parameterized() { throw "destructor"; } +}; + +struct Concrete { + Parameterized member; +}; + +int main() { Concrete c; } diff --git a/cpp/ql/test/library-tests/templates/destructors/destructors.expected b/cpp/ql/test/library-tests/templates/destructors/destructors.expected new file mode 100644 index 000000000000..d2a219e25b63 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/destructors/destructors.expected @@ -0,0 +1,3 @@ +| destructors.cpp:3:3:3:16 | ~Parameterized | destructors.cpp:2:8:2:20 | Parameterized | +| destructors.cpp:3:3:3:16 | ~Parameterized | destructors.cpp:2:8:2:20 | Parameterized | +| destructors.cpp:6:8:6:8 | ~Concrete | destructors.cpp:6:8:6:15 | Concrete | diff --git a/cpp/ql/test/library-tests/templates/destructors/destructors.ql b/cpp/ql/test/library-tests/templates/destructors/destructors.ql new file mode 100644 index 000000000000..7fd35ea49fbf --- /dev/null +++ b/cpp/ql/test/library-tests/templates/destructors/destructors.ql @@ -0,0 +1,4 @@ +import cpp + +from Destructor d +select d, d.getDeclaringType() diff --git a/cpp/ql/test/library-tests/templates/diagnostics/diagnostics.cpp b/cpp/ql/test/library-tests/templates/diagnostics/diagnostics.cpp new file mode 100644 index 000000000000..bd03389281d8 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/diagnostics/diagnostics.cpp @@ -0,0 +1,13 @@ +// semmle-extractor-options: --expect_errors +template +void report_type_via_error(T&& t) { + static_assert(sizeof(T) == 0, ""); +} + +static void foo() { + int i; + double d; + report_type_via_error(i); + report_type_via_error(d); +} + diff --git a/cpp/ql/test/library-tests/templates/diagnostics/diagnostics.expected b/cpp/ql/test/library-tests/templates/diagnostics/diagnostics.expected new file mode 100644 index 000000000000..ee7f42600665 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/diagnostics/diagnostics.expected @@ -0,0 +1,3 @@ +| diagnostics.cpp:4:3:4:3 | static assertion failed with '' | +| diagnostics.cpp:4:3:4:3 | static assertion failed with '' | +| file://:0:0:0:0 | There was an error during this compilation | diff --git a/cpp/ql/test/library-tests/templates/diagnostics/diagnostics.ql b/cpp/ql/test/library-tests/templates/diagnostics/diagnostics.ql new file mode 100644 index 000000000000..3fa864748e16 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/diagnostics/diagnostics.ql @@ -0,0 +1,4 @@ +import cpp + +from Diagnostic d +select d diff --git a/cpp/ql/test/library-tests/templates/diagnostics/parameter_names.cpp b/cpp/ql/test/library-tests/templates/diagnostics/parameter_names.cpp new file mode 100644 index 000000000000..5031753e035d --- /dev/null +++ b/cpp/ql/test/library-tests/templates/diagnostics/parameter_names.cpp @@ -0,0 +1,10 @@ + +// This should be error-free + +template struct S { + ~S(); + static void operator delete(void*, int); +}; +template S::~S() {} +template void S::operator delete(void*, int) {} + diff --git a/cpp/ql/test/library-tests/templates/extern/elements.expected b/cpp/ql/test/library-tests/templates/extern/elements.expected new file mode 100644 index 000000000000..038f05ed3093 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/extern/elements.expected @@ -0,0 +1,10 @@ +| extern.cpp:0:0:0:0 | extern.cpp | +| extern.cpp:1:20:1:20 | T | +| extern.cpp:1:20:1:20 | definition of T | +| extern.cpp:2:5:2:5 | declaration of f | +| extern.cpp:2:5:2:5 | f | +| extern.cpp:2:5:2:5 | f | +| extern.cpp:2:7:2:7 | declaration of 1st parameter | +| extern.cpp:2:7:2:7 | p#0 | +| extern.cpp:2:7:2:7 | p#0 | +| extern.cpp:4:1:4:58 | // Currently we don't have an element for this declaration | diff --git a/cpp/ql/test/library-tests/templates/extern/elements.ql b/cpp/ql/test/library-tests/templates/extern/elements.ql new file mode 100644 index 000000000000..6c5dd498c099 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/extern/elements.ql @@ -0,0 +1,7 @@ +import cpp + +from Element e +where exists(e.getLocation()) + and not e.getLocation() instanceof UnknownLocation + and not e instanceof Folder +select e diff --git a/cpp/ql/test/library-tests/templates/extern/extern.cpp b/cpp/ql/test/library-tests/templates/extern/extern.cpp new file mode 100644 index 000000000000..031df830039b --- /dev/null +++ b/cpp/ql/test/library-tests/templates/extern/extern.cpp @@ -0,0 +1,5 @@ +template +int f(T); + +// Currently we don't have an element for this declaration +extern template int f(int); diff --git a/cpp/ql/test/library-tests/templates/friends/decls.expected b/cpp/ql/test/library-tests/templates/friends/decls.expected new file mode 100644 index 000000000000..7190a37e0574 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/friends/decls.expected @@ -0,0 +1,33 @@ +| file://:0:0:0:0 | C's friend | +| file://:0:0:0:0 | C's friend | +| file://:0:0:0:0 | auto | +| file://:0:0:0:0 | f | +| file://:0:0:0:0 | fp_offset | +| file://:0:0:0:0 | gp_offset | +| file://:0:0:0:0 | operator= | +| file://:0:0:0:0 | operator= | +| file://:0:0:0:0 | overflow_arg_area | +| 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 | reg_save_area | +| friends.cpp:2:19:2:21 | TTT | +| friends.cpp:4:19:4:21 | TTT | +| friends.cpp:4:29:4:29 | f | +| friends.cpp:4:31:4:36 | p#0 | +| friends.cpp:4:31:4:36 | p#0 | +| friends.cpp:4:31:4:36 | p#0 | +| friends.cpp:6:19:6:21 | TTT | +| friends.cpp:7:9:7:9 | C | +| friends.cpp:7:9:7:9 | C | +| friends.cpp:7:9:7:9 | C | +| friends.cpp:7:9:7:9 | C | +| friends.cpp:7:9:7:9 | operator= | +| friends.cpp:7:9:7:9 | operator= | +| friends.cpp:7:9:7:9 | operator= | +| friends.cpp:7:9:7:9 | operator= | +| friends.cpp:9:17:9:19 | C's friend | +| friends.cpp:12:17:12:17 | f | +| friends.cpp:13:17:13:17 | f | diff --git a/cpp/ql/test/library-tests/templates/friends/decls.ql b/cpp/ql/test/library-tests/templates/friends/decls.ql new file mode 100644 index 000000000000..ee5f89681b1d --- /dev/null +++ b/cpp/ql/test/library-tests/templates/friends/decls.ql @@ -0,0 +1,6 @@ +import cpp + +from Declaration d +where d.getName() != "__va_list_tag" +select d + diff --git a/cpp/ql/test/library-tests/templates/friends/friends.cpp b/cpp/ql/test/library-tests/templates/friends/friends.cpp new file mode 100644 index 000000000000..33a8c389ba37 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/friends/friends.cpp @@ -0,0 +1,20 @@ + +template class C; + +template long f(C*); + +template + class C { + public: + friend long f<>(C*); + }; + +template<> long f(C* i); +template<> long f(C* i); + +extern template class C; +extern template long f(C*); + +extern template class C; +extern template long f(C*); + diff --git a/cpp/ql/test/library-tests/templates/functions/functions.expected b/cpp/ql/test/library-tests/templates/functions/functions.expected new file mode 100644 index 000000000000..15c847d45d25 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/functions/functions.expected @@ -0,0 +1,8 @@ +| template_functions.cpp:3:8:3:8 | X | template_functions.cpp:6:5:6:15 | operator T * | template_functions.cpp:6:28:6:32 | (T *)... | +| template_functions.cpp:3:8:3:8 | X | template_functions.cpp:6:5:6:15 | operator T * | template_functions.cpp:6:32:6:32 | 1 | +| template_functions.cpp:3:8:3:8 | X | template_functions.cpp:6:5:6:15 | operator int * | template_functions.cpp:6:28:6:32 | (int *)... | +| template_functions.cpp:3:8:3:8 | X | template_functions.cpp:6:5:6:15 | operator int * | template_functions.cpp:6:32:6:32 | 1 | +| template_functions.cpp:13:10:13:14 | S | template_functions.cpp:16:5:16:15 | operator Q * | template_functions.cpp:16:28:16:32 | (Q *)... | +| template_functions.cpp:13:10:13:14 | S | template_functions.cpp:16:5:16:15 | operator Q * | template_functions.cpp:16:32:16:32 | 2 | +| template_functions.cpp:13:10:13:14 | S | template_functions.cpp:16:5:16:15 | operator int * | template_functions.cpp:16:28:16:32 | (int *)... | +| template_functions.cpp:13:10:13:14 | S | template_functions.cpp:16:5:16:15 | operator int * | template_functions.cpp:16:32:16:32 | 2 | diff --git a/cpp/ql/test/library-tests/templates/functions/functions.ql b/cpp/ql/test/library-tests/templates/functions/functions.ql new file mode 100644 index 000000000000..df9e560fcbe2 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/functions/functions.ql @@ -0,0 +1,7 @@ +import cpp + +from Type t, Function f, Expr e +where t = f.getDeclaringType() + and f = e.getEnclosingFunction() +select t, f, e + diff --git a/cpp/ql/test/library-tests/templates/functions/template_functions.cpp b/cpp/ql/test/library-tests/templates/functions/template_functions.cpp new file mode 100644 index 000000000000..102009cad7cb --- /dev/null +++ b/cpp/ql/test/library-tests/templates/functions/template_functions.cpp @@ -0,0 +1,25 @@ + +template +struct X +{ + public: + operator T*() { return (T*)1; }; +}; + +template + struct S; + +template + struct S + { + public: + operator Q*() { return (Q*)2; } + }; + +void f(void) { + struct X x; + (int *)x; + struct S s; + (int *)s; +} + diff --git a/cpp/ql/test/library-tests/templates/incomplete_instantiations/a.cpp b/cpp/ql/test/library-tests/templates/incomplete_instantiations/a.cpp new file mode 100644 index 000000000000..e836b207a1bc --- /dev/null +++ b/cpp/ql/test/library-tests/templates/incomplete_instantiations/a.cpp @@ -0,0 +1,11 @@ +#include "h.h" + +C ci; +C *pc; + +static void f(void) { + D x; + E y; + x.fun(3); +} + diff --git a/cpp/ql/test/library-tests/templates/incomplete_instantiations/b.cpp b/cpp/ql/test/library-tests/templates/incomplete_instantiations/b.cpp new file mode 100644 index 000000000000..bb8a892f624c --- /dev/null +++ b/cpp/ql/test/library-tests/templates/incomplete_instantiations/b.cpp @@ -0,0 +1,11 @@ +#include "h.h" + +C *pi; +C cc; + +static void f(void) { + D x; + E y; + y.fun(3); +} + diff --git a/cpp/ql/test/library-tests/templates/incomplete_instantiations/h.h b/cpp/ql/test/library-tests/templates/incomplete_instantiations/h.h new file mode 100644 index 000000000000..199717d74cf3 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/incomplete_instantiations/h.h @@ -0,0 +1,18 @@ + +template +class C { + void fun(T) { return; } +}; + +template +class D { + public: + void fun(T) { return; } +}; + +template +class E { + public: + void fun(T) { return; } +}; + diff --git a/cpp/ql/test/library-tests/templates/incomplete_instantiations/test.expected b/cpp/ql/test/library-tests/templates/incomplete_instantiations/test.expected new file mode 100644 index 000000000000..4743633a2c4e --- /dev/null +++ b/cpp/ql/test/library-tests/templates/incomplete_instantiations/test.expected @@ -0,0 +1,15 @@ +| h.h:3:7:3:7 | C | h.h:4:10:4:12 | fun | 0 | +| h.h:3:7:3:7 | C | h.h:3:7:3:7 | operator= | 0 | +| h.h:3:7:3:7 | C | h.h:3:7:3:7 | operator= | 0 | +| h.h:3:7:3:7 | C | h.h:4:10:4:12 | fun | 0 | +| h.h:3:7:3:7 | C | h.h:3:7:3:7 | operator= | 0 | +| h.h:3:7:3:7 | C | h.h:3:7:3:7 | operator= | 0 | +| h.h:3:7:3:7 | C | h.h:4:10:4:12 | fun | 0 | +| h.h:8:7:8:7 | D | h.h:10:10:10:12 | fun | 2 | +| h.h:8:7:8:7 | D | h.h:8:7:8:7 | operator= | 0 | +| h.h:8:7:8:7 | D | h.h:8:7:8:7 | operator= | 0 | +| h.h:8:7:8:7 | D | h.h:10:10:10:12 | fun | 2 | +| h.h:14:7:14:7 | E | h.h:16:10:16:12 | fun | 2 | +| h.h:14:7:14:7 | E | h.h:14:7:14:7 | operator= | 0 | +| h.h:14:7:14:7 | E | h.h:14:7:14:7 | operator= | 0 | +| h.h:14:7:14:7 | E | h.h:16:10:16:12 | fun | 2 | diff --git a/cpp/ql/test/library-tests/templates/incomplete_instantiations/test.ql b/cpp/ql/test/library-tests/templates/incomplete_instantiations/test.ql new file mode 100644 index 000000000000..b5d70f52fa29 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/incomplete_instantiations/test.ql @@ -0,0 +1,8 @@ +import cpp + +from Class c, Function f +where c = f.getDeclaringType() + and exists(f.getLocation()) +select c, f, count(Stmt s | s.getEnclosingFunction() = f) + + diff --git a/cpp/ql/test/library-tests/templates/instantiation_directive/functions.expected b/cpp/ql/test/library-tests/templates/instantiation_directive/functions.expected new file mode 100644 index 000000000000..efee8321dbd5 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/instantiation_directive/functions.expected @@ -0,0 +1,3 @@ +| test.cpp:2:6:2:8 | foo | file://:0:0:0:0 | float | +| test.cpp:2:6:2:8 | foo | file://:0:0:0:0 | int | +| test.cpp:2:6:2:8 | foo | test.cpp:1:19:1:19 | T | diff --git a/cpp/ql/test/library-tests/templates/instantiation_directive/functions.ql b/cpp/ql/test/library-tests/templates/instantiation_directive/functions.ql new file mode 100644 index 000000000000..bc3ccc611f90 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/instantiation_directive/functions.ql @@ -0,0 +1,5 @@ +import cpp + +from Function f +select f, f.getAParameter().getType() + diff --git a/cpp/ql/test/library-tests/templates/instantiation_directive/test.cpp b/cpp/ql/test/library-tests/templates/instantiation_directive/test.cpp new file mode 100644 index 000000000000..5e3dc8b3c8ae --- /dev/null +++ b/cpp/ql/test/library-tests/templates/instantiation_directive/test.cpp @@ -0,0 +1,6 @@ +template +void foo(T t) { }; + +template void foo(int i); +template void foo(float f); + diff --git a/cpp/ql/test/library-tests/templates/instantiations_functions/elements.expected b/cpp/ql/test/library-tests/templates/instantiations_functions/elements.expected new file mode 100644 index 000000000000..a368e9100f0a --- /dev/null +++ b/cpp/ql/test/library-tests/templates/instantiations_functions/elements.expected @@ -0,0 +1,366 @@ +| file://:0:0:0:0 | | +| file://:0:0:0:0 | (composite *)... | +| file://:0:0:0:0 | (composite *)... | +| file://:0:0:0:0 | (global namespace) | +| file://:0:0:0:0 | ..()(..) | +| file://:0:0:0:0 | ..()(..) | +| file://:0:0:0:0 | ..()(..) | +| file://:0:0:0:0 | ..(*)(..) | +| file://:0:0:0:0 | ..(*)(..) | +| file://:0:0:0:0 | ..(*)(..) | +| file://:0:0:0:0 | A & | +| file://:0:0:0:0 | _Complex __float128 | +| file://:0:0:0:0 | _Complex double | +| file://:0:0:0:0 | _Complex float | +| file://:0:0:0:0 | _Complex long double | +| file://:0:0:0:0 | _Decimal32 | +| file://:0:0:0:0 | _Decimal64 | +| file://:0:0:0:0 | _Decimal128 | +| file://:0:0:0:0 | _Float32 | +| file://:0:0:0:0 | _Float32x | +| file://:0:0:0:0 | _Float64 | +| file://:0:0:0:0 | _Float64x | +| file://:0:0:0:0 | _Float128 | +| file://:0:0:0:0 | _Float128x | +| file://:0:0:0:0 | _Imaginary double | +| file://:0:0:0:0 | _Imaginary float | +| file://:0:0:0:0 | _Imaginary long double | +| file://:0:0:0:0 | __block | +| file://:0:0:0:0 | __float128 | +| file://:0:0:0:0 | __int128 | +| file://:0:0:0:0 | __interface | +| file://:0:0:0:0 | __ptr32 | +| file://:0:0:0:0 | __ptr64 | +| file://:0:0:0:0 | __sptr | +| file://:0:0:0:0 | __uptr | +| file://:0:0:0:0 | __va_list_tag | +| file://:0:0:0:0 | __va_list_tag & | +| file://:0:0:0:0 | abstract | +| file://:0:0:0:0 | action * | +| file://:0:0:0:0 | action *const | +| file://:0:0:0:0 | action>> & | +| file://:0:0:0:0 | action>> && | +| file://:0:0:0:0 | action>> * | +| file://:0:0:0:0 | action>> *const | +| file://:0:0:0:0 | actor1 * | +| file://:0:0:0:0 | actor1 *const | +| file://:0:0:0:0 | actor1> & | +| file://:0:0:0:0 | actor1> && | +| file://:0:0:0:0 | actor1> * | +| file://:0:0:0:0 | actor1> *const | +| file://:0:0:0:0 | atomic | +| file://:0:0:0:0 | auto | +| file://:0:0:0:0 | auto | +| file://:0:0:0:0 | bool | +| file://:0:0:0:0 | char | +| 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 | const | +| file://:0:0:0:0 | const action>> | +| file://:0:0:0:0 | const action>> & | +| file://:0:0:0:0 | const actor1> | +| file://:0:0:0:0 | const actor1> & | +| file://:0:0:0:0 | const composite | +| file://:0:0:0:0 | const composite & | +| file://:0:0:0:0 | const grammar_helper_base | +| file://:0:0:0:0 | const grammar_helper_base & | +| file://:0:0:0:0 | const rule | +| file://:0:0:0:0 | const rule & | +| file://:0:0:0:0 | declaration of 1st parameter | +| file://:0:0:0:0 | declaration of 1st parameter | +| file://:0:0:0:0 | declaration of 1st parameter | +| file://:0:0:0:0 | declaration of 1st parameter | +| file://:0:0:0:0 | declaration of 1st parameter | +| file://:0:0:0:0 | declaration of 1st parameter | +| file://:0:0:0:0 | declaration of 1st parameter | +| file://:0:0:0:0 | declaration of 1st parameter | +| file://:0:0:0:0 | declaration of 1st parameter | +| file://:0:0:0:0 | declaration of 1st parameter | +| file://:0:0:0:0 | declaration of 1st parameter | +| file://:0:0:0:0 | declaration of 1st parameter | +| file://:0:0:0:0 | declaration of 1st parameter | +| file://:0:0:0:0 | declaration of 1st parameter | +| file://:0:0:0:0 | declaration of 1st parameter | +| file://:0:0:0:0 | declaration of 1st parameter | +| file://:0:0:0:0 | decltype(nullptr) | +| file://:0:0:0:0 | definition of __va_list_tag | +| file://:0:0:0:0 | definition of fp_offset | +| file://:0:0:0:0 | definition of gp_offset | +| file://:0:0:0:0 | definition of overflow_arg_area | +| file://:0:0:0:0 | definition of reg_save_area | +| file://:0:0:0:0 | dllexport | +| file://:0:0:0:0 | dllimport | +| file://:0:0:0:0 | double | +| file://:0:0:0:0 | error | +| file://:0:0:0:0 | explicit | +| file://:0:0:0:0 | extern | +| file://:0:0:0:0 | far | +| file://:0:0:0:0 | float | +| file://:0:0:0:0 | forceinline | +| file://:0:0:0:0 | fp_offset | +| file://:0:0:0:0 | gp_offset | +| file://:0:0:0:0 | grammar_helper_base & | +| file://:0:0:0:0 | grammar_helper_base && | +| file://:0:0:0:0 | implicit_int | +| file://:0:0:0:0 | inline | +| file://:0:0:0:0 | int | +| file://:0:0:0:0 | int & | +| file://:0:0:0:0 | int * | +| file://:0:0:0:0 | long | +| file://:0:0:0:0 | long double | +| file://:0:0:0:0 | long long | +| file://:0:0:0:0 | microsoft_inline | +| file://:0:0:0:0 | naked | +| file://:0:0:0:0 | near | +| file://:0:0:0:0 | noalias | +| file://:0:0:0:0 | noinline | +| file://:0:0:0:0 | noreturn | +| file://:0:0:0:0 | nothrow | +| file://:0:0:0:0 | novtable | +| file://:0:0:0:0 | operator= | +| file://:0:0:0:0 | operator= | +| file://:0:0:0:0 | optional | +| file://:0:0:0:0 | overflow_arg_area | +| file://:0:0:0:0 | override | +| 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 | +| file://:0:0:0:0 | p#0 | +| file://:0:0:0:0 | p#0 | +| file://:0:0:0:0 | private | +| file://:0:0:0:0 | protected | +| file://:0:0:0:0 | public | +| file://:0:0:0:0 | pure | +| file://:0:0:0:0 | reg_save_area | +| file://:0:0:0:0 | register | +| file://:0:0:0:0 | restrict | +| 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 | +| file://:0:0:0:0 | signed __int128 | +| file://:0:0:0:0 | signed char | +| file://:0:0:0:0 | signed int | +| file://:0:0:0:0 | signed long | +| file://:0:0:0:0 | signed long long | +| file://:0:0:0:0 | signed short | +| file://:0:0:0:0 | static | +| file://:0:0:0:0 | this | +| file://:0:0:0:0 | this | +| file://:0:0:0:0 | this | +| file://:0:0:0:0 | this | +| file://:0:0:0:0 | thread | +| file://:0:0:0:0 | unaligned | +| file://:0:0:0:0 | unknown | +| file://:0:0:0:0 | unsigned __int128 | +| file://:0:0:0:0 | unsigned char | +| file://:0:0:0:0 | unsigned int | +| file://:0:0:0:0 | unsigned long | +| file://:0:0:0:0 | unsigned long long | +| file://:0:0:0:0 | unsigned short | +| file://:0:0:0:0 | varargs | +| file://:0:0:0:0 | virtual | +| file://:0:0:0:0 | void | +| file://:0:0:0:0 | void * | +| file://:0:0:0:0 | volatile | +| file://:0:0:0:0 | wchar_t | +| header.h:0:0:0:0 | header.h | +| header.h:2:20:2:24 | BaseT | +| header.h:2:20:2:24 | definition of BaseT | +| header.h:3:8:3:8 | actor1 | +| header.h:3:8:3:8 | actor1 | +| header.h:3:8:3:8 | declaration of actor1 | +| header.h:3:8:3:8 | declaration of actor1 | +| header.h:3:8:3:8 | declaration of operator= | +| header.h:3:8:3:8 | declaration of operator= | +| header.h:3:8:3:8 | operator= | +| header.h:3:8:3:8 | operator= | +| header.h:3:8:3:13 | actor1 | +| header.h:3:8:3:13 | actor1> | +| header.h:3:8:3:13 | definition of actor1 | +| header.h:3:17:3:37 | derivation | +| header.h:3:17:3:37 | derivation | +| header.h:4:5:4:10 | actor1 | +| header.h:4:5:4:10 | actor1 | +| header.h:4:5:4:10 | declaration of actor1 | +| header.h:4:5:4:10 | declaration of actor1 | +| header.h:6:24:6:24 | A | +| header.h:6:24:6:24 | definition of A | +| header.h:8:5:8:8 | declaration of funx | +| header.h:8:5:8:8 | definition of funx | +| header.h:8:5:8:8 | funx | +| header.h:8:5:8:8 | funx | +| header.h:8:5:8:8 | funx | +| header.h:8:13:8:14 | a_ | +| header.h:8:13:8:14 | a_ | +| header.h:8:13:8:14 | a_ | +| header.h:8:13:8:14 | declaration of a_ | +| header.h:8:13:8:14 | definition of a_ | +| header.h:8:17:11:5 | { ... } | +| header.h:8:17:11:5 | { ... } | +| header.h:9:9:9:14 | declaration | +| header.h:9:9:9:14 | declaration | +| header.h:9:13:9:13 | definition of i | +| header.h:9:13:9:13 | definition of i | +| header.h:9:13:9:13 | i | +| header.h:9:13:9:13 | i | +| header.h:10:9:10:24 | composite:: | +| header.h:10:9:10:24 | composite:: | +| header.h:10:9:10:28 | call to eval | +| header.h:10:9:10:28 | call to eval | +| header.h:10:9:10:32 | ExprStmt | +| header.h:10:9:10:32 | ExprStmt | +| header.h:10:30:10:30 | i | +| header.h:10:30:10:30 | i | +| header.h:11:5:11:5 | return ... | +| header.h:11:5:11:5 | return ... | +| test.cpp:0:0:0:0 | test.cpp | +| test.cpp:2:8:2:8 | declaration of operator= | +| test.cpp:2:8:2:8 | declaration of operator= | +| test.cpp:2:8:2:8 | operator= | +| test.cpp:2:8:2:8 | operator= | +| test.cpp:2:8:2:26 | definition of grammar_helper_base | +| test.cpp:2:8:2:26 | grammar_helper_base | +| test.cpp:3:9:3:16 | declaration of undefine | +| test.cpp:3:9:3:16 | undefine | +| test.cpp:3:18:3:20 | declaration of 1st parameter | +| test.cpp:3:18:3:20 | p#0 | +| test.cpp:6:20:6:20 | B | +| test.cpp:6:20:6:20 | definition of B | +| test.cpp:7:8:7:8 | declaration of operator= | +| test.cpp:7:8:7:8 | declaration of operator= | +| test.cpp:7:8:7:8 | operator= | +| test.cpp:7:8:7:8 | operator= | +| test.cpp:7:8:7:16 | composite | +| test.cpp:7:8:7:16 | composite | +| test.cpp:7:8:7:16 | definition of composite | +| test.cpp:8:24:8:29 | TupleT | +| test.cpp:8:24:8:29 | definition of TupleT | +| test.cpp:9:10:9:13 | declaration of eval | +| test.cpp:9:10:9:13 | definition of eval | +| test.cpp:9:10:9:13 | eval | +| test.cpp:9:10:9:13 | eval | +| test.cpp:9:10:9:13 | eval | +| test.cpp:9:22:9:25 | args | +| test.cpp:9:22:9:25 | args | +| test.cpp:9:22:9:25 | args | +| test.cpp:9:22:9:25 | declaration of args | +| test.cpp:9:22:9:25 | definition of args | +| test.cpp:9:28:9:30 | { ... } | +| test.cpp:9:28:9:30 | { ... } | +| test.cpp:9:30:9:30 | return ... | +| test.cpp:9:30:9:30 | return ... | +| test.cpp:12:1:12:19 | #include "header.h" | +| test.cpp:14:20:14:26 | ActionT | +| test.cpp:14:20:14:26 | definition of ActionT | +| test.cpp:15:7:15:7 | Unknown literal | +| test.cpp:15:7:15:7 | action | +| test.cpp:15:7:15:7 | action | +| test.cpp:15:7:15:7 | action | +| test.cpp:15:7:15:7 | call to actor1 | +| test.cpp:15:7:15:7 | constructor init of field actor | +| test.cpp:15:7:15:7 | constructor init of field actor | +| test.cpp:15:7:15:7 | declaration of action | +| test.cpp:15:7:15:7 | declaration of operator= | +| test.cpp:15:7:15:7 | declaration of operator= | +| test.cpp:15:7:15:7 | definition of action | +| test.cpp:15:7:15:7 | definition of action | +| test.cpp:15:7:15:7 | operator= | +| test.cpp:15:7:15:7 | operator= | +| test.cpp:15:7:15:7 | return ... | +| test.cpp:15:7:15:7 | return ... | +| test.cpp:15:7:15:7 | { ... } | +| test.cpp:15:7:15:7 | { ... } | +| test.cpp:15:7:15:12 | action | +| test.cpp:15:7:15:12 | action>> | +| test.cpp:15:7:15:12 | definition of action | +| test.cpp:17:10:17:15 | definition of eparse | +| test.cpp:17:10:17:15 | eparse | +| test.cpp:17:10:17:15 | eparse | +| test.cpp:17:19:20:5 | { ... } | +| test.cpp:17:19:20:5 | { ... } | +| test.cpp:18:9:18:17 | declaration | +| test.cpp:18:9:18:17 | declaration | +| test.cpp:18:13:18:16 | definition of valx | +| test.cpp:18:13:18:16 | definition of valx | +| test.cpp:18:13:18:16 | valx | +| test.cpp:18:13:18:16 | valx | +| test.cpp:19:9:19:13 | actor | +| test.cpp:19:9:19:13 | actor | +| test.cpp:19:9:19:24 | call to expression | +| test.cpp:19:9:19:25 | ExprStmt | +| test.cpp:19:9:19:25 | ExprStmt | +| test.cpp:19:15:19:18 | Unknown literal | +| test.cpp:19:15:19:18 | call to funx | +| test.cpp:19:20:19:23 | (reference to) | +| test.cpp:19:20:19:23 | valx | +| test.cpp:19:20:19:23 | valx | +| test.cpp:20:5:20:5 | return ... | +| test.cpp:20:5:20:5 | return ... | +| test.cpp:23:13:23:17 | actor | +| test.cpp:23:13:23:17 | actor | +| test.cpp:23:13:23:17 | definition of actor | +| test.cpp:23:13:23:17 | definition of actor | +| test.cpp:26:7:26:7 | declaration of operator= | +| test.cpp:26:7:26:7 | declaration of operator= | +| test.cpp:26:7:26:7 | declaration of rule | +| test.cpp:26:7:26:7 | declaration of rule | +| test.cpp:26:7:26:7 | operator= | +| test.cpp:26:7:26:7 | operator= | +| test.cpp:26:7:26:7 | rule | +| test.cpp:26:7:26:7 | rule | +| test.cpp:26:7:26:10 | definition of rule | +| test.cpp:26:7:26:10 | rule | +| test.cpp:28:28:28:34 | ParserT | +| test.cpp:28:28:28:34 | definition of ParserT | +| test.cpp:29:9:29:12 | definition of rule | +| test.cpp:29:9:29:12 | rule | +| test.cpp:29:9:29:12 | rule | +| test.cpp:29:22:29:22 | definition of p | +| test.cpp:29:22:29:22 | p | +| test.cpp:29:22:29:22 | p | +| test.cpp:29:25:31:9 | { ... } | +| test.cpp:29:25:31:9 | { ... } | +| test.cpp:30:13:30:13 | p | +| test.cpp:30:13:30:13 | p | +| test.cpp:30:13:30:22 | call to expression | +| test.cpp:30:13:30:23 | ExprStmt | +| test.cpp:30:13:30:23 | ExprStmt | +| test.cpp:30:15:30:20 | Unknown literal | +| test.cpp:30:15:30:20 | call to eparse | +| test.cpp:31:9:31:9 | return ... | +| test.cpp:31:9:31:9 | return ... | +| test.cpp:34:6:34:11 | define | +| test.cpp:34:6:34:11 | definition of define | +| test.cpp:34:15:37:1 | { ... } | +| test.cpp:35:5:35:10 | mention of action>> | +| test.cpp:35:5:35:37 | declaration | +| test.cpp:35:12:35:17 | mention of actor1> | +| test.cpp:35:19:35:27 | mention of composite | +| test.cpp:35:36:35:36 | call to action | +| test.cpp:35:36:35:36 | definition of z | +| test.cpp:35:36:35:36 | initializer for z | +| test.cpp:35:36:35:36 | z | +| test.cpp:36:5:36:8 | mention of rule | +| test.cpp:36:5:36:27 | declaration | +| test.cpp:36:10:36:22 | definition of pp_expression | +| test.cpp:36:10:36:22 | pp_expression | +| test.cpp:36:25:36:26 | call to rule | +| test.cpp:36:25:36:26 | initializer for pp_expression | +| test.cpp:36:26:36:26 | z | +| test.cpp:37:1:37:1 | return ... | diff --git a/cpp/ql/test/library-tests/templates/instantiations_functions/elements.ql b/cpp/ql/test/library-tests/templates/instantiations_functions/elements.ql new file mode 100644 index 000000000000..8d2ce8f5e94f --- /dev/null +++ b/cpp/ql/test/library-tests/templates/instantiations_functions/elements.ql @@ -0,0 +1,5 @@ +import cpp + +from Element e +where not e instanceof Folder +select e diff --git a/cpp/ql/test/library-tests/templates/instantiations_functions/header.h b/cpp/ql/test/library-tests/templates/instantiations_functions/header.h new file mode 100644 index 000000000000..9bf726e784c0 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/instantiations_functions/header.h @@ -0,0 +1,13 @@ + +template +struct actor1 : public composite { + actor1(); + + template + inline void + funx(A& a_) { + int i; + composite::eval(i); + } +}; + diff --git a/cpp/ql/test/library-tests/templates/instantiations_functions/test.cpp b/cpp/ql/test/library-tests/templates/instantiations_functions/test.cpp new file mode 100644 index 000000000000..09ca255b4e17 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/instantiations_functions/test.cpp @@ -0,0 +1,38 @@ + +struct grammar_helper_base { + int undefine(int *); +}; + +template +struct composite { + template + void eval(TupleT args) { } +}; + +#include "header.h" + +template +class action { +public: + void eparse() { + int valx; + actor.funx(valx); + } + +private: + ActionT actor; +}; + +class rule { + public: + template + rule(ParserT p) { + p.eparse(); + } +}; + +void define() { + action>> z; + rule pp_expression = z; +} + diff --git a/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/instantiations.expected b/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/instantiations.expected new file mode 100644 index 000000000000..778353664010 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/instantiations.expected @@ -0,0 +1,13 @@ +| isfromtemplateinstantiation.cpp:12:24:12:46 | inner_template_function | FunctionTemplateInstantiation | file://:0:0:0:0 | long | +| isfromtemplateinstantiation.cpp:12:24:12:46 | inner_template_function | FunctionTemplateInstantiation | file://:0:0:0:0 | short | +| isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function | FunctionTemplateInstantiation | file://:0:0:0:0 | long | +| isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function | FunctionTemplateInstantiation | file://:0:0:0:0 | short | +| isfromtemplateinstantiation.cpp:38:26:38:42 | a_template_method | FunctionTemplateInstantiation | file://:0:0:0:0 | short | +| isfromtemplateinstantiation.cpp:44:26:44:39 | template_class | ClassTemplateInstantiation | file://:0:0:0:0 | char | +| isfromtemplateinstantiation.cpp:53:26:53:42 | b_template_method | FunctionTemplateInstantiation | file://:0:0:0:0 | long | +| isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | ClassTemplateInstantiation | file://:0:0:0:0 | int | +| isfromtemplateinstantiation.cpp:128:7:128:30 | AnotherTemplateClass | ClassTemplateInstantiation | file://:0:0:0:0 | long * | +| isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | ClassTemplateInstantiation | file://:0:0:0:0 | int | +| isfromtemplateinstantiation.cpp:135:31:135:35 | Inner | ClassTemplateInstantiation | file://:0:0:0:0 | long | +| load.cpp:13:7:13:27 | basic_text_iprimitive | ClassTemplateInstantiation | load.cpp:3:7:3:24 | std_istream_mockup | +| load.cpp:22:10:22:13 | load | FunctionTemplateInstantiation | file://:0:0:0:0 | short | diff --git a/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/instantiations.ql b/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/instantiations.ql new file mode 100644 index 000000000000..c2e851c051f6 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/instantiations.ql @@ -0,0 +1,12 @@ +import cpp + +from Element e, string s, Type arg +where + ( + s = "ClassTemplateInstantiation" and + arg = e.(ClassTemplateInstantiation).getATemplateArgument() + ) or ( + s = "FunctionTemplateInstantiation" and + arg = e.(FunctionTemplateInstantiation).getATemplateArgument() + ) +select e, s, arg diff --git a/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/isfromtemplateinstantiation.cpp b/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/isfromtemplateinstantiation.cpp new file mode 100644 index 000000000000..5229f913078a --- /dev/null +++ b/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/isfromtemplateinstantiation.cpp @@ -0,0 +1,150 @@ +#define A_MACRO i++ +// semmle-extractor-options: -std=c++14 +// non-template function +void normal_function() +{ + int i = 0; + + i++; +} + +// nested template function +template void inner_template_function() +{ + T t; + + t++; +} + +template void outer_template_function() +{ + int i = 0; + + A_MACRO; + + inner_template_function(); +} + +// non-template class +class normal_class +{ + int i; + +public: + void a_method() + { + } + + template void a_template_method() + { + } +}; + +// template class +template class template_class +{ + T t; + +public: + void b_method() + { + } + + template void b_template_method(U u) + { + } +}; + +// main +int main() +{ + normal_function(); + outer_template_function(); + outer_template_function(); + + { + normal_class o1; + template_class o2; + + o1.a_method(); + o1.a_template_method(); + o2.b_method(); + o2.b_template_method('a'); + } +} + +// another template class +template class AnotherTemplateClass +{ +public: + typedef T _t; + + struct MyClassStruct + { + _t value; + }; + MyClassStruct l; + + enum MyClassEnum + { + MyClassEnumConst + }; + + void myMethod1(MyClassEnum mce1 = MyClassEnumConst) {} + void myMethod2(MyClassEnum mce2 = MyClassEnumConst); +}; + +template void AnotherTemplateClass :: myMethod2(MyClassEnum mce2) +{ +} + +AnotherTemplateClass atc_int; + +void anotherMain() +{ + atc_int.myMethod1(); + atc_int.myMethod2(); +} + +template +T var_template; + +int normal_var; + +void function_using_vars() +{ + var_template = 1; + normal_var = 2; +} + +// full specialization: _not_ an uninstantiated template. +template<> +class AnotherTemplateClass { + int f() { return 0; } +}; + +// partial specialization: _is_ and uninstantiated template. +template +class AnotherTemplateClass { + int f() { return 1; } +}; + +template class AnotherTemplateClass; + +template struct Outer { + template struct Inner { + U x; + T y; + }; +}; + +template class Outer::Inner; + + +template class incomplete_never_instantiated; + +template class never_instantiated { T x; }; + +template T decl_never_instantiated(T x); + +template T def_never_instantiated(T x) { return x; } diff --git a/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/isfromtemplateinstantiation.expected b/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/isfromtemplateinstantiation.expected new file mode 100644 index 000000000000..dc73e749a4cd --- /dev/null +++ b/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/isfromtemplateinstantiation.expected @@ -0,0 +1,209 @@ +| isfromtemplateinstantiation.cpp:13:1:17:1 | { ... } | isfromtemplateinstantiation.cpp:12:24:12:46 | inner_template_function() | +| isfromtemplateinstantiation.cpp:13:1:17:1 | { ... } | isfromtemplateinstantiation.cpp:12:24:12:46 | inner_template_function() | +| isfromtemplateinstantiation.cpp:14:2:14:5 | declaration | isfromtemplateinstantiation.cpp:12:24:12:46 | inner_template_function() | +| isfromtemplateinstantiation.cpp:14:2:14:5 | declaration | isfromtemplateinstantiation.cpp:12:24:12:46 | inner_template_function() | +| isfromtemplateinstantiation.cpp:14:4:14:4 | definition of t | isfromtemplateinstantiation.cpp:12:24:12:46 | inner_template_function() | +| isfromtemplateinstantiation.cpp:14:4:14:4 | definition of t | isfromtemplateinstantiation.cpp:12:24:12:46 | inner_template_function() | +| isfromtemplateinstantiation.cpp:14:4:14:4 | t | isfromtemplateinstantiation.cpp:12:24:12:46 | inner_template_function() | +| isfromtemplateinstantiation.cpp:14:4:14:4 | t | isfromtemplateinstantiation.cpp:12:24:12:46 | inner_template_function() | +| isfromtemplateinstantiation.cpp:16:2:16:2 | t | isfromtemplateinstantiation.cpp:12:24:12:46 | inner_template_function() | +| isfromtemplateinstantiation.cpp:16:2:16:2 | t | isfromtemplateinstantiation.cpp:12:24:12:46 | inner_template_function() | +| isfromtemplateinstantiation.cpp:16:2:16:4 | ... ++ | isfromtemplateinstantiation.cpp:12:24:12:46 | inner_template_function() | +| isfromtemplateinstantiation.cpp:16:2:16:4 | ... ++ | isfromtemplateinstantiation.cpp:12:24:12:46 | inner_template_function() | +| isfromtemplateinstantiation.cpp:16:2:16:5 | ExprStmt | isfromtemplateinstantiation.cpp:12:24:12:46 | inner_template_function() | +| isfromtemplateinstantiation.cpp:16:2:16:5 | ExprStmt | isfromtemplateinstantiation.cpp:12:24:12:46 | inner_template_function() | +| isfromtemplateinstantiation.cpp:17:1:17:1 | return ... | isfromtemplateinstantiation.cpp:12:24:12:46 | inner_template_function() | +| isfromtemplateinstantiation.cpp:17:1:17:1 | return ... | isfromtemplateinstantiation.cpp:12:24:12:46 | inner_template_function() | +| isfromtemplateinstantiation.cpp:20:1:26:1 | { ... } | isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function() | +| isfromtemplateinstantiation.cpp:20:1:26:1 | { ... } | isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function() | +| isfromtemplateinstantiation.cpp:21:2:21:11 | declaration | isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function() | +| isfromtemplateinstantiation.cpp:21:2:21:11 | declaration | isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function() | +| isfromtemplateinstantiation.cpp:21:6:21:6 | definition of i | isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function() | +| isfromtemplateinstantiation.cpp:21:6:21:6 | definition of i | isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function() | +| isfromtemplateinstantiation.cpp:21:6:21:6 | i | isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function() | +| isfromtemplateinstantiation.cpp:21:6:21:6 | i | isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function() | +| isfromtemplateinstantiation.cpp:21:9:21:10 | 0 | isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function() | +| isfromtemplateinstantiation.cpp:21:9:21:10 | 0 | isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function() | +| isfromtemplateinstantiation.cpp:21:9:21:10 | initializer for i | isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function() | +| isfromtemplateinstantiation.cpp:21:9:21:10 | initializer for i | isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function() | +| isfromtemplateinstantiation.cpp:23:2:23:8 | ... ++ | isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function() | +| isfromtemplateinstantiation.cpp:23:2:23:8 | ... ++ | isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function() | +| isfromtemplateinstantiation.cpp:23:2:23:8 | i | isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function() | +| isfromtemplateinstantiation.cpp:23:2:23:8 | i | isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function() | +| isfromtemplateinstantiation.cpp:23:2:23:9 | ExprStmt | isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function() | +| isfromtemplateinstantiation.cpp:23:2:23:9 | ExprStmt | isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function() | +| isfromtemplateinstantiation.cpp:25:2:25:27 | call to inner_template_function | isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function() | +| isfromtemplateinstantiation.cpp:25:2:25:27 | call to inner_template_function | isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function() | +| isfromtemplateinstantiation.cpp:25:2:25:30 | ExprStmt | isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function() | +| isfromtemplateinstantiation.cpp:25:2:25:30 | ExprStmt | isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function() | +| isfromtemplateinstantiation.cpp:26:1:26:1 | return ... | isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function() | +| isfromtemplateinstantiation.cpp:26:1:26:1 | return ... | isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function() | +| isfromtemplateinstantiation.cpp:39:2:40:2 | { ... } | isfromtemplateinstantiation.cpp:38:26:38:42 | normal_class::a_template_method() | +| isfromtemplateinstantiation.cpp:40:2:40:2 | return ... | isfromtemplateinstantiation.cpp:38:26:38:42 | 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 | 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: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:14 | template_class::b_method() | isfromtemplateinstantiation.cpp:44:26:44:39 | template_class | +| isfromtemplateinstantiation.cpp:50:2:51:2 | { ... } | isfromtemplateinstantiation.cpp:44:26:44:39 | template_class | +| isfromtemplateinstantiation.cpp:50:2:51:2 | { ... } | isfromtemplateinstantiation.cpp:49:7:49:14 | template_class::b_method() | +| isfromtemplateinstantiation.cpp:51:2:51:2 | return ... | isfromtemplateinstantiation.cpp:44:26:44:39 | template_class | +| isfromtemplateinstantiation.cpp:51:2:51:2 | return ... | isfromtemplateinstantiation.cpp:49:7:49:14 | template_class::b_method() | +| isfromtemplateinstantiation.cpp:53:26:53:42 | declaration of b_template_method | isfromtemplateinstantiation.cpp:44:26:44:39 | template_class | +| isfromtemplateinstantiation.cpp:53:26:53:42 | template_class::b_template_method(U) | isfromtemplateinstantiation.cpp:44:26:44:39 | template_class | +| isfromtemplateinstantiation.cpp:53:26:53:42 | template_class::b_template_method(long) | isfromtemplateinstantiation.cpp:44:26:44:39 | template_class | +| isfromtemplateinstantiation.cpp:53:46:53:46 | U u | isfromtemplateinstantiation.cpp:44:26:44:39 | template_class | +| isfromtemplateinstantiation.cpp:53:46:53:46 | declaration of u | isfromtemplateinstantiation.cpp:44:26:44:39 | template_class | +| isfromtemplateinstantiation.cpp:53:46:53:46 | long u | isfromtemplateinstantiation.cpp:44:26:44:39 | template_class | +| isfromtemplateinstantiation.cpp:53:46:53:46 | long u | isfromtemplateinstantiation.cpp:53:26:53:42 | template_class::b_template_method(long) | +| isfromtemplateinstantiation.cpp:54:2:55:2 | { ... } | isfromtemplateinstantiation.cpp:44:26:44:39 | template_class | +| isfromtemplateinstantiation.cpp:54:2:55:2 | { ... } | isfromtemplateinstantiation.cpp:53:26:53:42 | template_class::b_template_method(long) | +| isfromtemplateinstantiation.cpp:55:2:55:2 | return ... | isfromtemplateinstantiation.cpp:44:26:44:39 | template_class | +| isfromtemplateinstantiation.cpp:55:2:55:2 | return ... | isfromtemplateinstantiation.cpp:53:26:53:42 | template_class::b_template_method(long) | +| isfromtemplateinstantiation.cpp:77:26:77:26 | AnotherTemplateClass::operator=(AnotherTemplateClass &&) | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:77:26:77:26 | AnotherTemplateClass::operator=(const AnotherTemplateClass &) | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:77:26:77:26 | AnotherTemplateClass::operator=(AnotherTemplateClass &&) | isfromtemplateinstantiation.cpp:128:7:128:30 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:77:26:77:26 | AnotherTemplateClass::operator=(const AnotherTemplateClass &) | isfromtemplateinstantiation.cpp:128:7:128:30 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:77:26:77:26 | declaration of operator= | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:77:26:77:26 | declaration of operator= | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:77:26:77:26 | declaration of operator= | isfromtemplateinstantiation.cpp:128:7:128:30 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:77:26:77:26 | declaration of operator= | isfromtemplateinstantiation.cpp:128:7:128:30 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:82:9:82:9 | AnotherTemplateClass::MyClassStruct::operator=(MyClassStruct &&) | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:82:9:82:9 | AnotherTemplateClass::MyClassStruct::operator=(MyClassStruct &&) | isfromtemplateinstantiation.cpp:82:9:82:21 | MyClassStruct | +| isfromtemplateinstantiation.cpp:82:9:82:9 | AnotherTemplateClass::MyClassStruct::operator=(const MyClassStruct &) | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:82:9:82:9 | AnotherTemplateClass::MyClassStruct::operator=(const MyClassStruct &) | isfromtemplateinstantiation.cpp:82:9:82:21 | MyClassStruct | +| isfromtemplateinstantiation.cpp:82:9:82:9 | declaration of operator= | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:82:9:82:9 | declaration of operator= | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:82:9:82:9 | declaration of operator= | isfromtemplateinstantiation.cpp:82:9:82:21 | MyClassStruct | +| isfromtemplateinstantiation.cpp:82:9:82:9 | declaration of operator= | isfromtemplateinstantiation.cpp:82:9:82:21 | MyClassStruct | +| isfromtemplateinstantiation.cpp:82:9:82:21 | MyClassStruct | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:84:6:84:10 | definition of value | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:84:6:84:10 | definition of value | isfromtemplateinstantiation.cpp:82:9:82:21 | MyClassStruct | +| isfromtemplateinstantiation.cpp:84:6:84:10 | value | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:84:6:84:10 | value | isfromtemplateinstantiation.cpp:82:9:82:21 | MyClassStruct | +| isfromtemplateinstantiation.cpp:86:16:86:16 | definition of l | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:86:16:86:16 | l | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:90:3:90:18 | MyClassEnumConst | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:93:7:93:15 | AnotherTemplateClass::myMethod1(MyClassEnum) | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:93:29:93:32 | MyClassEnum mce1 | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:93:29:93:32 | MyClassEnum mce1 | isfromtemplateinstantiation.cpp:93:7:93:15 | AnotherTemplateClass::myMethod1(MyClassEnum) | +| isfromtemplateinstantiation.cpp:93:36:93:51 | MyClassEnumConst | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:93:36:93:51 | MyClassEnumConst | isfromtemplateinstantiation.cpp:93:7:93:15 | AnotherTemplateClass::myMethod1(MyClassEnum) | +| isfromtemplateinstantiation.cpp:93:54:93:55 | { ... } | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:93:54:93:55 | { ... } | isfromtemplateinstantiation.cpp:93:7:93:15 | AnotherTemplateClass::myMethod1(MyClassEnum) | +| isfromtemplateinstantiation.cpp:93:55:93:55 | return ... | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:93:55:93:55 | return ... | isfromtemplateinstantiation.cpp:93:7:93:15 | AnotherTemplateClass::myMethod1(MyClassEnum) | +| isfromtemplateinstantiation.cpp:94:36:94:51 | MyClassEnumConst | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:94:36:94:51 | MyClassEnumConst | isfromtemplateinstantiation.cpp:97:25:97:60 | AnotherTemplateClass::myMethod2(MyClassEnum) | +| isfromtemplateinstantiation.cpp:97:25:97:60 | AnotherTemplateClass::myMethod2(MyClassEnum) | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:97:74:97:77 | MyClassEnum mce2 | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:97:74:97:77 | MyClassEnum mce2 | isfromtemplateinstantiation.cpp:97:25:97:60 | AnotherTemplateClass::myMethod2(MyClassEnum) | +| isfromtemplateinstantiation.cpp:98:1:99:1 | { ... } | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:98:1:99:1 | { ... } | isfromtemplateinstantiation.cpp:97:25:97:60 | AnotherTemplateClass::myMethod2(MyClassEnum) | +| isfromtemplateinstantiation.cpp:99:1:99:1 | return ... | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:99:1:99:1 | return ... | isfromtemplateinstantiation.cpp:97:25:97:60 | AnotherTemplateClass::myMethod2(MyClassEnum) | +| isfromtemplateinstantiation.cpp:110:3:110:3 | definition of var_template | isfromtemplateinstantiation.cpp:110:3:110:3 | var_template | +| isfromtemplateinstantiation.cpp:129:6:129:6 | AnotherTemplateClass::f() | isfromtemplateinstantiation.cpp:128:7:128:30 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:129:10:129:22 | { ... } | isfromtemplateinstantiation.cpp:128:7:128:30 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:129:10:129:22 | { ... } | isfromtemplateinstantiation.cpp:129:6:129:6 | AnotherTemplateClass::f() | +| isfromtemplateinstantiation.cpp:129:12:129:20 | return ... | isfromtemplateinstantiation.cpp:128:7:128:30 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:129:12:129:20 | return ... | isfromtemplateinstantiation.cpp:129:6:129:6 | AnotherTemplateClass::f() | +| isfromtemplateinstantiation.cpp:129:19:129:19 | 1 | isfromtemplateinstantiation.cpp:128:7:128:30 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:129:19:129:19 | 1 | isfromtemplateinstantiation.cpp:129:6:129:6 | AnotherTemplateClass::f() | +| isfromtemplateinstantiation.cpp:134:29:134:29 | Outer::operator=(Outer &&) | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | +| isfromtemplateinstantiation.cpp:134:29:134:29 | Outer::operator=(const Outer &) | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | +| isfromtemplateinstantiation.cpp:134:29:134:29 | declaration of operator= | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | +| isfromtemplateinstantiation.cpp:134:29:134:29 | declaration of operator= | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | +| isfromtemplateinstantiation.cpp:135:31:135:31 | Outer::Inner::operator=(Inner &&) | isfromtemplateinstantiation.cpp:135:31:135:35 | Inner | +| isfromtemplateinstantiation.cpp:135:31:135:31 | Outer::Inner::operator=(const Inner &) | isfromtemplateinstantiation.cpp:135:31:135:35 | Inner | +| isfromtemplateinstantiation.cpp:135:31:135:31 | declaration of operator= | isfromtemplateinstantiation.cpp:135:31:135:35 | Inner | +| isfromtemplateinstantiation.cpp:135:31:135:31 | declaration of operator= | isfromtemplateinstantiation.cpp:135:31:135:35 | Inner | +| isfromtemplateinstantiation.cpp:136:7:136:7 | definition of x | isfromtemplateinstantiation.cpp:135:31:135:35 | Inner | +| isfromtemplateinstantiation.cpp:136:7:136:7 | x | isfromtemplateinstantiation.cpp:135:31:135:35 | Inner | +| isfromtemplateinstantiation.cpp:137:7:137:7 | definition of y | isfromtemplateinstantiation.cpp:135:31:135:35 | Inner | +| isfromtemplateinstantiation.cpp:137:7:137:7 | y | isfromtemplateinstantiation.cpp:135:31:135:35 | Inner | +| load.cpp:13:7:13:7 | basic_text_iprimitive::basic_text_iprimitive(basic_text_iprimitive &&) | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:13:7:13:7 | basic_text_iprimitive::basic_text_iprimitive(const basic_text_iprimitive &) | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:13:7:13:7 | basic_text_iprimitive::operator=(const basic_text_iprimitive &) | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:13:7:13:7 | declaration of basic_text_iprimitive | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:13:7:13:7 | declaration of basic_text_iprimitive | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:13:7:13:7 | definition of operator= | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:15:14:15:15 | definition of is | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:15:14:15:15 | is | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:18:5:18:25 | basic_text_iprimitive::basic_text_iprimitive(std_istream_mockup &) | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:18:36:18:42 | std_istream_mockup & isParam | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:18:36:18:42 | std_istream_mockup & isParam | load.cpp:18:5:18:25 | basic_text_iprimitive::basic_text_iprimitive(std_istream_mockup &) | +| load.cpp:19:11:19:21 | constructor init of field is | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:19:11:19:21 | constructor init of field is | load.cpp:18:5:18:25 | basic_text_iprimitive::basic_text_iprimitive(std_istream_mockup &) | +| load.cpp:19:14:19:20 | (reference dereference) | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:19:14:19:20 | (reference dereference) | load.cpp:18:5:18:25 | basic_text_iprimitive::basic_text_iprimitive(std_istream_mockup &) | +| load.cpp:19:14:19:20 | (reference to) | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:19:14:19:20 | (reference to) | load.cpp:18:5:18:25 | basic_text_iprimitive::basic_text_iprimitive(std_istream_mockup &) | +| load.cpp:19:14:19:20 | isParam | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:19:14:19:20 | isParam | load.cpp:18:5:18:25 | basic_text_iprimitive::basic_text_iprimitive(std_istream_mockup &) | +| load.cpp:19:23:19:24 | { ... } | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:19:23:19:24 | { ... } | load.cpp:18:5:18:25 | basic_text_iprimitive::basic_text_iprimitive(std_istream_mockup &) | +| load.cpp:19:24:19:24 | return ... | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:19:24:19:24 | return ... | load.cpp:18:5:18:25 | basic_text_iprimitive::basic_text_iprimitive(std_istream_mockup &) | +| load.cpp:22:10:22:13 | basic_text_iprimitive::load(T &) | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:22:10:22:13 | basic_text_iprimitive::load(short &) | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:22:10:22:13 | declaration of load | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:22:19:22:19 | T & t | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:22:19:22:19 | declaration of t | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:22:19:22:19 | short & t | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:22:19:22:19 | short & t | load.cpp:22:10:22:13 | basic_text_iprimitive::load(short &) | +| load.cpp:23:5:25:5 | { ... } | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:23:5:25:5 | { ... } | load.cpp:22:10:22:13 | basic_text_iprimitive::load(short &) | +| load.cpp:24:9:24:10 | (reference dereference) | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:24:9:24:10 | (reference dereference) | load.cpp:22:10:22:13 | basic_text_iprimitive::load(short &) | +| load.cpp:24:9:24:10 | is | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:24:9:24:10 | is | load.cpp:22:10:22:13 | basic_text_iprimitive::load(short &) | +| load.cpp:24:9:24:16 | ExprStmt | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:24:9:24:16 | ExprStmt | load.cpp:22:10:22:13 | basic_text_iprimitive::load(short &) | +| load.cpp:24:12:24:12 | call to operator>> | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:24:12:24:12 | call to operator>> | load.cpp:22:10:22:13 | basic_text_iprimitive::load(short &) | +| load.cpp:24:12:24:16 | (reference dereference) | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:24:12:24:16 | (reference dereference) | load.cpp:22:10:22:13 | basic_text_iprimitive::load(short &) | +| load.cpp:24:15:24:15 | (reference dereference) | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:24:15:24:15 | (reference dereference) | load.cpp:22:10:22:13 | basic_text_iprimitive::load(short &) | +| load.cpp:24:15:24:15 | (reference to) | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:24:15:24:15 | (reference to) | load.cpp:22:10:22:13 | basic_text_iprimitive::load(short &) | +| load.cpp:24:15:24:15 | t | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:24:15:24:15 | t | load.cpp:22:10:22:13 | basic_text_iprimitive::load(short &) | +| load.cpp:25:5:25:5 | return ... | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:25:5:25:5 | return ... | load.cpp:22:10:22:13 | basic_text_iprimitive::load(short &) | +| load.cpp:27:10:27:13 | basic_text_iprimitive::load(char &) | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:27:22:27:22 | char & t | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:27:22:27:22 | char & t | load.cpp:27:10:27:13 | basic_text_iprimitive::load(char &) | +| load.cpp:28:5:32:5 | { ... } | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:28:5:32:5 | { ... } | load.cpp:27:10:27:13 | basic_text_iprimitive::load(char &) | +| load.cpp:29:9:29:20 | declaration | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:29:9:29:20 | declaration | load.cpp:27:10:27:13 | basic_text_iprimitive::load(char &) | +| load.cpp:29:19:29:19 | definition of i | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:29:19:29:19 | definition of i | load.cpp:27:10:27:13 | basic_text_iprimitive::load(char &) | +| load.cpp:29:19:29:19 | i | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:29:19:29:19 | i | load.cpp:27:10:27:13 | basic_text_iprimitive::load(char &) | +| load.cpp:30:9:30:12 | call to load | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:30:9:30:12 | call to load | load.cpp:27:10:27:13 | basic_text_iprimitive::load(char &) | +| load.cpp:30:9:30:16 | ExprStmt | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:30:9:30:16 | ExprStmt | load.cpp:27:10:27:13 | basic_text_iprimitive::load(char &) | +| load.cpp:30:14:30:14 | (reference to) | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:30:14:30:14 | (reference to) | load.cpp:27:10:27:13 | basic_text_iprimitive::load(char &) | +| load.cpp:30:14:30:14 | i | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:30:14:30:14 | i | load.cpp:27:10:27:13 | basic_text_iprimitive::load(char &) | +| load.cpp:31:9:31:9 | (reference dereference) | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:31:9:31:9 | (reference dereference) | load.cpp:27:10:27:13 | basic_text_iprimitive::load(char &) | +| load.cpp:31:9:31:9 | t | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:31:9:31:9 | t | load.cpp:27:10:27:13 | basic_text_iprimitive::load(char &) | +| load.cpp:31:9:31:13 | ... = ... | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:31:9:31:13 | ... = ... | load.cpp:27:10:27:13 | basic_text_iprimitive::load(char &) | +| load.cpp:31:9:31:14 | ExprStmt | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:31:9:31:14 | ExprStmt | load.cpp:27:10:27:13 | basic_text_iprimitive::load(char &) | +| load.cpp:31:13:31:13 | (char)... | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:31:13:31:13 | (char)... | load.cpp:27:10:27:13 | basic_text_iprimitive::load(char &) | +| load.cpp:31:13:31:13 | i | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:31:13:31:13 | i | load.cpp:27:10:27:13 | basic_text_iprimitive::load(char &) | +| load.cpp:32:5:32:5 | return ... | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:32:5:32:5 | return ... | load.cpp:27:10:27:13 | basic_text_iprimitive::load(char &) | diff --git a/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/isfromtemplateinstantiation.ql b/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/isfromtemplateinstantiation.ql new file mode 100644 index 000000000000..479c11bde0ad --- /dev/null +++ b/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/isfromtemplateinstantiation.ql @@ -0,0 +1,28 @@ +import cpp + +class FunctionMonkeyPatch extends Function { + language[monotonicAggregates] + override string toString() { + exists(string name, string templateArgs, string args | + result = name + templateArgs + args + and name = this.getQualifiedName() + and if exists(this.getATemplateArgument()) + then templateArgs = "<" + concat(int i | exists(this.getTemplateArgument(i)) | this.getTemplateArgument(i).toString(), "," order by i) + ">" + else templateArgs = "" + and args = "(" + concat(int i | exists(this.getParameter(i)) | this.getParameter(i).getType().toString(), "," order by i) + ")") + } +} + +class ParameterMonkeyPatch extends Parameter { + override string toString() { + result = super.getType().getName() + " " + super.toString() + } +} + +from Element e, Element ti +where e.getLocation().getStartLine() != 0 // unhelpful +and not (function_instantiation(e, _) and e = ti) // trivial +and not (class_instantiation(e, _) and e = ti) // trivial +and not (variable_instantiation(e, _) and e = ti) // trivial +and e.isFromTemplateInstantiation(ti) +select e, ti diff --git a/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/isfromuninstantiatedtemplate.expected b/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/isfromuninstantiatedtemplate.expected new file mode 100644 index 000000000000..2c5010b7bfb0 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/isfromuninstantiatedtemplate.expected @@ -0,0 +1,540 @@ +isFromUninstantiatedTemplate +| file://:0:0:0:0 | Unknown literal | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| file://:0:0:0:0 | Unknown literal | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| file://:0:0:0:0 | declaration of 1st parameter | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | +| file://:0:0:0:0 | declaration of 1st parameter | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | +| file://:0:0:0:0 | initializer for MyClassEnumConst | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| file://:0:0:0:0 | initializer for MyClassEnumConst | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| file://:0:0:0:0 | p#0 | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | +| file://:0:0:0:0 | p#0 | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | +| file://:0:0:0:0 | this | load.cpp:13:7:13:27 | basic_text_iprimitive | +| file://:0:0:0:0 | this | load.cpp:22:10:22:13 | load | +| isfromtemplateinstantiation.cpp:12:24:12:46 | definition of inner_template_function | isfromtemplateinstantiation.cpp:12:24:12:46 | inner_template_function | +| isfromtemplateinstantiation.cpp:12:24:12:46 | inner_template_function | isfromtemplateinstantiation.cpp:12:24:12:46 | inner_template_function | +| isfromtemplateinstantiation.cpp:13:1:17:1 | { ... } | isfromtemplateinstantiation.cpp:12:24:12:46 | inner_template_function | +| isfromtemplateinstantiation.cpp:14:2:14:5 | declaration | isfromtemplateinstantiation.cpp:12:24:12:46 | inner_template_function | +| isfromtemplateinstantiation.cpp:14:4:14:4 | definition of t | isfromtemplateinstantiation.cpp:12:24:12:46 | inner_template_function | +| isfromtemplateinstantiation.cpp:14:4:14:4 | t | isfromtemplateinstantiation.cpp:12:24:12:46 | inner_template_function | +| isfromtemplateinstantiation.cpp:16:2:16:2 | t | isfromtemplateinstantiation.cpp:12:24:12:46 | inner_template_function | +| isfromtemplateinstantiation.cpp:16:2:16:4 | ... ++ | isfromtemplateinstantiation.cpp:12:24:12:46 | inner_template_function | +| isfromtemplateinstantiation.cpp:16:2:16:5 | ExprStmt | isfromtemplateinstantiation.cpp:12:24:12:46 | inner_template_function | +| isfromtemplateinstantiation.cpp:17:1:17:1 | return ... | isfromtemplateinstantiation.cpp:12:24:12:46 | inner_template_function | +| isfromtemplateinstantiation.cpp:19:24:19:46 | definition of outer_template_function | isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function | +| isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function | isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function | +| isfromtemplateinstantiation.cpp:20:1:26:1 | { ... } | isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function | +| isfromtemplateinstantiation.cpp:21:2:21:11 | declaration | isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function | +| isfromtemplateinstantiation.cpp:21:6:21:6 | definition of i | isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function | +| isfromtemplateinstantiation.cpp:21:6:21:6 | i | isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function | +| isfromtemplateinstantiation.cpp:21:9:21:10 | 0 | isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function | +| isfromtemplateinstantiation.cpp:21:9:21:10 | initializer for i | isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function | +| isfromtemplateinstantiation.cpp:23:2:23:8 | ... ++ | isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function | +| isfromtemplateinstantiation.cpp:23:2:23:8 | i | isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function | +| isfromtemplateinstantiation.cpp:23:2:23:9 | ExprStmt | isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function | +| isfromtemplateinstantiation.cpp:25:2:25:27 | Unknown literal | isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function | +| isfromtemplateinstantiation.cpp:25:2:25:29 | call to expression | isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function | +| isfromtemplateinstantiation.cpp:25:2:25:30 | ExprStmt | isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function | +| isfromtemplateinstantiation.cpp:26:1:26:1 | return ... | isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function | +| isfromtemplateinstantiation.cpp:38:26:38:42 | a_template_method | isfromtemplateinstantiation.cpp:38:26:38:42 | a_template_method | +| isfromtemplateinstantiation.cpp:38:26:38:42 | definition of a_template_method | isfromtemplateinstantiation.cpp:38:26:38:42 | a_template_method | +| isfromtemplateinstantiation.cpp:39:2:40:2 | { ... } | isfromtemplateinstantiation.cpp:38:26:38:42 | a_template_method | +| isfromtemplateinstantiation.cpp:40:2:40:2 | return ... | isfromtemplateinstantiation.cpp:38:26:38:42 | a_template_method | +| isfromtemplateinstantiation.cpp:44:26:44:39 | definition of template_class | isfromtemplateinstantiation.cpp:44:26:44:39 | template_class | +| isfromtemplateinstantiation.cpp:44:26:44:39 | 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:14 | b_method | isfromtemplateinstantiation.cpp:44:26:44:39 | template_class | +| isfromtemplateinstantiation.cpp:49:7:49:14 | b_method | isfromtemplateinstantiation.cpp:49:7:49:14 | b_method | +| isfromtemplateinstantiation.cpp:49:7:49:14 | definition of b_method | isfromtemplateinstantiation.cpp:44:26:44:39 | template_class | +| isfromtemplateinstantiation.cpp:49:7:49:14 | definition of b_method | isfromtemplateinstantiation.cpp:49:7:49:14 | b_method | +| isfromtemplateinstantiation.cpp:50:2:51:2 | { ... } | isfromtemplateinstantiation.cpp:44:26:44:39 | template_class | +| isfromtemplateinstantiation.cpp:50:2:51:2 | { ... } | isfromtemplateinstantiation.cpp:49:7:49:14 | b_method | +| isfromtemplateinstantiation.cpp:51:2:51:2 | return ... | isfromtemplateinstantiation.cpp:44:26:44:39 | template_class | +| isfromtemplateinstantiation.cpp:51:2:51:2 | return ... | isfromtemplateinstantiation.cpp:49:7:49:14 | b_method | +| isfromtemplateinstantiation.cpp:53:26:53:42 | b_template_method | isfromtemplateinstantiation.cpp:44:26:44:39 | template_class | +| isfromtemplateinstantiation.cpp:53:26:53:42 | b_template_method | isfromtemplateinstantiation.cpp:53:26:53:42 | b_template_method | +| isfromtemplateinstantiation.cpp:53:26:53:42 | b_template_method | isfromtemplateinstantiation.cpp:53:26:53:42 | b_template_method | +| isfromtemplateinstantiation.cpp:53:26:53:42 | declaration of b_template_method | isfromtemplateinstantiation.cpp:53:26:53:42 | b_template_method | +| isfromtemplateinstantiation.cpp:53:26:53:42 | definition of b_template_method | isfromtemplateinstantiation.cpp:44:26:44:39 | template_class | +| isfromtemplateinstantiation.cpp:53:26:53:42 | definition of b_template_method | isfromtemplateinstantiation.cpp:53:26:53:42 | b_template_method | +| isfromtemplateinstantiation.cpp:53:46:53:46 | declaration of u | isfromtemplateinstantiation.cpp:53:26:53:42 | b_template_method | +| isfromtemplateinstantiation.cpp:53:46:53:46 | definition of u | isfromtemplateinstantiation.cpp:44:26:44:39 | template_class | +| isfromtemplateinstantiation.cpp:53:46:53:46 | definition of u | isfromtemplateinstantiation.cpp:53:26:53:42 | b_template_method | +| isfromtemplateinstantiation.cpp:53:46:53:46 | u | isfromtemplateinstantiation.cpp:44:26:44:39 | template_class | +| isfromtemplateinstantiation.cpp:53:46:53:46 | u | isfromtemplateinstantiation.cpp:53:26:53:42 | b_template_method | +| isfromtemplateinstantiation.cpp:53:46:53:46 | u | isfromtemplateinstantiation.cpp:53:26:53:42 | b_template_method | +| isfromtemplateinstantiation.cpp:54:2:55:2 | { ... } | isfromtemplateinstantiation.cpp:44:26:44:39 | template_class | +| isfromtemplateinstantiation.cpp:54:2:55:2 | { ... } | isfromtemplateinstantiation.cpp:53:26:53:42 | b_template_method | +| isfromtemplateinstantiation.cpp:55:2:55:2 | return ... | isfromtemplateinstantiation.cpp:44:26:44:39 | template_class | +| isfromtemplateinstantiation.cpp:55:2:55:2 | return ... | isfromtemplateinstantiation.cpp:53:26:53:42 | b_template_method | +| isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:77:26:77:45 | definition of AnotherTemplateClass | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:80:12:80:13 | _t | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:80:12:80:13 | declaration of _t | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:82:9:82:21 | MyClassStruct | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:82:9:82:21 | MyClassStruct | isfromtemplateinstantiation.cpp:82:9:82:21 | MyClassStruct | +| isfromtemplateinstantiation.cpp:82:9:82:21 | definition of MyClassStruct | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:82:9:82:21 | definition of MyClassStruct | isfromtemplateinstantiation.cpp:82:9:82:21 | MyClassStruct | +| isfromtemplateinstantiation.cpp:84:6:84:10 | definition of value | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:84:6:84:10 | definition of value | isfromtemplateinstantiation.cpp:82:9:82:21 | MyClassStruct | +| isfromtemplateinstantiation.cpp:84:6:84:10 | value | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:84:6:84:10 | value | isfromtemplateinstantiation.cpp:82:9:82:21 | MyClassStruct | +| isfromtemplateinstantiation.cpp:86:16:86:16 | definition of l | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:86:16:86:16 | l | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:88:7:88:17 | MyClassEnum | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:88:7:88:17 | definition of MyClassEnum | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:90:3:90:18 | MyClassEnumConst | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:93:7:93:15 | definition of myMethod1 | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:93:7:93:15 | definition of myMethod1 | isfromtemplateinstantiation.cpp:93:7:93:15 | myMethod1 | +| isfromtemplateinstantiation.cpp:93:7:93:15 | myMethod1 | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:93:7:93:15 | myMethod1 | isfromtemplateinstantiation.cpp:93:7:93:15 | myMethod1 | +| isfromtemplateinstantiation.cpp:93:29:93:32 | definition of mce1 | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:93:29:93:32 | definition of mce1 | isfromtemplateinstantiation.cpp:93:7:93:15 | myMethod1 | +| isfromtemplateinstantiation.cpp:93:29:93:32 | mce1 | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:93:29:93:32 | mce1 | isfromtemplateinstantiation.cpp:93:7:93:15 | myMethod1 | +| isfromtemplateinstantiation.cpp:93:54:93:55 | { ... } | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:93:54:93:55 | { ... } | isfromtemplateinstantiation.cpp:93:7:93:15 | myMethod1 | +| isfromtemplateinstantiation.cpp:93:55:93:55 | return ... | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:93:55:93:55 | return ... | isfromtemplateinstantiation.cpp:93:7:93:15 | myMethod1 | +| isfromtemplateinstantiation.cpp:94:7:94:15 | declaration of myMethod2 | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:94:7:94:15 | declaration of myMethod2 | isfromtemplateinstantiation.cpp:97:25:97:60 | myMethod2 | +| isfromtemplateinstantiation.cpp:94:29:94:32 | declaration of mce2 | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:94:29:94:32 | declaration of mce2 | isfromtemplateinstantiation.cpp:97:25:97:60 | myMethod2 | +| isfromtemplateinstantiation.cpp:97:25:97:60 | definition of myMethod2 | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:97:25:97:60 | definition of myMethod2 | isfromtemplateinstantiation.cpp:97:25:97:60 | myMethod2 | +| isfromtemplateinstantiation.cpp:97:25:97:60 | myMethod2 | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:97:25:97:60 | myMethod2 | isfromtemplateinstantiation.cpp:97:25:97:60 | myMethod2 | +| isfromtemplateinstantiation.cpp:97:74:97:77 | definition of mce2 | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:97:74:97:77 | definition of mce2 | isfromtemplateinstantiation.cpp:97:25:97:60 | myMethod2 | +| isfromtemplateinstantiation.cpp:97:74:97:77 | mce2 | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:97:74:97:77 | mce2 | isfromtemplateinstantiation.cpp:97:25:97:60 | myMethod2 | +| isfromtemplateinstantiation.cpp:98:1:99:1 | { ... } | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:98:1:99:1 | { ... } | isfromtemplateinstantiation.cpp:97:25:97:60 | myMethod2 | +| isfromtemplateinstantiation.cpp:99:1:99:1 | return ... | isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:99:1:99:1 | return ... | isfromtemplateinstantiation.cpp:97:25:97:60 | myMethod2 | +| isfromtemplateinstantiation.cpp:110:15:110:15 | definition of var_template | isfromtemplateinstantiation.cpp:110:15:110:15 | var_template | +| isfromtemplateinstantiation.cpp:110:15:110:15 | var_template | isfromtemplateinstantiation.cpp:110:15:110:15 | var_template | +| isfromtemplateinstantiation.cpp:128:7:128:30 | AnotherTemplateClass | isfromtemplateinstantiation.cpp:128:7:128:30 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:128:7:128:30 | definition of AnotherTemplateClass | isfromtemplateinstantiation.cpp:128:7:128:30 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:129:6:129:6 | definition of f | isfromtemplateinstantiation.cpp:128:7:128:30 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:129:6:129:6 | definition of f | isfromtemplateinstantiation.cpp:129:6:129:6 | f | +| isfromtemplateinstantiation.cpp:129:6:129:6 | f | isfromtemplateinstantiation.cpp:128:7:128:30 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:129:6:129:6 | f | isfromtemplateinstantiation.cpp:129:6:129:6 | f | +| isfromtemplateinstantiation.cpp:129:10:129:22 | { ... } | isfromtemplateinstantiation.cpp:128:7:128:30 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:129:10:129:22 | { ... } | isfromtemplateinstantiation.cpp:129:6:129:6 | f | +| isfromtemplateinstantiation.cpp:129:12:129:20 | return ... | isfromtemplateinstantiation.cpp:128:7:128:30 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:129:12:129:20 | return ... | isfromtemplateinstantiation.cpp:129:6:129:6 | f | +| isfromtemplateinstantiation.cpp:129:19:129:19 | 1 | isfromtemplateinstantiation.cpp:128:7:128:30 | AnotherTemplateClass | +| isfromtemplateinstantiation.cpp:129:19:129:19 | 1 | isfromtemplateinstantiation.cpp:129:6:129:6 | f | +| isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | +| isfromtemplateinstantiation.cpp:134:29:134:33 | definition of Outer | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | +| isfromtemplateinstantiation.cpp:135:31:135:31 | declaration of operator= | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | +| isfromtemplateinstantiation.cpp:135:31:135:31 | declaration of operator= | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | +| isfromtemplateinstantiation.cpp:135:31:135:31 | operator= | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | +| isfromtemplateinstantiation.cpp:135:31:135:31 | operator= | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | +| isfromtemplateinstantiation.cpp:135:31:135:35 | Inner | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | +| isfromtemplateinstantiation.cpp:135:31:135:35 | Inner | isfromtemplateinstantiation.cpp:135:31:135:35 | Inner | +| isfromtemplateinstantiation.cpp:135:31:135:35 | Inner | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | +| isfromtemplateinstantiation.cpp:135:31:135:35 | definition of Inner | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | +| isfromtemplateinstantiation.cpp:135:31:135:35 | definition of Inner | isfromtemplateinstantiation.cpp:135:31:135:35 | Inner | +| isfromtemplateinstantiation.cpp:136:7:136:7 | definition of x | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | +| isfromtemplateinstantiation.cpp:136:7:136:7 | definition of x | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | +| isfromtemplateinstantiation.cpp:136:7:136:7 | definition of x | isfromtemplateinstantiation.cpp:135:31:135:35 | Inner | +| isfromtemplateinstantiation.cpp:136:7:136:7 | x | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | +| isfromtemplateinstantiation.cpp:136:7:136:7 | x | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | +| isfromtemplateinstantiation.cpp:136:7:136:7 | x | isfromtemplateinstantiation.cpp:135:31:135:35 | Inner | +| isfromtemplateinstantiation.cpp:137:7:137:7 | definition of y | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | +| isfromtemplateinstantiation.cpp:137:7:137:7 | definition of y | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | +| isfromtemplateinstantiation.cpp:137:7:137:7 | definition of y | isfromtemplateinstantiation.cpp:135:31:135:35 | Inner | +| isfromtemplateinstantiation.cpp:137:7:137:7 | y | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | +| isfromtemplateinstantiation.cpp:137:7:137:7 | y | isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | +| isfromtemplateinstantiation.cpp:137:7:137:7 | y | isfromtemplateinstantiation.cpp:135:31:135:35 | Inner | +| isfromtemplateinstantiation.cpp:144:28:144:56 | declaration of incomplete_never_instantiated | isfromtemplateinstantiation.cpp:144:28:144:56 | incomplete_never_instantiated | +| isfromtemplateinstantiation.cpp:144:28:144:56 | incomplete_never_instantiated | isfromtemplateinstantiation.cpp:144:28:144:56 | incomplete_never_instantiated | +| isfromtemplateinstantiation.cpp:146:28:146:45 | definition of never_instantiated | isfromtemplateinstantiation.cpp:146:28:146:45 | never_instantiated | +| isfromtemplateinstantiation.cpp:146:28:146:45 | never_instantiated | isfromtemplateinstantiation.cpp:146:28:146:45 | never_instantiated | +| isfromtemplateinstantiation.cpp:146:51:146:51 | definition of x | isfromtemplateinstantiation.cpp:146:28:146:45 | never_instantiated | +| isfromtemplateinstantiation.cpp:146:51:146:51 | x | isfromtemplateinstantiation.cpp:146:28:146:45 | never_instantiated | +| isfromtemplateinstantiation.cpp:148:24:148:46 | decl_never_instantiated | isfromtemplateinstantiation.cpp:148:24:148:46 | decl_never_instantiated | +| isfromtemplateinstantiation.cpp:148:24:148:46 | declaration of decl_never_instantiated | isfromtemplateinstantiation.cpp:148:24:148:46 | decl_never_instantiated | +| isfromtemplateinstantiation.cpp:148:50:148:50 | declaration of x | isfromtemplateinstantiation.cpp:148:24:148:46 | decl_never_instantiated | +| isfromtemplateinstantiation.cpp:148:50:148:50 | x | isfromtemplateinstantiation.cpp:148:24:148:46 | decl_never_instantiated | +| isfromtemplateinstantiation.cpp:150:24:150:45 | def_never_instantiated | isfromtemplateinstantiation.cpp:150:24:150:45 | def_never_instantiated | +| isfromtemplateinstantiation.cpp:150:24:150:45 | definition of def_never_instantiated | isfromtemplateinstantiation.cpp:150:24:150:45 | def_never_instantiated | +| isfromtemplateinstantiation.cpp:150:49:150:49 | definition of x | isfromtemplateinstantiation.cpp:150:24:150:45 | def_never_instantiated | +| isfromtemplateinstantiation.cpp:150:49:150:49 | x | isfromtemplateinstantiation.cpp:150:24:150:45 | def_never_instantiated | +| load.cpp:13:7:13:27 | basic_text_iprimitive | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:13:7:13:27 | definition of basic_text_iprimitive | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:15:14:15:15 | definition of is | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:15:14:15:15 | is | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:18:5:18:25 | basic_text_iprimitive | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:18:5:18:25 | basic_text_iprimitive | load.cpp:18:5:18:25 | basic_text_iprimitive | +| load.cpp:18:5:18:25 | definition of basic_text_iprimitive | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:18:5:18:25 | definition of basic_text_iprimitive | load.cpp:18:5:18:25 | basic_text_iprimitive | +| load.cpp:18:36:18:42 | definition of isParam | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:18:36:18:42 | definition of isParam | load.cpp:18:5:18:25 | basic_text_iprimitive | +| load.cpp:18:36:18:42 | isParam | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:18:36:18:42 | isParam | load.cpp:18:5:18:25 | basic_text_iprimitive | +| load.cpp:19:11:19:21 | constructor init of field is | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:19:11:19:21 | constructor init of field is | load.cpp:18:5:18:25 | basic_text_iprimitive | +| load.cpp:19:14:19:20 | (reference dereference) | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:19:14:19:20 | (reference dereference) | load.cpp:18:5:18:25 | basic_text_iprimitive | +| load.cpp:19:14:19:20 | (reference to) | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:19:14:19:20 | (reference to) | load.cpp:18:5:18:25 | basic_text_iprimitive | +| load.cpp:19:14:19:20 | isParam | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:19:14:19:20 | isParam | load.cpp:18:5:18:25 | basic_text_iprimitive | +| load.cpp:19:23:19:24 | { ... } | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:19:23:19:24 | { ... } | load.cpp:18:5:18:25 | basic_text_iprimitive | +| load.cpp:19:24:19:24 | return ... | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:19:24:19:24 | return ... | load.cpp:18:5:18:25 | basic_text_iprimitive | +| load.cpp:22:10:22:13 | declaration of load | load.cpp:22:10:22:13 | load | +| load.cpp:22:10:22:13 | definition of load | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:22:10:22:13 | definition of load | load.cpp:22:10:22:13 | load | +| load.cpp:22:10:22:13 | load | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:22:10:22:13 | load | load.cpp:22:10:22:13 | load | +| load.cpp:22:10:22:13 | load | load.cpp:22:10:22:13 | load | +| load.cpp:22:19:22:19 | declaration of t | load.cpp:22:10:22:13 | load | +| load.cpp:22:19:22:19 | definition of t | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:22:19:22:19 | definition of t | load.cpp:22:10:22:13 | load | +| load.cpp:22:19:22:19 | t | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:22:19:22:19 | t | load.cpp:22:10:22:13 | load | +| load.cpp:22:19:22:19 | t | load.cpp:22:10:22:13 | load | +| load.cpp:23:5:25:5 | { ... } | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:23:5:25:5 | { ... } | load.cpp:22:10:22:13 | load | +| load.cpp:24:9:24:10 | (reference dereference) | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:24:9:24:10 | (reference dereference) | load.cpp:22:10:22:13 | load | +| load.cpp:24:9:24:10 | is | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:24:9:24:10 | is | load.cpp:22:10:22:13 | load | +| load.cpp:24:9:24:15 | ... >> ... | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:24:9:24:15 | ... >> ... | load.cpp:22:10:22:13 | load | +| load.cpp:24:9:24:16 | ExprStmt | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:24:9:24:16 | ExprStmt | load.cpp:22:10:22:13 | load | +| load.cpp:24:15:24:15 | (reference dereference) | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:24:15:24:15 | (reference dereference) | load.cpp:22:10:22:13 | load | +| load.cpp:24:15:24:15 | t | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:24:15:24:15 | t | load.cpp:22:10:22:13 | load | +| load.cpp:25:5:25:5 | return ... | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:25:5:25:5 | return ... | load.cpp:22:10:22:13 | load | +| load.cpp:27:10:27:13 | definition of load | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:27:10:27:13 | definition of load | load.cpp:27:10:27:13 | load | +| load.cpp:27:10:27:13 | load | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:27:10:27:13 | load | load.cpp:27:10:27:13 | load | +| load.cpp:27:22:27:22 | definition of t | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:27:22:27:22 | definition of t | load.cpp:27:10:27:13 | load | +| load.cpp:27:22:27:22 | t | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:27:22:27:22 | t | load.cpp:27:10:27:13 | load | +| load.cpp:28:5:32:5 | { ... } | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:28:5:32:5 | { ... } | load.cpp:27:10:27:13 | load | +| load.cpp:29:9:29:20 | declaration | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:29:9:29:20 | declaration | load.cpp:27:10:27:13 | load | +| load.cpp:29:19:29:19 | definition of i | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:29:19:29:19 | definition of i | load.cpp:27:10:27:13 | load | +| load.cpp:29:19:29:19 | i | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:29:19:29:19 | i | load.cpp:27:10:27:13 | load | +| load.cpp:30:9:30:12 | Unknown literal | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:30:9:30:12 | Unknown literal | load.cpp:27:10:27:13 | load | +| load.cpp:30:9:30:15 | call to expression | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:30:9:30:15 | call to expression | load.cpp:27:10:27:13 | load | +| load.cpp:30:9:30:16 | ExprStmt | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:30:9:30:16 | ExprStmt | load.cpp:27:10:27:13 | load | +| load.cpp:30:14:30:14 | i | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:30:14:30:14 | i | load.cpp:27:10:27:13 | load | +| load.cpp:31:9:31:9 | (reference dereference) | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:31:9:31:9 | (reference dereference) | load.cpp:27:10:27:13 | load | +| load.cpp:31:9:31:9 | t | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:31:9:31:9 | t | load.cpp:27:10:27:13 | load | +| load.cpp:31:9:31:13 | ... = ... | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:31:9:31:13 | ... = ... | load.cpp:27:10:27:13 | load | +| load.cpp:31:9:31:14 | ExprStmt | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:31:9:31:14 | ExprStmt | load.cpp:27:10:27:13 | load | +| load.cpp:31:13:31:13 | (char)... | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:31:13:31:13 | (char)... | load.cpp:27:10:27:13 | load | +| load.cpp:31:13:31:13 | i | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:31:13:31:13 | i | load.cpp:27:10:27:13 | load | +| load.cpp:32:5:32:5 | return ... | load.cpp:13:7:13:27 | basic_text_iprimitive | +| load.cpp:32:5:32:5 | return ... | load.cpp:27:10:27:13 | load | +#select +| isfromtemplateinstantiation.cpp:4:6:4:20 | normal_function | | | Declaration | | +| isfromtemplateinstantiation.cpp:12:24:12:46 | inner_template_function | | T | Declaration | | +| isfromtemplateinstantiation.cpp:12:24:12:46 | inner_template_function | I | | Declaration | | +| isfromtemplateinstantiation.cpp:12:24:12:46 | inner_template_function | I | | Declaration | | +| isfromtemplateinstantiation.cpp:13:1:17:1 | { ... } | | T | Stmt | | +| isfromtemplateinstantiation.cpp:13:1:17:1 | { ... } | I | | Stmt | | +| isfromtemplateinstantiation.cpp:13:1:17:1 | { ... } | I | | Stmt | | +| isfromtemplateinstantiation.cpp:14:2:14:5 | declaration | | T | Stmt | | +| isfromtemplateinstantiation.cpp:14:2:14:5 | declaration | I | | Stmt | | +| isfromtemplateinstantiation.cpp:14:2:14:5 | declaration | I | | Stmt | | +| isfromtemplateinstantiation.cpp:14:4:14:4 | definition of t | | T | Definition | | +| isfromtemplateinstantiation.cpp:14:4:14:4 | definition of t | I | | Definition | | +| isfromtemplateinstantiation.cpp:14:4:14:4 | definition of t | I | | Definition | | +| isfromtemplateinstantiation.cpp:14:4:14:4 | t | | T | Declaration | | +| isfromtemplateinstantiation.cpp:14:4:14:4 | t | I | | Declaration | | +| isfromtemplateinstantiation.cpp:14:4:14:4 | t | I | | Declaration | | +| isfromtemplateinstantiation.cpp:16:2:16:2 | t | | T | Expr | Not ref | +| isfromtemplateinstantiation.cpp:16:2:16:2 | t | I | | Expr | Not ref | +| isfromtemplateinstantiation.cpp:16:2:16:2 | t | I | | Expr | Not ref | +| isfromtemplateinstantiation.cpp:16:2:16:4 | ... ++ | | T | Expr | | +| isfromtemplateinstantiation.cpp:16:2:16:4 | ... ++ | I | | Expr | | +| isfromtemplateinstantiation.cpp:16:2:16:4 | ... ++ | I | | Expr | | +| isfromtemplateinstantiation.cpp:16:2:16:5 | ExprStmt | | T | Stmt | | +| isfromtemplateinstantiation.cpp:16:2:16:5 | ExprStmt | I | | Stmt | | +| isfromtemplateinstantiation.cpp:16:2:16:5 | ExprStmt | I | | Stmt | | +| isfromtemplateinstantiation.cpp:17:1:17:1 | return ... | | T | Stmt | | +| isfromtemplateinstantiation.cpp:17:1:17:1 | return ... | I | | Stmt | | +| isfromtemplateinstantiation.cpp:17:1:17:1 | return ... | I | | Stmt | | +| isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function | | T | Declaration | | +| isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function | I | | Declaration | | +| isfromtemplateinstantiation.cpp:19:24:19:46 | outer_template_function | I | | Declaration | | +| isfromtemplateinstantiation.cpp:20:1:26:1 | { ... } | | T | Stmt | | +| isfromtemplateinstantiation.cpp:20:1:26:1 | { ... } | I | | Stmt | | +| isfromtemplateinstantiation.cpp:20:1:26:1 | { ... } | I | | Stmt | | +| isfromtemplateinstantiation.cpp:21:2:21:11 | declaration | | T | Stmt | | +| isfromtemplateinstantiation.cpp:21:2:21:11 | declaration | I | | Stmt | | +| isfromtemplateinstantiation.cpp:21:2:21:11 | declaration | I | | Stmt | | +| isfromtemplateinstantiation.cpp:21:6:21:6 | definition of i | | T | Definition | | +| isfromtemplateinstantiation.cpp:21:6:21:6 | definition of i | I | | Definition | | +| isfromtemplateinstantiation.cpp:21:6:21:6 | definition of i | I | | Definition | | +| isfromtemplateinstantiation.cpp:21:6:21:6 | i | | T | Declaration | | +| isfromtemplateinstantiation.cpp:21:6:21:6 | i | I | | Declaration | | +| isfromtemplateinstantiation.cpp:21:6:21:6 | i | I | | Declaration | | +| isfromtemplateinstantiation.cpp:21:9:21:10 | 0 | | T | Expr | | +| isfromtemplateinstantiation.cpp:21:9:21:10 | 0 | I | | Expr | | +| isfromtemplateinstantiation.cpp:21:9:21:10 | 0 | I | | Expr | | +| isfromtemplateinstantiation.cpp:21:9:21:10 | initializer for i | | T | Initializer | | +| isfromtemplateinstantiation.cpp:21:9:21:10 | initializer for i | I | | Initializer | | +| isfromtemplateinstantiation.cpp:21:9:21:10 | initializer for i | I | | Initializer | | +| isfromtemplateinstantiation.cpp:23:2:23:8 | ... ++ | | T | Expr | | +| isfromtemplateinstantiation.cpp:23:2:23:8 | ... ++ | I | | Expr | | +| isfromtemplateinstantiation.cpp:23:2:23:8 | ... ++ | I | | Expr | | +| isfromtemplateinstantiation.cpp:23:2:23:8 | i | | T | Expr | Not ref | +| isfromtemplateinstantiation.cpp:23:2:23:8 | i | I | | Expr | Not ref | +| isfromtemplateinstantiation.cpp:23:2:23:8 | i | I | | Expr | Not ref | +| isfromtemplateinstantiation.cpp:23:2:23:9 | ExprStmt | | T | Stmt | | +| isfromtemplateinstantiation.cpp:23:2:23:9 | ExprStmt | I | | Stmt | | +| isfromtemplateinstantiation.cpp:23:2:23:9 | ExprStmt | I | | Stmt | | +| isfromtemplateinstantiation.cpp:25:2:25:27 | Unknown literal | | T | Expr | | +| isfromtemplateinstantiation.cpp:25:2:25:27 | call to inner_template_function | I | | Expr | | +| isfromtemplateinstantiation.cpp:25:2:25:27 | call to inner_template_function | I | | Expr | | +| isfromtemplateinstantiation.cpp:25:2:25:30 | ExprStmt | | T | Stmt | | +| isfromtemplateinstantiation.cpp:25:2:25:30 | ExprStmt | I | | Stmt | | +| isfromtemplateinstantiation.cpp:25:2:25:30 | ExprStmt | I | | Stmt | | +| isfromtemplateinstantiation.cpp:26:1:26:1 | return ... | | T | Stmt | | +| isfromtemplateinstantiation.cpp:26:1:26:1 | return ... | I | | Stmt | | +| 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 | operator= | | | Declaration | | +| isfromtemplateinstantiation.cpp:29:7:29:7 | operator= | | | Declaration | | +| isfromtemplateinstantiation.cpp:29:7:29:18 | normal_class | | | Declaration | | +| isfromtemplateinstantiation.cpp:34:7:34:14 | a_method | | | Declaration | | +| isfromtemplateinstantiation.cpp:38:26:38:42 | a_template_method | | T | Declaration | | +| isfromtemplateinstantiation.cpp:38:26:38:42 | a_template_method | I | | Declaration | | +| isfromtemplateinstantiation.cpp:39:2:40:2 | { ... } | | T | Stmt | | +| isfromtemplateinstantiation.cpp:39:2:40:2 | { ... } | I | | Stmt | | +| isfromtemplateinstantiation.cpp:40:2:40:2 | return ... | | T | Stmt | | +| isfromtemplateinstantiation.cpp:40:2:40:2 | return ... | I | | Stmt | | +| isfromtemplateinstantiation.cpp:44:26:44:26 | declaration of operator= | I | | DeclarationEntry | | +| 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: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 | | +| isfromtemplateinstantiation.cpp:46:4:46:4 | definition of t | I | | Definition | | +| isfromtemplateinstantiation.cpp:46:4:46:4 | t | | T | Declaration | | +| isfromtemplateinstantiation.cpp:46:4:46:4 | t | I | | Declaration | | +| isfromtemplateinstantiation.cpp:49:7:49:14 | b_method | | T | Declaration | | +| isfromtemplateinstantiation.cpp:49:7:49:14 | b_method | I | | Declaration | | +| isfromtemplateinstantiation.cpp:50:2:51:2 | { ... } | | T | Stmt | | +| isfromtemplateinstantiation.cpp:50:2:51:2 | { ... } | I | | Stmt | | +| isfromtemplateinstantiation.cpp:51:2:51:2 | return ... | | T | Stmt | | +| isfromtemplateinstantiation.cpp:51:2:51:2 | return ... | I | | Stmt | | +| isfromtemplateinstantiation.cpp:53:26:53:42 | b_template_method | | T | Declaration | | +| isfromtemplateinstantiation.cpp:53:26:53:42 | b_template_method | I | | Declaration | | +| isfromtemplateinstantiation.cpp:53:26:53:42 | b_template_method | I | T | Declaration | | +| isfromtemplateinstantiation.cpp:53:46:53:46 | u | | T | Declaration | | +| isfromtemplateinstantiation.cpp:53:46:53:46 | u | I | | Declaration | | +| isfromtemplateinstantiation.cpp:53:46:53:46 | u | I | T | Declaration | | +| isfromtemplateinstantiation.cpp:54:2:55:2 | { ... } | | T | Stmt | | +| isfromtemplateinstantiation.cpp:54:2:55:2 | { ... } | I | | Stmt | | +| isfromtemplateinstantiation.cpp:55:2:55:2 | return ... | | T | Stmt | | +| isfromtemplateinstantiation.cpp:55:2:55:2 | return ... | I | | Stmt | | +| isfromtemplateinstantiation.cpp:59:5:59:8 | main | | | Declaration | | +| isfromtemplateinstantiation.cpp:72:30:72:32 | 97 | | | Expr | | +| isfromtemplateinstantiation.cpp:72:30:72:32 | (long)... | | | Expr | | +| isfromtemplateinstantiation.cpp:77:26:77:26 | declaration of operator= | I | | DeclarationEntry | | +| isfromtemplateinstantiation.cpp:77:26:77:26 | declaration of operator= | I | | DeclarationEntry | | +| isfromtemplateinstantiation.cpp:77:26:77:26 | declaration of operator= | I | | DeclarationEntry | | +| isfromtemplateinstantiation.cpp:77:26:77:26 | declaration of operator= | I | | DeclarationEntry | | +| isfromtemplateinstantiation.cpp:77:26:77:26 | operator= | I | | Declaration | | +| isfromtemplateinstantiation.cpp:77:26:77:26 | operator= | I | | Declaration | | +| isfromtemplateinstantiation.cpp:77:26:77:26 | operator= | I | | Declaration | | +| isfromtemplateinstantiation.cpp:77:26:77:26 | operator= | I | | Declaration | | +| isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | | T | Declaration | | +| isfromtemplateinstantiation.cpp:77:26:77:45 | AnotherTemplateClass | I | | Declaration | | +| isfromtemplateinstantiation.cpp:82:9:82:9 | declaration of operator= | I | | DeclarationEntry | | +| isfromtemplateinstantiation.cpp:82:9:82:9 | declaration of operator= | I | | DeclarationEntry | | +| isfromtemplateinstantiation.cpp:82:9:82:9 | operator= | I | | Declaration | | +| isfromtemplateinstantiation.cpp:82:9:82:9 | operator= | I | | Declaration | | +| isfromtemplateinstantiation.cpp:82:9:82:21 | MyClassStruct | | T | Declaration | | +| isfromtemplateinstantiation.cpp:82:9:82:21 | MyClassStruct | I | | Declaration | | +| isfromtemplateinstantiation.cpp:84:6:84:10 | definition of value | | T | Definition | | +| isfromtemplateinstantiation.cpp:84:6:84:10 | definition of value | I | | Definition | | +| isfromtemplateinstantiation.cpp:84:6:84:10 | value | | T | Declaration | | +| isfromtemplateinstantiation.cpp:84:6:84:10 | value | I | | Declaration | | +| isfromtemplateinstantiation.cpp:86:16:86:16 | definition of l | | T | Definition | | +| isfromtemplateinstantiation.cpp:86:16:86:16 | definition of l | I | | Definition | | +| isfromtemplateinstantiation.cpp:86:16:86:16 | l | | T | Declaration | | +| isfromtemplateinstantiation.cpp:86:16:86:16 | l | I | | Declaration | | +| isfromtemplateinstantiation.cpp:90:3:90:18 | MyClassEnumConst | | T | Declaration | | +| isfromtemplateinstantiation.cpp:90:3:90:18 | MyClassEnumConst | I | | Declaration | | +| isfromtemplateinstantiation.cpp:93:7:93:15 | myMethod1 | | T | Declaration | | +| isfromtemplateinstantiation.cpp:93:7:93:15 | myMethod1 | I | | Declaration | | +| isfromtemplateinstantiation.cpp:93:29:93:32 | mce1 | | T | Declaration | | +| isfromtemplateinstantiation.cpp:93:29:93:32 | mce1 | I | | Declaration | | +| isfromtemplateinstantiation.cpp:93:54:93:55 | { ... } | | T | Stmt | | +| isfromtemplateinstantiation.cpp:93:54:93:55 | { ... } | I | | Stmt | | +| isfromtemplateinstantiation.cpp:93:55:93:55 | return ... | | T | Stmt | | +| isfromtemplateinstantiation.cpp:93:55:93:55 | return ... | I | | Stmt | | +| isfromtemplateinstantiation.cpp:97:25:97:60 | myMethod2 | | T | Declaration | | +| isfromtemplateinstantiation.cpp:97:25:97:60 | myMethod2 | I | | Declaration | | +| isfromtemplateinstantiation.cpp:97:74:97:77 | mce2 | | T | Declaration | | +| isfromtemplateinstantiation.cpp:97:74:97:77 | mce2 | I | | Declaration | | +| isfromtemplateinstantiation.cpp:98:1:99:1 | { ... } | | T | Stmt | | +| isfromtemplateinstantiation.cpp:98:1:99:1 | { ... } | I | | Stmt | | +| isfromtemplateinstantiation.cpp:99:1:99:1 | return ... | | T | Stmt | | +| isfromtemplateinstantiation.cpp:99:1:99:1 | return ... | I | | Stmt | | +| isfromtemplateinstantiation.cpp:103:6:103:16 | anotherMain | | | Declaration | | +| isfromtemplateinstantiation.cpp:114:6:114:24 | function_using_vars | | | Declaration | | +| isfromtemplateinstantiation.cpp:122:7:122:7 | declaration of operator= | | | DeclarationEntry | | +| isfromtemplateinstantiation.cpp:122:7:122:7 | declaration of operator= | | | DeclarationEntry | | +| isfromtemplateinstantiation.cpp:122:7:122:7 | operator= | | | Declaration | | +| isfromtemplateinstantiation.cpp:122:7:122:7 | operator= | | | Declaration | | +| isfromtemplateinstantiation.cpp:122:7:122:32 | AnotherTemplateClass | | | Declaration | | +| isfromtemplateinstantiation.cpp:123:6:123:6 | f | | | Declaration | | +| isfromtemplateinstantiation.cpp:128:7:128:30 | AnotherTemplateClass | | T | Declaration | | +| isfromtemplateinstantiation.cpp:128:7:128:30 | AnotherTemplateClass | I | | Declaration | | +| isfromtemplateinstantiation.cpp:129:6:129:6 | f | | T | Declaration | | +| isfromtemplateinstantiation.cpp:129:6:129:6 | f | I | | Declaration | | +| isfromtemplateinstantiation.cpp:129:10:129:22 | { ... } | | T | Stmt | | +| isfromtemplateinstantiation.cpp:129:10:129:22 | { ... } | I | | Stmt | | +| isfromtemplateinstantiation.cpp:129:12:129:20 | return ... | | T | Stmt | | +| isfromtemplateinstantiation.cpp:129:12:129:20 | return ... | I | | Stmt | | +| isfromtemplateinstantiation.cpp:129:19:129:19 | 1 | | T | Expr | | +| isfromtemplateinstantiation.cpp:129:19:129:19 | 1 | I | | Expr | | +| isfromtemplateinstantiation.cpp:134:29:134:29 | declaration of operator= | I | | DeclarationEntry | | +| isfromtemplateinstantiation.cpp:134:29:134:29 | declaration of operator= | I | | DeclarationEntry | | +| isfromtemplateinstantiation.cpp:134:29:134:29 | operator= | I | | Declaration | | +| isfromtemplateinstantiation.cpp:134:29:134:29 | operator= | I | | Declaration | | +| isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | | T | Declaration | | +| isfromtemplateinstantiation.cpp:134:29:134:33 | Outer | I | | Declaration | | +| isfromtemplateinstantiation.cpp:135:31:135:31 | declaration of operator= | I | T | DeclarationEntry | | +| isfromtemplateinstantiation.cpp:135:31:135:31 | declaration of operator= | I | T | DeclarationEntry | | +| isfromtemplateinstantiation.cpp:135:31:135:31 | operator= | I | T | Declaration | | +| isfromtemplateinstantiation.cpp:135:31:135:31 | operator= | I | T | Declaration | | +| isfromtemplateinstantiation.cpp:135:31:135:35 | Inner | | T | Declaration | | +| isfromtemplateinstantiation.cpp:135:31:135:35 | Inner | I | T | Declaration | | +| isfromtemplateinstantiation.cpp:136:7:136:7 | definition of x | | T | Definition | | +| isfromtemplateinstantiation.cpp:136:7:136:7 | definition of x | I | T | Definition | | +| isfromtemplateinstantiation.cpp:136:7:136:7 | x | | T | Declaration | | +| isfromtemplateinstantiation.cpp:136:7:136:7 | x | I | T | Declaration | | +| isfromtemplateinstantiation.cpp:137:7:137:7 | definition of y | | T | Definition | | +| isfromtemplateinstantiation.cpp:137:7:137:7 | definition of y | I | T | Definition | | +| isfromtemplateinstantiation.cpp:137:7:137:7 | y | | T | Declaration | | +| isfromtemplateinstantiation.cpp:137:7:137:7 | y | I | T | Declaration | | +| isfromtemplateinstantiation.cpp:144:28:144:56 | incomplete_never_instantiated | | T | Declaration | | +| isfromtemplateinstantiation.cpp:146:28:146:45 | never_instantiated | | T | Declaration | | +| isfromtemplateinstantiation.cpp:148:24:148:46 | decl_never_instantiated | | T | Declaration | | +| isfromtemplateinstantiation.cpp:150:24:150:45 | def_never_instantiated | | T | Declaration | | +| load.cpp:3:7:3:7 | declaration of operator= | | | DeclarationEntry | | +| load.cpp:3:7:3:7 | declaration of operator= | | | DeclarationEntry | | +| load.cpp:3:7:3:7 | operator= | | | Declaration | | +| load.cpp:3:7:3:7 | operator= | | | Declaration | | +| load.cpp:3:7:3:24 | std_istream_mockup | | | Declaration | | +| load.cpp:5:25:5:34 | operator>> | | | Declaration | | +| load.cpp:6:9:6:9 | (reference dereference) | | | Expr | | +| load.cpp:6:9:6:9 | i | | | Expr | Not ref | +| load.cpp:6:13:6:21 | (short)... | | | Expr | | +| load.cpp:6:13:6:21 | externInt | | | Expr | Not ref | +| load.cpp:7:16:7:20 | (reference to) | | | Expr | | +| load.cpp:7:16:7:20 | * ... | | | Expr | | +| load.cpp:13:7:13:7 | basic_text_iprimitive | I | | Declaration | | +| load.cpp:13:7:13:7 | basic_text_iprimitive | I | | Declaration | | +| load.cpp:13:7:13:7 | declaration of basic_text_iprimitive | I | | DeclarationEntry | | +| load.cpp:13:7:13:7 | declaration of basic_text_iprimitive | I | | DeclarationEntry | | +| load.cpp:13:7:13:7 | operator= | I | | Declaration | | +| load.cpp:13:7:13:27 | basic_text_iprimitive | | T | Declaration | | +| load.cpp:13:7:13:27 | basic_text_iprimitive | I | | Declaration | | +| load.cpp:15:14:15:15 | definition of is | | T | Definition | | +| load.cpp:15:14:15:15 | definition of is | I | | Definition | | +| load.cpp:15:14:15:15 | is | | T | Declaration | | +| load.cpp:15:14:15:15 | is | I | | Declaration | | +| load.cpp:18:5:18:25 | basic_text_iprimitive | | T | Declaration | | +| load.cpp:18:5:18:25 | basic_text_iprimitive | I | | Declaration | | +| load.cpp:18:36:18:42 | isParam | | T | Declaration | | +| load.cpp:18:36:18:42 | isParam | I | | Declaration | | +| load.cpp:19:11:19:21 | constructor init of field is | | T | Expr | | +| load.cpp:19:11:19:21 | constructor init of field is | I | | Expr | | +| load.cpp:19:14:19:20 | (reference dereference) | | T | Expr | | +| load.cpp:19:14:19:20 | (reference dereference) | I | | Expr | | +| load.cpp:19:14:19:20 | (reference to) | | T | Expr | | +| load.cpp:19:14:19:20 | (reference to) | I | | Expr | | +| load.cpp:19:14:19:20 | isParam | | T | Expr | Ref | +| load.cpp:19:14:19:20 | isParam | I | | Expr | Ref | +| load.cpp:19:23:19:24 | { ... } | | T | Stmt | | +| load.cpp:19:23:19:24 | { ... } | I | | Stmt | | +| load.cpp:19:24:19:24 | return ... | | T | Stmt | | +| load.cpp:19:24:19:24 | return ... | I | | Stmt | | +| load.cpp:22:10:22:13 | load | | T | Declaration | | +| load.cpp:22:10:22:13 | load | I | | Declaration | | +| load.cpp:22:10:22:13 | load | I | T | Declaration | | +| load.cpp:22:19:22:19 | t | | T | Declaration | | +| load.cpp:22:19:22:19 | t | I | | Declaration | | +| load.cpp:22:19:22:19 | t | I | T | Declaration | | +| load.cpp:23:5:25:5 | { ... } | | T | Stmt | | +| load.cpp:23:5:25:5 | { ... } | I | | Stmt | | +| load.cpp:24:9:24:10 | (reference dereference) | | T | Expr | | +| load.cpp:24:9:24:10 | (reference dereference) | I | | Expr | | +| load.cpp:24:9:24:10 | is | | T | Expr | Not ref | +| load.cpp:24:9:24:10 | is | I | | Expr | Not ref | +| load.cpp:24:9:24:16 | ExprStmt | | T | Stmt | | +| load.cpp:24:9:24:16 | ExprStmt | I | | Stmt | | +| load.cpp:24:15:24:15 | (reference dereference) | | T | Expr | | +| load.cpp:24:15:24:15 | (reference dereference) | I | | Expr | | +| load.cpp:24:15:24:15 | (reference to) | I | | Expr | | +| load.cpp:24:15:24:15 | t | | T | Expr | Not ref | +| load.cpp:24:15:24:15 | t | I | | Expr | Ref | +| load.cpp:25:5:25:5 | return ... | | T | Stmt | | +| load.cpp:25:5:25:5 | return ... | I | | Stmt | | +| load.cpp:27:10:27:13 | load | | T | Declaration | | +| load.cpp:27:10:27:13 | load | I | | Declaration | | +| load.cpp:27:22:27:22 | t | | T | Declaration | | +| load.cpp:27:22:27:22 | t | I | | Declaration | | +| load.cpp:28:5:32:5 | { ... } | | T | Stmt | | +| load.cpp:28:5:32:5 | { ... } | I | | Stmt | | +| load.cpp:29:9:29:20 | declaration | | T | Stmt | | +| load.cpp:29:9:29:20 | declaration | I | | Stmt | | +| load.cpp:29:19:29:19 | definition of i | | T | Definition | | +| load.cpp:29:19:29:19 | definition of i | I | | Definition | | +| load.cpp:29:19:29:19 | i | | T | Declaration | | +| load.cpp:29:19:29:19 | i | I | | Declaration | | +| load.cpp:30:9:30:12 | Unknown literal | | T | Expr | | +| load.cpp:30:9:30:12 | call to load | I | | Expr | | +| load.cpp:30:9:30:16 | ExprStmt | | T | Stmt | | +| load.cpp:30:9:30:16 | ExprStmt | I | | Stmt | | +| load.cpp:30:14:30:14 | (reference to) | I | | Expr | | +| load.cpp:30:14:30:14 | i | | T | Expr | Not ref | +| load.cpp:30:14:30:14 | i | I | | Expr | Ref | +| load.cpp:31:9:31:9 | (reference dereference) | | T | Expr | | +| load.cpp:31:9:31:9 | (reference dereference) | I | | Expr | | +| load.cpp:31:9:31:9 | t | | T | Expr | Not ref | +| load.cpp:31:9:31:9 | t | I | | Expr | Not ref | +| load.cpp:31:9:31:13 | ... = ... | | T | Expr | | +| load.cpp:31:9:31:13 | ... = ... | I | | Expr | | +| load.cpp:31:9:31:14 | ExprStmt | | T | Stmt | | +| load.cpp:31:9:31:14 | ExprStmt | I | | Stmt | | +| load.cpp:31:13:31:13 | (char)... | | T | Expr | | +| load.cpp:31:13:31:13 | (char)... | I | | Expr | | +| load.cpp:31:13:31:13 | i | | T | Expr | Not ref | +| load.cpp:31:13:31:13 | i | I | | Expr | Not ref | +| load.cpp:32:5:32:5 | return ... | | T | Stmt | | +| load.cpp:32:5:32:5 | return ... | I | | Stmt | | diff --git a/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/isfromuninstantiatedtemplate.ql b/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/isfromuninstantiatedtemplate.ql new file mode 100644 index 000000000000..4f7e4b3dcbe9 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/isfromuninstantiatedtemplate.ql @@ -0,0 +1,70 @@ +import cpp + +string knownKind(Element e) { + e instanceof Expr and result = "Expr" + or + e instanceof DeclarationEntry and + ( if e.(DeclarationEntry).isDefinition() + then result = "Definition" + else result = "DeclarationEntry" ) + or + e instanceof Declaration and result = "Declaration" + or + e instanceof Initializer and result = "Initializer" + or + e instanceof Stmt and result = "Stmt" + or + e instanceof ClassDerivation and result = "ClassDerivation" +} + +string kind(Element e) { + result = strictconcat(knownKind(e), "+") + or + not exists(knownKind(e)) and + result = "other" +} + +predicate hasTwin(Element e) { + not e.getLocation() instanceof UnknownLocation and + strictcount(Element other | + exists(string file, int sl, int sc, int el, int ec | + other.getLocation().hasLocationInfo(file, sl, sc, el, ec) and + e.getLocation().hasLocationInfo(file, sl, sc, el, ec) and + kind(other) = kind(e) + ) + ) > 1 +} + +predicate isInteresting(Element el) { + not el.getLocation() instanceof UnknownLocation and + exists(el.getLocation()) and + ( el instanceof Class + or + el instanceof Function + ) +} + +// This is one case where the template before or after the instantiation is +// likely to be different. +string conversionString(Element el) { + if el instanceof VariableAccess + then + if el.(VariableAccess).getConversion+() instanceof ReferenceToExpr + then result = "Ref" + else result = "Not ref" + else + result = "" +} + +query predicate isFromUninstantiatedTemplate(Element e, Element template) { + e.isFromUninstantiatedTemplate(template) +} + +from Element el +where (hasTwin(el) or isInteresting(el)) +select + el, + any(string s | if el.isFromTemplateInstantiation(_) then s="I" else s=""), + any(string s | if el.isFromUninstantiatedTemplate(_) then s="T" else s=""), + kind(el), + conversionString(el) diff --git a/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/load.cpp b/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/load.cpp new file mode 100644 index 000000000000..4977e69b010b --- /dev/null +++ b/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/load.cpp @@ -0,0 +1,35 @@ +extern int externInt; + +class std_istream_mockup { +public: + std_istream_mockup &operator>>(short int &i) { + i = externInt; + return *this; + } +}; + + +template +class basic_text_iprimitive +{ + IStream &is; +public: + + basic_text_iprimitive(IStream &isParam) + : is(isParam) {} + + template + void load(T & t) + { + is >> t; + } + + void load(char & t) + { + short int i; + load(i); + t = i; + } +}; + +template class basic_text_iprimitive; diff --git a/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/template_is_template.expected b/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/template_is_template.expected new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/template_is_template.ql b/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/template_is_template.ql new file mode 100644 index 000000000000..b6c4d4df4da2 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/template_is_template.ql @@ -0,0 +1,27 @@ +import cpp + +predicate canBeTemplate(Declaration d) { + d instanceof Class + or + d instanceof Function + or + d instanceof Variable +} + +predicate isTemplate(Declaration d) { + d instanceof TemplateClass + or + d instanceof TemplateFunction + or + d instanceof TemplateVariable +} + +from Element e, string msg +where canBeTemplate(e) + and ( isTemplate(e) and + not e.isFromUninstantiatedTemplate(_) and + msg = "only isTemplate" ) + and ( e.isFromUninstantiatedTemplate(_) and + not isTemplate(e) and + msg = "only isFromUninstantiatedTemplate" ) +select e, msg diff --git a/cpp/ql/test/library-tests/templates/prototype_bodies/add_expr.expected b/cpp/ql/test/library-tests/templates/prototype_bodies/add_expr.expected new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/cpp/ql/test/library-tests/templates/prototype_bodies/add_expr.ql b/cpp/ql/test/library-tests/templates/prototype_bodies/add_expr.ql new file mode 100644 index 000000000000..7ca0f81db5ba --- /dev/null +++ b/cpp/ql/test/library-tests/templates/prototype_bodies/add_expr.ql @@ -0,0 +1,4 @@ +import cpp + +from AddExpr ae +select ae diff --git a/cpp/ql/test/library-tests/templates/prototype_bodies/call_operands.expected b/cpp/ql/test/library-tests/templates/prototype_bodies/call_operands.expected new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/cpp/ql/test/library-tests/templates/prototype_bodies/call_operands.ql b/cpp/ql/test/library-tests/templates/prototype_bodies/call_operands.ql new file mode 100644 index 000000000000..25937972c15e --- /dev/null +++ b/cpp/ql/test/library-tests/templates/prototype_bodies/call_operands.ql @@ -0,0 +1,7 @@ +import cpp + +from Expr e, int column +where exists(Call c | c = e.getParent+() | c.getLocation().getStartLine() = 5) + and column = e.getLocation().getStartColumn() + and column > 10 // Excludes the function name token, which isn't represented nicely yet. +select column, e.toString() diff --git a/cpp/ql/test/library-tests/templates/prototype_bodies/initialiser.expected b/cpp/ql/test/library-tests/templates/prototype_bodies/initialiser.expected new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/cpp/ql/test/library-tests/templates/prototype_bodies/initialiser.ql b/cpp/ql/test/library-tests/templates/prototype_bodies/initialiser.ql new file mode 100644 index 000000000000..25a6caa1ad9d --- /dev/null +++ b/cpp/ql/test/library-tests/templates/prototype_bodies/initialiser.ql @@ -0,0 +1,4 @@ +import cpp + +from Variable t +select t, t.getInitializer() diff --git a/cpp/ql/test/library-tests/templates/prototype_bodies/isdef_hasblock.expected b/cpp/ql/test/library-tests/templates/prototype_bodies/isdef_hasblock.expected new file mode 100644 index 000000000000..f6d6c8135b29 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/prototype_bodies/isdef_hasblock.expected @@ -0,0 +1,4 @@ +| f | defined | no block | +| g | not defined | no block | +| main | defined | has block | +| operator= | not defined | no block | diff --git a/cpp/ql/test/library-tests/templates/prototype_bodies/isdef_hasblock.ql b/cpp/ql/test/library-tests/templates/prototype_bodies/isdef_hasblock.ql new file mode 100644 index 000000000000..51f8e15ae01b --- /dev/null +++ b/cpp/ql/test/library-tests/templates/prototype_bodies/isdef_hasblock.ql @@ -0,0 +1,8 @@ +import cpp + +// It should be the case that "f.isDefined()" is equivalent to "exists(f.getBlock())". + +from Function f, string isdef, string hasblock +where (if f.isDefined() then isdef = "defined" else isdef = "not defined") + and (if exists(f.getBlock()) then hasblock = "has block" else hasblock = "no block") +select f.getName(), isdef, hasblock diff --git a/cpp/ql/test/library-tests/templates/prototype_bodies/main.cpp b/cpp/ql/test/library-tests/templates/prototype_bodies/main.cpp new file mode 100644 index 000000000000..90969dae8b25 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/prototype_bodies/main.cpp @@ -0,0 +1,12 @@ +template +void f() { + T t = 4; + t + t; + some_function(&t, sizeof(T), sizeof(t), alignof(T), alignof(t)); +} + +void g(); + +int main() { + return 0; +} diff --git a/cpp/ql/test/library-tests/templates/segfault/segfault.cpp b/cpp/ql/test/library-tests/templates/segfault/segfault.cpp new file mode 100644 index 000000000000..a8a996d88035 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/segfault/segfault.cpp @@ -0,0 +1,8 @@ + +class c { + struct t { + template< template< typename V0 > class V > struct s { }; + template< typename V > static void f( s < V::template r >* = 0 ); + }; +}; + diff --git a/cpp/ql/test/library-tests/templates/segfault/vars.expected b/cpp/ql/test/library-tests/templates/segfault/vars.expected new file mode 100644 index 000000000000..3901b33f2de9 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/segfault/vars.expected @@ -0,0 +1,5 @@ +| file://:0:0:0:0 | __va_list_tag | +| segfault.cpp:2:7:2:7 | c | +| segfault.cpp:3:12:3:12 | t | +| segfault.cpp:4:60:4:60 | s | +| segfault.cpp:4:60:4:60 | s | diff --git a/cpp/ql/test/library-tests/templates/segfault/vars.ql b/cpp/ql/test/library-tests/templates/segfault/vars.ql new file mode 100644 index 000000000000..4174e095ec74 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/segfault/vars.ql @@ -0,0 +1,5 @@ +import cpp + +from Class t +select t + diff --git a/cpp/ql/test/library-tests/templates/segfault2/expr.expected b/cpp/ql/test/library-tests/templates/segfault2/expr.expected new file mode 100644 index 000000000000..f35b93a3195a --- /dev/null +++ b/cpp/ql/test/library-tests/templates/segfault2/expr.expected @@ -0,0 +1,9 @@ +| test.cpp:10:9:10:26 | static_cast... | +| test.cpp:10:9:10:26 | static_cast... | +| test.cpp:10:9:10:32 | (vacuous destructor call) | +| test.cpp:10:9:10:32 | call to expression | +| test.cpp:10:25:10:25 | t | +| test.cpp:10:25:10:25 | t | +| test.cpp:10:29:10:30 | Unknown literal | +| test.cpp:15:13:15:13 | call to f | +| test.cpp:15:15:15:21 | m | diff --git a/cpp/ql/test/library-tests/templates/segfault2/expr.ql b/cpp/ql/test/library-tests/templates/segfault2/expr.ql new file mode 100644 index 000000000000..2f525817cb22 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/segfault2/expr.ql @@ -0,0 +1,4 @@ +import cpp + +from Expr e +select e diff --git a/cpp/ql/test/library-tests/templates/segfault2/test.cpp b/cpp/ql/test/library-tests/templates/segfault2/test.cpp new file mode 100644 index 000000000000..1afff484ef53 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/segfault2/test.cpp @@ -0,0 +1,17 @@ + +typedef void (*ft)(void *); +int f(ft fp); + +class C {}; + +template +struct S { + static void m(void *t) { + static_cast(t)->~T(); + } +}; + +void g() { + int i = f(S::m); +} + diff --git a/cpp/ql/test/library-tests/templates/type_instantiations/test.cpp b/cpp/ql/test/library-tests/templates/type_instantiations/test.cpp new file mode 100644 index 000000000000..2a7bd1f7bcc0 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/type_instantiations/test.cpp @@ -0,0 +1,5 @@ + +#include "test.h" + +Cl *x; + diff --git a/cpp/ql/test/library-tests/templates/type_instantiations/test.h b/cpp/ql/test/library-tests/templates/type_instantiations/test.h new file mode 100644 index 000000000000..476502ff02de --- /dev/null +++ b/cpp/ql/test/library-tests/templates/type_instantiations/test.h @@ -0,0 +1,7 @@ + +struct Sa; +struct Sb; + +template +class Cl; + diff --git a/cpp/ql/test/library-tests/templates/type_instantiations/types.expected b/cpp/ql/test/library-tests/templates/type_instantiations/types.expected new file mode 100644 index 000000000000..31d7b7caa944 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/type_instantiations/types.expected @@ -0,0 +1,58 @@ +| file://:0:0:0:0 | Cl * | +| file://:0:0:0:0 | _Complex __float128 | +| file://:0:0:0:0 | _Complex double | +| file://:0:0:0:0 | _Complex float | +| file://:0:0:0:0 | _Complex long double | +| file://:0:0:0:0 | _Decimal32 | +| file://:0:0:0:0 | _Decimal64 | +| file://:0:0:0:0 | _Decimal128 | +| file://:0:0:0:0 | _Float32 | +| file://:0:0:0:0 | _Float32x | +| file://:0:0:0:0 | _Float64 | +| file://:0:0:0:0 | _Float64x | +| file://:0:0:0:0 | _Float128 | +| file://:0:0:0:0 | _Float128x | +| file://:0:0:0:0 | _Imaginary double | +| file://:0:0:0:0 | _Imaginary float | +| file://:0:0:0:0 | _Imaginary long double | +| file://:0:0:0:0 | __float128 | +| file://:0:0:0:0 | __int128 | +| file://:0:0:0:0 | __va_list_tag | +| file://:0:0:0:0 | __va_list_tag & | +| file://:0:0:0:0 | auto | +| file://:0:0:0:0 | bool | +| file://:0:0:0:0 | char | +| file://:0:0:0:0 | char16_t | +| file://:0:0:0:0 | char32_t | +| file://:0:0:0:0 | decltype(nullptr) | +| file://:0:0:0:0 | double | +| file://:0:0:0:0 | error | +| file://:0:0:0:0 | float | +| file://:0:0:0:0 | int | +| file://:0:0:0:0 | long | +| file://:0:0:0:0 | long double | +| file://:0:0:0:0 | long long | +| file://:0:0:0:0 | short | +| file://:0:0:0:0 | signed __int128 | +| file://:0:0:0:0 | signed char | +| file://:0:0:0:0 | signed int | +| file://:0:0:0:0 | signed long | +| file://:0:0:0:0 | signed long long | +| file://:0:0:0:0 | signed short | +| file://:0:0:0:0 | unknown | +| file://:0:0:0:0 | unsigned __int128 | +| file://:0:0:0:0 | unsigned char | +| file://:0:0:0:0 | unsigned int | +| file://:0:0:0:0 | unsigned long | +| file://:0:0:0:0 | unsigned long long | +| file://:0:0:0:0 | unsigned short | +| file://:0:0:0:0 | void | +| file://:0:0:0:0 | void * | +| file://:0:0:0:0 | wchar_t | +| test.h:2:8:2:9 | Sa | +| test.h:3:8:3:9 | Sb | +| test.h:5:16:5:16 | A | +| test.h:5:25:5:25 | B | +| test.h:5:34:5:34 | C | +| test.h:6:7:6:8 | Cl | +| test.h:6:7:6:8 | Cl | diff --git a/cpp/ql/test/library-tests/templates/type_instantiations/types.ql b/cpp/ql/test/library-tests/templates/type_instantiations/types.ql new file mode 100644 index 000000000000..4c1bd1a3d5a0 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/type_instantiations/types.ql @@ -0,0 +1,4 @@ +import cpp + +from Type t +select t diff --git a/cpp/ql/test/library-tests/templates/typedefs/template_typedefs.cpp b/cpp/ql/test/library-tests/templates/typedefs/template_typedefs.cpp new file mode 100644 index 000000000000..6f40c242accb --- /dev/null +++ b/cpp/ql/test/library-tests/templates/typedefs/template_typedefs.cpp @@ -0,0 +1,56 @@ +typedef int my_int; + +template void a(Ta t) {} +template void b(Tb t) {} +template void c(Tc t) {} +template void d(Td t) {} +template void e(Te t) {} +template void f(Tf t) {} + +void functions() { + a(static_cast(0)); + b(static_cast(0)); + c(0); + d(0); + + e(0); + e(0); + + f(0); + f(0); +} + +template struct A {}; +template struct B {}; +template struct C {}; +template struct D {}; +template struct E {}; +template struct F {}; +template struct G {}; + +struct S { int x; }; +typedef S S_t; + +typedef C C1; +typedef D D1; + +void types() { + A* a; + B* b; + C1 c; + D1 d; + + E e1; + E e2; + + F f1; + F f2; + + G g1; + G g2; + G g3; + G g4; + G g5; + G g6; + G g7; +} diff --git a/cpp/ql/test/library-tests/templates/typedefs/template_typedefs.expected b/cpp/ql/test/library-tests/templates/typedefs/template_typedefs.expected new file mode 100644 index 000000000000..23174bb3e9a2 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/typedefs/template_typedefs.expected @@ -0,0 +1,32 @@ +| A | TA | +| A | int | +| B | TB | +| B | int | +| C | TC | +| C | int | +| D | TD | +| D | int | +| E | TE | +| E | int | +| F | TF | +| F | int | +| G<..(*)(..)> | pointer to {function returning {pointer to {int}} with arguments (int)} | +| G | TG | +| G<__attribute((vector_size(32))) int> | {GNU 8 element vector of {int}} | +| G | const {pointer to {const {int}}} | +| G | reference to {pointer to {pointer to {int}}} | +| G | pointer to {int} | +| G | pointer to member of S with type {int} | +| G | reference to {array of 3 {int}} | +| a | Ta | +| a | int | +| b | Tb | +| b | int | +| c | Tc | +| c | int | +| d | Td | +| d | int | +| e | Te | +| e | int | +| f | Tf | +| f | int | diff --git a/cpp/ql/test/library-tests/templates/typedefs/template_typedefs.ql b/cpp/ql/test/library-tests/templates/typedefs/template_typedefs.ql new file mode 100644 index 000000000000..72e7836b5a28 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/typedefs/template_typedefs.ql @@ -0,0 +1,6 @@ +import cpp + +from string name, Type arg +where exists(Function f | name = f.getName() and arg = f.getATemplateArgument()) + or exists(Class c | name = c.getName() and arg = c.getATemplateArgument()) +select name, arg.explain() diff --git a/cpp/ql/test/library-tests/templates/variables/options b/cpp/ql/test/library-tests/templates/variables/options new file mode 100644 index 000000000000..1d5826f83ec4 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/variables/options @@ -0,0 +1 @@ +extractor_flags: -std=c++14 diff --git a/cpp/ql/test/library-tests/templates/variables/template_variables.expected b/cpp/ql/test/library-tests/templates/variables/template_variables.expected new file mode 100644 index 000000000000..ebc70b54b963 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/variables/template_variables.expected @@ -0,0 +1,35 @@ +| variables.cpp:2:13:2:13 | pi | variables.cpp:25:12:25:16 | pi | 0 | T | +| variables.cpp:2:13:2:13 | pi | variables.cpp:25:12:25:16 | pi | 0 | float | +| variables.cpp:2:13:2:13 | pi | variables.cpp:25:12:25:16 | pi | 0 | int | +| variables.cpp:2:13:2:13 | pi | variables.cpp:37:16:37:24 | pi | 0 | float | +| variables.cpp:2:13:2:13 | pi | variables.cpp:38:16:38:22 | pi | 0 | int | +| variables.cpp:8:13:8:13 | multi_arg | variables.cpp:33:19:33:33 | multi_arg | 0 | S | +| variables.cpp:8:13:8:13 | multi_arg | variables.cpp:33:19:33:33 | multi_arg | 0 | float | +| variables.cpp:8:13:8:13 | multi_arg | variables.cpp:33:19:33:33 | multi_arg | 0 | short | +| variables.cpp:8:13:8:13 | multi_arg | variables.cpp:33:19:33:33 | multi_arg | 1 | T | +| variables.cpp:8:13:8:13 | multi_arg | variables.cpp:33:19:33:33 | multi_arg | 1 | char | +| variables.cpp:8:13:8:13 | multi_arg | variables.cpp:33:19:33:33 | multi_arg | 1 | long | +| variables.cpp:8:13:8:13 | multi_arg | variables.cpp:40:23:40:60 | multi_arg | 0 | unsigned int | +| variables.cpp:8:13:8:13 | multi_arg | variables.cpp:40:23:40:60 | multi_arg | 1 | unsigned char | +| variables.cpp:8:13:8:13 | multi_arg | variables.cpp:41:23:41:42 | multi_arg | 0 | int | +| variables.cpp:8:13:8:13 | multi_arg | variables.cpp:41:23:41:42 | multi_arg | 1 | char | +| variables.cpp:11:3:11:3 | mutable_val | variables.cpp:26:3:26:16 | mutable_val | 0 | T | +| variables.cpp:11:3:11:3 | mutable_val | variables.cpp:26:3:26:16 | mutable_val | 0 | float | +| variables.cpp:11:3:11:3 | mutable_val | variables.cpp:26:3:26:16 | mutable_val | 0 | int | +| variables.cpp:11:3:11:3 | mutable_val | variables.cpp:43:3:43:18 | mutable_val | 0 | int | +| variables.cpp:11:3:11:3 | mutable_val | variables.cpp:44:3:44:19 | mutable_val | 0 | long | +| variables.cpp:19:8:19:8 | bar | variables.cpp:27:3:27:13 | bar | 0 | T | +| variables.cpp:19:8:19:8 | bar | variables.cpp:27:3:27:13 | bar | 0 | float | +| variables.cpp:19:8:19:8 | bar | variables.cpp:27:3:27:13 | bar | 0 | int | +| variables.cpp:19:8:19:8 | bar | variables.cpp:46:3:46:17 | bar | 0 | short | +| variables.cpp:19:8:19:8 | bar | variables.cpp:47:3:47:18 | bar | 0 | double | +| variables.cpp:21:5:21:15 | no_template | variables.cpp:28:3:28:13 | no_template | -1 | | +| variables.cpp:21:5:21:15 | no_template | variables.cpp:28:3:28:13 | no_template | -1 | | +| variables.cpp:21:5:21:15 | no_template | variables.cpp:28:3:28:13 | no_template | -1 | | +| variables.cpp:21:5:21:15 | no_template | variables.cpp:49:3:49:13 | no_template | -1 | | +| variables.cpp:24:27:24:29 | val | variables.cpp:26:20:26:22 | val | -1 | | +| variables.cpp:24:27:24:29 | val | variables.cpp:26:20:26:22 | val | -1 | | +| variables.cpp:24:27:24:29 | val | variables.cpp:26:20:26:22 | val | -1 | | +| variables.cpp:24:27:24:29 | val | variables.cpp:27:17:27:19 | val | -1 | | +| variables.cpp:24:27:24:29 | val | variables.cpp:27:17:27:19 | val | -1 | | +| variables.cpp:24:27:24:29 | val | variables.cpp:27:17:27:19 | val | -1 | | diff --git a/cpp/ql/test/library-tests/templates/variables/template_variables.ql b/cpp/ql/test/library-tests/templates/variables/template_variables.ql new file mode 100644 index 000000000000..ddda2c6439ff --- /dev/null +++ b/cpp/ql/test/library-tests/templates/variables/template_variables.ql @@ -0,0 +1,8 @@ +import cpp + +from Variable v, VariableAccess a, int i, string s +where v = a.getTarget() + and if exists(v.getATemplateArgument()) + then s = v.getTemplateArgument(i).toString() + else (s = "" and i = -1) +select v, a, i, s diff --git a/cpp/ql/test/library-tests/templates/variables/variables.cpp b/cpp/ql/test/library-tests/templates/variables/variables.cpp new file mode 100644 index 000000000000..e5bc95a18eb0 --- /dev/null +++ b/cpp/ql/test/library-tests/templates/variables/variables.cpp @@ -0,0 +1,58 @@ +template +constexpr T pi = T(3.141592653589793238462643383); + +template<> +constexpr const char* pi = "pi"; + +template +constexpr S multi_arg = T(1) + S(2); + +template +T mutable_val = T(7); + +struct Foo { + template + static T bar; +}; + +template +T Foo::bar = T(0); + +int no_template = 9; + +template +void access_generically(T val) { + T pi_t = pi; + mutable_val = val; + Foo::bar = val; + no_template = 123; +} + +template +void access_generically_multi() { + S multi_arg_s = multi_arg; +} + +void access_concretely() { + float pi_f = pi; + int pi_i = pi; + + float multi_arg_a = multi_arg; + int multi_arg_b = multi_arg; + + mutable_val = 8; + mutable_val = 9; + + Foo::bar = 1; + Foo::bar = 3.14; + + no_template = 234; +} + +int main() { + access_generically(1.0f); + access_generically(1); + access_generically_multi(); + access_generically_multi(); + access_concretely(); +} diff --git a/cpp/ql/test/library-tests/ti_compiler/function.expected b/cpp/ql/test/library-tests/ti_compiler/function.expected new file mode 100644 index 000000000000..79a818fefda7 --- /dev/null +++ b/cpp/ql/test/library-tests/ti_compiler/function.expected @@ -0,0 +1,2 @@ +| ti.c:2:11:2:12 | f1 | file://:0:0:0:0 | char far * | pointer to {far {char}} | +| ti.c:6:11:6:12 | f2 | file://:0:0:0:0 | char far * | pointer to {far {char}} | diff --git a/cpp/ql/test/library-tests/ti_compiler/function.ql b/cpp/ql/test/library-tests/ti_compiler/function.ql new file mode 100644 index 000000000000..ca4f850ea163 --- /dev/null +++ b/cpp/ql/test/library-tests/ti_compiler/function.ql @@ -0,0 +1,5 @@ +import cpp + +from Function f, Type t +where t = f.getType() +select f, t, t.explain() diff --git a/cpp/ql/test/library-tests/ti_compiler/options b/cpp/ql/test/library-tests/ti_compiler/options new file mode 100644 index 000000000000..4861c6eb8263 --- /dev/null +++ b/cpp/ql/test/library-tests/ti_compiler/options @@ -0,0 +1 @@ +extractor_flags: --edg --ti diff --git a/cpp/ql/test/library-tests/ti_compiler/ti.c b/cpp/ql/test/library-tests/ti_compiler/ti.c new file mode 100644 index 000000000000..9107b1cba5ea --- /dev/null +++ b/cpp/ql/test/library-tests/ti_compiler/ti.c @@ -0,0 +1,9 @@ + +char far *f1(char far *s) { + return s; +} + +far char *f2(far char *s) { + return s; +} + diff --git a/cpp/ql/test/library-tests/ti_compiler/variable.expected b/cpp/ql/test/library-tests/ti_compiler/variable.expected new file mode 100644 index 000000000000..6bde09372add --- /dev/null +++ b/cpp/ql/test/library-tests/ti_compiler/variable.expected @@ -0,0 +1,6 @@ +| file://:0:0:0:0 | fp_offset | file://:0:0:0:0 | unsigned int | unsigned int | +| file://:0:0:0:0 | gp_offset | file://:0:0:0:0 | unsigned int | unsigned int | +| file://:0:0:0:0 | overflow_arg_area | file://:0:0:0:0 | void * | pointer to {void} | +| file://:0:0:0:0 | reg_save_area | file://:0:0:0:0 | void * | pointer to {void} | +| ti.c:2:24:2:24 | s | file://:0:0:0:0 | char far * | pointer to {far {char}} | +| ti.c:6:24:6:24 | s | file://:0:0:0:0 | char far * | pointer to {far {char}} | diff --git a/cpp/ql/test/library-tests/ti_compiler/variable.ql b/cpp/ql/test/library-tests/ti_compiler/variable.ql new file mode 100644 index 000000000000..37f7e0820990 --- /dev/null +++ b/cpp/ql/test/library-tests/ti_compiler/variable.ql @@ -0,0 +1,5 @@ +import cpp + +from Variable v, Type t +where t = v.getType() +select v, t, t.explain() diff --git a/cpp/ql/test/library-tests/type_sizes/a.cpp b/cpp/ql/test/library-tests/type_sizes/a.cpp new file mode 100644 index 000000000000..62eba0f16e1b --- /dev/null +++ b/cpp/ql/test/library-tests/type_sizes/a.cpp @@ -0,0 +1,49 @@ + +#define NULL ((void *)0) +typedef void *id; +int i; +unsigned long long ull; + +class ClassNoDef; + +class ClassWithDef { + int x; int y; +}; + +struct StructNoDef; +struct StructWithDef { + int x; int y; +}; + +typedef struct StructWithDef TypeDefStructWithDef; + +union UnionNoDef; +union UnionWithDef { + int x; int y; +}; + +void *voidStar; +char *charStar; + +char * const charStarConst = (char * const)NULL; +const char * constCharStar; +const char * const constCharStarConst = "test"; + +int &ref = i; + +int arr[3]; +int arrr[3][4]; + +typedef int v4si __attribute__ ((vector_size (16))); + +int fun(int x) {}; +int (*funptr)(int x); +int (&funref)(int x) = fun; + +int &&rvalref = i+1; + +typedef char *SEL; + +auto blockPtr = ^ int (int x, int y) {return x + y;}; + + diff --git a/cpp/ql/test/library-tests/type_sizes/options b/cpp/ql/test/library-tests/type_sizes/options new file mode 100644 index 000000000000..81574938c6ae --- /dev/null +++ b/cpp/ql/test/library-tests/type_sizes/options @@ -0,0 +1 @@ +extractor_flags: -fblocks diff --git a/cpp/ql/test/library-tests/type_sizes/type_sizes.expected b/cpp/ql/test/library-tests/type_sizes/type_sizes.expected new file mode 100644 index 000000000000..12a38db4f930 --- /dev/null +++ b/cpp/ql/test/library-tests/type_sizes/type_sizes.expected @@ -0,0 +1,90 @@ +| a.cpp:3:15:3:16 | id | 8 | +| a.cpp:7:7:7:16 | ClassNoDef | | +| a.cpp:9:7:9:18 | ClassWithDef | 8 | +| a.cpp:13:8:13:18 | StructNoDef | | +| a.cpp:14:8:14:20 | StructWithDef | 8 | +| a.cpp:18:30:18:49 | TypeDefStructWithDef | 8 | +| a.cpp:20:7:20:16 | UnionNoDef | | +| a.cpp:21:7:21:18 | UnionWithDef | 4 | +| a.cpp:37:13:37:16 | v4si | 16 | +| a.cpp:45:15:45:17 | SEL | 8 | +| file://:0:0:0:0 | ..(&)(..) | 1 | +| file://:0:0:0:0 | ..()(..) | | +| file://:0:0:0:0 | ..()(..) | | +| file://:0:0:0:0 | ..(*)(..) | 8 | +| file://:0:0:0:0 | ..(^)(..) | 8 | +| file://:0:0:0:0 | ClassWithDef & | 8 | +| file://:0:0:0:0 | ClassWithDef && | 8 | +| file://:0:0:0:0 | StructWithDef & | 8 | +| file://:0:0:0:0 | StructWithDef && | 8 | +| file://:0:0:0:0 | UnionWithDef & | 8 | +| file://:0:0:0:0 | UnionWithDef && | 8 | +| file://:0:0:0:0 | _Complex __float128 | 32 | +| file://:0:0:0:0 | _Complex double | 16 | +| file://:0:0:0:0 | _Complex float | 8 | +| file://:0:0:0:0 | _Complex long double | 32 | +| file://:0:0:0:0 | _Decimal32 | 4 | +| file://:0:0:0:0 | _Decimal64 | 8 | +| file://:0:0:0:0 | _Decimal128 | 16 | +| file://:0:0:0:0 | _Float32 | 4 | +| file://:0:0:0:0 | _Float32x | 8 | +| file://:0:0:0:0 | _Float64 | 8 | +| file://:0:0:0:0 | _Float64x | 16 | +| file://:0:0:0:0 | _Float128 | 16 | +| file://:0:0:0:0 | _Float128x | 16 | +| file://:0:0:0:0 | _Imaginary double | 8 | +| file://:0:0:0:0 | _Imaginary float | 4 | +| file://:0:0:0:0 | _Imaginary long double | 16 | +| file://:0:0:0:0 | __attribute((vector_size(16))) int | 16 | +| file://:0:0:0:0 | __float128 | 16 | +| file://:0:0:0:0 | __int128 | 16 | +| file://:0:0:0:0 | __va_list_tag | 24 | +| file://:0:0:0:0 | __va_list_tag & | 8 | +| file://:0:0:0:0 | auto | | +| file://:0:0:0:0 | bool | 1 | +| file://:0:0:0:0 | char | 1 | +| file://:0:0:0:0 | char16_t | 2 | +| file://:0:0:0:0 | char32_t | 4 | +| file://:0:0:0:0 | char * | 8 | +| file://:0:0:0:0 | char *const | 8 | +| file://:0:0:0:0 | char[5] | 5 | +| file://:0:0:0:0 | const ClassWithDef | 8 | +| file://:0:0:0:0 | const ClassWithDef & | 8 | +| file://:0:0:0:0 | const StructWithDef | 8 | +| file://:0:0:0:0 | const StructWithDef & | 8 | +| file://:0:0:0:0 | const UnionWithDef | 4 | +| file://:0:0:0:0 | const UnionWithDef & | 8 | +| file://:0:0:0:0 | const char | 1 | +| file://:0:0:0:0 | const char * | 8 | +| file://:0:0:0:0 | const char *const | 8 | +| file://:0:0:0:0 | const char[5] | 5 | +| file://:0:0:0:0 | decltype(nullptr) | 8 | +| file://:0:0:0:0 | double | 8 | +| file://:0:0:0:0 | error | 0 | +| file://:0:0:0:0 | float | 4 | +| file://:0:0:0:0 | int | 4 | +| file://:0:0:0:0 | int & | 8 | +| file://:0:0:0:0 | int && | 8 | +| file://:0:0:0:0 | int[3] | 12 | +| file://:0:0:0:0 | int[3][4] | 48 | +| file://:0:0:0:0 | int[4] | 16 | +| file://:0:0:0:0 | long | 8 | +| file://:0:0:0:0 | long double | 16 | +| file://:0:0:0:0 | long long | 8 | +| file://:0:0:0:0 | short | 2 | +| file://:0:0:0:0 | signed __int128 | 16 | +| file://:0:0:0:0 | signed char | 1 | +| file://:0:0:0:0 | signed int | 4 | +| file://:0:0:0:0 | signed long | 8 | +| file://:0:0:0:0 | signed long long | 8 | +| file://:0:0:0:0 | signed short | 2 | +| file://:0:0:0:0 | unknown | 0 | +| file://:0:0:0:0 | unsigned __int128 | 16 | +| file://:0:0:0:0 | unsigned char | 1 | +| file://:0:0:0:0 | unsigned int | 4 | +| file://:0:0:0:0 | unsigned long | 8 | +| file://:0:0:0:0 | unsigned long long | 8 | +| file://:0:0:0:0 | unsigned short | 2 | +| file://:0:0:0:0 | void | 0 | +| file://:0:0:0:0 | void * | 8 | +| file://:0:0:0:0 | wchar_t | 4 | diff --git a/cpp/ql/test/library-tests/type_sizes/type_sizes.ql b/cpp/ql/test/library-tests/type_sizes/type_sizes.ql new file mode 100644 index 000000000000..e687f24bc3c8 --- /dev/null +++ b/cpp/ql/test/library-tests/type_sizes/type_sizes.ql @@ -0,0 +1,7 @@ +import cpp + +from Type t, string s +where if exists(t.getSize().toString()) + then s = t.getSize().toString() + else s = "" +select t, s diff --git a/cpp/ql/test/library-tests/type_strings/exprs.expected b/cpp/ql/test/library-tests/type_strings/exprs.expected new file mode 100644 index 000000000000..5a771796a187 --- /dev/null +++ b/cpp/ql/test/library-tests/type_strings/exprs.expected @@ -0,0 +1,83 @@ +| type_strings.cpp:6:59:6:60 | 10 | int | int | +| type_strings.cpp:11:39:11:41 | 120 | char | char | +| type_strings.cpp:18:12:18:14 | 99 | char | char | +| type_strings.cpp:23:28:23:35 | & ... | ..(*)(..) | pointer to {function returning {char} with arguments (int,int)} | +| type_strings.cpp:23:29:23:35 | someFun | ..()(..) | function returning {char} with arguments (int,int) | +| type_strings.cpp:24:29:24:35 | (reference to) | ..(&)(..) | reference to {function returning {char} with arguments (int,int)} | +| type_strings.cpp:24:29:24:35 | someFun | ..()(..) | function returning {char} with arguments (int,int) | +| type_strings.cpp:25:5:25:5 | c | char | char | +| type_strings.cpp:25:5:25:16 | ... = ... | char | char | +| type_strings.cpp:25:9:25:10 | fp | ..(*)(..) | pointer to {function returning {char} with arguments (int,int)} | +| type_strings.cpp:25:9:25:16 | call to expression | char | char | +| type_strings.cpp:25:12:25:12 | 4 | int | int | +| type_strings.cpp:25:15:25:15 | 5 | int | int | +| type_strings.cpp:26:5:26:5 | c | char | char | +| type_strings.cpp:26:5:26:19 | ... = ... | char | char | +| type_strings.cpp:26:9:26:13 | (...) | ..(*)(..) | pointer to {function returning {char} with arguments (int,int)} | +| type_strings.cpp:26:9:26:19 | call to expression | char | char | +| type_strings.cpp:26:10:26:12 | * ... | ..(*)(..) | pointer to {function returning {char} with arguments (int,int)} | +| type_strings.cpp:26:11:26:12 | fp | ..(*)(..) | pointer to {function returning {char} with arguments (int,int)} | +| type_strings.cpp:26:15:26:15 | 4 | int | int | +| type_strings.cpp:26:18:26:18 | 5 | int | int | +| type_strings.cpp:27:5:27:5 | c | char | char | +| type_strings.cpp:27:5:27:22 | ... = ... | char | char | +| type_strings.cpp:27:9:27:16 | (...) | ..(*)(..) | pointer to {function returning {char} with arguments (int,int)} | +| type_strings.cpp:27:9:27:22 | call to expression | char | char | +| type_strings.cpp:27:10:27:15 | * ... | ..(*)(..) | pointer to {function returning {char} with arguments (int,int)} | +| type_strings.cpp:27:11:27:15 | (...) | ..(*)(..) | pointer to {function returning {char} with arguments (int,int)} | +| type_strings.cpp:27:12:27:14 | * ... | ..(*)(..) | pointer to {function returning {char} with arguments (int,int)} | +| type_strings.cpp:27:13:27:14 | fp | ..(*)(..) | pointer to {function returning {char} with arguments (int,int)} | +| type_strings.cpp:27:18:27:18 | 4 | int | int | +| type_strings.cpp:27:21:27:21 | 5 | int | int | +| type_strings.cpp:28:5:28:5 | c | char | char | +| type_strings.cpp:28:5:28:25 | ... = ... | char | char | +| type_strings.cpp:28:9:28:19 | (...) | ..(*)(..) | pointer to {function returning {char} with arguments (int,int)} | +| type_strings.cpp:28:9:28:25 | call to expression | char | char | +| type_strings.cpp:28:10:28:18 | * ... | ..(*)(..) | pointer to {function returning {char} with arguments (int,int)} | +| type_strings.cpp:28:11:28:18 | (...) | ..(*)(..) | pointer to {function returning {char} with arguments (int,int)} | +| type_strings.cpp:28:12:28:17 | * ... | ..(*)(..) | pointer to {function returning {char} with arguments (int,int)} | +| type_strings.cpp:28:13:28:17 | (...) | ..(*)(..) | pointer to {function returning {char} with arguments (int,int)} | +| type_strings.cpp:28:14:28:16 | * ... | ..(*)(..) | pointer to {function returning {char} with arguments (int,int)} | +| type_strings.cpp:28:15:28:16 | fp | ..(*)(..) | pointer to {function returning {char} with arguments (int,int)} | +| type_strings.cpp:28:21:28:21 | 4 | int | int | +| type_strings.cpp:28:24:28:24 | 5 | int | int | +| type_strings.cpp:29:5:29:5 | c | char | char | +| type_strings.cpp:29:5:29:21 | ... = ... | char | char | +| type_strings.cpp:29:9:29:15 | (...) | ..(*)(..) | pointer to {function returning {char} with arguments (int,int)} | +| type_strings.cpp:29:9:29:21 | call to expression | char | char | +| type_strings.cpp:29:10:29:14 | * ... | ..(*)(..) | pointer to {function returning {char} with arguments (int,int)} | +| type_strings.cpp:29:11:29:14 | * ... | ..(*)(..) | pointer to {function returning {char} with arguments (int,int)} | +| type_strings.cpp:29:12:29:14 | * ... | ..(*)(..) | pointer to {function returning {char} with arguments (int,int)} | +| type_strings.cpp:29:13:29:14 | fp | ..(*)(..) | pointer to {function returning {char} with arguments (int,int)} | +| type_strings.cpp:29:17:29:17 | 4 | int | int | +| type_strings.cpp:29:20:29:20 | 5 | int | int | +| type_strings.cpp:30:5:30:5 | c | char | char | +| type_strings.cpp:30:5:30:17 | ... = ... | char | char | +| type_strings.cpp:30:9:30:11 | (reference dereference) | ..(*)(..) | pointer to {function returning {char} with arguments (int,int)} | +| type_strings.cpp:30:9:30:11 | fp2 | ..(&)(..) | reference to {function returning {char} with arguments (int,int)} | +| type_strings.cpp:30:9:30:17 | call to expression | char | char | +| type_strings.cpp:30:13:30:13 | 4 | int | int | +| type_strings.cpp:30:16:30:16 | 5 | int | int | +| type_strings.cpp:31:5:31:5 | c | char | char | +| type_strings.cpp:31:5:31:20 | ... = ... | char | char | +| type_strings.cpp:31:9:31:14 | (...) | ..(*)(..) | pointer to {function returning {char} with arguments (int,int)} | +| type_strings.cpp:31:9:31:20 | call to expression | char | char | +| type_strings.cpp:31:10:31:13 | * ... | ..(*)(..) | pointer to {function returning {char} with arguments (int,int)} | +| type_strings.cpp:31:11:31:13 | (reference dereference) | ..(*)(..) | pointer to {function returning {char} with arguments (int,int)} | +| type_strings.cpp:31:11:31:13 | fp2 | ..(&)(..) | reference to {function returning {char} with arguments (int,int)} | +| type_strings.cpp:31:16:31:16 | 4 | int | int | +| type_strings.cpp:31:19:31:19 | 5 | int | int | +| type_strings.cpp:32:5:32:5 | c | char | char | +| type_strings.cpp:32:5:32:22 | ... = ... | char | char | +| type_strings.cpp:32:9:32:16 | (...) | ..(*)(..) | pointer to {function returning {char} with arguments (int,int)} | +| type_strings.cpp:32:9:32:22 | call to expression | char | char | +| type_strings.cpp:32:10:32:15 | * ... | ..(*)(..) | pointer to {function returning {char} with arguments (int,int)} | +| type_strings.cpp:32:11:32:15 | * ... | ..(*)(..) | pointer to {function returning {char} with arguments (int,int)} | +| type_strings.cpp:32:12:32:15 | * ... | ..(*)(..) | pointer to {function returning {char} with arguments (int,int)} | +| type_strings.cpp:32:13:32:15 | (reference dereference) | ..(*)(..) | pointer to {function returning {char} with arguments (int,int)} | +| type_strings.cpp:32:13:32:15 | fp2 | ..(&)(..) | reference to {function returning {char} with arguments (int,int)} | +| type_strings.cpp:32:18:32:18 | 4 | int | int | +| type_strings.cpp:32:21:32:21 | 5 | int | int | +| type_strings.cpp:35:8:35:9 | 10 | int | int | +| type_strings.cpp:35:12:35:13 | 20 | int | int | +| type_strings.cpp:35:16:35:17 | 30 | int | int | diff --git a/cpp/ql/test/library-tests/type_strings/exprs.ql b/cpp/ql/test/library-tests/type_strings/exprs.ql new file mode 100644 index 000000000000..2a036139d21b --- /dev/null +++ b/cpp/ql/test/library-tests/type_strings/exprs.ql @@ -0,0 +1,7 @@ +import cpp + +from Expr e +where not (e instanceof CStyleCast and e.getValue().matches("%0")) +select e, + e.getType().toString(), + e.getType().explain() diff --git a/cpp/ql/test/library-tests/type_strings/ms_types.cpp b/cpp/ql/test/library-tests/type_strings/ms_types.cpp new file mode 100644 index 000000000000..7e92a5833422 --- /dev/null +++ b/cpp/ql/test/library-tests/type_strings/ms_types.cpp @@ -0,0 +1,9 @@ +int ms_int; +int __w64 ms_w64_int; +int* __w64 ms_w64_int_ptr; +int* ms_int_ptr; + +int ( *ms_fptr1)(void); +int (__cdecl *ms_fptr2)(void); +int (__stdcall *ms_fptr3)(void); +// semmle-extractor-options: --microsoft diff --git a/cpp/ql/test/library-tests/type_strings/type_strings.cpp b/cpp/ql/test/library-tests/type_strings/type_strings.cpp new file mode 100644 index 000000000000..3adb53e3016e --- /dev/null +++ b/cpp/ql/test/library-tests/type_strings/type_strings.cpp @@ -0,0 +1,36 @@ +// Compilable with: +// gcc -c type_strings.cpp + +void (*functionPtrReturningVoidWithVoidArgument)(void); +int (*functionPtrReturningIntWithIntAndCharArguments)(int, char); +void (*arrayOfTenFunctionPtrsReturningVoidWithIntArgument[10])(int); + +class Foo { + public: + Foo (int i){ } + char f(int x, int y) { return 'x'; } +}; + +Foo *fooPointer; +char (Foo::*fPointer)(int x, int y); + +char someFun(int x, int y) { + return 'c'; +} + +void test(void) { + char c; + char (*fp)(int, int) = &someFun; + char (&fp2)(int, int) = someFun; + c = fp(4, 5); + c = (*fp)(4, 5); + c = (*(*fp))(4, 5); + c = (*(*(*fp)))(4, 5); + c = (***fp)(4, 5); + c = fp2(4, 5); + c = (*fp2)(4, 5); + c = (***fp2)(4, 5); +} + +int xs[10][20][30]; + diff --git a/cpp/ql/test/library-tests/type_strings/type_strings.expected b/cpp/ql/test/library-tests/type_strings/type_strings.expected new file mode 100644 index 000000000000..30eb20f79ddc --- /dev/null +++ b/cpp/ql/test/library-tests/type_strings/type_strings.expected @@ -0,0 +1,23 @@ +| file://:0:0:0:0 | operator= | __va_list_tag & | reference to {struct __va_list_tag} | +| file://:0:0:0:0 | operator= | __va_list_tag & | reference to {struct __va_list_tag} | +| ms_types.cpp:1:12:1:17 | ms_int | int | int | +| ms_types.cpp:2:12:2:21 | ms_w64_int | int | int | +| ms_types.cpp:3:12:3:25 | ms_w64_int_ptr | int * | pointer to {int} | +| ms_types.cpp:4:12:4:21 | ms_int_ptr | int * | pointer to {int} | +| ms_types.cpp:6:17:6:24 | ms_fptr1 | ..(*)(..) | pointer to {function returning {int} with arguments ()} | +| ms_types.cpp:7:17:7:24 | ms_fptr2 | ..(*)(..) | pointer to {function returning {int} with arguments ()} | +| ms_types.cpp:8:17:8:24 | ms_fptr3 | ..(*)(..) | pointer to {function returning {int} with arguments ()} | +| type_strings.cpp:4:8:4:47 | functionPtrReturningVoidWithVoidArgument | ..(*)(..) | pointer to {function returning {void} with arguments ()} | +| type_strings.cpp:5:7:5:52 | functionPtrReturningIntWithIntAndCharArguments | ..(*)(..) | pointer to {function returning {int} with arguments (int,char)} | +| type_strings.cpp:6:8:6:57 | arrayOfTenFunctionPtrsReturningVoidWithIntArgument | ..(*[10])(..) | array of 10 {pointer to {function returning {void} with arguments (int)}} | +| type_strings.cpp:8:7:8:7 | Foo | void | void | +| type_strings.cpp:8:7:8:7 | Foo | void | void | +| type_strings.cpp:8:7:8:7 | operator= | Foo & | reference to {class Foo} | +| type_strings.cpp:8:7:8:7 | operator= | Foo & | reference to {class Foo} | +| type_strings.cpp:10:9:10:11 | Foo | void | void | +| type_strings.cpp:11:14:11:14 | f | char | char | +| type_strings.cpp:14:6:14:15 | fooPointer | Foo * | pointer to {class Foo} | +| type_strings.cpp:15:13:15:20 | fPointer | ..:: * | pointer to member of Foo with type {function returning {char} with arguments (int,int)} | +| type_strings.cpp:17:6:17:12 | someFun | char | char | +| type_strings.cpp:21:6:21:9 | test | void | void | +| type_strings.cpp:35:5:35:6 | xs | int[10][20][30] | array of 10 {array of 20 {array of 30 {int}}} | diff --git a/cpp/ql/test/library-tests/type_strings/type_strings.ql b/cpp/ql/test/library-tests/type_strings/type_strings.ql new file mode 100644 index 000000000000..f874534c0919 --- /dev/null +++ b/cpp/ql/test/library-tests/type_strings/type_strings.ql @@ -0,0 +1,12 @@ +import cpp + +Type getDeclType(Declaration d) { + result = ((GlobalVariable)d).getType() or + result = ((Function )d).getType() +} + +from Declaration d +select d, + getDeclType(d).toString(), + getDeclType(d).explain() + diff --git a/cpp/ql/test/library-tests/typedefs/ODASA-6095-A.hpp b/cpp/ql/test/library-tests/typedefs/ODASA-6095-A.hpp new file mode 100644 index 000000000000..a6fa8fd0c8c9 --- /dev/null +++ b/cpp/ql/test/library-tests/typedefs/ODASA-6095-A.hpp @@ -0,0 +1,6 @@ +template +struct MyTemplate { + MyTemplate() {} + + typedef MyTemplate mytype; +}; diff --git a/cpp/ql/test/library-tests/typedefs/ODASA-6095-B.hpp b/cpp/ql/test/library-tests/typedefs/ODASA-6095-B.hpp new file mode 100644 index 000000000000..9dd7da1d8708 --- /dev/null +++ b/cpp/ql/test/library-tests/typedefs/ODASA-6095-B.hpp @@ -0,0 +1,9 @@ +#include "ODASA-6095-A.hpp" + +template +struct MyTemplate2 { + static X field; + typedef decltype(MyTemplate2::field) mytype2; +}; + +extern template class MyTemplate::mytype2>; diff --git a/cpp/ql/test/library-tests/typedefs/ODASA-6095.cpp b/cpp/ql/test/library-tests/typedefs/ODASA-6095.cpp new file mode 100644 index 000000000000..d9227d2abf3c --- /dev/null +++ b/cpp/ql/test/library-tests/typedefs/ODASA-6095.cpp @@ -0,0 +1 @@ +#include "ODASA-6095-B.hpp" diff --git a/cpp/ql/test/library-tests/typedefs/ODASA-6095.expected b/cpp/ql/test/library-tests/typedefs/ODASA-6095.expected new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/cpp/ql/test/library-tests/typedefs/ODASA-6095.ql b/cpp/ql/test/library-tests/typedefs/ODASA-6095.ql new file mode 100644 index 000000000000..d1c39010917e --- /dev/null +++ b/cpp/ql/test/library-tests/typedefs/ODASA-6095.ql @@ -0,0 +1,4 @@ +import cpp +from TypedefType t +where not exists (t.getBaseType().getUnspecifiedType()) +select t.getUnspecifiedType(), t.getUnspecifiedType().getFile() diff --git a/cpp/ql/test/library-tests/typedefs/Typedefs1.expected b/cpp/ql/test/library-tests/typedefs/Typedefs1.expected new file mode 100644 index 000000000000..21b506c015a4 --- /dev/null +++ b/cpp/ql/test/library-tests/typedefs/Typedefs1.expected @@ -0,0 +1,9 @@ +| file://:0:0:0:0 | (global namespace) | ODASA-6095-A.hpp:2:8:2:17 | MyTemplate | +| file://:0:0:0:0 | (global namespace) | ODASA-6095-A.hpp:2:8:2:17 | MyTemplate | +| file://:0:0:0:0 | (global namespace) | ODASA-6095-B.hpp:4:8:4:18 | MyTemplate2 | +| file://:0:0:0:0 | (global namespace) | ODASA-6095-B.hpp:4:8:4:18 | MyTemplate2 | +| file://:0:0:0:0 | (global namespace) | file://:0:0:0:0 | __va_list_tag | +| file://:0:0:0:0 | (global namespace) | typedefs.cpp:6:6:6:7 | f1 | +| file://:0:0:0:0 | (global namespace) | typedefs.cpp:12:7:12:7 | A | +| file://:0:0:0:0 | (global namespace) | typedefs.cpp:26:7:26:7 | B | +| typedefs.cpp:1:11:1:13 | NS1 | typedefs.cpp:3:15:3:19 | WIDTH | diff --git a/cpp/ql/test/library-tests/typedefs/Typedefs1.ql b/cpp/ql/test/library-tests/typedefs/Typedefs1.ql new file mode 100644 index 000000000000..97542ce285c1 --- /dev/null +++ b/cpp/ql/test/library-tests/typedefs/Typedefs1.ql @@ -0,0 +1,4 @@ +import cpp + +from Namespace n +select n, n.getADeclaration() diff --git a/cpp/ql/test/library-tests/typedefs/Typedefs2.expected b/cpp/ql/test/library-tests/typedefs/Typedefs2.expected new file mode 100644 index 000000000000..fb034dbec9f2 --- /dev/null +++ b/cpp/ql/test/library-tests/typedefs/Typedefs2.expected @@ -0,0 +1,2 @@ +| typedefs.cpp:6:6:6:7 | f1 | typedefs.cpp:8:15:8:18 | TYPE | LocalTypedefType | +| typedefs.cpp:6:6:6:7 | f1 | typedefs.cpp:9:9:9:9 | D | LocalClass, MetricClass, StructLikeClass | diff --git a/cpp/ql/test/library-tests/typedefs/Typedefs2.ql b/cpp/ql/test/library-tests/typedefs/Typedefs2.ql new file mode 100644 index 000000000000..fafe9ec44d23 --- /dev/null +++ b/cpp/ql/test/library-tests/typedefs/Typedefs2.ql @@ -0,0 +1,6 @@ +import cpp + +from Function f1, Block body, Declaration d +where body = f1.getBlock() + and d = body.getADeclaration() +select f1, d, concat(d.getAQlClass(), ", ") diff --git a/cpp/ql/test/library-tests/typedefs/Typedefs3.expected b/cpp/ql/test/library-tests/typedefs/Typedefs3.expected new file mode 100644 index 000000000000..f3af3c430a00 --- /dev/null +++ b/cpp/ql/test/library-tests/typedefs/Typedefs3.expected @@ -0,0 +1,12 @@ +WARNING: Predicate isPrivate has been deprecated and may be removed in future (Typedefs3.ql:11,27-36) +WARNING: Predicate isProtected has been deprecated and may be removed in future (Typedefs3.ql:14,27-38) +WARNING: Predicate isPublic has been deprecated and may be removed in future (Typedefs3.ql:17,27-35) +| ODASA-6095-A.hpp:5:25:5:30 | mytype | MyTemplate::mytype | NestedTypedefType, getBaseType() = MyTemplate, member of MyTemplate | +| ODASA-6095-B.hpp:6:43:6:49 | mytype2 | MyTemplate2::mytype2 | NestedTypedefType, getBaseType() = decltype(...), member of MyTemplate2 | +| file://:0:0:0:0 | mytype | MyTemplate::mytype | NestedTypedefType, getBaseType() = MyTemplate, member of MyTemplate | +| file://:0:0:0:0 | mytype2 | MyTemplate2::mytype2 | NestedTypedefType, getBaseType() = decltype(...), member of MyTemplate2 | +| typedefs.cpp:3:15:3:19 | WIDTH | NS1::WIDTH | getBaseType() = int | +| typedefs.cpp:16:21:16:25 | WIDTH | A::WIDTH | NestedTypedefType, getBaseType() = NS1::WIDTH, member of A | +| typedefs.cpp:17:16:17:21 | WIDTH2 | A::WIDTH2 | NestedTypedefType, getBaseType() = A::WIDTH, member of A | +| typedefs.cpp:20:16:20:21 | WIDTH3 | A::WIDTH3 | NestedTypedefType, getBaseType() = A::WIDTH, member of A | +| typedefs.cpp:23:16:23:21 | WIDTH4 | A::WIDTH4 | NestedTypedefType, getBaseType() = A::WIDTH, member of A | diff --git a/cpp/ql/test/library-tests/typedefs/Typedefs3.ql b/cpp/ql/test/library-tests/typedefs/Typedefs3.ql new file mode 100644 index 000000000000..8345b494ddba --- /dev/null +++ b/cpp/ql/test/library-tests/typedefs/Typedefs3.ql @@ -0,0 +1,36 @@ +import cpp + +string describe(TypedefType t) { + ( + t instanceof LocalTypedefType and + result = "LocalTypedefType" + ) or ( + t instanceof NestedTypedefType and + result = "NestedTypedefType" + ) or ( + t.(NestedTypedefType).isPrivate() and + result = "(NestedTypedefType).isPrivate()" + ) or ( + t.(NestedTypedefType).isProtected() and + result = "(NestedTypedefType).isProtected()" + ) or ( + t.(NestedTypedefType).isPublic() and + result = "(NestedTypedefType).isPublic()" + ) or exists(Type base | + base = t.getBaseType() and + ( + ( + result = "getBaseType() = " + base.(Declaration).getQualifiedName() + ) or ( + not base instanceof Declaration and + result = "getBaseType() = " + base.toString() + ) + ) + ) or exists(Class c | + c.getAMember() = t and + result = "member of " + c.toString() + ) +} + +from TypedefType t +select t, t.getQualifiedName(), concat(describe(t), ", ") diff --git a/cpp/ql/test/library-tests/typedefs/typedefs.cpp b/cpp/ql/test/library-tests/typedefs/typedefs.cpp new file mode 100644 index 000000000000..37043e5cf46f --- /dev/null +++ b/cpp/ql/test/library-tests/typedefs/typedefs.cpp @@ -0,0 +1,31 @@ +namespace NS1 +{ + typedef int WIDTH; +} + +void f1() +{ + typedef int TYPE; + class D {}; +} + +class A +{ + +protected: + typedef NS1::WIDTH WIDTH; + typedef WIDTH WIDTH2; + +public: + typedef WIDTH WIDTH3; + +private: + typedef WIDTH WIDTH4; +}; + +class B: public A { + WIDTH i; + WIDTH2 i2; + WIDTH3 i3; + //WIDTH4 i4; --- inaccessible +}; diff --git a/cpp/ql/test/library-tests/typename/typename.cpp b/cpp/ql/test/library-tests/typename/typename.cpp new file mode 100644 index 000000000000..b9006cd996ab --- /dev/null +++ b/cpp/ql/test/library-tests/typename/typename.cpp @@ -0,0 +1,17 @@ + +class myClass +{ +public: + int x, y; +}; + +template +int myFunction() +{ + return __is_pod(T); +} + +int main() +{ + return __is_pod(int) + __has_trivial_destructor(myClass) + myFunction(); +} diff --git a/cpp/ql/test/library-tests/typename/typename.expected b/cpp/ql/test/library-tests/typename/typename.expected new file mode 100644 index 000000000000..09bff6e139e5 --- /dev/null +++ b/cpp/ql/test/library-tests/typename/typename.expected @@ -0,0 +1,4 @@ +| file://:0:0:0:0 | T | +| file://:0:0:0:0 | int | +| file://:0:0:0:0 | myClass | +| file://:0:0:0:0 | short | diff --git a/cpp/ql/test/library-tests/typename/typename.ql b/cpp/ql/test/library-tests/typename/typename.ql new file mode 100644 index 000000000000..a3b546842950 --- /dev/null +++ b/cpp/ql/test/library-tests/typename/typename.ql @@ -0,0 +1,4 @@ +import cpp + +from TypeName t +select t diff --git a/cpp/ql/test/library-tests/types/__wchar_t/ms.cpp b/cpp/ql/test/library-tests/types/__wchar_t/ms.cpp new file mode 100644 index 000000000000..38f3a0f11f73 --- /dev/null +++ b/cpp/ql/test/library-tests/types/__wchar_t/ms.cpp @@ -0,0 +1,5 @@ + +typedef wchar_t WCHAR; +typedef wchar_t *PWSTR; + +const wchar_t cwc = 0; diff --git a/cpp/ql/test/library-tests/types/__wchar_t/options b/cpp/ql/test/library-tests/types/__wchar_t/options new file mode 100644 index 000000000000..1acaf0e0c515 --- /dev/null +++ b/cpp/ql/test/library-tests/types/__wchar_t/options @@ -0,0 +1 @@ +extractor_flags: --microsoft diff --git a/cpp/ql/test/library-tests/types/__wchar_t/wchar_t.expected b/cpp/ql/test/library-tests/types/__wchar_t/wchar_t.expected new file mode 100644 index 000000000000..84106f07a920 --- /dev/null +++ b/cpp/ql/test/library-tests/types/__wchar_t/wchar_t.expected @@ -0,0 +1,3 @@ +| file://:0:0:0:0 | __wchar_t * | PointerType | Wchar_t, WideCharType | +| file://:0:0:0:0 | const __wchar_t | SpecifiedType | Wchar_t, WideCharType | +| file://:0:0:0:0 | wchar_t | Wchar_t, WideCharType | | diff --git a/cpp/ql/test/library-tests/types/__wchar_t/wchar_t.ql b/cpp/ql/test/library-tests/types/__wchar_t/wchar_t.ql new file mode 100644 index 000000000000..d546ee79ce77 --- /dev/null +++ b/cpp/ql/test/library-tests/types/__wchar_t/wchar_t.ql @@ -0,0 +1,9 @@ +import cpp + +from Type t +where + t.getName().matches("%wchar%") +select + t, + concat(t.getAQlClass(), ", "), + concat(t.(DerivedType).getBaseType().getAQlClass(), ", ") diff --git a/cpp/ql/test/library-tests/types/alignof/alignof.cpp b/cpp/ql/test/library-tests/types/alignof/alignof.cpp new file mode 100644 index 000000000000..66cc4098f351 --- /dev/null +++ b/cpp/ql/test/library-tests/types/alignof/alignof.cpp @@ -0,0 +1,29 @@ + +typedef unsigned int size_t; + +class MyClass +{ +public: + int x; + char c; + int *ptr; +}; + +void func() { + int i; + char c; + int * ptr; + MyClass mc; + int arr[10]; + + size_t sz1 = alignof(int); + size_t sz2 = alignof(char); + size_t sz3 = alignof(int *); + size_t sz4 = alignof(MyClass); + size_t sz5 = alignof(i); + size_t sz6 = alignof(c); + size_t sz7 = alignof(ptr); + size_t sz8 = alignof(mc); + size_t sz9 = alignof(arr); + size_t sz10 = alignof(arr[4]); +} diff --git a/cpp/ql/test/library-tests/types/alignof/alignof.expected b/cpp/ql/test/library-tests/types/alignof/alignof.expected new file mode 100644 index 000000000000..39dc7a43343b --- /dev/null +++ b/cpp/ql/test/library-tests/types/alignof/alignof.expected @@ -0,0 +1,10 @@ +| alignof.cpp:19:15:19:26 | alignof(int) | AlignofTypeOperator.getTypeOperand() | file://:0:0:0:0 | int | +| alignof.cpp:20:15:20:27 | alignof(char) | AlignofTypeOperator.getTypeOperand() | file://:0:0:0:0 | char | +| alignof.cpp:21:15:21:28 | alignof(int *) | AlignofTypeOperator.getTypeOperand() | file://:0:0:0:0 | int * | +| alignof.cpp:22:15:22:30 | alignof(MyClass) | AlignofTypeOperator.getTypeOperand() | alignof.cpp:4:7:4:13 | MyClass | +| alignof.cpp:23:15:23:24 | alignof() | AlignofExprOperator.getExprOperand() | alignof.cpp:23:23:23:23 | i | +| alignof.cpp:24:15:24:24 | alignof() | AlignofExprOperator.getExprOperand() | alignof.cpp:24:23:24:23 | c | +| alignof.cpp:25:15:25:26 | alignof() | AlignofExprOperator.getExprOperand() | alignof.cpp:25:23:25:25 | ptr | +| alignof.cpp:26:15:26:25 | alignof() | AlignofExprOperator.getExprOperand() | alignof.cpp:26:23:26:24 | mc | +| alignof.cpp:27:15:27:26 | alignof() | AlignofExprOperator.getExprOperand() | alignof.cpp:27:23:27:25 | arr | +| alignof.cpp:28:16:28:30 | alignof() | AlignofExprOperator.getExprOperand() | alignof.cpp:28:24:28:29 | access to array | diff --git a/cpp/ql/test/library-tests/types/alignof/alignof.ql b/cpp/ql/test/library-tests/types/alignof/alignof.ql new file mode 100644 index 000000000000..49927bff49eb --- /dev/null +++ b/cpp/ql/test/library-tests/types/alignof/alignof.ql @@ -0,0 +1,12 @@ +import cpp + +from AlignofOperator sto, string elemDesc, Element e +where + ( + elemDesc = "AlignofTypeOperator.getTypeOperand()" and + e = sto.(AlignofTypeOperator).getTypeOperand() + ) or ( + elemDesc = "AlignofExprOperator.getExprOperand()" and + e = sto.(AlignofExprOperator).getExprOperand() + ) +select sto, elemDesc, e diff --git a/cpp/ql/test/library-tests/types/integral_types/integral_type.expected b/cpp/ql/test/library-tests/types/integral_types/integral_type.expected new file mode 100644 index 000000000000..3b6f9d09cb66 --- /dev/null +++ b/cpp/ql/test/library-tests/types/integral_types/integral_type.expected @@ -0,0 +1,22 @@ +| 4 | bool | ------ | -------- | ---------------- | ------------------ | ---------------- | 1 | 1 | | | +| 5 | char | signed | -------- | ---------------- | ------------------ | implicitlySigned | 1 | 1 | unsigned char | MicrosoftInt8Type | +| 6 | unsigned char | ------ | unsigned | ---------------- | explicitlyUnsigned | ---------------- | 1 | 1 | unsigned char | | +| 7 | signed char | signed | -------- | explicitlySigned | ------------------ | ---------------- | 1 | 1 | unsigned char | | +| 8 | short | signed | -------- | ---------------- | ------------------ | implicitlySigned | 2 | 2 | unsigned short | MicrosoftInt16Type | +| 9 | unsigned short | ------ | unsigned | ---------------- | explicitlyUnsigned | ---------------- | 2 | 2 | unsigned short | | +| 10 | signed short | signed | -------- | explicitlySigned | ------------------ | ---------------- | 2 | 2 | unsigned short | | +| 11 | int | signed | -------- | ---------------- | ------------------ | implicitlySigned | 4 | 4 | unsigned int | MicrosoftInt32Type | +| 12 | unsigned int | ------ | unsigned | ---------------- | explicitlyUnsigned | ---------------- | 4 | 4 | unsigned int | | +| 13 | signed int | signed | -------- | explicitlySigned | ------------------ | ---------------- | 4 | 4 | unsigned int | | +| 14 | long | signed | -------- | ---------------- | ------------------ | implicitlySigned | 8 | 8 | unsigned long | | +| 15 | unsigned long | ------ | unsigned | ---------------- | explicitlyUnsigned | ---------------- | 8 | 8 | unsigned long | | +| 16 | signed long | signed | -------- | explicitlySigned | ------------------ | ---------------- | 8 | 8 | unsigned long | | +| 17 | long long | signed | -------- | ---------------- | ------------------ | implicitlySigned | 8 | 8 | unsigned long long | MicrosoftInt64Type | +| 18 | unsigned long long | ------ | unsigned | ---------------- | explicitlyUnsigned | ---------------- | 8 | 8 | unsigned long long | | +| 19 | signed long long | signed | -------- | explicitlySigned | ------------------ | ---------------- | 8 | 8 | unsigned long long | | +| 33 | wchar_t | ------ | -------- | ---------------- | ------------------ | ---------------- | 4 | 4 | | | +| 35 | __int128 | signed | -------- | ---------------- | ------------------ | implicitlySigned | 16 | 16 | unsigned __int128 | | +| 36 | unsigned __int128 | ------ | unsigned | ---------------- | explicitlyUnsigned | ---------------- | 16 | 16 | unsigned __int128 | | +| 37 | signed __int128 | signed | -------- | explicitlySigned | ------------------ | ---------------- | 16 | 16 | unsigned __int128 | | +| 43 | char16_t | ------ | -------- | ---------------- | ------------------ | ---------------- | 2 | 2 | | | +| 44 | char32_t | ------ | -------- | ---------------- | ------------------ | ---------------- | 4 | 4 | | | diff --git a/cpp/ql/test/library-tests/types/integral_types/integral_type.ql b/cpp/ql/test/library-tests/types/integral_types/integral_type.ql new file mode 100644 index 000000000000..325dd8fd58a9 --- /dev/null +++ b/cpp/ql/test/library-tests/types/integral_types/integral_type.ql @@ -0,0 +1,47 @@ +import cpp + +string integralTypeKind(IntegralType t) { + if exists(int kind | builtintypes(t, _, kind, _, _, _)) + then exists(int kind | builtintypes(t, _, kind, _, _, _) | result = (kind.toString() + " ").prefix(2)) + else result = "--" +} + +string getUnsignedType(IntegralType t) { + if exists(t.getUnsigned()) + then result = t.getUnsigned().toString() + else result = "" +} + +string describe(IntegralType t) { + ( + t instanceof MicrosoftInt8Type and + result = "MicrosoftInt8Type" + ) or ( + t instanceof MicrosoftInt16Type and + result = "MicrosoftInt16Type" + ) or ( + t instanceof MicrosoftInt32Type and + result = "MicrosoftInt32Type" + ) or ( + t instanceof MicrosoftInt64Type and + result = "MicrosoftInt64Type" + ) +} + +from IntegralType t, string tStr, string signed, string unsigned, + string explicitlySigned, string explicitlyUnsigned, + string implicitlySigned +where tStr = (t.toString() + " ").prefix(18) + and if t.isSigned() then signed = "signed" + else signed = "------" + and if t.isUnsigned() then unsigned = "unsigned" + else unsigned = "--------" + and if t.isExplicitlySigned() then explicitlySigned = "explicitlySigned" + else explicitlySigned = "----------------" + and if t.isExplicitlyUnsigned() then explicitlyUnsigned = "explicitlyUnsigned" + else explicitlyUnsigned = "------------------" + and if t.isImplicitlySigned() then implicitlySigned = "implicitlySigned" + else implicitlySigned = "----------------" +select integralTypeKind(t), tStr, signed, unsigned, + explicitlySigned, explicitlyUnsigned, + implicitlySigned, t.getSize(), t.getAlignment(), getUnsignedType(t), concat(describe(t), ", ") diff --git a/cpp/ql/test/library-tests/types/integral_types/integral_types.cpp b/cpp/ql/test/library-tests/types/integral_types/integral_types.cpp new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/cpp/ql/test/library-tests/types/integral_types_ms/integral_type.expected b/cpp/ql/test/library-tests/types/integral_types_ms/integral_type.expected new file mode 100644 index 000000000000..8f5ca495f7f3 --- /dev/null +++ b/cpp/ql/test/library-tests/types/integral_types_ms/integral_type.expected @@ -0,0 +1,22 @@ +| 4 | bool | ------ | -------- | ---------------- | ------------------ | ---------------- | 1 | 1 | | | +| 5 | char | signed | -------- | ---------------- | ------------------ | implicitlySigned | 1 | 1 | unsigned char | MicrosoftInt8Type | +| 6 | unsigned char | ------ | unsigned | ---------------- | explicitlyUnsigned | ---------------- | 1 | 1 | unsigned char | | +| 7 | signed char | signed | -------- | explicitlySigned | ------------------ | ---------------- | 1 | 1 | unsigned char | | +| 8 | short | signed | -------- | ---------------- | ------------------ | implicitlySigned | 2 | 2 | unsigned short | MicrosoftInt16Type | +| 9 | unsigned short | ------ | unsigned | ---------------- | explicitlyUnsigned | ---------------- | 2 | 2 | unsigned short | | +| 10 | signed short | signed | -------- | explicitlySigned | ------------------ | ---------------- | 2 | 2 | unsigned short | | +| 11 | int | signed | -------- | ---------------- | ------------------ | implicitlySigned | 4 | 4 | unsigned int | MicrosoftInt32Type | +| 12 | unsigned int | ------ | unsigned | ---------------- | explicitlyUnsigned | ---------------- | 4 | 4 | unsigned int | | +| 13 | signed int | signed | -------- | explicitlySigned | ------------------ | ---------------- | 4 | 4 | unsigned int | | +| 14 | long | signed | -------- | ---------------- | ------------------ | implicitlySigned | 4 | 4 | unsigned long | | +| 15 | unsigned long | ------ | unsigned | ---------------- | explicitlyUnsigned | ---------------- | 4 | 4 | unsigned long | | +| 16 | signed long | signed | -------- | explicitlySigned | ------------------ | ---------------- | 4 | 4 | unsigned long | | +| 17 | long long | signed | -------- | ---------------- | ------------------ | implicitlySigned | 8 | 8 | unsigned long long | MicrosoftInt64Type | +| 18 | unsigned long long | ------ | unsigned | ---------------- | explicitlyUnsigned | ---------------- | 8 | 8 | unsigned long long | | +| 19 | signed long long | signed | -------- | explicitlySigned | ------------------ | ---------------- | 8 | 8 | unsigned long long | | +| 33 | wchar_t | ------ | -------- | ---------------- | ------------------ | ---------------- | 2 | 2 | | | +| 35 | __int128 | signed | -------- | ---------------- | ------------------ | implicitlySigned | 16 | 16 | unsigned __int128 | | +| 36 | unsigned __int128 | ------ | unsigned | ---------------- | explicitlyUnsigned | ---------------- | 16 | 16 | unsigned __int128 | | +| 37 | signed __int128 | signed | -------- | explicitlySigned | ------------------ | ---------------- | 16 | 16 | unsigned __int128 | | +| 43 | char16_t | ------ | -------- | ---------------- | ------------------ | ---------------- | 2 | 2 | | | +| 44 | char32_t | ------ | -------- | ---------------- | ------------------ | ---------------- | 4 | 4 | | | diff --git a/cpp/ql/test/library-tests/types/integral_types_ms/integral_type.qlref b/cpp/ql/test/library-tests/types/integral_types_ms/integral_type.qlref new file mode 100644 index 000000000000..e5e0e3cdf432 --- /dev/null +++ b/cpp/ql/test/library-tests/types/integral_types_ms/integral_type.qlref @@ -0,0 +1 @@ +../integral_types/integral_type.ql diff --git a/cpp/ql/test/library-tests/types/integral_types_ms/integral_types.cpp b/cpp/ql/test/library-tests/types/integral_types_ms/integral_types.cpp new file mode 100644 index 000000000000..aab2fa52924e --- /dev/null +++ b/cpp/ql/test/library-tests/types/integral_types_ms/integral_types.cpp @@ -0,0 +1,5 @@ + +__int8 i8; +__int16 i16; +__int32 i32; +__int64 i64; diff --git a/cpp/ql/test/library-tests/types/integral_types_ms/options b/cpp/ql/test/library-tests/types/integral_types_ms/options new file mode 100644 index 000000000000..ec5545ba4a7e --- /dev/null +++ b/cpp/ql/test/library-tests/types/integral_types_ms/options @@ -0,0 +1 @@ +extractor_flags: --microsoft --edg --target --edg win64 diff --git a/cpp/ql/test/library-tests/types/integral_types_ms/vars.expected b/cpp/ql/test/library-tests/types/integral_types_ms/vars.expected new file mode 100644 index 000000000000..d2aac7454fdb --- /dev/null +++ b/cpp/ql/test/library-tests/types/integral_types_ms/vars.expected @@ -0,0 +1,4 @@ +| integral_types.cpp:2:8:2:9 | i8 | file://:0:0:0:0 | char | MicrosoftInt8Type, PlainCharType | +| integral_types.cpp:3:9:3:11 | i16 | file://:0:0:0:0 | short | MicrosoftInt16Type, ShortType | +| integral_types.cpp:4:9:4:11 | i32 | file://:0:0:0:0 | int | IntType, MicrosoftInt32Type | +| integral_types.cpp:5:9:5:11 | i64 | file://:0:0:0:0 | long long | LongLongType, MicrosoftInt64Type | diff --git a/cpp/ql/test/library-tests/types/integral_types_ms/vars.ql b/cpp/ql/test/library-tests/types/integral_types_ms/vars.ql new file mode 100644 index 000000000000..68040ee6f63f --- /dev/null +++ b/cpp/ql/test/library-tests/types/integral_types_ms/vars.ql @@ -0,0 +1,5 @@ +import cpp + +from Variable v, Type t +where v.getType() = t +select v, t, concat(t.getAQlClass(), ", ") diff --git a/cpp/ql/test/library-tests/types/pointertypes/options b/cpp/ql/test/library-tests/types/pointertypes/options new file mode 100644 index 000000000000..2f420c68c886 --- /dev/null +++ b/cpp/ql/test/library-tests/types/pointertypes/options @@ -0,0 +1 @@ +extractor_flags: -std=c99 diff --git a/cpp/ql/test/library-tests/types/pointertypes/pointertypes.c b/cpp/ql/test/library-tests/types/pointertypes/pointertypes.c new file mode 100644 index 000000000000..2881dc878539 --- /dev/null +++ b/cpp/ql/test/library-tests/types/pointertypes/pointertypes.c @@ -0,0 +1,40 @@ + +char c; +char *cp; + +const char cc; +char const cc2; +const char *ccp; +char const *ccp2; +char * const ccp3; +char const * const ccp4; + +volatile char vc; +char volatile vc2; +volatile char *vcp; +char volatile *vcp2; +char * volatile vcp3; +char volatile * volatile vcp4; + +char * restrict rcp; +char *__restrict__ rcp2; + +void *vp; + +const void *cvp; +void const *cvp2; +void * const cvp3; +void const * const cvp4; + +void *restrict rvp1; +void *__restrict__ rvp2; + +const struct s *csp; +struct s const *csp2; +struct s * const csp3; +struct s const * const csp4; + +struct s *restrict rsp1; +struct s *__restrict__ rsp2; + +char * const volatile restrict cvrcp; diff --git a/cpp/ql/test/library-tests/types/pointertypes/pointertypes.expected b/cpp/ql/test/library-tests/types/pointertypes/pointertypes.expected new file mode 100644 index 000000000000..23d1d1fa8552 --- /dev/null +++ b/cpp/ql/test/library-tests/types/pointertypes/pointertypes.expected @@ -0,0 +1,135 @@ +| file://:0:0:0:0 | fp_offset | unsigned int | getUnspecifiedType() | unsigned int | +| file://:0:0:0:0 | gp_offset | unsigned int | getUnspecifiedType() | unsigned int | +| file://:0:0:0:0 | overflow_arg_area | void * | PointerType | | +| file://:0:0:0:0 | overflow_arg_area | void * | VoidPointerType | | +| file://:0:0:0:0 | overflow_arg_area | void * | getBaseType() | void | +| file://:0:0:0:0 | overflow_arg_area | void * | getUnspecifiedType() | void * | +| file://:0:0:0:0 | reg_save_area | void * | PointerType | | +| file://:0:0:0:0 | reg_save_area | void * | VoidPointerType | | +| file://:0:0:0:0 | reg_save_area | void * | getBaseType() | void | +| file://:0:0:0:0 | reg_save_area | void * | getUnspecifiedType() | void * | +| pointertypes.c:2:6:2:6 | c | char | getUnspecifiedType() | char | +| pointertypes.c:3:7:3:8 | cp | char * | CharPointerType | | +| pointertypes.c:3:7:3:8 | cp | char * | PointerType | | +| pointertypes.c:3:7:3:8 | cp | char * | getBaseType() | char | +| pointertypes.c:3:7:3:8 | cp | char * | getUnspecifiedType() | char * | +| pointertypes.c:5:12:5:13 | cc | const char | SpecifiedType | | +| pointertypes.c:5:12:5:13 | cc | const char | getASpecifier() | const | +| pointertypes.c:5:12:5:13 | cc | const char | getBaseType() | char | +| pointertypes.c:5:12:5:13 | cc | const char | getUnspecifiedType() | char | +| pointertypes.c:5:12:5:13 | cc | const char | isConst() | | +| pointertypes.c:6:12:6:14 | cc2 | const char | SpecifiedType | | +| pointertypes.c:6:12:6:14 | cc2 | const char | getASpecifier() | const | +| pointertypes.c:6:12:6:14 | cc2 | const char | getBaseType() | char | +| pointertypes.c:6:12:6:14 | cc2 | const char | getUnspecifiedType() | char | +| pointertypes.c:6:12:6:14 | cc2 | const char | isConst() | | +| pointertypes.c:7:13:7:15 | ccp | const char * | PointerType | | +| pointertypes.c:7:13:7:15 | ccp | const char * | getBaseType() | const char | +| pointertypes.c:7:13:7:15 | ccp | const char * | getUnspecifiedType() | char * | +| pointertypes.c:8:13:8:16 | ccp2 | const char * | PointerType | | +| pointertypes.c:8:13:8:16 | ccp2 | const char * | getBaseType() | const char | +| pointertypes.c:8:13:8:16 | ccp2 | const char * | getUnspecifiedType() | char * | +| pointertypes.c:9:14:9:17 | ccp3 | char *const | SpecifiedType | | +| pointertypes.c:9:14:9:17 | ccp3 | char *const | getASpecifier() | const | +| pointertypes.c:9:14:9:17 | ccp3 | char *const | getBaseType() | char * | +| pointertypes.c:9:14:9:17 | ccp3 | char *const | getUnspecifiedType() | char * | +| pointertypes.c:9:14:9:17 | ccp3 | char *const | isConst() | | +| pointertypes.c:10:20:10:23 | ccp4 | const char *const | SpecifiedType | | +| pointertypes.c:10:20:10:23 | ccp4 | const char *const | getASpecifier() | const | +| pointertypes.c:10:20:10:23 | ccp4 | const char *const | getBaseType() | const char * | +| pointertypes.c:10:20:10:23 | ccp4 | const char *const | getUnspecifiedType() | char * | +| pointertypes.c:10:20:10:23 | ccp4 | const char *const | isConst() | | +| pointertypes.c:12:15:12:16 | vc | volatile char | SpecifiedType | | +| pointertypes.c:12:15:12:16 | vc | volatile char | getASpecifier() | volatile | +| pointertypes.c:12:15:12:16 | vc | volatile char | getBaseType() | char | +| pointertypes.c:12:15:12:16 | vc | volatile char | getUnspecifiedType() | char | +| pointertypes.c:12:15:12:16 | vc | volatile char | isVolatile() | | +| pointertypes.c:13:15:13:17 | vc2 | volatile char | SpecifiedType | | +| pointertypes.c:13:15:13:17 | vc2 | volatile char | getASpecifier() | volatile | +| pointertypes.c:13:15:13:17 | vc2 | volatile char | getBaseType() | char | +| pointertypes.c:13:15:13:17 | vc2 | volatile char | getUnspecifiedType() | char | +| pointertypes.c:13:15:13:17 | vc2 | volatile char | isVolatile() | | +| pointertypes.c:14:16:14:18 | vcp | volatile char * | PointerType | | +| pointertypes.c:14:16:14:18 | vcp | volatile char * | getBaseType() | volatile char | +| pointertypes.c:14:16:14:18 | vcp | volatile char * | getUnspecifiedType() | char * | +| pointertypes.c:15:16:15:19 | vcp2 | volatile char * | PointerType | | +| pointertypes.c:15:16:15:19 | vcp2 | volatile char * | getBaseType() | volatile char | +| pointertypes.c:15:16:15:19 | vcp2 | volatile char * | getUnspecifiedType() | char * | +| pointertypes.c:16:17:16:20 | vcp3 | char *volatile | SpecifiedType | | +| pointertypes.c:16:17:16:20 | vcp3 | char *volatile | getASpecifier() | volatile | +| pointertypes.c:16:17:16:20 | vcp3 | char *volatile | getBaseType() | char * | +| pointertypes.c:16:17:16:20 | vcp3 | char *volatile | getUnspecifiedType() | char * | +| pointertypes.c:16:17:16:20 | vcp3 | char *volatile | isVolatile() | | +| pointertypes.c:17:26:17:29 | vcp4 | volatile char *volatile | SpecifiedType | | +| pointertypes.c:17:26:17:29 | vcp4 | volatile char *volatile | getASpecifier() | volatile | +| pointertypes.c:17:26:17:29 | vcp4 | volatile char *volatile | getBaseType() | volatile char * | +| pointertypes.c:17:26:17:29 | vcp4 | volatile char *volatile | getUnspecifiedType() | char * | +| pointertypes.c:17:26:17:29 | vcp4 | volatile char *volatile | isVolatile() | | +| pointertypes.c:19:17:19:19 | rcp | char *__restrict__ | SpecifiedType | | +| pointertypes.c:19:17:19:19 | rcp | char *__restrict__ | getASpecifier() | restrict | +| pointertypes.c:19:17:19:19 | rcp | char *__restrict__ | getBaseType() | char * | +| pointertypes.c:19:17:19:19 | rcp | char *__restrict__ | getUnspecifiedType() | char * | +| pointertypes.c:20:20:20:23 | rcp2 | char *__restrict__ | SpecifiedType | | +| pointertypes.c:20:20:20:23 | rcp2 | char *__restrict__ | getASpecifier() | restrict | +| pointertypes.c:20:20:20:23 | rcp2 | char *__restrict__ | getBaseType() | char * | +| pointertypes.c:20:20:20:23 | rcp2 | char *__restrict__ | getUnspecifiedType() | char * | +| pointertypes.c:22:7:22:8 | vp | void * | PointerType | | +| pointertypes.c:22:7:22:8 | vp | void * | VoidPointerType | | +| pointertypes.c:22:7:22:8 | vp | void * | getBaseType() | void | +| pointertypes.c:22:7:22:8 | vp | void * | getUnspecifiedType() | void * | +| pointertypes.c:24:13:24:15 | cvp | const void * | PointerType | | +| pointertypes.c:24:13:24:15 | cvp | const void * | getBaseType() | const void | +| pointertypes.c:24:13:24:15 | cvp | const void * | getUnspecifiedType() | void * | +| pointertypes.c:25:13:25:16 | cvp2 | const void * | PointerType | | +| pointertypes.c:25:13:25:16 | cvp2 | const void * | getBaseType() | const void | +| pointertypes.c:25:13:25:16 | cvp2 | const void * | getUnspecifiedType() | void * | +| pointertypes.c:26:14:26:17 | cvp3 | void *const | SpecifiedType | | +| pointertypes.c:26:14:26:17 | cvp3 | void *const | getASpecifier() | const | +| pointertypes.c:26:14:26:17 | cvp3 | void *const | getBaseType() | void * | +| pointertypes.c:26:14:26:17 | cvp3 | void *const | getUnspecifiedType() | void * | +| pointertypes.c:26:14:26:17 | cvp3 | void *const | isConst() | | +| pointertypes.c:27:20:27:23 | cvp4 | const void *const | SpecifiedType | | +| pointertypes.c:27:20:27:23 | cvp4 | const void *const | getASpecifier() | const | +| pointertypes.c:27:20:27:23 | cvp4 | const void *const | getBaseType() | const void * | +| pointertypes.c:27:20:27:23 | cvp4 | const void *const | getUnspecifiedType() | void * | +| pointertypes.c:27:20:27:23 | cvp4 | const void *const | isConst() | | +| pointertypes.c:29:16:29:19 | rvp1 | void *__restrict__ | SpecifiedType | | +| pointertypes.c:29:16:29:19 | rvp1 | void *__restrict__ | getASpecifier() | restrict | +| pointertypes.c:29:16:29:19 | rvp1 | void *__restrict__ | getBaseType() | void * | +| pointertypes.c:29:16:29:19 | rvp1 | void *__restrict__ | getUnspecifiedType() | void * | +| pointertypes.c:30:20:30:23 | rvp2 | void *__restrict__ | SpecifiedType | | +| pointertypes.c:30:20:30:23 | rvp2 | void *__restrict__ | getASpecifier() | restrict | +| pointertypes.c:30:20:30:23 | rvp2 | void *__restrict__ | getBaseType() | void * | +| pointertypes.c:30:20:30:23 | rvp2 | void *__restrict__ | getUnspecifiedType() | void * | +| pointertypes.c:32:17:32:19 | csp | const s * | PointerType | | +| pointertypes.c:32:17:32:19 | csp | const s * | getBaseType() | const s | +| pointertypes.c:32:17:32:19 | csp | const s * | getUnspecifiedType() | s * | +| pointertypes.c:33:17:33:20 | csp2 | const s * | PointerType | | +| pointertypes.c:33:17:33:20 | csp2 | const s * | getBaseType() | const s | +| pointertypes.c:33:17:33:20 | csp2 | const s * | getUnspecifiedType() | s * | +| pointertypes.c:34:18:34:21 | csp3 | s *const | SpecifiedType | | +| pointertypes.c:34:18:34:21 | csp3 | s *const | getASpecifier() | const | +| pointertypes.c:34:18:34:21 | csp3 | s *const | getBaseType() | s * | +| pointertypes.c:34:18:34:21 | csp3 | s *const | getUnspecifiedType() | s * | +| pointertypes.c:34:18:34:21 | csp3 | s *const | isConst() | | +| pointertypes.c:35:24:35:27 | csp4 | const s *const | SpecifiedType | | +| pointertypes.c:35:24:35:27 | csp4 | const s *const | getASpecifier() | const | +| pointertypes.c:35:24:35:27 | csp4 | const s *const | getBaseType() | const s * | +| pointertypes.c:35:24:35:27 | csp4 | const s *const | getUnspecifiedType() | s * | +| pointertypes.c:35:24:35:27 | csp4 | const s *const | isConst() | | +| pointertypes.c:37:20:37:23 | rsp1 | s *__restrict__ | SpecifiedType | | +| pointertypes.c:37:20:37:23 | rsp1 | s *__restrict__ | getASpecifier() | restrict | +| pointertypes.c:37:20:37:23 | rsp1 | s *__restrict__ | getBaseType() | s * | +| pointertypes.c:37:20:37:23 | rsp1 | s *__restrict__ | getUnspecifiedType() | s * | +| pointertypes.c:38:24:38:27 | rsp2 | s *__restrict__ | SpecifiedType | | +| pointertypes.c:38:24:38:27 | rsp2 | s *__restrict__ | getASpecifier() | restrict | +| pointertypes.c:38:24:38:27 | rsp2 | s *__restrict__ | getBaseType() | s * | +| pointertypes.c:38:24:38:27 | rsp2 | s *__restrict__ | getUnspecifiedType() | s * | +| pointertypes.c:40:32:40:36 | cvrcp | char *const volatile __restrict__ | SpecifiedType | | +| pointertypes.c:40:32:40:36 | cvrcp | char *const volatile __restrict__ | getASpecifier() | const | +| pointertypes.c:40:32:40:36 | cvrcp | char *const volatile __restrict__ | getASpecifier() | restrict | +| pointertypes.c:40:32:40:36 | cvrcp | char *const volatile __restrict__ | getASpecifier() | volatile | +| pointertypes.c:40:32:40:36 | cvrcp | char *const volatile __restrict__ | getBaseType() | char * | +| pointertypes.c:40:32:40:36 | cvrcp | char *const volatile __restrict__ | getUnspecifiedType() | char * | +| pointertypes.c:40:32:40:36 | cvrcp | char *const volatile __restrict__ | isConst() | | +| pointertypes.c:40:32:40:36 | cvrcp | char *const volatile __restrict__ | isVolatile() | | diff --git a/cpp/ql/test/library-tests/types/pointertypes/pointertypes.ql b/cpp/ql/test/library-tests/types/pointertypes/pointertypes.ql new file mode 100644 index 000000000000..293346a3f6d6 --- /dev/null +++ b/cpp/ql/test/library-tests/types/pointertypes/pointertypes.ql @@ -0,0 +1,38 @@ +import cpp + +predicate describe(Type t, string a, string b) { + ( + a = "getUnspecifiedType()" and + b = t.getUnspecifiedType().toString() + ) or ( + a = "PointerType" and b = "" and + t instanceof PointerType + ) or ( + a = "VoidPointerType" and b = "" and + t instanceof VoidPointerType + ) or ( + a = "CharPointerType" and b = "" and + t instanceof CharPointerType + ) or ( + a = "SpecifiedType" and b = "" and + t instanceof SpecifiedType + ) or ( + a = "getASpecifier()" and + b = t.getASpecifier().toString() + ) or ( + a = "isConst()" and b = "" and + t.isConst() + ) or ( + a = "isVolatile()" and b = "" and + t.isVolatile() + ) or ( + a = "getBaseType()" and + b = t.(DerivedType).getBaseType().toString() + ) +} + +from Variable v, Type t, string a, string b +where + t = v.getType() and + describe(t, a, b) +select v, t.toString(), a, b diff --git a/cpp/ql/test/library-tests/types/refersTo/refersTo.cpp b/cpp/ql/test/library-tests/types/refersTo/refersTo.cpp new file mode 100644 index 000000000000..05564ae604be --- /dev/null +++ b/cpp/ql/test/library-tests/types/refersTo/refersTo.cpp @@ -0,0 +1,20 @@ + +class a +{ +}; + +typedef a &a_ref; +typedef a *a_ptr; +typedef a a_array[10]; + +template +class container +{ +public: + T t; +}; + +class strange : public container > +{ +public: +}; diff --git a/cpp/ql/test/library-tests/types/refersTo/refersTo.expected b/cpp/ql/test/library-tests/types/refersTo/refersTo.expected new file mode 100644 index 000000000000..4d040f251236 --- /dev/null +++ b/cpp/ql/test/library-tests/types/refersTo/refersTo.expected @@ -0,0 +1,63 @@ +| 0 | file://:0:0:0:0 | a & | refersTo.cpp:2:7:2:7 | a | direct | +| 0 | file://:0:0:0:0 | a && | refersTo.cpp:2:7:2:7 | a | direct | +| 0 | file://:0:0:0:0 | a * | refersTo.cpp:2:7:2:7 | a | direct | +| 0 | file://:0:0:0:0 | a[10] | refersTo.cpp:2:7:2:7 | a | direct | +| 0 | file://:0:0:0:0 | const a | refersTo.cpp:2:7:2:7 | a | direct | +| 0 | file://:0:0:0:0 | const a & | refersTo.cpp:2:7:2:7 | a | | +| 0 | file://:0:0:0:0 | const container | refersTo.cpp:2:7:2:7 | a | | +| 0 | file://:0:0:0:0 | const container | refersTo.cpp:10:16:10:16 | T | | +| 0 | file://:0:0:0:0 | const container | refersTo.cpp:11:7:11:15 | container | | +| 0 | file://:0:0:0:0 | const container | refersTo.cpp:11:7:11:15 | container | direct | +| 0 | file://:0:0:0:0 | const container & | refersTo.cpp:2:7:2:7 | a | | +| 0 | file://:0:0:0:0 | const container & | refersTo.cpp:10:16:10:16 | T | | +| 0 | file://:0:0:0:0 | const container & | refersTo.cpp:11:7:11:15 | container | | +| 0 | file://:0:0:0:0 | const container & | refersTo.cpp:11:7:11:15 | container | | +| 0 | file://:0:0:0:0 | const container> | refersTo.cpp:2:7:2:7 | a | | +| 0 | file://:0:0:0:0 | const container> | refersTo.cpp:10:16:10:16 | T | | +| 0 | file://:0:0:0:0 | const container> | refersTo.cpp:11:7:11:15 | container | | +| 0 | file://:0:0:0:0 | const container> | refersTo.cpp:11:7:11:15 | container | | +| 0 | file://:0:0:0:0 | const container> | refersTo.cpp:11:7:11:15 | container> | direct | +| 0 | file://:0:0:0:0 | const container> & | refersTo.cpp:2:7:2:7 | a | | +| 0 | file://:0:0:0:0 | const container> & | refersTo.cpp:10:16:10:16 | T | | +| 0 | file://:0:0:0:0 | const container> & | refersTo.cpp:11:7:11:15 | container | | +| 0 | file://:0:0:0:0 | const container> & | refersTo.cpp:11:7:11:15 | container | | +| 0 | file://:0:0:0:0 | const container> & | refersTo.cpp:11:7:11:15 | container> | | +| 0 | file://:0:0:0:0 | const strange | refersTo.cpp:17:7:17:13 | strange | direct | +| 0 | file://:0:0:0:0 | const strange & | refersTo.cpp:17:7:17:13 | strange | | +| 0 | file://:0:0:0:0 | container & | refersTo.cpp:2:7:2:7 | a | | +| 0 | file://:0:0:0:0 | container & | refersTo.cpp:10:16:10:16 | T | | +| 0 | file://:0:0:0:0 | container & | refersTo.cpp:11:7:11:15 | container | | +| 0 | file://:0:0:0:0 | container & | refersTo.cpp:11:7:11:15 | container | direct | +| 0 | file://:0:0:0:0 | container && | refersTo.cpp:2:7:2:7 | a | | +| 0 | file://:0:0:0:0 | container && | refersTo.cpp:10:16:10:16 | T | | +| 0 | file://:0:0:0:0 | container && | refersTo.cpp:11:7:11:15 | container | | +| 0 | file://:0:0:0:0 | container && | refersTo.cpp:11:7:11:15 | container | direct | +| 0 | file://:0:0:0:0 | container> & | refersTo.cpp:2:7:2:7 | a | | +| 0 | file://:0:0:0:0 | container> & | refersTo.cpp:10:16:10:16 | T | | +| 0 | file://:0:0:0:0 | container> & | refersTo.cpp:11:7:11:15 | container | | +| 0 | file://:0:0:0:0 | container> & | refersTo.cpp:11:7:11:15 | container | | +| 0 | file://:0:0:0:0 | container> & | refersTo.cpp:11:7:11:15 | container> | direct | +| 0 | file://:0:0:0:0 | container> && | refersTo.cpp:2:7:2:7 | a | | +| 0 | file://:0:0:0:0 | container> && | refersTo.cpp:10:16:10:16 | T | | +| 0 | file://:0:0:0:0 | container> && | refersTo.cpp:11:7:11:15 | container | | +| 0 | file://:0:0:0:0 | container> && | refersTo.cpp:11:7:11:15 | container | | +| 0 | file://:0:0:0:0 | container> && | refersTo.cpp:11:7:11:15 | container> | direct | +| 0 | file://:0:0:0:0 | strange & | refersTo.cpp:17:7:17:13 | strange | direct | +| 0 | file://:0:0:0:0 | strange && | refersTo.cpp:17:7:17:13 | strange | direct | +| 2 | refersTo.cpp:2:7:2:7 | a | refersTo.cpp:2:7:2:7 | a | | +| 6 | refersTo.cpp:6:12:6:16 | a_ref | refersTo.cpp:6:12:6:16 | a_ref | | +| 7 | refersTo.cpp:7:12:7:16 | a_ptr | refersTo.cpp:7:12:7:16 | a_ptr | | +| 8 | refersTo.cpp:8:11:8:17 | a_array | refersTo.cpp:8:11:8:17 | a_array | | +| 10 | refersTo.cpp:10:16:10:16 | T | refersTo.cpp:10:16:10:16 | T | | +| 11 | refersTo.cpp:11:7:11:15 | container | refersTo.cpp:10:16:10:16 | T | direct | +| 11 | refersTo.cpp:11:7:11:15 | container | refersTo.cpp:11:7:11:15 | container | | +| 11 | refersTo.cpp:11:7:11:15 | container | refersTo.cpp:2:7:2:7 | a | direct | +| 11 | refersTo.cpp:11:7:11:15 | container | refersTo.cpp:10:16:10:16 | T | | +| 11 | refersTo.cpp:11:7:11:15 | container | refersTo.cpp:11:7:11:15 | container | direct | +| 11 | refersTo.cpp:11:7:11:15 | container | refersTo.cpp:11:7:11:15 | container | | +| 11 | refersTo.cpp:11:7:11:15 | container> | refersTo.cpp:2:7:2:7 | a | | +| 11 | refersTo.cpp:11:7:11:15 | container> | refersTo.cpp:10:16:10:16 | T | | +| 11 | refersTo.cpp:11:7:11:15 | container> | refersTo.cpp:11:7:11:15 | container | direct | +| 11 | refersTo.cpp:11:7:11:15 | container> | refersTo.cpp:11:7:11:15 | container | direct | +| 11 | refersTo.cpp:11:7:11:15 | container> | refersTo.cpp:11:7:11:15 | container> | | +| 17 | refersTo.cpp:17:7:17:13 | strange | refersTo.cpp:17:7:17:13 | strange | | diff --git a/cpp/ql/test/library-tests/types/refersTo/refersTo.ql b/cpp/ql/test/library-tests/types/refersTo/refersTo.ql new file mode 100644 index 000000000000..2c2e1184b6c1 --- /dev/null +++ b/cpp/ql/test/library-tests/types/refersTo/refersTo.ql @@ -0,0 +1,7 @@ +import cpp + +from Type a, Type b, string str +where a.refersTo(b) +and if a.refersToDirectly(b) then str = "direct" else str = "" +and b.getFile().toString() != "" +select a.getLocation().getStartLine(), a, b, str diff --git a/cpp/ql/test/library-tests/types/scope/expr.expected b/cpp/ql/test/library-tests/types/scope/expr.expected new file mode 100644 index 000000000000..e277ae00b007 --- /dev/null +++ b/cpp/ql/test/library-tests/types/scope/expr.expected @@ -0,0 +1,34 @@ +| scope.c:12:11:12:14 | 2048 | scope.c:11:5:11:6 | f1 | +| scope.c:13:12:13:12 | p | scope.c:11:5:11:6 | f1 | +| scope.c:13:12:13:15 | array to pointer conversion | scope.c:11:5:11:6 | f1 | +| scope.c:13:12:13:18 | access to array | scope.c:11:5:11:6 | f1 | +| scope.c:13:15:13:15 | v | scope.c:11:5:11:6 | f1 | +| scope.c:13:17:13:17 | 1 | scope.c:11:5:11:6 | f1 | +| scope.c:17:11:17:14 | 2048 | scope.c:16:5:16:6 | f2 | +| scope.c:18:12:18:12 | p | scope.c:16:5:16:6 | f2 | +| scope.c:18:12:18:15 | array to pointer conversion | scope.c:16:5:16:6 | f2 | +| scope.c:18:12:18:18 | access to array | scope.c:16:5:16:6 | f2 | +| scope.c:18:15:18:15 | v | scope.c:16:5:16:6 | f2 | +| scope.c:18:17:18:17 | 1 | scope.c:16:5:16:6 | f2 | +| scope.c:21:11:21:14 | 2048 | scope.c:20:5:20:6 | f3 | +| scope.c:22:12:22:12 | p | scope.c:20:5:20:6 | f3 | +| scope.c:22:12:22:15 | array to pointer conversion | scope.c:20:5:20:6 | f3 | +| scope.c:22:12:22:18 | access to array | scope.c:20:5:20:6 | f3 | +| scope.c:22:15:22:15 | v | scope.c:20:5:20:6 | f3 | +| scope.c:22:17:22:17 | 1 | scope.c:20:5:20:6 | f3 | +| scope.c:26:11:26:14 | 2048 | scope.c:25:5:25:6 | f4 | +| scope.c:27:12:27:12 | p | scope.c:25:5:25:6 | f4 | +| scope.c:27:12:27:15 | array to pointer conversion | scope.c:25:5:25:6 | f4 | +| scope.c:27:12:27:18 | access to array | scope.c:25:5:25:6 | f4 | +| scope.c:27:15:27:15 | v | scope.c:25:5:25:6 | f4 | +| scope.c:27:17:27:17 | 1 | scope.c:25:5:25:6 | f4 | +| scope.cpp:7:11:7:13 | 400 | scope.cpp:6:6:6:7 | g1 | +| scope.cpp:9:28:9:30 | 500 | scope.cpp:6:6:6:7 | g1 | +| scope.cpp:18:19:18:21 | 600 | scope.cpp:16:14:16:15 | g2 | +| scope.cpp:24:13:24:28 | (int)... | scope.cpp:6:6:6:7 | g1 | +| scope.cpp:24:13:24:28 | sizeof(int[800]) | scope.cpp:6:6:6:7 | g1 | +| scope.cpp:24:24:24:26 | 800 | scope.cpp:6:6:6:7 | g1 | +| scope.cpp:25:14:25:25 | new[] | scope.cpp:6:6:6:7 | g1 | +| scope.cpp:25:22:25:24 | 900 | scope.cpp:6:6:6:7 | g1 | +| scope.cpp:26:14:26:26 | new[] | scope.cpp:6:6:6:7 | g1 | +| scope.cpp:26:22:26:25 | 1000 | scope.cpp:6:6:6:7 | g1 | diff --git a/cpp/ql/test/library-tests/types/scope/expr.ql b/cpp/ql/test/library-tests/types/scope/expr.ql new file mode 100644 index 000000000000..85db6f217846 --- /dev/null +++ b/cpp/ql/test/library-tests/types/scope/expr.ql @@ -0,0 +1,5 @@ +import cpp + +from Expr e +select e, e.getEnclosingFunction() + diff --git a/cpp/ql/test/library-tests/types/scope/scope.c b/cpp/ql/test/library-tests/types/scope/scope.c new file mode 100644 index 000000000000..aaf856c05e53 --- /dev/null +++ b/cpp/ql/test/library-tests/types/scope/scope.c @@ -0,0 +1,29 @@ + +typedef struct { + int v[1024]; +} t; + +int f1(t *p); +int f2(t *p); +int f3(const t *p); +int f4(const t *p); + +int f1(t *p) { + int w[2048]; + return p->v[1]; +} + +int f2(t *p) { + int w[2048]; + return p->v[1]; +} +int f3(const t *p) { + int w[2048]; + return p->v[1]; +} + +int f4(const t *p) { + int w[2048]; + return p->v[1]; +} + diff --git a/cpp/ql/test/library-tests/types/scope/scope.cpp b/cpp/ql/test/library-tests/types/scope/scope.cpp new file mode 100644 index 000000000000..c9d0d962d628 --- /dev/null +++ b/cpp/ql/test/library-tests/types/scope/scope.cpp @@ -0,0 +1,27 @@ + +int a[100]; +typedef int my_array[200]; + +void g1(int b[300]); +void g1(int b[300]) { + int c[400]; + + typedef int my_array_2[500]; + my_array d; + my_array_2 e; + + class f + { + public: + void g2() + { + int g[600]; + } + + int h[700]; + }; + + int i = sizeof(int[800]); + auto j = new int[900]; + int *k = new int[1000]; +} diff --git a/cpp/ql/test/library-tests/types/segfault/segfault.c b/cpp/ql/test/library-tests/types/segfault/segfault.c new file mode 100644 index 000000000000..12704074b24d --- /dev/null +++ b/cpp/ql/test/library-tests/types/segfault/segfault.c @@ -0,0 +1,4 @@ + +void f(enum E *e) { +} + diff --git a/cpp/ql/test/library-tests/types/segfault/vars.expected b/cpp/ql/test/library-tests/types/segfault/vars.expected new file mode 100644 index 000000000000..fe5df80bab8f --- /dev/null +++ b/cpp/ql/test/library-tests/types/segfault/vars.expected @@ -0,0 +1,5 @@ +| file://:0:0:0:0 | fp_offset | file://:0:0:0:0 | unsigned int | +| file://:0:0:0:0 | gp_offset | file://:0:0:0:0 | unsigned int | +| file://:0:0:0:0 | overflow_arg_area | file://:0:0:0:0 | void * | +| file://:0:0:0:0 | reg_save_area | file://:0:0:0:0 | void * | +| segfault.c:2:16:2:16 | e | file://:0:0:0:0 | E * | diff --git a/cpp/ql/test/library-tests/types/segfault/vars.ql b/cpp/ql/test/library-tests/types/segfault/vars.ql new file mode 100644 index 000000000000..2f75e89c1cf1 --- /dev/null +++ b/cpp/ql/test/library-tests/types/segfault/vars.ql @@ -0,0 +1,5 @@ +import cpp + +from Variable v +select v, v.getType() + diff --git a/cpp/ql/test/library-tests/types/sizeof/sizeof.cpp b/cpp/ql/test/library-tests/types/sizeof/sizeof.cpp new file mode 100644 index 000000000000..15229b3496ae --- /dev/null +++ b/cpp/ql/test/library-tests/types/sizeof/sizeof.cpp @@ -0,0 +1,29 @@ + +typedef unsigned int size_t; + +class MyClass +{ +public: + int x; + char c; + int *ptr; +}; + +void func() { + int i; + char c; + int * ptr; + MyClass mc; + int arr[10]; + + size_t sz1 = sizeof(int); + size_t sz2 = sizeof(char); + size_t sz3 = sizeof(int *); + size_t sz4 = sizeof(MyClass); + size_t sz5 = sizeof(i); + size_t sz6 = sizeof(c); + size_t sz7 = sizeof(ptr); + size_t sz8 = sizeof(mc); + size_t sz9 = sizeof(arr); + size_t sz10 = sizeof(arr[4]); +} diff --git a/cpp/ql/test/library-tests/types/sizeof/sizeof.expected b/cpp/ql/test/library-tests/types/sizeof/sizeof.expected new file mode 100644 index 000000000000..e75583fb9e56 --- /dev/null +++ b/cpp/ql/test/library-tests/types/sizeof/sizeof.expected @@ -0,0 +1,10 @@ +| sizeof.cpp:19:15:19:25 | sizeof(int) | SizeofTypeOperator.getTypeOperand() | file://:0:0:0:0 | int | +| sizeof.cpp:20:15:20:26 | sizeof(char) | SizeofTypeOperator.getTypeOperand() | file://:0:0:0:0 | char | +| sizeof.cpp:21:15:21:27 | sizeof(int *) | SizeofTypeOperator.getTypeOperand() | file://:0:0:0:0 | int * | +| sizeof.cpp:22:15:22:29 | sizeof(MyClass) | SizeofTypeOperator.getTypeOperand() | sizeof.cpp:4:7:4:13 | MyClass | +| sizeof.cpp:23:15:23:23 | sizeof() | SizeofExprOperator.getExprOperand() | sizeof.cpp:23:22:23:22 | i | +| sizeof.cpp:24:15:24:23 | sizeof() | SizeofExprOperator.getExprOperand() | sizeof.cpp:24:22:24:22 | c | +| sizeof.cpp:25:15:25:25 | sizeof() | SizeofExprOperator.getExprOperand() | sizeof.cpp:25:22:25:24 | ptr | +| sizeof.cpp:26:15:26:24 | sizeof() | SizeofExprOperator.getExprOperand() | sizeof.cpp:26:22:26:23 | mc | +| sizeof.cpp:27:15:27:25 | sizeof() | SizeofExprOperator.getExprOperand() | sizeof.cpp:27:22:27:24 | arr | +| sizeof.cpp:28:16:28:29 | sizeof() | SizeofExprOperator.getExprOperand() | sizeof.cpp:28:23:28:28 | access to array | diff --git a/cpp/ql/test/library-tests/types/sizeof/sizeof.ql b/cpp/ql/test/library-tests/types/sizeof/sizeof.ql new file mode 100644 index 000000000000..ed9b58b594de --- /dev/null +++ b/cpp/ql/test/library-tests/types/sizeof/sizeof.ql @@ -0,0 +1,12 @@ +import cpp + +from SizeofOperator sto, string elemDesc, Element e +where + ( + elemDesc = "SizeofTypeOperator.getTypeOperand()" and + e = sto.(SizeofTypeOperator).getTypeOperand() + ) or ( + elemDesc = "SizeofExprOperator.getExprOperand()" and + e = sto.(SizeofExprOperator).getExprOperand() + ) +select sto, elemDesc, e diff --git a/cpp/ql/test/library-tests/types/typeid/typeid.cpp b/cpp/ql/test/library-tests/types/typeid/typeid.cpp new file mode 100644 index 000000000000..0f053decfaef --- /dev/null +++ b/cpp/ql/test/library-tests/types/typeid/typeid.cpp @@ -0,0 +1,35 @@ + +extern "C++" { + namespace std { + class type_info { + }; + } +} + +class Base +{ +public: + Base() {}; + + virtual void v() {} +}; + +class Derived : public Base { +public: + Derived() {}; +}; + +void func() { + Base *base_ptr = new Base(); + Derived *derived_ptr = new Derived(); + Base *base_derived_ptr = derived_ptr; + unsigned short us; + + const std::type_info &info1 = typeid(base_ptr); + const std::type_info &info2 = typeid(derived_ptr); + const std::type_info &info3 = typeid(base_derived_ptr); + const std::type_info &info4 = typeid(Base); // [GOOD - the test result for this line should not have a getExpr()] + const std::type_info &info5 = typeid(Derived); // [GOOD - the test result for this line should not have a getExpr()] + const std::type_info &info6 = typeid(us); + const std::type_info &info7 = typeid(unsigned short); // [GOOD - the test result for this line should not have a getExpr()] +} diff --git a/cpp/ql/test/library-tests/types/typeid/typeid.expected b/cpp/ql/test/library-tests/types/typeid/typeid.expected new file mode 100644 index 000000000000..b5ff02cb5212 --- /dev/null +++ b/cpp/ql/test/library-tests/types/typeid/typeid.expected @@ -0,0 +1,7 @@ +| typeid.cpp:28:32:28:47 | typeid ... | file://:0:0:0:0 | Base * | typeid.cpp:28:39:28:46 | base_ptr | +| typeid.cpp:29:32:29:50 | typeid ... | file://:0:0:0:0 | Derived * | typeid.cpp:29:39:29:49 | derived_ptr | +| typeid.cpp:30:32:30:55 | typeid ... | file://:0:0:0:0 | Base * | typeid.cpp:30:39:30:54 | base_derived_ptr | +| typeid.cpp:31:32:31:43 | typeid ... | typeid.cpp:9:7:9:10 | Base | | | +| typeid.cpp:32:32:32:46 | typeid ... | typeid.cpp:17:7:17:13 | Derived | | | +| typeid.cpp:33:32:33:41 | typeid ... | file://:0:0:0:0 | unsigned short | typeid.cpp:33:39:33:40 | us | +| typeid.cpp:34:32:34:53 | typeid ... | file://:0:0:0:0 | unsigned short | | | diff --git a/cpp/ql/test/library-tests/types/typeid/typeid.ql b/cpp/ql/test/library-tests/types/typeid/typeid.ql new file mode 100644 index 000000000000..b3f4221bc14c --- /dev/null +++ b/cpp/ql/test/library-tests/types/typeid/typeid.ql @@ -0,0 +1,12 @@ +import cpp + +from TypeidOperator to, string exprLoc, string expr +where + if exists(to.getExpr()) then ( + exprLoc = to.getExpr().getLocation().toString() and + expr = to.getExpr().toString() + ) else ( + expr = "" and + exprLoc = "" + ) +select to, to.getResultType(), exprLoc, expr diff --git a/cpp/ql/test/library-tests/types/types/Types.expected b/cpp/ql/test/library-tests/types/types/Types.expected new file mode 100644 index 000000000000..63feb266b6ee --- /dev/null +++ b/cpp/ql/test/library-tests/types/types/Types.expected @@ -0,0 +1,48 @@ +| file://:0:0:0:0 | fp_offset | | file://:0:0:0:0 | unsigned int | IntType | +| file://:0:0:0:0 | gp_offset | | file://:0:0:0:0 | unsigned int | IntType | +| file://:0:0:0:0 | overflow_arg_area | | file://:0:0:0:0 | void * | PointerType, VoidPointerType, base: void | +| file://:0:0:0:0 | reg_save_area | | file://:0:0:0:0 | void * | PointerType, VoidPointerType, base: void | +| types.cpp:1:12:1:12 | i | | file://:0:0:0:0 | int | IntType | +| types.cpp:3:11:3:11 | c | isConst | file://:0:0:0:0 | const int | base: int, isConst | +| types.cpp:4:14:4:15 | pi | isConst | file://:0:0:0:0 | const double | base: double, isConst | +| types.cpp:6:10:6:10 | a | | file://:0:0:0:0 | unsigned int | IntType | +| types.cpp:8:14:8:14 | b | | file://:0:0:0:0 | unsigned int | IntType | +| types.cpp:12:13:12:17 | kings | | file://:0:0:0:0 | const char *[] | base: const char * | +| types.cpp:14:6:14:6 | p | | file://:0:0:0:0 | int * | PointerType, base: int | +| types.cpp:14:9:14:9 | q | | file://:0:0:0:0 | int | IntType | +| types.cpp:15:5:15:6 | v1 | | file://:0:0:0:0 | int[10] | base: int | +| types.cpp:15:14:15:15 | pv | | file://:0:0:0:0 | int * | PointerType, base: int | +| types.cpp:17:7:17:8 | fp | | file://:0:0:0:0 | ..(*)(..) | base: ..()(..) | +| types.cpp:19:7:19:8 | v2 | | file://:0:0:0:0 | float[3] | base: float | +| types.cpp:20:7:20:8 | v3 | | file://:0:0:0:0 | char *[32] | base: char * | +| types.cpp:22:5:22:6 | d2 | | file://:0:0:0:0 | int[10][20] | base: int[20] | +| types.cpp:24:6:24:7 | v4 | | file://:0:0:0:0 | char[3] | base: char | +| types.cpp:26:5:26:6 | v5 | | file://:0:0:0:0 | int[8] | base: int | +| types.cpp:28:7:28:8 | p2 | | file://:0:0:0:0 | char * | PointerType, base: char | +| types.cpp:29:6:29:7 | p3 | | file://:0:0:0:0 | char[] | base: char | +| types.cpp:31:6:31:10 | alpha | | file://:0:0:0:0 | char[] | base: char | +| types.cpp:34:5:34:6 | av | | file://:0:0:0:0 | int[] | base: int | +| types.cpp:35:6:35:8 | ap1 | | file://:0:0:0:0 | int * | PointerType, base: int | +| types.cpp:36:6:36:8 | ap2 | | file://:0:0:0:0 | int * | PointerType, base: int | +| types.cpp:37:6:37:8 | ap3 | | file://:0:0:0:0 | int * | PointerType, base: int | +| types.cpp:39:12:39:13 | pi | Parameter | file://:0:0:0:0 | int * | PointerType, base: int | +| types.cpp:41:8:41:9 | pv | LocalVariable | file://:0:0:0:0 | void * | PointerType, VoidPointerType, base: void | +| types.cpp:45:15:45:15 | p | Parameter | file://:0:0:0:0 | char * | PointerType, base: char | +| types.cpp:47:8:47:8 | s | LocalVariable | file://:0:0:0:0 | char[] | base: char | +| types.cpp:48:15:48:16 | pc | LocalVariable | file://:0:0:0:0 | const char * | PointerType, base: const char | +| types.cpp:51:15:51:16 | cp | LocalVariable, isConst | file://:0:0:0:0 | char *const | base: char *, isConst | +| types.cpp:54:21:54:23 | cpc | LocalVariable, isConst | file://:0:0:0:0 | const char *const | base: const char *, isConst | +| types.cpp:55:15:55:17 | pc2 | LocalVariable | file://:0:0:0:0 | const char * | PointerType, base: const char | +| types.cpp:60:7:60:7 | i | LocalVariable | file://:0:0:0:0 | int | IntType | +| types.cpp:61:8:61:8 | r | LocalVariable | file://:0:0:0:0 | int & | ReferenceType, base: int | +| types.cpp:62:7:62:7 | x | LocalVariable | file://:0:0:0:0 | int | IntType | +| types.cpp:64:17:64:19 | cdr | LocalVariable | file://:0:0:0:0 | const double & | ReferenceType, base: const double | +| types.cpp:67:21:67:22 | aa | Parameter | file://:0:0:0:0 | int & | ReferenceType, base: int | +| types.cpp:69:10:69:13 | i128 | | file://:0:0:0:0 | __int128 | | +| types.cpp:70:17:70:21 | i128s | | file://:0:0:0:0 | signed __int128 | | +| types.cpp:71:19:71:23 | i128u | | file://:0:0:0:0 | unsigned __int128 | | +| types.cpp:73:7:73:11 | pchar | | types.cpp:10:15:10:19 | Pchar | TypedefType | +| types.cpp:79:8:79:9 | st | | types.cpp:75:23:75:28 | size_t | Size_t, TypedefType | +| types.cpp:80:9:80:11 | sst | | types.cpp:76:14:76:20 | ssize_t | Ssize_t, TypedefType | +| types.cpp:81:11:81:13 | pdt | | types.cpp:77:14:77:22 | ptrdiff_t | Ptrdiff_t, TypedefType | +| types.cpp:82:9:82:11 | wct | | file://:0:0:0:0 | wchar_t | Wchar_t | diff --git a/cpp/ql/test/library-tests/types/types/Types.ql b/cpp/ql/test/library-tests/types/types/Types.ql new file mode 100644 index 000000000000..c7eee4c22685 --- /dev/null +++ b/cpp/ql/test/library-tests/types/types/Types.ql @@ -0,0 +1,68 @@ +import cpp + +string describeType(Type t) +{ + ( + t instanceof TypedefType and + result = "TypedefType" + ) or ( + t instanceof PointerType and + result = "PointerType" + ) or ( + t instanceof VoidPointerType and + result = "VoidPointerType" + ) or ( + t instanceof ReferenceType and + result = "ReferenceType" + ) or exists(Type base | + base = t.(DerivedType).getBaseType() and + result = "base: " + base.getName() + ) or ( + t instanceof IntType and + result = "IntType" + ) or ( + t instanceof CharType and + result = "CharType" + ) or ( + t instanceof DoubleType and + result = "DoubleType" + ) or ( + t instanceof Size_t and + result = "Size_t" + ) or ( + t instanceof Ssize_t and + result = "Ssize_t" + ) or ( + t instanceof Ptrdiff_t and + result = "Ptrdiff_t" + ) or ( + t instanceof Wchar_t and + result = "Wchar_t" + ) or ( + t.isConst() and + result = "isConst" + ) +} + +string describeVar(Variable v) +{ + ( + v.isConst() and + result = "isConst" + ) or ( + v instanceof LocalVariable and + result = "LocalVariable" + ) or ( + v instanceof Parameter and + result = "Parameter" + ) +} + +from Variable v, Type t +where + v.getType() = t +select + v, + concat(describeVar(v), ", "), + t, + concat(describeType(t), ", ") diff --git a/cpp/ql/test/library-tests/types/types/options b/cpp/ql/test/library-tests/types/types/options new file mode 100644 index 000000000000..08e376c83fb4 --- /dev/null +++ b/cpp/ql/test/library-tests/types/types/options @@ -0,0 +1,2 @@ +extractor_flags: --gnu_version 40700 + diff --git a/cpp/ql/test/library-tests/types/types/types.cpp b/cpp/ql/test/library-tests/types/types/types.cpp new file mode 100644 index 000000000000..ab811aea8298 --- /dev/null +++ b/cpp/ql/test/library-tests/types/types/types.cpp @@ -0,0 +1,82 @@ +extern int i; + +const int c = 7; +const double pi = 3.1415926535897932385; + +unsigned a; + +unsigned int b; + +typedef char* Pchar; + +const char* kings[] = { "Antigonus", "Seleucus", "Ptolemy" }; + +int* p, q; +int v1[10], *pv; + +int (*fp)(char *); // pointer to function + +float v2[3]; +char* v3[32]; + +int d2[10][20]; + +char v4[3] = { 'a', 'b', 0 }; + +int v5[8] = { 1, 2, 3, 4 }; + +char* p2 = "Plato"; +char p3[] = "Zeno"; + +char alpha[] = "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + +int av[] = { 1, 2 , 3, 4 }; +int* ap1 = av; +int* ap2 = &av[0]; +int* ap3 = &av[4]; + +int f(int* pi) +{ + void* pv = pi; + return 0; +} + +void f1(char* p) +{ + char s[] = "Gorm"; + const char* pc = s; + pc = p; + + char *const cp = s; + cp[3] = 'a'; + + const char *const cpc = s; + char const* pc2 = s; +} + +void f2() +{ + int i = 1; + int& r = i; + int x = r; + r = 2; + const double& cdr = 1; +} + +void increment(int& aa) { aa++; } + +__int128 i128; +signed __int128 i128s; +unsigned __int128 i128u; + +Pchar pchar; + +typedef unsigned long size_t; +typedef long ssize_t; +typedef long ptrdiff_t; + +size_t st; +ssize_t sst; +ptrdiff_t pdt; +wchar_t wct; diff --git a/cpp/ql/test/library-tests/types/unspecified/exprs.expected b/cpp/ql/test/library-tests/types/unspecified/exprs.expected new file mode 100644 index 000000000000..ac49b6d061b8 --- /dev/null +++ b/cpp/ql/test/library-tests/types/unspecified/exprs.expected @@ -0,0 +1,20 @@ +| test.cpp:6:8:6:8 | c | struct C | struct C | +| test.cpp:6:8:6:8 | call to ~C | void | void | +| test.cpp:6:8:6:8 | destructor field destruction of c | struct C | struct C | +| test.cpp:8:11:8:13 | 0 | struct C | struct C | +| test.cpp:8:11:8:13 | constructor init of field c | struct C | struct C | +| test.cpp:11:8:11:8 | call to ~D | void | void | +| test.cpp:11:8:11:8 | d | struct D | struct D | +| test.cpp:11:8:11:8 | destructor field destruction of d | struct D | struct D | +| test.cpp:13:15:13:15 | call to D | void | void | +| test.cpp:13:15:13:15 | constructor init of field d | struct D | struct D | +| test.cpp:19:30:19:30 | (reference to) | reference to {const {struct E}} | reference to {struct E} | +| test.cpp:19:30:19:30 | call to E | void | void | +| test.cpp:19:30:19:30 | w | pointer to {int} | pointer to {int} | +| test.cpp:23:12:23:12 | (const F *)... | pointer to {const {struct F}} | pointer to {struct F} | +| test.cpp:23:12:23:12 | f | pointer to {struct F} | pointer to {struct F} | +| test.cpp:23:15:23:17 | call to fun | pointer to {struct E} | pointer to {struct E} | +| test.cpp:23:19:23:24 | (const E)... | const {struct E} | struct E | +| test.cpp:23:19:23:24 | (reference dereference) | struct E | struct E | +| test.cpp:23:19:23:24 | (reference to) | reference to {const {struct E}} | reference to {struct E} | +| test.cpp:23:19:23:24 | domain | reference to {struct E} | reference to {struct E} | diff --git a/cpp/ql/test/library-tests/types/unspecified/exprs.ql b/cpp/ql/test/library-tests/types/unspecified/exprs.ql new file mode 100644 index 000000000000..b23383a8ec22 --- /dev/null +++ b/cpp/ql/test/library-tests/types/unspecified/exprs.ql @@ -0,0 +1,6 @@ +import cpp + +from Expr e, Type t +where t = e.getType() +select e, t.explain(), t.getUnspecifiedType().explain() + diff --git a/cpp/ql/test/library-tests/types/unspecified/test.cpp b/cpp/ql/test/library-tests/types/unspecified/test.cpp new file mode 100644 index 000000000000..7b3ea964678c --- /dev/null +++ b/cpp/ql/test/library-tests/types/unspecified/test.cpp @@ -0,0 +1,25 @@ + +struct C { + ~C() { } +}; + +struct D { + C c; + D() : c() { } +}; + +struct E { + D d; + E(int *i) {} +}; + +extern int* w; + +struct F { + E *fun(const E& domain = w) const; +}; + +void g(E& domain, F *f) { + E *e = f->fun(domain); +} + diff --git a/cpp/ql/test/library-tests/types/wchar_t_typedef/ms.c b/cpp/ql/test/library-tests/types/wchar_t_typedef/ms.c new file mode 100644 index 000000000000..e1c0bcc4c4a2 --- /dev/null +++ b/cpp/ql/test/library-tests/types/wchar_t_typedef/ms.c @@ -0,0 +1,4 @@ + +typedef unsigned short wchar_t; + +wchar_t *wstring; diff --git a/cpp/ql/test/library-tests/types/wchar_t_typedef/options b/cpp/ql/test/library-tests/types/wchar_t_typedef/options new file mode 100644 index 000000000000..7b8a8f3f4bba --- /dev/null +++ b/cpp/ql/test/library-tests/types/wchar_t_typedef/options @@ -0,0 +1 @@ +extractor_flags: --microsoft --edg --target --edg win32 diff --git a/cpp/ql/test/library-tests/types/wchar_t_typedef/wchar_t.expected b/cpp/ql/test/library-tests/types/wchar_t_typedef/wchar_t.expected new file mode 100644 index 000000000000..3e89d75be03a --- /dev/null +++ b/cpp/ql/test/library-tests/types/wchar_t_typedef/wchar_t.expected @@ -0,0 +1,3 @@ +| file://:0:0:0:0 | wchar_t | Wchar_t, WideCharType | | +| file://:0:0:0:0 | wchar_t * | PointerType | TypedefType, Wchar_t | +| ms.c:2:24:2:30 | wchar_t | TypedefType, Wchar_t | | diff --git a/cpp/ql/test/library-tests/types/wchar_t_typedef/wchar_t.ql b/cpp/ql/test/library-tests/types/wchar_t_typedef/wchar_t.ql new file mode 100644 index 000000000000..d546ee79ce77 --- /dev/null +++ b/cpp/ql/test/library-tests/types/wchar_t_typedef/wchar_t.ql @@ -0,0 +1,9 @@ +import cpp + +from Type t +where + t.getName().matches("%wchar%") +select + t, + concat(t.getAQlClass(), ", "), + concat(t.(DerivedType).getBaseType().getAQlClass(), ", ") diff --git a/cpp/ql/test/library-tests/udl/options b/cpp/ql/test/library-tests/udl/options new file mode 100644 index 000000000000..540576d53a9a --- /dev/null +++ b/cpp/ql/test/library-tests/udl/options @@ -0,0 +1 @@ +extractor_flags: --gnu_version 40801 --clang -Xclang-only=-Wno-reserved-user-defined-literal diff --git a/cpp/ql/test/library-tests/udl/udl.cpp b/cpp/ql/test/library-tests/udl/udl.cpp new file mode 100644 index 000000000000..4110c4c3e4cf --- /dev/null +++ b/cpp/ql/test/library-tests/udl/udl.cpp @@ -0,0 +1,8 @@ +#define Y "y" + +static const char* operator"" _Y ( const char* str, decltype(sizeof(0))) { return str + 1; } + +static const char* xy = "X"Y; +static const char* xyxy = "X"Y"X"Y; +static const char* x_y = "X"_Y; +static const char* x_yx_y = "X"_Y"X"_Y; diff --git a/cpp/ql/test/library-tests/udl/udl.expected b/cpp/ql/test/library-tests/udl/udl.expected new file mode 100644 index 000000000000..fce69664a066 --- /dev/null +++ b/cpp/ql/test/library-tests/udl/udl.expected @@ -0,0 +1,4 @@ +| udl.cpp:5:25:5:28 | Xy | udl.cpp:5:25:5:28 | initializer for xy | +| udl.cpp:6:27:6:34 | XyXy | udl.cpp:6:27:6:34 | initializer for xyxy | +| udl.cpp:7:26:7:30 | X | udl.cpp:7:26:7:26 | call to operator "_Y | +| udl.cpp:8:29:8:38 | XX | udl.cpp:8:29:8:29 | call to operator "_Y | diff --git a/cpp/ql/test/library-tests/udl/udl.ql b/cpp/ql/test/library-tests/udl/udl.ql new file mode 100644 index 000000000000..4290355e4079 --- /dev/null +++ b/cpp/ql/test/library-tests/udl/udl.ql @@ -0,0 +1,4 @@ +import cpp + +from StringLiteral sl +select sl, sl.getParent() diff --git a/cpp/ql/test/library-tests/unions/Unions1.expected b/cpp/ql/test/library-tests/unions/Unions1.expected new file mode 100644 index 000000000000..50fb595144ff --- /dev/null +++ b/cpp/ql/test/library-tests/unions/Unions1.expected @@ -0,0 +1,3 @@ +| unions.cpp:4:8:4:12 | Entry | struct | | +| unions.cpp:13:7:13:11 | Value | struct | union | +| unions.cpp:19:8:19:22 | EntryWithMethod | struct | | diff --git a/cpp/ql/test/library-tests/unions/Unions1.ql b/cpp/ql/test/library-tests/unions/Unions1.ql new file mode 100644 index 000000000000..17e7fc9dd595 --- /dev/null +++ b/cpp/ql/test/library-tests/unions/Unions1.ql @@ -0,0 +1,6 @@ +import cpp + +from Class t, string struct, string union +where if t instanceof Struct then struct = "struct" else struct = "" + and if t instanceof Union then union = "union" else union = "" +select t, struct, union diff --git a/cpp/ql/test/library-tests/unions/Unions4.expected b/cpp/ql/test/library-tests/unions/Unions4.expected new file mode 100644 index 000000000000..d4fe50152d32 --- /dev/null +++ b/cpp/ql/test/library-tests/unions/Unions4.expected @@ -0,0 +1,3 @@ +| unions.cpp:19:8:19:22 | EntryWithMethod | unions.cpp:4:8:4:12 | Entry | unions.cpp:19:8:19:8 | operator= | +| unions.cpp:19:8:19:22 | EntryWithMethod | unions.cpp:4:8:4:12 | Entry | unions.cpp:19:8:19:8 | operator= | +| unions.cpp:19:8:19:22 | EntryWithMethod | unions.cpp:4:8:4:12 | Entry | unions.cpp:20:7:20:14 | getAsInt | diff --git a/cpp/ql/test/library-tests/unions/Unions4.ql b/cpp/ql/test/library-tests/unions/Unions4.ql new file mode 100644 index 000000000000..5bb587f37bd4 --- /dev/null +++ b/cpp/ql/test/library-tests/unions/Unions4.ql @@ -0,0 +1,4 @@ +import cpp + +from Struct s +select s, s.getABaseClass(), s.getAMemberFunction() diff --git a/cpp/ql/test/library-tests/unions/options b/cpp/ql/test/library-tests/unions/options new file mode 100644 index 000000000000..91adeb70204c --- /dev/null +++ b/cpp/ql/test/library-tests/unions/options @@ -0,0 +1 @@ +extractor_flags: --microsoft diff --git a/cpp/ql/test/library-tests/unions/unions.cpp b/cpp/ql/test/library-tests/unions/unions.cpp new file mode 100644 index 000000000000..6122c4e379d4 --- /dev/null +++ b/cpp/ql/test/library-tests/unions/unions.cpp @@ -0,0 +1,23 @@ + +enum Type { S, I }; + +struct Entry { + + char* name; + Type t; + char* s; + int i; + +}; + +union Value { + char* s; + int i; +}; + + +struct EntryWithMethod: Entry { + int getAsInt() { + return i; + } +}; diff --git a/cpp/ql/test/library-tests/unnamed/elements.expected b/cpp/ql/test/library-tests/unnamed/elements.expected new file mode 100644 index 000000000000..4aca4dd6877d --- /dev/null +++ b/cpp/ql/test/library-tests/unnamed/elements.expected @@ -0,0 +1,113 @@ +| 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 | _Complex __float128 | Other | +| file://:0:0:0:0 | _Complex double | Other | +| file://:0:0:0:0 | _Complex float | Other | +| file://:0:0:0:0 | _Complex long double | Other | +| file://:0:0:0:0 | _Decimal32 | Other | +| file://:0:0:0:0 | _Decimal64 | Other | +| file://:0:0:0:0 | _Decimal128 | Other | +| file://:0:0:0:0 | _Float32 | Other | +| file://:0:0:0:0 | _Float32x | Other | +| file://:0:0:0:0 | _Float64 | Other | +| file://:0:0:0:0 | _Float64x | Other | +| file://:0:0:0:0 | _Float128 | Other | +| file://:0:0:0:0 | _Float128x | Other | +| file://:0:0:0:0 | _Imaginary double | Other | +| file://:0:0:0:0 | _Imaginary float | Other | +| file://:0:0:0:0 | _Imaginary long double | Other | +| file://:0:0:0:0 | __block | Other | +| file://:0:0:0:0 | __float128 | Other | +| file://:0:0:0:0 | __int128 | Other | +| file://:0:0:0:0 | __interface | Other | +| file://:0:0:0:0 | __ptr32 | Other | +| file://:0:0:0:0 | __ptr64 | Other | +| file://:0:0:0:0 | __sptr | Other | +| file://:0:0:0:0 | __uptr | Other | +| file://:0:0:0:0 | __va_list_tag | Other | +| file://:0:0:0:0 | abstract | Other | +| file://:0:0:0:0 | atomic | Other | +| file://:0:0:0:0 | auto | Other | +| file://:0:0:0:0 | auto | Other | +| file://:0:0:0:0 | bool | Other | +| file://:0:0:0:0 | char | Other | +| file://:0:0:0:0 | char16_t | Other | +| file://:0:0:0:0 | char32_t | Other | +| file://:0:0:0:0 | const | Other | +| file://:0:0:0:0 | decltype(nullptr) | Other | +| file://:0:0:0:0 | definition of __va_list_tag | Other | +| file://:0:0:0:0 | definition of fp_offset | Other | +| file://:0:0:0:0 | definition of gp_offset | Other | +| file://:0:0:0:0 | definition of overflow_arg_area | Other | +| file://:0:0:0:0 | definition of reg_save_area | Other | +| file://:0:0:0:0 | dllexport | Other | +| file://:0:0:0:0 | dllimport | Other | +| file://:0:0:0:0 | double | Other | +| file://:0:0:0:0 | error | Other | +| file://:0:0:0:0 | explicit | Other | +| file://:0:0:0:0 | extern | Other | +| file://:0:0:0:0 | far | Other | +| file://:0:0:0:0 | float | Other | +| file://:0:0:0:0 | forceinline | Other | +| file://:0:0:0:0 | fp_offset | Other | +| file://:0:0:0:0 | gp_offset | Other | +| file://:0:0:0:0 | implicit_int | Other | +| file://:0:0:0:0 | inline | Other | +| file://:0:0:0:0 | int | Other | +| file://:0:0:0:0 | int * | Other | +| file://:0:0:0:0 | int[0] | Other | +| file://:0:0:0:0 | long | Other | +| file://:0:0:0:0 | long double | Other | +| file://:0:0:0:0 | long long | Other | +| file://:0:0:0:0 | microsoft_inline | Other | +| file://:0:0:0:0 | naked | Other | +| file://:0:0:0:0 | near | Other | +| file://:0:0:0:0 | noalias | Other | +| file://:0:0:0:0 | noinline | Other | +| file://:0:0:0:0 | noreturn | Other | +| file://:0:0:0:0 | nothrow | Other | +| file://:0:0:0:0 | novtable | Other | +| file://:0:0:0:0 | optional | Other | +| file://:0:0:0:0 | overflow_arg_area | Other | +| file://:0:0:0:0 | override | Other | +| file://:0:0:0:0 | private | Other | +| file://:0:0:0:0 | protected | Other | +| file://:0:0:0:0 | public | Other | +| file://:0:0:0:0 | pure | Other | +| file://:0:0:0:0 | reg_save_area | Other | +| file://:0:0:0:0 | register | Other | +| file://:0:0:0:0 | restrict | Other | +| file://:0:0:0:0 | sealed | Other | +| file://:0:0:0:0 | selectany | Other | +| file://:0:0:0:0 | short | Other | +| file://:0:0:0:0 | signed __int128 | Other | +| file://:0:0:0:0 | signed char | Other | +| file://:0:0:0:0 | signed int | Other | +| file://:0:0:0:0 | signed long | Other | +| file://:0:0:0:0 | signed long long | Other | +| file://:0:0:0:0 | signed short | Other | +| file://:0:0:0:0 | static | Other | +| file://:0:0:0:0 | thread | Other | +| file://:0:0:0:0 | unaligned | Other | +| file://:0:0:0:0 | unknown | Other | +| file://:0:0:0:0 | unsigned __int128 | Other | +| file://:0:0:0:0 | unsigned char | Other | +| file://:0:0:0:0 | unsigned int | Other | +| file://:0:0:0:0 | unsigned long | Other | +| file://:0:0:0:0 | unsigned long long | Other | +| file://:0:0:0:0 | unsigned short | Other | +| file://:0:0:0:0 | varargs | Other | +| file://:0:0:0:0 | virtual | Other | +| file://:0:0:0:0 | void | Other | +| file://:0:0:0:0 | void * | Other | +| file://:0:0:0:0 | volatile | Other | +| file://:0:0:0:0 | wchar_t | Other | +| 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 | 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 | {...} | Other | diff --git a/cpp/ql/test/library-tests/unnamed/elements.ql b/cpp/ql/test/library-tests/unnamed/elements.ql new file mode 100644 index 000000000000..211558bd78e0 --- /dev/null +++ b/cpp/ql/test/library-tests/unnamed/elements.ql @@ -0,0 +1,8 @@ +import cpp + +from Element e, string s +where not e instanceof Folder + and if e instanceof VariableAccess + then s = "Variable access" + else s = "Other" +select e, s diff --git a/cpp/ql/test/library-tests/unnamed/test.c b/cpp/ql/test/library-tests/unnamed/test.c new file mode 100644 index 000000000000..151cbf593605 --- /dev/null +++ b/cpp/ql/test/library-tests/unnamed/test.c @@ -0,0 +1,3 @@ + +int *a = (int[]){}; + diff --git a/cpp/ql/test/library-tests/unspecified_type/types/types.cpp b/cpp/ql/test/library-tests/unspecified_type/types/types.cpp new file mode 100644 index 000000000000..192c9635fb60 --- /dev/null +++ b/cpp/ql/test/library-tests/unspecified_type/types/types.cpp @@ -0,0 +1,47 @@ + +template +void fn1(void) { + int i = n1; +} + +template +void fn2(void) { + int i = n2; +} + +void fn2call(void) { + fn2<5>(); +} + +template +void fn3(void) { + T3 i = 1; +} + +template +void fn4(void) { + T4 i = 1; +} + +void fn4call(void) { + fn4(); +} + +template +class clX { + typedef int TX3; + void f(TX3 m); +}; + +template +void clX::f(TX3 n) { +} + +template